sweet-moon 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +2 -0
  3. data/.rspec +1 -0
  4. data/.rubocop.yml +40 -0
  5. data/Gemfile +12 -0
  6. data/Gemfile.lock +61 -0
  7. data/README.md +1149 -0
  8. data/components/api.rb +83 -0
  9. data/components/injections/injections_503.rb +21 -0
  10. data/components/injections/injections_514.rb +29 -0
  11. data/components/injections/injections_542.rb +49 -0
  12. data/components/injections.rb +11 -0
  13. data/components/interpreters/50/function.rb +52 -0
  14. data/components/interpreters/50/interpreter.rb +105 -0
  15. data/components/interpreters/50/reader.rb +65 -0
  16. data/components/interpreters/50/table.rb +99 -0
  17. data/components/interpreters/50/writer.rb +45 -0
  18. data/components/interpreters/51/function.rb +52 -0
  19. data/components/interpreters/51/interpreter.rb +104 -0
  20. data/components/interpreters/51/reader.rb +65 -0
  21. data/components/interpreters/51/table.rb +60 -0
  22. data/components/interpreters/51/writer.rb +45 -0
  23. data/components/interpreters/54/function.rb +52 -0
  24. data/components/interpreters/54/interpreter.rb +100 -0
  25. data/components/interpreters/54/reader.rb +65 -0
  26. data/components/interpreters/54/table.rb +60 -0
  27. data/components/interpreters/54/writer.rb +45 -0
  28. data/components/interpreters.rb +11 -0
  29. data/components/io.rb +11 -0
  30. data/config/tests.sample.yml +15 -0
  31. data/controllers/api.rb +143 -0
  32. data/controllers/cli/cli.rb +32 -0
  33. data/controllers/cli/help.rb +24 -0
  34. data/controllers/cli/signatures.rb +179 -0
  35. data/controllers/cli/version.rb +14 -0
  36. data/controllers/interpreter.rb +68 -0
  37. data/controllers/state.rb +74 -0
  38. data/dsl/api.rb +31 -0
  39. data/dsl/cache.rb +118 -0
  40. data/dsl/concerns/fennel.rb +13 -0
  41. data/dsl/concerns/packages.rb +37 -0
  42. data/dsl/errors.rb +28 -0
  43. data/dsl/fennel.rb +47 -0
  44. data/dsl/global.rb +42 -0
  45. data/dsl/state.rb +104 -0
  46. data/dsl/sweet_moon.rb +53 -0
  47. data/logic/api.rb +17 -0
  48. data/logic/interpreter.rb +84 -0
  49. data/logic/interpreters/interpreter_50.rb +34 -0
  50. data/logic/interpreters/interpreter_51.rb +37 -0
  51. data/logic/interpreters/interpreter_54.rb +41 -0
  52. data/logic/io.rb +6 -0
  53. data/logic/options.rb +14 -0
  54. data/logic/shared_object.rb +52 -0
  55. data/logic/signature.rb +258 -0
  56. data/logic/signatures/ffi_types.rb +27 -0
  57. data/logic/signatures/signatures_322.rb +418 -0
  58. data/logic/signatures/signatures_401.rb +243 -0
  59. data/logic/signatures/signatures_503.rb +575 -0
  60. data/logic/signatures/signatures_514.rb +460 -0
  61. data/logic/signatures/signatures_542.rb +591 -0
  62. data/logic/spec.rb +13 -0
  63. data/logic/tables.rb +32 -0
  64. data/ports/in/dsl/sweet-moon/errors.rb +3 -0
  65. data/ports/in/dsl/sweet-moon.rb +1 -0
  66. data/ports/in/shell/sweet-moon +5 -0
  67. data/ports/in/shell.rb +21 -0
  68. data/ports/out/shell.rb +9 -0
  69. data/sweet-moon.gemspec +35 -0
  70. metadata +137 -0
@@ -0,0 +1,37 @@
1
+ require_relative '../errors'
2
+
3
+ module DSL
4
+ module Concerns
5
+ module Packages
6
+ def add_package_path(path, target = 'package.path')
7
+ _ensure_min_version!(target, '5.1', '2')
8
+
9
+ paths = path
10
+ paths = [path] unless paths.is_a? Array
11
+
12
+ self.eval("#{target} = #{target} .. \";#{paths.join(';')}\"")
13
+ end
14
+
15
+ def package_path(target = 'package.path')
16
+ _ensure_min_version!(target, '5.1', '2')
17
+ self.eval("return #{target}").split(';')
18
+ end
19
+
20
+ def add_package_cpath(path)
21
+ add_package_path(path, 'package.cpath')
22
+ end
23
+
24
+ def package_cpath
25
+ package_path('package.cpath')
26
+ end
27
+
28
+ def require_module(module_name)
29
+ require_module_as(module_name, module_name)
30
+ end
31
+
32
+ def require_module_as(module_name, variable)
33
+ self.eval("#{variable} = require \"#{module_name}\"")
34
+ end
35
+ end
36
+ end
37
+ end
data/dsl/errors.rb ADDED
@@ -0,0 +1,28 @@
1
+ module SweetMoon
2
+ module Errors
3
+ class SweetMoonError < StandardError; end
4
+ class LuaError < SweetMoonError; end
5
+
6
+ class LuaRuntimeError < LuaError; end
7
+ class LuaMemoryAllocationError < LuaError; end
8
+ class LuaMessageHandlerError < LuaError; end
9
+ class LuaSyntaxError < LuaError; end
10
+ class LuaFileError < LuaError; end
11
+
12
+ module SweetMoonErrorHelper
13
+ def for(status)
14
+ case status
15
+ when :runtime then LuaRuntimeError
16
+ when :memory_allocation then LuaMemoryAllocationError
17
+ when :message_handler then LuaMessageHandlerError
18
+ when :syntax then LuaSyntaxError
19
+ when :file then LuaFileError
20
+ else
21
+ LuaError
22
+ end
23
+ end
24
+
25
+ module_function :for
26
+ end
27
+ end
28
+ end
data/dsl/fennel.rb ADDED
@@ -0,0 +1,47 @@
1
+ module DSL
2
+ class Fennel
3
+ attr_reader :meta
4
+
5
+ def initialize(state)
6
+ @state = state
7
+
8
+ @state.require_module(:fennel)
9
+
10
+ @state.eval(
11
+ 'table.insert(package.loaders or package.searchers, fennel.searcher)'
12
+ )
13
+
14
+ @eval = @state.get(:fennel, :eval)
15
+ @dofile = @state.get(:fennel, :dofile)
16
+ @version = @state.get(:fennel, :version)
17
+
18
+ build_meta
19
+ end
20
+
21
+ def eval(input, outputs = 1)
22
+ @eval.([input], outputs)
23
+ end
24
+
25
+ def load(path, outputs = 1)
26
+ @dofile.([path], outputs)
27
+ end
28
+
29
+ def build_meta
30
+ meta_data = @state.meta.to_h
31
+
32
+ meta_data = meta_data.merge(
33
+ runtime: "Fennel #{@version} on #{meta_data[:runtime]}"
34
+ )
35
+
36
+ @meta = Struct.new(*meta_data.keys).new(*meta_data.values)
37
+ end
38
+
39
+ def respond_to_missing?(method_name)
40
+ @state.respond_to? method_name
41
+ end
42
+
43
+ def method_missing(method_name, *arguments, &block)
44
+ @state.public_send(method_name, *arguments, &block)
45
+ end
46
+ end
47
+ end
data/dsl/global.rb ADDED
@@ -0,0 +1,42 @@
1
+ require_relative '../logic/options'
2
+
3
+ require_relative 'cache'
4
+
5
+ module Global
6
+ def api
7
+ Cache.instance.global_api
8
+ end
9
+
10
+ def state
11
+ Cache.instance.global_state
12
+ end
13
+
14
+ def config(options = {})
15
+ options = Logic::Options[:normalize].(options)
16
+
17
+ if options.key?(:shared_objects) || options.key?(:api_reference)
18
+ Cache.instance.global_api(options, recreate: true)
19
+ end
20
+
21
+ return unless
22
+ options.key?(:interpreter) ||
23
+ options.key?(:package_path) ||
24
+ options.key?(:package_cpath)
25
+
26
+ Cache.instance.global_state(options, recreate: true)
27
+
28
+ nil
29
+ end
30
+
31
+ def cached(all: false)
32
+ return Cache.instance.keys if all
33
+
34
+ Cache.instance.keys.select { |key| key[/^global/] }
35
+ end
36
+
37
+ def clear
38
+ Cache.instance.clear_global!
39
+ end
40
+
41
+ module_function :api, :state, :config, :cached, :clear
42
+ end
data/dsl/state.rb ADDED
@@ -0,0 +1,104 @@
1
+ require_relative 'errors'
2
+ require_relative 'concerns/packages'
3
+ require_relative 'concerns/fennel'
4
+
5
+ module DSL
6
+ class State
7
+ include DSL::Concerns::Packages
8
+ include DSL::Concerns::Fennel
9
+
10
+ attr_reader :meta
11
+
12
+ def initialize(api_component, interpreter_component, controller, options = {})
13
+ @api = api_component[:api]
14
+ @interpreter = interpreter_component[:interpreter]
15
+ @controller = controller
16
+
17
+ @state = @controller[:create!].(@api, @interpreter)[:state]
18
+
19
+ build_meta(api_component, interpreter_component)
20
+
21
+ add_package_path(options[:package_path]) if options[:package_path]
22
+ add_package_cpath(options[:package_cpath]) if options[:package_cpath]
23
+ end
24
+
25
+ def eval(input, outputs = 1)
26
+ @controller[:eval!].(@api, @interpreter, state, input, outputs)[:output]
27
+ end
28
+
29
+ def load(path, outputs = 1)
30
+ @controller[:load!].(@api, @interpreter, state, path, outputs)[:output]
31
+ end
32
+
33
+ def get(variable, key = nil)
34
+ @controller[:get!].(@api, @interpreter, state, variable, key)[:output]
35
+ end
36
+
37
+ def set(variable, value)
38
+ @controller[:set!].(@api, @interpreter, state, variable, value)[:output]
39
+ end
40
+
41
+ def destroy
42
+ @controller[:destroy!].(@api, @interpreter, state) if @state
43
+ @state = nil
44
+ end
45
+
46
+ def clear
47
+ @controller[:destroy!].(@api, @interpreter, state) if @state
48
+ @state = @controller[:create!].(@api, @interpreter)[:state]
49
+ nil
50
+ end
51
+
52
+ def _ensure_min_version!(purpose, lua, jit = nil)
53
+ version = lua
54
+ version = jit if meta.interpreter[/jit/] && jit
55
+
56
+ return unless Gem::Version.new(
57
+ meta.interpreter.gsub(/.+:/, '')
58
+ ) < Gem::Version.new(version)
59
+
60
+ message = "#{purpose} requires Lua >= #{lua}"
61
+ message = "#{message} or LuaJIT >= #{jit}" if jit
62
+
63
+ raise SweetMoon::Errors::SweetMoonError,
64
+ "#{message}; Current: #{meta.runtime} (#{meta.interpreter})"
65
+ end
66
+
67
+ def _unsafely_destroy
68
+ @controller[:destroy!].(@api, @interpreter, state)
69
+ @state = nil
70
+ end
71
+
72
+ def inspect
73
+ output = "#<#{self.class}:0x#{format('%016x', object_id)}"
74
+
75
+ variables = ['@meta'].map do |struct_name|
76
+ "#{struct_name}=#{instance_variable_get(struct_name).inspect}"
77
+ end
78
+
79
+ "#{output} #{variables.join(' ')}>"
80
+ end
81
+
82
+ private
83
+
84
+ def build_meta(api_component, interpreter_component)
85
+ meta_data = {
86
+ api_reference: api_component[:meta][:elected][:api_reference],
87
+ shared_objects: api_component[:meta][:elected][:shared_objects],
88
+ interpreter: interpreter_component[:meta][:elected][:interpreter],
89
+ runtime: interpreter_component[:meta][:runtime][:lua]
90
+ }
91
+
92
+ @meta = Struct.new(*meta_data.keys).new(*meta_data.values)
93
+ end
94
+
95
+ def state
96
+ unless @state
97
+ raise SweetMoon::Errors::SweetMoonError,
98
+ 'The state no longer exists.'
99
+ end
100
+
101
+ @state
102
+ end
103
+ end
104
+ end
data/dsl/sweet_moon.rb ADDED
@@ -0,0 +1,53 @@
1
+ require_relative '../logic/options'
2
+ require_relative '../logic/spec'
3
+
4
+ require_relative 'cache'
5
+ require_relative 'state'
6
+ require_relative 'global'
7
+
8
+ require_relative '../logic/api'
9
+ require_relative '../logic/interpreter'
10
+
11
+ module SweetMoon
12
+ module API
13
+ def new(options = {})
14
+ Cache.instance.api(Logic::Options[:normalize].(options))
15
+ end
16
+
17
+ module_function :new
18
+ end
19
+
20
+ module State
21
+ def new(options = {})
22
+ options = Logic::Options[:normalize].(options)
23
+
24
+ api = Cache.instance.api_module(options)
25
+
26
+ interpreter = Cache.instance.interpreter_module(api, options)
27
+
28
+ DSL::State.new(api, interpreter, Controller::State, options)
29
+ end
30
+
31
+ module_function :new
32
+ end
33
+
34
+ def meta
35
+ meta_data = {
36
+ version: Logic::Spec[:version],
37
+ api_references: Logic::API[:candidates].values.map do |candidate|
38
+ candidate[:version]
39
+ end,
40
+ interpreters: Logic::Interpreter[:candidates].values.map do |candidate|
41
+ candidate[:version]
42
+ end
43
+ }
44
+
45
+ Struct.new(*meta_data.keys).new(*meta_data.values)
46
+ end
47
+
48
+ def global
49
+ Global
50
+ end
51
+
52
+ module_function :meta, :global
53
+ end
data/logic/api.rb ADDED
@@ -0,0 +1,17 @@
1
+ require_relative 'signatures/signatures_322'
2
+ require_relative 'signatures/signatures_401'
3
+ require_relative 'signatures/signatures_503'
4
+ require_relative 'signatures/signatures_514'
5
+ require_relative 'signatures/signatures_542'
6
+
7
+ module Logic
8
+ API = {
9
+ candidates: {
10
+ '3.2.2' => { version: '3.2.2', signatures: V322::Signatures },
11
+ '4.0.1' => { version: '4.0.1', signatures: V401::Signatures },
12
+ '5.0.3' => { version: '5.0.3', signatures: V503::Signatures },
13
+ '5.1.4' => { version: '5.1.4', signatures: V514::Signatures },
14
+ '5.4.2' => { version: '5.4.2', signatures: V542::Signatures }
15
+ }
16
+ }
17
+ end
@@ -0,0 +1,84 @@
1
+ require_relative 'interpreters/interpreter_50'
2
+ require_relative 'interpreters/interpreter_51'
3
+ require_relative 'interpreters/interpreter_54'
4
+
5
+ module Logic
6
+ Interpreter = {
7
+ candidates: {
8
+ '5.0' => { version: '5.0', requires: V50::Interpreter[:requires] },
9
+ '5.1' => { version: '5.1', requires: V51::Interpreter[:requires] },
10
+ '5.4' => { version: '5.4', requires: V54::Interpreter[:requires] }
11
+ },
12
+
13
+ elect: ->(signatures, api_version, options = {}) {
14
+ interpreters = Interpreter[:candidates].values
15
+
16
+ if options[:interpreter]
17
+ interpreters = interpreters.select do |interpreter|
18
+ interpreter[:version].to_s == options[:interpreter]
19
+ end
20
+
21
+ if interpreters.size.zero?
22
+ return {
23
+ compatible: false,
24
+ error: "Interpreter #{options[:interpreter]} not available."
25
+ }
26
+ end
27
+ end
28
+
29
+ results = {}
30
+
31
+ interpreters.each do |interpreter|
32
+ results[interpreter[:version]] = Interpreter[:check_compatibility].(
33
+ interpreter, signatures
34
+ )
35
+ end
36
+
37
+ result = Interpreter[:choose_compatible].(results)
38
+
39
+ return result if result
40
+
41
+ { compatible: false,
42
+ error: Interpreter[:closest_error_message].(api_version, results) }
43
+ },
44
+
45
+ closest_error_message: ->(api_version, candidates) {
46
+ closest = candidates.values.sort_by do |candidate|
47
+ Gem::Version.new(candidate[:version].gsub(/.+:/, '') || '0')
48
+ end.reverse
49
+
50
+ closest = closest.min_by { |candidate| candidate[:missing].size }
51
+
52
+ message = "Missing in the closest version (#{closest[:version]}):"
53
+
54
+ closest[:missing].sort.each_slice(4).each do |functions|
55
+ message += "\n #{functions.join(' ')}"
56
+ end
57
+
58
+ "No compatible interpreter found for Lua C API #{api_version}.\n#{message}"
59
+ },
60
+
61
+ choose_compatible: ->(candidates) {
62
+ candidates = candidates.values.select do |candidate|
63
+ candidate[:compatible]
64
+ end
65
+
66
+ candidates.max_by do |candidate|
67
+ Gem::Version.new(candidate[:version].gsub(/.+:/, '') || '0')
68
+ end
69
+ },
70
+
71
+ check_compatibility: ->(interpreter, signatures) {
72
+ result = { version: interpreter[:version], compatible: true, missing: [] }
73
+
74
+ interpreter[:requires].each do |required|
75
+ unless signatures[required]
76
+ result[:compatible] = false
77
+ result[:missing] << required
78
+ end
79
+ end
80
+
81
+ result
82
+ }
83
+ }
84
+ end
@@ -0,0 +1,34 @@
1
+ module Logic
2
+ module V50
3
+ Interpreter = {
4
+ version: '5.0',
5
+
6
+ LUA_REGISTRYINDEX: -10_000,
7
+ LUA_GLOBALSINDEX: -10_001,
8
+
9
+ requires: %i[
10
+ lua_close lua_gettable lua_gettop lua_insert lua_newtable lua_next lua_open
11
+ lua_pcall lua_pushboolean lua_pushcclosure lua_pushnil lua_pushnumber
12
+ lua_pushstring lua_rawgeti lua_settable lua_settop lua_toboolean lua_tonumber
13
+ lua_topointer lua_tostring lua_type lua_typename luaL_loadbuffer luaL_loadfile
14
+ luaL_ref luaopen_base luaopen_io luaopen_math luaopen_string luaopen_table
15
+ ],
16
+
17
+ status: {
18
+ 1 => :LUA_ERRRUN,
19
+ 2 => :LUA_ERRFILE,
20
+ 3 => :LUA_ERRSYNTAX,
21
+ 4 => :LUA_ERRMEM,
22
+ 5 => :LUA_ERRERR
23
+ },
24
+
25
+ error: {
26
+ LUA_ERRRUN: :runtime,
27
+ LUA_ERRFILE: :file,
28
+ LUA_ERRSYNTAX: :syntax,
29
+ LUA_ERRMEM: :memory_allocation,
30
+ LUA_ERRERR: :message_handler
31
+ }
32
+ }
33
+ end
34
+ end
@@ -0,0 +1,37 @@
1
+ module Logic
2
+ module V51
3
+ Interpreter = {
4
+ version: '5.1',
5
+
6
+ LUA_REGISTRYINDEX: -10_000,
7
+ LUA_GLOBALSINDEX: -10_002,
8
+
9
+ # lua_isinteger lua_pushinteger lua_tointeger
10
+ requires: %i[
11
+ lua_close lua_createtable lua_getfield lua_gettable lua_gettop lua_insert
12
+ lua_next lua_pcall lua_pushboolean lua_pushcclosure lua_pushnil lua_pushnumber
13
+ lua_pushstring lua_rawgeti lua_settable lua_settop lua_toboolean lua_tonumber
14
+ lua_topointer lua_tostring lua_type lua_typename luaL_loadfile luaL_loadstring
15
+ luaL_newstate luaL_openlibs luaL_ref
16
+ ],
17
+
18
+ status: {
19
+ 0 => :LUA_OK,
20
+ 1 => :LUA_YIELD,
21
+ 2 => :LUA_ERRRUN,
22
+ 3 => :LUA_ERRSYNTAX,
23
+ 4 => :LUA_ERRMEM,
24
+ 5 => :LUA_ERRERR,
25
+ 6 => :LUA_ERRFILE
26
+ },
27
+
28
+ error: {
29
+ LUA_ERRRUN: :runtime,
30
+ LUA_ERRSYNTAX: :syntax,
31
+ LUA_ERRMEM: :memory_allocation,
32
+ LUA_ERRERR: :message_handler,
33
+ LUA_ERRFILE: :file
34
+ }
35
+ }
36
+ end
37
+ end
@@ -0,0 +1,41 @@
1
+ module Logic
2
+ module V54
3
+ Interpreter = {
4
+ version: '5.4',
5
+
6
+ LUA_RIDX_GLOBALS: 2,
7
+
8
+ # TODO: It's possible to read C constants?
9
+ # LUAI_MAXSTACK = 1_000_000
10
+ # -LUAI_MAXSTACK - 1000
11
+ LUA_REGISTRYINDEX: -1_000_000 - 1000,
12
+
13
+ # lua_isinteger lua_pushinteger lua_tointeger
14
+ requires: %i[
15
+ lua_close lua_createtable lua_getfield lua_getglobal lua_gettop lua_next
16
+ lua_pcall lua_pushboolean lua_pushcclosure lua_pushnil lua_pushnumber
17
+ lua_pushstring lua_rawgeti lua_setglobal lua_settable lua_settop lua_toboolean
18
+ lua_tonumber lua_topointer lua_tostring lua_type lua_typename luaL_loadfile
19
+ luaL_loadstring luaL_newstate luaL_openlibs luaL_ref
20
+ ],
21
+
22
+ status: {
23
+ 0 => :LUA_OK,
24
+ 1 => :LUA_YIELD,
25
+ 2 => :LUA_ERRRUN,
26
+ 3 => :LUA_ERRSYNTAX,
27
+ 4 => :LUA_ERRMEM,
28
+ 5 => :LUA_ERRERR,
29
+ 6 => :LUA_ERRFILE
30
+ },
31
+
32
+ error: {
33
+ LUA_ERRRUN: :runtime,
34
+ LUA_ERRSYNTAX: :syntax,
35
+ LUA_ERRMEM: :memory_allocation,
36
+ LUA_ERRERR: :message_handler,
37
+ LUA_ERRFILE: :file
38
+ }
39
+ }
40
+ end
41
+ end
data/logic/io.rb ADDED
@@ -0,0 +1,6 @@
1
+ module Logic
2
+ IO = {
3
+ extension: ->(path) { File.extname(path) },
4
+ file_name: ->(path) { File.basename(path) }
5
+ }
6
+ end
data/logic/options.rb ADDED
@@ -0,0 +1,14 @@
1
+ module Logic
2
+ Options = {
3
+ normalize: ->(original_options) {
4
+ options = original_options.clone
5
+
6
+ if options[:shared_object]
7
+ options[:shared_objects] = [options[:shared_object]]
8
+ options.delete(:shared_object)
9
+ end
10
+
11
+ options
12
+ }
13
+ }
14
+ end
@@ -0,0 +1,52 @@
1
+ require_relative './io'
2
+
3
+ module Logic
4
+ SharedObject = {
5
+ choose: ->(candidate_paths, options = {}) {
6
+ candidates = candidate_paths.map do |path|
7
+ SharedObject[:normalize].(path)
8
+ end
9
+
10
+ unless options[:jit].nil?
11
+ candidates = candidates.select do |candidate|
12
+ options[:jit] ? candidate[:inferences][:jit] : !candidate[:inferences][:jit]
13
+ end
14
+ end
15
+
16
+ if options[:version]
17
+ candidates = candidates.select do |candidate|
18
+ if candidate[:inferences][:version]
19
+ !candidate[:inferences][:version][/#{options[:version]}/].nil?
20
+ else
21
+ false
22
+ end
23
+ end
24
+ end
25
+
26
+ elected = candidates.max_by do |candidate|
27
+ Gem::Version.new(candidate[:inferences][:version] || '0')
28
+ end
29
+
30
+ return [] if elected.nil?
31
+
32
+ [elected]
33
+ },
34
+
35
+ normalize: ->(path) {
36
+ inferred_versions = IO[:file_name].(
37
+ path
38
+ ).scan(/(\d+(\.\d+)*)/).map(&:first)
39
+
40
+ inferred_versions = inferred_versions.map do |inferred_version|
41
+ inferred_version.split('.').map { |part| part.chars.join('.') }.join('.')
42
+ end
43
+
44
+ inferred_version = inferred_versions.max_by do |raw_inferred_version|
45
+ Gem::Version.new(raw_inferred_version || '0')
46
+ end
47
+
48
+ { path: path,
49
+ inferences: { jit: !path[/jit/].nil?, version: inferred_version } }
50
+ }
51
+ }
52
+ end