Class: RBS::WASM::Runtime
| Relationships & Source Files | |
| Super Chains via Extension / Inclusion / Inheritance | |
|
Instance Chain:
self,
MonitorMixin
|
|
| Inherits: | Object |
| Defined in: | lib/rbs/wasm/runtime.rb |
Overview
Loads rbs_parser.wasm into a JVM WebAssembly runtime (Chicory) and drives
it. This is the JRuby counterpart of the C extension's main.c: it copies a
source string into the module's linear memory, runs the parser, and returns
the serialized result for Deserializer to rebuild.
Chicory is a pure-Java runtime, so there is no native dependency. The
.wasm ships in the gem; the Chicory jars are fetched from Maven by
jar-dependencies (see lib/rbs_jars.rb and rbs.gemspec).
Constant Summary
-
INVALID_RANGE =
# File 'lib/rbs/wasm/runtime.rb', line 23-1
-
INVALID_START_POS =
# File 'lib/rbs/wasm/runtime.rb', line 22
Statuses the parse entry points return (see rbs_wasm.c). A negative one is about the range the caller asked for rather than the source text, and comes with an empty result.
-2
-
OK =
# File 'lib/rbs/wasm/runtime.rb', line 251 -
PARSE_ERROR =
# File 'lib/rbs/wasm/runtime.rb', line 240
Class Method Summary
- .instance
- .new ⇒ Runtime constructor
- .wasm_path
Instance Method Summary
- #lex(content, encoding, end_pos)
- #parse_inline_leading_annotation(content, encoding, start_pos, end_pos, variables)
- #parse_inline_trailing_annotation(content, encoding, start_pos, end_pos, variables)
- #parse_method_type(content, encoding, start_pos, end_pos, variables, require_eof)
-
#parse_signature(content, encoding, start_pos, end_pos)
contentis the whole buffer;start_pos/end_posare the character range within it to parse. - #parse_type(content, encoding, start_pos, end_pos, variables, require_eof, void_allowed, self_allowed, classish_allowed)
- #parse_type_params(content, encoding, start_pos, end_pos, module_type_params)
- #bool(value) private
- #build_instance private
-
#i32(value)
private
A WebAssembly i32 comes back in a JVM long, so read the low 32 bits as signed: the negative statuses have to stay negative on this side.
-
#machine_factory(wasm_module)
private
Chicory's AOT compiler when its jars are present and usable, otherwise nil (the builder then uses the interpreter).
- #read_result private
-
#run(source, encoding)
private
Copies
sourceand its encoding name into linear memory, yields their pointers/lengths to the block (which invokes the parser and returns its status), then reads the result back out. -
#with_variables(variables)
private
Allocates a buffer of newline-separated variable names and yields its pointer/length.
Constructor Details
.new ⇒ Runtime
# File 'lib/rbs/wasm/runtime.rb', line 37
def initialize super() # rbs_jars.rb require_jars the Chicory/ASM jars from the local Maven # repository (~/.m2), where jar-dependencies puts them at gem install (or # `rake wasm:install_jars` when running from source). require "rbs_jars" @wasm = build_instance @memory = @wasm.memory @alloc = @wasm.export("rbs_wasm_alloc") @free = @wasm.export("rbs_wasm_free") @result_ptr = @wasm.export("rbs_wasm_result_ptr") @result_len = @wasm.export("rbs_wasm_result_len") @parse_signature = @wasm.export("rbs_wasm_parse_signature") @parse_type = @wasm.export("rbs_wasm_parse_type") @parse_method_type = @wasm.export("rbs_wasm_parse_method_type") @parse_type_params = @wasm.export("rbs_wasm_parse_type_params") @parse_inline_leading_annotation = @wasm.export("rbs_wasm_parse_inline_leading_annotation") @parse_inline_trailing_annotation = @wasm.export("rbs_wasm_parse_inline_trailing_annotation") @lex = @wasm.export("rbs_wasm_lex") end
Class Method Details
.instance
[ GitHub ]# File 'lib/rbs/wasm/runtime.rb', line 28
def instance @instance ||= new end
.wasm_path
[ GitHub ]# File 'lib/rbs/wasm/runtime.rb', line 32
def wasm_path ENV["RBS_WASM_PARSER"] || File.("rbs_parser.wasm", __dir__) end
Instance Method Details
#bool(value) (private)
[ GitHub ]# File 'lib/rbs/wasm/runtime.rb', line 174
def bool(value) value ? 1 : 0 end
#build_instance (private)
[ GitHub ]# File 'lib/rbs/wasm/runtime.rb', line 178
def build_instance parser = Java::ComDylibsoChicoryWasm::Parser instance_class = Java::ComDylibsoChicoryRuntime::Instance import_values = Java::ComDylibsoChicoryRuntime::ImportValues wasi_preview1 = Java::ComDylibsoChicoryWasi::WasiPreview1 = Java::ComDylibsoChicoryWasi::WasiOptions wasm_module = parser.parse(java.io.File.new(self.class.wasm_path)) wasi = wasi_preview1.builder.(.builder.build).build imports = import_values.builder.add_function(wasi.to_host_functions).build builder = instance_class.builder(wasm_module).with_import_values(imports) if (factory = machine_factory(wasm_module)) builder = builder.with_machine_factory(factory) end wasm = builder.build wasm.export("_initialize").apply wasm end
#i32(value) (private)
A WebAssembly i32 comes back in a JVM long, so read the low 32 bits as signed: the negative statuses have to stay negative on this side.
# File 'lib/rbs/wasm/runtime.rb', line 169
def i32(value) value &= 0xFFFF_FFFF value >= 0x8000_0000 ? value - 0x1_0000_0000 : value end
#lex(content, encoding, end_pos)
[ GitHub ]# File 'lib/rbs/wasm/runtime.rb', line 105
def lex(content, encoding, end_pos) run(content, encoding) do |ptr, len, enc_ptr, enc_len| @lex.apply(ptr, len, enc_ptr, enc_len, end_pos)[0] end end
#machine_factory(wasm_module) (private)
Chicory's AOT compiler when its jars are present and usable, otherwise nil (the builder then uses the interpreter). NameError covers a missing compiler class; LinkageError covers an incompatible/missing ASM (so a bad jar set degrades to the interpreter instead of crashing).
# File 'lib/rbs/wasm/runtime.rb', line 203
def machine_factory(wasm_module) Java::ComDylibsoChicoryCompiler::MachineFactoryCompiler.compile(wasm_module) rescue NameError, Java::JavaLang::LinkageError nil end
#parse_inline_leading_annotation(content, encoding, start_pos, end_pos, variables)
[ GitHub ]# File 'lib/rbs/wasm/runtime.rb', line 89
def parse_inline_leading_annotation(content, encoding, start_pos, end_pos, variables) with_variables(variables) do |vars_ptr, vars_len| run(content, encoding) do |ptr, len, enc_ptr, enc_len| @parse_inline_leading_annotation.apply(ptr, len, enc_ptr, enc_len, start_pos, end_pos, vars_ptr, vars_len)[0] end end end
#parse_inline_trailing_annotation(content, encoding, start_pos, end_pos, variables)
[ GitHub ]# File 'lib/rbs/wasm/runtime.rb', line 97
def parse_inline_trailing_annotation(content, encoding, start_pos, end_pos, variables) with_variables(variables) do |vars_ptr, vars_len| run(content, encoding) do |ptr, len, enc_ptr, enc_len| @parse_inline_trailing_annotation.apply(ptr, len, enc_ptr, enc_len, start_pos, end_pos, vars_ptr, vars_len)[0] end end end
#parse_method_type(content, encoding, start_pos, end_pos, variables, require_eof)
[ GitHub ]# File 'lib/rbs/wasm/runtime.rb', line 75
def parse_method_type(content, encoding, start_pos, end_pos, variables, require_eof) with_variables(variables) do |vars_ptr, vars_len| run(content, encoding) do |ptr, len, enc_ptr, enc_len| @parse_method_type.apply(ptr, len, enc_ptr, enc_len, start_pos, end_pos, vars_ptr, vars_len, bool(require_eof))[0] end end end
#parse_signature(content, encoding, start_pos, end_pos)
content is the whole buffer; start_pos/end_pos are the character
range within it to parse. Each method returns [status, bytes]: with OK
bytes is the serialized ::RBS::AST, with PARSE_ERROR it is the error blob (see
set_error_result in rbs_wasm.c), and with a negative status it is empty.
# File 'lib/rbs/wasm/runtime.rb', line 63
def parse_signature(content, encoding, start_pos, end_pos) run(content, encoding) { |ptr, len, enc_ptr, enc_len| @parse_signature.apply(ptr, len, enc_ptr, enc_len, start_pos, end_pos)[0] } end
#parse_type(content, encoding, start_pos, end_pos, variables, require_eof, void_allowed, self_allowed, classish_allowed)
[ GitHub ]# File 'lib/rbs/wasm/runtime.rb', line 67
def parse_type(content, encoding, start_pos, end_pos, variables, require_eof, void_allowed, self_allowed, classish_allowed) with_variables(variables) do |vars_ptr, vars_len| run(content, encoding) do |ptr, len, enc_ptr, enc_len| @parse_type.apply(ptr, len, enc_ptr, enc_len, start_pos, end_pos, vars_ptr, vars_len, bool(require_eof), bool(void_allowed), bool(self_allowed), bool(classish_allowed))[0] end end end
#parse_type_params(content, encoding, start_pos, end_pos, module_type_params)
[ GitHub ]#read_result (private)
[ GitHub ]# File 'lib/rbs/wasm/runtime.rb', line 137
def read_result pointer = @result_ptr.apply[0] length = @result_len.apply[0] return "".b if length.zero? String.from_java_bytes(@memory.read_bytes(pointer, length)).b end
#run(source, encoding) (private)
Copies source and its encoding name into linear memory, yields their
pointers/lengths to the block (which invokes the parser and returns its
status), then reads the result back out. Serialized through the monitor
because the module keeps its result in a single shared location.
# File 'lib/rbs/wasm/runtime.rb', line 117
def run(source, encoding) synchronize do bytes = source.b length = bytes.bytesize name = encoding.to_s.b name_length = name.bytesize source_ptr = @alloc.apply(length)[0] name_ptr = @alloc.apply(name_length)[0] begin @memory.write(source_ptr, bytes.to_java_bytes) @memory.write(name_ptr, name.to_java_bytes) unless name_length.zero? status = yield(source_ptr, length, name_ptr, name_length) [i32(status), read_result] ensure @free.apply(source_ptr) @free.apply(name_ptr) end end end
#with_variables(variables) (private)
Allocates a buffer of newline-separated variable names and yields its
pointer/length. A nil variables is passed as length -1 ("no variables").
# File 'lib/rbs/wasm/runtime.rb', line 147
def with_variables(variables) names = variables&.map(&:to_s)&.join("\n") if names.nil? || names.empty? return yield(0, variables.nil? ? -1 : 0) end bytes = names.b length = bytes.bytesize synchronize do pointer = @alloc.apply(length)[0] begin @memory.write(pointer, bytes.to_java_bytes) yield(pointer, length) ensure @free.apply(pointer) end end end