ryac 0.2.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/LICENSE +21 -0
- data/README.md +145 -0
- data/Rakefile +45 -0
- data/Steepfile +9 -0
- data/bin/console +11 -0
- data/bin/ryac +134 -0
- data/bin/setup +8 -0
- data/lib/ryac/analysis/constant/collection.rb +332 -0
- data/lib/ryac/analysis/constant/rename_mapping.rb +346 -0
- data/lib/ryac/analysis/cvar/collection.rb +37 -0
- data/lib/ryac/analysis/gvar/collection.rb +36 -0
- data/lib/ryac/analysis/gvar/rename_mapping.rb +82 -0
- data/lib/ryac/analysis/ivar/collection.rb +281 -0
- data/lib/ryac/analysis/keyword/collection.rb +213 -0
- data/lib/ryac/analysis/keyword/rename_mapping.rb +167 -0
- data/lib/ryac/analysis/local_scopes.rb +582 -0
- data/lib/ryac/analysis/method/collection.rb +298 -0
- data/lib/ryac/analysis/method/rename_mapping.rb +337 -0
- data/lib/ryac/analysis/method_aliases.rb +83 -0
- data/lib/ryac/analysis/method_aliasing.rb +65 -0
- data/lib/ryac/analysis/nesting.rb +123 -0
- data/lib/ryac/analysis/scope_management.rb +25 -0
- data/lib/ryac/analysis/site_bucket_mapping.rb +127 -0
- data/lib/ryac/analysis/type_oracle.rb +386 -0
- data/lib/ryac/ast_utils.rb +321 -0
- data/lib/ryac/errors.rb +32 -0
- data/lib/ryac/gem_resolver.rb +55 -0
- data/lib/ryac/minifier.rb +169 -0
- data/lib/ryac/name_generator.rb +68 -0
- data/lib/ryac/pipeline/analyzer.rb +264 -0
- data/lib/ryac/pipeline/attr_decl_shorten.rb +61 -0
- data/lib/ryac/pipeline/boolean_shorten.rb +26 -0
- data/lib/ryac/pipeline/char_shorten.rb +20 -0
- data/lib/ryac/pipeline/compactor.rb +1202 -0
- data/lib/ryac/pipeline/concatenator.rb +248 -0
- data/lib/ryac/pipeline/constant_aliaser.rb +240 -0
- data/lib/ryac/pipeline/constant_fold.rb +93 -0
- data/lib/ryac/pipeline/control_flow_simplify.rb +352 -0
- data/lib/ryac/pipeline/data_types.rb +179 -0
- data/lib/ryac/pipeline/endless_method.rb +60 -0
- data/lib/ryac/pipeline/errors.rb +94 -0
- data/lib/ryac/pipeline/file_collector.rb +360 -0
- data/lib/ryac/pipeline/method_renamer.rb +151 -0
- data/lib/ryac/pipeline/paren_optimizer.rb +161 -0
- data/lib/ryac/pipeline/rename_patcher.rb +274 -0
- data/lib/ryac/pipeline/source_patcher.rb +54 -0
- data/lib/ryac/pipeline/spelling_shorten.rb +60 -0
- data/lib/ryac/pipeline/stage.rb +72 -0
- data/lib/ryac/pipeline/stage_runner.rb +84 -0
- data/lib/ryac/pipeline/variable_renamer.rb +125 -0
- data/lib/ryac/union_find.rb +42 -0
- data/lib/ryac/version.rb +5 -0
- data/lib/ryac.rb +29 -0
- data/rbs_collection.lock.yaml +224 -0
- data/rbs_collection.yaml +25 -0
- data/sig/ryac/analysis/constant/collection.rbs +30 -0
- data/sig/ryac/analysis/constant/rename_mapping.rbs +60 -0
- data/sig/ryac/analysis/cvar/collection.rbs +11 -0
- data/sig/ryac/analysis/gvar/collection.rbs +9 -0
- data/sig/ryac/analysis/gvar/rename_mapping.rbs +23 -0
- data/sig/ryac/analysis/ivar/collection.rbs +26 -0
- data/sig/ryac/analysis/keyword/collection.rbs +27 -0
- data/sig/ryac/analysis/keyword/rename_mapping.rbs +31 -0
- data/sig/ryac/analysis/local_scopes.rbs +77 -0
- data/sig/ryac/analysis/method/collection.rbs +25 -0
- data/sig/ryac/analysis/method/rename_mapping.rbs +82 -0
- data/sig/ryac/analysis/method_aliases.rbs +10 -0
- data/sig/ryac/analysis/method_aliasing.rbs +9 -0
- data/sig/ryac/analysis/nesting.rbs +9 -0
- data/sig/ryac/analysis/scope_management.rbs +5 -0
- data/sig/ryac/analysis/site_bucket_mapping.rbs +26 -0
- data/sig/ryac/analysis/type_oracle.rbs +61 -0
- data/sig/ryac/ast_utils.rbs +34 -0
- data/sig/ryac/errors.rbs +21 -0
- data/sig/ryac/gem_resolver.rbs +28 -0
- data/sig/ryac/minifier.rbs +33 -0
- data/sig/ryac/name_generator.rbs +14 -0
- data/sig/ryac/pipeline/analyzer.rbs +34 -0
- data/sig/ryac/pipeline/attr_decl_shorten.rbs +17 -0
- data/sig/ryac/pipeline/boolean_shorten.rbs +11 -0
- data/sig/ryac/pipeline/char_shorten.rbs +11 -0
- data/sig/ryac/pipeline/compactor.rbs +125 -0
- data/sig/ryac/pipeline/concatenator.rbs +28 -0
- data/sig/ryac/pipeline/constant_aliaser.rbs +35 -0
- data/sig/ryac/pipeline/constant_fold.rbs +20 -0
- data/sig/ryac/pipeline/control_flow_simplify.rbs +34 -0
- data/sig/ryac/pipeline/data_types.rbs +151 -0
- data/sig/ryac/pipeline/endless_method.rbs +14 -0
- data/sig/ryac/pipeline/errors.rbs +46 -0
- data/sig/ryac/pipeline/file_collector.rbs +34 -0
- data/sig/ryac/pipeline/method_renamer.rbs +25 -0
- data/sig/ryac/pipeline/paren_optimizer.rbs +16 -0
- data/sig/ryac/pipeline/rename_patcher.rbs +38 -0
- data/sig/ryac/pipeline/source_patcher.rbs +12 -0
- data/sig/ryac/pipeline/spelling_shorten.rbs +14 -0
- data/sig/ryac/pipeline/stage.rbs +28 -0
- data/sig/ryac/pipeline/stage_runner.rbs +18 -0
- data/sig/ryac/pipeline/variable_renamer.rbs +21 -0
- data/sig/ryac/union_find.rbs +17 -0
- data/sig/ryac.rbs +13 -0
- data/sig/shims/env.rbs +19 -0
- data/sig/shims/typeprof/core/ast.rbs +299 -0
- data/sig/shims/typeprof/core/type.rbs +22 -0
- data/sig/shims/typeprof/core.rbs +73 -0
- data/tools/regen_pins.rb +45 -0
- metadata +201 -0
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ryac
|
|
4
|
+
class KeywordRenameMapping
|
|
5
|
+
include UnionFind
|
|
6
|
+
|
|
7
|
+
def initialize
|
|
8
|
+
uf_init
|
|
9
|
+
@methods = {}
|
|
10
|
+
@node_short_names = {}
|
|
11
|
+
@keyword_maps = {}
|
|
12
|
+
@frozen = false
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def add_keyword_def(method_key, keyword_sym)
|
|
16
|
+
init_method(method_key)
|
|
17
|
+
@methods[method_key][:keywords] << keyword_sym
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def add_keyword_call(method_key, keyword_sym, symbol_node, val_node)
|
|
21
|
+
init_method(method_key)
|
|
22
|
+
@methods[method_key][:call_entries][keyword_sym] ||= []
|
|
23
|
+
@methods[method_key][:call_entries][keyword_sym] << { symbol_node: symbol_node, val_node: val_node }
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def exclude_method(method_key)
|
|
27
|
+
init_method(method_key)
|
|
28
|
+
root = uf_root(method_key)
|
|
29
|
+
@methods.each_key do |k|
|
|
30
|
+
@methods[k][:excluded] = true if uf_root(k) == root
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def exclude_methods_by_mid(mids)
|
|
35
|
+
@methods.keys.each do |key|
|
|
36
|
+
exclude_method(key) if mids.include?(key[2])
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def merge_groups(key1, key2)
|
|
41
|
+
init_method(key1)
|
|
42
|
+
init_method(key2)
|
|
43
|
+
super
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def each_method_key(&block)
|
|
47
|
+
@methods.each_key(&block)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def assign_short_names
|
|
51
|
+
groups = Hash.new { |h, k| h[k] = [] } #: Hash[method_key, Array[method_key]]
|
|
52
|
+
@methods.each_key { |key| groups[uf_root(key)] << key }
|
|
53
|
+
|
|
54
|
+
groups.each do |_root, keys|
|
|
55
|
+
next if keys.any? { |k| @methods[k][:excluded] }
|
|
56
|
+
|
|
57
|
+
all_keywords = keys.flat_map { |k| @methods[k][:keywords].to_a }.uniq
|
|
58
|
+
total_call_entries = keys.sum { |k| @methods[k][:call_entries].values.sum(&:size) }
|
|
59
|
+
next if total_call_entries == 0
|
|
60
|
+
|
|
61
|
+
generator = NameGenerator.new
|
|
62
|
+
keyword_map = {} #: Hash[Symbol, String]
|
|
63
|
+
occurrences_by_sym = Hash.new(0) #: Hash[Symbol, Integer]
|
|
64
|
+
keys.each do |k|
|
|
65
|
+
info = @methods[k]
|
|
66
|
+
info[:keywords].each { |sym| occurrences_by_sym[sym] += 1 }
|
|
67
|
+
info[:call_entries].each { |sym, entries| occurrences_by_sym[sym] += entries.size }
|
|
68
|
+
end
|
|
69
|
+
# Sorted by total bytes at stake — the same greedy order every other
|
|
70
|
+
# rename family uses, so the shortest names go where they pay most.
|
|
71
|
+
all_keywords.sort_by { |sym| -(sym.to_s.size * occurrences_by_sym[sym]) }.each do |sym|
|
|
72
|
+
next if sym.to_s.size <= NameGenerator::KEPT_NAME_MAX
|
|
73
|
+
|
|
74
|
+
short = generator.next_name
|
|
75
|
+
savings = (sym.to_s.size - short.size) * occurrences_by_sym[sym]
|
|
76
|
+
next unless savings > MethodRenameMapping::MIN_GROUP_SAVINGS
|
|
77
|
+
|
|
78
|
+
keyword_map[sym] = short
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
next if keyword_map.empty?
|
|
82
|
+
|
|
83
|
+
@keyword_maps[_root] = keyword_map
|
|
84
|
+
|
|
85
|
+
keys.each do |key|
|
|
86
|
+
@methods[key][:call_entries].each do |sym, entries|
|
|
87
|
+
short = keyword_map[sym]
|
|
88
|
+
next unless short
|
|
89
|
+
entries.each { |e| @node_short_names[AstUtils.location_key(e[:symbol_node])] = short }
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
@frozen = true
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def node_mapping
|
|
98
|
+
@node_short_names.dup
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Keyed by the def's location, the coordinate the scope analysis works in.
|
|
102
|
+
def def_node_mapping(def_node_registry)
|
|
103
|
+
result = {} #: Hash[location_key, Hash[Symbol, String]]
|
|
104
|
+
def_node_registry.each do |method_key, def_nodes|
|
|
105
|
+
root = uf_root(method_key)
|
|
106
|
+
keyword_map = @keyword_maps[root]
|
|
107
|
+
next unless keyword_map
|
|
108
|
+
|
|
109
|
+
next if @methods[method_key]&.[](:excluded)
|
|
110
|
+
|
|
111
|
+
def_nodes.each do |def_node|
|
|
112
|
+
mapping = {} #: Hash[Symbol, String]
|
|
113
|
+
keyword_map.each do |sym, short|
|
|
114
|
+
mapping[sym] = short
|
|
115
|
+
end
|
|
116
|
+
result[AstUtils.location_key(def_node)] = mapping unless mapping.empty?
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
result
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# A call passing a local as a keyword value — `f(code: c)` — pays nothing
|
|
123
|
+
# when the local is named like the keyword, because the shorthand form
|
|
124
|
+
# `f(code:)` applies. These hints suggest that name to the local's scope.
|
|
125
|
+
#
|
|
126
|
+
# The scope containing the call site is the caller's concern: pass a block
|
|
127
|
+
# mapping a call-argument node to its scope id.
|
|
128
|
+
def build_variable_hints
|
|
129
|
+
hints = {} #: scope_mapping_table
|
|
130
|
+
|
|
131
|
+
@methods.each do |method_key, info|
|
|
132
|
+
next if info[:excluded]
|
|
133
|
+
root = uf_root(method_key)
|
|
134
|
+
keyword_map = @keyword_maps[root] || {}
|
|
135
|
+
|
|
136
|
+
info[:call_entries].each do |keyword_sym, entries|
|
|
137
|
+
# Use renamed name if available; for already-short keywords,
|
|
138
|
+
# use original name to preserve idempotency across re-minification passes
|
|
139
|
+
final_name = keyword_map[keyword_sym]
|
|
140
|
+
final_name ||= keyword_sym.to_s if keyword_sym.to_s.size <= NameGenerator::KEPT_NAME_MAX
|
|
141
|
+
next unless final_name
|
|
142
|
+
|
|
143
|
+
entries.each do |entry|
|
|
144
|
+
val_node = entry[:val_node]
|
|
145
|
+
next unless val_node.is_a?(Prism::LocalVariableReadNode)
|
|
146
|
+
|
|
147
|
+
scope_id = yield(val_node)
|
|
148
|
+
next unless scope_id
|
|
149
|
+
|
|
150
|
+
hints[scope_id] ||= {}
|
|
151
|
+
hints[scope_id][val_node.name] ||= final_name
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
hints
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
private
|
|
160
|
+
|
|
161
|
+
def init_method(method_key)
|
|
162
|
+
return if @methods.key?(method_key)
|
|
163
|
+
@methods[method_key] = { keywords: Set.new, call_entries: {}, excluded: false }
|
|
164
|
+
uf_add(method_key)
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
end
|