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,33 @@
|
|
|
1
|
+
require_relative '../spec_helper'
|
|
2
|
+
|
|
3
|
+
# Runs a shit-ton of expectations common among annotations. Since an annotaiton
|
|
4
|
+
# just adds an attribute to a Sexp, we have a very common set of examples o
|
|
5
|
+
# the form:
|
|
6
|
+
# tree[0].new_attribute.should == some_val
|
|
7
|
+
# tree[1][0].new_attribute.should == some_val
|
|
8
|
+
# ...
|
|
9
|
+
# tree[2][2].new_attribute.should == cool_inferred_val
|
|
10
|
+
# and so on. A ton of repetitiveness can be captured by using hashes in the form:
|
|
11
|
+
#
|
|
12
|
+
# {new_attribute: { some_val => [tree[1][0], tree[0], ...],
|
|
13
|
+
# { cool_inferred_val => [tree[2][2], ...] }}}
|
|
14
|
+
#
|
|
15
|
+
# Each attribute is a key in the toplevel hash, and each possible value is a key
|
|
16
|
+
# in the second-level hash. Technically this doesn't remove all duplication, but
|
|
17
|
+
# it's good.
|
|
18
|
+
def expectalot(expectation)
|
|
19
|
+
messages = expectation.keys
|
|
20
|
+
messages.each do |message|
|
|
21
|
+
examples = expectation[message]
|
|
22
|
+
examples.each do |expected_value, recipients|
|
|
23
|
+
recipients.each do |recipient|
|
|
24
|
+
begin
|
|
25
|
+
recipient.send(message).should == expected_value
|
|
26
|
+
rescue Exception => err
|
|
27
|
+
p "#{recipient.inspect}'s #{message} should be #{expected_value.inspect}"
|
|
28
|
+
raise err
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
require_relative 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe Analysis::ArgumentExpansion do
|
|
4
|
+
describe '#arity' do
|
|
5
|
+
it 'can figure out the arity of a simple method call with no arguments' do
|
|
6
|
+
tree = annotate_all('foo( )')[1][0][2]
|
|
7
|
+
ArgumentExpansion.new(tree).arity.should == (0..0)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
it 'can figure out the arity of a simple method call' do
|
|
11
|
+
tree = annotate_all('foo(1, 2, 3)')[1][0][2]
|
|
12
|
+
ArgumentExpansion.new(tree).arity.should == (3..3)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it 'does not count block arguments in arity' do
|
|
16
|
+
tree = annotate_all('foo(1, 2, 3, &d())')[1][0][2]
|
|
17
|
+
ArgumentExpansion.new(tree).arity.should == (3..3)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it 'has an infinite maximum arity in the presence of un-computable splats' do
|
|
21
|
+
tree = annotate_all('foo(1, 2, 3, *foobar())')[1][0][2]
|
|
22
|
+
ArgumentExpansion.new(tree).arity.should == (3..Float::INFINITY)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it 'count arguments after splats' do
|
|
26
|
+
tree = annotate_all('foo(1, 2, 3, *foobar(), 4, 5)')[1][0][2]
|
|
27
|
+
ArgumentExpansion.new(tree).arity.should == (5..Float::INFINITY)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it 'splats constant arguments to precise arity' do
|
|
31
|
+
tree = annotate_all('foo(1, 2, 3, *[:a, :b, :c], 4, 5)')[1][0][2]
|
|
32
|
+
ArgumentExpansion.new(tree).arity.should == (8..8)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
it 'splats constant range arguments to precise arity' do
|
|
36
|
+
tree = annotate_all('foo(1, 2, 3, *(2...10), 4, 5)')[1][0][2]
|
|
37
|
+
ArgumentExpansion.new(tree).arity.should == (13..13)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
describe '#empty?' do
|
|
42
|
+
it 'returns true when no args are being passed' do
|
|
43
|
+
tree = annotate_all('foo( )')[1][0][2]
|
|
44
|
+
ArgumentExpansion.new(tree).should be_empty
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
it 'returns false when args are being passed' do
|
|
48
|
+
tree = annotate_all('foo( 2 )')[1][0][2]
|
|
49
|
+
ArgumentExpansion.new(tree).should_not be_empty
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
describe '#has_block?' do
|
|
54
|
+
it 'returns false with no arguments given' do
|
|
55
|
+
tree = annotate_all('foo()')[1][0][2]
|
|
56
|
+
ArgumentExpansion.new(tree).has_block?.should be false
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
it 'returns true if there is an explicit block argument' do
|
|
60
|
+
tree = annotate_all('foo(1, 2, 3, &d)')[1][0][2]
|
|
61
|
+
ArgumentExpansion.new(tree).has_block?.should be_true
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
it 'returns false if there is an explicit block argument' do
|
|
65
|
+
tree = annotate_all('foo(1, 2, 3, *foobar())')[1][0][2]
|
|
66
|
+
ArgumentExpansion.new(tree).has_block?.should be false
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
describe '#is_constant?' do
|
|
71
|
+
it 'returns true for no params' do
|
|
72
|
+
tree = annotate_all('foo()')[1][0][2]
|
|
73
|
+
ArgumentExpansion.new(tree).is_constant?.should be_true
|
|
74
|
+
end
|
|
75
|
+
it 'returns true if all constituent arguments are constant' do
|
|
76
|
+
tree = annotate_all('foo(1, 2, 3)')[1][0][2]
|
|
77
|
+
ArgumentExpansion.new(tree).is_constant?.should be_true
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
it 'returns true if all constituent arguments are constant in the presenece of splats' do
|
|
81
|
+
tree = annotate_all('foo(1, 2, *[1, 2], 4, *("a"..."d"))')[1][0][2]
|
|
82
|
+
ArgumentExpansion.new(tree).is_constant?.should be_true
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
it 'returns false in the presence of non-constant arguments' do
|
|
86
|
+
tree = annotate_all('foo(1, foobar(), 3)')[1][0][2]
|
|
87
|
+
ArgumentExpansion.new(tree).is_constant?.should be_false
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
describe '#constant_values' do
|
|
92
|
+
it 'returns true for no params' do
|
|
93
|
+
tree = annotate_all('foo()')[1][0][2]
|
|
94
|
+
ArgumentExpansion.new(tree).constant_values.should == []
|
|
95
|
+
end
|
|
96
|
+
it 'returns true if all constituent arguments are constant' do
|
|
97
|
+
tree = annotate_all('foo(1, 2, 3)')[1][0][2]
|
|
98
|
+
ArgumentExpansion.new(tree).constant_values.should == [1, 2, 3]
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
it 'returns true if all constituent arguments are constant in the presence of splats' do
|
|
102
|
+
tree = annotate_all('foo(1, 2, *[1, 2], 4, *("a"..."d"))')[1][0][2]
|
|
103
|
+
ArgumentExpansion.new(tree).constant_values.should == [1, 2, 1, 2, 4, 'a', 'b', 'c']
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
it 'expands a splatted literal array' do
|
|
107
|
+
tree = annotate_all('foobar *[:a, :b]')[1][0][2]
|
|
108
|
+
expansion = ArgumentExpansion.new(tree)
|
|
109
|
+
expansion.is_constant?.should be_true
|
|
110
|
+
expansion.constant_values.should == [:a, :b]
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
require_relative 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe Analysis::Bindings::Base do
|
|
4
|
+
describe '#initialize' do
|
|
5
|
+
it 'has a simple struct-like initializer' do
|
|
6
|
+
name, value = many_mocks(2)
|
|
7
|
+
sym = Analysis::Bindings::Base.new(name, value)
|
|
8
|
+
sym.name.should == name
|
|
9
|
+
sym.value.should == value
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
describe '#<=>' do
|
|
14
|
+
it 'should compare based on name alone' do
|
|
15
|
+
value = mock
|
|
16
|
+
sym1 = Analysis::Bindings::Base.new('hello', value)
|
|
17
|
+
sym2 = Analysis::Bindings::Base.new('helga', value)
|
|
18
|
+
sym1.should > sym2
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
describe Analysis::Bindings::ConstantBinding do
|
|
24
|
+
describe '#bind!' do
|
|
25
|
+
it 'should raise on a rebinding when not forcing' do
|
|
26
|
+
sym = Analysis::Bindings::ConstantBinding.new('hi', 1)
|
|
27
|
+
expect { sym.bind!(2) }.to raise_error(TypeError)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it 'should not raise on a rebinding when forcing' do
|
|
31
|
+
sym = Analysis::Bindings::ConstantBinding.new('hi', 3)
|
|
32
|
+
sym.bind!(4, true)
|
|
33
|
+
sym.value.should == 4
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
require_relative 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe Laser::Comment do
|
|
4
|
+
before do
|
|
5
|
+
body = <<-EOF
|
|
6
|
+
##
|
|
7
|
+
# Returns a repository object for the given path. Should respond to the standard repository
|
|
8
|
+
# API to the best of its ability, and raise a CapabilityError if asked to do something it
|
|
9
|
+
# cannot do from the API.
|
|
10
|
+
#
|
|
11
|
+
# @param [AmpConfig] config the configuration of the current environment, loaded from
|
|
12
|
+
# appropriate configuration files
|
|
13
|
+
# @param [String] path the path/URL in which to open the repository.
|
|
14
|
+
# @param [Boolean] create should a repository be created in the given directory/URL?
|
|
15
|
+
# @return [AbstractLocalRepository] the repository for the given URL
|
|
16
|
+
# @example
|
|
17
|
+
# Repo.pick('abc/def') do |foo|
|
|
18
|
+
# File.open(foo + '/.hg/hgrc')
|
|
19
|
+
# end
|
|
20
|
+
EOF
|
|
21
|
+
@comment = Laser::Comment.new(body, 2, 4)
|
|
22
|
+
normal_annotated_body = <<-EOF
|
|
23
|
+
##
|
|
24
|
+
# Returns a repository object for the given path. Should respond to the standard repository
|
|
25
|
+
# cannot do from the API.
|
|
26
|
+
#
|
|
27
|
+
# config: AmpConfig
|
|
28
|
+
# path: String=
|
|
29
|
+
# create: TrueClass | FalseClass
|
|
30
|
+
# return: AbstractLocalRepository
|
|
31
|
+
# @example
|
|
32
|
+
# Repo.pick('abc/def') do |foo|
|
|
33
|
+
# File.open(foo + '/.hg/hgrc')
|
|
34
|
+
# end
|
|
35
|
+
EOF
|
|
36
|
+
@annotated_comment = Laser::Comment.new(normal_annotated_body, 8, 12)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
describe '#location' do
|
|
40
|
+
it 'packs up the line and column' do
|
|
41
|
+
@comment.location.should == [2, 4]
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
describe '#features' do
|
|
46
|
+
it 'should extract features based on whether line breaks are followed by many spaces' do
|
|
47
|
+
@comment.features.should == [
|
|
48
|
+
'Returns a repository object for the given path. Should respond to the standard repository',
|
|
49
|
+
'API to the best of its ability, and raise a CapabilityError if asked to do something it',
|
|
50
|
+
'cannot do from the API.',
|
|
51
|
+
"@param [AmpConfig] config the configuration of the current environment, loaded from\n"+
|
|
52
|
+
" appropriate configuration files",
|
|
53
|
+
'@param [String] path the path/URL in which to open the repository.',
|
|
54
|
+
'@param [Boolean] create should a repository be created in the given directory/URL?',
|
|
55
|
+
'@return [AbstractLocalRepository] the repository for the given URL',
|
|
56
|
+
"@example\n"+
|
|
57
|
+
" Repo.pick('abc/def') do |foo|\n"+
|
|
58
|
+
" File.open(foo + '/.hg/hgrc')\n"+
|
|
59
|
+
" end"
|
|
60
|
+
]
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
describe '#annotations' do
|
|
65
|
+
it 'should extract annotations and parse them' do
|
|
66
|
+
notes = @annotated_comment.annotations
|
|
67
|
+
parts = notes.map(&:name).zip notes.map(&:type)
|
|
68
|
+
parts.should == [
|
|
69
|
+
['config', Types::ClassType.new('AmpConfig', :covariant)],
|
|
70
|
+
['path', Types::ClassType.new('String', :invariant)],
|
|
71
|
+
['create', Types::UnionType.new([
|
|
72
|
+
Types::ClassType.new('TrueClass', :covariant),
|
|
73
|
+
Types::ClassType.new('FalseClass', :covariant)])],
|
|
74
|
+
['return', Types::ClassType.new('AbstractLocalRepository', :covariant)]
|
|
75
|
+
]
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
describe '#annotation_map' do
|
|
80
|
+
it 'should extract annotations and convert them to a hash keyed by name' do
|
|
81
|
+
map = @annotated_comment.annotation_map.to_a
|
|
82
|
+
parts = map.map { |name, note| [name, note[0].name, note[0].type] }
|
|
83
|
+
parts.should == [
|
|
84
|
+
['config', 'config', Types::ClassType.new('AmpConfig', :covariant)],
|
|
85
|
+
['path', 'path', Types::ClassType.new('String', :invariant)],
|
|
86
|
+
['create', 'create', Types::UnionType.new([
|
|
87
|
+
Types::ClassType.new('TrueClass', :covariant),
|
|
88
|
+
Types::ClassType.new('FalseClass', :covariant)])],
|
|
89
|
+
['return', 'return', Types::ClassType.new('AbstractLocalRepository', :covariant)]
|
|
90
|
+
]
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
require 'set'
|
|
2
|
+
require_relative 'spec_helper'
|
|
3
|
+
|
|
4
|
+
describe ControlFlow::Instruction do
|
|
5
|
+
describe '#explicit_targets' do
|
|
6
|
+
before do
|
|
7
|
+
@temp = Bindings::TemporaryBinding.new('%t1', nil)
|
|
8
|
+
@call_target = Bindings::TemporaryBinding.new('%t2', nil)
|
|
9
|
+
@ary = Bindings::TemporaryBinding.new('%t3', nil)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
it 'should return an empty set for a return node' do
|
|
13
|
+
ControlFlow::Instruction.new([:return, 1, 2]).
|
|
14
|
+
explicit_targets.should == ::Set[]
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
it 'should return an empty set for a jump node' do
|
|
18
|
+
ControlFlow::Instruction.new([:jump, 'B1']).
|
|
19
|
+
explicit_targets.should == ::Set[]
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
it 'should return an empty set for a branch node' do
|
|
23
|
+
ControlFlow::Instruction.new([:branch, 't1', 'b2', 'b3']).
|
|
24
|
+
explicit_targets.should == ::Set[]
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
it 'should return the target for an assign instruction' do
|
|
28
|
+
ControlFlow::Instruction.new([:assign, @temp, 2]).
|
|
29
|
+
explicit_targets.should == ::Set[@temp]
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it 'should return the target for a call instruction' do
|
|
33
|
+
ControlFlow::Instruction.new([:call, @call_target, @temp, 'to_i', :block => false]).
|
|
34
|
+
explicit_targets.should == ::Set[@call_target]
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
it 'should return the target for a call_vararg instruction' do
|
|
38
|
+
ControlFlow::Instruction.new([:call_vararg, @call_target, @temp, 'to_i', @ary, :block => false]).
|
|
39
|
+
explicit_targets.should == ::Set[@call_target]
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
it 'should return the target for a super instruction' do
|
|
43
|
+
ControlFlow::Instruction.new([:super, @call_target, :block => false]).
|
|
44
|
+
explicit_targets.should == ::Set[@call_target]
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
it 'should return the target for a super_vararg instruction' do
|
|
48
|
+
ControlFlow::Instruction.new([:super_vararg, @call_target, @ary, :block => false]).
|
|
49
|
+
explicit_targets.should == ::Set[@call_target]
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
it 'should return the target for a lambda instruction' do
|
|
53
|
+
ControlFlow::Instruction.new([:lambda, @temp, 'B2']).
|
|
54
|
+
explicit_targets.should == ::Set[@temp]
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
describe '#operands' do
|
|
59
|
+
before(:all) do
|
|
60
|
+
@temp = Bindings::TemporaryBinding.new('%t1', nil)
|
|
61
|
+
@call_target = Bindings::TemporaryBinding.new('%t2', nil)
|
|
62
|
+
@ary = Bindings::TemporaryBinding.new('%t3', nil)
|
|
63
|
+
@temp_2 = Bindings::TemporaryBinding.new('%t4', nil)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
it 'should return a set with the returned value for a return node' do
|
|
67
|
+
ControlFlow::Instruction.new([:return, @temp]).
|
|
68
|
+
operands.should == [@temp]
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
it 'should return an empty set for a jump node' do
|
|
72
|
+
ControlFlow::Instruction.new([:jump, 'B1']).
|
|
73
|
+
operands.should == []
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
it 'should return a set with the branched-on value for a branch node' do
|
|
77
|
+
ControlFlow::Instruction.new([:branch, @temp, 'b2', 'b3']).
|
|
78
|
+
operands.should == [@temp]
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
it 'should return the value temporary for an assign instruction' do
|
|
82
|
+
ControlFlow::Instruction.new([:assign, @temp, @ary]).
|
|
83
|
+
operands.should == [@ary]
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
it 'should return the receiver and arguments for a call instruction' do
|
|
87
|
+
ControlFlow::Instruction.new([:call, @call_target, @temp, 'to_i', @temp_2, :block => false]).
|
|
88
|
+
operands.should == [@temp, @temp_2]
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
it 'should return the receiver and arguments for a call_vararg instruction' do
|
|
92
|
+
ControlFlow::Instruction.new([:call_vararg, @call_target, @temp, 'to_i', @ary, :block => false]).
|
|
93
|
+
operands.should == [@temp, @ary]
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
it 'should return the arguments for a super instruction' do
|
|
97
|
+
ControlFlow::Instruction.new([:super, @call_target, @temp_2, :block => false]).
|
|
98
|
+
operands.should == [@temp_2]
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
it 'should return the arguments for a super_vararg instruction' do
|
|
102
|
+
ControlFlow::Instruction.new([:super_vararg, @call_target, @ary, :block => false]).
|
|
103
|
+
operands.should == [@ary]
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
it 'should return the empty set for a lambda instruction' do
|
|
107
|
+
ControlFlow::Instruction.new([:lambda, @temp, 'B2']).
|
|
108
|
+
operands.should == []
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
@@ -0,0 +1,560 @@
|
|
|
1
|
+
require_relative 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe ControlFlow::ConstantPropagation do
|
|
4
|
+
it 'should propagate simple constants along linear code' do
|
|
5
|
+
g = cfg_method <<-EOF
|
|
6
|
+
def foo(x)
|
|
7
|
+
z = 1024
|
|
8
|
+
y = z
|
|
9
|
+
w = y
|
|
10
|
+
end
|
|
11
|
+
EOF
|
|
12
|
+
g.should have_constant('w').with_value(1024)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it 'should infer constants due to semantics of mismatched parallel assignments' do
|
|
16
|
+
g = cfg_method <<-EOF
|
|
17
|
+
def foo(x)
|
|
18
|
+
a, b, c = x, 10
|
|
19
|
+
end
|
|
20
|
+
EOF
|
|
21
|
+
g.should have_constant('b').with_value(10)
|
|
22
|
+
g.should have_constant('c').with_value(nil)
|
|
23
|
+
g.should_not have_constant('a')
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it 'should infer constants due to semantics of 1-to-N parallel assignment' do
|
|
27
|
+
g = cfg_method <<-EOF
|
|
28
|
+
def foo(x)
|
|
29
|
+
a = 1, (2 ** 10), 3
|
|
30
|
+
end
|
|
31
|
+
EOF
|
|
32
|
+
g.should have_constant('a').with_value([1, 1024, 3])
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
it 'should infer constants due to semantics of 1-to-N parallel assignment with splats' do
|
|
36
|
+
g = cfg_method <<-EOF
|
|
37
|
+
def foo(x)
|
|
38
|
+
a = 1, (2 ** 10), 3, *(1..3), 'hi', :a
|
|
39
|
+
end
|
|
40
|
+
EOF
|
|
41
|
+
g.should have_constant('a').with_value([1, 1024, 3, 1, 2, 3, 'hi', :a])
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it 'should infer constants due to semantics of N-to-1 parallel assignment' do
|
|
45
|
+
g = cfg_method <<-EOF
|
|
46
|
+
def foo(x)
|
|
47
|
+
a, b, c = [1, 2, 3]
|
|
48
|
+
end
|
|
49
|
+
EOF
|
|
50
|
+
g.should have_constant('a').with_value(1)
|
|
51
|
+
g.should have_constant('b').with_value(2)
|
|
52
|
+
g.should have_constant('c').with_value(3)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
it 'should infer constants due to semantics of mismatched N-to-1 parallel assignment and Array indexing' do
|
|
56
|
+
g = cfg_method <<-EOF
|
|
57
|
+
def foo(x)
|
|
58
|
+
a, b, c, d = [1, 2, 3]
|
|
59
|
+
end
|
|
60
|
+
EOF
|
|
61
|
+
g.should have_constant('a').with_value(1)
|
|
62
|
+
g.should have_constant('b').with_value(2)
|
|
63
|
+
g.should have_constant('c').with_value(3)
|
|
64
|
+
g.should have_constant('d').with_value(nil)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
it 'should infer constants with LHS star and no RHS star' do
|
|
68
|
+
g = cfg_method <<-EOF
|
|
69
|
+
def foo(x)
|
|
70
|
+
a, *b, d = 1, 2, 3, 4
|
|
71
|
+
end
|
|
72
|
+
EOF
|
|
73
|
+
g.should have_constant('a').with_value(1)
|
|
74
|
+
g.should have_constant('b').with_value([2, 3])
|
|
75
|
+
g.should have_constant('d').with_value(4)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
it 'should infer constants with discarded LHS star and no RHS star' do
|
|
79
|
+
g = cfg_method <<-EOF
|
|
80
|
+
def foo(x)
|
|
81
|
+
a, *, d = 1, 2, 3, :a, 'hi', gets, 4
|
|
82
|
+
end
|
|
83
|
+
EOF
|
|
84
|
+
g.should have_constant('a').with_value(1)
|
|
85
|
+
g.should have_constant('d').with_value(4)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
it 'should infer constants with LHS star and no RHS star and mismatched sizes' do
|
|
89
|
+
g = cfg_method <<-EOF
|
|
90
|
+
def foo(x)
|
|
91
|
+
a, *b, d, e = 1, 2
|
|
92
|
+
end
|
|
93
|
+
EOF
|
|
94
|
+
g.should have_constant('a').with_value(1)
|
|
95
|
+
g.should have_constant('b').with_value([])
|
|
96
|
+
g.should have_constant('d').with_value(2)
|
|
97
|
+
g.should have_constant('e').with_value(nil)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
it 'should infer constants with LHS star and no RHS star and mismatched sizes: part two' do
|
|
101
|
+
g = cfg_method <<-EOF
|
|
102
|
+
def foo(x)
|
|
103
|
+
a, b, c, *d = 1, 2
|
|
104
|
+
end
|
|
105
|
+
EOF
|
|
106
|
+
g.should have_constant('a').with_value(1)
|
|
107
|
+
g.should have_constant('b').with_value(2)
|
|
108
|
+
g.should have_constant('c').with_value(nil)
|
|
109
|
+
g.should have_constant('d').with_value([])
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
context 'with implicitly-splatted single RHS' do
|
|
113
|
+
it 'should infer constants with LHS star and no RHS star' do
|
|
114
|
+
g = cfg_method <<-EOF
|
|
115
|
+
def foo(x)
|
|
116
|
+
a, *b, d = [1, 2, 3, 4]
|
|
117
|
+
x = a
|
|
118
|
+
y = b
|
|
119
|
+
z = d
|
|
120
|
+
end
|
|
121
|
+
EOF
|
|
122
|
+
g.should have_constant('x').with_value(1)
|
|
123
|
+
g.should have_constant('y').with_value([2, 3])
|
|
124
|
+
g.should have_constant('z').with_value(4)
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
it 'should infer constants with discarded LHS star and no RHS star' do
|
|
128
|
+
g = cfg_method <<-EOF
|
|
129
|
+
def foo(x)
|
|
130
|
+
a, *, d = [1, 2, 3, :a, 'hi', {}, 4]
|
|
131
|
+
x = a
|
|
132
|
+
y = d
|
|
133
|
+
end
|
|
134
|
+
EOF
|
|
135
|
+
g.should have_constant('x').with_value(1)
|
|
136
|
+
g.should have_constant('y').with_value(4)
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
it 'should infer constants with LHS star and no RHS star and mismatched sizes' do
|
|
140
|
+
g = cfg_method <<-EOF
|
|
141
|
+
def foo(x)
|
|
142
|
+
a, *b, d, e = [1, 2]
|
|
143
|
+
rest = b
|
|
144
|
+
x = d
|
|
145
|
+
y = e
|
|
146
|
+
end
|
|
147
|
+
EOF
|
|
148
|
+
g.should have_constant('a').with_value(1)
|
|
149
|
+
g.should have_constant('rest').with_value([])
|
|
150
|
+
g.should have_constant('x').with_value(2)
|
|
151
|
+
g.should have_constant('y').with_value(nil)
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
it 'should infer constants with LHS star and no RHS star and mismatched sizes: part two' do
|
|
155
|
+
g = cfg_method <<-EOF
|
|
156
|
+
def foo(x)
|
|
157
|
+
a, b, c, *d = [1, 2]
|
|
158
|
+
rest = d
|
|
159
|
+
end
|
|
160
|
+
EOF
|
|
161
|
+
g.should have_constant('a').with_value(1)
|
|
162
|
+
g.should have_constant('b').with_value(2)
|
|
163
|
+
g.should have_constant('c').with_value(nil)
|
|
164
|
+
g.should have_constant('rest').with_value([])
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
it 'should infer constants with an RHS star' do
|
|
169
|
+
g = cfg_method <<-EOF
|
|
170
|
+
def foo(x)
|
|
171
|
+
a, b, c, d, e, f, g, h = *[2, 3, 4], :a, *[5], *['hi'], 9, 10, 11, 12
|
|
172
|
+
end
|
|
173
|
+
EOF
|
|
174
|
+
g.should have_constant('a').with_value(2)
|
|
175
|
+
g.should have_constant('b').with_value(3)
|
|
176
|
+
g.should have_constant('c').with_value(4)
|
|
177
|
+
g.should have_constant('d').with_value(:a)
|
|
178
|
+
g.should have_constant('e').with_value(5)
|
|
179
|
+
g.should have_constant('f').with_value('hi')
|
|
180
|
+
g.should have_constant('g').with_value(9)
|
|
181
|
+
g.should have_constant('h').with_value(10)
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
it 'should infer constants with an RHS star and unmet lhs vars' do
|
|
185
|
+
g = cfg_method <<-EOF
|
|
186
|
+
def foo(x)
|
|
187
|
+
a, b, c, d, e, f, g, h = *[2, 3, 4], :a
|
|
188
|
+
end
|
|
189
|
+
EOF
|
|
190
|
+
g.should have_constant('a').with_value(2)
|
|
191
|
+
g.should have_constant('b').with_value(3)
|
|
192
|
+
g.should have_constant('c').with_value(4)
|
|
193
|
+
g.should have_constant('d').with_value(:a)
|
|
194
|
+
g.should have_constant('e').with_value(nil)
|
|
195
|
+
g.should have_constant('f').with_value(nil)
|
|
196
|
+
g.should have_constant('g').with_value(nil)
|
|
197
|
+
g.should have_constant('h').with_value(nil)
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
it 'should infer constants with LHS and RHS stars' do
|
|
201
|
+
g = cfg_method <<-EOF
|
|
202
|
+
def foo(x)
|
|
203
|
+
a, b, *rest, f, g, h = *[2, 3, 4], :a, *[5], *['hi'], 9, 10, 11, 12
|
|
204
|
+
end
|
|
205
|
+
EOF
|
|
206
|
+
g.should have_constant('a').with_value(2)
|
|
207
|
+
g.should have_constant('b').with_value(3)
|
|
208
|
+
g.should have_constant('rest').with_value([4, :a, 5, 'hi', 9])
|
|
209
|
+
g.should have_constant('f').with_value(10)
|
|
210
|
+
g.should have_constant('g').with_value(11)
|
|
211
|
+
g.should have_constant('h').with_value(12)
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
it 'should propagate when the same variable is assigned to the same constant in branches' do
|
|
215
|
+
g = cfg_method <<-EOF
|
|
216
|
+
def foo(x)
|
|
217
|
+
if gets.size > 0
|
|
218
|
+
y = 20
|
|
219
|
+
else
|
|
220
|
+
y = 20
|
|
221
|
+
end
|
|
222
|
+
z = y
|
|
223
|
+
end
|
|
224
|
+
EOF
|
|
225
|
+
g.should have_constant('z').with_value(20)
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
it 'should propagate manipulation of hashes' do
|
|
229
|
+
g = cfg_method <<-EOF
|
|
230
|
+
def foo(x)
|
|
231
|
+
z = {a: 3, 'd' => 4.5}
|
|
232
|
+
j = z[:a]
|
|
233
|
+
arr = z.values
|
|
234
|
+
end
|
|
235
|
+
EOF
|
|
236
|
+
g.should have_constant('j').with_value(3)
|
|
237
|
+
g.should have_constant('arr').with_value([3, 4.5])
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
it 'should not propagate when the same variable is assigned to distinct constants in branches' do
|
|
241
|
+
g = cfg_method <<-EOF
|
|
242
|
+
def foo(x)
|
|
243
|
+
if gets.size > 0
|
|
244
|
+
y = 20
|
|
245
|
+
else
|
|
246
|
+
y = 30
|
|
247
|
+
end
|
|
248
|
+
z = y
|
|
249
|
+
end
|
|
250
|
+
EOF
|
|
251
|
+
g.should_not have_constant('z')
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
it 'should ignore branches on false' do
|
|
255
|
+
g = cfg_method <<-EOF
|
|
256
|
+
def foo(x)
|
|
257
|
+
if false
|
|
258
|
+
y = 40
|
|
259
|
+
else
|
|
260
|
+
y = 30
|
|
261
|
+
end
|
|
262
|
+
z = y
|
|
263
|
+
end
|
|
264
|
+
EOF
|
|
265
|
+
g.should have_constant('z').with_value(30)
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
it 'should ignore else branches on true' do
|
|
269
|
+
g = cfg_method <<-EOF
|
|
270
|
+
def foo(x)
|
|
271
|
+
if true
|
|
272
|
+
y = 40
|
|
273
|
+
else
|
|
274
|
+
y = 30
|
|
275
|
+
end
|
|
276
|
+
z = y
|
|
277
|
+
end
|
|
278
|
+
EOF
|
|
279
|
+
g.should have_constant('z').with_value(40)
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
it 'should ignore branches on constant variables' do
|
|
283
|
+
g = cfg_method <<-EOF
|
|
284
|
+
def foo(x)
|
|
285
|
+
a = true ? 30 : false
|
|
286
|
+
if a
|
|
287
|
+
y = 40
|
|
288
|
+
else
|
|
289
|
+
y = 30
|
|
290
|
+
end
|
|
291
|
+
z = y
|
|
292
|
+
end
|
|
293
|
+
EOF
|
|
294
|
+
g.should have_constant('z').with_value(40)
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
it 'should ignore branches on fixnum equality' do
|
|
298
|
+
g = cfg_method <<-EOF
|
|
299
|
+
def foo(x)
|
|
300
|
+
a = true ? 30 : false
|
|
301
|
+
if a == 30
|
|
302
|
+
y = 40
|
|
303
|
+
else
|
|
304
|
+
y = 30
|
|
305
|
+
end
|
|
306
|
+
z = y
|
|
307
|
+
end
|
|
308
|
+
EOF
|
|
309
|
+
g.should have_constant('z').with_value(40)
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
it 'should ignore branches on non-constants whose types are never be nil or false' do
|
|
313
|
+
g = cfg_method <<-EOF
|
|
314
|
+
def foo(x)
|
|
315
|
+
a = x.nil? ? 30 : 'hello'
|
|
316
|
+
b = a * 2
|
|
317
|
+
if b
|
|
318
|
+
y = 40
|
|
319
|
+
else
|
|
320
|
+
y = 30
|
|
321
|
+
end
|
|
322
|
+
z = y
|
|
323
|
+
end
|
|
324
|
+
EOF
|
|
325
|
+
g.should have_constant('z').with_value(40)
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
it 'should calculate bignum arithmetic' do
|
|
329
|
+
g = cfg_method <<-EOF
|
|
330
|
+
def foo(x)
|
|
331
|
+
a = 3 * (1 << 3)
|
|
332
|
+
b = a ** a
|
|
333
|
+
c = b - a
|
|
334
|
+
end
|
|
335
|
+
EOF
|
|
336
|
+
g.should have_constant('c').with_value(1333735776850284124449081472843752)
|
|
337
|
+
end
|
|
338
|
+
|
|
339
|
+
it 'should handle string constants' do
|
|
340
|
+
g = cfg_method <<-EOF
|
|
341
|
+
def foo(x)
|
|
342
|
+
y = 'hello' * 3
|
|
343
|
+
if y == 'hellohellohello'
|
|
344
|
+
puts gets
|
|
345
|
+
z = 3
|
|
346
|
+
else
|
|
347
|
+
z = 10
|
|
348
|
+
end
|
|
349
|
+
a = z
|
|
350
|
+
end
|
|
351
|
+
EOF
|
|
352
|
+
g.should have_constant('a').with_value(3)
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
it 'should calculate 0 * (varying numeric) = 0' do
|
|
356
|
+
g = cfg_method <<-EOF
|
|
357
|
+
def foo(x)
|
|
358
|
+
if gets.size > 0
|
|
359
|
+
a = 5
|
|
360
|
+
else
|
|
361
|
+
a = 10.11
|
|
362
|
+
end
|
|
363
|
+
b = 0 * a
|
|
364
|
+
c = a * 0
|
|
365
|
+
end
|
|
366
|
+
EOF
|
|
367
|
+
g.should have_constant('b').with_value(0)
|
|
368
|
+
g.should have_constant('c').with_value(0)
|
|
369
|
+
end
|
|
370
|
+
|
|
371
|
+
it 'should calculate (varying string) * 0 = ""' do
|
|
372
|
+
g = cfg_method <<-EOF
|
|
373
|
+
def foo(x)
|
|
374
|
+
if gets.size > 0
|
|
375
|
+
a = 'hello'
|
|
376
|
+
else
|
|
377
|
+
a = 'world'
|
|
378
|
+
end
|
|
379
|
+
c = a * 0
|
|
380
|
+
end
|
|
381
|
+
EOF
|
|
382
|
+
g.should have_constant('c').with_value('')
|
|
383
|
+
end
|
|
384
|
+
|
|
385
|
+
it 'should calculate (varying array) * 0 = []' do
|
|
386
|
+
g = cfg_method <<-EOF
|
|
387
|
+
def foo(x)
|
|
388
|
+
if gets.size > 0
|
|
389
|
+
a = [1, 'hello']
|
|
390
|
+
else
|
|
391
|
+
a = [2, 'world', 'thing']
|
|
392
|
+
end
|
|
393
|
+
c = a * 0
|
|
394
|
+
end
|
|
395
|
+
EOF
|
|
396
|
+
g.should have_constant('c').with_value([])
|
|
397
|
+
end
|
|
398
|
+
|
|
399
|
+
it 'should calculate 0 * (varying numeric | string) = varying' do
|
|
400
|
+
g = cfg_method <<-EOF
|
|
401
|
+
def foo(x)
|
|
402
|
+
if gets.size > 0
|
|
403
|
+
a = 5
|
|
404
|
+
else
|
|
405
|
+
a = 'hello'
|
|
406
|
+
end
|
|
407
|
+
b = 0 * a
|
|
408
|
+
c = a * 0
|
|
409
|
+
end
|
|
410
|
+
EOF
|
|
411
|
+
g.should_not have_constant('b')
|
|
412
|
+
g.should_not have_constant('c')
|
|
413
|
+
end
|
|
414
|
+
|
|
415
|
+
it 'should calculate (varying numeric) ** 0 = 1' do
|
|
416
|
+
g = cfg_method <<-EOF
|
|
417
|
+
def foo(x)
|
|
418
|
+
if gets.size > 0
|
|
419
|
+
a = 5.93
|
|
420
|
+
else
|
|
421
|
+
a = 10
|
|
422
|
+
end
|
|
423
|
+
c = a ** 0
|
|
424
|
+
end
|
|
425
|
+
EOF
|
|
426
|
+
g.should have_constant('c').with_value(1)
|
|
427
|
+
end
|
|
428
|
+
|
|
429
|
+
it 'should calculate (varying numeric | string) ** 0 = varying' do
|
|
430
|
+
g = cfg_method <<-EOF
|
|
431
|
+
def foo(x)
|
|
432
|
+
if gets.size > 0
|
|
433
|
+
a = 5
|
|
434
|
+
else
|
|
435
|
+
a = 'hello'
|
|
436
|
+
end
|
|
437
|
+
c = a ** 0
|
|
438
|
+
end
|
|
439
|
+
EOF
|
|
440
|
+
g.should_not have_constant('c')
|
|
441
|
+
end
|
|
442
|
+
|
|
443
|
+
it 'should calculate 1 ** (varying numeric) = 1' do
|
|
444
|
+
g = cfg_method <<-EOF
|
|
445
|
+
def foo(x)
|
|
446
|
+
if gets.size > 0
|
|
447
|
+
a = 5
|
|
448
|
+
else
|
|
449
|
+
a = 10.2
|
|
450
|
+
end
|
|
451
|
+
c = 1 ** a
|
|
452
|
+
end
|
|
453
|
+
EOF
|
|
454
|
+
g.should have_constant('c').with_value(1)
|
|
455
|
+
end
|
|
456
|
+
|
|
457
|
+
it 'should calculate 1 ** (varying numeric | string) = varying' do
|
|
458
|
+
g = cfg_method <<-EOF
|
|
459
|
+
def foo(x)
|
|
460
|
+
if gets.size > 0
|
|
461
|
+
a = 5
|
|
462
|
+
else
|
|
463
|
+
a = 'hello'
|
|
464
|
+
end
|
|
465
|
+
c = 1 ** a
|
|
466
|
+
end
|
|
467
|
+
EOF
|
|
468
|
+
g.should_not have_constant('c')
|
|
469
|
+
end
|
|
470
|
+
|
|
471
|
+
it 'should infer that assignments after a guaranteed raise do not affect CP' do
|
|
472
|
+
g = cfg_method <<-EOF
|
|
473
|
+
def foo(x)
|
|
474
|
+
if gets.size > 0
|
|
475
|
+
a = 5
|
|
476
|
+
else
|
|
477
|
+
raise 'arrrrrrr'
|
|
478
|
+
a = 10.2
|
|
479
|
+
end
|
|
480
|
+
c = a
|
|
481
|
+
end
|
|
482
|
+
EOF
|
|
483
|
+
g.should have_constant('c').with_value(5)
|
|
484
|
+
end
|
|
485
|
+
|
|
486
|
+
it 'should infer that assignments after a guaranteed raise (by CP simulation) do not affect CP' do
|
|
487
|
+
g = cfg_method <<-EOF
|
|
488
|
+
def foo(x)
|
|
489
|
+
if gets.size > 0
|
|
490
|
+
a = 5
|
|
491
|
+
else
|
|
492
|
+
a = 5 / 0
|
|
493
|
+
end
|
|
494
|
+
c = a
|
|
495
|
+
end
|
|
496
|
+
EOF
|
|
497
|
+
g.should have_constant('c').with_value(5)
|
|
498
|
+
g.return_type.should equal_type Types::FIXNUM
|
|
499
|
+
end
|
|
500
|
+
|
|
501
|
+
it 'should always simulate annotated-pure methods' do
|
|
502
|
+
g = cfg <<-EOF
|
|
503
|
+
module CPSim1
|
|
504
|
+
# pure: true
|
|
505
|
+
def self.make(x, y)
|
|
506
|
+
{x: x, y: y}
|
|
507
|
+
end
|
|
508
|
+
end
|
|
509
|
+
EOF
|
|
510
|
+
g2 = cfg_method <<-EOF
|
|
511
|
+
def foo(x)
|
|
512
|
+
made = CPSim1.make(3, 'foo')
|
|
513
|
+
z1 = made[:x]
|
|
514
|
+
z2 = made[:y]
|
|
515
|
+
end
|
|
516
|
+
EOF
|
|
517
|
+
g2.should have_constant('z1').with_value(3)
|
|
518
|
+
g2.should have_constant('z2').with_value('foo')
|
|
519
|
+
g2.return_type.should equal_type Types::STRING
|
|
520
|
+
ClassRegistry['CPSim1'].singleton_class.instance_method(:make).
|
|
521
|
+
return_type_for_types(Utilities.type_for(ClassRegistry['CPSim1']),
|
|
522
|
+
[Types::FIXNUM, Types::STRING], Types::NILCLASS).should equal_type(Types::UnionType.new([Types::HASH]))
|
|
523
|
+
end
|
|
524
|
+
|
|
525
|
+
it 'should handle the tricky x = y unless defined? x case' do
|
|
526
|
+
g = cfg_method <<-EOF
|
|
527
|
+
def foo
|
|
528
|
+
y = 5 unless defined? y
|
|
529
|
+
z = y
|
|
530
|
+
end
|
|
531
|
+
EOF
|
|
532
|
+
g.should have_constant('z').with_value(nil)
|
|
533
|
+
end
|
|
534
|
+
|
|
535
|
+
it 'should handle a basic positive defined?(Const) case' do
|
|
536
|
+
g = cfg <<-EOF
|
|
537
|
+
module CP2
|
|
538
|
+
end
|
|
539
|
+
CP3 = 10 if defined?(CP2)
|
|
540
|
+
EOF
|
|
541
|
+
ClassRegistry['Object'].const_get('CP3').should == 10
|
|
542
|
+
end
|
|
543
|
+
|
|
544
|
+
it 'should handle a basic negative defined?(Const) case' do
|
|
545
|
+
g = cfg <<-EOF
|
|
546
|
+
CP4 = 10 unless defined?(CPNone)
|
|
547
|
+
EOF
|
|
548
|
+
ClassRegistry['Object'].const_get('CP4').should == 10
|
|
549
|
+
end
|
|
550
|
+
|
|
551
|
+
it 'should handle blocks used in constant propagation' do
|
|
552
|
+
g = cfg_method <<-EOF
|
|
553
|
+
def foo_blockitized
|
|
554
|
+
z = %w(a b c).map { |y| y * 2 }
|
|
555
|
+
10
|
|
556
|
+
end
|
|
557
|
+
EOF
|
|
558
|
+
g.should have_constant('z').with_value(['aa', 'bb', 'cc'])
|
|
559
|
+
end
|
|
560
|
+
end
|