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,26 @@
|
|
|
1
|
+
module Laser
|
|
2
|
+
module Analysis
|
|
3
|
+
module ControlFlow
|
|
4
|
+
# Finds the properties of how the code yields to a block argument.
|
|
5
|
+
# Should not be used on top-level code, naturally.
|
|
6
|
+
module MethodCallSearch
|
|
7
|
+
def find_method_calls(method_to_find, opts)
|
|
8
|
+
possible_calls = []
|
|
9
|
+
reachable_vertices do |block|
|
|
10
|
+
block.instructions.each do |insn|
|
|
11
|
+
cur_insn = insn
|
|
12
|
+
if (insn.type == :call || insn.type == :call_vararg)
|
|
13
|
+
# check if method could be from insn receiver
|
|
14
|
+
if insn.possible_methods(opts).include?(method_to_find)
|
|
15
|
+
possible_calls << insn
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
possible_calls
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end # ControlFlow
|
|
24
|
+
end # Analysis
|
|
25
|
+
end #
|
|
26
|
+
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
module Laser
|
|
2
|
+
module Analysis
|
|
3
|
+
module ControlFlow
|
|
4
|
+
# Finds the properties of how the code yields to a block argument.
|
|
5
|
+
# Should not be used on top-level code, naturally.
|
|
6
|
+
module RaiseProperties
|
|
7
|
+
def find_raise_properties
|
|
8
|
+
find_raise_frequency
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# Finds whether the method raises always, never, or sometimes.
|
|
12
|
+
def find_raise_frequency
|
|
13
|
+
fail_block = exception_postdominator
|
|
14
|
+
if fail_block.nil? || fail_block.real_predecessors.empty?
|
|
15
|
+
@raise_frequency = Frequency::NEVER
|
|
16
|
+
elsif (@exit.normal_predecessors & @exit.real_predecessors).empty?
|
|
17
|
+
@raise_frequency = Frequency::ALWAYS
|
|
18
|
+
else
|
|
19
|
+
@raise_frequency = Frequency::MAYBE
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
module Laser
|
|
2
|
+
module Analysis
|
|
3
|
+
module ControlFlow
|
|
4
|
+
# Simulation of a CFG. Requires values for the formal arguments, if any.
|
|
5
|
+
# Stops as soon as an unpredictable statement is reached.
|
|
6
|
+
module Simulation
|
|
7
|
+
class SimulationError < StandardError; end
|
|
8
|
+
class NonDeterminismHappened < StandardError; end
|
|
9
|
+
class SimulationNonterminationError < StandardError; end
|
|
10
|
+
class ExitedNormally < StandardError
|
|
11
|
+
attr_reader :result
|
|
12
|
+
def initialize(result)
|
|
13
|
+
@result = result
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
class ExitedAbnormally < StandardError
|
|
17
|
+
attr_reader :error
|
|
18
|
+
def initialize(error)
|
|
19
|
+
@error = error
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
MAX_SIMULATION_BLOCKS = 100_000
|
|
23
|
+
DEFAULT_SIMULATION_OPTS =
|
|
24
|
+
{on_raise: :annotate, invocation_sites: Hash.new { |h, k| h[k] = Set.new },
|
|
25
|
+
invocation_counts: Hash.new do |h1, block|
|
|
26
|
+
h1[block] = Hash.new { |h2, callsite| h2[callsite] = 0 }
|
|
27
|
+
end,
|
|
28
|
+
remaining_blocks: MAX_SIMULATION_BLOCKS,
|
|
29
|
+
method: nil }
|
|
30
|
+
# simulates the CFG, with different possible assumptions about
|
|
31
|
+
# how we should treat the global environment/self object.
|
|
32
|
+
def simulate(formal_vals=[], opts={})
|
|
33
|
+
opts = DEFAULT_SIMULATION_OPTS.merge(opts)
|
|
34
|
+
opts[:formals] = formal_vals
|
|
35
|
+
clear_analyses
|
|
36
|
+
current_block = opts[:start_block] || @enter
|
|
37
|
+
previous_block = nil
|
|
38
|
+
begin
|
|
39
|
+
loop do
|
|
40
|
+
simulate_take_step(opts)
|
|
41
|
+
from, next_block = simulate_block(current_block,
|
|
42
|
+
opts.merge(previous_block: previous_block, current_block: current_block))
|
|
43
|
+
from.add_flag(next_block, ControlFlowGraph::EDGE_EXECUTABLE)
|
|
44
|
+
current_block, previous_block = next_block, current_block
|
|
45
|
+
end
|
|
46
|
+
rescue ExitedNormally => err
|
|
47
|
+
current_block.add_flag(current_block.normal_successors.first,
|
|
48
|
+
ControlFlowGraph::EDGE_EXECUTABLE)
|
|
49
|
+
err.result
|
|
50
|
+
rescue NonDeterminismHappened => err
|
|
51
|
+
Laser.debug_puts "Simulation ended at nondeterminism: #{err.message}"
|
|
52
|
+
Laser.debug_puts '>>> -- Starting Backup CP -- <<<'
|
|
53
|
+
perform_constant_propagation(initial_block: current_block, no_wipe: true)
|
|
54
|
+
Laser.debug_puts '>>> -- Finished Backup CP -- <<<'
|
|
55
|
+
raise err
|
|
56
|
+
rescue SimulationNonterminationError => err
|
|
57
|
+
Laser.debug_puts "Simulation failed to terminate: #{err.message}"
|
|
58
|
+
Laser.debug_p err.backtrace
|
|
59
|
+
@root.add_error(TopLevelSimulationRaised.new(err.message, @root, err))
|
|
60
|
+
rescue ExitedAbnormally => err
|
|
61
|
+
current_block.add_flag(current_block.exception_successors.first,
|
|
62
|
+
ControlFlowGraph::EDGE_EXECUTABLE)
|
|
63
|
+
if opts[:on_raise] == :annotate
|
|
64
|
+
msg = LaserObject === err.error ? err.error.laser_simulate('message', []) : err.error.message
|
|
65
|
+
Laser.debug_puts "Simulation exited abnormally: #{msg}"
|
|
66
|
+
@root.add_error(TopLevelSimulationRaised.new(msg, @root, err.error))
|
|
67
|
+
elsif opts[:on_raise] == :raise
|
|
68
|
+
raise err
|
|
69
|
+
end
|
|
70
|
+
rescue NotImplementedError => err
|
|
71
|
+
Laser.debug_puts "Simulation attempted: #{err.message}"
|
|
72
|
+
Laser.debug_p err.backtrace
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def simulate_take_step(opts)
|
|
77
|
+
if (opts[:remaining_blocks] -= 1) <= 0
|
|
78
|
+
raise SimulationNonterminationError.new('Simulation failed to terminate')
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def simulate_block(block, opts)
|
|
83
|
+
return if block.name == 'Exit'
|
|
84
|
+
|
|
85
|
+
Laser.debug_puts "Entering block #{block.name}"
|
|
86
|
+
# phi nodes always go first
|
|
87
|
+
block.phi_nodes.each do |node|
|
|
88
|
+
simulate_deterministic_phi_node(node, opts)
|
|
89
|
+
end
|
|
90
|
+
block.natural_instructions[0..-2].each do |insn|
|
|
91
|
+
simulate_instruction(insn, opts)
|
|
92
|
+
end
|
|
93
|
+
if block.instructions.empty?
|
|
94
|
+
[opts[:current_block], block.real_successors.first]
|
|
95
|
+
else
|
|
96
|
+
simulate_exit_instruction(block.instructions.last, opts)
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def simulate_exit_instruction(insn, opts)
|
|
101
|
+
Laser.debug_puts "Simulating exit insn: #{insn.inspect}"
|
|
102
|
+
natural_exit = insn.block.normal_successors.first
|
|
103
|
+
case insn[0]
|
|
104
|
+
when :jump
|
|
105
|
+
[opts[:current_block], insn.block.real_successors.first]
|
|
106
|
+
when :branch
|
|
107
|
+
Laser.debug_puts "Branching on: #{insn[1].value.inspect}"
|
|
108
|
+
[opts[:current_block], insn[1].value ? insn.true_successor : insn.false_successor]
|
|
109
|
+
when :call, :call_vararg, :super, :super_vararg
|
|
110
|
+
# todo: block edge!
|
|
111
|
+
begin
|
|
112
|
+
simulate_instruction(insn, opts)
|
|
113
|
+
if (call_block = insn[-1][:block])
|
|
114
|
+
block_proc = call_block.value
|
|
115
|
+
if opts[:invocation_counts][block_proc][opts[:current_block]] > 0
|
|
116
|
+
executed_succ = opts[:current_block].successors.find { |b| b.name == block_proc.start_block.name }
|
|
117
|
+
opts[:current_block].add_flag(executed_succ,
|
|
118
|
+
ControlFlowGraph::EDGE_EXECUTABLE)
|
|
119
|
+
[self[block_proc.exit_block], natural_exit]
|
|
120
|
+
else
|
|
121
|
+
[opts[:current_block], natural_exit]
|
|
122
|
+
end
|
|
123
|
+
else
|
|
124
|
+
[opts[:current_block], natural_exit]
|
|
125
|
+
end
|
|
126
|
+
rescue ExitedAbnormally => err
|
|
127
|
+
Laser.debug_puts "Exception raised by call, taking abnormal edge. Error: #{err.error}"
|
|
128
|
+
Laser.debug_pp(err.backtrace)
|
|
129
|
+
[opts[:current_block], insn.block.exception_successors.first]
|
|
130
|
+
end
|
|
131
|
+
when :return
|
|
132
|
+
raise ExitedNormally.new(insn[1].value)
|
|
133
|
+
when :raise
|
|
134
|
+
raise ExitedAbnormally.new(insn[1].value)
|
|
135
|
+
else
|
|
136
|
+
raise ArgumentError.new("Unexpected instruction type #{insn[0].inspect}")
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def simulate_deterministic_phi_node(node, opts)
|
|
141
|
+
# Laser.debug_puts "Simulating #{node.inspect} from #{opts[:previous_block].name}"
|
|
142
|
+
index_of_predecessor = node.block.predecessors.to_a.index(opts[:previous_block])
|
|
143
|
+
simulate_assignment(node[1], node[2 + index_of_predecessor], opts)
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def simulate_assignment(lhs, rhs, opts)
|
|
147
|
+
if Bindings::Base === rhs
|
|
148
|
+
lhs.bind! rhs.value
|
|
149
|
+
else
|
|
150
|
+
lhs.bind! rhs
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def simulate_instruction(insn, opts)
|
|
155
|
+
Laser.debug_puts "Simulating insn: #{insn.inspect}"
|
|
156
|
+
case insn[0]
|
|
157
|
+
when :assign then simulate_assignment(insn[1], insn[2], opts)
|
|
158
|
+
when :call, :call_vararg
|
|
159
|
+
# cases: special method we intercept, builtin we direct-send, and cfg we simulate
|
|
160
|
+
simulate_call_instruction(insn, opts)
|
|
161
|
+
when :super, :super_vararg
|
|
162
|
+
if opts[:method].nil?
|
|
163
|
+
raise ExitedAbnormally.new(NoMethodError.new('super called outside of method'))
|
|
164
|
+
else
|
|
165
|
+
simulate_call_instruction(insn, opts)
|
|
166
|
+
end
|
|
167
|
+
when :declare
|
|
168
|
+
# do nothing
|
|
169
|
+
else
|
|
170
|
+
raise ArgumentError.new("Unexpected instruction type #{insn[0].inspect}")
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def simulate_call_instruction(insn, opts)
|
|
175
|
+
if insn.operands.any? { |op| op.value == VARYING || op.value == UNDEFINED }
|
|
176
|
+
raise NonDeterminismHappened.new(
|
|
177
|
+
"Nondeterministic operand (#{op.name}=#{op.value} in #{insn.inspect})")
|
|
178
|
+
end
|
|
179
|
+
receiver = insn[2].value
|
|
180
|
+
klass = Utilities.klass_for(receiver)
|
|
181
|
+
if insn.type == :call || insn.type == :call_vararg
|
|
182
|
+
method = klass.instance_method(insn[3]) # normal method dispatch
|
|
183
|
+
else
|
|
184
|
+
# super method dispatch
|
|
185
|
+
method = opts[:method].owner.parent.instance_method(opts[:method].name)
|
|
186
|
+
end
|
|
187
|
+
if !method
|
|
188
|
+
simulate_method_missing(insn, klass)
|
|
189
|
+
elsif should_simulate_call(method, opts)
|
|
190
|
+
simulate_call(receiver, method, insn, opts)
|
|
191
|
+
else
|
|
192
|
+
raise NonDeterminismHappened.new("Nondeterministic call: #{method.owner.name}##{method.name}")
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
def simulate_call(receiver, method, insn, opts)
|
|
197
|
+
args = simulate_args(insn)
|
|
198
|
+
block_to_use = insn[-1][:block] && insn[-1][:block].value
|
|
199
|
+
result = simulate_call_dispatch(receiver, method, args, block_to_use, opts)
|
|
200
|
+
insn[1].bind! result if insn[1]
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
def simulate_args(insn)
|
|
204
|
+
case insn.type
|
|
205
|
+
when :call
|
|
206
|
+
insn[4..-2].map(&:value)
|
|
207
|
+
when :call_vararg
|
|
208
|
+
insn[4].value
|
|
209
|
+
when :super
|
|
210
|
+
insn[2..-2].map(&:value)
|
|
211
|
+
when :super_vararg
|
|
212
|
+
insn[2].value
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
def should_simulate_call(method, opts)
|
|
217
|
+
method.predictable && (opts[:mutation] || !method.mutation)
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
def simulate_call_dispatch(receiver, method, args, block, opts)
|
|
221
|
+
Laser.debug_puts("simulate_call(#{receiver.inspect}, #{method.name}, #{args.inspect}, #{block.inspect}, #{opts.inspect})")
|
|
222
|
+
opts = opts.merge(method: method)
|
|
223
|
+
if block
|
|
224
|
+
new_invocation_sites = opts[:invocation_sites].merge(block => opts[:invocation_sites][block] | Set[opts[:current_block]])
|
|
225
|
+
opts = opts.merge(invocation_sites: new_invocation_sites, on_raise: :raise)
|
|
226
|
+
end
|
|
227
|
+
method.been_used!
|
|
228
|
+
if method.special
|
|
229
|
+
simulate_special_method(receiver, method, args, block, opts)
|
|
230
|
+
elsif method.builtin
|
|
231
|
+
begin
|
|
232
|
+
if !block
|
|
233
|
+
receiver.send(method.name, *args)
|
|
234
|
+
else
|
|
235
|
+
result = receiver.send(method.name, *args) do |*given_args, &given_blk|
|
|
236
|
+
# self is this because no builtins change self... right?
|
|
237
|
+
block.simulate(given_args, given_blk, opts)
|
|
238
|
+
end
|
|
239
|
+
result
|
|
240
|
+
end
|
|
241
|
+
rescue ExitedAbnormally => abnormal
|
|
242
|
+
Bootstrap::EXCEPTION_STACK.value.push(abnormal.error)
|
|
243
|
+
raise abnormal
|
|
244
|
+
# Extremely unsafe exception handler: assumes my code does not raise
|
|
245
|
+
# exceptions!
|
|
246
|
+
rescue Exception => err
|
|
247
|
+
Laser.debug_puts("Internal exception raised: #{err.message}")
|
|
248
|
+
Laser.debug_pp(err.backtrace)
|
|
249
|
+
Bootstrap::EXCEPTION_STACK.value.push(err)
|
|
250
|
+
raise ExitedAbnormally.new(err)
|
|
251
|
+
end
|
|
252
|
+
else
|
|
253
|
+
Laser.debug_puts "About to simulate #{method.owner.name}##{method.name}"
|
|
254
|
+
result = method.simulate_with_args(receiver, args, block, opts)
|
|
255
|
+
Laser.debug_puts "Finished simulating #{method.owner.name}##{method.name} => #{result.inspect}"
|
|
256
|
+
result
|
|
257
|
+
end
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
def simulate_method_missing(insn, klass)
|
|
261
|
+
error_klass = insn.node.type == :vcall ? 'NameError' : 'NoMethodError'
|
|
262
|
+
missing_method_error = ClassRegistry[error_klass].laser_simulate(
|
|
263
|
+
'new', ["Method missing: #{klass.name}##{insn[3]}", insn[3].to_s])
|
|
264
|
+
Bootstrap::EXCEPTION_STACK.value.push(missing_method_error)
|
|
265
|
+
raise ExitedAbnormally.new(missing_method_error)
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
def simulate_special_method(receiver, method, args, block, opts)
|
|
269
|
+
case method
|
|
270
|
+
when ClassRegistry['Class'].instance_method(:allocate)
|
|
271
|
+
LaserObject.new(receiver, nil)
|
|
272
|
+
when ClassRegistry['Class'].singleton_class.instance_method(:new)
|
|
273
|
+
LaserClass.new(ClassRegistry['Class'], nil) do |klass|
|
|
274
|
+
klass.superclass = args.first
|
|
275
|
+
end
|
|
276
|
+
when ClassRegistry['Module'].singleton_class.instance_method(:new)
|
|
277
|
+
LaserModule.new
|
|
278
|
+
when ClassRegistry['Kernel'].instance_method(:require)
|
|
279
|
+
simulate_require(args)
|
|
280
|
+
when ClassRegistry['Kernel'].instance_method(:send)
|
|
281
|
+
method_name, *actual_args = args
|
|
282
|
+
klass = Utilities.klass_for(receiver)
|
|
283
|
+
method = klass.instance_method(method_name)
|
|
284
|
+
if should_simulate_call(method, opts)
|
|
285
|
+
simulate_call_dispatch(receiver, method, actual_args, block, opts)
|
|
286
|
+
else
|
|
287
|
+
raise NonDeterminismHappened.new("Nondeterministic call: #{method.inspect}")
|
|
288
|
+
end
|
|
289
|
+
when ClassRegistry['Module'].instance_method(:define_method)
|
|
290
|
+
simulate_define_method(receiver, args, block)
|
|
291
|
+
when ClassRegistry['Module'].instance_method(:module_eval)
|
|
292
|
+
simulate_module_eval(receiver, args, block)
|
|
293
|
+
when ClassRegistry['Laser#Magic'].singleton_class.instance_method(:get_global)
|
|
294
|
+
Scope::GlobalScope.lookup(args.first).value
|
|
295
|
+
when ClassRegistry['Laser#Magic'].singleton_class.instance_method(:set_global)
|
|
296
|
+
Scope::GlobalScope.lookup(args[0]).bind!(args[1])
|
|
297
|
+
when ClassRegistry['Laser#Magic'].singleton_class.instance_method(:current_self)
|
|
298
|
+
opts[:self]
|
|
299
|
+
when ClassRegistry['Laser#Magic'].singleton_class.instance_method(:current_block)
|
|
300
|
+
opts[:block]
|
|
301
|
+
when ClassRegistry['Laser#Magic'].singleton_class.instance_method(:current_arity)
|
|
302
|
+
opts[:formals].size
|
|
303
|
+
when ClassRegistry['Laser#Magic'].singleton_class.instance_method(:current_argument)
|
|
304
|
+
opts[:formals][args.first]
|
|
305
|
+
when ClassRegistry['Laser#Magic'].singleton_class.instance_method(:current_argument_range)
|
|
306
|
+
opts[:formals][args[0], args[1]]
|
|
307
|
+
when ClassRegistry['Laser#Magic'].singleton_class.instance_method(:current_exception),
|
|
308
|
+
ClassRegistry['Laser#Magic'].singleton_class.instance_method(:get_just_raised_exception)
|
|
309
|
+
Bootstrap::EXCEPTION_STACK.value.last
|
|
310
|
+
when ClassRegistry['Laser#Magic'].singleton_class.instance_method(:push_exception)
|
|
311
|
+
Bootstrap::EXCEPTION_STACK.value.push args[0]
|
|
312
|
+
when ClassRegistry['Laser#Magic'].singleton_class.instance_method(:pop_exception)
|
|
313
|
+
Bootstrap::EXCEPTION_STACK.value.pop
|
|
314
|
+
when ClassRegistry['Laser#Magic'].singleton_class.instance_method(:responds?)
|
|
315
|
+
receiver, method_name = args
|
|
316
|
+
Utilities.klass_for(receiver).instance_method(method_name)
|
|
317
|
+
when Scope::GlobalScope.self_ptr.singleton_class.instance_method(:private)
|
|
318
|
+
ClassRegistry['Object'].private(*args)
|
|
319
|
+
when Scope::GlobalScope.self_ptr.singleton_class.instance_method(:public)
|
|
320
|
+
ClassRegistry['Object'].public(*args)
|
|
321
|
+
end
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
def reset_simulation!
|
|
325
|
+
all_variables.each do |temp|
|
|
326
|
+
temp.bind! UNDEFINED
|
|
327
|
+
temp.inferred_type = nil
|
|
328
|
+
end
|
|
329
|
+
vertices.each do |block|
|
|
330
|
+
block.successors.each do |succ|
|
|
331
|
+
block.remove_flag(succ, ControlFlowGraph::EDGE_EXECUTABLE)
|
|
332
|
+
end
|
|
333
|
+
end
|
|
334
|
+
end
|
|
335
|
+
|
|
336
|
+
def simulate_define_method(receiver, args, block)
|
|
337
|
+
if args.size == 1 && block
|
|
338
|
+
receiver.define_method(args.first, block)
|
|
339
|
+
elsif args.size == 2
|
|
340
|
+
receiver.define_method(args[0], args[1])
|
|
341
|
+
else
|
|
342
|
+
raise ArgumentError.new("wrong number of arguments (#{args.size} for 1..2)")
|
|
343
|
+
end
|
|
344
|
+
end
|
|
345
|
+
|
|
346
|
+
# TODO(adgar): validate arguments
|
|
347
|
+
# TODO(adgar): make errors visible
|
|
348
|
+
def simulate_require(args)
|
|
349
|
+
file = args.first
|
|
350
|
+
load_path = Scope::GlobalScope.lookup('$:').value
|
|
351
|
+
loaded_values = Scope::GlobalScope.lookup('$"').value
|
|
352
|
+
to_load = file + '.rb'
|
|
353
|
+
load_path.each do |path|
|
|
354
|
+
joined = File.join(path, to_load)
|
|
355
|
+
if File.exist?(joined)
|
|
356
|
+
if !loaded_values.include?(joined)
|
|
357
|
+
tree = Annotations.annotate_inputs([[joined, File.read(joined)]], optimize: false)
|
|
358
|
+
#node.errors.concat tree[0][1].all_errors
|
|
359
|
+
return true
|
|
360
|
+
end
|
|
361
|
+
return false
|
|
362
|
+
end
|
|
363
|
+
end
|
|
364
|
+
raise LoadError.new("No such file: #{file}")
|
|
365
|
+
end
|
|
366
|
+
|
|
367
|
+
def simulate_module_eval(receiver, args, block)
|
|
368
|
+
# essentially: parse, compile with a custom scope + lexical target (cref), simulate.
|
|
369
|
+
if args.size > 0
|
|
370
|
+
text = args[0]
|
|
371
|
+
file = args[1] || "(eval)"
|
|
372
|
+
line = args[2] || 1 # ignored currently
|
|
373
|
+
tree = Sexp.new(RipperPlus.sexp(text), file, text)
|
|
374
|
+
Annotations.ordered_annotations.each do |annotator|
|
|
375
|
+
annotator.annotate_with_text(tree, text)
|
|
376
|
+
end
|
|
377
|
+
custom_scope = ClosedScope.new(Scope::GlobalScope, receiver.binding)
|
|
378
|
+
cfg = GraphBuilder.new(tree, [], custom_scope).build
|
|
379
|
+
cfg.analyze
|
|
380
|
+
end
|
|
381
|
+
end
|
|
382
|
+
end # Simulation
|
|
383
|
+
end # ControlFlow
|
|
384
|
+
end # Analysis
|
|
385
|
+
end # Laser
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
module Laser
|
|
2
|
+
module Analysis
|
|
3
|
+
module ControlFlow
|
|
4
|
+
# Methods for computing the static single assignment form of a
|
|
5
|
+
# Control Flow Graph.
|
|
6
|
+
module StaticSingleAssignment
|
|
7
|
+
# SSA Form
|
|
8
|
+
def static_single_assignment_form
|
|
9
|
+
calculate_live
|
|
10
|
+
dom_tree = dominator_tree
|
|
11
|
+
place_phi_nodes(dom_tree)
|
|
12
|
+
ssa_name_formals
|
|
13
|
+
rename_for_ssa(enter, dom_tree)
|
|
14
|
+
@in_ssa = true
|
|
15
|
+
self
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
# Places phi nodes, minimally, using DF+
|
|
21
|
+
def place_phi_nodes(dom_tree)
|
|
22
|
+
@globals.each do |temp|
|
|
23
|
+
set = @definition_blocks[temp] | Set[enter]
|
|
24
|
+
iterated_dominance_frontier(set, dom_tree).each do |block|
|
|
25
|
+
if @live[temp].include?(block)
|
|
26
|
+
n = block.real_predecessors.size
|
|
27
|
+
block.instructions.unshift(Instruction.new([:phi, temp, *([temp] * n)], block: block))
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Sets up SSA to handle the formal arguments
|
|
34
|
+
def ssa_name_formals
|
|
35
|
+
self_binding = @root.scope.lookup('self')
|
|
36
|
+
@name_stack[self_binding].push(self_binding)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Renames all variables in the block, and all blocks it dominates,
|
|
40
|
+
# to SSA-form variables by adding a suffix of the form #\d. Since
|
|
41
|
+
# '#' is not allowed in an identifier's name, it will be clear
|
|
42
|
+
# visually what part of the name is the SSA suffix, and it is trivially
|
|
43
|
+
# stripped algorithmically to determine which original temp it refers to.
|
|
44
|
+
#
|
|
45
|
+
# p.175, Morgan
|
|
46
|
+
def rename_for_ssa(block, dom_tree)
|
|
47
|
+
# Note the definition caused by all phi nodes in the block, as
|
|
48
|
+
# phi nodes are evaluated immediately upon entering a block.
|
|
49
|
+
block.phi_nodes.each do |phi_node|
|
|
50
|
+
temp = phi_node[1]
|
|
51
|
+
@name_stack[temp].push(new_ssa_name(temp))
|
|
52
|
+
ssa_name_for(temp).definition = phi_node
|
|
53
|
+
@all_cached_variables << ssa_name_for(temp)
|
|
54
|
+
end
|
|
55
|
+
# Replace current operands and note new definitions
|
|
56
|
+
block.natural_instructions.each do |ins|
|
|
57
|
+
new_operands = []
|
|
58
|
+
ins.operands.each do |temp|
|
|
59
|
+
ssa_uninitialize_fix(temp, block, ins) if ssa_name_for(temp).nil?
|
|
60
|
+
ssa_name_for(temp).uses << ins
|
|
61
|
+
new_operands << ssa_name_for(temp)
|
|
62
|
+
end
|
|
63
|
+
if ins.block_operand
|
|
64
|
+
new_block_operand = ssa_name_for(ins.block_operand)
|
|
65
|
+
new_block_operand.uses << ins
|
|
66
|
+
ins.replace_block_operand(new_block_operand)
|
|
67
|
+
end
|
|
68
|
+
ins.replace_operands(new_operands) unless ins.operands.empty?
|
|
69
|
+
ins.explicit_targets.each do |temp|
|
|
70
|
+
if temp != Bootstrap::VISIBILITY_STACK
|
|
71
|
+
@name_stack[temp].push(new_ssa_name(temp))
|
|
72
|
+
ssa_name_for(temp).definition = ins
|
|
73
|
+
@all_cached_variables << ssa_name_for(temp)
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
# Update all phi nodes this block leads to with the name of
|
|
78
|
+
# the variable this block uses
|
|
79
|
+
# TODO(adgar): make ordering more reliable, it currently relies
|
|
80
|
+
# on the fact that Set uses Hash, and Hashes in 1.9 are ordered
|
|
81
|
+
block.real_successors.each do |succ|
|
|
82
|
+
j = succ.real_predecessors.to_a.index(block)
|
|
83
|
+
tweaked_phi_nodes = ssa_uninitialized_phinode_fix(block, succ, j)
|
|
84
|
+
succ.phi_nodes.each do |phi_node|
|
|
85
|
+
replacement = ssa_name_for(phi_node[j + 2])
|
|
86
|
+
phi_node[j + 2] = replacement
|
|
87
|
+
replacement.uses << phi_node
|
|
88
|
+
end
|
|
89
|
+
tweaked_phi_nodes.each do |phi_node|
|
|
90
|
+
@name_stack[phi_node[1]].pop
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
# Recurse to dominated blocks
|
|
94
|
+
dom_tree[block].each_real_predecessors do |pred|
|
|
95
|
+
rename_for_ssa(self[pred], dom_tree)
|
|
96
|
+
end
|
|
97
|
+
# Update all targets with the current definition
|
|
98
|
+
block.natural_instructions.reverse_each do |ins|
|
|
99
|
+
ins.explicit_targets.each do |target|
|
|
100
|
+
if target != Bootstrap::VISIBILITY_STACK
|
|
101
|
+
ins.replace_target(target, @name_stack[target].pop)
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
block.phi_nodes.each do |ins|
|
|
107
|
+
ins[1] = @name_stack[ins[1]].pop
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def ssa_uninitialized_phinode_fix(block, succ, j)
|
|
112
|
+
phi_nodes_with_undefined_ops = succ.phi_nodes.select do |phi_node|
|
|
113
|
+
ssa_name_for(phi_node[j+2]).nil?
|
|
114
|
+
end
|
|
115
|
+
if phi_nodes_with_undefined_ops.any?
|
|
116
|
+
fixup_block = ssa_phinode_fixup_block(block, succ, j)
|
|
117
|
+
phi_nodes_with_undefined_ops.each do |phi_node|
|
|
118
|
+
assign = ssa_uninitialize_fixing_instruction(
|
|
119
|
+
phi_node[1], phi_node, fixup_block, true)
|
|
120
|
+
fixup_block.instructions << assign
|
|
121
|
+
end
|
|
122
|
+
fixup_block.instructions << Instruction.new([:jump, succ.name], node: nil, block: fixup_block)
|
|
123
|
+
end
|
|
124
|
+
phi_nodes_with_undefined_ops
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def ssa_phinode_fixup_block(predecessor, block, j)
|
|
128
|
+
fixup_block = BasicBlock.new("SSA-FIX-#{block.name}-#{j}")
|
|
129
|
+
add_vertex(fixup_block)
|
|
130
|
+
|
|
131
|
+
predecessor.insert_block_on_edge(block, fixup_block)
|
|
132
|
+
|
|
133
|
+
exit_insn = predecessor.instructions.last
|
|
134
|
+
if exit_insn && exit_insn.type == :jump
|
|
135
|
+
exit_insn[1] = fixup_block.name
|
|
136
|
+
elsif exit_insn && exit_insn.type == :branch
|
|
137
|
+
exit_insn[exit_insn.index(block.name)] = fixup_block.name
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
fixup_block
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# If a block uses a variable in a non-phi instruction, but there
|
|
144
|
+
# is no name for that variable on the current path, then it is
|
|
145
|
+
# being read before it has been written. Uninitialized variables
|
|
146
|
+
# have value nil, so before the read, insert an assignment to
|
|
147
|
+
# nil. SSA will take over from there.
|
|
148
|
+
def ssa_uninitialize_fix(temp, block, ins)
|
|
149
|
+
assignment = ssa_uninitialize_fixing_instruction(temp, ins, block)
|
|
150
|
+
block.instructions.insert(block.instructions.index(ins), assignment)
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def ssa_uninitialize_fixing_instruction(temp, reading_ins, block, renamed=false)
|
|
154
|
+
@name_stack[temp].push(new_ssa_name(temp))
|
|
155
|
+
@all_cached_variables << ssa_name_for(temp)
|
|
156
|
+
target = renamed ? ssa_name_for(temp) : temp
|
|
157
|
+
assignment = Instruction.new([:assign, target, nil],
|
|
158
|
+
node: reading_ins.node, block: block)
|
|
159
|
+
ssa_name_for(temp).definition = assignment
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def ssa_name_for(temp)
|
|
163
|
+
@name_stack[temp].last
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def new_ssa_name(temp)
|
|
167
|
+
@name_count[temp] += 1
|
|
168
|
+
name = "#{temp.name}##{@name_count[temp]}"
|
|
169
|
+
result = Bindings::TemporaryBinding.new(name, nil)
|
|
170
|
+
if temp == @final_return
|
|
171
|
+
@final_return = result
|
|
172
|
+
elsif temp == @final_exception
|
|
173
|
+
@final_exception = result
|
|
174
|
+
elsif temp == @block_register
|
|
175
|
+
@block_register = result
|
|
176
|
+
elsif (scope_owner = temp.self_owner)
|
|
177
|
+
result.self_owner = scope_owner
|
|
178
|
+
scope_owner.self_ptr = result
|
|
179
|
+
end
|
|
180
|
+
result
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
end
|