prism 0.21.0 → 0.23.0

Sign up to get free protection for your applications and to get access to all the features.
data/src/serialize.c CHANGED
@@ -1843,6 +1843,17 @@ pm_serialize_node(pm_parser_t *parser, pm_node_t *node, pm_buffer_t *buffer) {
1843
1843
  }
1844
1844
  }
1845
1845
 
1846
+ static void
1847
+ pm_serialize_newline_list(pm_newline_list_t *list, pm_buffer_t *buffer) {
1848
+ uint32_t size = pm_sizet_to_u32(list->size);
1849
+ pm_buffer_append_varuint(buffer, size);
1850
+
1851
+ for (uint32_t i = 0; i < size; i++) {
1852
+ uint32_t offset = pm_sizet_to_u32(list->offsets[i]);
1853
+ pm_buffer_append_varuint(buffer, offset);
1854
+ }
1855
+ }
1856
+
1846
1857
  static void
1847
1858
  pm_serialize_comment(pm_parser_t *parser, pm_comment_t *comment, pm_buffer_t *buffer) {
1848
1859
  // serialize type
@@ -1929,19 +1940,25 @@ pm_serialize_encoding(const pm_encoding_t *encoding, pm_buffer_t *buffer) {
1929
1940
  pm_buffer_append_string(buffer, encoding->name, encoding_length);
1930
1941
  }
1931
1942
 
1932
- #line 218 "serialize.c.erb"
1933
- /**
1934
- * Serialize the encoding, metadata, nodes, and constant pool.
1935
- */
1936
- void
1937
- pm_serialize_content(pm_parser_t *parser, pm_node_t *node, pm_buffer_t *buffer) {
1943
+ static void
1944
+ pm_serialize_metadata(pm_parser_t *parser, pm_buffer_t *buffer) {
1938
1945
  pm_serialize_encoding(parser->encoding, buffer);
1939
1946
  pm_buffer_append_varsint(buffer, parser->start_line);
1947
+ pm_serialize_newline_list(&parser->newline_list, buffer);
1940
1948
  pm_serialize_comment_list(parser, &parser->comment_list, buffer);
1941
1949
  pm_serialize_magic_comment_list(parser, &parser->magic_comment_list, buffer);
1942
1950
  pm_serialize_data_loc(parser, buffer);
1943
1951
  pm_serialize_diagnostic_list(parser, &parser->error_list, buffer);
1944
1952
  pm_serialize_diagnostic_list(parser, &parser->warning_list, buffer);
1953
+ }
1954
+
1955
+ #line 243 "serialize.c.erb"
1956
+ /**
1957
+ * Serialize the metadata, nodes, and constant pool.
1958
+ */
1959
+ void
1960
+ pm_serialize_content(pm_parser_t *parser, pm_node_t *node, pm_buffer_t *buffer) {
1961
+ pm_serialize_metadata(parser, buffer);
1945
1962
 
1946
1963
  // Here we're going to leave space for the offset of the constant pool in
1947
1964
  // the buffer.
@@ -2032,13 +2049,7 @@ pm_serialize_lex(pm_buffer_t *buffer, const uint8_t *source, size_t size, const
2032
2049
  // Append 0 to mark end of tokens.
2033
2050
  pm_buffer_append_byte(buffer, 0);
2034
2051
 
2035
- pm_serialize_encoding(parser.encoding, buffer);
2036
- pm_buffer_append_varsint(buffer, parser.start_line);
2037
- pm_serialize_comment_list(&parser, &parser.comment_list, buffer);
2038
- pm_serialize_magic_comment_list(&parser, &parser.magic_comment_list, buffer);
2039
- pm_serialize_data_loc(&parser, buffer);
2040
- pm_serialize_diagnostic_list(&parser, &parser.error_list, buffer);
2041
- pm_serialize_diagnostic_list(&parser, &parser.warning_list, buffer);
2052
+ pm_serialize_metadata(&parser, buffer);
2042
2053
 
2043
2054
  pm_node_destroy(&parser, node);
2044
2055
  pm_parser_free(&parser);
data/src/token_type.c CHANGED
@@ -469,7 +469,7 @@ pm_token_type_human(pm_token_type_t token_type) {
469
469
  case PM_TOKEN_HEREDOC_START:
470
470
  return "heredoc beginning";
471
471
  case PM_TOKEN_IDENTIFIER:
472
- return "local variable or method identifier";
472
+ return "local variable or method";
473
473
  case PM_TOKEN_IGNORED_NEWLINE:
474
474
  return "ignored newline";
475
475
  case PM_TOKEN_INSTANCE_VARIABLE:
@@ -579,7 +579,7 @@ pm_token_type_human(pm_token_type_t token_type) {
579
579
  case PM_TOKEN_LABEL:
580
580
  return "label";
581
581
  case PM_TOKEN_LABEL_END:
582
- return "':'";
582
+ return "label terminator";
583
583
  case PM_TOKEN_LAMBDA_BEGIN:
584
584
  return "'{'";
585
585
  case PM_TOKEN_LESS:
@@ -681,7 +681,7 @@ pm_token_type_human(pm_token_type_t token_type) {
681
681
  case PM_TOKEN_UPLUS:
682
682
  return "'+'";
683
683
  case PM_TOKEN_USTAR:
684
- return "'*'";
684
+ return "*";
685
685
  case PM_TOKEN_USTAR_STAR:
686
686
  return "'**'";
687
687
  case PM_TOKEN_WORDS_SEP:
@@ -186,7 +186,7 @@ pm_constant_pool_id_to_constant(const pm_constant_pool_t *pool, pm_constant_id_t
186
186
  * the constant is not found.
187
187
  */
188
188
  pm_constant_id_t
189
- pm_constant_pool_find(pm_constant_pool_t *pool, const uint8_t *start, size_t length) {
189
+ pm_constant_pool_find(const pm_constant_pool_t *pool, const uint8_t *start, size_t length) {
190
190
  assert(is_power_of_two(pool->capacity));
191
191
  const uint32_t mask = pool->capacity - 1;
192
192
 
data/src/util/pm_string.c CHANGED
@@ -65,7 +65,6 @@ pm_string_mapped_init(pm_string_t *string, const char *filepath) {
65
65
  HANDLE file = CreateFile(filepath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
66
66
 
67
67
  if (file == INVALID_HANDLE_VALUE) {
68
- perror("CreateFile failed");
69
68
  return false;
70
69
  }
71
70
 
@@ -73,7 +72,6 @@ pm_string_mapped_init(pm_string_t *string, const char *filepath) {
73
72
  DWORD file_size = GetFileSize(file, NULL);
74
73
  if (file_size == INVALID_FILE_SIZE) {
75
74
  CloseHandle(file);
76
- perror("GetFileSize failed");
77
75
  return false;
78
76
  }
79
77
 
@@ -90,7 +88,6 @@ pm_string_mapped_init(pm_string_t *string, const char *filepath) {
90
88
  HANDLE mapping = CreateFileMapping(file, NULL, PAGE_READONLY, 0, 0, NULL);
91
89
  if (mapping == NULL) {
92
90
  CloseHandle(file);
93
- perror("CreateFileMapping failed");
94
91
  return false;
95
92
  }
96
93
 
@@ -100,7 +97,6 @@ pm_string_mapped_init(pm_string_t *string, const char *filepath) {
100
97
  CloseHandle(file);
101
98
 
102
99
  if (source == NULL) {
103
- perror("MapViewOfFile failed");
104
100
  return false;
105
101
  }
106
102
 
@@ -110,7 +106,6 @@ pm_string_mapped_init(pm_string_t *string, const char *filepath) {
110
106
  // Open the file for reading
111
107
  int fd = open(filepath, O_RDONLY);
112
108
  if (fd == -1) {
113
- perror("open");
114
109
  return false;
115
110
  }
116
111
 
@@ -118,7 +113,6 @@ pm_string_mapped_init(pm_string_t *string, const char *filepath) {
118
113
  struct stat sb;
119
114
  if (fstat(fd, &sb) == -1) {
120
115
  close(fd);
121
- perror("fstat");
122
116
  return false;
123
117
  }
124
118
 
@@ -135,7 +129,6 @@ pm_string_mapped_init(pm_string_t *string, const char *filepath) {
135
129
 
136
130
  source = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
137
131
  if (source == MAP_FAILED) {
138
- perror("Map failed");
139
132
  return false;
140
133
  }
141
134
 
@@ -1,10 +1,18 @@
1
1
  #include "prism/util/pm_strpbrk.h"
2
2
 
3
3
  /**
4
- * This is the slow path that does care about the encoding.
4
+ * Add an invalid multibyte character error to the parser.
5
+ */
6
+ static inline void
7
+ pm_strpbrk_invalid_multibyte_character(pm_parser_t *parser, const uint8_t *start, const uint8_t *end) {
8
+ pm_diagnostic_list_append_format(&parser->error_list, start, end, PM_ERR_INVALID_MULTIBYTE_CHARACTER, *start);
9
+ }
10
+
11
+ /**
12
+ * This is the default path.
5
13
  */
6
14
  static inline const uint8_t *
7
- pm_strpbrk_multi_byte(const pm_parser_t *parser, const uint8_t *source, const uint8_t *charset, size_t maximum) {
15
+ pm_strpbrk_utf8(pm_parser_t *parser, const uint8_t *source, const uint8_t *charset, size_t maximum, bool validate) {
8
16
  size_t index = 0;
9
17
 
10
18
  while (index < maximum) {
@@ -12,22 +20,39 @@ pm_strpbrk_multi_byte(const pm_parser_t *parser, const uint8_t *source, const ui
12
20
  return source + index;
13
21
  }
14
22
 
15
- size_t width = parser->encoding->char_width(source + index, (ptrdiff_t) (maximum - index));
16
- if (width == 0) {
17
- return NULL;
18
- }
23
+ if (source[index] < 0x80) {
24
+ index++;
25
+ } else {
26
+ size_t width = pm_encoding_utf_8_char_width(source + index, (ptrdiff_t) (maximum - index));
19
27
 
20
- index += width;
28
+ if (width > 0) {
29
+ index += width;
30
+ } else if (!validate) {
31
+ index++;
32
+ } else {
33
+ // At this point we know we have an invalid multibyte character.
34
+ // We'll walk forward as far as we can until we find the next
35
+ // valid character so that we don't spam the user with a ton of
36
+ // the same kind of error.
37
+ const size_t start = index;
38
+
39
+ do {
40
+ index++;
41
+ } while (index < maximum && pm_encoding_utf_8_char_width(source + index, (ptrdiff_t) (maximum - index)) == 0);
42
+
43
+ pm_strpbrk_invalid_multibyte_character(parser, source + start, source + index);
44
+ }
45
+ }
21
46
  }
22
47
 
23
48
  return NULL;
24
49
  }
25
50
 
26
51
  /**
27
- * This is the fast path that does not care about the encoding.
52
+ * This is the path when the encoding is ASCII-8BIT.
28
53
  */
29
54
  static inline const uint8_t *
30
- pm_strpbrk_single_byte(const uint8_t *source, const uint8_t *charset, size_t maximum) {
55
+ pm_strpbrk_ascii_8bit(const uint8_t *source, const uint8_t *charset, size_t maximum) {
31
56
  size_t index = 0;
32
57
 
33
58
  while (index < maximum) {
@@ -41,6 +66,85 @@ pm_strpbrk_single_byte(const uint8_t *source, const uint8_t *charset, size_t max
41
66
  return NULL;
42
67
  }
43
68
 
69
+ /**
70
+ * This is the slow path that does care about the encoding.
71
+ */
72
+ static inline const uint8_t *
73
+ pm_strpbrk_multi_byte(pm_parser_t *parser, const uint8_t *source, const uint8_t *charset, size_t maximum, bool validate) {
74
+ size_t index = 0;
75
+
76
+ while (index < maximum) {
77
+ if (strchr((const char *) charset, source[index]) != NULL) {
78
+ return source + index;
79
+ }
80
+
81
+ if (source[index] < 0x80) {
82
+ index++;
83
+ } else {
84
+ size_t width = parser->encoding->char_width(source + index, (ptrdiff_t) (maximum - index));
85
+
86
+ if (width > 0) {
87
+ index += width;
88
+ } else if (!validate) {
89
+ index++;
90
+ } else {
91
+ // At this point we know we have an invalid multibyte character.
92
+ // We'll walk forward as far as we can until we find the next
93
+ // valid character so that we don't spam the user with a ton of
94
+ // the same kind of error.
95
+ const size_t start = index;
96
+
97
+ do {
98
+ index++;
99
+ } while (index < maximum && parser->encoding->char_width(source + index, (ptrdiff_t) (maximum - index)) == 0);
100
+
101
+ pm_strpbrk_invalid_multibyte_character(parser, source + start, source + index);
102
+ }
103
+ }
104
+ }
105
+
106
+ return NULL;
107
+ }
108
+
109
+ /**
110
+ * This is the fast path that does not care about the encoding because we know
111
+ * the encoding only supports single-byte characters.
112
+ */
113
+ static inline const uint8_t *
114
+ pm_strpbrk_single_byte(pm_parser_t *parser, const uint8_t *source, const uint8_t *charset, size_t maximum, bool validate) {
115
+ size_t index = 0;
116
+
117
+ while (index < maximum) {
118
+ if (strchr((const char *) charset, source[index]) != NULL) {
119
+ return source + index;
120
+ }
121
+
122
+ if (source[index] < 0x80 || !validate) {
123
+ index++;
124
+ } else {
125
+ size_t width = parser->encoding->char_width(source + index, (ptrdiff_t) (maximum - index));
126
+
127
+ if (width > 0) {
128
+ index += width;
129
+ } else {
130
+ // At this point we know we have an invalid multibyte character.
131
+ // We'll walk forward as far as we can until we find the next
132
+ // valid character so that we don't spam the user with a ton of
133
+ // the same kind of error.
134
+ const size_t start = index;
135
+
136
+ do {
137
+ index++;
138
+ } while (index < maximum && parser->encoding->char_width(source + index, (ptrdiff_t) (maximum - index)) == 0);
139
+
140
+ pm_strpbrk_invalid_multibyte_character(parser, source + start, source + index);
141
+ }
142
+ }
143
+ }
144
+
145
+ return NULL;
146
+ }
147
+
44
148
  /**
45
149
  * Here we have rolled our own version of strpbrk. The standard library strpbrk
46
150
  * has undefined behavior when the source string is not null-terminated. We want
@@ -57,16 +161,20 @@ pm_strpbrk_single_byte(const uint8_t *source, const uint8_t *charset, size_t max
57
161
  *
58
162
  * Finally, we want to support encodings wherein the charset could contain
59
163
  * characters that are trailing bytes of multi-byte characters. For example, in
60
- * Shift-JIS, the backslash character can be a trailing byte. In that case we
164
+ * Shift_JIS, the backslash character can be a trailing byte. In that case we
61
165
  * need to take a slower path and iterate one multi-byte character at a time.
62
166
  */
63
167
  const uint8_t *
64
- pm_strpbrk(const pm_parser_t *parser, const uint8_t *source, const uint8_t *charset, ptrdiff_t length) {
168
+ pm_strpbrk(pm_parser_t *parser, const uint8_t *source, const uint8_t *charset, ptrdiff_t length, bool validate) {
65
169
  if (length <= 0) {
66
170
  return NULL;
67
- } else if (parser->encoding_changed && parser->encoding->multibyte) {
68
- return pm_strpbrk_multi_byte(parser, source, charset, (size_t) length);
171
+ } else if (!parser->encoding_changed) {
172
+ return pm_strpbrk_utf8(parser, source, charset, (size_t) length, validate);
173
+ } else if (parser->encoding == PM_ENCODING_ASCII_8BIT_ENTRY) {
174
+ return pm_strpbrk_ascii_8bit(source, charset, (size_t) length);
175
+ } else if (parser->encoding->multibyte) {
176
+ return pm_strpbrk_multi_byte(parser, source, charset, (size_t) length, validate);
69
177
  } else {
70
- return pm_strpbrk_single_byte(source, charset, (size_t) length);
178
+ return pm_strpbrk_single_byte(parser, source, charset, (size_t) length, validate);
71
179
  }
72
180
  }
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: prism
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.21.0
4
+ version: 0.23.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shopify
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-02-05 00:00:00.000000000 Z
11
+ date: 2024-02-14 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description:
14
14
  email:
@@ -40,6 +40,7 @@ files:
40
40
  - docs/releasing.md
41
41
  - docs/ripper.md
42
42
  - docs/ruby_api.md
43
+ - docs/ruby_parser_translation.md
43
44
  - docs/serialization.md
44
45
  - docs/testing.md
45
46
  - ext/prism/api_node.c
@@ -88,13 +89,14 @@ files:
88
89
  - lib/prism/parse_result/comments.rb
89
90
  - lib/prism/parse_result/newlines.rb
90
91
  - lib/prism/pattern.rb
91
- - lib/prism/ripper_compat.rb
92
92
  - lib/prism/serialize.rb
93
93
  - lib/prism/translation.rb
94
94
  - lib/prism/translation/parser.rb
95
95
  - lib/prism/translation/parser/compiler.rb
96
96
  - lib/prism/translation/parser/lexer.rb
97
97
  - lib/prism/translation/parser/rubocop.rb
98
+ - lib/prism/translation/ripper.rb
99
+ - lib/prism/translation/ruby_parser.rb
98
100
  - lib/prism/visitor.rb
99
101
  - prism.gemspec
100
102
  - rbi/prism.rbi
@@ -137,7 +139,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
137
139
  requirements:
138
140
  - - ">="
139
141
  - !ruby/object:Gem::Version
140
- version: 3.0.0
142
+ version: 2.7.0
141
143
  required_rubygems_version: !ruby/object:Gem::Requirement
142
144
  requirements:
143
145
  - - ">="
@@ -1,207 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "ripper"
4
-
5
- module Prism
6
- # Note: This integration is not finished, and therefore still has many
7
- # inconsistencies with Ripper. If you'd like to help out, pull requests would
8
- # be greatly appreciated!
9
- #
10
- # This class is meant to provide a compatibility layer between prism and
11
- # Ripper. It functions by parsing the entire tree first and then walking it
12
- # and executing each of the Ripper callbacks as it goes.
13
- #
14
- # This class is going to necessarily be slower than the native Ripper API. It
15
- # is meant as a stopgap until developers migrate to using prism. It is also
16
- # meant as a test harness for the prism parser.
17
- #
18
- # To use this class, you treat `Prism::RipperCompat` effectively as you would
19
- # treat the `Ripper` class.
20
- class RipperCompat < Visitor
21
- # This class mirrors the ::Ripper::SexpBuilder subclass of ::Ripper that
22
- # returns the arrays of [type, *children].
23
- class SexpBuilder < RipperCompat
24
- private
25
-
26
- Ripper::PARSER_EVENTS.each do |event|
27
- define_method(:"on_#{event}") do |*args|
28
- [event, *args]
29
- end
30
- end
31
-
32
- Ripper::SCANNER_EVENTS.each do |event|
33
- define_method(:"on_#{event}") do |value|
34
- [:"@#{event}", value, [lineno, column]]
35
- end
36
- end
37
- end
38
-
39
- # This class mirrors the ::Ripper::SexpBuilderPP subclass of ::Ripper that
40
- # returns the same values as ::Ripper::SexpBuilder except with a couple of
41
- # niceties that flatten linked lists into arrays.
42
- class SexpBuilderPP < SexpBuilder
43
- private
44
-
45
- def _dispatch_event_new # :nodoc:
46
- []
47
- end
48
-
49
- def _dispatch_event_push(list, item) # :nodoc:
50
- list << item
51
- list
52
- end
53
-
54
- Ripper::PARSER_EVENT_TABLE.each do |event, arity|
55
- case event
56
- when /_new\z/
57
- alias_method :"on_#{event}", :_dispatch_event_new if arity == 0
58
- when /_add\z/
59
- alias_method :"on_#{event}", :_dispatch_event_push
60
- end
61
- end
62
- end
63
-
64
- # The source that is being parsed.
65
- attr_reader :source
66
-
67
- # The current line number of the parser.
68
- attr_reader :lineno
69
-
70
- # The current column number of the parser.
71
- attr_reader :column
72
-
73
- # Create a new RipperCompat object with the given source.
74
- def initialize(source)
75
- @source = source
76
- @result = nil
77
- @lineno = nil
78
- @column = nil
79
- end
80
-
81
- ############################################################################
82
- # Public interface
83
- ############################################################################
84
-
85
- # True if the parser encountered an error during parsing.
86
- def error?
87
- result.failure?
88
- end
89
-
90
- # Parse the source and return the result.
91
- def parse
92
- result.magic_comments.each do |magic_comment|
93
- on_magic_comment(magic_comment.key, magic_comment.value)
94
- end
95
-
96
- if error?
97
- result.errors.each do |error|
98
- on_parse_error(error.message)
99
- end
100
- else
101
- result.value.accept(self)
102
- end
103
- end
104
-
105
- ############################################################################
106
- # Visitor methods
107
- ############################################################################
108
-
109
- # Visit a CallNode node.
110
- def visit_call_node(node)
111
- if !node.message.match?(/^[[:alpha:]_]/) && node.opening_loc.nil? && node.arguments&.arguments&.length == 1
112
- left = visit(node.receiver)
113
- right = visit(node.arguments.arguments.first)
114
-
115
- bounds(node.location)
116
- on_binary(left, node.name, right)
117
- else
118
- raise NotImplementedError
119
- end
120
- end
121
-
122
- # Visit a FloatNode node.
123
- def visit_float_node(node)
124
- bounds(node.location)
125
- on_float(node.slice)
126
- end
127
-
128
- # Visit a ImaginaryNode node.
129
- def visit_imaginary_node(node)
130
- bounds(node.location)
131
- on_imaginary(node.slice)
132
- end
133
-
134
- # Visit an IntegerNode node.
135
- def visit_integer_node(node)
136
- bounds(node.location)
137
- on_int(node.slice)
138
- end
139
-
140
- # Visit a RationalNode node.
141
- def visit_rational_node(node)
142
- bounds(node.location)
143
- on_rational(node.slice)
144
- end
145
-
146
- # Visit a StatementsNode node.
147
- def visit_statements_node(node)
148
- bounds(node.location)
149
- node.body.inject(on_stmts_new) do |stmts, stmt|
150
- on_stmts_add(stmts, visit(stmt))
151
- end
152
- end
153
-
154
- # Visit a ProgramNode node.
155
- def visit_program_node(node)
156
- statements = visit(node.statements)
157
- bounds(node.location)
158
- on_program(statements)
159
- end
160
-
161
- ############################################################################
162
- # Entrypoints for subclasses
163
- ############################################################################
164
-
165
- # This is a convenience method that runs the SexpBuilder subclass parser.
166
- def self.sexp_raw(source)
167
- SexpBuilder.new(source).parse
168
- end
169
-
170
- # This is a convenience method that runs the SexpBuilderPP subclass parser.
171
- def self.sexp(source)
172
- SexpBuilderPP.new(source).parse
173
- end
174
-
175
- private
176
-
177
- # This method is responsible for updating lineno and column information
178
- # to reflect the current node.
179
- #
180
- # This method could be drastically improved with some caching on the start
181
- # of every line, but for now it's good enough.
182
- def bounds(location)
183
- @lineno = location.start_line
184
- @column = location.start_column
185
- end
186
-
187
- # Lazily initialize the parse result.
188
- def result
189
- @result ||= Prism.parse(source)
190
- end
191
-
192
- def _dispatch0; end # :nodoc:
193
- def _dispatch1(_); end # :nodoc:
194
- def _dispatch2(_, _); end # :nodoc:
195
- def _dispatch3(_, _, _); end # :nodoc:
196
- def _dispatch4(_, _, _, _); end # :nodoc:
197
- def _dispatch5(_, _, _, _, _); end # :nodoc:
198
- def _dispatch7(_, _, _, _, _, _, _); end # :nodoc:
199
-
200
- alias_method :on_parse_error, :_dispatch1
201
- alias_method :on_magic_comment, :_dispatch2
202
-
203
- (Ripper::SCANNER_EVENT_TABLE.merge(Ripper::PARSER_EVENT_TABLE)).each do |event, arity|
204
- alias_method :"on_#{event}", :"_dispatch#{arity}"
205
- end
206
- end
207
- end