laser 0.7.0.pre1
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.
- data/.document +5 -0
- data/.rspec +1 -0
- data/Gemfile +14 -0
- data/LICENSE +661 -0
- data/README.md +158 -0
- data/Rakefile +104 -0
- data/VERSION +1 -0
- data/bin/laser +7 -0
- data/design_docs/goals.md +57 -0
- data/design_docs/object_regex.md +426 -0
- data/design_docs/type_annotations.md +80 -0
- data/ext/laser/BasicBlock.cpp +572 -0
- data/ext/laser/BasicBlock.h +118 -0
- data/ext/laser/extconf.rb +3 -0
- data/features/laser.feature +25 -0
- data/features/step_definitions/laser_steps.rb +39 -0
- data/features/support/env.rb +14 -0
- data/features/support/testdata/1_input +1 -0
- data/features/support/testdata/1_output +1 -0
- data/features/support/testdata/2_input +4 -0
- data/features/support/testdata/2_output +4 -0
- data/features/support/testdata/3_input +8 -0
- data/features/support/testdata/3_output +11 -0
- data/features/support/testdata/4_input +5 -0
- data/features/support/testdata/4_output +5 -0
- data/features/support/testdata/5_input +13 -0
- data/laser.gemspec +382 -0
- data/lib/laser.rb +98 -0
- data/lib/laser/analysis/annotations.rb +95 -0
- data/lib/laser/analysis/annotations/annotation_config.yaml +3 -0
- data/lib/laser/analysis/annotations/comment_attachment_annotation.rb +66 -0
- data/lib/laser/analysis/annotations/node_pointers_annotation.rb +36 -0
- data/lib/laser/analysis/annotations/runtime_annotation.rb +55 -0
- data/lib/laser/analysis/argument_expansion.rb +132 -0
- data/lib/laser/analysis/arity.rb +34 -0
- data/lib/laser/analysis/bindings.rb +144 -0
- data/lib/laser/analysis/bootstrap/bootstrap.rb +298 -0
- data/lib/laser/analysis/bootstrap/laser_class.rb +106 -0
- data/lib/laser/analysis/bootstrap/laser_method.rb +255 -0
- data/lib/laser/analysis/bootstrap/laser_module.rb +403 -0
- data/lib/laser/analysis/bootstrap/laser_module_copy.rb +74 -0
- data/lib/laser/analysis/bootstrap/laser_object.rb +69 -0
- data/lib/laser/analysis/bootstrap/laser_proc.rb +150 -0
- data/lib/laser/analysis/bootstrap/laser_singleton_class.rb +44 -0
- data/lib/laser/analysis/comments.rb +35 -0
- data/lib/laser/analysis/control_flow.rb +28 -0
- data/lib/laser/analysis/control_flow/alias_analysis.rb +31 -0
- data/lib/laser/analysis/control_flow/basic_block.rb +105 -0
- data/lib/laser/analysis/control_flow/cfg_builder.rb +2505 -0
- data/lib/laser/analysis/control_flow/cfg_instruction.rb +190 -0
- data/lib/laser/analysis/control_flow/constant_propagation.rb +742 -0
- data/lib/laser/analysis/control_flow/control_flow_graph.rb +370 -0
- data/lib/laser/analysis/control_flow/lifetime_analysis.rb +91 -0
- data/lib/laser/analysis/control_flow/method_call_search.rb +26 -0
- data/lib/laser/analysis/control_flow/raise_properties.rb +25 -0
- data/lib/laser/analysis/control_flow/simulation.rb +385 -0
- data/lib/laser/analysis/control_flow/static_single_assignment.rb +185 -0
- data/lib/laser/analysis/control_flow/unreachability_analysis.rb +57 -0
- data/lib/laser/analysis/control_flow/unused_variables.rb +91 -0
- data/lib/laser/analysis/control_flow/yield_properties.rb +103 -0
- data/lib/laser/analysis/errors.rb +131 -0
- data/lib/laser/analysis/laser_utils.rb +18 -0
- data/lib/laser/analysis/lexical_analysis.rb +172 -0
- data/lib/laser/analysis/method_call.rb +68 -0
- data/lib/laser/analysis/protocol_registry.rb +30 -0
- data/lib/laser/analysis/scope.rb +118 -0
- data/lib/laser/analysis/sexp.rb +159 -0
- data/lib/laser/analysis/sexp_analysis.rb +40 -0
- data/lib/laser/analysis/sexp_extensions/constant_extraction.rb +115 -0
- data/lib/laser/analysis/sexp_extensions/source_location.rb +164 -0
- data/lib/laser/analysis/sexp_extensions/type_inference.rb +47 -0
- data/lib/laser/analysis/signature.rb +76 -0
- data/lib/laser/analysis/special_methods/send.rb +67 -0
- data/lib/laser/analysis/unused_methods.rb +21 -0
- data/lib/laser/analysis/visitor.rb +141 -0
- data/lib/laser/annotation_parser/annotations.treetop +126 -0
- data/lib/laser/annotation_parser/annotations_parser.rb +748 -0
- data/lib/laser/annotation_parser/class_annotations.treetop +82 -0
- data/lib/laser/annotation_parser/class_annotations_parser.rb +654 -0
- data/lib/laser/annotation_parser/overload.treetop +24 -0
- data/lib/laser/annotation_parser/overload_parser.rb +167 -0
- data/lib/laser/annotation_parser/parsers.rb +6 -0
- data/lib/laser/annotation_parser/structural.treetop +37 -0
- data/lib/laser/annotation_parser/structural_parser.rb +406 -0
- data/lib/laser/annotation_parser/useful_parsers.treetop +47 -0
- data/lib/laser/annotation_parser/useful_parsers_parser.rb +674 -0
- data/lib/laser/rake/task.rb +46 -0
- data/lib/laser/runner.rb +189 -0
- data/lib/laser/scanner.rb +169 -0
- data/lib/laser/standard_library/_thread.rb +110 -0
- data/lib/laser/standard_library/abbrev.rb +103 -0
- data/lib/laser/standard_library/array.rb +418 -0
- data/lib/laser/standard_library/base64.rb +91 -0
- data/lib/laser/standard_library/basic_object.rb +55 -0
- data/lib/laser/standard_library/benchmark.rb +556 -0
- data/lib/laser/standard_library/bignum.rb +185 -0
- data/lib/laser/standard_library/cgi.rb +275 -0
- data/lib/laser/standard_library/cgi/cookie.rb +147 -0
- data/lib/laser/standard_library/cgi/core.rb +791 -0
- data/lib/laser/standard_library/cgi/html.rb +1021 -0
- data/lib/laser/standard_library/cgi/session.rb +537 -0
- data/lib/laser/standard_library/cgi/session/pstore.rb +111 -0
- data/lib/laser/standard_library/cgi/util.rb +188 -0
- data/lib/laser/standard_library/class_definitions.rb +333 -0
- data/lib/laser/standard_library/comparable.rb +125 -0
- data/lib/laser/standard_library/complex.rb +162 -0
- data/lib/laser/standard_library/enumerable.rb +178 -0
- data/lib/laser/standard_library/exceptions.rb +135 -0
- data/lib/laser/standard_library/fixnum.rb +188 -0
- data/lib/laser/standard_library/float.rb +180 -0
- data/lib/laser/standard_library/hash.rb +237 -0
- data/lib/laser/standard_library/integer.rb +123 -0
- data/lib/laser/standard_library/laser_magic.rb +7 -0
- data/lib/laser/standard_library/nil_false_true.rb +113 -0
- data/lib/laser/standard_library/numbers.rb +192 -0
- data/lib/laser/standard_library/proc.rb +31 -0
- data/lib/laser/standard_library/set.rb +1348 -0
- data/lib/laser/standard_library/string.rb +666 -0
- data/lib/laser/standard_library/stringio.rb +2 -0
- data/lib/laser/standard_library/symbol.rb +125 -0
- data/lib/laser/standard_library/tsort.rb +242 -0
- data/lib/laser/support/acts_as_struct.rb +66 -0
- data/lib/laser/support/frequency.rb +55 -0
- data/lib/laser/support/inheritable_attributes.rb +145 -0
- data/lib/laser/support/module_extensions.rb +94 -0
- data/lib/laser/support/placeholder_object.rb +13 -0
- data/lib/laser/third_party/rgl/adjacency.rb +221 -0
- data/lib/laser/third_party/rgl/base.rb +228 -0
- data/lib/laser/third_party/rgl/bidirectional.rb +39 -0
- data/lib/laser/third_party/rgl/condensation.rb +47 -0
- data/lib/laser/third_party/rgl/connected_components.rb +138 -0
- data/lib/laser/third_party/rgl/control_flow.rb +170 -0
- data/lib/laser/third_party/rgl/depth_first_spanning_tree.rb +37 -0
- data/lib/laser/third_party/rgl/dominators.rb +124 -0
- data/lib/laser/third_party/rgl/dot.rb +93 -0
- data/lib/laser/third_party/rgl/graphxml.rb +51 -0
- data/lib/laser/third_party/rgl/implicit.rb +174 -0
- data/lib/laser/third_party/rgl/mutable.rb +117 -0
- data/lib/laser/third_party/rgl/rdot.rb +445 -0
- data/lib/laser/third_party/rgl/topsort.rb +72 -0
- data/lib/laser/third_party/rgl/transitivity.rb +180 -0
- data/lib/laser/third_party/rgl/traversal.rb +348 -0
- data/lib/laser/types/types.rb +433 -0
- data/lib/laser/version.rb +14 -0
- data/lib/laser/warning.rb +149 -0
- data/lib/laser/warning_sets/default.yml +13 -0
- data/lib/laser/warnings/assignment_in_condition.rb +20 -0
- data/lib/laser/warnings/comment_spacing.rb +31 -0
- data/lib/laser/warnings/extra_blank_lines.rb +30 -0
- data/lib/laser/warnings/extra_whitespace.rb +16 -0
- data/lib/laser/warnings/hash_symbol_18_warning.rb +63 -0
- data/lib/laser/warnings/hash_symbol_19_warning.rb +29 -0
- data/lib/laser/warnings/line_length.rb +115 -0
- data/lib/laser/warnings/misaligned_unindentation.rb +17 -0
- data/lib/laser/warnings/operator_spacing.rb +68 -0
- data/lib/laser/warnings/parens_on_declaration.rb +30 -0
- data/lib/laser/warnings/rescue_exception.rb +42 -0
- data/lib/laser/warnings/semicolon.rb +25 -0
- data/lib/laser/warnings/sexp_errors.rb +24 -0
- data/lib/laser/warnings/uncalled_method_warning.rb +7 -0
- data/lib/laser/warnings/useless_double_quotes.rb +38 -0
- data/spec/analysis_specs/annotations_spec.rb +47 -0
- data/spec/analysis_specs/annotations_specs/comment_attachment_spec.rb +68 -0
- data/spec/analysis_specs/annotations_specs/node_pointers_annotation_spec.rb +90 -0
- data/spec/analysis_specs/annotations_specs/runtime_annotation_spec.rb +135 -0
- data/spec/analysis_specs/annotations_specs/spec_helper.rb +33 -0
- data/spec/analysis_specs/argument_expansion_spec.rb +113 -0
- data/spec/analysis_specs/bindings_spec.rb +36 -0
- data/spec/analysis_specs/comment_spec.rb +93 -0
- data/spec/analysis_specs/control_flow_specs/cfg_instruction_spec.rb +111 -0
- data/spec/analysis_specs/control_flow_specs/constant_propagation_spec.rb +560 -0
- data/spec/analysis_specs/control_flow_specs/control_flow_graph_spec.rb +5 -0
- data/spec/analysis_specs/control_flow_specs/raise_properties_spec.rb +310 -0
- data/spec/analysis_specs/control_flow_specs/raise_type_inference_spec.rb +301 -0
- data/spec/analysis_specs/control_flow_specs/return_type_inference_spec.rb +431 -0
- data/spec/analysis_specs/control_flow_specs/simulation_spec.rb +158 -0
- data/spec/analysis_specs/control_flow_specs/spec_helper.rb +110 -0
- data/spec/analysis_specs/control_flow_specs/tuple_misuse_inference_spec.rb +125 -0
- data/spec/analysis_specs/control_flow_specs/unreachability_analysis_spec.rb +76 -0
- data/spec/analysis_specs/control_flow_specs/unused_variable_spec.rb +99 -0
- data/spec/analysis_specs/control_flow_specs/yield_properties_spec.rb +372 -0
- data/spec/analysis_specs/error_spec.rb +30 -0
- data/spec/analysis_specs/laser_class_spec.rb +322 -0
- data/spec/analysis_specs/lexical_analysis_spec.rb +184 -0
- data/spec/analysis_specs/protocol_registry_spec.rb +63 -0
- data/spec/analysis_specs/scope_annotation_spec.rb +1013 -0
- data/spec/analysis_specs/scope_spec.rb +126 -0
- data/spec/analysis_specs/sexp_analysis_spec.rb +30 -0
- data/spec/analysis_specs/sexp_extension_specs/constant_extraction_spec.rb +309 -0
- data/spec/analysis_specs/sexp_extension_specs/source_location_spec.rb +231 -0
- data/spec/analysis_specs/sexp_extension_specs/spec_helper.rb +1 -0
- data/spec/analysis_specs/sexp_extension_specs/type_inference_spec.rb +252 -0
- data/spec/analysis_specs/sexp_spec.rb +167 -0
- data/spec/analysis_specs/spec_helper.rb +27 -0
- data/spec/analysis_specs/unused_methods_spec.rb +65 -0
- data/spec/analysis_specs/visitor_spec.rb +64 -0
- data/spec/annotation_parser_specs/annotations_parser_spec.rb +89 -0
- data/spec/annotation_parser_specs/class_annotation_parser_spec.rb +120 -0
- data/spec/annotation_parser_specs/overload_parser_spec.rb +39 -0
- data/spec/annotation_parser_specs/parsers_spec.rb +14 -0
- data/spec/annotation_parser_specs/spec_helper.rb +1 -0
- data/spec/annotation_parser_specs/structural_parser_spec.rb +67 -0
- data/spec/laser_spec.rb +14 -0
- data/spec/rake_specs/spec_helper.rb +1 -0
- data/spec/rake_specs/task_spec.rb +67 -0
- data/spec/runner_spec.rb +207 -0
- data/spec/scanner_spec.rb +75 -0
- data/spec/spec_helper.rb +121 -0
- data/spec/standard_library/exceptions_spec.rb +19 -0
- data/spec/standard_library/globals_spec.rb +14 -0
- data/spec/standard_library/set_spec.rb +31 -0
- data/spec/standard_library/spec_helper.rb +1 -0
- data/spec/standard_library/standard_library_spec.rb +302 -0
- data/spec/support_specs/acts_as_struct_spec.rb +94 -0
- data/spec/support_specs/frequency_spec.rb +23 -0
- data/spec/support_specs/module_extensions_spec.rb +117 -0
- data/spec/support_specs/spec_helper.rb +1 -0
- data/spec/type_specs/spec_helper.rb +1 -0
- data/spec/type_specs/types_spec.rb +133 -0
- data/spec/warning_spec.rb +95 -0
- data/spec/warning_specs/assignment_in_condition_spec.rb +68 -0
- data/spec/warning_specs/comment_spacing_spec.rb +65 -0
- data/spec/warning_specs/extra_blank_lines_spec.rb +70 -0
- data/spec/warning_specs/extra_whitespace_spec.rb +33 -0
- data/spec/warning_specs/hash_symbol_18_warning_spec.rb +89 -0
- data/spec/warning_specs/hash_symbol_19_warning_spec.rb +63 -0
- data/spec/warning_specs/line_length_spec.rb +173 -0
- data/spec/warning_specs/misaligned_unindentation_spec.rb +35 -0
- data/spec/warning_specs/operator_spacing_spec.rb +104 -0
- data/spec/warning_specs/parens_on_declaration_spec.rb +57 -0
- data/spec/warning_specs/rescue_exception_spec.rb +105 -0
- data/spec/warning_specs/semicolon_spec.rb +58 -0
- data/spec/warning_specs/spec_helper.rb +1 -0
- data/spec/warning_specs/useless_double_quotes_spec.rb +74 -0
- data/status_reports/2010/12/2010-12-14.md +163 -0
- data/status_reports/2010/12/2010-12-23.md +298 -0
- data/status_reports/2010/12/2010-12-24.md +6 -0
- data/test/third_party_tests/rgl_tests/TestComponents.rb +65 -0
- data/test/third_party_tests/rgl_tests/TestCycles.rb +61 -0
- data/test/third_party_tests/rgl_tests/TestDirectedGraph.rb +125 -0
- data/test/third_party_tests/rgl_tests/TestDot.rb +18 -0
- data/test/third_party_tests/rgl_tests/TestEdge.rb +34 -0
- data/test/third_party_tests/rgl_tests/TestGraph.rb +71 -0
- data/test/third_party_tests/rgl_tests/TestGraphXML.rb +57 -0
- data/test/third_party_tests/rgl_tests/TestImplicit.rb +52 -0
- data/test/third_party_tests/rgl_tests/TestRdot.rb +863 -0
- data/test/third_party_tests/rgl_tests/TestTransitivity.rb +129 -0
- data/test/third_party_tests/rgl_tests/TestTraversal.rb +220 -0
- data/test/third_party_tests/rgl_tests/TestUnDirectedGraph.rb +102 -0
- data/test/third_party_tests/rgl_tests/examples/north/Graph.log +128 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.0.graphml +28 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.1.graphml +28 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.11.graphml +31 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.12.graphml +27 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.13.graphml +27 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.14.graphml +27 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.15.graphml +26 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.16.graphml +26 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.17.graphml +26 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.19.graphml +37 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.2.graphml +28 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.20.graphml +38 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.22.graphml +43 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.24.graphml +30 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.25.graphml +45 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.27.graphml +38 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.28.graphml +30 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.29.graphml +38 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.3.graphml +26 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.30.graphml +34 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.31.graphml +42 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.34.graphml +42 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.37.graphml +28 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.38.graphml +38 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.39.graphml +36 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.4.graphml +26 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.40.graphml +37 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.41.graphml +37 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.42.graphml +26 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.45.graphml +28 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.46.graphml +32 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.5.graphml +31 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.50.graphml +30 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.56.graphml +29 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.57.graphml +32 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.58.graphml +32 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.6.graphml +26 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.60.graphml +32 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.61.graphml +34 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.62.graphml +34 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.68.graphml +30 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.69.graphml +32 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.7.graphml +29 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.70.graphml +26 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.71.graphml +27 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.72.graphml +28 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.74.graphml +29 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.75.graphml +29 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.78.graphml +27 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.79.graphml +34 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.8.graphml +29 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.80.graphml +34 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.82.graphml +35 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.83.graphml +32 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.85.graphml +34 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.86.graphml +34 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.88.graphml +37 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.89.graphml +29 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.9.graphml +26 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.90.graphml +32 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.91.graphml +31 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.92.graphml +26 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.93.graphml +32 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.94.graphml +34 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.12.8.graphml +40 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.14.9.graphml +36 -0
- data/test/third_party_tests/rgl_tests/test_helper.rb +7 -0
- data/test/third_party_tests/test_inheritable_attributes.rb +187 -0
- metadata +470 -0
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
require 'set'
|
|
2
|
+
module Laser
|
|
3
|
+
module Analysis
|
|
4
|
+
module ControlFlow
|
|
5
|
+
class Instruction < BasicObject
|
|
6
|
+
attr_reader :node, :block, :body, :ignore_privacy
|
|
7
|
+
attr_accessor :raise_frequency, :raise_type
|
|
8
|
+
def initialize(body, opts={})
|
|
9
|
+
@body = body
|
|
10
|
+
@node = opts[:node]
|
|
11
|
+
@block = opts[:block]
|
|
12
|
+
@raise_frequency = :unknown
|
|
13
|
+
@raise_type = Types::EMPTY
|
|
14
|
+
@ignore_privacy = opts[:ignore_privacy]
|
|
15
|
+
@true_successor = @false_successor = nil
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def type
|
|
19
|
+
@body[0]
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def class
|
|
23
|
+
Instruction
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def ==(other)
|
|
27
|
+
@body == other.body
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def deep_dup(temp_lookup, opts={})
|
|
31
|
+
new_body = @body[1..-1].map do |arg|
|
|
32
|
+
case arg
|
|
33
|
+
when Bindings::ConstantBinding then arg
|
|
34
|
+
when Bindings::Base then temp_lookup[arg]
|
|
35
|
+
when ::Hash
|
|
36
|
+
if arg[:block]
|
|
37
|
+
then arg.merge(block: temp_lookup[arg[:block]])
|
|
38
|
+
else arg
|
|
39
|
+
end
|
|
40
|
+
else arg.dup rescue arg
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
new_body.unshift(self[0]) # self[0] always symbol
|
|
45
|
+
new_opts = {node: @node, block: temp_lookup[@block], ignore_privacy: @ignore_privacy}.merge(opts)
|
|
46
|
+
self.class.new(new_body, new_opts)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def method_missing(meth, *args, &blk)
|
|
50
|
+
@body.send(meth, *args, &blk)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def method_call?
|
|
54
|
+
[:call, :call_vararg, :super, :super_vararg].include?(type)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def require_method_call
|
|
58
|
+
unless method_call?
|
|
59
|
+
raise TypeError.new("#possible_methods is not defined on #{type} instructions.")
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def require_branch(method_needed='the requested operation')
|
|
64
|
+
unless type == :branch
|
|
65
|
+
raise TypeError.new("#{method_needed} is not defined on #{type} instructions.")
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def true_successor
|
|
70
|
+
require_branch('#true_successor')
|
|
71
|
+
calculate_branch_successors
|
|
72
|
+
return @true_successor
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def false_successor
|
|
76
|
+
require_branch('#false_successor')
|
|
77
|
+
calculate_branch_successors
|
|
78
|
+
return @false_successor
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def calculate_branch_successors
|
|
82
|
+
return if @true_successor
|
|
83
|
+
successors = block.successors.to_a
|
|
84
|
+
if successors[0].name == self[2]
|
|
85
|
+
then @true_successor, @false_successor = successors[0..1]
|
|
86
|
+
else @false_successor, @true_successor = successors[0..1]
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def possible_public_methods
|
|
91
|
+
require_method_call
|
|
92
|
+
if type == :call || type == :call_vararg
|
|
93
|
+
if Bindings::ConstantBinding === self[2]
|
|
94
|
+
[self[2].value.singleton_class.public_instance_method(self[3])].compact
|
|
95
|
+
elsif LaserObject === self[2].value
|
|
96
|
+
[self[2].value.klass.public_instance_method(self[3])].compact
|
|
97
|
+
else
|
|
98
|
+
self[2].expr_type.public_matching_methods(self[3])
|
|
99
|
+
end
|
|
100
|
+
else
|
|
101
|
+
#TODO(adgar): SUPER
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def possible_methods(opts)
|
|
106
|
+
require_method_call
|
|
107
|
+
if type == :call || type == :call_vararg
|
|
108
|
+
if Bindings::ConstantBinding === self[2]
|
|
109
|
+
[self[2].value.singleton_class.instance_method(self[3])].compact
|
|
110
|
+
elsif LaserObject === self[2].value
|
|
111
|
+
[self[2].value.klass.instance_method(self[3])].compact
|
|
112
|
+
else
|
|
113
|
+
self[2].expr_type.matching_methods(self[3])
|
|
114
|
+
end
|
|
115
|
+
else
|
|
116
|
+
[opts[:method].owner.parent.instance_method(opts[:method].name)]
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# Gets all bindings that are explicitly set in this instruction (no aliasing
|
|
121
|
+
# concerns)
|
|
122
|
+
def explicit_targets
|
|
123
|
+
case self[0]
|
|
124
|
+
when :assign, :call, :call_vararg, :super, :super_vararg, :lambda, :phi
|
|
125
|
+
self[1] ? ::Set[self[1]] : ::Set[]
|
|
126
|
+
else
|
|
127
|
+
::Set[]
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def block_operand
|
|
132
|
+
::Hash === last ? last[:block] : nil
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def replace_block_operand(new_block)
|
|
136
|
+
last[:block] = new_block
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# Gets all bindings that are operands in this instruction
|
|
140
|
+
def operands
|
|
141
|
+
self[operand_range].select { |x| Bindings::Base === x && x != Bootstrap::VISIBILITY_STACK }
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
# Replaces the operands with a new list. Used by SSA renaming.
|
|
145
|
+
def replace_operands(new_operands)
|
|
146
|
+
# splice in new operands: replace bindings with bindings.
|
|
147
|
+
index = operand_range.begin
|
|
148
|
+
while new_operands.any? && index < @body.size
|
|
149
|
+
if Bindings::Base === self[index] && self[index] != Bootstrap::VISIBILITY_STACK
|
|
150
|
+
self[index] = new_operands.shift
|
|
151
|
+
end
|
|
152
|
+
index += 1
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
# Replaces a target of the instruction. Used by SSA renaming.
|
|
157
|
+
# Currently, all instructions only have at most 1 target.
|
|
158
|
+
def replace_target(original_target, new_target)
|
|
159
|
+
if self[1] == original_target
|
|
160
|
+
self[1] = new_target
|
|
161
|
+
else
|
|
162
|
+
raise ArgumentError.new("#{original_target.inspect} is not a "+
|
|
163
|
+
"target of #{self.inspect}")
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
private
|
|
168
|
+
|
|
169
|
+
def operand_range
|
|
170
|
+
case self[0]
|
|
171
|
+
when :assign, :call_vararg, :super, :super_vararg, :lambda, :phi
|
|
172
|
+
2..-1
|
|
173
|
+
when :declare
|
|
174
|
+
case self[1]
|
|
175
|
+
when :alias then 2..-1
|
|
176
|
+
when :expect_tuple_size then 4..-1
|
|
177
|
+
end
|
|
178
|
+
when :call
|
|
179
|
+
# check for hardcoded call on a constant class. Used by literals.
|
|
180
|
+
if Bindings::ConstantBinding === self[2]
|
|
181
|
+
then 3..-1
|
|
182
|
+
else 2..-1
|
|
183
|
+
end
|
|
184
|
+
else 1..-1
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
end
|
|
@@ -0,0 +1,742 @@
|
|
|
1
|
+
module Laser
|
|
2
|
+
module Analysis
|
|
3
|
+
module ControlFlow
|
|
4
|
+
INAPPLICABLE = PlaceholderObject.new('INAPPLICABLE')
|
|
5
|
+
|
|
6
|
+
# Sparse Conditional Constant Propagation: Wegman and Zadeck
|
|
7
|
+
# Love those IBMers
|
|
8
|
+
# Using Morgan's implementation though.
|
|
9
|
+
module ConstantPropagation
|
|
10
|
+
attr_reader :constants
|
|
11
|
+
|
|
12
|
+
# Only public method: mutably turns the CFG into a constant-propagated
|
|
13
|
+
# one. Each binding will have a value assigned to it afterward: either
|
|
14
|
+
# the constant, as a Ruby object (or a proxy to one), UNDEFINED, or VARYING.
|
|
15
|
+
def perform_constant_propagation(opts={})
|
|
16
|
+
opts = {fixed_methods: {}, initial_block: self.enter}.merge(opts)
|
|
17
|
+
|
|
18
|
+
initialize_constant_propagation(opts)
|
|
19
|
+
visited = Set.new
|
|
20
|
+
worklist = Set.new
|
|
21
|
+
blocklist = Set[opts[:initial_block]]
|
|
22
|
+
while worklist.any? || blocklist.any?
|
|
23
|
+
while worklist.any?
|
|
24
|
+
constant_propagation_for_instruction(
|
|
25
|
+
worklist.pop, blocklist, worklist, opts)
|
|
26
|
+
end
|
|
27
|
+
while blocklist.any?
|
|
28
|
+
constant_propagation_for_block(
|
|
29
|
+
blocklist.pop, visited, blocklist, worklist, opts)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
teardown_constant_propagation
|
|
33
|
+
@constants = find_remaining_constants
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
def find_remaining_constants
|
|
39
|
+
result = {}
|
|
40
|
+
all_variables.select do |variable|
|
|
41
|
+
variable.value != VARYING && variable.value != UNDEFINED
|
|
42
|
+
end.each do |constant|
|
|
43
|
+
result[constant] = constant.value
|
|
44
|
+
end
|
|
45
|
+
result
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Initializes the variables, formals, and edges for constant propagation.
|
|
49
|
+
# Morgan, p. 201
|
|
50
|
+
def initialize_constant_propagation(opts)
|
|
51
|
+
@constants.clear
|
|
52
|
+
# value cells for :call nodes that discard their argument.
|
|
53
|
+
@cp_private_cells = Hash.new do |h, k|
|
|
54
|
+
h[k] = Bindings::TemporaryBinding.new(k.hash.to_s, UNDEFINED)
|
|
55
|
+
h[k].inferred_type = nil
|
|
56
|
+
h[k]
|
|
57
|
+
end
|
|
58
|
+
@cp_self_cells = Hash.new do |h, k|
|
|
59
|
+
h[k] = Bindings::TemporaryBinding.new(k.hash.to_s, VARYING)
|
|
60
|
+
h[k].inferred_type = real_self_type
|
|
61
|
+
h[k]
|
|
62
|
+
end
|
|
63
|
+
clear_analyses unless opts[:no_wipe]
|
|
64
|
+
@_cp_fixed_methods = opts[:fixed_methods]
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def teardown_constant_propagation
|
|
68
|
+
@cp_private_cells.clear
|
|
69
|
+
@cp_self_cells.clear
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def cp_cell_for(instruction)
|
|
73
|
+
@cp_private_cells[instruction]
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def cp_self_cell(instruction)
|
|
77
|
+
@cp_self_cells[instruction]
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Simulates a block. As we know, phi nodes execute simultaneously
|
|
81
|
+
# and immediately upon block entry, so first we check phi nodes to
|
|
82
|
+
# see if they form a constant. This happens unconditionally, as
|
|
83
|
+
# phi nodes must be checked repeatedly.
|
|
84
|
+
#
|
|
85
|
+
# Then, if the block hasn't been visited before, simulate the normal
|
|
86
|
+
# instructions, and mark it so it is not visited again.
|
|
87
|
+
#
|
|
88
|
+
# Morgan, p.200
|
|
89
|
+
def constant_propagation_for_block(block, visited, blocklist, worklist, opts)
|
|
90
|
+
block.phi_nodes.each do |phi_node|
|
|
91
|
+
constant_propagation_for_instruction(
|
|
92
|
+
phi_node, blocklist, worklist, opts)
|
|
93
|
+
end
|
|
94
|
+
if visited.add?(block)
|
|
95
|
+
block.natural_instructions.each do |instruction|
|
|
96
|
+
constant_propagation_for_instruction(
|
|
97
|
+
instruction, blocklist, worklist, opts)
|
|
98
|
+
end
|
|
99
|
+
if block.fall_through_block?
|
|
100
|
+
block.successors.each do |succ|
|
|
101
|
+
constant_propagation_consider_edge block, succ, blocklist
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
private :constant_propagation_for_block
|
|
107
|
+
|
|
108
|
+
def constant_propagation_for_instruction(instruction, blocklist, worklist, opts)
|
|
109
|
+
return if instruction.type != :phi && instruction.block.executed_predecessors.empty?
|
|
110
|
+
Laser.debug_p(instruction)
|
|
111
|
+
block = instruction.block
|
|
112
|
+
case instruction.type
|
|
113
|
+
when :assign, :phi
|
|
114
|
+
if constant_propagation_evaluate(instruction)
|
|
115
|
+
add_target_uses_to_worklist instruction, worklist
|
|
116
|
+
end
|
|
117
|
+
when :call, :call_vararg, :super, :super_vararg
|
|
118
|
+
changed, raised, raise_changed = constant_propagation_for_call(instruction, opts)
|
|
119
|
+
if changed
|
|
120
|
+
if instruction[1] && instruction[1].value != UNDEFINED
|
|
121
|
+
add_target_uses_to_worklist instruction, worklist
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
if raise_changed && instruction == block.instructions.last
|
|
125
|
+
raise_capture_insn = instruction.block.exception_successors.first.instructions.first
|
|
126
|
+
raise_capture_insn[1].bind!(VARYING)
|
|
127
|
+
raise_capture_insn[1].inferred_type = instruction.raise_type
|
|
128
|
+
add_target_uses_to_worklist raise_capture_insn, worklist
|
|
129
|
+
end
|
|
130
|
+
if raised != instruction.raise_frequency && instruction == block.instructions.last
|
|
131
|
+
instruction.raise_frequency = raised
|
|
132
|
+
successors = case raised
|
|
133
|
+
when :unknown then []
|
|
134
|
+
when Frequency::MAYBE then block.successors
|
|
135
|
+
when Frequency::NEVER then block.normal_successors
|
|
136
|
+
when Frequency::ALWAYS then block.abnormal_successors
|
|
137
|
+
end
|
|
138
|
+
successors.each do |succ|
|
|
139
|
+
constant_propagation_consider_edge block, succ, blocklist
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
when :branch
|
|
143
|
+
constant_propagation_for_branch(instruction, blocklist)
|
|
144
|
+
when :jump, :raise, :return
|
|
145
|
+
succ = block.real_successors.first
|
|
146
|
+
constant_propagation_consider_edge block, succ, blocklist
|
|
147
|
+
when :declare
|
|
148
|
+
case instruction[1]
|
|
149
|
+
when :expect_tuple_size
|
|
150
|
+
# check array/tuple size against expectation - issue warning if fail
|
|
151
|
+
validate_tuple_expectation(instruction)
|
|
152
|
+
end
|
|
153
|
+
# don't do shit
|
|
154
|
+
else
|
|
155
|
+
raise ArgumentError("Unknown instruction evaluation type: #{instruction.type}")
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def add_target_uses_to_worklist(instruction, worklist)
|
|
160
|
+
uses = if instruction[0] == :call && instruction[2] == ClassRegistry['Laser#Magic'].binding &&
|
|
161
|
+
instruction[3] == :set_global
|
|
162
|
+
Scope::GlobalScope.lookup(instruction[4].value).uses
|
|
163
|
+
elsif instruction[0] == :call && instruction[3] == :instance_variable_set &&
|
|
164
|
+
instruction[4] != UNDEFINED && instruction[4] != VARYING
|
|
165
|
+
receiver = instruction[2]
|
|
166
|
+
klass = LaserObject === receiver.value ? receiver.value.normal_class : receiver.expr_type.possible_classes.first
|
|
167
|
+
klass.instance_variable(instruction[4].value).uses
|
|
168
|
+
else
|
|
169
|
+
instruction.explicit_targets.map(&:uses).inject(:|) || []
|
|
170
|
+
end
|
|
171
|
+
uses.each { |use| worklist.add(use) if use }
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
# Examines the branch for newly executable edges, and adds them to
|
|
175
|
+
# the blocklist.
|
|
176
|
+
def constant_propagation_for_branch(instruction, blocklist)
|
|
177
|
+
block = instruction.block
|
|
178
|
+
executable_successors = constant_propagation_branch_successors(instruction)
|
|
179
|
+
executable_successors.each do |succ|
|
|
180
|
+
constant_propagation_consider_edge block, succ, blocklist
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def constant_propagation_branch_successors(instruction)
|
|
185
|
+
condition = instruction[1]
|
|
186
|
+
case condition.value
|
|
187
|
+
when VARYING
|
|
188
|
+
if Types.overlap?(condition.expr_type, Types::FALSY)
|
|
189
|
+
instruction.block.real_successors
|
|
190
|
+
else
|
|
191
|
+
[instruction.true_successor]
|
|
192
|
+
end
|
|
193
|
+
when UNDEFINED then []
|
|
194
|
+
when nil, false then [instruction.false_successor]
|
|
195
|
+
else [instruction.true_successor]
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
def constant_propagation_consider_edge(block, succ, blocklist)
|
|
200
|
+
if !is_executable?(block, succ) && !is_fake?(block, succ)
|
|
201
|
+
add_flag(block, succ, ControlFlowGraph::EDGE_EXECUTABLE)
|
|
202
|
+
blocklist << succ
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
def constant_propagation_for_call(instruction, cp_opts)
|
|
207
|
+
target = instruction[1] || cp_cell_for(instruction)
|
|
208
|
+
original = target.value
|
|
209
|
+
old_type = target.inferred_type
|
|
210
|
+
old_raise_type = instruction.raise_type
|
|
211
|
+
|
|
212
|
+
if instruction.type == :call
|
|
213
|
+
receiver, method_name, *args, opts = instruction[2..-1]
|
|
214
|
+
components = [receiver, *args]
|
|
215
|
+
fixed_arity = true
|
|
216
|
+
elsif instruction.type == :call_vararg
|
|
217
|
+
receiver, method_name, args, opts = instruction[2..-1]
|
|
218
|
+
components = [receiver, args]
|
|
219
|
+
elsif instruction.type == :super
|
|
220
|
+
*args, opts = instruction[2..-1]
|
|
221
|
+
fixed_arity = issuper = true
|
|
222
|
+
components = args.dup
|
|
223
|
+
receiver = cp_self_cell(instruction)
|
|
224
|
+
elsif instruction.type == :super_vararg
|
|
225
|
+
args, opts = instruction[2..3]
|
|
226
|
+
issuper = true
|
|
227
|
+
components = [args]
|
|
228
|
+
receiver = cp_self_cell(instruction)
|
|
229
|
+
end
|
|
230
|
+
components << opts[:block] if opts[:block]
|
|
231
|
+
|
|
232
|
+
if components.any? { |arg| arg.value == UNDEFINED }
|
|
233
|
+
# cannot evaluate unless all args and receiver are defined
|
|
234
|
+
return [false, :unknown]
|
|
235
|
+
end
|
|
236
|
+
# check purity
|
|
237
|
+
methods = instruction.possible_methods(cp_opts)
|
|
238
|
+
# Require precise resolution
|
|
239
|
+
method = methods.size == 1 ? methods.first : nil
|
|
240
|
+
if methods.empty?
|
|
241
|
+
new_value = UNDEFINED
|
|
242
|
+
new_type = Types::TOP
|
|
243
|
+
raised = Frequency::ALWAYS # NoMethodError
|
|
244
|
+
raise_type = Types::UnionType.new([Types::ClassObjectType.new('NoMethodError')])
|
|
245
|
+
elsif method && (@_cp_fixed_methods.has_key?(method))
|
|
246
|
+
result = @_cp_fixed_methods[method]
|
|
247
|
+
new_value = result
|
|
248
|
+
new_type = Utilities.type_for(result)
|
|
249
|
+
raised = Frequency::NEVER
|
|
250
|
+
raise_type = Types::EMPTY
|
|
251
|
+
elsif fixed_arity && !issuper &&
|
|
252
|
+
(special_result, special_type =
|
|
253
|
+
apply_special_case(instruction, receiver, method_name, *args);
|
|
254
|
+
special_result != INAPPLICABLE)
|
|
255
|
+
new_value = special_result
|
|
256
|
+
new_type = special_type
|
|
257
|
+
raised = Frequency::NEVER
|
|
258
|
+
raise_type = Types::EMPTY
|
|
259
|
+
elsif components.any? { |arg| arg.value == VARYING }
|
|
260
|
+
new_value = VARYING
|
|
261
|
+
new_type, raised, raise_type = infer_type_and_raising(instruction, receiver, method_name, args, cp_opts)
|
|
262
|
+
# All components constant, and never evaluated before.
|
|
263
|
+
elsif original == UNDEFINED
|
|
264
|
+
if !issuper && method && (method.pure || allow_impure_method?(method))
|
|
265
|
+
arg_array = fixed_arity ? args.map(&:value) : args.value
|
|
266
|
+
block = opts[:block] && opts[:block].value
|
|
267
|
+
new_value, new_type, raised, raise_type = adapt_simulation_of_method(
|
|
268
|
+
instruction, receiver.value, method, arg_array, block, cp_opts)
|
|
269
|
+
else
|
|
270
|
+
new_value = VARYING
|
|
271
|
+
new_type, raised, raise_type = infer_type_and_raising(instruction, receiver, method_name, args, cp_opts)
|
|
272
|
+
end
|
|
273
|
+
else
|
|
274
|
+
# all components constant, nothing changed, shouldn't happen, but okay
|
|
275
|
+
new_value = original
|
|
276
|
+
new_type = old_type
|
|
277
|
+
raised = instruction.raise_frequency
|
|
278
|
+
raise_type = instruction.raise_type
|
|
279
|
+
end
|
|
280
|
+
# At this point, we should prune raise edges!
|
|
281
|
+
if original != new_value
|
|
282
|
+
target.bind! new_value
|
|
283
|
+
changed = true
|
|
284
|
+
end
|
|
285
|
+
if old_type != new_type
|
|
286
|
+
Laser.debug_puts "-> Return type: #{new_type.inspect}"
|
|
287
|
+
target.inferred_type = new_type
|
|
288
|
+
changed = true
|
|
289
|
+
end
|
|
290
|
+
Laser.debug_puts "-> Raise freq: #{raised.inspect}"
|
|
291
|
+
if raise_type != old_raise_type
|
|
292
|
+
Laser.debug_puts "-> Raise Type: #{raise_type.inspect}"
|
|
293
|
+
instruction.raise_type = raise_type
|
|
294
|
+
raise_changed = true
|
|
295
|
+
end
|
|
296
|
+
[changed, raised, raise_changed]
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
# Runs method call simulation from the Simulation modules, and adapts
|
|
300
|
+
# the output for consumption by constant propagation.
|
|
301
|
+
def adapt_simulation_of_method(insn, receiver, method, args, block, opts)
|
|
302
|
+
opts = Simulation::DEFAULT_SIMULATION_OPTS.merge(opts)
|
|
303
|
+
opts.merge!(current_block: insn.block)
|
|
304
|
+
begin
|
|
305
|
+
new_value = simulate_call_dispatch(receiver, method, args, block, opts)
|
|
306
|
+
new_type = Utilities.type_for(new_value)
|
|
307
|
+
raised = Frequency::NEVER
|
|
308
|
+
raise_type = Types::EMPTY
|
|
309
|
+
rescue Simulation::ExitedAbnormally => err
|
|
310
|
+
new_value = UNDEFINED
|
|
311
|
+
new_type = Types::TOP
|
|
312
|
+
raised = Frequency::ALWAYS
|
|
313
|
+
raise_type = Types::UnionType.new([Types::ClassObjectType.new(err.error.class.name)])
|
|
314
|
+
end
|
|
315
|
+
[new_value, new_type, raised, raise_type]
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
def infer_type_and_raising(instruction, receiver, method_name, args, opts)
|
|
319
|
+
begin
|
|
320
|
+
type, raise_freq, raise_type = cpa_call_properties(
|
|
321
|
+
receiver, method_name, args, instruction, opts)
|
|
322
|
+
rescue TypeError => err
|
|
323
|
+
type = Types::TOP
|
|
324
|
+
raise_freq = Frequency::ALWAYS
|
|
325
|
+
raise_type = Types::UnionType.new([Types::ClassObjectType.new('TypeError')])
|
|
326
|
+
Laser.debug_puts("No method named #{method_name} with matching types was found")
|
|
327
|
+
instruction.node.add_error(NoMatchingTypeSignature.new(
|
|
328
|
+
"No method named #{method_name} with matching types was found", instruction.node))
|
|
329
|
+
end
|
|
330
|
+
[type, raise_freq, raise_type]
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
# Calculates all possible return types, raise types, and the raise
|
|
334
|
+
# frequency for a method call using CPA.
|
|
335
|
+
def cpa_call_properties(receiver, method, args, instruction, opts)
|
|
336
|
+
ignore_privacy, block = instruction.ignore_privacy, instruction.block_operand
|
|
337
|
+
dispatches = cpa_dispatches(receiver, instruction, method, opts)
|
|
338
|
+
cartesian = calculate_possible_templates(dispatches, args, block)
|
|
339
|
+
result = cpa_for_templates(dispatches, cartesian)
|
|
340
|
+
raise_result, raise_type = raisability_for_templates(dispatches, cartesian, ignore_privacy)
|
|
341
|
+
if result.empty?
|
|
342
|
+
raise TypeError.new("No methods named #{method} with matching types were found.")
|
|
343
|
+
end
|
|
344
|
+
[Types::UnionType.new(result), raise_result, raise_type]
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
# Calculates all possible (self_type, dispatches) pairs for a call.
|
|
348
|
+
def cpa_dispatches(receiver, instruction, method, opts)
|
|
349
|
+
if instruction.type == :call || instruction.type == :call_vararg
|
|
350
|
+
receiver.expr_type.member_types.map do |type|
|
|
351
|
+
[type, type.matching_methods(method)]
|
|
352
|
+
end
|
|
353
|
+
else
|
|
354
|
+
dispatches = instruction.possible_methods(opts)
|
|
355
|
+
receiver.expr_type.member_types.map do |type|
|
|
356
|
+
[type, dispatches]
|
|
357
|
+
end
|
|
358
|
+
end
|
|
359
|
+
end
|
|
360
|
+
|
|
361
|
+
# Calculates the set of methods potentially invoked in dynamic dispatch,
|
|
362
|
+
# and the set of all possible argument type combinations.
|
|
363
|
+
def calculate_possible_templates(possible_dispatches, args, block)
|
|
364
|
+
if Bindings::Base === args && Types::TupleType === args.expr_type
|
|
365
|
+
cartesian_parts = args.element_types
|
|
366
|
+
empty = cartesian_parts.empty?
|
|
367
|
+
elsif Bindings::Base === args && Types::UnionType === args.expr_type &&
|
|
368
|
+
Types::TupleType === args.expr_type.member_types.first
|
|
369
|
+
cartesian_parts = args.expr_type.member_types.first.element_types.map { |x| [x] }
|
|
370
|
+
empty = cartesian_parts.empty?
|
|
371
|
+
else
|
|
372
|
+
cartesian_parts = args.map(&:expr_type).map(&:member_types).map(&:to_a)
|
|
373
|
+
empty = args.empty?
|
|
374
|
+
end
|
|
375
|
+
if empty && !block
|
|
376
|
+
cartesian = [ [Types::NILCLASS] ]
|
|
377
|
+
else
|
|
378
|
+
if block
|
|
379
|
+
then cartesian_parts << block.expr_type.member_types.to_a
|
|
380
|
+
else cartesian_parts << [Types::NILCLASS]
|
|
381
|
+
end
|
|
382
|
+
cartesian = cartesian_parts[0].product(*cartesian_parts[1..-1])
|
|
383
|
+
end
|
|
384
|
+
cartesian
|
|
385
|
+
end
|
|
386
|
+
|
|
387
|
+
# Calculates the CPA-based return type of a dynamic call.
|
|
388
|
+
def cpa_for_templates(possible_dispatches, cartesian)
|
|
389
|
+
result = Set.new
|
|
390
|
+
possible_dispatches.each do |self_type, methods|
|
|
391
|
+
result |= methods.map do |method|
|
|
392
|
+
cartesian.map do |*type_list, block_type|
|
|
393
|
+
begin
|
|
394
|
+
method.return_type_for_types(self_type, type_list, block_type)
|
|
395
|
+
rescue TypeError => err
|
|
396
|
+
Laser.debug_puts("Invalid argument types found.")
|
|
397
|
+
nil
|
|
398
|
+
end
|
|
399
|
+
end.compact
|
|
400
|
+
end.flatten
|
|
401
|
+
end
|
|
402
|
+
result
|
|
403
|
+
end
|
|
404
|
+
|
|
405
|
+
# TODO(adgar): Optimize this. Use lattice-style expression of raisability
|
|
406
|
+
# until types need to be added too.
|
|
407
|
+
def raisability_for_templates(possible_dispatches, cartesian, ignore_privacy)
|
|
408
|
+
raise_type = Types::EMPTY
|
|
409
|
+
seen_public = seen_private = seen_raise = seen_succeed = seen_any = seen_missing = false
|
|
410
|
+
seen_valid_arity = seen_invalid_arity = false
|
|
411
|
+
arity = cartesian.first.size - 1 # -1 for block arg
|
|
412
|
+
possible_dispatches.each do |self_type, methods|
|
|
413
|
+
seen_any = true if methods.size > 0 && !seen_any
|
|
414
|
+
seen_missing = true if methods.empty? && !seen_missing
|
|
415
|
+
methods.each do |method|
|
|
416
|
+
if !seen_valid_arity && method.valid_arity?(arity)
|
|
417
|
+
seen_valid_arity = true
|
|
418
|
+
end
|
|
419
|
+
if !seen_invalid_arity && !method.valid_arity?(arity)
|
|
420
|
+
seen_invalid_arity = true
|
|
421
|
+
end
|
|
422
|
+
if !ignore_privacy
|
|
423
|
+
self_type.possible_classes.each do |self_class|
|
|
424
|
+
if self_class.visibility_for(method.name) == :public
|
|
425
|
+
seen_public = true
|
|
426
|
+
method.been_used! if method.valid_arity?(arity)
|
|
427
|
+
end
|
|
428
|
+
if !seen_private
|
|
429
|
+
seen_private = (self_class.visibility_for(method.name) != :public)
|
|
430
|
+
end
|
|
431
|
+
end
|
|
432
|
+
else
|
|
433
|
+
method.been_used! if method.valid_arity?(arity)
|
|
434
|
+
end
|
|
435
|
+
cartesian.each do |*type_list, block_type|
|
|
436
|
+
raise_frequency = method.raise_frequency_for_types(self_type, type_list, block_type)
|
|
437
|
+
if raise_frequency > Frequency::NEVER
|
|
438
|
+
seen_raise = true
|
|
439
|
+
raise_type = raise_type | method.raise_type_for_types(self_type, type_list, block_type)
|
|
440
|
+
end
|
|
441
|
+
seen_succeed = raise_frequency < Frequency::ALWAYS if !seen_succeed
|
|
442
|
+
end
|
|
443
|
+
end
|
|
444
|
+
end
|
|
445
|
+
|
|
446
|
+
if seen_any
|
|
447
|
+
fails_lookup = seen_missing ? Frequency::MAYBE : Frequency::NEVER
|
|
448
|
+
fails_privacy = if ignore_privacy
|
|
449
|
+
then Frequency::NEVER
|
|
450
|
+
else Frequency.for_samples(seen_private, seen_public)
|
|
451
|
+
end
|
|
452
|
+
failed_arity = Frequency.for_samples(seen_invalid_arity, seen_valid_arity)
|
|
453
|
+
if fails_privacy == Frequency::ALWAYS
|
|
454
|
+
raise_type = ClassRegistry['NoMethodError'].as_type
|
|
455
|
+
elsif failed_arity == Frequency::ALWAYS
|
|
456
|
+
raise_type = ClassRegistry['ArgumentError'].as_type
|
|
457
|
+
else
|
|
458
|
+
if fails_lookup > Frequency::NEVER || fails_privacy > Frequency::NEVER
|
|
459
|
+
raise_type |= ClassRegistry['NoMethodError'].as_type
|
|
460
|
+
end
|
|
461
|
+
if failed_arity > Frequency::NEVER
|
|
462
|
+
raise_type |= ClassRegistry['ArgumentError'].as_type
|
|
463
|
+
end
|
|
464
|
+
end
|
|
465
|
+
raised = Frequency.for_samples(seen_raise, seen_succeed)
|
|
466
|
+
raise_freq = [fails_privacy, raised, fails_lookup, failed_arity].max
|
|
467
|
+
else
|
|
468
|
+
raise_freq = Frequency::ALWAYS # no method!
|
|
469
|
+
raise_type = ClassRegistry['NoMethodError'].as_type
|
|
470
|
+
end
|
|
471
|
+
[raise_freq, raise_type]
|
|
472
|
+
end
|
|
473
|
+
|
|
474
|
+
# Evaluates the instruction, and if the constant value is lowered,
|
|
475
|
+
# then return true. Otherwise, return false.
|
|
476
|
+
def constant_propagation_evaluate(instruction)
|
|
477
|
+
target = instruction[1] || cp_cell_for(instruction)
|
|
478
|
+
original = target.value
|
|
479
|
+
old_type = target.inferred_type
|
|
480
|
+
changed = false
|
|
481
|
+
case instruction.type
|
|
482
|
+
when :assign
|
|
483
|
+
rhs = instruction[2]
|
|
484
|
+
if Bindings::Base === rhs
|
|
485
|
+
# temporary <- temporary
|
|
486
|
+
new_value = rhs.value
|
|
487
|
+
new_type = rhs.inferred_type
|
|
488
|
+
else
|
|
489
|
+
# temporary <- constant
|
|
490
|
+
new_value = rhs
|
|
491
|
+
new_type = Utilities.type_for(rhs)
|
|
492
|
+
end
|
|
493
|
+
when :phi
|
|
494
|
+
components = instruction[2..-1]
|
|
495
|
+
if components.any? { |var| var.value == VARYING }
|
|
496
|
+
new_value = VARYING
|
|
497
|
+
new_type = Types::UnionType.new(components.select { |c| c.value != UNDEFINED }.map(&:inferred_type).compact.uniq)
|
|
498
|
+
else
|
|
499
|
+
possible_values = components.map(&:value).uniq - [UNDEFINED]
|
|
500
|
+
Laser.debug_puts("CP_Phi(#{instruction.inspect}, #{possible_values.inspect})")
|
|
501
|
+
if possible_values == []
|
|
502
|
+
new_value = UNDEFINED
|
|
503
|
+
new_type = nil
|
|
504
|
+
elsif possible_values.size == 1
|
|
505
|
+
new_value = possible_values.first
|
|
506
|
+
new_type = Utilities.type_for(new_value)
|
|
507
|
+
else
|
|
508
|
+
new_value = VARYING
|
|
509
|
+
new_type = Types::UnionType.new(components.select { |c| c.value != UNDEFINED }.map(&:inferred_type).compact.uniq)
|
|
510
|
+
end
|
|
511
|
+
end
|
|
512
|
+
when :super, :super_vararg
|
|
513
|
+
new_value = VARYING
|
|
514
|
+
new_type = Types::TOP
|
|
515
|
+
else
|
|
516
|
+
raise ArgumentError("Invalid evaluate instruction evaluation type: #{instruction.type}")
|
|
517
|
+
end
|
|
518
|
+
if original != new_value
|
|
519
|
+
target.bind! new_value
|
|
520
|
+
changed = true
|
|
521
|
+
end
|
|
522
|
+
if old_type != new_type
|
|
523
|
+
target.inferred_type = new_type
|
|
524
|
+
changed = true
|
|
525
|
+
end
|
|
526
|
+
changed
|
|
527
|
+
end
|
|
528
|
+
|
|
529
|
+
def is_numeric?(temp)
|
|
530
|
+
temp.value != UNDEFINED && Types.subtype?(temp.inferred_type,
|
|
531
|
+
Types::ClassType.new('Numeric', :covariant))
|
|
532
|
+
end
|
|
533
|
+
|
|
534
|
+
def uses_method?(temp, method)
|
|
535
|
+
temp.value != UNDEFINED && temp.expr_type.matching_methods(method.name) == [method]
|
|
536
|
+
end
|
|
537
|
+
|
|
538
|
+
# TODO(adgar): Add typechecking. Forealz.
|
|
539
|
+
def apply_special_case(instruction, receiver, method_name, *args)
|
|
540
|
+
if method_name == :*
|
|
541
|
+
# 0 * n == 0
|
|
542
|
+
# n * 0 == 0
|
|
543
|
+
if (receiver.value == 0 && is_numeric?(args.first)) ||
|
|
544
|
+
(args.first.value == 0 && is_numeric?(receiver))
|
|
545
|
+
return [0, Types::FIXNUM]
|
|
546
|
+
elsif (args.first.value == 0 &&
|
|
547
|
+
uses_method?(receiver, ClassRegistry['String'].instance_method(:*)))
|
|
548
|
+
return ['', Types::STRING]
|
|
549
|
+
elsif (args.first.value == 0 &&
|
|
550
|
+
receiver.expr_type.member_types.all? { |t|
|
|
551
|
+
Types::TupleType === t || t.matching_methods(:*) == [ClassRegistry['Array'].instance_method(:*)]
|
|
552
|
+
})
|
|
553
|
+
return [[], Types::ARRAY]
|
|
554
|
+
end
|
|
555
|
+
elsif method_name == :**
|
|
556
|
+
# n ** 0 == 1
|
|
557
|
+
if args.first.value == 0 && is_numeric?(receiver)
|
|
558
|
+
return [1, Types::FIXNUM]
|
|
559
|
+
# 1 ** n == 1
|
|
560
|
+
elsif receiver.value == 1 && is_numeric?(args.first)
|
|
561
|
+
return [1, Types::FIXNUM]
|
|
562
|
+
end
|
|
563
|
+
elsif method_name == :===
|
|
564
|
+
if LaserModule === receiver.value && args.first != UNDEFINED
|
|
565
|
+
instance_type = args.first.expr_type
|
|
566
|
+
module_type = Types::ClassType.new(receiver.value.path, :covariant)
|
|
567
|
+
result = Types.subtype?(instance_type, module_type)
|
|
568
|
+
if result
|
|
569
|
+
return [true, Types::TRUECLASS]
|
|
570
|
+
elsif !(Types.overlap?(instance_type, module_type))
|
|
571
|
+
return [false, Types::FALSECLASS]
|
|
572
|
+
end
|
|
573
|
+
end
|
|
574
|
+
elsif method_name == :instance_variable_get
|
|
575
|
+
if args.first.value != UNDEFINED && args.first.value != VARYING
|
|
576
|
+
klass = LaserObject === receiver.value ? receiver.value.normal_class : receiver.expr_type.possible_classes.first
|
|
577
|
+
ivar = klass.instance_variable(args.first.value)
|
|
578
|
+
ivar.uses.add(instruction)
|
|
579
|
+
return [VARYING, ivar.expr_type]
|
|
580
|
+
end
|
|
581
|
+
elsif method_name == :instance_variable_set
|
|
582
|
+
if args.first.value != UNDEFINED && args.first.value != VARYING
|
|
583
|
+
klass = LaserObject === receiver.value ? receiver.value.normal_class : receiver.expr_type.possible_classes.first
|
|
584
|
+
ivar = klass.instance_variable(args.first.value)
|
|
585
|
+
unless Types.subtype?(args[1].expr_type, ivar.expr_type)
|
|
586
|
+
ivar.inferred_type = Types::UnionType.new([args[1].expr_type, ivar.expr_type])
|
|
587
|
+
end
|
|
588
|
+
return [args[1].value, ivar.expr_type]
|
|
589
|
+
end
|
|
590
|
+
elsif receiver == ClassRegistry['Laser#Magic'].binding
|
|
591
|
+
magic_result, magic_type = cp_magic(instruction, method_name, *args)
|
|
592
|
+
if magic_result != INAPPLICABLE
|
|
593
|
+
return [magic_result, magic_type]
|
|
594
|
+
end
|
|
595
|
+
elsif (receiver.value == ClassRegistry['Proc'] && method_name == :new) ||
|
|
596
|
+
((method_name == :block_given? || method_name == :iterable?) &&
|
|
597
|
+
(uses_method?(receiver, ClassRegistry['Kernel'].instance_method(:block_given?)))) # and check no block
|
|
598
|
+
return cp_magic(instruction, :current_block)
|
|
599
|
+
elsif receiver.value == ClassRegistry['Array'] && method_name == :[]
|
|
600
|
+
if args.all? { |arg| arg.value != UNDEFINED }
|
|
601
|
+
tuple_type = Types::TupleType.new(args.map(&:expr_type))
|
|
602
|
+
if args.all? { |arg| arg.value != VARYING }
|
|
603
|
+
return [args.map(&:value), tuple_type]
|
|
604
|
+
else
|
|
605
|
+
return [VARYING, tuple_type]
|
|
606
|
+
end
|
|
607
|
+
end
|
|
608
|
+
elsif receiver.value == ClassRegistry['Array'] && method_name == :new
|
|
609
|
+
if args.all? { |arg| arg.value != UNDEFINED }
|
|
610
|
+
if args.size == 0
|
|
611
|
+
return [[], Types::TupleType.new([])]
|
|
612
|
+
elsif args.size == 1 && Types::equal(Types::ARRAY, args.first.expr_type)
|
|
613
|
+
return [args.first.value, args.first.expr_type]
|
|
614
|
+
else
|
|
615
|
+
# TODO(adgar): add integer, val, and integer, block case
|
|
616
|
+
end
|
|
617
|
+
end
|
|
618
|
+
elsif receiver.expr_type.member_types.size == 1 &&
|
|
619
|
+
Types::TupleType === receiver.expr_type.member_types.first
|
|
620
|
+
tuple_type = receiver.expr_type.member_types.first
|
|
621
|
+
# switch on method object
|
|
622
|
+
case tuple_type.matching_methods(method_name)[0]
|
|
623
|
+
when ClassRegistry['Array'].instance_method(:size)
|
|
624
|
+
size = tuple_type.size
|
|
625
|
+
return [size, Utilities.type_for(size)]
|
|
626
|
+
end
|
|
627
|
+
elsif method_name == :+
|
|
628
|
+
if receiver.value != VARYING && receiver.value != UNDEFINED &&
|
|
629
|
+
Utilities.normal_class_for(receiver.value) == ClassRegistry['Array'] &&
|
|
630
|
+
args[0].value == VARYING && args[0].expr_type.member_types.all? { |t| Types::TupleType === t }
|
|
631
|
+
constant, tuple = receiver, args[0]
|
|
632
|
+
elsif args[0].value != VARYING && args[0].value != UNDEFINED &&
|
|
633
|
+
Utilities.normal_class_for(args[0].value) == ClassRegistry['Array'] &&
|
|
634
|
+
receiver.value == VARYING &&
|
|
635
|
+
receiver.expr_type.member_types.all? { |t| Types::TupleType === t }
|
|
636
|
+
constant, tuple = args[0], receiver
|
|
637
|
+
end
|
|
638
|
+
if constant
|
|
639
|
+
constant_types = constant.value.map { |v| Utilities.type_for(v) }
|
|
640
|
+
new_types = tuple.expr_type.member_types.map do |tuple_type|
|
|
641
|
+
Types::TupleType.new(constant_types + tuple_type.element_types)
|
|
642
|
+
end
|
|
643
|
+
return [VARYING, Types::UnionType.new(new_types)]
|
|
644
|
+
end
|
|
645
|
+
end
|
|
646
|
+
[INAPPLICABLE, Types::EMPTY]
|
|
647
|
+
end
|
|
648
|
+
|
|
649
|
+
def allow_impure_method?(method)
|
|
650
|
+
method == ClassRegistry['Module'].instance_method(:const_get)
|
|
651
|
+
end
|
|
652
|
+
|
|
653
|
+
def cp_magic(instruction, method_name, *args)
|
|
654
|
+
case method_name
|
|
655
|
+
when :current_self
|
|
656
|
+
return [VARYING, real_self_type]
|
|
657
|
+
when :current_argument
|
|
658
|
+
return [VARYING, real_formal_type(args[0].value)]
|
|
659
|
+
when :current_argument_range
|
|
660
|
+
if bound_argument_types?
|
|
661
|
+
tuple_args = ((args[0].value)...(args[0].value + args[1].value)).map do |num|
|
|
662
|
+
real_formal_type(num)
|
|
663
|
+
end
|
|
664
|
+
return [VARYING, Types::TupleType.new(tuple_args)]
|
|
665
|
+
else
|
|
666
|
+
return [VARYING, Types::ARRAY]
|
|
667
|
+
end
|
|
668
|
+
when :current_arity
|
|
669
|
+
if bound_argument_types?
|
|
670
|
+
return [bound_arity, Types::FIXNUM]
|
|
671
|
+
else
|
|
672
|
+
return [VARYING, Types::FIXNUM]
|
|
673
|
+
end
|
|
674
|
+
when :current_block
|
|
675
|
+
if real_block_type == Types::NILCLASS
|
|
676
|
+
return [nil, Types::NILCLASS]
|
|
677
|
+
else
|
|
678
|
+
return [VARYING, real_block_type]
|
|
679
|
+
end
|
|
680
|
+
when :current_exception
|
|
681
|
+
return [VARYING, Types::EMPTY]
|
|
682
|
+
when :get_just_raised_exception
|
|
683
|
+
# Actually assigned to by the call's raisability-inference.
|
|
684
|
+
# return current state.
|
|
685
|
+
result_holder = instruction[1]
|
|
686
|
+
return [result_holder.value, result_holder.expr_type]
|
|
687
|
+
when :get_global
|
|
688
|
+
global = Scope::GlobalScope.lookup(args[0].value)
|
|
689
|
+
global.uses.add(instruction)
|
|
690
|
+
return [VARYING, global.expr_type]
|
|
691
|
+
when :set_global
|
|
692
|
+
if args[1].value != UNDEFINED
|
|
693
|
+
global = Scope::GlobalScope.lookup(args[0].value)
|
|
694
|
+
unless Types.subtype?(args[1].expr_type, global.expr_type)
|
|
695
|
+
global.inferred_type = Types::UnionType.new([args[1].expr_type, global.expr_type])
|
|
696
|
+
end
|
|
697
|
+
return [VARYING, global.expr_type]
|
|
698
|
+
end
|
|
699
|
+
when :responds?
|
|
700
|
+
name = args[1].value
|
|
701
|
+
seen_yes = seen_no = false
|
|
702
|
+
args[0].expr_type.possible_classes.each do |klass|
|
|
703
|
+
if klass.instance_method(name)
|
|
704
|
+
then seen_yes = true; break if seen_no
|
|
705
|
+
else seen_no = true; break if seen_yes
|
|
706
|
+
end
|
|
707
|
+
end
|
|
708
|
+
if seen_yes && !seen_no
|
|
709
|
+
return [true, Types::TRUECLASS]
|
|
710
|
+
elsif !seen_yes && seen_no
|
|
711
|
+
return [false, Types::FALSECLASS]
|
|
712
|
+
elsif seen_yes && seen_no
|
|
713
|
+
return [VARYING, Types::BOOLEAN]
|
|
714
|
+
else # receiver's type is empty. YAGNI?
|
|
715
|
+
return [false, Types::FALSECLASS]
|
|
716
|
+
end
|
|
717
|
+
end
|
|
718
|
+
[INAPPLICABLE, nil]
|
|
719
|
+
end
|
|
720
|
+
|
|
721
|
+
def validate_tuple_expectation(instruction)
|
|
722
|
+
expect_type, val, array = instruction[2..-1]
|
|
723
|
+
return if array.value == UNDEFINED
|
|
724
|
+
node = instruction.node
|
|
725
|
+
array.expr_type.member_types.each do |type|
|
|
726
|
+
if Types::TupleType === type
|
|
727
|
+
if type.size < val && expect_type == :==
|
|
728
|
+
node.add_error(UnassignedLHSError.new('LHS never assigned - defaults to nil', node))
|
|
729
|
+
elsif type.size <= val && expect_type == :>
|
|
730
|
+
node.add_error(UnassignedLHSError.new('LHS splat is always empty ([]) - not useful', node))
|
|
731
|
+
elsif type.size > val && expect_type == :==
|
|
732
|
+
node.add_error(DiscardedRHSError.new('RHS value being discarded', node))
|
|
733
|
+
elsif type.size >= val && expect_type == :<
|
|
734
|
+
node.add_error(DiscardedRHSError.new('RHS splat expanded and then discarded', node))
|
|
735
|
+
end
|
|
736
|
+
end
|
|
737
|
+
end
|
|
738
|
+
end
|
|
739
|
+
end # ConstantPropagation
|
|
740
|
+
end # ControlFlow
|
|
741
|
+
end # Analysis
|
|
742
|
+
end #
|