@ball-lang/cli 1.42.0 → 1.43.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.
@@ -648,6 +648,24 @@ export function hasBoolValue(obj: any): boolean { return _has(obj, 'boolValue');
648
648
  export function hasNumberValue(obj: any): boolean { return _has(obj, 'numberValue'); }
649
649
  export function hasResult(obj: any): boolean { return _has(obj, 'result'); }
650
650
  export function hasCall(obj: any): boolean { return _has(obj, 'call'); }
651
+ // Statement oneof presence (let, expression) + FieldAccess object presence,
652
+ // issue 362. The self-hosted ball audit analyzers and engine.dart read these
653
+ // via hasLet, hasExpression, hasObject; they route to ball_proto and compile to
654
+ // free calls, so they need top-level definitions here (mirrors hasHttp etc.).
655
+ export function hasLet(obj: any): boolean { return _has(obj, 'let'); }
656
+ export function hasExpression(obj: any): boolean { return _has(obj, 'expression'); }
657
+ export function hasObject(obj: any): boolean { return _has(obj, 'object'); }
658
+ // Expression oneof presence (issue 362): the self-hosted ball audit
659
+ // capability/termination analyzers dispatch expression kinds via a hasX()
660
+ // presence cascade (hasCall/hasLiteral/hasBlock/hasLambda/hasMessageCreation/
661
+ // hasFieldAccess/hasReference) instead of the whichExpr() enum, so these route
662
+ // to ball_proto and compile to free calls needing top-level definitions here.
663
+ export function hasLiteral(obj: any): boolean { return _has(obj, 'literal'); }
664
+ export function hasBlock(obj: any): boolean { return _has(obj, 'block'); }
665
+ export function hasLambda(obj: any): boolean { return _has(obj, 'lambda'); }
666
+ export function hasMessageCreation(obj: any): boolean { return _has(obj, 'messageCreation'); }
667
+ export function hasFieldAccess(obj: any): boolean { return _has(obj, 'fieldAccess'); }
668
+ export function hasReference(obj: any): boolean { return _has(obj, 'reference'); }
651
669
  export function hasListValue(obj: any): boolean { return _has(obj, 'listValue'); }
652
670
  export function hasNullValue(obj: any): boolean { return _has(obj, 'nullValue'); }
653
671
  export function hasStructValue(obj: any): boolean { return _has(obj, 'structValue'); }
@@ -1715,14 +1733,12 @@ export function _importSource(imp: any): any {
1715
1733
  export function auditReport(program: any): any {
1716
1734
  const input = program;
1717
1735
  let report = analyzeCapabilities(program);
1718
- let buf = "";
1719
- buf.writeln(formatCapabilityReport(report));
1720
- let termReport = analyzeTermination(program);
1721
- if (!(termReport.warnings.length === 0)) {
1722
- buf.writeln('');
1723
- buf.writeln(formatTerminationReport(termReport));
1736
+ let out = (__ball_to_string(formatCapabilityReport(report)) + '\n');
1737
+ let termWarnings = analyzeTermination(program);
1738
+ if (!(termWarnings.length === 0)) {
1739
+ out = (((__ball_to_string(out) + '\n') + __ball_to_string(formatTerminationReport(termWarnings))) + '\n');
1724
1740
  }
1725
- return __ball_to_string(buf);
1741
+ return out;
1726
1742
  }
1727
1743
 
1728
1744
  export function _allBase(functions: any): any {
@@ -1734,3 +1750,1551 @@ export function _allBase(functions: any): any {
1734
1750
  }
1735
1751
  return true;
1736
1752
  }
1753
+
1754
+ export function capabilityNames(): any {
1755
+ return ['pure', 'io', 'fs', 'process', 'time', 'random', 'memory', 'concurrency', 'network', 'async'];
1756
+ }
1757
+
1758
+ export function capabilityRisk(capability: any): any {
1759
+ const input = capability;
1760
+ if (__ball_eq(capability, 'pure')) {
1761
+ return 'none';
1762
+ }
1763
+ if (__ball_eq(capability, 'io')) {
1764
+ return 'low';
1765
+ }
1766
+ if (__ball_eq(capability, 'fs')) {
1767
+ return 'medium';
1768
+ }
1769
+ if (__ball_eq(capability, 'process')) {
1770
+ return 'high';
1771
+ }
1772
+ if (__ball_eq(capability, 'time')) {
1773
+ return 'low';
1774
+ }
1775
+ if (__ball_eq(capability, 'random')) {
1776
+ return 'low';
1777
+ }
1778
+ if (__ball_eq(capability, 'memory')) {
1779
+ return 'high';
1780
+ }
1781
+ if (__ball_eq(capability, 'concurrency')) {
1782
+ return 'medium';
1783
+ }
1784
+ if (__ball_eq(capability, 'network')) {
1785
+ return 'high';
1786
+ }
1787
+ if (__ball_eq(capability, 'async')) {
1788
+ return 'low';
1789
+ }
1790
+ return 'none';
1791
+ }
1792
+
1793
+ export function lookupCapability(table: any, module: any, function_: any): any {
1794
+ let key = ((__ball_to_string(module) + '.') + __ball_to_string(function_));
1795
+ if ((key in __ball_require_map(table, 'map_contains_key'))) {
1796
+ return __ball_index(table, key);
1797
+ }
1798
+ return '';
1799
+ }
1800
+
1801
+ export function capabilityModuleNames(): any {
1802
+ return ['std', 'std_io', 'std_fs', 'std_collections', 'std_convert', 'std_time', 'std_memory', 'std_concurrency'];
1803
+ }
1804
+
1805
+ export function lookupCapabilityByName(table: any, function_: any): any {
1806
+ let modules = capabilityModuleNames();
1807
+ for (const m of modules) {
1808
+ let cap = lookupCapability(table, m, function_);
1809
+ if (!(cap.length === 0)) {
1810
+ return cap;
1811
+ }
1812
+ }
1813
+ return '';
1814
+ }
1815
+
1816
+ export function buildCapabilityTable(): any {
1817
+ return { ['std.print']: 'io', ['std.add']: 'pure', ['std.subtract']: 'pure', ['std.multiply']: 'pure', ['std.divide']: 'pure', ['std.divide_double']: 'pure', ['std.modulo']: 'pure', ['std.negate']: 'pure', ['std.equals']: 'pure', ['std.not_equals']: 'pure', ['std.less_than']: 'pure', ['std.greater_than']: 'pure', ['std.lte']: 'pure', ['std.gte']: 'pure', ['std.and']: 'pure', ['std.or']: 'pure', ['std.not']: 'pure', ['std.bitwise_and']: 'pure', ['std.bitwise_or']: 'pure', ['std.bitwise_xor']: 'pure', ['std.bitwise_not']: 'pure', ['std.left_shift']: 'pure', ['std.right_shift']: 'pure', ['std.unsigned_right_shift']: 'pure', ['std.pre_increment']: 'pure', ['std.pre_decrement']: 'pure', ['std.post_increment']: 'pure', ['std.post_decrement']: 'pure', ['std.concat']: 'pure', ['std.length']: 'pure', ['std.to_string']: 'pure', ['std.int_to_string']: 'pure', ['std.double_to_string']: 'pure', ['std.string_to_int']: 'pure', ['std.string_to_double']: 'pure', ['std.null_coalesce']: 'pure', ['std.null_check']: 'pure', ['std.if']: 'pure', ['std.for']: 'pure', ['std.for_in']: 'pure', ['std.while']: 'pure', ['std.do_while']: 'pure', ['std.switch']: 'pure', ['std.try']: 'pure', ['std.throw']: 'pure', ['std.rethrow']: 'pure', ['std.assert']: 'pure', ['std.return']: 'pure', ['std.break']: 'pure', ['std.continue']: 'pure', ['std.yield']: 'async', ['std.yield_each']: 'async', ['std.await']: 'async', ['std.async']: 'async', ['std.assign']: 'pure', ['std.compound_assign']: 'pure', ['std.is']: 'pure', ['std.is_not']: 'pure', ['std.as']: 'pure', ['std.index']: 'pure', ['std.index_assign']: 'pure', ['std.labeled']: 'pure', ['std.label']: 'pure', ['std.goto']: 'pure', ['std.paren']: 'pure', ['std.string_length']: 'pure', ['std.string_is_empty']: 'pure', ['std.string_concat']: 'pure', ['std.string_contains']: 'pure', ['std.string_starts_with']: 'pure', ['std.string_ends_with']: 'pure', ['std.string_index_of']: 'pure', ['std.string_last_index_of']: 'pure', ['std.string_substring']: 'pure', ['std.string_char_at']: 'pure', ['std.string_char_code_at']: 'pure', ['std.string_from_char_code']: 'pure', ['std.string_to_upper']: 'pure', ['std.string_to_lower']: 'pure', ['std.string_trim']: 'pure', ['std.string_trim_start']: 'pure', ['std.string_trim_end']: 'pure', ['std.string_replace']: 'pure', ['std.string_replace_all']: 'pure', ['std.string_split']: 'pure', ['std.string_repeat']: 'pure', ['std.string_pad_left']: 'pure', ['std.string_pad_right']: 'pure', ['std.string_interpolation']: 'pure', ['std.regex_match']: 'pure', ['std.regex_find']: 'pure', ['std.regex_find_all']: 'pure', ['std.regex_replace']: 'pure', ['std.regex_replace_all']: 'pure', ['std.math_abs']: 'pure', ['std.math_floor']: 'pure', ['std.math_ceil']: 'pure', ['std.math_round']: 'pure', ['std.math_trunc']: 'pure', ['std.math_sqrt']: 'pure', ['std.math_pow']: 'pure', ['std.math_log']: 'pure', ['std.math_log2']: 'pure', ['std.math_log10']: 'pure', ['std.math_exp']: 'pure', ['std.math_sin']: 'pure', ['std.math_cos']: 'pure', ['std.math_tan']: 'pure', ['std.math_asin']: 'pure', ['std.math_acos']: 'pure', ['std.math_atan']: 'pure', ['std.math_atan2']: 'pure', ['std.math_min']: 'pure', ['std.math_max']: 'pure', ['std.math_clamp']: 'pure', ['std.math_pi']: 'pure', ['std.math_e']: 'pure', ['std.math_infinity']: 'pure', ['std.math_nan']: 'pure', ['std.math_is_nan']: 'pure', ['std.math_is_finite']: 'pure', ['std.math_is_infinite']: 'pure', ['std.math_sign']: 'pure', ['std.math_gcd']: 'pure', ['std.math_lcm']: 'pure', ['std_io.print_error']: 'io', ['std_io.read_line']: 'io', ['std_io.exit']: 'process', ['std_io.panic']: 'process', ['std_io.sleep_ms']: 'time', ['std_io.timestamp_ms']: 'time', ['std_io.random_int']: 'random', ['std_io.random_double']: 'random', ['std_io.env_get']: 'io', ['std_io.args_get']: 'io', ['std_fs.file_read']: 'fs', ['std_fs.file_read_bytes']: 'fs', ['std_fs.file_write']: 'fs', ['std_fs.file_write_bytes']: 'fs', ['std_fs.file_append']: 'fs', ['std_fs.file_exists']: 'fs', ['std_fs.file_delete']: 'fs', ['std_fs.dir_list']: 'fs', ['std_fs.dir_create']: 'fs', ['std_fs.dir_exists']: 'fs', ['std_collections.list_push']: 'pure', ['std_collections.list_pop']: 'pure', ['std_collections.list_insert']: 'pure', ['std_collections.list_remove_at']: 'pure', ['std_collections.list_get']: 'pure', ['std_collections.list_set']: 'pure', ['std_collections.list_length']: 'pure', ['std_collections.list_is_empty']: 'pure', ['std_collections.list_first']: 'pure', ['std_collections.list_last']: 'pure', ['std_collections.list_single']: 'pure', ['std_collections.list_contains']: 'pure', ['std_collections.list_index_of']: 'pure', ['std_collections.list_map']: 'pure', ['std_collections.list_filter']: 'pure', ['std_collections.list_reduce']: 'pure', ['std_collections.list_find']: 'pure', ['std_collections.list_any']: 'pure', ['std_collections.list_all']: 'pure', ['std_collections.list_none']: 'pure', ['std_collections.list_sort']: 'pure', ['std_collections.list_sort_by']: 'pure', ['std_collections.list_reverse']: 'pure', ['std_collections.list_slice']: 'pure', ['std_collections.list_flat_map']: 'pure', ['std_collections.list_zip']: 'pure', ['std_collections.list_take']: 'pure', ['std_collections.list_drop']: 'pure', ['std_collections.list_concat']: 'pure', ['std_collections.map_get']: 'pure', ['std_collections.map_set']: 'pure', ['std_collections.map_delete']: 'pure', ['std_collections.map_contains_key']: 'pure', ['std_collections.map_keys']: 'pure', ['std_collections.map_values']: 'pure', ['std_collections.map_entries']: 'pure', ['std_collections.map_from_entries']: 'pure', ['std_collections.map_merge']: 'pure', ['std_collections.map_map']: 'pure', ['std_collections.map_filter']: 'pure', ['std_collections.map_is_empty']: 'pure', ['std_collections.map_length']: 'pure', ['std_collections.set_create']: 'pure', ['std_collections.set_add']: 'pure', ['std_collections.set_remove']: 'pure', ['std_collections.set_contains']: 'pure', ['std_collections.set_union']: 'pure', ['std_collections.set_intersection']: 'pure', ['std_collections.set_difference']: 'pure', ['std_collections.set_length']: 'pure', ['std_collections.set_is_empty']: 'pure', ['std_collections.set_to_list']: 'pure', ['std_collections.string_join']: 'pure', ['std_convert.json_encode']: 'pure', ['std_convert.json_decode']: 'pure', ['std_convert.utf8_encode']: 'pure', ['std_convert.utf8_decode']: 'pure', ['std_convert.base64_encode']: 'pure', ['std_convert.base64_decode']: 'pure', ['std_time.now']: 'time', ['std_time.now_micros']: 'time', ['std_time.format_timestamp']: 'time', ['std_time.parse_timestamp']: 'time', ['std_time.duration_add']: 'pure', ['std_time.duration_subtract']: 'pure', ['std_time.year']: 'time', ['std_time.month']: 'time', ['std_time.day']: 'time', ['std_time.hour']: 'time', ['std_time.minute']: 'time', ['std_time.second']: 'time', ['std_memory.memory_alloc']: 'memory', ['std_memory.memory_free']: 'memory', ['std_memory.memory_realloc']: 'memory', ['std_memory.memory_read_i8']: 'memory', ['std_memory.memory_read_u8']: 'memory', ['std_memory.memory_read_i16']: 'memory', ['std_memory.memory_read_u16']: 'memory', ['std_memory.memory_read_i32']: 'memory', ['std_memory.memory_read_u32']: 'memory', ['std_memory.memory_read_i64']: 'memory', ['std_memory.memory_read_u64']: 'memory', ['std_memory.memory_read_f32']: 'memory', ['std_memory.memory_read_f64']: 'memory', ['std_memory.memory_write_i8']: 'memory', ['std_memory.memory_write_u8']: 'memory', ['std_memory.memory_write_i16']: 'memory', ['std_memory.memory_write_u16']: 'memory', ['std_memory.memory_write_i32']: 'memory', ['std_memory.memory_write_u32']: 'memory', ['std_memory.memory_write_i64']: 'memory', ['std_memory.memory_write_u64']: 'memory', ['std_memory.memory_write_f32']: 'memory', ['std_memory.memory_write_f64']: 'memory', ['std_memory.memory_copy']: 'memory', ['std_memory.memory_set']: 'memory', ['std_memory.memory_compare']: 'memory', ['std_memory.ptr_add']: 'memory', ['std_memory.ptr_sub']: 'memory', ['std_memory.ptr_diff']: 'memory', ['std_memory.stack_alloc']: 'memory', ['std_memory.stack_push_frame']: 'memory', ['std_memory.stack_pop_frame']: 'memory', ['std_memory.memory_sizeof']: 'memory', ['std_memory.address_of']: 'memory', ['std_memory.deref']: 'memory', ['std_memory.nullptr']: 'memory', ['std_memory.memory_heap_size']: 'memory', ['std_memory.memory_stack_size']: 'memory', ['std_concurrency.thread_spawn']: 'concurrency', ['std_concurrency.thread_join']: 'concurrency', ['std_concurrency.mutex_create']: 'concurrency', ['std_concurrency.mutex_lock']: 'concurrency', ['std_concurrency.mutex_unlock']: 'concurrency', ['std_concurrency.scoped_lock']: 'concurrency', ['std_concurrency.atomic_load']: 'concurrency', ['std_concurrency.atomic_store']: 'concurrency', ['std_concurrency.atomic_compare_exchange']: 'concurrency' };
1818
+ }
1819
+
1820
+ export function analyzeCapabilities(program: any): any {
1821
+ const input = program;
1822
+ return _analyzeCapabilitiesCore({ ['modules']: program.modules, ['programName']: program.name, ['programVersion']: program.version });
1823
+ }
1824
+
1825
+ export function analyzeModuleCapabilities(module: any, imports: any): any {
1826
+ let modules = [module];
1827
+ for (const m of imports) {
1828
+ modules = (modules.push(m), modules);
1829
+ }
1830
+ return _analyzeCapabilitiesCore({ ['modules']: modules, ['programName']: module.name, ['programVersion']: '' });
1831
+ }
1832
+
1833
+ export function analyzeCapabilitiesReachable(program: any): any {
1834
+ const input = program;
1835
+ let table = buildCapabilityTable();
1836
+ let baseModules = _identifyBaseModules(program.modules);
1837
+ let userFns = _collectUserFunctionNames(program.modules);
1838
+ let fnCaps = {};
1839
+ let capSites = {};
1840
+ let visited = [];
1841
+ _analyzeReachableFn({ ['modules']: program.modules, ['baseModules']: baseModules, ['table']: table, ['userFns']: userFns, ['fnCaps']: fnCaps, ['capSites']: capSites, ['visited']: visited, ['module']: program.entryModule, ['function']: program.entryFunction });
1842
+ let functionsOut = [];
1843
+ for (const key of fnCaps.keys) {
1844
+ let dot = key.indexOf('.');
1845
+ let mod = key.substring(0, dot);
1846
+ let fn = key.substring(__ball_add(dot, 1));
1847
+ functionsOut = (functionsOut.push({ ['module']: mod, ['function']: fn, ['capabilities']: __ball_index(fnCaps, key) }), functionsOut);
1848
+ }
1849
+ return _buildReportFromFunctions(program.name, program.version, functionsOut, capSites);
1850
+ }
1851
+
1852
+ export function _analyzeReachableFn(ctx: any): any {
1853
+ const input = ctx;
1854
+ let modules = __ball_index(ctx, 'modules');
1855
+ let baseModules = __ball_index(ctx, 'baseModules');
1856
+ let table = __ball_index(ctx, 'table');
1857
+ let userFns = __ball_index(ctx, 'userFns');
1858
+ let fnCaps = __ball_index(ctx, 'fnCaps');
1859
+ let capSites = __ball_index(ctx, 'capSites');
1860
+ let visited = __ball_index(ctx, 'visited');
1861
+ let moduleName = __ball_index(ctx, 'module');
1862
+ let fnName = __ball_index(ctx, 'function');
1863
+ let key = ((__ball_to_string(moduleName) + '.') + __ball_to_string(fnName));
1864
+ if (visited.includes(key)) {
1865
+ return;
1866
+ }
1867
+ visited = (visited.push(key), visited);
1868
+ if (baseModules.includes(moduleName)) {
1869
+ return;
1870
+ }
1871
+ for (const module of modules) {
1872
+ if (!__ball_eq(module.name, moduleName)) {
1873
+ continue;
1874
+ }
1875
+ for (const fn of module.functions) {
1876
+ if (!__ball_eq(fn.name, fnName)) {
1877
+ continue;
1878
+ }
1879
+ if (fn.isBase) {
1880
+ return;
1881
+ }
1882
+ let caps = [];
1883
+ let callees = [];
1884
+ if (!hasBody(fn)) {
1885
+ fnCaps[key] = caps;
1886
+ return;
1887
+ }
1888
+ _walkCap({ ['expr']: fn.body, ['module']: moduleName, ['function']: fnName, ['caps']: caps, ['capSites']: capSites, ['table']: table, ['callees']: callees, ['userFns']: userFns });
1889
+ fnCaps[key] = caps;
1890
+ for (const callee of callees) {
1891
+ _analyzeReachableFn({ ['modules']: modules, ['baseModules']: baseModules, ['table']: table, ['userFns']: userFns, ['fnCaps']: fnCaps, ['capSites']: capSites, ['visited']: visited, ['module']: __ball_index(callee, 'module'), ['function']: __ball_index(callee, 'function') });
1892
+ let calleeKey = ((__ball_to_string(__ball_index(callee, 'module')) + '.') + __ball_to_string(__ball_index(callee, 'function')));
1893
+ if ((calleeKey in __ball_require_map(fnCaps, 'map_contains_key'))) {
1894
+ let calleeCaps = __ball_index(fnCaps, calleeKey);
1895
+ for (const c of calleeCaps) {
1896
+ if (!caps.includes(c)) {
1897
+ caps = (caps.push(c), caps);
1898
+ }
1899
+ }
1900
+ }
1901
+ }
1902
+ return;
1903
+ }
1904
+ }
1905
+ }
1906
+
1907
+ export function _analyzeCapabilitiesCore(ctx: any): any {
1908
+ const input = ctx;
1909
+ let modules = __ball_index(ctx, 'modules');
1910
+ let programName = __ball_index(ctx, 'programName');
1911
+ let programVersion = __ball_index(ctx, 'programVersion');
1912
+ let table = buildCapabilityTable();
1913
+ let baseModules = _identifyBaseModules(modules);
1914
+ let userFns = _collectUserFunctionNames(modules);
1915
+ let functionsOut = [];
1916
+ let capSites = {};
1917
+ for (const module of modules) {
1918
+ if (baseModules.includes(module.name)) {
1919
+ continue;
1920
+ }
1921
+ for (const fn of module.functions) {
1922
+ if (fn.isBase) {
1923
+ continue;
1924
+ }
1925
+ let caps = [];
1926
+ if (hasBody(fn)) {
1927
+ _walkCap({ ['expr']: fn.body, ['module']: module.name, ['function']: fn.name, ['caps']: caps, ['capSites']: capSites, ['table']: table, ['callees']: null, ['userFns']: userFns });
1928
+ }
1929
+ functionsOut = (functionsOut.push({ ['module']: module.name, ['function']: fn.name, ['capabilities']: caps }), functionsOut);
1930
+ }
1931
+ }
1932
+ return _buildReportFromFunctions(programName, programVersion, functionsOut, capSites);
1933
+ }
1934
+
1935
+ export function _identifyBaseModules(modules: any): any {
1936
+ const input = modules;
1937
+ let baseModules = [];
1938
+ for (const module of modules) {
1939
+ let allBase = true;
1940
+ let hasAny = false;
1941
+ for (const f of module.functions) {
1942
+ hasAny = true;
1943
+ if (!f.isBase) {
1944
+ allBase = false;
1945
+ }
1946
+ }
1947
+ if ((allBase && hasAny)) {
1948
+ baseModules = (baseModules.push(module.name), baseModules);
1949
+ }
1950
+ }
1951
+ return baseModules;
1952
+ }
1953
+
1954
+ export function _collectUserFunctionNames(modules: any): any {
1955
+ const input = modules;
1956
+ let names = [];
1957
+ for (const module of modules) {
1958
+ for (const f of module.functions) {
1959
+ if (!f.isBase) {
1960
+ names = (names.push(f.name), names);
1961
+ }
1962
+ }
1963
+ }
1964
+ return names;
1965
+ }
1966
+
1967
+ export function _walkCap(ctx: any): any {
1968
+ const input = ctx;
1969
+ let expr = __ball_index(ctx, 'expr');
1970
+ let module = __ball_index(ctx, 'module');
1971
+ let function_ = __ball_index(ctx, 'function');
1972
+ let caps = __ball_index(ctx, 'caps');
1973
+ let capSites = __ball_index(ctx, 'capSites');
1974
+ let table = __ball_index(ctx, 'table');
1975
+ let callees = __ball_index(ctx, 'callees');
1976
+ let userFns = __ball_index(ctx, 'userFns');
1977
+ if (__ball_eq(expr, null)) {
1978
+ return;
1979
+ }
1980
+ if (hasCall(expr)) {
1981
+ _walkCapCall({ ['call']: expr.call, ['module']: module, ['function']: function_, ['caps']: caps, ['capSites']: capSites, ['table']: table, ['callees']: callees, ['userFns']: userFns });
1982
+ } else {
1983
+ if (hasLiteral(expr)) {
1984
+ let lit = expr.literal;
1985
+ if (hasListValue(lit)) {
1986
+ for (const elem of lit.listValue.elements) {
1987
+ _walkCap({ ['expr']: elem, ['module']: module, ['function']: function_, ['caps']: caps, ['capSites']: capSites, ['table']: table, ['callees']: callees, ['userFns']: userFns });
1988
+ }
1989
+ }
1990
+ } else {
1991
+ if (hasBlock(expr)) {
1992
+ for (const stmt of expr.block.statements) {
1993
+ if (hasLet(stmt)) {
1994
+ _walkCap({ ['expr']: stmt.let.value, ['module']: module, ['function']: function_, ['caps']: caps, ['capSites']: capSites, ['table']: table, ['callees']: callees, ['userFns']: userFns });
1995
+ }
1996
+ if (hasExpression(stmt)) {
1997
+ _walkCap({ ['expr']: stmt.expression, ['module']: module, ['function']: function_, ['caps']: caps, ['capSites']: capSites, ['table']: table, ['callees']: callees, ['userFns']: userFns });
1998
+ }
1999
+ }
2000
+ if (hasResult(expr.block)) {
2001
+ _walkCap({ ['expr']: expr.block.result, ['module']: module, ['function']: function_, ['caps']: caps, ['capSites']: capSites, ['table']: table, ['callees']: callees, ['userFns']: userFns });
2002
+ }
2003
+ } else {
2004
+ if (hasLambda(expr)) {
2005
+ _walkCap({ ['expr']: expr.lambda.body, ['module']: module, ['function']: function_, ['caps']: caps, ['capSites']: capSites, ['table']: table, ['callees']: callees, ['userFns']: userFns });
2006
+ } else {
2007
+ if (hasMessageCreation(expr)) {
2008
+ for (const field of expr.messageCreation.fields) {
2009
+ _walkCap({ ['expr']: field.value, ['module']: module, ['function']: function_, ['caps']: caps, ['capSites']: capSites, ['table']: table, ['callees']: callees, ['userFns']: userFns });
2010
+ }
2011
+ } else {
2012
+ if (hasFieldAccess(expr)) {
2013
+ if (hasObject(expr.fieldAccess)) {
2014
+ _walkCap({ ['expr']: expr.fieldAccess.object, ['module']: module, ['function']: function_, ['caps']: caps, ['capSites']: capSites, ['table']: table, ['callees']: callees, ['userFns']: userFns });
2015
+ }
2016
+ }
2017
+ }
2018
+ }
2019
+ }
2020
+ }
2021
+ }
2022
+ }
2023
+
2024
+ export function _walkCapCall(ctx: any): any {
2025
+ const input = ctx;
2026
+ let call = __ball_index(ctx, 'call');
2027
+ let contextModule = __ball_index(ctx, 'module');
2028
+ let contextFunction = __ball_index(ctx, 'function');
2029
+ let caps = __ball_index(ctx, 'caps');
2030
+ let capSites = __ball_index(ctx, 'capSites');
2031
+ let table = __ball_index(ctx, 'table');
2032
+ let callees = __ball_index(ctx, 'callees');
2033
+ let userFns = __ball_index(ctx, 'userFns');
2034
+ let module = ((call.module.length === 0) ? contextModule : call.module);
2035
+ let fn = call.function;
2036
+ let cap = lookupCapability(table, module, fn);
2037
+ if ((cap.length === 0)) {
2038
+ let isUserFn = false;
2039
+ if (!__ball_eq(userFns, null)) {
2040
+ for (const u of userFns) {
2041
+ if (__ball_eq(u, fn)) {
2042
+ isUserFn = true;
2043
+ }
2044
+ }
2045
+ }
2046
+ if (!isUserFn) {
2047
+ cap = lookupCapabilityByName(table, fn);
2048
+ }
2049
+ }
2050
+ if (!(cap.length === 0)) {
2051
+ if (!caps.includes(cap)) {
2052
+ caps = (caps.push(cap), caps);
2053
+ }
2054
+ if (!__ball_eq(cap, 'pure')) {
2055
+ let sites;
2056
+ if ((cap in __ball_require_map(capSites, 'map_contains_key'))) {
2057
+ sites = __ball_index(capSites, cap);
2058
+ } else {
2059
+ sites = [];
2060
+ capSites[cap] = sites;
2061
+ }
2062
+ sites = (sites.push({ ['module']: contextModule, ['function']: contextFunction, ['calleeModule']: module, ['calleeFunction']: fn }), sites);
2063
+ }
2064
+ } else {
2065
+ if (!__ball_eq(callees, null)) {
2066
+ callees = (callees.push({ ['module']: module, ['function']: fn }), callees);
2067
+ }
2068
+ }
2069
+ if (hasInput(call)) {
2070
+ _walkCap({ ['expr']: call.input, ['module']: contextModule, ['function']: contextFunction, ['caps']: caps, ['capSites']: capSites, ['table']: table, ['callees']: callees, ['userFns']: userFns });
2071
+ }
2072
+ }
2073
+
2074
+ export function _buildReportFromFunctions(programName: any, programVersion: any, functionsOut: any, capSites: any): any {
2075
+ let allCaps = [];
2076
+ let totalFns = 0;
2077
+ let pureFns = 0;
2078
+ let effectfulFns = 0;
2079
+ for (const entry of functionsOut) {
2080
+ let entryCaps = __ball_index(entry, 'capabilities');
2081
+ for (const c of entryCaps) {
2082
+ if (!allCaps.includes(c)) {
2083
+ allCaps = (allCaps.push(c), allCaps);
2084
+ }
2085
+ }
2086
+ (totalFns++);
2087
+ let onlyPure = true;
2088
+ for (const c of entryCaps) {
2089
+ if (!__ball_eq(c, 'pure')) {
2090
+ onlyPure = false;
2091
+ }
2092
+ }
2093
+ if (onlyPure) {
2094
+ (pureFns++);
2095
+ } else {
2096
+ (effectfulFns++);
2097
+ }
2098
+ }
2099
+ let capabilitiesOut = [];
2100
+ for (const cap of capabilityNames()) {
2101
+ let present = allCaps.includes(cap);
2102
+ if ((!present && !__ball_eq(cap, 'pure'))) {
2103
+ continue;
2104
+ }
2105
+ let sites;
2106
+ if ((cap in __ball_require_map(capSites, 'map_contains_key'))) {
2107
+ sites = __ball_index(capSites, cap);
2108
+ } else {
2109
+ sites = [];
2110
+ }
2111
+ if (((__ball_eq(cap, 'pure') && (sites.length === 0)) && present)) {
2112
+ capabilitiesOut = (capabilitiesOut.push({ ['capability']: cap, ['riskLevel']: capabilityRisk(cap), ['callSites']: [] }), capabilitiesOut);
2113
+ continue;
2114
+ }
2115
+ if (!(sites.length === 0)) {
2116
+ capabilitiesOut = (capabilitiesOut.push({ ['capability']: cap, ['riskLevel']: capabilityRisk(cap), ['callSites']: sites }), capabilitiesOut);
2117
+ }
2118
+ }
2119
+ let ioSites = (('io' in __ball_require_map(capSites, 'map_contains_key')) ? __ball_index(capSites, 'io') : []);
2120
+ let readsStdin = false;
2121
+ let writesStdout = false;
2122
+ let writesStderr = false;
2123
+ let readsEnvironment = false;
2124
+ for (const s of ioSites) {
2125
+ let callee = __ball_index(s, 'calleeFunction');
2126
+ if (__ball_eq(callee, 'read_line')) {
2127
+ readsStdin = true;
2128
+ }
2129
+ if ((__ball_eq(callee, 'print') || __ball_eq(callee, 'print_error'))) {
2130
+ writesStdout = true;
2131
+ }
2132
+ if (__ball_eq(callee, 'print_error')) {
2133
+ writesStderr = true;
2134
+ }
2135
+ if ((__ball_eq(callee, 'env_get') || __ball_eq(callee, 'args_get'))) {
2136
+ readsEnvironment = true;
2137
+ }
2138
+ }
2139
+ let isPure = true;
2140
+ for (const c of allCaps) {
2141
+ if (!__ball_eq(c, 'pure')) {
2142
+ isPure = false;
2143
+ }
2144
+ }
2145
+ let summary = { ['isPure']: isPure, ['readsFilesystem']: allCaps.includes('fs'), ['writesFilesystem']: allCaps.includes('fs'), ['readsStdin']: readsStdin, ['writesStdout']: writesStdout, ['writesStderr']: writesStderr, ['readsEnvironment']: readsEnvironment, ['controlsProcess']: allCaps.includes('process'), ['usesMemory']: allCaps.includes('memory'), ['usesTime']: allCaps.includes('time'), ['usesRandom']: allCaps.includes('random'), ['usesConcurrency']: allCaps.includes('concurrency'), ['usesNetwork']: allCaps.includes('network'), ['totalFunctions']: totalFns, ['pureFunctions']: pureFns, ['effectfulFunctions']: effectfulFns };
2146
+ return { ['programName']: programName, ['programVersion']: programVersion, ['capabilities']: capabilitiesOut, ['functions']: functionsOut, ['summary']: summary };
2147
+ }
2148
+
2149
+ export function formatCapabilityReport(report: any): any {
2150
+ const input = report;
2151
+ let lines = [];
2152
+ lines = (lines.push(((('Ball Capability Audit: ' + __ball_to_string(__ball_index(report, 'programName'))) + ' v') + __ball_to_string(__ball_index(report, 'programVersion')))), lines);
2153
+ lines = (lines.push('============================================================'), lines);
2154
+ lines = (lines.push(''), lines);
2155
+ lines = (lines.push('Capabilities:'), lines);
2156
+ let capabilities = __ball_index(report, 'capabilities');
2157
+ for (const entry of capabilities) {
2158
+ let icon = (__ball_eq(__ball_index(entry, 'riskLevel'), 'none') ? '\u2713' : '\u26a0');
2159
+ let callSites = __ball_index(entry, 'callSites');
2160
+ let siteCount = callSites.length;
2161
+ if (__ball_eq(siteCount, 0)) {
2162
+ lines = (lines.push(((((' ' + __ball_to_string(icon)) + ' ') + __ball_to_string(__ball_index(entry, 'capability'))) + ' (pure computation)')), lines);
2163
+ } else {
2164
+ let siteStrs = [];
2165
+ for (const s of callSites) {
2166
+ siteStrs = (siteStrs.push(((((((__ball_to_string(__ball_index(s, 'module')) + '.') + __ball_to_string(__ball_index(s, 'function'))) + ' \u2192 ') + __ball_to_string(__ball_index(s, 'calleeModule'))) + '.') + __ball_to_string(__ball_index(s, 'calleeFunction')))), siteStrs);
2167
+ }
2168
+ let sites = siteStrs.join(', ');
2169
+ lines = (lines.push(((((((((' ' + __ball_to_string(icon)) + ' ') + __ball_to_string(__ball_index(entry, 'capability'))) + ' (') + __ball_to_string(siteCount)) + ' call sites: ') + __ball_to_string(sites)) + ')')), lines);
2170
+ }
2171
+ }
2172
+ let s = __ball_index(report, 'summary');
2173
+ let absent = [];
2174
+ let readsFs = __ball_index(s, 'readsFilesystem');
2175
+ let writesFs = __ball_index(s, 'writesFilesystem');
2176
+ if ((__ball_eq(readsFs, false) && __ball_eq(writesFs, false))) {
2177
+ absent = (absent.push('filesystem'), absent);
2178
+ }
2179
+ if (__ball_eq(__ball_index(s, 'usesNetwork'), false)) {
2180
+ absent = (absent.push('network'), absent);
2181
+ }
2182
+ if (__ball_eq(__ball_index(s, 'controlsProcess'), false)) {
2183
+ absent = (absent.push('process'), absent);
2184
+ }
2185
+ if (__ball_eq(__ball_index(s, 'usesMemory'), false)) {
2186
+ absent = (absent.push('memory'), absent);
2187
+ }
2188
+ if (__ball_eq(__ball_index(s, 'usesConcurrency'), false)) {
2189
+ absent = (absent.push('concurrency'), absent);
2190
+ }
2191
+ if (__ball_eq(__ball_index(s, 'usesRandom'), false)) {
2192
+ absent = (absent.push('random'), absent);
2193
+ }
2194
+ if (!(absent.length === 0)) {
2195
+ lines = (lines.push((' \u2717 NONE: ' + __ball_to_string(absent.join(', ')))), lines);
2196
+ }
2197
+ lines = (lines.push(''), lines);
2198
+ let isPure = __ball_eq(__ball_index(s, 'isPure'), true);
2199
+ let controlsProcess = __ball_eq(__ball_index(s, 'controlsProcess'), true);
2200
+ let usesMemory = __ball_eq(__ball_index(s, 'usesMemory'), true);
2201
+ let usesNetwork = __ball_eq(__ball_index(s, 'usesNetwork'), true);
2202
+ let rFs = __ball_eq(__ball_index(s, 'readsFilesystem'), true);
2203
+ let wFs = __ball_eq(__ball_index(s, 'writesFilesystem'), true);
2204
+ let usesConcurrency = __ball_eq(__ball_index(s, 'usesConcurrency'), true);
2205
+ let risk;
2206
+ if (isPure) {
2207
+ risk = 'NO RISK \u2014 pure computation only';
2208
+ } else {
2209
+ if (((controlsProcess || usesMemory) || usesNetwork)) {
2210
+ risk = 'HIGH RISK';
2211
+ } else {
2212
+ if (((rFs || wFs) || usesConcurrency)) {
2213
+ risk = 'MEDIUM RISK';
2214
+ } else {
2215
+ risk = 'LOW RISK';
2216
+ }
2217
+ }
2218
+ }
2219
+ lines = (lines.push(('Summary: ' + __ball_to_string(risk))), lines);
2220
+ lines = (lines.push(((((((' ' + __ball_to_string(__ball_index(s, 'totalFunctions'))) + ' functions: ') + __ball_to_string(__ball_index(s, 'pureFunctions'))) + ' pure, ') + __ball_to_string(__ball_index(s, 'effectfulFunctions'))) + ' effectful')), lines);
2221
+ lines = (lines.push(''), lines);
2222
+ lines = (lines.push('Per-function breakdown:'), lines);
2223
+ let functions = __ball_index(report, 'functions');
2224
+ for (const fn of functions) {
2225
+ let fnCaps = __ball_index(fn, 'capabilities');
2226
+ let nonPure = [];
2227
+ for (const c of fnCaps) {
2228
+ if (!__ball_eq(c, 'pure')) {
2229
+ nonPure = (nonPure.push(c), nonPure);
2230
+ }
2231
+ }
2232
+ let label = ((nonPure.length === 0) ? 'pure' : nonPure.join(', '));
2233
+ lines = (lines.push((((((' ' + __ball_to_string(__ball_index(fn, 'module'))) + '.') + __ball_to_string(__ball_index(fn, 'function'))) + ' \u2192 ') + __ball_to_string(label))), lines);
2234
+ }
2235
+ return (__ball_to_string(lines.join('\n')) + '\n');
2236
+ }
2237
+
2238
+ export function checkPolicy(report: any, deny: any): any {
2239
+ let denyList = [];
2240
+ for (const d of deny) {
2241
+ denyList = (denyList.push(d), denyList);
2242
+ }
2243
+ return checkPolicyViolations({ ['report']: report, ['deny']: denyList });
2244
+ }
2245
+
2246
+ export function checkPolicyViolations(ctx: any): any {
2247
+ const input = ctx;
2248
+ let report = __ball_index(ctx, 'report');
2249
+ let deny = __ball_index(ctx, 'deny');
2250
+ let violations = [];
2251
+ let capabilities = __ball_index(report, 'capabilities');
2252
+ for (const entry of capabilities) {
2253
+ if (deny.includes(__ball_index(entry, 'capability'))) {
2254
+ let callSites = __ball_index(entry, 'callSites');
2255
+ for (const site of callSites) {
2256
+ violations = (violations.push(((((((__ball_to_string(__ball_index(entry, 'capability')) + ': ') + __ball_to_string(__ball_index(site, 'module'))) + '.') + __ball_to_string(__ball_index(site, 'function'))) + ' calls ') + ((__ball_to_string(__ball_index(site, 'calleeModule')) + '.') + __ball_to_string(__ball_index(site, 'calleeFunction'))))), violations);
2257
+ }
2258
+ }
2259
+ }
2260
+ return violations;
2261
+ }
2262
+
2263
+ export function analyzeTermination(program: any): any {
2264
+ const input = program;
2265
+ return _analyzeTerminationCore({ ['modules']: program.modules });
2266
+ }
2267
+
2268
+ export function analyzeModuleTermination(module: any, imports: any): any {
2269
+ let modules = [module];
2270
+ for (const m of imports) {
2271
+ modules = (modules.push(m), modules);
2272
+ }
2273
+ return _analyzeTerminationCore({ ['modules']: modules });
2274
+ }
2275
+
2276
+ export function _analyzeTerminationCore(ctx: any): any {
2277
+ const input = ctx;
2278
+ let modules = __ball_index(ctx, 'modules');
2279
+ let baseModules = _identifyBaseModules(modules);
2280
+ let warnings = [];
2281
+ let callGraph = _buildCallGraph({ ['modules']: modules, ['baseModules']: baseModules });
2282
+ _checkLoops({ ['modules']: modules, ['baseModules']: baseModules, ['warnings']: warnings });
2283
+ _checkRecursion({ ['modules']: modules, ['callGraph']: callGraph, ['warnings']: warnings });
2284
+ _checkUnreachableCode({ ['modules']: modules, ['baseModules']: baseModules, ['warnings']: warnings });
2285
+ _checkOrphanedLabels({ ['modules']: modules, ['baseModules']: baseModules, ['warnings']: warnings });
2286
+ return warnings;
2287
+ }
2288
+
2289
+ export function formatTerminationReport(warnings: any): any {
2290
+ const input = warnings;
2291
+ let lines = [];
2292
+ lines = (lines.push('Termination Analysis'), lines);
2293
+ lines = (lines.push('============================================================'), lines);
2294
+ lines = (lines.push(''), lines);
2295
+ if ((warnings.length === 0)) {
2296
+ lines = (lines.push('No issues found.'), lines);
2297
+ return (__ball_to_string(lines.join('\n')) + '\n');
2298
+ }
2299
+ let categoryOrder = [];
2300
+ let byCategory = {};
2301
+ for (const w of warnings) {
2302
+ let cat = __ball_index(w, 'category');
2303
+ if (!(cat in __ball_require_map(byCategory, 'map_contains_key'))) {
2304
+ categoryOrder = (categoryOrder.push(cat), categoryOrder);
2305
+ byCategory[cat] = [];
2306
+ }
2307
+ let bucket = __ball_index(byCategory, cat);
2308
+ bucket = (bucket.push(w), bucket);
2309
+ }
2310
+ for (const cat of categoryOrder) {
2311
+ let bucket = __ball_index(byCategory, cat);
2312
+ lines = (lines.push((((__ball_to_string(_categoryLabel(cat)) + ' (') + __ball_to_string(bucket.length)) + '):')), lines);
2313
+ for (const w of bucket) {
2314
+ let sev = __ball_index(w, 'severity');
2315
+ let icon = (__ball_eq(sev, 'error') ? '\u2716' : ((__ball_eq(sev, 'warning') ? '\u26a0' : '\u2139')));
2316
+ lines = (lines.push((((((' ' + __ball_to_string(icon)) + ' ') + __ball_to_string(__ball_index(w, 'location'))) + ': ') + __ball_to_string(__ball_index(w, 'message')))), lines);
2317
+ }
2318
+ lines = (lines.push(''), lines);
2319
+ }
2320
+ let errors = 0;
2321
+ let warns = 0;
2322
+ let infos = 0;
2323
+ for (const w of warnings) {
2324
+ let sev = __ball_index(w, 'severity');
2325
+ if (__ball_eq(sev, 'error')) {
2326
+ (errors++);
2327
+ }
2328
+ if (__ball_eq(sev, 'warning')) {
2329
+ (warns++);
2330
+ }
2331
+ if (__ball_eq(sev, 'info')) {
2332
+ (infos++);
2333
+ }
2334
+ }
2335
+ lines = (lines.push((((((('Total: ' + __ball_to_string(errors)) + ' error(s), ') + __ball_to_string(warns)) + ' warning(s), ') + __ball_to_string(infos)) + ' info(s)')), lines);
2336
+ return (__ball_to_string(lines.join('\n')) + '\n');
2337
+ }
2338
+
2339
+ export function _categoryLabel(category: any): any {
2340
+ const input = category;
2341
+ if (__ball_eq(category, 'infinite_loop')) {
2342
+ return 'Potential Infinite Loops';
2343
+ }
2344
+ if (__ball_eq(category, 'unbounded_recursion')) {
2345
+ return 'Unbounded Recursion';
2346
+ }
2347
+ if (__ball_eq(category, 'unreachable_code')) {
2348
+ return 'Unreachable Code';
2349
+ }
2350
+ if (__ball_eq(category, 'orphaned_label')) {
2351
+ return 'Orphaned Labels';
2352
+ }
2353
+ return category;
2354
+ }
2355
+
2356
+ export function terminationHasErrors(warnings: any): any {
2357
+ const input = warnings;
2358
+ for (const w of warnings) {
2359
+ if (__ball_eq(__ball_index(w, 'severity'), 'error')) {
2360
+ return true;
2361
+ }
2362
+ }
2363
+ return false;
2364
+ }
2365
+
2366
+ export function _buildCallGraph(ctx: any): any {
2367
+ const input = ctx;
2368
+ let modules = __ball_index(ctx, 'modules');
2369
+ let baseModules = __ball_index(ctx, 'baseModules');
2370
+ let graph = [];
2371
+ for (const module of modules) {
2372
+ if (baseModules.includes(module.name)) {
2373
+ continue;
2374
+ }
2375
+ for (const fn of module.functions) {
2376
+ if (fn.isBase) {
2377
+ continue;
2378
+ }
2379
+ if (!hasBody(fn)) {
2380
+ continue;
2381
+ }
2382
+ let key = ((__ball_to_string(module.name) + '.') + __ball_to_string(fn.name));
2383
+ let callees = [];
2384
+ _collectCallees({ ['expr']: fn.body, ['contextModule']: module.name, ['baseModules']: baseModules, ['callees']: callees });
2385
+ graph = (graph.push({ ['key']: key, ['callees']: callees }), graph);
2386
+ }
2387
+ }
2388
+ return graph;
2389
+ }
2390
+
2391
+ export function _collectCallees(ctx: any): any {
2392
+ const input = ctx;
2393
+ let expr = __ball_index(ctx, 'expr');
2394
+ let contextModule = __ball_index(ctx, 'contextModule');
2395
+ let baseModules = __ball_index(ctx, 'baseModules');
2396
+ let callees = __ball_index(ctx, 'callees');
2397
+ if (__ball_eq(expr, null)) {
2398
+ return;
2399
+ }
2400
+ if (hasCall(expr)) {
2401
+ let call = expr.call;
2402
+ let module = ((call.module.length === 0) ? contextModule : call.module);
2403
+ let fn = call.function;
2404
+ if (!baseModules.includes(module)) {
2405
+ let ck = ((__ball_to_string(module) + '.') + __ball_to_string(fn));
2406
+ if (!callees.includes(ck)) {
2407
+ callees = (callees.push(ck), callees);
2408
+ }
2409
+ }
2410
+ if (hasInput(call)) {
2411
+ _collectCallees({ ['expr']: call.input, ['contextModule']: contextModule, ['baseModules']: baseModules, ['callees']: callees });
2412
+ }
2413
+ } else {
2414
+ if (hasBlock(expr)) {
2415
+ for (const stmt of expr.block.statements) {
2416
+ if (hasLet(stmt)) {
2417
+ _collectCallees({ ['expr']: stmt.let.value, ['contextModule']: contextModule, ['baseModules']: baseModules, ['callees']: callees });
2418
+ }
2419
+ if (hasExpression(stmt)) {
2420
+ _collectCallees({ ['expr']: stmt.expression, ['contextModule']: contextModule, ['baseModules']: baseModules, ['callees']: callees });
2421
+ }
2422
+ }
2423
+ if (hasResult(expr.block)) {
2424
+ _collectCallees({ ['expr']: expr.block.result, ['contextModule']: contextModule, ['baseModules']: baseModules, ['callees']: callees });
2425
+ }
2426
+ } else {
2427
+ if (hasLambda(expr)) {
2428
+ _collectCallees({ ['expr']: expr.lambda.body, ['contextModule']: contextModule, ['baseModules']: baseModules, ['callees']: callees });
2429
+ } else {
2430
+ if (hasMessageCreation(expr)) {
2431
+ for (const field of expr.messageCreation.fields) {
2432
+ _collectCallees({ ['expr']: field.value, ['contextModule']: contextModule, ['baseModules']: baseModules, ['callees']: callees });
2433
+ }
2434
+ } else {
2435
+ if (hasFieldAccess(expr)) {
2436
+ if (hasObject(expr.fieldAccess)) {
2437
+ _collectCallees({ ['expr']: expr.fieldAccess.object, ['contextModule']: contextModule, ['baseModules']: baseModules, ['callees']: callees });
2438
+ }
2439
+ } else {
2440
+ if (hasLiteral(expr)) {
2441
+ if (hasListValue(expr.literal)) {
2442
+ for (const elem of expr.literal.listValue.elements) {
2443
+ _collectCallees({ ['expr']: elem, ['contextModule']: contextModule, ['baseModules']: baseModules, ['callees']: callees });
2444
+ }
2445
+ }
2446
+ }
2447
+ }
2448
+ }
2449
+ }
2450
+ }
2451
+ }
2452
+ }
2453
+
2454
+ export function _checkLoops(ctx: any): any {
2455
+ const input = ctx;
2456
+ let modules = __ball_index(ctx, 'modules');
2457
+ let baseModules = __ball_index(ctx, 'baseModules');
2458
+ let warnings = __ball_index(ctx, 'warnings');
2459
+ for (const module of modules) {
2460
+ if (baseModules.includes(module.name)) {
2461
+ continue;
2462
+ }
2463
+ for (const fn of module.functions) {
2464
+ if (fn.isBase) {
2465
+ continue;
2466
+ }
2467
+ if (!hasBody(fn)) {
2468
+ continue;
2469
+ }
2470
+ _checkLoopsInExpr({ ['expr']: fn.body, ['moduleName']: module.name, ['fnName']: fn.name, ['warnings']: warnings });
2471
+ }
2472
+ }
2473
+ }
2474
+
2475
+ export function _checkLoopsInExpr(ctx: any): any {
2476
+ const input = ctx;
2477
+ let expr = __ball_index(ctx, 'expr');
2478
+ let moduleName = __ball_index(ctx, 'moduleName');
2479
+ let fnName = __ball_index(ctx, 'fnName');
2480
+ let warnings = __ball_index(ctx, 'warnings');
2481
+ if (__ball_eq(expr, null)) {
2482
+ return;
2483
+ }
2484
+ if (hasCall(expr)) {
2485
+ let call = expr.call;
2486
+ let callModule = ((call.module.length === 0) ? 'std' : call.module);
2487
+ let callFn = call.function;
2488
+ if ((__ball_eq(callModule, 'std') && __ball_eq(callFn, 'while'))) {
2489
+ _checkWhileLoop({ ['call']: call, ['moduleName']: moduleName, ['fnName']: fnName, ['warnings']: warnings, ['kind']: 'while' });
2490
+ } else {
2491
+ if ((__ball_eq(callModule, 'std') && __ball_eq(callFn, 'do_while'))) {
2492
+ _checkWhileLoop({ ['call']: call, ['moduleName']: moduleName, ['fnName']: fnName, ['warnings']: warnings, ['kind']: 'do-while' });
2493
+ } else {
2494
+ if ((__ball_eq(callModule, 'std') && __ball_eq(callFn, 'for'))) {
2495
+ _checkForLoop({ ['call']: call, ['moduleName']: moduleName, ['fnName']: fnName, ['warnings']: warnings });
2496
+ }
2497
+ }
2498
+ }
2499
+ if (hasInput(call)) {
2500
+ _checkLoopsInExpr({ ['expr']: call.input, ['moduleName']: moduleName, ['fnName']: fnName, ['warnings']: warnings });
2501
+ }
2502
+ } else {
2503
+ if (hasBlock(expr)) {
2504
+ for (const stmt of expr.block.statements) {
2505
+ if (hasLet(stmt)) {
2506
+ _checkLoopsInExpr({ ['expr']: stmt.let.value, ['moduleName']: moduleName, ['fnName']: fnName, ['warnings']: warnings });
2507
+ }
2508
+ if (hasExpression(stmt)) {
2509
+ _checkLoopsInExpr({ ['expr']: stmt.expression, ['moduleName']: moduleName, ['fnName']: fnName, ['warnings']: warnings });
2510
+ }
2511
+ }
2512
+ if (hasResult(expr.block)) {
2513
+ _checkLoopsInExpr({ ['expr']: expr.block.result, ['moduleName']: moduleName, ['fnName']: fnName, ['warnings']: warnings });
2514
+ }
2515
+ } else {
2516
+ if (hasLambda(expr)) {
2517
+ _checkLoopsInExpr({ ['expr']: expr.lambda.body, ['moduleName']: moduleName, ['fnName']: fnName, ['warnings']: warnings });
2518
+ } else {
2519
+ if (hasMessageCreation(expr)) {
2520
+ for (const field of expr.messageCreation.fields) {
2521
+ _checkLoopsInExpr({ ['expr']: field.value, ['moduleName']: moduleName, ['fnName']: fnName, ['warnings']: warnings });
2522
+ }
2523
+ } else {
2524
+ if (hasFieldAccess(expr)) {
2525
+ if (hasObject(expr.fieldAccess)) {
2526
+ _checkLoopsInExpr({ ['expr']: expr.fieldAccess.object, ['moduleName']: moduleName, ['fnName']: fnName, ['warnings']: warnings });
2527
+ }
2528
+ }
2529
+ }
2530
+ }
2531
+ }
2532
+ }
2533
+ }
2534
+
2535
+ export function _checkWhileLoop(ctx: any): any {
2536
+ const input = ctx;
2537
+ let call = __ball_index(ctx, 'call');
2538
+ let moduleName = __ball_index(ctx, 'moduleName');
2539
+ let fnName = __ball_index(ctx, 'fnName');
2540
+ let warnings = __ball_index(ctx, 'warnings');
2541
+ let kind = __ball_index(ctx, 'kind');
2542
+ let location = ((__ball_to_string(moduleName) + '.') + __ball_to_string(fnName));
2543
+ if (!hasInput(call)) {
2544
+ return;
2545
+ }
2546
+ let callInput = call.input;
2547
+ if (!hasMessageCreation(callInput)) {
2548
+ return;
2549
+ }
2550
+ let fields = callInput.messageCreation.fields;
2551
+ let condition = _getFieldValue({ ['fields']: fields, ['name']: 'condition' });
2552
+ let body = _getFieldValue({ ['fields']: fields, ['name']: 'body' });
2553
+ if ((__ball_eq(condition, null) || __ball_eq(body, null))) {
2554
+ return;
2555
+ }
2556
+ let isLiteralTrue = _isLiteralTrue(condition);
2557
+ let condVars = [];
2558
+ _collectReferencedVars({ ['expr']: condition, ['vars']: condVars });
2559
+ let hasExit = _exprHasExitSignal(body);
2560
+ let mutatedVars = [];
2561
+ _collectMutatedVars({ ['expr']: body, ['vars']: mutatedVars });
2562
+ if ((isLiteralTrue && !hasExit)) {
2563
+ warnings = (warnings.push({ ['severity']: 'warning', ['category']: 'infinite_loop', ['message']: (__ball_to_string(kind) + '(true) loop without break or return in body'), ['location']: location }), warnings);
2564
+ return;
2565
+ }
2566
+ let intersects = false;
2567
+ for (const v of condVars) {
2568
+ if (mutatedVars.includes(v)) {
2569
+ intersects = true;
2570
+ }
2571
+ }
2572
+ if ((((!isLiteralTrue && !(condVars.length === 0)) && !hasExit) && !intersects)) {
2573
+ warnings = (warnings.push({ ['severity']: 'warning', ['category']: 'infinite_loop', ['message']: ((((__ball_to_string(kind) + ' loop condition references ') + __ball_to_string(condVars.join(', '))) + ' but body ') + 'does not modify any of them and has no break/return'), ['location']: location }), warnings);
2574
+ }
2575
+ }
2576
+
2577
+ export function _checkForLoop(ctx: any): any {
2578
+ const input = ctx;
2579
+ let call = __ball_index(ctx, 'call');
2580
+ let moduleName = __ball_index(ctx, 'moduleName');
2581
+ let fnName = __ball_index(ctx, 'fnName');
2582
+ let warnings = __ball_index(ctx, 'warnings');
2583
+ let location = ((__ball_to_string(moduleName) + '.') + __ball_to_string(fnName));
2584
+ if (!hasInput(call)) {
2585
+ return;
2586
+ }
2587
+ let callInput = call.input;
2588
+ if (!hasMessageCreation(callInput)) {
2589
+ return;
2590
+ }
2591
+ let fields = callInput.messageCreation.fields;
2592
+ let update = _getFieldValue({ ['fields']: fields, ['name']: 'update' });
2593
+ let body = _getFieldValue({ ['fields']: fields, ['name']: 'body' });
2594
+ let hasUpdate = (!__ball_eq(update, null) && _exprIsSet(update));
2595
+ let hasExit = (!__ball_eq(body, null) && _exprHasExitSignal(body));
2596
+ if ((!hasUpdate && !hasExit)) {
2597
+ warnings = (warnings.push({ ['severity']: 'warning', ['category']: 'infinite_loop', ['message']: 'for loop without update expression and no break/return in body', ['location']: location }), warnings);
2598
+ }
2599
+ }
2600
+
2601
+ export function _checkRecursion(ctx: any): any {
2602
+ const input = ctx;
2603
+ let modules = __ball_index(ctx, 'modules');
2604
+ let callGraph = __ball_index(ctx, 'callGraph');
2605
+ let warnings = __ball_index(ctx, 'warnings');
2606
+ let cycles = _findCycles({ ['callGraph']: callGraph });
2607
+ for (const cycle of cycles) {
2608
+ let cycleList = cycle;
2609
+ for (const fnKey of cycleList) {
2610
+ if (!_hasBaseCase({ ['modules']: modules, ['fnKey']: fnKey })) {
2611
+ let cycleDesc = (__ball_eq(cycleList.length, 1) ? 'direct recursion' : ('mutual recursion cycle: ' + __ball_to_string(cycleList.join(' -> '))));
2612
+ warnings = (warnings.push({ ['severity']: 'warning', ['category']: 'unbounded_recursion', ['message']: (__ball_to_string(cycleDesc) + ' without conditional return (no base case detected)'), ['location']: fnKey }), warnings);
2613
+ break;
2614
+ }
2615
+ }
2616
+ }
2617
+ }
2618
+
2619
+ export function _findCycles(ctx: any): any {
2620
+ const input = ctx;
2621
+ let callGraph = __ball_index(ctx, 'callGraph');
2622
+ let visited = [];
2623
+ let stack = [];
2624
+ let cycles = [];
2625
+ let reportedCycles = [];
2626
+ for (const entry of callGraph) {
2627
+ _dfsCycles({ ['callGraph']: callGraph, ['visited']: visited, ['stack']: stack, ['cycles']: cycles, ['reportedCycles']: reportedCycles, ['node']: __ball_index(entry, 'key') });
2628
+ }
2629
+ return cycles;
2630
+ }
2631
+
2632
+ export function _dfsCycles(ctx: any): any {
2633
+ const input = ctx;
2634
+ let callGraph = __ball_index(ctx, 'callGraph');
2635
+ let visited = __ball_index(ctx, 'visited');
2636
+ let stack = __ball_index(ctx, 'stack');
2637
+ let cycles = __ball_index(ctx, 'cycles');
2638
+ let reportedCycles = __ball_index(ctx, 'reportedCycles');
2639
+ let node = __ball_index(ctx, 'node');
2640
+ if (visited.includes(node)) {
2641
+ return;
2642
+ }
2643
+ visited = (visited.push(node), visited);
2644
+ stack = (stack.push(node), stack);
2645
+ let neighbors = _calleesOf(callGraph, node);
2646
+ for (const neighbor of neighbors) {
2647
+ if (stack.includes(neighbor)) {
2648
+ let cycleStart = stack.indexOf(neighbor);
2649
+ if (__ball_ge(cycleStart, 0)) {
2650
+ let cycle = stack.slice(cycleStart);
2651
+ let normalized = [];
2652
+ for (const c of cycle) {
2653
+ normalized = (normalized.push(c), normalized);
2654
+ }
2655
+ normalized = [...normalized].sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
2656
+ let key = normalized.join(',');
2657
+ if (!reportedCycles.includes(key)) {
2658
+ reportedCycles = (reportedCycles.push(key), reportedCycles);
2659
+ cycles = (cycles.push(cycle), cycles);
2660
+ }
2661
+ }
2662
+ } else {
2663
+ if (!visited.includes(neighbor)) {
2664
+ _dfsCycles({ ['callGraph']: callGraph, ['visited']: visited, ['stack']: stack, ['cycles']: cycles, ['reportedCycles']: reportedCycles, ['node']: neighbor });
2665
+ }
2666
+ }
2667
+ }
2668
+ stack.pop();
2669
+ }
2670
+
2671
+ export function _calleesOf(callGraph: any, node: any): any {
2672
+ for (const entry of callGraph) {
2673
+ if (__ball_eq(__ball_index(entry, 'key'), node)) {
2674
+ return __ball_index(entry, 'callees');
2675
+ }
2676
+ }
2677
+ return [];
2678
+ }
2679
+
2680
+ export function _hasBaseCase(ctx: any): any {
2681
+ const input = ctx;
2682
+ let modules = __ball_index(ctx, 'modules');
2683
+ let fnKey = __ball_index(ctx, 'fnKey');
2684
+ let fn = _findFunction(modules, fnKey);
2685
+ if (__ball_eq(fn, null)) {
2686
+ return true;
2687
+ }
2688
+ if (!hasBody(fn)) {
2689
+ return true;
2690
+ }
2691
+ return _exprContainsConditionalReturn(fn.body);
2692
+ }
2693
+
2694
+ export function _exprContainsConditionalReturn(expr: any): any {
2695
+ const input = expr;
2696
+ if (__ball_eq(expr, null)) {
2697
+ return false;
2698
+ }
2699
+ if (hasCall(expr)) {
2700
+ let call = expr.call;
2701
+ let module = ((call.module.length === 0) ? 'std' : call.module);
2702
+ if (((__ball_eq(module, 'std') && __ball_eq(call.function, 'if')) && hasInput(call))) {
2703
+ let callInput = call.input;
2704
+ if (hasMessageCreation(callInput)) {
2705
+ let thenBranch = _getFieldValue({ ['fields']: callInput.messageCreation.fields, ['name']: 'then' });
2706
+ let elseBranch = _getFieldValue({ ['fields']: callInput.messageCreation.fields, ['name']: 'else' });
2707
+ if ((!__ball_eq(thenBranch, null) && _exprContainsReturn(thenBranch))) {
2708
+ return true;
2709
+ }
2710
+ if ((!__ball_eq(elseBranch, null) && _exprContainsReturn(elseBranch))) {
2711
+ return true;
2712
+ }
2713
+ }
2714
+ }
2715
+ if (hasInput(call)) {
2716
+ if (_exprContainsConditionalReturn(call.input)) {
2717
+ return true;
2718
+ }
2719
+ }
2720
+ return false;
2721
+ } else {
2722
+ if (hasBlock(expr)) {
2723
+ for (const stmt of expr.block.statements) {
2724
+ if ((hasLet(stmt) && _exprContainsConditionalReturn(stmt.let.value))) {
2725
+ return true;
2726
+ }
2727
+ if ((hasExpression(stmt) && _exprContainsConditionalReturn(stmt.expression))) {
2728
+ return true;
2729
+ }
2730
+ }
2731
+ if ((hasResult(expr.block) && _exprContainsConditionalReturn(expr.block.result))) {
2732
+ return true;
2733
+ }
2734
+ return false;
2735
+ } else {
2736
+ if (hasLambda(expr)) {
2737
+ return _exprContainsConditionalReturn(expr.lambda.body);
2738
+ } else {
2739
+ if (hasMessageCreation(expr)) {
2740
+ for (const field of expr.messageCreation.fields) {
2741
+ if (_exprContainsConditionalReturn(field.value)) {
2742
+ return true;
2743
+ }
2744
+ }
2745
+ return false;
2746
+ } else {
2747
+ if (hasFieldAccess(expr)) {
2748
+ if (hasObject(expr.fieldAccess)) {
2749
+ return _exprContainsConditionalReturn(expr.fieldAccess.object);
2750
+ }
2751
+ return false;
2752
+ }
2753
+ }
2754
+ }
2755
+ }
2756
+ }
2757
+ return false;
2758
+ }
2759
+
2760
+ export function _exprContainsReturn(expr: any): any {
2761
+ const input = expr;
2762
+ if (__ball_eq(expr, null)) {
2763
+ return false;
2764
+ }
2765
+ if (hasCall(expr)) {
2766
+ let call = expr.call;
2767
+ let module = ((call.module.length === 0) ? 'std' : call.module);
2768
+ if ((__ball_eq(module, 'std') && __ball_eq(call.function, 'return'))) {
2769
+ return true;
2770
+ }
2771
+ if (hasInput(call)) {
2772
+ if (_exprContainsReturn(call.input)) {
2773
+ return true;
2774
+ }
2775
+ }
2776
+ return false;
2777
+ } else {
2778
+ if (hasBlock(expr)) {
2779
+ for (const stmt of expr.block.statements) {
2780
+ if ((hasLet(stmt) && _exprContainsReturn(stmt.let.value))) {
2781
+ return true;
2782
+ }
2783
+ if ((hasExpression(stmt) && _exprContainsReturn(stmt.expression))) {
2784
+ return true;
2785
+ }
2786
+ }
2787
+ if ((hasResult(expr.block) && _exprContainsReturn(expr.block.result))) {
2788
+ return true;
2789
+ }
2790
+ return false;
2791
+ } else {
2792
+ if (hasLambda(expr)) {
2793
+ return _exprContainsReturn(expr.lambda.body);
2794
+ } else {
2795
+ if (hasMessageCreation(expr)) {
2796
+ for (const field of expr.messageCreation.fields) {
2797
+ if (_exprContainsReturn(field.value)) {
2798
+ return true;
2799
+ }
2800
+ }
2801
+ return false;
2802
+ } else {
2803
+ if (hasFieldAccess(expr)) {
2804
+ if (hasObject(expr.fieldAccess)) {
2805
+ return _exprContainsReturn(expr.fieldAccess.object);
2806
+ }
2807
+ return false;
2808
+ }
2809
+ }
2810
+ }
2811
+ }
2812
+ }
2813
+ return false;
2814
+ }
2815
+
2816
+ export function _findFunction(modules: any, key: any): any {
2817
+ let dot = key.indexOf('.');
2818
+ if (__ball_lt(dot, 0)) {
2819
+ return null;
2820
+ }
2821
+ let moduleName = key.substring(0, dot);
2822
+ let fnName = key.substring(__ball_add(dot, 1));
2823
+ for (const module of modules) {
2824
+ if (!__ball_eq(module.name, moduleName)) {
2825
+ continue;
2826
+ }
2827
+ for (const fn of module.functions) {
2828
+ if (__ball_eq(fn.name, fnName)) {
2829
+ return fn;
2830
+ }
2831
+ }
2832
+ }
2833
+ }
2834
+
2835
+ export function _checkUnreachableCode(ctx: any): any {
2836
+ const input = ctx;
2837
+ let modules = __ball_index(ctx, 'modules');
2838
+ let baseModules = __ball_index(ctx, 'baseModules');
2839
+ let warnings = __ball_index(ctx, 'warnings');
2840
+ for (const module of modules) {
2841
+ if (baseModules.includes(module.name)) {
2842
+ continue;
2843
+ }
2844
+ for (const fn of module.functions) {
2845
+ if (fn.isBase) {
2846
+ continue;
2847
+ }
2848
+ if (!hasBody(fn)) {
2849
+ continue;
2850
+ }
2851
+ _checkUnreachableInExpr({ ['expr']: fn.body, ['moduleName']: module.name, ['fnName']: fn.name, ['warnings']: warnings });
2852
+ }
2853
+ }
2854
+ }
2855
+
2856
+ export function _checkUnreachableInExpr(ctx: any): any {
2857
+ const input = ctx;
2858
+ let expr = __ball_index(ctx, 'expr');
2859
+ let moduleName = __ball_index(ctx, 'moduleName');
2860
+ let fnName = __ball_index(ctx, 'fnName');
2861
+ let warnings = __ball_index(ctx, 'warnings');
2862
+ if (__ball_eq(expr, null)) {
2863
+ return;
2864
+ }
2865
+ if (hasBlock(expr)) {
2866
+ _checkBlockUnreachable({ ['block']: expr.block, ['moduleName']: moduleName, ['fnName']: fnName, ['warnings']: warnings });
2867
+ for (const stmt of expr.block.statements) {
2868
+ if (hasLet(stmt)) {
2869
+ _checkUnreachableInExpr({ ['expr']: stmt.let.value, ['moduleName']: moduleName, ['fnName']: fnName, ['warnings']: warnings });
2870
+ }
2871
+ if (hasExpression(stmt)) {
2872
+ _checkUnreachableInExpr({ ['expr']: stmt.expression, ['moduleName']: moduleName, ['fnName']: fnName, ['warnings']: warnings });
2873
+ }
2874
+ }
2875
+ if (hasResult(expr.block)) {
2876
+ _checkUnreachableInExpr({ ['expr']: expr.block.result, ['moduleName']: moduleName, ['fnName']: fnName, ['warnings']: warnings });
2877
+ }
2878
+ } else {
2879
+ if (hasCall(expr)) {
2880
+ if (hasInput(expr.call)) {
2881
+ _checkUnreachableInExpr({ ['expr']: expr.call.input, ['moduleName']: moduleName, ['fnName']: fnName, ['warnings']: warnings });
2882
+ }
2883
+ } else {
2884
+ if (hasLambda(expr)) {
2885
+ _checkUnreachableInExpr({ ['expr']: expr.lambda.body, ['moduleName']: moduleName, ['fnName']: fnName, ['warnings']: warnings });
2886
+ } else {
2887
+ if (hasMessageCreation(expr)) {
2888
+ for (const field of expr.messageCreation.fields) {
2889
+ _checkUnreachableInExpr({ ['expr']: field.value, ['moduleName']: moduleName, ['fnName']: fnName, ['warnings']: warnings });
2890
+ }
2891
+ } else {
2892
+ if (hasFieldAccess(expr)) {
2893
+ if (hasObject(expr.fieldAccess)) {
2894
+ _checkUnreachableInExpr({ ['expr']: expr.fieldAccess.object, ['moduleName']: moduleName, ['fnName']: fnName, ['warnings']: warnings });
2895
+ }
2896
+ }
2897
+ }
2898
+ }
2899
+ }
2900
+ }
2901
+ }
2902
+
2903
+ export function _checkBlockUnreachable(ctx: any): any {
2904
+ const input = ctx;
2905
+ let block = __ball_index(ctx, 'block');
2906
+ let moduleName = __ball_index(ctx, 'moduleName');
2907
+ let fnName = __ball_index(ctx, 'fnName');
2908
+ let warnings = __ball_index(ctx, 'warnings');
2909
+ let statements = block.statements;
2910
+ let count = statements.length;
2911
+ for (let i = 0; __ball_lt(i, count); (i++)) {
2912
+ let stmt = __ball_index(statements, i);
2913
+ if ((_isTerminatingStatement(stmt) && __ball_lt(i, __ball_sub(count, 1)))) {
2914
+ let unreachableCount = __ball_sub(__ball_sub(count, 1), i);
2915
+ warnings = (warnings.push({ ['severity']: 'warning', ['category']: 'unreachable_code', ['message']: (((__ball_to_string(unreachableCount) + ' statement(s) after ') + __ball_to_string(_terminatingCallName(stmt))) + ' are unreachable'), ['location']: (((((__ball_to_string(moduleName) + '.') + __ball_to_string(fnName)) + ':stmt[') + __ball_to_string(__ball_add(i, 1))) + ']') }), warnings);
2916
+ break;
2917
+ }
2918
+ }
2919
+ }
2920
+
2921
+ export function _isTerminatingStatement(stmt: any): any {
2922
+ const input = stmt;
2923
+ if (!hasExpression(stmt)) {
2924
+ return false;
2925
+ }
2926
+ return _isTerminatingExpr(stmt.expression);
2927
+ }
2928
+
2929
+ export function _isTerminatingExpr(expr: any): any {
2930
+ const input = expr;
2931
+ if (!hasCall(expr)) {
2932
+ return false;
2933
+ }
2934
+ let call = expr.call;
2935
+ let module = ((call.module.length === 0) ? 'std' : call.module);
2936
+ return (__ball_eq(module, 'std') && ((__ball_eq(call.function, 'return') || __ball_eq(call.function, 'throw')) || __ball_eq(call.function, 'rethrow')));
2937
+ }
2938
+
2939
+ export function _terminatingCallName(stmt: any): any {
2940
+ const input = stmt;
2941
+ if (!hasExpression(stmt)) {
2942
+ return '?';
2943
+ }
2944
+ let expr = stmt.expression;
2945
+ if (!hasCall(expr)) {
2946
+ return '?';
2947
+ }
2948
+ return ('std.' + __ball_to_string(expr.call.function));
2949
+ }
2950
+
2951
+ export function _checkOrphanedLabels(ctx: any): any {
2952
+ const input = ctx;
2953
+ let modules = __ball_index(ctx, 'modules');
2954
+ let baseModules = __ball_index(ctx, 'baseModules');
2955
+ let warnings = __ball_index(ctx, 'warnings');
2956
+ for (const module of modules) {
2957
+ if (baseModules.includes(module.name)) {
2958
+ continue;
2959
+ }
2960
+ for (const fn of module.functions) {
2961
+ if (fn.isBase) {
2962
+ continue;
2963
+ }
2964
+ if (!hasBody(fn)) {
2965
+ continue;
2966
+ }
2967
+ let definedLabels = [];
2968
+ _collectDefinedLabels({ ['expr']: fn.body, ['labels']: definedLabels });
2969
+ let usedLabels = [];
2970
+ _collectLabelUsages({ ['expr']: fn.body, ['usages']: usedLabels });
2971
+ for (const usage of usedLabels) {
2972
+ let label = __ball_index(usage, 'label');
2973
+ if ((!(label.length === 0) && !definedLabels.includes(label))) {
2974
+ warnings = (warnings.push({ ['severity']: 'error', ['category']: 'orphaned_label', ['message']: ((((('std.' + __ball_to_string(__ball_index(usage, 'kind'))) + '(label: "') + __ball_to_string(label)) + '") references ') + (('undefined label "' + __ball_to_string(label)) + '"')), ['location']: ((__ball_to_string(module.name) + '.') + __ball_to_string(fn.name)) }), warnings);
2975
+ }
2976
+ }
2977
+ }
2978
+ }
2979
+ }
2980
+
2981
+ export function _collectDefinedLabels(ctx: any): any {
2982
+ const input = ctx;
2983
+ let expr = __ball_index(ctx, 'expr');
2984
+ let labels = __ball_index(ctx, 'labels');
2985
+ if (__ball_eq(expr, null)) {
2986
+ return;
2987
+ }
2988
+ if (hasCall(expr)) {
2989
+ let call = expr.call;
2990
+ let module = ((call.module.length === 0) ? 'std' : call.module);
2991
+ if (((__ball_eq(module, 'std') && __ball_eq(call.function, 'label')) && hasInput(call))) {
2992
+ let callInput = call.input;
2993
+ if (hasMessageCreation(callInput)) {
2994
+ let name = _getStringFieldValue({ ['fields']: callInput.messageCreation.fields, ['name']: 'name' });
2995
+ if ((!__ball_eq(name, null) && !(name.length === 0))) {
2996
+ if (!labels.includes(name)) {
2997
+ labels = (labels.push(name), labels);
2998
+ }
2999
+ }
3000
+ }
3001
+ }
3002
+ if (hasInput(call)) {
3003
+ _collectDefinedLabels({ ['expr']: call.input, ['labels']: labels });
3004
+ }
3005
+ } else {
3006
+ if (hasBlock(expr)) {
3007
+ for (const stmt of expr.block.statements) {
3008
+ if (hasLet(stmt)) {
3009
+ _collectDefinedLabels({ ['expr']: stmt.let.value, ['labels']: labels });
3010
+ }
3011
+ if (hasExpression(stmt)) {
3012
+ _collectDefinedLabels({ ['expr']: stmt.expression, ['labels']: labels });
3013
+ }
3014
+ }
3015
+ if (hasResult(expr.block)) {
3016
+ _collectDefinedLabels({ ['expr']: expr.block.result, ['labels']: labels });
3017
+ }
3018
+ } else {
3019
+ if (hasLambda(expr)) {
3020
+ _collectDefinedLabels({ ['expr']: expr.lambda.body, ['labels']: labels });
3021
+ } else {
3022
+ if (hasMessageCreation(expr)) {
3023
+ for (const field of expr.messageCreation.fields) {
3024
+ _collectDefinedLabels({ ['expr']: field.value, ['labels']: labels });
3025
+ }
3026
+ } else {
3027
+ if (hasFieldAccess(expr)) {
3028
+ if (hasObject(expr.fieldAccess)) {
3029
+ _collectDefinedLabels({ ['expr']: expr.fieldAccess.object, ['labels']: labels });
3030
+ }
3031
+ }
3032
+ }
3033
+ }
3034
+ }
3035
+ }
3036
+ }
3037
+
3038
+ export function _collectLabelUsages(ctx: any): any {
3039
+ const input = ctx;
3040
+ let expr = __ball_index(ctx, 'expr');
3041
+ let usages = __ball_index(ctx, 'usages');
3042
+ if (__ball_eq(expr, null)) {
3043
+ return;
3044
+ }
3045
+ if (hasCall(expr)) {
3046
+ let call = expr.call;
3047
+ let module = ((call.module.length === 0) ? 'std' : call.module);
3048
+ if (((__ball_eq(module, 'std') && (__ball_eq(call.function, 'break') || __ball_eq(call.function, 'continue'))) && hasInput(call))) {
3049
+ let callInput = call.input;
3050
+ if (hasMessageCreation(callInput)) {
3051
+ let label = _getStringFieldValue({ ['fields']: callInput.messageCreation.fields, ['name']: 'label' });
3052
+ if ((!__ball_eq(label, null) && !(label.length === 0))) {
3053
+ usages = (usages.push({ ['kind']: call.function, ['label']: label }), usages);
3054
+ }
3055
+ }
3056
+ }
3057
+ if (hasInput(call)) {
3058
+ _collectLabelUsages({ ['expr']: call.input, ['usages']: usages });
3059
+ }
3060
+ } else {
3061
+ if (hasBlock(expr)) {
3062
+ for (const stmt of expr.block.statements) {
3063
+ if (hasLet(stmt)) {
3064
+ _collectLabelUsages({ ['expr']: stmt.let.value, ['usages']: usages });
3065
+ }
3066
+ if (hasExpression(stmt)) {
3067
+ _collectLabelUsages({ ['expr']: stmt.expression, ['usages']: usages });
3068
+ }
3069
+ }
3070
+ if (hasResult(expr.block)) {
3071
+ _collectLabelUsages({ ['expr']: expr.block.result, ['usages']: usages });
3072
+ }
3073
+ } else {
3074
+ if (hasLambda(expr)) {
3075
+ _collectLabelUsages({ ['expr']: expr.lambda.body, ['usages']: usages });
3076
+ } else {
3077
+ if (hasMessageCreation(expr)) {
3078
+ for (const field of expr.messageCreation.fields) {
3079
+ _collectLabelUsages({ ['expr']: field.value, ['usages']: usages });
3080
+ }
3081
+ } else {
3082
+ if (hasFieldAccess(expr)) {
3083
+ if (hasObject(expr.fieldAccess)) {
3084
+ _collectLabelUsages({ ['expr']: expr.fieldAccess.object, ['usages']: usages });
3085
+ }
3086
+ }
3087
+ }
3088
+ }
3089
+ }
3090
+ }
3091
+ }
3092
+
3093
+ export function _getFieldValue(ctx: any): any {
3094
+ const input = ctx;
3095
+ let fields = __ball_index(ctx, 'fields');
3096
+ let name = __ball_index(ctx, 'name');
3097
+ for (const f of fields) {
3098
+ if (__ball_eq(f.name, name)) {
3099
+ return f.value;
3100
+ }
3101
+ }
3102
+ }
3103
+
3104
+ export function _getStringFieldValue(ctx: any): any {
3105
+ const input = ctx;
3106
+ let fields = __ball_index(ctx, 'fields');
3107
+ let name = __ball_index(ctx, 'name');
3108
+ for (const f of fields) {
3109
+ if (__ball_eq(f.name, name)) {
3110
+ let val = f.value;
3111
+ if ((hasLiteral(val) && hasStringValue(val.literal))) {
3112
+ return val.literal.stringValue;
3113
+ }
3114
+ }
3115
+ }
3116
+ }
3117
+
3118
+ export function _isLiteralTrue(expr: any): any {
3119
+ const input = expr;
3120
+ if (__ball_eq(expr, null)) {
3121
+ return false;
3122
+ }
3123
+ if (hasLiteral(expr)) {
3124
+ return (hasBoolValue(expr.literal) && expr.literal.boolValue);
3125
+ }
3126
+ return false;
3127
+ }
3128
+
3129
+ export function _exprIsSet(expr: any): any {
3130
+ const input = expr;
3131
+ if (__ball_eq(expr, null)) {
3132
+ return false;
3133
+ }
3134
+ return ((((((hasCall(expr) || hasLiteral(expr)) || hasReference(expr)) || hasFieldAccess(expr)) || hasMessageCreation(expr)) || hasBlock(expr)) || hasLambda(expr));
3135
+ }
3136
+
3137
+ export function _collectReferencedVars(ctx: any): any {
3138
+ const input = ctx;
3139
+ let expr = __ball_index(ctx, 'expr');
3140
+ let vars = __ball_index(ctx, 'vars');
3141
+ if (__ball_eq(expr, null)) {
3142
+ return;
3143
+ }
3144
+ if (hasReference(expr)) {
3145
+ if (!(expr.reference.name.length === 0)) {
3146
+ if (!vars.includes(expr.reference.name)) {
3147
+ vars = (vars.push(expr.reference.name), vars);
3148
+ }
3149
+ }
3150
+ } else {
3151
+ if (hasCall(expr)) {
3152
+ if (hasInput(expr.call)) {
3153
+ _collectReferencedVars({ ['expr']: expr.call.input, ['vars']: vars });
3154
+ }
3155
+ } else {
3156
+ if (hasBlock(expr)) {
3157
+ for (const stmt of expr.block.statements) {
3158
+ if (hasLet(stmt)) {
3159
+ _collectReferencedVars({ ['expr']: stmt.let.value, ['vars']: vars });
3160
+ }
3161
+ if (hasExpression(stmt)) {
3162
+ _collectReferencedVars({ ['expr']: stmt.expression, ['vars']: vars });
3163
+ }
3164
+ }
3165
+ if (hasResult(expr.block)) {
3166
+ _collectReferencedVars({ ['expr']: expr.block.result, ['vars']: vars });
3167
+ }
3168
+ } else {
3169
+ if (hasMessageCreation(expr)) {
3170
+ for (const field of expr.messageCreation.fields) {
3171
+ _collectReferencedVars({ ['expr']: field.value, ['vars']: vars });
3172
+ }
3173
+ } else {
3174
+ if (hasFieldAccess(expr)) {
3175
+ if (hasObject(expr.fieldAccess)) {
3176
+ _collectReferencedVars({ ['expr']: expr.fieldAccess.object, ['vars']: vars });
3177
+ }
3178
+ } else {
3179
+ if (hasLambda(expr)) {
3180
+ _collectReferencedVars({ ['expr']: expr.lambda.body, ['vars']: vars });
3181
+ }
3182
+ }
3183
+ }
3184
+ }
3185
+ }
3186
+ }
3187
+ }
3188
+
3189
+ export function _collectMutatedVars(ctx: any): any {
3190
+ const input = ctx;
3191
+ let expr = __ball_index(ctx, 'expr');
3192
+ let vars = __ball_index(ctx, 'vars');
3193
+ if (__ball_eq(expr, null)) {
3194
+ return;
3195
+ }
3196
+ if (hasCall(expr)) {
3197
+ let call = expr.call;
3198
+ let module = ((call.module.length === 0) ? 'std' : call.module);
3199
+ if (((__ball_eq(module, 'std') && __ball_eq(call.function, 'assign')) && hasInput(call))) {
3200
+ let callInput = call.input;
3201
+ if (hasMessageCreation(callInput)) {
3202
+ let target = _getFieldValue({ ['fields']: callInput.messageCreation.fields, ['name']: 'target' });
3203
+ if ((!__ball_eq(target, null) && hasReference(target))) {
3204
+ if (!vars.includes(target.reference.name)) {
3205
+ vars = (vars.push(target.reference.name), vars);
3206
+ }
3207
+ }
3208
+ }
3209
+ }
3210
+ if (hasInput(call)) {
3211
+ _collectMutatedVars({ ['expr']: call.input, ['vars']: vars });
3212
+ }
3213
+ } else {
3214
+ if (hasBlock(expr)) {
3215
+ for (const stmt of expr.block.statements) {
3216
+ if (hasLet(stmt)) {
3217
+ _collectMutatedVars({ ['expr']: stmt.let.value, ['vars']: vars });
3218
+ }
3219
+ if (hasExpression(stmt)) {
3220
+ _collectMutatedVars({ ['expr']: stmt.expression, ['vars']: vars });
3221
+ }
3222
+ }
3223
+ if (hasResult(expr.block)) {
3224
+ _collectMutatedVars({ ['expr']: expr.block.result, ['vars']: vars });
3225
+ }
3226
+ } else {
3227
+ if (hasLambda(expr)) {
3228
+ _collectMutatedVars({ ['expr']: expr.lambda.body, ['vars']: vars });
3229
+ } else {
3230
+ if (hasMessageCreation(expr)) {
3231
+ for (const field of expr.messageCreation.fields) {
3232
+ _collectMutatedVars({ ['expr']: field.value, ['vars']: vars });
3233
+ }
3234
+ } else {
3235
+ if (hasFieldAccess(expr)) {
3236
+ if (hasObject(expr.fieldAccess)) {
3237
+ _collectMutatedVars({ ['expr']: expr.fieldAccess.object, ['vars']: vars });
3238
+ }
3239
+ }
3240
+ }
3241
+ }
3242
+ }
3243
+ }
3244
+ }
3245
+
3246
+ export function _exprHasExitSignal(expr: any): any {
3247
+ const input = expr;
3248
+ if (__ball_eq(expr, null)) {
3249
+ return false;
3250
+ }
3251
+ if (hasCall(expr)) {
3252
+ let call = expr.call;
3253
+ let module = ((call.module.length === 0) ? 'std' : call.module);
3254
+ if ((__ball_eq(module, 'std') && ((__ball_eq(call.function, 'break') || __ball_eq(call.function, 'return')) || __ball_eq(call.function, 'throw')))) {
3255
+ return true;
3256
+ }
3257
+ if (hasInput(call)) {
3258
+ if (_exprHasExitSignal(call.input)) {
3259
+ return true;
3260
+ }
3261
+ }
3262
+ return false;
3263
+ } else {
3264
+ if (hasBlock(expr)) {
3265
+ for (const stmt of expr.block.statements) {
3266
+ if ((hasLet(stmt) && _exprHasExitSignal(stmt.let.value))) {
3267
+ return true;
3268
+ }
3269
+ if ((hasExpression(stmt) && _exprHasExitSignal(stmt.expression))) {
3270
+ return true;
3271
+ }
3272
+ }
3273
+ if ((hasResult(expr.block) && _exprHasExitSignal(expr.block.result))) {
3274
+ return true;
3275
+ }
3276
+ return false;
3277
+ } else {
3278
+ if (hasLambda(expr)) {
3279
+ return false;
3280
+ } else {
3281
+ if (hasMessageCreation(expr)) {
3282
+ for (const field of expr.messageCreation.fields) {
3283
+ if (_exprHasExitSignal(field.value)) {
3284
+ return true;
3285
+ }
3286
+ }
3287
+ return false;
3288
+ } else {
3289
+ if (hasFieldAccess(expr)) {
3290
+ if (hasObject(expr.fieldAccess)) {
3291
+ return _exprHasExitSignal(expr.fieldAccess.object);
3292
+ }
3293
+ return false;
3294
+ }
3295
+ }
3296
+ }
3297
+ }
3298
+ }
3299
+ return false;
3300
+ }