konpeito 0.1.0
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.
- checksums.yaml +7 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +75 -0
- data/CONTRIBUTING.md +123 -0
- data/LICENSE +21 -0
- data/README.md +257 -0
- data/Rakefile +11 -0
- data/bin/konpeito +6 -0
- data/konpeito.gemspec +43 -0
- data/lib/konpeito/ast/typed_ast.rb +620 -0
- data/lib/konpeito/ast/visitor.rb +78 -0
- data/lib/konpeito/cache/cache_manager.rb +230 -0
- data/lib/konpeito/cache/dependency_graph.rb +192 -0
- data/lib/konpeito/cache.rb +8 -0
- data/lib/konpeito/cli/base_command.rb +187 -0
- data/lib/konpeito/cli/build_command.rb +220 -0
- data/lib/konpeito/cli/check_command.rb +104 -0
- data/lib/konpeito/cli/config.rb +231 -0
- data/lib/konpeito/cli/deps_command.rb +128 -0
- data/lib/konpeito/cli/doctor_command.rb +340 -0
- data/lib/konpeito/cli/fmt_command.rb +199 -0
- data/lib/konpeito/cli/init_command.rb +312 -0
- data/lib/konpeito/cli/lsp_command.rb +40 -0
- data/lib/konpeito/cli/run_command.rb +150 -0
- data/lib/konpeito/cli/test_command.rb +248 -0
- data/lib/konpeito/cli/watch_command.rb +212 -0
- data/lib/konpeito/cli.rb +301 -0
- data/lib/konpeito/codegen/builtin_methods.rb +229 -0
- data/lib/konpeito/codegen/cruby_backend.rb +1090 -0
- data/lib/konpeito/codegen/debug_info.rb +352 -0
- data/lib/konpeito/codegen/inliner.rb +486 -0
- data/lib/konpeito/codegen/jvm_backend.rb +197 -0
- data/lib/konpeito/codegen/jvm_generator.rb +13412 -0
- data/lib/konpeito/codegen/llvm_generator.rb +13191 -0
- data/lib/konpeito/codegen/loop_optimizer.rb +363 -0
- data/lib/konpeito/codegen/monomorphizer.rb +359 -0
- data/lib/konpeito/codegen/profile_runtime.c +341 -0
- data/lib/konpeito/codegen/profiler.rb +99 -0
- data/lib/konpeito/compiler.rb +592 -0
- data/lib/konpeito/dependency_resolver.rb +296 -0
- data/lib/konpeito/diagnostics/collector.rb +127 -0
- data/lib/konpeito/diagnostics/diagnostic.rb +237 -0
- data/lib/konpeito/diagnostics/renderer.rb +144 -0
- data/lib/konpeito/formatter/formatter.rb +1214 -0
- data/lib/konpeito/hir/builder.rb +7167 -0
- data/lib/konpeito/hir/nodes.rb +2465 -0
- data/lib/konpeito/lsp/document_manager.rb +820 -0
- data/lib/konpeito/lsp/server.rb +183 -0
- data/lib/konpeito/lsp/transport.rb +38 -0
- data/lib/konpeito/parser/prism_adapter.rb +65 -0
- data/lib/konpeito/platform.rb +103 -0
- data/lib/konpeito/profile/report.rb +136 -0
- data/lib/konpeito/rbs_inline/preprocessor.rb +199 -0
- data/lib/konpeito/stdlib/compression/compression.rb +72 -0
- data/lib/konpeito/stdlib/compression/compression.rbs +60 -0
- data/lib/konpeito/stdlib/compression/compression_native.c +415 -0
- data/lib/konpeito/stdlib/compression/extconf.rb +19 -0
- data/lib/konpeito/stdlib/crypto/crypto.rb +85 -0
- data/lib/konpeito/stdlib/crypto/crypto.rbs +74 -0
- data/lib/konpeito/stdlib/crypto/crypto_native.c +312 -0
- data/lib/konpeito/stdlib/crypto/extconf.rb +40 -0
- data/lib/konpeito/stdlib/http/extconf.rb +19 -0
- data/lib/konpeito/stdlib/http/http.rb +125 -0
- data/lib/konpeito/stdlib/http/http.rbs +57 -0
- data/lib/konpeito/stdlib/http/http_native.c +440 -0
- data/lib/konpeito/stdlib/json/extconf.rb +17 -0
- data/lib/konpeito/stdlib/json/json.rb +44 -0
- data/lib/konpeito/stdlib/json/json.rbs +33 -0
- data/lib/konpeito/stdlib/json/json_native.c +286 -0
- data/lib/konpeito/stdlib/ui/extconf.rb +216 -0
- data/lib/konpeito/stdlib/ui/konpeito_ui_native.cpp +1625 -0
- data/lib/konpeito/stdlib/ui/konpeito_ui_native.h +162 -0
- data/lib/konpeito/stdlib/ui/ui.rb +318 -0
- data/lib/konpeito/stdlib/ui/ui.rbs +247 -0
- data/lib/konpeito/type_checker/annotation_parser.rb +67 -0
- data/lib/konpeito/type_checker/hm_inferrer.rb +2565 -0
- data/lib/konpeito/type_checker/inferrer.rb +565 -0
- data/lib/konpeito/type_checker/rbs_loader.rb +1621 -0
- data/lib/konpeito/type_checker/type_resolver.rb +276 -0
- data/lib/konpeito/type_checker/types.rb +1434 -0
- data/lib/konpeito/type_checker/unification.rb +323 -0
- data/lib/konpeito/ui/animation/animated_state.rb +80 -0
- data/lib/konpeito/ui/animation/easing.rb +59 -0
- data/lib/konpeito/ui/animation/value_tween.rb +66 -0
- data/lib/konpeito/ui/app.rb +379 -0
- data/lib/konpeito/ui/box.rb +38 -0
- data/lib/konpeito/ui/castella.rb +70 -0
- data/lib/konpeito/ui/castella_native.rb +76 -0
- data/lib/konpeito/ui/chart/area_chart.rb +305 -0
- data/lib/konpeito/ui/chart/bar_chart.rb +288 -0
- data/lib/konpeito/ui/chart/base_chart.rb +210 -0
- data/lib/konpeito/ui/chart/chart_helpers.rb +79 -0
- data/lib/konpeito/ui/chart/gauge_chart.rb +171 -0
- data/lib/konpeito/ui/chart/heatmap_chart.rb +222 -0
- data/lib/konpeito/ui/chart/line_chart.rb +289 -0
- data/lib/konpeito/ui/chart/pie_chart.rb +219 -0
- data/lib/konpeito/ui/chart/scales.rb +77 -0
- data/lib/konpeito/ui/chart/scatter_chart.rb +303 -0
- data/lib/konpeito/ui/chart/stacked_bar_chart.rb +276 -0
- data/lib/konpeito/ui/column.rb +271 -0
- data/lib/konpeito/ui/core.rb +2199 -0
- data/lib/konpeito/ui/dsl.rb +443 -0
- data/lib/konpeito/ui/frame.rb +171 -0
- data/lib/konpeito/ui/frame_native.rb +494 -0
- data/lib/konpeito/ui/markdown/ast.rb +124 -0
- data/lib/konpeito/ui/markdown/mermaid/layout.rb +387 -0
- data/lib/konpeito/ui/markdown/mermaid/models.rb +232 -0
- data/lib/konpeito/ui/markdown/mermaid/parser.rb +519 -0
- data/lib/konpeito/ui/markdown/mermaid/renderer.rb +336 -0
- data/lib/konpeito/ui/markdown/parser.rb +805 -0
- data/lib/konpeito/ui/markdown/renderer.rb +639 -0
- data/lib/konpeito/ui/markdown/theme.rb +165 -0
- data/lib/konpeito/ui/render_node.rb +260 -0
- data/lib/konpeito/ui/row.rb +207 -0
- data/lib/konpeito/ui/spacer.rb +18 -0
- data/lib/konpeito/ui/style.rb +799 -0
- data/lib/konpeito/ui/theme.rb +563 -0
- data/lib/konpeito/ui/themes/material.rb +35 -0
- data/lib/konpeito/ui/themes/tokyo_night.rb +6 -0
- data/lib/konpeito/ui/widgets/button.rb +103 -0
- data/lib/konpeito/ui/widgets/calendar.rb +1034 -0
- data/lib/konpeito/ui/widgets/checkbox.rb +119 -0
- data/lib/konpeito/ui/widgets/container.rb +91 -0
- data/lib/konpeito/ui/widgets/data_table.rb +667 -0
- data/lib/konpeito/ui/widgets/divider.rb +29 -0
- data/lib/konpeito/ui/widgets/image.rb +105 -0
- data/lib/konpeito/ui/widgets/input.rb +485 -0
- data/lib/konpeito/ui/widgets/markdown.rb +57 -0
- data/lib/konpeito/ui/widgets/modal.rb +163 -0
- data/lib/konpeito/ui/widgets/multiline_input.rb +968 -0
- data/lib/konpeito/ui/widgets/multiline_text.rb +180 -0
- data/lib/konpeito/ui/widgets/net_image.rb +100 -0
- data/lib/konpeito/ui/widgets/progress_bar.rb +70 -0
- data/lib/konpeito/ui/widgets/radio_buttons.rb +93 -0
- data/lib/konpeito/ui/widgets/slider.rb +133 -0
- data/lib/konpeito/ui/widgets/switch.rb +84 -0
- data/lib/konpeito/ui/widgets/tabs.rb +157 -0
- data/lib/konpeito/ui/widgets/text.rb +110 -0
- data/lib/konpeito/ui/widgets/tree.rb +426 -0
- data/lib/konpeito/version.rb +5 -0
- data/lib/konpeito.rb +109 -0
- data/test_native_array.rb +172 -0
- data/test_native_array_class.rb +197 -0
- data/test_native_class.rb +151 -0
- data/tools/konpeito-asm/build.sh +65 -0
- data/tools/konpeito-asm/lib/asm-9.7.1.jar +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KArray.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KCompression.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KConditionVariable.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KCrypto.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KFile.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KHTTP.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KHash.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KJSON$Parser.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KJSON.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KMath.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KRactor.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KRactorPort.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KSizedQueue.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KThread.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KTime.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/RubyDispatch.class +0 -0
- data/tools/konpeito-asm/src/ClassIntrospector.java +312 -0
- data/tools/konpeito-asm/src/KonpeitoAssembler.java +659 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KArray.java +390 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KCompression.java +168 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KConditionVariable.java +48 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KCrypto.java +151 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KFile.java +100 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KHTTP.java +113 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KHash.java +228 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KJSON.java +405 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KMath.java +54 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KRactor.java +244 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KRactorPort.java +53 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KSizedQueue.java +49 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KThread.java +49 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KTime.java +53 -0
- data/tools/konpeito-asm/src/konpeito/runtime/RubyDispatch.java +416 -0
- metadata +267 -0
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Konpeito
|
|
4
|
+
module Codegen
|
|
5
|
+
# Profiling instrumentation for LLVM IR code generation.
|
|
6
|
+
# Inserts function entry/exit probes that call into C runtime
|
|
7
|
+
# for collecting call counts and execution time.
|
|
8
|
+
class Profiler
|
|
9
|
+
attr_reader :function_ids
|
|
10
|
+
|
|
11
|
+
def initialize(llvm_module, builder)
|
|
12
|
+
@mod = llvm_module
|
|
13
|
+
@builder = builder
|
|
14
|
+
@function_ids = {} # function_name => unique_id
|
|
15
|
+
@next_id = 0
|
|
16
|
+
|
|
17
|
+
declare_runtime_functions
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Declare external C runtime functions for profiling
|
|
21
|
+
def declare_runtime_functions
|
|
22
|
+
ptr_type = LLVM::Pointer(LLVM::Int8)
|
|
23
|
+
|
|
24
|
+
# void konpeito_profile_enter(int func_id, const char* func_name)
|
|
25
|
+
@profile_enter = @mod.functions.add(
|
|
26
|
+
"konpeito_profile_enter",
|
|
27
|
+
[LLVM::Int32, ptr_type],
|
|
28
|
+
LLVM.Void
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
# void konpeito_profile_exit(int func_id)
|
|
32
|
+
@profile_exit = @mod.functions.add(
|
|
33
|
+
"konpeito_profile_exit",
|
|
34
|
+
[LLVM::Int32],
|
|
35
|
+
LLVM.Void
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
# void konpeito_profile_init(int num_functions, const char* output_path)
|
|
39
|
+
@profile_init = @mod.functions.add(
|
|
40
|
+
"konpeito_profile_init",
|
|
41
|
+
[LLVM::Int32, ptr_type],
|
|
42
|
+
LLVM.Void
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
# void konpeito_profile_finalize(void)
|
|
46
|
+
@profile_finalize = @mod.functions.add(
|
|
47
|
+
"konpeito_profile_finalize",
|
|
48
|
+
[],
|
|
49
|
+
LLVM.Void
|
|
50
|
+
)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Register a function for profiling and return its ID
|
|
54
|
+
def register_function(name)
|
|
55
|
+
return @function_ids[name] if @function_ids.key?(name)
|
|
56
|
+
|
|
57
|
+
id = @next_id
|
|
58
|
+
@next_id += 1
|
|
59
|
+
@function_ids[name] = id
|
|
60
|
+
id
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Insert entry probe at function start
|
|
64
|
+
# Call this right after positioning builder at entry block
|
|
65
|
+
def insert_entry_probe(function_name)
|
|
66
|
+
func_id = register_function(function_name)
|
|
67
|
+
|
|
68
|
+
# Use global_string_pointer to create a string constant and get its pointer
|
|
69
|
+
func_name_ptr = @builder.global_string_pointer(function_name)
|
|
70
|
+
|
|
71
|
+
@builder.call(@profile_enter, LLVM::Int32.from_i(func_id), func_name_ptr)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Insert exit probe before return instruction
|
|
75
|
+
# Call this before generating return instruction
|
|
76
|
+
def insert_exit_probe(function_name)
|
|
77
|
+
func_id = @function_ids[function_name]
|
|
78
|
+
return unless func_id
|
|
79
|
+
|
|
80
|
+
@builder.call(@profile_exit, LLVM::Int32.from_i(func_id))
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Generate initialization call (called once at module init)
|
|
84
|
+
def generate_init_call(builder, output_path)
|
|
85
|
+
num_funcs = LLVM::Int32.from_i(@function_ids.size)
|
|
86
|
+
|
|
87
|
+
# Use global_string_pointer to create a string constant and get its pointer
|
|
88
|
+
path_ptr = builder.global_string_pointer(output_path)
|
|
89
|
+
|
|
90
|
+
builder.call(@profile_init, num_funcs, path_ptr)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Get total number of registered functions
|
|
94
|
+
def num_functions
|
|
95
|
+
@function_ids.size
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
@@ -0,0 +1,592 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rbconfig"
|
|
4
|
+
|
|
5
|
+
module Konpeito
|
|
6
|
+
# Compilation statistics collected during a build
|
|
7
|
+
CompileStats = Data.define(
|
|
8
|
+
:resolved_files, # Integer - number of source files resolved
|
|
9
|
+
:rbs_count, # Integer - number of RBS definitions loaded
|
|
10
|
+
:functions, # Integer - number of functions compiled
|
|
11
|
+
:classes, # Integer - number of classes compiled
|
|
12
|
+
:modules, # Integer - number of modules compiled
|
|
13
|
+
:specializations, # Integer - monomorphization specializations generated
|
|
14
|
+
:inlined, # Integer - call sites inlined
|
|
15
|
+
:hoisted, # Integer - loop-invariant instructions hoisted
|
|
16
|
+
:duration_s # Float - total compile time in seconds
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
class Compiler
|
|
20
|
+
attr_reader :source_file, :output_file, :format, :verbose, :rbs_paths, :require_paths, :diagnostics, :debug, :profile, :incremental, :compile_stats
|
|
21
|
+
|
|
22
|
+
def initialize(source_file:, output_file:, format: :cruby_ext, verbose: false, rbs_paths: [], optimize: true, require_paths: [], debug: false, profile: false, incremental: false, clean_cache: false, inline_rbs: false, target: :native, run_after: false, emit_ir: false, classpath: nil, library: false)
|
|
23
|
+
@source_file = source_file
|
|
24
|
+
@format = format
|
|
25
|
+
@verbose = verbose
|
|
26
|
+
@rbs_paths = rbs_paths
|
|
27
|
+
@require_paths = require_paths
|
|
28
|
+
@rbs_loader = nil
|
|
29
|
+
@hm_inferrer = nil
|
|
30
|
+
@optimize = optimize
|
|
31
|
+
@stdlib_requires = []
|
|
32
|
+
@diagnostics = []
|
|
33
|
+
@debug = debug
|
|
34
|
+
@profile = profile
|
|
35
|
+
@incremental = incremental
|
|
36
|
+
@clean_cache = clean_cache
|
|
37
|
+
@cache_manager = nil
|
|
38
|
+
@inline_rbs = inline_rbs
|
|
39
|
+
@target = target
|
|
40
|
+
@run_after = run_after
|
|
41
|
+
@emit_ir = emit_ir
|
|
42
|
+
@classpath = classpath
|
|
43
|
+
@library = library
|
|
44
|
+
@output_file = output_file || default_output_file(target: target)
|
|
45
|
+
@compile_stats = nil
|
|
46
|
+
@_resolved_file_count = 0
|
|
47
|
+
@_specialization_count = 0
|
|
48
|
+
@_inlined_count = 0
|
|
49
|
+
@_hoisted_count = 0
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def compile
|
|
53
|
+
t0 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
54
|
+
|
|
55
|
+
setup_cache if @incremental
|
|
56
|
+
|
|
57
|
+
@parsed_ast = parse
|
|
58
|
+
typed_ast = type_check_ast(@parsed_ast)
|
|
59
|
+
hir = generate_hir(typed_ast)
|
|
60
|
+
hir = optimize_hir(hir) if @optimize
|
|
61
|
+
resolve_types(hir) if @target == :jvm
|
|
62
|
+
generate_code(hir)
|
|
63
|
+
|
|
64
|
+
save_cache if @incremental
|
|
65
|
+
|
|
66
|
+
duration = Process.clock_gettime(Process::CLOCK_MONOTONIC) - t0
|
|
67
|
+
|
|
68
|
+
# Collect stats
|
|
69
|
+
rbs_count = @rbs_paths.size
|
|
70
|
+
func_count = hir.functions.size
|
|
71
|
+
class_count = hir.classes.size
|
|
72
|
+
mod_count = hir.respond_to?(:modules) ? hir.modules.size : 0
|
|
73
|
+
|
|
74
|
+
@compile_stats = CompileStats.new(
|
|
75
|
+
resolved_files: @_resolved_file_count,
|
|
76
|
+
rbs_count: rbs_count,
|
|
77
|
+
functions: func_count,
|
|
78
|
+
classes: class_count,
|
|
79
|
+
modules: mod_count,
|
|
80
|
+
specializations: @_specialization_count,
|
|
81
|
+
inlined: @_inlined_count,
|
|
82
|
+
hoisted: @_hoisted_count,
|
|
83
|
+
duration_s: duration
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
output_file
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def type_check
|
|
90
|
+
t0 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
91
|
+
|
|
92
|
+
@parsed_ast = parse
|
|
93
|
+
type_check_ast(@parsed_ast)
|
|
94
|
+
|
|
95
|
+
duration = Process.clock_gettime(Process::CLOCK_MONOTONIC) - t0
|
|
96
|
+
@compile_stats = CompileStats.new(
|
|
97
|
+
resolved_files: @_resolved_file_count,
|
|
98
|
+
rbs_count: @rbs_paths.size,
|
|
99
|
+
functions: 0,
|
|
100
|
+
classes: 0,
|
|
101
|
+
modules: 0,
|
|
102
|
+
specializations: 0,
|
|
103
|
+
inlined: 0,
|
|
104
|
+
hoisted: 0,
|
|
105
|
+
duration_s: duration
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
true
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
private
|
|
112
|
+
|
|
113
|
+
def parse
|
|
114
|
+
log "Resolving dependencies for #{source_file}..."
|
|
115
|
+
resolver = DependencyResolver.new(
|
|
116
|
+
base_paths: @require_paths,
|
|
117
|
+
verbose: verbose,
|
|
118
|
+
cache_manager: @cache_manager
|
|
119
|
+
)
|
|
120
|
+
merged_ast, auto_rbs_paths, stdlib_requires, runtime_native_exts = resolver.resolve(source_file)
|
|
121
|
+
|
|
122
|
+
# Merge auto-detected RBS paths (normalize to absolute for dedup)
|
|
123
|
+
@rbs_paths = (@rbs_paths.map { |p| File.expand_path(p) } + auto_rbs_paths).uniq
|
|
124
|
+
|
|
125
|
+
# Store stdlib requires for runtime loading
|
|
126
|
+
@stdlib_requires = stdlib_requires
|
|
127
|
+
|
|
128
|
+
# Store runtime native extensions (excluded from linker flags)
|
|
129
|
+
@runtime_native_extensions = runtime_native_exts || []
|
|
130
|
+
|
|
131
|
+
@_resolved_file_count = resolver.resolved_files.size
|
|
132
|
+
@_resolved_file_paths = resolver.resolved_files.keys
|
|
133
|
+
|
|
134
|
+
if verbose && resolver.resolved_files.size > 1
|
|
135
|
+
log "Resolved #{resolver.resolved_files.size} files:"
|
|
136
|
+
resolver.resolved_files.keys.each { |f| log " - #{f}" }
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
if verbose && !@stdlib_requires.empty?
|
|
140
|
+
log "Stdlib requires (will be loaded at runtime):"
|
|
141
|
+
@stdlib_requires.each { |lib| log " - #{lib}" }
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
merged_ast
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def rbs_loader
|
|
148
|
+
@rbs_loader ||= begin
|
|
149
|
+
loader = TypeChecker::RBSLoader.new
|
|
150
|
+
|
|
151
|
+
# Process inline RBS from all resolved source files
|
|
152
|
+
# Auto-detect files with "# rbs_inline: enabled" even without --inline flag
|
|
153
|
+
inline_rbs_content = nil
|
|
154
|
+
files_to_scan = @_resolved_file_paths || (File.exist?(source_file) ? [source_file] : [])
|
|
155
|
+
preprocessor = RBSInline::Preprocessor.new
|
|
156
|
+
inline_parts = []
|
|
157
|
+
files_to_scan.each do |path|
|
|
158
|
+
next unless File.exist?(path)
|
|
159
|
+
content = File.read(path)
|
|
160
|
+
# Process if --inline flag is set OR file has rbs_inline marker
|
|
161
|
+
if @inline_rbs || RBSInline::Preprocessor.has_inline_rbs?(content)
|
|
162
|
+
rbs_output = preprocessor.process(content, filename: path)
|
|
163
|
+
inline_parts << rbs_output unless rbs_output.strip.empty?
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
unless inline_parts.empty?
|
|
167
|
+
inline_rbs_content = inline_parts.join("\n")
|
|
168
|
+
log "Generated RBS from inline annotations (#{inline_parts.size} files)" if verbose
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
loader.load(
|
|
172
|
+
rbs_paths: rbs_paths,
|
|
173
|
+
stdlib_libraries: @stdlib_requires,
|
|
174
|
+
inline_rbs_content: inline_rbs_content
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
# Auto-register Java:: references from AST before classpath introspection
|
|
178
|
+
if @target == :jvm && @classpath && !@classpath.empty? && @parsed_ast
|
|
179
|
+
# ClassIntrospector requires absolute paths in classpath
|
|
180
|
+
abs_classpath = @classpath.split(Platform.classpath_separator).map { |p| File.expand_path(p) }.join(Platform.classpath_separator)
|
|
181
|
+
java_refs = scan_java_references(@parsed_ast)
|
|
182
|
+
loader.register_java_references(java_refs, abs_classpath) unless java_refs[:refs].empty?
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
if @target == :jvm
|
|
186
|
+
abs_cp = @classpath ? @classpath.split(Platform.classpath_separator).map { |p| File.expand_path(p) }.join(Platform.classpath_separator) : @classpath
|
|
187
|
+
loader.load_classpath_types(abs_cp)
|
|
188
|
+
end
|
|
189
|
+
loader
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
def type_check_ast(ast)
|
|
194
|
+
log "Type checking..."
|
|
195
|
+
|
|
196
|
+
# Read source content for diagnostic messages
|
|
197
|
+
source_content = File.exist?(source_file) ? File.read(source_file) : nil
|
|
198
|
+
|
|
199
|
+
# Build typed AST with HM inference
|
|
200
|
+
builder = AST::TypedASTBuilder.new(
|
|
201
|
+
rbs_loader,
|
|
202
|
+
use_hm: true,
|
|
203
|
+
file_path: source_file,
|
|
204
|
+
source: source_content
|
|
205
|
+
)
|
|
206
|
+
typed_ast = builder.build(ast)
|
|
207
|
+
|
|
208
|
+
# Save HM inferrer for monomorphization
|
|
209
|
+
@hm_inferrer = builder.instance_variable_get(:@hm_inferrer)
|
|
210
|
+
|
|
211
|
+
# Store diagnostics for later display
|
|
212
|
+
@diagnostics = builder.diagnostics
|
|
213
|
+
|
|
214
|
+
# Show inferred types in verbose mode
|
|
215
|
+
if verbose && @hm_inferrer
|
|
216
|
+
show_inferred_types(@hm_inferrer)
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
# Log unresolved type warnings (Kotlin-style validation)
|
|
220
|
+
if verbose && @hm_inferrer && @hm_inferrer.respond_to?(:unresolved_type_warnings)
|
|
221
|
+
warnings = @hm_inferrer.unresolved_type_warnings
|
|
222
|
+
unless warnings.empty?
|
|
223
|
+
log "Unresolved type warnings (#{warnings.size}):"
|
|
224
|
+
warnings.each do |w|
|
|
225
|
+
case w[:kind]
|
|
226
|
+
when :param
|
|
227
|
+
log " #{w[:function]} param[#{w[:index]}]: #{w[:typevar]}"
|
|
228
|
+
when :return
|
|
229
|
+
log " #{w[:function]} return: #{w[:typevar]}"
|
|
230
|
+
end
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
typed_ast
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
def show_inferred_types(hm_inferrer)
|
|
239
|
+
func_types = hm_inferrer.instance_variable_get(:@function_types)
|
|
240
|
+
env = hm_inferrer.instance_variable_get(:@env).first
|
|
241
|
+
|
|
242
|
+
has_output = false
|
|
243
|
+
|
|
244
|
+
unless func_types.empty?
|
|
245
|
+
log "Inferred function types:"
|
|
246
|
+
func_types.each do |name, func_type|
|
|
247
|
+
final_type = hm_inferrer.finalize(func_type)
|
|
248
|
+
log " #{name}: #{final_type}"
|
|
249
|
+
end
|
|
250
|
+
has_output = true
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
# Show inferred variable types (excluding functions)
|
|
254
|
+
var_types = env.reject do |name, scheme|
|
|
255
|
+
hm_inferrer.finalize(scheme.type).is_a?(TypeChecker::FunctionType)
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
unless var_types.empty?
|
|
259
|
+
log "Inferred variable types:" if has_output
|
|
260
|
+
var_types.each do |name, scheme|
|
|
261
|
+
final_type = hm_inferrer.finalize(scheme.type)
|
|
262
|
+
log " #{name}: #{final_type}"
|
|
263
|
+
end
|
|
264
|
+
end
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
def generate_hir(typed_ast)
|
|
268
|
+
log "Generating HIR..."
|
|
269
|
+
|
|
270
|
+
builder = HIR::Builder.new(rbs_loader: @rbs_loader)
|
|
271
|
+
builder.build(typed_ast)
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
def optimize_hir(hir)
|
|
275
|
+
log "Optimizing HIR..."
|
|
276
|
+
|
|
277
|
+
# Apply monomorphization
|
|
278
|
+
if @hm_inferrer
|
|
279
|
+
log " - Monomorphization"
|
|
280
|
+
@monomorphizer = Codegen::Monomorphizer.new(hir, @hm_inferrer)
|
|
281
|
+
@monomorphizer.analyze
|
|
282
|
+
@monomorphizer.transform
|
|
283
|
+
|
|
284
|
+
if verbose && !@monomorphizer.specializations.empty?
|
|
285
|
+
log " Generated specializations:"
|
|
286
|
+
@monomorphizer.specializations.each do |(func, types), name|
|
|
287
|
+
log " #{func}(#{types.join(', ')}) -> #{name}"
|
|
288
|
+
end
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
if verbose && !@monomorphizer.union_dispatches.empty?
|
|
292
|
+
log " Union type dispatches:"
|
|
293
|
+
@monomorphizer.union_dispatches.each do |(_target, _types), info|
|
|
294
|
+
log " #{info[:target]}(#{info[:original_types].map(&:to_s).join(', ')}):"
|
|
295
|
+
info[:specializations].each do |concrete_types, specialized_name|
|
|
296
|
+
log " -> #{specialized_name} for (#{concrete_types.join(', ')})"
|
|
297
|
+
end
|
|
298
|
+
end
|
|
299
|
+
end
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
@_specialization_count = @monomorphizer ? @monomorphizer.specializations.size : 0
|
|
303
|
+
|
|
304
|
+
# Apply inlining
|
|
305
|
+
log " - Inlining"
|
|
306
|
+
inliner = Codegen::Inliner.new(hir)
|
|
307
|
+
inliner.optimize
|
|
308
|
+
@_inlined_count = inliner.inlined_count
|
|
309
|
+
|
|
310
|
+
if verbose && inliner.inlined_count > 0
|
|
311
|
+
log " Inlined #{inliner.inlined_count} call site(s)"
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
# Apply loop optimizations (LICM)
|
|
315
|
+
log " - Loop optimization"
|
|
316
|
+
loop_optimizer = Codegen::LoopOptimizer.new(hir)
|
|
317
|
+
loop_optimizer.optimize
|
|
318
|
+
@_hoisted_count = loop_optimizer.hoisted_count
|
|
319
|
+
|
|
320
|
+
if verbose && loop_optimizer.hoisted_count > 0
|
|
321
|
+
log " Hoisted #{loop_optimizer.hoisted_count} loop-invariant instruction(s)"
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
hir
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
def resolve_types(hir)
|
|
328
|
+
require_relative "type_checker/type_resolver"
|
|
329
|
+
|
|
330
|
+
log "Resolving types..."
|
|
331
|
+
|
|
332
|
+
# Collect JVM interop class info from rbs_loader if available
|
|
333
|
+
jvm_interop_classes = {}
|
|
334
|
+
if @rbs_loader && @rbs_loader.respond_to?(:jvm_classes)
|
|
335
|
+
jvm_interop_classes = @rbs_loader.jvm_classes || {}
|
|
336
|
+
end
|
|
337
|
+
|
|
338
|
+
resolver = TypeChecker::TypeResolver.new(
|
|
339
|
+
hir,
|
|
340
|
+
hm_inferrer: @hm_inferrer,
|
|
341
|
+
rbs_loader: @rbs_loader,
|
|
342
|
+
jvm_interop_classes: jvm_interop_classes,
|
|
343
|
+
monomorphizer: @monomorphizer
|
|
344
|
+
)
|
|
345
|
+
|
|
346
|
+
unless resolver.resolve!
|
|
347
|
+
@type_resolution_errors = resolver.errors
|
|
348
|
+
if @verbose
|
|
349
|
+
resolver.errors.each { |e| puts " #{e}" }
|
|
350
|
+
puts ""
|
|
351
|
+
puts " Type inference could not determine the receiver type."
|
|
352
|
+
puts " These methods may not work correctly if called at runtime."
|
|
353
|
+
puts ""
|
|
354
|
+
end
|
|
355
|
+
end
|
|
356
|
+
end
|
|
357
|
+
|
|
358
|
+
def generate_code(hir)
|
|
359
|
+
if @target == :jvm
|
|
360
|
+
generate_jvm(hir)
|
|
361
|
+
else
|
|
362
|
+
log "Generating #{format} code..."
|
|
363
|
+
case format
|
|
364
|
+
when :cruby_ext
|
|
365
|
+
generate_cruby_extension(hir)
|
|
366
|
+
when :standalone
|
|
367
|
+
generate_standalone(hir)
|
|
368
|
+
else
|
|
369
|
+
raise CodegenError, "Unknown format: #{format}"
|
|
370
|
+
end
|
|
371
|
+
end
|
|
372
|
+
end
|
|
373
|
+
|
|
374
|
+
def generate_cruby_extension(hir)
|
|
375
|
+
log "Generating LLVM IR..."
|
|
376
|
+
|
|
377
|
+
# Detect if HIR uses JSON parse_as
|
|
378
|
+
uses_json_parse_as = hir_uses_json_parse_as?(hir)
|
|
379
|
+
log " - JSON parse_as detected" if uses_json_parse_as && verbose
|
|
380
|
+
|
|
381
|
+
# Generate LLVM IR with monomorphization support
|
|
382
|
+
llvm_gen = Codegen::LLVMGenerator.new(
|
|
383
|
+
module_name: module_name,
|
|
384
|
+
monomorphizer: @monomorphizer,
|
|
385
|
+
rbs_loader: @rbs_loader,
|
|
386
|
+
debug: @debug,
|
|
387
|
+
profile: @profile,
|
|
388
|
+
source_file: source_file
|
|
389
|
+
)
|
|
390
|
+
llvm_gen.generate(hir)
|
|
391
|
+
|
|
392
|
+
log "Compiling to native code..."
|
|
393
|
+
|
|
394
|
+
# Compile to .so/.bundle
|
|
395
|
+
backend = Codegen::CRubyBackend.new(
|
|
396
|
+
llvm_gen,
|
|
397
|
+
output_file: output_file,
|
|
398
|
+
module_name: module_name,
|
|
399
|
+
rbs_loader: @rbs_loader,
|
|
400
|
+
stdlib_requires: @stdlib_requires,
|
|
401
|
+
runtime_native_extensions: @runtime_native_extensions || [],
|
|
402
|
+
debug: @debug,
|
|
403
|
+
profile: @profile,
|
|
404
|
+
uses_json_parse_as: uses_json_parse_as
|
|
405
|
+
)
|
|
406
|
+
backend.generate
|
|
407
|
+
|
|
408
|
+
log "Generated: #{output_file}"
|
|
409
|
+
end
|
|
410
|
+
|
|
411
|
+
def generate_standalone(hir)
|
|
412
|
+
raise CodegenError, "Standalone generation not yet implemented"
|
|
413
|
+
end
|
|
414
|
+
|
|
415
|
+
def generate_jvm(hir)
|
|
416
|
+
require_relative "codegen/jvm_generator"
|
|
417
|
+
require_relative "codegen/jvm_backend"
|
|
418
|
+
|
|
419
|
+
@jvm_gen = Codegen::JVMGenerator.new(
|
|
420
|
+
module_name: module_name,
|
|
421
|
+
monomorphizer: @monomorphizer,
|
|
422
|
+
rbs_loader: @rbs_loader,
|
|
423
|
+
verbose: @verbose
|
|
424
|
+
)
|
|
425
|
+
@jvm_gen.generate(hir)
|
|
426
|
+
|
|
427
|
+
# TypeVar fallbacks in codegen — unresolved types use invokedynamic for
|
|
428
|
+
# runtime method resolution. Report as warning, not error.
|
|
429
|
+
if @jvm_gen.typevar_fallback_count > 0
|
|
430
|
+
warn_msg = "#{@jvm_gen.typevar_fallback_count} unresolved type(s) — using invokedynamic fallback."
|
|
431
|
+
log warn_msg
|
|
432
|
+
end
|
|
433
|
+
|
|
434
|
+
if @jvm_gen.rbs_fallback_count > 0
|
|
435
|
+
log "Type resolution: #{@jvm_gen.rbs_fallback_count} RBS fallbacks (HM inference gaps)"
|
|
436
|
+
end
|
|
437
|
+
|
|
438
|
+
jvm_gen = @jvm_gen
|
|
439
|
+
|
|
440
|
+
backend = Codegen::JVMBackend.new(
|
|
441
|
+
jvm_gen,
|
|
442
|
+
output_file: output_file,
|
|
443
|
+
module_name: module_name,
|
|
444
|
+
run_after: @run_after,
|
|
445
|
+
emit_ir: @emit_ir,
|
|
446
|
+
classpath: @classpath,
|
|
447
|
+
library: @library
|
|
448
|
+
)
|
|
449
|
+
backend.generate
|
|
450
|
+
|
|
451
|
+
log "Generated: #{output_file}"
|
|
452
|
+
end
|
|
453
|
+
|
|
454
|
+
def hir_uses_json_parse_as?(hir)
|
|
455
|
+
hir.functions.any? do |func|
|
|
456
|
+
func.body.any? do |bb|
|
|
457
|
+
bb.instructions.any? do |instr|
|
|
458
|
+
instr.is_a?(HIR::JSONParseAs) || instr.is_a?(HIR::JSONParseArrayAs)
|
|
459
|
+
end
|
|
460
|
+
end
|
|
461
|
+
end
|
|
462
|
+
end
|
|
463
|
+
|
|
464
|
+
def module_name
|
|
465
|
+
@module_name ||= File.basename(source_file, ".rb").gsub(/[^a-zA-Z0-9_]/, "_")
|
|
466
|
+
end
|
|
467
|
+
|
|
468
|
+
def default_output_file(target: :native)
|
|
469
|
+
base = File.basename(source_file, ".rb")
|
|
470
|
+
if target == :jvm
|
|
471
|
+
"#{base}.jar"
|
|
472
|
+
else
|
|
473
|
+
case format
|
|
474
|
+
when :cruby_ext
|
|
475
|
+
"#{base}#{Platform.shared_lib_extension}"
|
|
476
|
+
when :standalone
|
|
477
|
+
base
|
|
478
|
+
else
|
|
479
|
+
base
|
|
480
|
+
end
|
|
481
|
+
end
|
|
482
|
+
end
|
|
483
|
+
|
|
484
|
+
def log(message)
|
|
485
|
+
puts message if verbose
|
|
486
|
+
end
|
|
487
|
+
|
|
488
|
+
# Scan AST for Java:: constant references and constant aliases.
|
|
489
|
+
# Returns { refs: { "Java::X::Y" => "x/Y" }, aliases: { "KCanvas" => "Java::X::Y" } }
|
|
490
|
+
def scan_java_references(ast)
|
|
491
|
+
refs = {}
|
|
492
|
+
aliases = {}
|
|
493
|
+
scan_node_for_java_refs(ast, refs, aliases)
|
|
494
|
+
{ refs: refs, aliases: aliases }
|
|
495
|
+
end
|
|
496
|
+
|
|
497
|
+
def scan_node_for_java_refs(node, refs, aliases)
|
|
498
|
+
return unless node
|
|
499
|
+
|
|
500
|
+
case node
|
|
501
|
+
when Prism::ConstantPathNode
|
|
502
|
+
full_name = extract_constant_path_name(node)
|
|
503
|
+
if full_name&.start_with?("Java::")
|
|
504
|
+
refs[full_name] = ruby_path_to_jvm_internal(full_name) unless refs.key?(full_name)
|
|
505
|
+
end
|
|
506
|
+
when Prism::ConstantWriteNode
|
|
507
|
+
# KCanvas = Java::Konpeito::Canvas::Canvas
|
|
508
|
+
value_node = node.value
|
|
509
|
+
if value_node.is_a?(Prism::ConstantPathNode)
|
|
510
|
+
full_name = extract_constant_path_name(value_node)
|
|
511
|
+
if full_name&.start_with?("Java::")
|
|
512
|
+
refs[full_name] = ruby_path_to_jvm_internal(full_name) unless refs.key?(full_name)
|
|
513
|
+
aliases[node.name.to_s] = full_name
|
|
514
|
+
end
|
|
515
|
+
end
|
|
516
|
+
end
|
|
517
|
+
|
|
518
|
+
# Recurse into child nodes
|
|
519
|
+
children = if node.respond_to?(:child_nodes)
|
|
520
|
+
node.child_nodes
|
|
521
|
+
elsif node.respond_to?(:compact_child_nodes)
|
|
522
|
+
node.compact_child_nodes
|
|
523
|
+
else
|
|
524
|
+
[]
|
|
525
|
+
end
|
|
526
|
+
children.each { |child| scan_node_for_java_refs(child, refs, aliases) }
|
|
527
|
+
end
|
|
528
|
+
|
|
529
|
+
def extract_constant_path_name(node)
|
|
530
|
+
parts = []
|
|
531
|
+
current = node
|
|
532
|
+
while current.is_a?(Prism::ConstantPathNode)
|
|
533
|
+
parts.unshift(current.name.to_s)
|
|
534
|
+
current = current.parent
|
|
535
|
+
end
|
|
536
|
+
parts.unshift(current.name.to_s) if current.respond_to?(:name)
|
|
537
|
+
parts.join("::")
|
|
538
|
+
end
|
|
539
|
+
|
|
540
|
+
# Convert "Java::Konpeito::Canvas::Canvas" → "konpeito/canvas/Canvas"
|
|
541
|
+
def ruby_path_to_jvm_internal(ruby_path)
|
|
542
|
+
segments = ruby_path.delete_prefix("Java::").split("::")
|
|
543
|
+
segments.each_with_index.map { |s, i|
|
|
544
|
+
i < segments.size - 1 ? s[0].downcase + s[1..] : s
|
|
545
|
+
}.join("/")
|
|
546
|
+
end
|
|
547
|
+
|
|
548
|
+
# Incremental compilation support
|
|
549
|
+
|
|
550
|
+
def setup_cache
|
|
551
|
+
require_relative "cache"
|
|
552
|
+
|
|
553
|
+
@cache_manager = Cache::CacheManager.new
|
|
554
|
+
|
|
555
|
+
if @clean_cache
|
|
556
|
+
log "Clearing compilation cache..."
|
|
557
|
+
@cache_manager.clean!
|
|
558
|
+
end
|
|
559
|
+
end
|
|
560
|
+
|
|
561
|
+
def save_cache
|
|
562
|
+
return unless @cache_manager
|
|
563
|
+
|
|
564
|
+
@cache_manager.save_manifest
|
|
565
|
+
log "Cache saved." if verbose
|
|
566
|
+
end
|
|
567
|
+
|
|
568
|
+
def cache_ast(path, ast)
|
|
569
|
+
return unless @cache_manager
|
|
570
|
+
|
|
571
|
+
@cache_manager.put_ast(path, ast)
|
|
572
|
+
end
|
|
573
|
+
|
|
574
|
+
def get_cached_ast(path)
|
|
575
|
+
return nil unless @cache_manager
|
|
576
|
+
|
|
577
|
+
@cache_manager.get_ast(path)
|
|
578
|
+
end
|
|
579
|
+
|
|
580
|
+
def cache_needs_recompile?(path)
|
|
581
|
+
return true unless @cache_manager
|
|
582
|
+
|
|
583
|
+
@cache_manager.needs_recompile?(path)
|
|
584
|
+
end
|
|
585
|
+
|
|
586
|
+
def register_dependency(from, to)
|
|
587
|
+
return unless @cache_manager
|
|
588
|
+
|
|
589
|
+
@cache_manager.add_dependency(from, to)
|
|
590
|
+
end
|
|
591
|
+
end
|
|
592
|
+
end
|