herb 0.11.1 → 0.11.2

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 (247) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +4 -2
  3. data/config.yml +6 -0
  4. data/ext/herb/extconf.rb +8 -31
  5. data/ext/herb/nodes.c +75 -75
  6. data/ext/herb/parser_options_helpers.c +26 -0
  7. data/lib/herb/analysis/partial_index.rb +1 -1
  8. data/lib/herb/analysis/partial_resolution.rb +27 -0
  9. data/lib/herb/ast/helpers.rb +1 -1
  10. data/lib/herb/configuration.rb +11 -1
  11. data/lib/herb/dev/classifier.rb +1 -1
  12. data/lib/herb/engine/compiler.rb +5 -8
  13. data/lib/herb/engine/slots/dynamics_compiler.rb +53 -14
  14. data/lib/herb/engine/slots/subtree_compiler.rb +1 -1
  15. data/lib/herb/engine/slots/visitor.rb +32 -4
  16. data/lib/herb/engine/visitors/instrumentation_visitor.rb +18 -6
  17. data/lib/herb/parser_options.rb +6 -3
  18. data/lib/herb/project.rb +19 -8
  19. data/lib/herb/version.rb +1 -1
  20. data/lib/herb.rb +1 -1
  21. data/sig/herb/analysis/partial_resolution.rbs +8 -0
  22. data/sig/herb/configuration.rbs +3 -0
  23. data/sig/herb/engine/slots/dynamics_compiler.rbs +24 -7
  24. data/sig/herb/engine/slots/visitor.rbs +14 -0
  25. data/sig/herb/engine/visitors/instrumentation_visitor.rbs +5 -1
  26. data/sig/herb/parser_options.rbs +6 -4
  27. data/sig/herb.rbs +2 -2
  28. data/src/analyze/action_view/attribute_extraction_helpers.c +40 -21
  29. data/src/analyze/action_view/generated_handlers.c +36 -36
  30. data/src/analyze/action_view/image_tag.c +7 -3
  31. data/src/analyze/action_view/javascript_include_tag.c +6 -3
  32. data/src/analyze/action_view/link_to.c +23 -23
  33. data/src/analyze/action_view/registry.c +7 -3
  34. data/src/analyze/action_view/tag_helper_node_builders.c +6 -13
  35. data/src/analyze/action_view/tag_helpers.c +139 -90
  36. data/src/analyze/action_view/turbo_frame_tag.c +5 -4
  37. data/src/analyze/analyze.c +35 -19
  38. data/src/analyze/analyze_helpers.c +161 -188
  39. data/src/analyze/analyzed_ruby.c +8 -7
  40. data/src/analyze/control_type.c +29 -22
  41. data/src/analyze/herb_directives.c +16 -20
  42. data/src/analyze/iteration_nodes.c +69 -24
  43. data/src/analyze/parse_errors.c +32 -22
  44. data/src/analyze/postfix_conditionals.c +23 -13
  45. data/src/analyze/prism_annotate.c +29 -28
  46. data/src/analyze/render_nodes.c +51 -41
  47. data/src/analyze/strict_locals.c +14 -15
  48. data/src/analyze/ternary_conditionals.c +14 -10
  49. data/src/herb.c +10 -8
  50. data/src/include/analyze/action_view/tag_helper_handler.h +1 -1
  51. data/src/include/analyze/action_view/tag_helper_node_builders.h +1 -0
  52. data/src/include/analyze/action_view/tag_helpers.h +1 -1
  53. data/src/include/analyze/analyze.h +3 -2
  54. data/src/include/analyze/analyzed_ruby.h +4 -2
  55. data/src/include/analyze/helpers.h +6 -6
  56. data/src/include/herb.h +3 -2
  57. data/src/include/parser/parser_options.h +5 -0
  58. data/src/include/prism/prism_context.h +12 -12
  59. data/src/include/prism/prism_helpers.h +30 -0
  60. data/src/include/prism_version.h +11 -0
  61. data/src/include/version.h +1 -1
  62. data/src/parser/parser_helpers.c +1 -2
  63. data/src/parser.c +6 -7
  64. data/src/parser_options_defaults.c +1 -0
  65. data/src/prism/prism_helpers.c +84 -50
  66. data/src/prism/ruby_parser.c +20 -20
  67. data/templates/ext/herb/nodes.c.erb +5 -5
  68. data/templates/ext/herb/parser_options_helpers.c.erb +33 -0
  69. data/templates/java/nodes.c.erb +6 -6
  70. data/templates/java/parser_options_helpers.c.erb +41 -2
  71. data/templates/java/parser_options_helpers.h.erb +2 -1
  72. data/templates/javascript/packages/node/extension/nodes.cpp.erb +5 -5
  73. data/templates/javascript/packages/node/extension/parser_options_helpers.cpp.erb +34 -2
  74. data/templates/javascript/packages/node/extension/parser_options_helpers.h.erb +1 -1
  75. data/templates/lib/herb/parser_options.rb.erb +1 -1
  76. data/templates/rust/src/prism/generated_deserialize.rs.erb +90 -44
  77. data/templates/rust/src/prism/generated_prettyprint.rs.erb +18 -27
  78. data/templates/src/analyze/action_view/generated_handlers.c.erb +23 -22
  79. data/templates/src/include/parser/parser_options.h.erb +10 -0
  80. data/templates/src/include/prism_version.h.erb +17 -0
  81. data/templates/src/parser_options_defaults.c.erb +1 -1
  82. data/templates/template.rb +17 -2
  83. data/templates/wasm/nodes.cpp.erb +5 -5
  84. data/templates/wasm/parser_options_helpers.cpp.erb +24 -0
  85. data/vendor/prism/Rakefile +76 -7
  86. data/vendor/prism/config.yml +174 -161
  87. data/vendor/prism/include/prism/arena.h +45 -0
  88. data/vendor/prism/include/prism/arena.hpp +42 -0
  89. data/vendor/prism/include/prism/ast.h +2738 -611
  90. data/vendor/prism/include/prism/ast.hpp +641 -0
  91. data/vendor/prism/include/prism/buffer.h +60 -0
  92. data/vendor/prism/include/prism/buffer.hpp +55 -0
  93. data/vendor/prism/include/prism/comments.h +72 -0
  94. data/vendor/prism/include/prism/compiler/accel.h +26 -0
  95. data/vendor/prism/include/prism/compiler/align.h +36 -0
  96. data/vendor/prism/include/prism/compiler/assume.h +25 -0
  97. data/vendor/prism/include/prism/compiler/branch.h +26 -0
  98. data/vendor/prism/include/prism/compiler/bswap.h +46 -0
  99. data/vendor/prism/include/prism/compiler/cplusplus.hpp +16 -0
  100. data/vendor/prism/include/prism/compiler/endianness.h +34 -0
  101. data/vendor/prism/include/prism/compiler/exceptions.hpp +17 -0
  102. data/vendor/prism/include/prism/compiler/exported.h +24 -0
  103. data/vendor/prism/include/prism/compiler/fallthrough.h +22 -0
  104. data/vendor/prism/include/prism/compiler/filesystem.h +32 -0
  105. data/vendor/prism/include/prism/compiler/flex_array.h +19 -0
  106. data/vendor/prism/include/prism/compiler/force_inline.h +21 -0
  107. data/vendor/prism/include/prism/compiler/format.h +27 -0
  108. data/vendor/prism/include/prism/compiler/inline.h +17 -0
  109. data/vendor/prism/include/prism/compiler/lifetimebound.hpp +25 -0
  110. data/vendor/prism/include/prism/compiler/nodiscard.h +22 -0
  111. data/vendor/prism/include/prism/compiler/nonnull.h +18 -0
  112. data/vendor/prism/include/prism/compiler/unused.h +18 -0
  113. data/vendor/prism/include/prism/constant_pool.h +106 -0
  114. data/vendor/prism/include/prism/diagnostic.h +55 -402
  115. data/vendor/prism/include/prism/errors_format.h +61 -0
  116. data/vendor/prism/include/prism/excludes.h +29 -0
  117. data/vendor/prism/include/prism/integer.h +41 -0
  118. data/vendor/prism/include/prism/internal/allocator.h +68 -0
  119. data/vendor/prism/include/prism/internal/allocator_debug.h +88 -0
  120. data/vendor/prism/include/prism/internal/arena.h +108 -0
  121. data/vendor/prism/include/prism/internal/bit.h +42 -0
  122. data/vendor/prism/include/prism/internal/buffer.h +94 -0
  123. data/vendor/prism/include/prism/internal/char.h +145 -0
  124. data/vendor/prism/include/prism/internal/comments.h +54 -0
  125. data/vendor/prism/include/prism/internal/constant_pool.h +152 -0
  126. data/vendor/prism/include/prism/internal/diagnostic.h +389 -0
  127. data/vendor/prism/include/prism/{encoding.h → internal/encoding.h} +27 -68
  128. data/vendor/prism/include/prism/internal/integer.h +68 -0
  129. data/vendor/prism/include/prism/internal/isinf.h +16 -0
  130. data/vendor/prism/include/prism/internal/line_offset_list.h +34 -0
  131. data/vendor/prism/include/prism/{util/pm_list.h → internal/list.h} +11 -52
  132. data/vendor/prism/include/prism/internal/magic_comments.h +23 -0
  133. data/vendor/prism/include/prism/internal/memchr.h +15 -0
  134. data/vendor/prism/include/prism/internal/node.h +42 -0
  135. data/vendor/prism/include/prism/internal/options.h +232 -0
  136. data/vendor/prism/include/prism/internal/parser.h +1015 -0
  137. data/vendor/prism/include/prism/internal/regexp.h +26 -0
  138. data/vendor/prism/include/prism/internal/serialize.h +39 -0
  139. data/vendor/prism/include/prism/internal/source.h +72 -0
  140. data/vendor/prism/include/prism/internal/static_literals.h +60 -0
  141. data/vendor/prism/include/prism/internal/stringy.h +30 -0
  142. data/vendor/prism/include/prism/{util/pm_strncasecmp.h → internal/strncasecmp.h} +3 -17
  143. data/vendor/prism/include/prism/{util/pm_strpbrk.h → internal/strpbrk.h} +7 -20
  144. data/vendor/prism/include/prism/internal/tokens.h +11 -0
  145. data/vendor/prism/include/prism/internal/xxh3.h +16 -0
  146. data/vendor/prism/include/prism/json.h +40 -0
  147. data/vendor/prism/include/prism/line_offset_list.h +69 -0
  148. data/vendor/prism/include/prism/magic_comments.h +53 -0
  149. data/vendor/prism/include/prism/node.h +59 -49
  150. data/vendor/prism/include/prism/node.hpp +272 -0
  151. data/vendor/prism/include/prism/node_type.hpp +38 -0
  152. data/vendor/prism/include/prism/options.h +154 -279
  153. data/vendor/prism/include/prism/options.hpp +43 -0
  154. data/vendor/prism/include/prism/parser.h +367 -870
  155. data/vendor/prism/include/prism/parser.hpp +274 -0
  156. data/vendor/prism/include/prism/prettyprint.h +14 -9
  157. data/vendor/prism/include/prism/serialize.h +123 -0
  158. data/vendor/prism/include/prism/source.h +163 -0
  159. data/vendor/prism/include/prism/stream.h +36 -0
  160. data/vendor/prism/include/prism/string_query.h +82 -0
  161. data/vendor/prism/include/prism/stringy.h +61 -0
  162. data/vendor/prism/include/prism/stringy.hpp +31 -0
  163. data/vendor/prism/include/prism/version.h +20 -3
  164. data/vendor/prism/include/prism.h +35 -320
  165. data/vendor/prism/include/prism.hpp +24 -0
  166. data/vendor/prism/src/arena.c +122 -0
  167. data/vendor/prism/src/{util/pm_buffer.c → buffer.c} +53 -21
  168. data/vendor/prism/src/{util/pm_char.c → char.c} +28 -57
  169. data/vendor/prism/src/comments.c +455 -0
  170. data/vendor/prism/src/constant_pool.c +406 -0
  171. data/vendor/prism/src/diagnostic.c +120 -68
  172. data/vendor/prism/src/encoding.c +8 -3
  173. data/vendor/prism/src/errors_format.c +519 -0
  174. data/vendor/prism/src/{util/pm_integer.c → integer.c} +38 -22
  175. data/vendor/prism/src/json.c +5733 -0
  176. data/vendor/prism/src/line_offset_list.c +100 -0
  177. data/vendor/prism/src/list.c +24 -0
  178. data/vendor/prism/src/{util/pm_memchr.c → memchr.c} +8 -6
  179. data/vendor/prism/src/node.c +3465 -7787
  180. data/vendor/prism/src/options.c +161 -54
  181. data/vendor/prism/src/parser.c +371 -0
  182. data/vendor/prism/src/prettyprint.c +421 -354
  183. data/vendor/prism/src/prism.c +8522 -7280
  184. data/vendor/prism/src/regexp.c +960 -45
  185. data/vendor/prism/src/serialize.c +2434 -2048
  186. data/vendor/prism/src/source.c +524 -0
  187. data/vendor/prism/src/static_literals.c +303 -360
  188. data/vendor/prism/src/string_query.c +180 -0
  189. data/vendor/prism/src/stringy.c +61 -0
  190. data/vendor/prism/src/{util/pm_strncasecmp.c → strncasecmp.c} +5 -4
  191. data/vendor/prism/src/strpbrk.c +447 -0
  192. data/vendor/prism/src/{token_type.c → tokens.c} +39 -18
  193. data/vendor/prism/src/xxh3.c +292 -0
  194. data/vendor/prism/templates/ext/prism/api_node.c.erb +58 -44
  195. data/vendor/prism/templates/include/prism/ast.h.erb +67 -17
  196. data/vendor/prism/templates/include/prism/ast.hpp.erb +31 -0
  197. data/vendor/prism/templates/include/prism/internal/diagnostic.h.erb +60 -0
  198. data/vendor/prism/templates/java/{org → api/src/main/java-templates/org/ruby_lang}/prism/AbstractNodeVisitor.java.erb +1 -1
  199. data/vendor/prism/templates/java/{org → api/src/main/java-templates/org/ruby_lang}/prism/Loader.java.erb +232 -133
  200. data/vendor/prism/templates/java/{org → api/src/main/java-templates/org/ruby_lang}/prism/Nodes.java.erb +62 -23
  201. data/vendor/prism/templates/javascript/src/deserialize.js.erb +153 -146
  202. data/vendor/prism/templates/javascript/src/nodes.js.erb +4 -9
  203. data/vendor/prism/templates/lib/prism/compiler.rb.erb +14 -5
  204. data/vendor/prism/templates/lib/prism/dispatcher.rb.erb +20 -12
  205. data/vendor/prism/templates/lib/prism/dot_visitor.rb.erb +37 -11
  206. data/vendor/prism/templates/lib/prism/dsl.rb.erb +43 -4
  207. data/vendor/prism/templates/lib/prism/inspect_visitor.rb.erb +26 -10
  208. data/vendor/prism/templates/lib/prism/mutation_compiler.rb.erb +5 -2
  209. data/vendor/prism/templates/lib/prism/node.rb.erb +283 -105
  210. data/vendor/prism/templates/lib/prism/reflection.rb.erb +11 -2
  211. data/vendor/prism/templates/lib/prism/serialize.rb.erb +291 -134
  212. data/vendor/prism/templates/lib/prism/visitor.rb.erb +18 -0
  213. data/vendor/prism/templates/src/diagnostic.c.erb +114 -63
  214. data/vendor/prism/templates/src/json.c.erb +130 -0
  215. data/vendor/prism/templates/src/node.c.erb +79 -236
  216. data/vendor/prism/templates/src/prettyprint.c.erb +24 -13
  217. data/vendor/prism/templates/src/serialize.c.erb +369 -183
  218. data/vendor/prism/templates/src/{token_type.c.erb → tokens.c.erb} +22 -11
  219. data/vendor/prism/templates/template.rb +193 -91
  220. metadata +100 -42
  221. data/vendor/prism/include/prism/defines.h +0 -260
  222. data/vendor/prism/include/prism/pack.h +0 -163
  223. data/vendor/prism/include/prism/regexp.h +0 -43
  224. data/vendor/prism/include/prism/static_literals.h +0 -121
  225. data/vendor/prism/include/prism/util/pm_buffer.h +0 -236
  226. data/vendor/prism/include/prism/util/pm_char.h +0 -204
  227. data/vendor/prism/include/prism/util/pm_constant_pool.h +0 -218
  228. data/vendor/prism/include/prism/util/pm_integer.h +0 -130
  229. data/vendor/prism/include/prism/util/pm_memchr.h +0 -29
  230. data/vendor/prism/include/prism/util/pm_newline_list.h +0 -113
  231. data/vendor/prism/include/prism/util/pm_string.h +0 -200
  232. data/vendor/prism/src/pack.c +0 -509
  233. data/vendor/prism/src/util/pm_constant_pool.c +0 -342
  234. data/vendor/prism/src/util/pm_list.c +0 -49
  235. data/vendor/prism/src/util/pm_newline_list.c +0 -125
  236. data/vendor/prism/src/util/pm_string.c +0 -381
  237. data/vendor/prism/src/util/pm_strpbrk.c +0 -206
  238. data/vendor/prism/templates/include/prism/diagnostic.h.erb +0 -130
  239. data/vendor/prism/templates/rbi/prism/dsl.rbi.erb +0 -68
  240. data/vendor/prism/templates/rbi/prism/node.rbi.erb +0 -167
  241. data/vendor/prism/templates/rbi/prism/visitor.rbi.erb +0 -18
  242. data/vendor/prism/templates/sig/prism/_private/dot_visitor.rbs.erb +0 -45
  243. data/vendor/prism/templates/sig/prism/dsl.rbs.erb +0 -31
  244. data/vendor/prism/templates/sig/prism/mutation_compiler.rbs.erb +0 -7
  245. data/vendor/prism/templates/sig/prism/node.rbs.erb +0 -136
  246. data/vendor/prism/templates/sig/prism/visitor.rbs.erb +0 -17
  247. data/vendor/prism/templates/sig/prism.rbs.erb +0 -92
@@ -0,0 +1,31 @@
1
+ /**
2
+ * @file ast.hpp
3
+ *
4
+ * The mapping between node structs and node types, generated from the same
5
+ * configuration as ast.h so that it covers every node.
6
+ */
7
+ #ifndef PRISM_AST_HPP
8
+ #define PRISM_AST_HPP
9
+
10
+ #include "prism/compiler/cplusplus.hpp"
11
+
12
+ #include "prism/ast.h"
13
+ #include "prism/node_type.hpp"
14
+
15
+ #include <cstddef>
16
+ #include <type_traits>
17
+
18
+ namespace prism {
19
+
20
+ /* Each node struct is cast to and from pm_node_t, which C++ allows only
21
+ * because the struct is standard-layout with pm_node_t as its first member. */
22
+ <%- nodes.each do |node| -%>
23
+
24
+ /** Maps pm_<%= node.human %>_t to <%= node.type %>. */
25
+ template <> struct node_type<pm_<%= node.human %>_t> : std::integral_constant<pm_node_type_t, <%= node.type %>> {};
26
+ static_assert(std::is_standard_layout_v<pm_<%= node.human %>_t> && offsetof(pm_<%= node.human %>_t, base) == 0, "pm_<%= node.human %>_t must begin with its pm_node_t");
27
+ <%- end -%>
28
+
29
+ }
30
+
31
+ #endif
@@ -0,0 +1,60 @@
1
+ #ifndef PRISM_INTERNAL_DIAGNOSTIC_H
2
+ #define PRISM_INTERNAL_DIAGNOSTIC_H
3
+
4
+ #include "prism/internal/list.h"
5
+
6
+ #include "prism/arena.h"
7
+ #include "prism/diagnostic.h"
8
+
9
+ /*
10
+ * The diagnostic IDs of all of the diagnostics, used to communicate the types
11
+ * of errors between the parser and the user.
12
+ */
13
+ typedef enum {
14
+ /* These are the error diagnostics. */
15
+ <%- errors.each do |error| -%>
16
+ PM_ERR_<%= error.name %>,
17
+ <%- end -%>
18
+
19
+ /* These are the warning diagnostics. */
20
+ <%- warnings.each do |warning| -%>
21
+ PM_WARN_<%= warning.name %>,
22
+ <%- end -%>
23
+ } pm_diagnostic_id_t;
24
+
25
+ /*
26
+ * This struct represents a diagnostic generated during parsing.
27
+ */
28
+ struct pm_diagnostic_t {
29
+ /* The embedded base node. */
30
+ pm_list_node_t node;
31
+
32
+ /* The location of the diagnostic in the source. */
33
+ pm_location_t location;
34
+
35
+ /* The ID of the diagnostic. */
36
+ pm_diagnostic_id_t diag_id;
37
+
38
+ /* The message associated with the diagnostic. */
39
+ const char *message;
40
+
41
+ /*
42
+ * The level of the diagnostic, see `pm_error_level_t` and
43
+ * `pm_warning_level_t` for possible values.
44
+ */
45
+ uint8_t level;
46
+ };
47
+
48
+ /*
49
+ * Append a diagnostic to the given list of diagnostics that is using shared
50
+ * memory for its message.
51
+ */
52
+ void pm_diagnostic_list_append(pm_arena_t *arena, pm_list_t *list, uint32_t start, uint32_t length, pm_diagnostic_id_t diag_id);
53
+
54
+ /*
55
+ * Append a diagnostic to the given list of diagnostics that is using a format
56
+ * string for its message.
57
+ */
58
+ void pm_diagnostic_list_append_format(pm_arena_t *arena, pm_list_t *list, uint32_t start, uint32_t length, pm_diagnostic_id_t diag_id, ...);
59
+
60
+ #endif
@@ -1,4 +1,4 @@
1
- package org.prism;
1
+ package org.ruby_lang.prism;
2
2
 
3
3
  // GENERATED BY <%= File.basename(__FILE__) %>
4
4
  // @formatter:off
@@ -1,77 +1,67 @@
1
- <%- string_type = Prism::Template::JAVA_STRING_TYPE -%>
2
- package org.prism;
3
-
4
- import org.prism.Nodes;
1
+ <%- id_type = Prism::Template::JAVA_IDENTIFIER_TYPE -%>
2
+ package org.ruby_lang.prism;
5
3
 
6
4
  import java.lang.Short;
7
5
  import java.math.BigInteger;
8
6
  import java.nio.ByteBuffer;
9
7
  import java.nio.ByteOrder;
10
- import java.nio.charset.Charset;
11
8
  import java.nio.charset.StandardCharsets;
12
- import java.util.Locale;
13
9
 
14
10
  // GENERATED BY <%= File.basename(__FILE__) %>
15
11
  // @formatter:off
16
12
  public class Loader {
17
13
 
18
- public static ParseResult load(byte[] serialized, byte[] sourceBytes) {
19
- return new Loader(serialized, sourceBytes).load();
14
+ public static ParseResult load(byte[] serialized) {
15
+ return new Loader(serialized).load();
20
16
  }
21
17
 
22
18
  // Overridable methods
23
19
 
24
- public Charset getEncodingCharset(String encodingName) {
25
- encodingName = encodingName.toLowerCase(Locale.ROOT);
26
- if (encodingName.equals("ascii-8bit")) {
27
- return StandardCharsets.US_ASCII;
28
- }
29
- return Charset.forName(encodingName);
30
- }
31
-
32
- public <%= string_type %> bytesToName(byte[] bytes) {
33
- <%- if string_type == "String" -%>
34
- return new String(bytes, encodingCharset).intern();
20
+ public <%= id_type %> bytesToName(byte[] serialized, int offset, int length) {
21
+ <%- if id_type == "byte[]" -%>
22
+ byte[] bytes = new byte[length];
23
+ System.arraycopy(serialized, offset, bytes, 0, length);
24
+ return bytes;
35
25
  <%- else -%>
36
- return null; // Must be implemented by subclassing Loader
26
+ throw new AbstractMethodError("Loader.bytesToName(<%= id_type %>) is not implemented");
37
27
  <%- end -%>
38
28
  }
39
29
 
30
+ /**
31
+ * Whether to skip the bodies of DefNodes while loading. A lazily loaded
32
+ * DefNode has a null body until it is replaced with DefNode#getNonLazy(),
33
+ * which loads the whole DefNode again from the serialized output.
34
+ */
35
+ protected boolean loadDefNodesLazily() {
36
+ return false;
37
+ }
38
+
40
39
  private static final class ConstantPool {
41
40
 
42
41
  private final Loader loader;
43
- private final byte[] source;
44
42
  private final int bufferOffset;
45
- private final <%= string_type %>[] cache;
43
+ private final <%= id_type %>[] cache;
46
44
 
47
- ConstantPool(Loader loader, byte[] source, int bufferOffset, int length) {
45
+ ConstantPool(Loader loader, int bufferOffset, int length) {
48
46
  this.loader = loader;
49
- this.source = source;
50
47
  this.bufferOffset = bufferOffset;
51
- cache = new <%= string_type %>[length];
48
+ <%- if id_type == "String" -%>
49
+ this.cache = new <%= id_type %>[length];
50
+ <%- else -%>
51
+ this.cache = new byte[length][];
52
+ <%- end -%>
52
53
  }
53
54
 
54
- <%= string_type %> get(ByteBuffer buffer, int oneBasedIndex) {
55
+ <%= id_type %> get(ByteBuffer buffer, int oneBasedIndex) {
55
56
  int index = oneBasedIndex - 1;
56
- <%= string_type %> constant = cache[index];
57
+ <%= id_type %> constant = cache[index];
57
58
 
58
59
  if (constant == null) {
59
60
  int offset = bufferOffset + index * 8;
60
61
  int start = buffer.getInt(offset);
61
62
  int length = buffer.getInt(offset + 4);
62
63
 
63
- byte[] bytes = new byte[length];
64
-
65
- if (Integer.compareUnsigned(start, 0x7FFFFFFF) <= 0) {
66
- System.arraycopy(source, start, bytes, 0, length);
67
- } else {
68
- int position = buffer.position();
69
- buffer.position(start & 0x7FFFFFFF);
70
- buffer.get(bytes, 0, length);
71
- buffer.position(position);
72
- }
73
-
74
- constant = loader.bytesToName(bytes);
64
+ constant = loader.bytesToName(loader.serialized, start, length);
75
65
  cache[index] = constant;
76
66
  }
77
67
 
@@ -80,28 +70,28 @@ public class Loader {
80
70
 
81
71
  }
82
72
 
73
+ private final byte[] serialized;
83
74
  private final ByteBuffer buffer;
84
- private final Nodes.Source source;
85
75
  protected String encodingName;
86
- <%- if string_type == "String" -%>
87
- private Charset encodingCharset;
88
- <%- end -%>
89
76
  private ConstantPool constantPool;
77
+ private Nodes.Source source = null;
90
78
 
91
- protected Loader(byte[] serialized, byte[] sourceBytes) {
79
+ protected Loader(byte[] serialized) {
80
+ this.serialized = serialized;
92
81
  this.buffer = ByteBuffer.wrap(serialized).order(ByteOrder.nativeOrder());
93
- this.source = new Nodes.Source(sourceBytes);
94
82
  }
95
83
 
96
84
  protected ParseResult load() {
85
+ this.source = new Nodes.Source();
86
+
97
87
  expect((byte) 'P', "incorrect prism header");
98
88
  expect((byte) 'R', "incorrect prism header");
99
89
  expect((byte) 'I', "incorrect prism header");
100
90
  expect((byte) 'S', "incorrect prism header");
101
91
  expect((byte) 'M', "incorrect prism header");
102
92
 
103
- expect((byte) 1, "prism major version does not match");
104
- expect((byte) 9, "prism minor version does not match");
93
+ expect((byte) 2, "prism major version does not match");
94
+ expect((byte) 1, "prism minor version does not match");
105
95
  expect((byte) 0, "prism patch version does not match");
106
96
 
107
97
  expect((byte) 1, "Loader.java requires no location fields in the serialized output");
@@ -111,63 +101,49 @@ public class Loader {
111
101
  byte[] encodingNameBytes = new byte[encodingLength];
112
102
  buffer.get(encodingNameBytes);
113
103
  this.encodingName = new String(encodingNameBytes, StandardCharsets.US_ASCII);
114
- <%- if string_type == "String" -%>
115
- this.encodingCharset = getEncodingCharset(this.encodingName);
116
- <%- end -%>
117
104
 
118
105
  source.setStartLine(loadVarSInt());
106
+
107
+ // Skip the 8-byte hash of the source, which this library does not expose.
108
+ buffer.getLong();
109
+
119
110
  source.setLineOffsets(loadLineOffsets());
120
111
 
121
112
  ParseResult.MagicComment[] magicComments = loadMagicComments();
122
113
  Nodes.Location dataLocation = loadOptionalLocation();
123
114
  ParseResult.Error[] errors = loadErrors();
124
115
  ParseResult.Warning[] warnings = loadWarnings();
116
+ boolean continuable = buffer.get() != 0;
125
117
 
126
118
  int constantPoolBufferOffset = buffer.getInt();
127
119
  int constantPoolLength = loadVarUInt();
128
- this.constantPool = new ConstantPool(this, source.bytes, constantPoolBufferOffset, constantPoolLength);
120
+ this.constantPool = new ConstantPool(this, constantPoolBufferOffset, constantPoolLength);
129
121
 
130
122
  Nodes.Node node;
131
123
  if (errors.length == 0) {
132
- node = loadNode();
124
+ node = loadNodes(constantPoolBufferOffset, -1);
133
125
 
134
126
  int left = constantPoolBufferOffset - buffer.position();
135
127
  if (left != 0) {
136
128
  throw new Error("Expected to consume all bytes while deserializing but there were " + left + " bytes left");
137
129
  }
138
130
 
139
- boolean[] newlineMarked = new boolean[1 + source.getLineCount()];
140
- MarkNewlinesVisitor visitor = new MarkNewlinesVisitor(source, newlineMarked);
131
+ MarkNewlinesVisitor visitor = new MarkNewlinesVisitor(source);
141
132
  node.accept(visitor);
142
133
  } else {
143
134
  node = null;
144
135
  }
145
136
 
146
- return new ParseResult(node, magicComments, dataLocation, errors, warnings, source);
137
+ return new ParseResult(node, magicComments, dataLocation, errors, warnings, continuable, source);
147
138
  }
148
139
 
149
- private byte[] loadEmbeddedString() {
140
+ private byte[] loadString() {
150
141
  int length = loadVarUInt();
151
142
  byte[] bytes = new byte[length];
152
143
  buffer.get(bytes);
153
144
  return bytes;
154
145
  }
155
146
 
156
- private byte[] loadString() {
157
- switch (buffer.get()) {
158
- case 1:
159
- int start = loadVarUInt();
160
- int length = loadVarUInt();
161
- byte[] bytes = new byte[length];
162
- System.arraycopy(source.bytes, start, bytes, 0, length);
163
- return bytes;
164
- case 2:
165
- return loadEmbeddedString();
166
- default:
167
- throw new Error("Expected 0 or 1 but was " + buffer.get());
168
- }
169
- }
170
-
171
147
  private int[] loadLineOffsets() {
172
148
  int count = loadVarUInt();
173
149
  int[] lineOffsets = new int[count];
@@ -199,7 +175,7 @@ public class Loader {
199
175
  // error messages only contain ASCII characters
200
176
  for (int i = 0; i < count; i++) {
201
177
  Nodes.ErrorType type = Nodes.ERROR_TYPES[loadVarUInt()];
202
- byte[] bytes = loadEmbeddedString();
178
+ byte[] bytes = loadString();
203
179
  String message = new String(bytes, StandardCharsets.US_ASCII);
204
180
  Nodes.Location location = loadLocation();
205
181
  ParseResult.ErrorLevel level = ParseResult.ERROR_LEVELS[buffer.get()];
@@ -218,7 +194,7 @@ public class Loader {
218
194
  // warning messages only contain ASCII characters
219
195
  for (int i = 0; i < count; i++) {
220
196
  Nodes.WarningType type = Nodes.WARNING_TYPES[loadVarUInt() - <%= errors.length %>];
221
- byte[] bytes = loadEmbeddedString();
197
+ byte[] bytes = loadString();
222
198
  String message = new String(bytes, StandardCharsets.US_ASCII);
223
199
  Nodes.Location location = loadLocation();
224
200
  ParseResult.WarningLevel level = ParseResult.WARNING_LEVELS[buffer.get()];
@@ -230,20 +206,11 @@ public class Loader {
230
206
  return warnings;
231
207
  }
232
208
 
233
- private Nodes.Node loadOptionalNode() {
234
- if (buffer.get(buffer.position()) != 0) {
235
- return loadNode();
236
- } else {
237
- buffer.position(buffer.position() + 1); // continue after the 0 byte
238
- return null;
239
- }
240
- }
241
-
242
- private <%= string_type %> loadConstant() {
209
+ private <%= id_type %> loadConstant() {
243
210
  return constantPool.get(buffer, loadVarUInt());
244
211
  }
245
212
 
246
- private <%= string_type %> loadOptionalConstant() {
213
+ private <%= id_type %> loadOptionalConstant() {
247
214
  if (buffer.get(buffer.position()) != 0) {
248
215
  return loadConstant();
249
216
  } else {
@@ -252,12 +219,16 @@ public class Loader {
252
219
  }
253
220
  }
254
221
 
255
- private <%= string_type %>[] loadConstants() {
222
+ private <%= id_type %>[] loadConstants() {
256
223
  int length = loadVarUInt();
257
224
  if (length == 0) {
258
- return Nodes.EMPTY_STRING_ARRAY;
225
+ return Nodes.EMPTY_IDENTIFIER_ARRAY;
259
226
  }
260
- <%= string_type %>[] constants = new <%= string_type %>[length];
227
+ <%- if id_type == "String" -%>
228
+ <%= id_type %>[] constants = new <%= id_type %>[length];
229
+ <%- else -%>
230
+ <%= id_type %>[] constants = new byte[length][];
231
+ <%- end -%>
261
232
  for (int i = 0; i < length; i++) {
262
233
  constants[i] = constantPool.get(buffer, loadVarUInt());
263
234
  }
@@ -356,67 +327,195 @@ public class Loader {
356
327
  return negative ? result.negate() : result;
357
328
  }
358
329
 
359
- private Nodes.Node loadNode() {
360
- int type = buffer.get() & 0xFF;
330
+ private static final int LAZY_MARKER = <%= Prism::Template::LAZY_MARKER %>;
331
+
332
+ /**
333
+ * The stack of nodes that have been loaded but not yet attached to their
334
+ * parent, along with the lazy subtrees that are pending.
335
+ */
336
+ private static final class NodeStack {
337
+
338
+ private Nodes.Node[] nodes = new Nodes.Node[64];
339
+ private int size = 0;
340
+
341
+ /** For each pending lazy subtree, its marker position, start, and end. */
342
+ private int[] lazy = new int[3 * 8];
343
+ private int lazySize = 0;
344
+
345
+ void push(Nodes.Node node) {
346
+ if (size == nodes.length) {
347
+ nodes = java.util.Arrays.copyOf(nodes, size * 2);
348
+ }
349
+ nodes[size++] = node;
350
+ }
351
+
352
+ Nodes.Node pop() {
353
+ Nodes.Node node = nodes[--size];
354
+ nodes[size] = null;
355
+ return node;
356
+ }
357
+
358
+ void pushLazy(int marker, int start, int end) {
359
+ if (lazySize * 3 == lazy.length) {
360
+ lazy = java.util.Arrays.copyOf(lazy, lazy.length * 2);
361
+ }
362
+ lazy[lazySize * 3] = marker;
363
+ lazy[lazySize * 3 + 1] = start;
364
+ lazy[lazySize * 3 + 2] = end;
365
+ lazySize++;
366
+ }
367
+
368
+ /**
369
+ * Returns the marker position of the most recently pushed lazy
370
+ * subtree, or -1 if there is none. That subtree always belongs to the
371
+ * node whose record is being loaded, because the lazy subtrees of its
372
+ * descendants are popped when their own records are loaded.
373
+ */
374
+ int popLazy() {
375
+ return lazySize == 0 ? -1 : lazy[--lazySize * 3];
376
+ }
377
+ }
378
+
379
+ /**
380
+ * Loads the nodes serialized from the current position, stopping at the
381
+ * given offset once a single root remains, and returns that root. Nodes
382
+ * are serialized in post-order, so the children of each node are the
383
+ * topmost values on the stack, in field order, by the time its record is
384
+ * loaded. The lazy marker at eagerMarker, if any, is loaded eagerly even
385
+ * when DefNodes are loaded lazily.
386
+ */
387
+ private Nodes.Node loadNodes(int end, int eagerMarker) {
388
+ boolean lazy = loadDefNodesLazily();
389
+ NodeStack stack = new NodeStack();
390
+
391
+ while (buffer.position() < end || stack.size != 1) {
392
+ if (stack.lazySize > 0 && buffer.position() == stack.lazy[stack.lazySize * 3 - 2]) {
393
+ buffer.position(stack.lazy[stack.lazySize * 3 - 1]);
394
+ continue;
395
+ }
396
+
397
+ int type = buffer.get() & 0xFF;
398
+ switch (type) {
399
+ case 0:
400
+ stack.push(null);
401
+ break;
402
+ case LAZY_MARKER: {
403
+ int marker = buffer.position() - 1;
404
+ int skip = buffer.getInt();
405
+ int length = buffer.getInt();
406
+ if (lazy && marker != eagerMarker) {
407
+ int start = buffer.position() + skip;
408
+ stack.pushLazy(marker, start, start + length);
409
+ }
410
+ break;
411
+ }
412
+ <%- nodes.each.with_index(1) do |node, index| -%>
413
+ case <%= index %>:
414
+ stack.push(load<%= node.name %>(stack));
415
+ break;
416
+ <%- end -%>
417
+ default:
418
+ throw new Error("Unknown node type: " + type);
419
+ }
420
+ }
421
+
422
+ return stack.pop();
423
+ }
424
+
425
+ Nodes.DefNode loadNonLazyDefNode(int lazyPosition) {
426
+ Nodes.DefNode node;
427
+ // This method mutates the buffer position and may be called from different threads so we must synchronize
428
+ synchronized (this) {
429
+ buffer.position(lazyPosition + 1);
430
+ int skip = buffer.getInt();
431
+ int length = buffer.getInt();
432
+ int end = buffer.position() + skip + length;
433
+
434
+ buffer.position(lazyPosition);
435
+ node = (Nodes.DefNode) loadNodes(end, lazyPosition);
436
+ }
437
+
438
+ MarkNewlinesVisitor visitor = new MarkNewlinesVisitor(source);
439
+ node.accept(visitor);
440
+
441
+ return node;
442
+ }
443
+ <%- array_types = [] -%>
444
+ <%- nodes.each do |node| -%>
445
+
446
+ private Nodes.<%= node.name %> load<%= node.name %>(NodeStack stack) {
361
447
  <%- if Prism::Template::INCLUDE_NODE_ID -%>
362
448
  int nodeId = loadVarUInt();
363
449
  <%- end -%>
364
450
  int startOffset = loadVarUInt();
365
451
  int length = loadVarUInt();
366
-
367
- switch (type) {
368
- <%- array_types = [] -%>
369
- <%- nodes.each_with_index do |node, index| -%>
370
- case <%= index + 1 %>:
371
- <%-
372
- params = []
373
- params << "nodeId" if Prism::Template::INCLUDE_NODE_ID
374
- params << "startOffset" << "length"
375
- params << "buffer.getInt()" if node.needs_serialized_length?
376
- params << "loadFlags()" if node.flags
377
- params.concat node.semantic_fields.map { |field|
378
- case field
379
- when Prism::Template::NodeField then "#{field.java_cast}loadNode()"
380
- when Prism::Template::OptionalNodeField then "#{field.java_cast}loadOptionalNode()"
381
- when Prism::Template::StringField then "loadString()"
382
- when Prism::Template::NodeListField then
383
- element_type = field.java_type.sub('[]', '')
384
- array_types << element_type
385
- "load#{element_type}s()"
386
- when Prism::Template::ConstantField then "loadConstant()"
387
- when Prism::Template::OptionalConstantField then "loadOptionalConstant()"
388
- when Prism::Template::ConstantListField then "loadConstants()"
389
- when Prism::Template::LocationField then "loadLocation()"
390
- when Prism::Template::OptionalLocationField then "loadOptionalLocation()"
391
- when Prism::Template::UInt8Field then "buffer.get()"
392
- when Prism::Template::UInt32Field then "loadVarUInt()"
393
- when Prism::Template::IntegerField then "loadInteger()"
394
- when Prism::Template::DoubleField then "buffer.getDouble()"
395
- else raise
396
- end
397
- }
398
- -%>
399
- return new Nodes.<%= node.name %>(<%= params.join(", ") -%>);
400
- <%- end -%>
401
- default:
402
- throw new Error("Unknown node type: " + type);
403
- }
452
+ <%- if node.flags -%>
453
+ short flags = loadFlags();
454
+ <%- end -%>
455
+ <%- node.semantic_fields.each do |field| -%>
456
+ <%- case field -%>
457
+ <%- when Prism::Template::NodeField, Prism::Template::OptionalNodeField -%>
458
+ <%- when Prism::Template::NodeListField -%>
459
+ int <%= field.name %>Size = loadVarUInt();
460
+ <%- else -%>
461
+ var field_<%= field.name %> = <%=
462
+ case field
463
+ when Prism::Template::StringField then "loadString()"
464
+ when Prism::Template::ConstantField then "loadConstant()"
465
+ when Prism::Template::OptionalConstantField then "loadOptionalConstant()"
466
+ when Prism::Template::ConstantListField then "loadConstants()"
467
+ when Prism::Template::LocationField then "loadLocation()"
468
+ when Prism::Template::OptionalLocationField then "loadOptionalLocation()"
469
+ when Prism::Template::UInt8Field then "buffer.get()"
470
+ when Prism::Template::UInt32Field then "loadVarUInt()"
471
+ when Prism::Template::IntegerField then "loadInteger()"
472
+ when Prism::Template::DoubleField then "buffer.getDouble()"
473
+ else raise
474
+ end
475
+ %>;
476
+ <%- end -%>
477
+ <%- end -%>
478
+ <%- if node.lazy_field -%>
479
+ int lazyPosition = stack.popLazy();
480
+ <%- end -%>
481
+ <%- node.child_fields.reverse_each do |field| -%>
482
+ <%- case field -%>
483
+ <%- when Prism::Template::NodeListField -%>
484
+ <%- element_type = field.java_type.sub("[]", ""); array_types << element_type -%>
485
+ var field_<%= field.name %> = pop<%= element_type %>s(stack, <%= field.name %>Size);
486
+ <%- else -%>
487
+ <%- if field == node.lazy_field -%>
488
+ var field_<%= field.name %> = lazyPosition == -1 ? <%= field.java_cast %>stack.pop() : null;
489
+ <%- else -%>
490
+ var field_<%= field.name %> = <%= field.java_cast %>stack.pop();
491
+ <%- end -%>
492
+ <%- end -%>
493
+ <%- end -%>
494
+ <%-
495
+ params = []
496
+ params << "nodeId" if Prism::Template::INCLUDE_NODE_ID
497
+ params << "startOffset" << "length"
498
+ params << "lazyPosition" << "lazyPosition == -1 ? null : this" if node.lazy_field
499
+ params << "flags" if node.flags
500
+ params.concat(node.semantic_fields.map { |field| "field_#{field.name}" })
501
+ -%>
502
+ return new Nodes.<%= node.name %>(<%= params.join(", ") %>);
404
503
  }
504
+ <%- end -%>
405
505
  <%- array_types.uniq.each do |type| -%>
406
506
 
407
507
  private static final Nodes.<%= type %>[] EMPTY_<%= type %>_ARRAY = {};
408
508
 
409
- private Nodes.<%= type %>[] load<%= type %>s() {
410
- int length = loadVarUInt();
509
+ private static Nodes.<%= type %>[] pop<%= type %>s(NodeStack stack, int length) {
411
510
  if (length == 0) {
412
511
  return EMPTY_<%= type %>_ARRAY;
413
512
  }
414
513
  Nodes.<%= type %>[] nodes = new Nodes.<%= type %>[length];
415
- for (int i = 0; i < length; i++) {
416
- <%- if type == 'Node' -%>
417
- nodes[i] = loadNode();
514
+ for (int i = length - 1; i >= 0; i--) {
515
+ <%- if type == "Node" -%>
516
+ nodes[i] = stack.pop();
418
517
  <%- else -%>
419
- nodes[i] = (Nodes.<%= type %>) loadNode();
518
+ nodes[i] = (Nodes.<%= type %>) stack.pop();
420
519
  <%- end -%>
421
520
  }
422
521
  return nodes;