rbs 4.1.3-java → 4.2.0.pre.1-java

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 (59) hide show
  1. checksums.yaml +4 -4
  2. data/.gitattributes +1 -0
  3. data/.github/workflows/bundle-update.yml +2 -2
  4. data/.github/workflows/c-check.yml +2 -2
  5. data/.github/workflows/changelog.yml +121 -0
  6. data/.github/workflows/comments.yml +2 -2
  7. data/.github/workflows/dependabot.yml +1 -1
  8. data/.github/workflows/jruby.yml +3 -3
  9. data/.github/workflows/release-gems.yml +6 -7
  10. data/.github/workflows/ruby.yml +6 -6
  11. data/.github/workflows/rust.yml +12 -12
  12. data/.github/workflows/truffleruby.yml +2 -2
  13. data/.github/workflows/typecheck.yml +2 -2
  14. data/.github/workflows/wasm.yml +3 -3
  15. data/.github/workflows/windows.yml +2 -2
  16. data/CHANGELOG.md +27 -0
  17. data/Rakefile +50 -21
  18. data/config.yml +5 -0
  19. data/docs/CONTRIBUTING.md +1 -1
  20. data/docs/release.md +57 -1
  21. data/docs/stdlib.md +8 -0
  22. data/docs/syntax.md +12 -0
  23. data/ext/rbs_extension/ast_translation.c +15 -0
  24. data/ext/rbs_extension/class_constants.c +2 -0
  25. data/ext/rbs_extension/class_constants.h +1 -0
  26. data/ext/rbs_extension/main.c +55 -19
  27. data/include/rbs/ast.h +21 -13
  28. data/include/rbs/defines.h +2 -2
  29. data/include/rbs/lexer.h +13 -12
  30. data/include/rbs/parser.h +37 -3
  31. data/lib/rbs/ast/declarations.rb +1 -1
  32. data/lib/rbs/ast/type_param.rb +1 -1
  33. data/lib/rbs/definition_builder/ancestor_builder.rb +8 -5
  34. data/lib/rbs/definition_builder/method_builder.rb +4 -4
  35. data/lib/rbs/definition_builder.rb +5 -2
  36. data/lib/rbs/environment/class_entry.rb +12 -0
  37. data/lib/rbs/environment/module_entry.rb +26 -1
  38. data/lib/rbs/parser_aux.rb +2 -2
  39. data/lib/rbs/types.rb +44 -2
  40. data/lib/rbs/version.rb +1 -1
  41. data/lib/rbs/wasm/parser.rb +62 -25
  42. data/lib/rbs/wasm/rbs_parser.wasm +0 -0
  43. data/lib/rbs/wasm/runtime.rb +19 -4
  44. data/lib/rbs/wasm/serialization_schema.rb +3 -2
  45. data/schema/function.json +12 -1
  46. data/sig/environment/class_entry.rbs +6 -0
  47. data/sig/environment/module_entry.rbs +15 -0
  48. data/sig/parser.rbs +4 -4
  49. data/sig/types.rbs +11 -1
  50. data/src/ast.c +17 -1
  51. data/src/lexer.c +1562 -1560
  52. data/src/lexer.re +50 -18
  53. data/src/lexstate.c +2 -1
  54. data/src/parser.c +130 -70
  55. data/src/serialize.c +20 -13
  56. data/src/util/rbs_encoding.c +195 -49
  57. data/wasm/README.md +20 -4
  58. data/wasm/rbs_wasm.c +93 -37
  59. metadata +3 -1
data/wasm/README.md CHANGED
@@ -27,6 +27,11 @@ $ rake wasm:install_jars # download the Chicory/ASM jars into ~/.m2 (run on JRub
27
27
 
28
28
  The compiled `rbs_parser.wasm` is a build artifact and is not checked in.
29
29
 
30
+ Like the MRI extension, the module is compiled with `-DNDEBUG`, which removes the
31
+ `RBS_ASSERT` checks — they sit in the lexer and the constant pool, so leaving them
32
+ in costs around 20% of parse time. `DEBUG=1 rake wasm:build` keeps them, which is
33
+ what you want when debugging the parser itself through the module.
34
+
30
35
  The WASI SDK is needed for the *build*, not for running the result — the host clang already
31
36
  knows the `wasm32` target, but there is no wasm32 libc on a normal machine, so it picks up the
32
37
  host headers and fails on the first `#include`. That is what the SDK supplies, along with the
@@ -76,10 +81,21 @@ Memory management and results:
76
81
 
77
82
  Parsing — each takes the whole buffer (`ptr`/`len`), its Ruby encoding name
78
83
  (`enc`/`enc_len`, e.g. `"UTF-8"` or `"EUC-JP"`; falls back to UTF-8 when empty or
79
- unknown), and the character range to parse (`start`/`end`). Each returns `1` on
80
- success or `0` on a parse error. On success the result is the serialized AST; on
81
- error it is an error blob (start/end positions, syntax flag, token type,
82
- message). Type/method-type parsing also takes a buffer of newline-separated
84
+ unknown), and the character range to parse (`start`/`end`). Each returns:
85
+
86
+ | Status | Meaning | Result |
87
+ | --- | --- | --- |
88
+ | `1` | Parsed. | The serialized AST. |
89
+ | `0` | Parse error. | An error blob (start/end positions, syntax flag, token type, message). |
90
+ | `-1` | Negative or reversed range. | Empty. |
91
+ | `-2` | `start` is a byte position no character starts at — inside a character, or past the end of the buffer. | Empty. |
92
+
93
+ An `end` past the end of the buffer is not an error: it is clamped to the
94
+ buffer, which is where lexing stops anyway. The two negative statuses are about
95
+ the range the caller asked for rather than the source text, and `RBS::Parser`
96
+ turns both into an `ArgumentError`, as the C extension does.
97
+
98
+ Type/method-type parsing also takes a buffer of newline-separated
83
99
  type-variable names (`vars`/`vars_len`, with `vars_len < 0` meaning "none"):
84
100
 
85
101
  | Export | Signature |
data/wasm/rbs_wasm.c CHANGED
@@ -29,6 +29,19 @@
29
29
  #include "rbs/util/rbs_buffer.h"
30
30
  #include "rbs/util/rbs_encoding.h"
31
31
 
32
+ // Status returned by the parse entry points.
33
+ //
34
+ // A negative status is about the range the caller asked for rather than the
35
+ // source text, and leaves the result empty: `RBS_WASM_INVALID_START_POS` is
36
+ // the `NULL` `rbs_parser_new` returns for a byte position no character starts
37
+ // at -- inside a character, or past the end of the buffer. `RBS::Parser`
38
+ // raises ArgumentError for both, as the C extension does for the same `NULL`
39
+ // (main.c).
40
+ #define RBS_WASM_INVALID_START_POS (-2)
41
+ #define RBS_WASM_INVALID_RANGE (-1)
42
+ #define RBS_WASM_PARSE_ERROR 0
43
+ #define RBS_WASM_OK 1
44
+
32
45
  // The result of the most recent parse, living in linear memory until the next
33
46
  // call replaces it. WebAssembly is little-endian, so the multi-byte integers
34
47
  // written below match the little-endian format the Ruby decoder expects.
@@ -82,7 +95,7 @@ rbs_wasm_result_len(void) {
82
95
  // [i32 start_char][i32 end_char][u8 syntax_error]
83
96
  // [u32 token_type_len][token_type bytes][u32 message_len][message bytes]
84
97
  //
85
- // Always returns 0, the failure status for the parse functions.
98
+ // Always returns RBS_WASM_PARSE_ERROR, the failure status for the parse functions.
86
99
  static int set_error_result(rbs_parser_t *parser) {
87
100
  rbs_error_t *error = parser->error;
88
101
  const char *token_type = rbs_token_type_str(error->token.type);
@@ -110,21 +123,30 @@ static int set_error_result(rbs_parser_t *parser) {
110
123
  p += 4;
111
124
  memcpy(p, message, message_len);
112
125
 
113
- return 0;
126
+ return RBS_WASM_PARSE_ERROR;
114
127
  }
115
128
 
116
129
  static int set_serialized_result(rbs_parser_t *parser, rbs_node_t *node) {
117
130
  rbs_string_t bytes = rbs_serialize_node(parser->allocator, &parser->constant_pool, node);
118
131
  size_t length = rbs_string_len(bytes);
119
132
  memcpy(allocate_result(length), bytes.start, length);
120
- return 1;
133
+ return RBS_WASM_OK;
121
134
  }
122
135
 
123
- // A reversed or out-of-bounds range would make the lexer loop forever, which
124
- // would hang the whole host. Hosts are expected to validate too (RBS::Parser
125
- // raises on bad ranges), but guard here so a stray caller can never wedge the VM.
126
- static bool range_is_valid(int start_pos, int end_pos, int length) {
127
- return start_pos >= 0 && end_pos >= 0 && start_pos <= end_pos && end_pos <= length;
136
+ // A negative or reversed range is the caller's mistake rather than anything
137
+ // about the source text. Hosts are expected to reject it too (RBS::Parser
138
+ // raises on bad ranges), but the ABI is public, so check here as well.
139
+ static bool range_is_valid(int start_pos, int end_pos) {
140
+ return start_pos >= 0 && end_pos >= 0 && start_pos <= end_pos;
141
+ }
142
+
143
+ // An `end_pos` past the end of the buffer is how a caller clamps without
144
+ // measuring, and the lexer stops at the end of the input on its own -- but it
145
+ // only recognises the end where it can read a NUL. The C extension has the
146
+ // Ruby string's terminator for that; a buffer the host wrote into linear
147
+ // memory has nothing behind it, so its size is where lexing has to stop.
148
+ static int clamp_end_pos(int end_pos, int length) {
149
+ return end_pos < length ? end_pos : length;
128
150
  }
129
151
 
130
152
  // Resolve a Ruby encoding name (e.g. "UTF-8", "EUC-JP") to an rbs encoding,
@@ -171,17 +193,22 @@ static void declare_variables(rbs_parser_t *parser, const char *variables, int v
171
193
  * to parse, so reported locations are absolute (this mirrors
172
194
  * RBS::Parser._parse_signature).
173
195
  *
174
- * @return 1 on success (result is the serialized AST), 0 on a parse error
175
- * (result is an error blob).
196
+ * @return RBS_WASM_OK on success (result is the serialized AST),
197
+ * RBS_WASM_PARSE_ERROR on a parse error (result is an error blob), or
198
+ * a negative status for a range the parser will not take.
176
199
  */
177
200
  __attribute__((export_name("rbs_wasm_parse_signature"))) int rbs_wasm_parse_signature(const char *source, int length, const char *encoding, int encoding_length, int start_pos, int end_pos) {
178
- if (!range_is_valid(start_pos, end_pos, length)) {
201
+ if (!range_is_valid(start_pos, end_pos)) {
179
202
  allocate_result(0);
180
- return 0;
203
+ return RBS_WASM_INVALID_RANGE;
181
204
  }
182
205
 
183
206
  rbs_string_t string = rbs_string_new(source, source + length);
184
- rbs_parser_t *parser = rbs_parser_new(string, resolve_encoding(encoding, encoding_length), start_pos, end_pos);
207
+ rbs_parser_t *parser = rbs_parser_new(string, resolve_encoding(encoding, encoding_length), start_pos, clamp_end_pos(end_pos, length));
208
+ if (parser == NULL) {
209
+ allocate_result(0);
210
+ return RBS_WASM_INVALID_START_POS;
211
+ }
185
212
 
186
213
  rbs_signature_t *signature = NULL;
187
214
  rbs_parse_signature(parser, &signature);
@@ -201,23 +228,30 @@ __attribute__((export_name("rbs_wasm_parse_signature"))) int rbs_wasm_parse_sign
201
228
  * Parse a single RBS type.
202
229
  *
203
230
  * @param variables Newline-separated type variable names (length < 0 for none).
204
- * @return 1 on success, 0 on a parse error. On success with an empty result
205
- * (`rbs_wasm_result_len` == 0), the input was empty (`nil`).
231
+ * @return RBS_WASM_OK on success, RBS_WASM_PARSE_ERROR on a parse error, or a
232
+ * negative status for a range the parser will not take. On success
233
+ * with an empty result (`rbs_wasm_result_len` == 0), the input was
234
+ * empty (`nil`).
206
235
  */
207
236
  __attribute__((export_name("rbs_wasm_parse_type"))) int rbs_wasm_parse_type(const char *source, int length, const char *encoding, int encoding_length, int start_pos, int end_pos, const char *variables, int variables_length, int require_eof, int void_allowed, int self_allowed, int classish_allowed) {
208
- if (!range_is_valid(start_pos, end_pos, length)) {
237
+ if (!range_is_valid(start_pos, end_pos)) {
209
238
  allocate_result(0);
210
- return 0;
239
+ return RBS_WASM_INVALID_RANGE;
211
240
  }
212
241
 
213
242
  rbs_string_t string = rbs_string_new(source, source + length);
214
- rbs_parser_t *parser = rbs_parser_new(string, resolve_encoding(encoding, encoding_length), start_pos, end_pos);
243
+ rbs_parser_t *parser = rbs_parser_new(string, resolve_encoding(encoding, encoding_length), start_pos, clamp_end_pos(end_pos, length));
244
+ if (parser == NULL) {
245
+ allocate_result(0);
246
+ return RBS_WASM_INVALID_START_POS;
247
+ }
248
+
215
249
  declare_variables(parser, variables, variables_length);
216
250
 
217
251
  int status;
218
252
  if (parser->next_token.type == pEOF) {
219
253
  allocate_result(0);
220
- status = 1;
254
+ status = RBS_WASM_OK;
221
255
  } else {
222
256
  rbs_node_t *type = NULL;
223
257
  rbs_parse_type(parser, &type, void_allowed != 0, self_allowed != 0, classish_allowed != 0);
@@ -240,23 +274,29 @@ __attribute__((export_name("rbs_wasm_parse_type"))) int rbs_wasm_parse_type(cons
240
274
  * Parse a single RBS method type.
241
275
  *
242
276
  * @param variables Newline-separated type variable names (length < 0 for none).
243
- * @return 1 on success, 0 on a parse error. On success with an empty result,
244
- * the input was empty (`nil`).
277
+ * @return RBS_WASM_OK on success, RBS_WASM_PARSE_ERROR on a parse error, or a
278
+ * negative status for a range the parser will not take. On success
279
+ * with an empty result, the input was empty (`nil`).
245
280
  */
246
281
  __attribute__((export_name("rbs_wasm_parse_method_type"))) int rbs_wasm_parse_method_type(const char *source, int length, const char *encoding, int encoding_length, int start_pos, int end_pos, const char *variables, int variables_length, int require_eof) {
247
- if (!range_is_valid(start_pos, end_pos, length)) {
282
+ if (!range_is_valid(start_pos, end_pos)) {
248
283
  allocate_result(0);
249
- return 0;
284
+ return RBS_WASM_INVALID_RANGE;
250
285
  }
251
286
 
252
287
  rbs_string_t string = rbs_string_new(source, source + length);
253
- rbs_parser_t *parser = rbs_parser_new(string, resolve_encoding(encoding, encoding_length), start_pos, end_pos);
288
+ rbs_parser_t *parser = rbs_parser_new(string, resolve_encoding(encoding, encoding_length), start_pos, clamp_end_pos(end_pos, length));
289
+ if (parser == NULL) {
290
+ allocate_result(0);
291
+ return RBS_WASM_INVALID_START_POS;
292
+ }
293
+
254
294
  declare_variables(parser, variables, variables_length);
255
295
 
256
296
  int status;
257
297
  if (parser->next_token.type == pEOF) {
258
298
  allocate_result(0);
259
- status = 1;
299
+ status = RBS_WASM_OK;
260
300
  } else {
261
301
  rbs_method_type_t *method_type = NULL;
262
302
  rbs_parse_method_type(parser, &method_type, require_eof != 0, true);
@@ -273,18 +313,22 @@ __attribute__((export_name("rbs_wasm_parse_method_type"))) int rbs_wasm_parse_me
273
313
  * is a serialized node list; an empty result means the input was empty (`nil`).
274
314
  */
275
315
  __attribute__((export_name("rbs_wasm_parse_type_params"))) int rbs_wasm_parse_type_params(const char *source, int length, const char *encoding, int encoding_length, int start_pos, int end_pos, int module_type_params) {
276
- if (!range_is_valid(start_pos, end_pos, length)) {
316
+ if (!range_is_valid(start_pos, end_pos)) {
277
317
  allocate_result(0);
278
- return 0;
318
+ return RBS_WASM_INVALID_RANGE;
279
319
  }
280
320
 
281
321
  rbs_string_t string = rbs_string_new(source, source + length);
282
- rbs_parser_t *parser = rbs_parser_new(string, resolve_encoding(encoding, encoding_length), start_pos, end_pos);
322
+ rbs_parser_t *parser = rbs_parser_new(string, resolve_encoding(encoding, encoding_length), start_pos, clamp_end_pos(end_pos, length));
323
+ if (parser == NULL) {
324
+ allocate_result(0);
325
+ return RBS_WASM_INVALID_START_POS;
326
+ }
283
327
 
284
328
  int status;
285
329
  if (parser->next_token.type == pEOF) {
286
330
  allocate_result(0);
287
- status = 1;
331
+ status = RBS_WASM_OK;
288
332
  } else {
289
333
  rbs_node_list_t *params = NULL;
290
334
  rbs_parse_type_params(parser, module_type_params != 0, &params);
@@ -293,7 +337,7 @@ __attribute__((export_name("rbs_wasm_parse_type_params"))) int rbs_wasm_parse_ty
293
337
  rbs_string_t bytes = rbs_serialize_node_list(parser->allocator, &parser->constant_pool, params);
294
338
  size_t n = rbs_string_len(bytes);
295
339
  memcpy(allocate_result(n), bytes.start, n);
296
- status = 1;
340
+ status = RBS_WASM_OK;
297
341
  } else {
298
342
  status = set_error_result(parser);
299
343
  }
@@ -305,13 +349,18 @@ __attribute__((export_name("rbs_wasm_parse_type_params"))) int rbs_wasm_parse_ty
305
349
 
306
350
  // Shared body for the leading/trailing inline annotation parsers.
307
351
  static int parse_inline_annotation(const char *source, int length, const char *encoding, int encoding_length, int start_pos, int end_pos, const char *variables, int variables_length, bool leading) {
308
- if (!range_is_valid(start_pos, end_pos, length)) {
352
+ if (!range_is_valid(start_pos, end_pos)) {
309
353
  allocate_result(0);
310
- return 0;
354
+ return RBS_WASM_INVALID_RANGE;
311
355
  }
312
356
 
313
357
  rbs_string_t string = rbs_string_new(source, source + length);
314
- rbs_parser_t *parser = rbs_parser_new(string, resolve_encoding(encoding, encoding_length), start_pos, end_pos);
358
+ rbs_parser_t *parser = rbs_parser_new(string, resolve_encoding(encoding, encoding_length), start_pos, clamp_end_pos(end_pos, length));
359
+ if (parser == NULL) {
360
+ allocate_result(0);
361
+ return RBS_WASM_INVALID_START_POS;
362
+ }
363
+
315
364
  declare_variables(parser, variables, variables_length);
316
365
 
317
366
  rbs_ast_ruby_annotations_t *annotation = NULL;
@@ -322,7 +371,7 @@ static int parse_inline_annotation(const char *source, int length, const char *e
322
371
  status = set_error_result(parser);
323
372
  } else if (!success || annotation == NULL) {
324
373
  allocate_result(0);
325
- status = 1;
374
+ status = RBS_WASM_OK;
326
375
  } else {
327
376
  status = set_serialized_result(parser, (rbs_node_t *) annotation);
328
377
  }
@@ -364,11 +413,18 @@ static void w_lex_u32(rbs_allocator_t *allocator, rbs_buffer_t *buffer, uint32_t
364
413
  *
365
414
  * The final token is always pEOF, mirroring RBS::Parser._lex.
366
415
  *
367
- * @return 1 always (lexing does not report parse errors here).
416
+ * @return RBS_WASM_OK, or RBS_WASM_INVALID_RANGE for a negative `end_pos`
417
+ * (lexing does not report parse errors here).
368
418
  */
369
419
  __attribute__((export_name("rbs_wasm_lex"))) int rbs_wasm_lex(const char *source, int length, const char *encoding, int encoding_length, int end_pos) {
420
+ if (!range_is_valid(0, end_pos)) {
421
+ allocate_result(0);
422
+ return RBS_WASM_INVALID_RANGE;
423
+ }
424
+
370
425
  rbs_allocator_t *allocator = rbs_allocator_init();
371
- rbs_lexer_t *lexer = rbs_lexer_new(allocator, rbs_string_new(source, source + length), resolve_encoding(encoding, encoding_length), 0, end_pos);
426
+ // Byte 0 is always a position the lexer can start on, so this is never NULL.
427
+ rbs_lexer_t *lexer = rbs_lexer_new(allocator, rbs_string_new(source, source + length), resolve_encoding(encoding, encoding_length), 0, clamp_end_pos(end_pos, length));
372
428
 
373
429
  rbs_buffer_t buffer;
374
430
  rbs_buffer_init(allocator, &buffer);
@@ -390,7 +446,7 @@ __attribute__((export_name("rbs_wasm_lex"))) int rbs_wasm_lex(const char *source
390
446
  memcpy(allocate_result(n), bytes.start, n);
391
447
 
392
448
  rbs_allocator_free(allocator);
393
- return 1;
449
+ return RBS_WASM_OK;
394
450
  }
395
451
 
396
452
  /**
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rbs
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.1.3
4
+ version: 4.2.0.pre.1
5
5
  platform: java
6
6
  authors:
7
7
  - Soutaro Matsumoto
@@ -77,9 +77,11 @@ files:
77
77
  - ".clang-format"
78
78
  - ".clangd"
79
79
  - ".dockerignore"
80
+ - ".gitattributes"
80
81
  - ".github/dependabot.yml"
81
82
  - ".github/workflows/bundle-update.yml"
82
83
  - ".github/workflows/c-check.yml"
84
+ - ".github/workflows/changelog.yml"
83
85
  - ".github/workflows/comments.yml"
84
86
  - ".github/workflows/dependabot.yml"
85
87
  - ".github/workflows/jruby.yml"