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,2505 @@
|
|
1
|
+
module Laser
|
2
|
+
module Analysis
|
3
|
+
module ControlFlow
|
4
|
+
# This class builds a control flow graph. The algorithm used is
|
5
|
+
# derived from Robert Morgan's "Building an Optimizing Compiler".
|
6
|
+
class GraphBuilder
|
7
|
+
attr_reader :graph, :enter, :exit, :temporary_counter, :current_block, :sexp
|
8
|
+
attr_reader :self_register
|
9
|
+
|
10
|
+
def initialize(sexp, formals=[], scope=Scope::GlobalScope)
|
11
|
+
@sexp = sexp
|
12
|
+
@formals = formals
|
13
|
+
@graph = @enter = @exit = nil
|
14
|
+
@scope_stack = [scope]
|
15
|
+
@self_stack = []
|
16
|
+
@temporary_counter = 0
|
17
|
+
@temporary_table = Hash.new do |hash, keys|
|
18
|
+
@temporary_counter += 1
|
19
|
+
hash[keys] = Bindings::TemporaryBinding.new("%t#{@temporary_counter}", nil)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def build
|
24
|
+
initialize_graph
|
25
|
+
@current_node = @sexp
|
26
|
+
@self_register = Bindings::TemporaryBinding.new('self', current_scope.self_ptr)
|
27
|
+
build_prologue
|
28
|
+
result = walk_node @sexp, value: true
|
29
|
+
if @sexp.type == :program
|
30
|
+
uncond_instruct @current_return
|
31
|
+
else
|
32
|
+
return_uncond_jump_instruct result
|
33
|
+
end
|
34
|
+
|
35
|
+
@graph.prune_totally_useless_blocks
|
36
|
+
@graph
|
37
|
+
end
|
38
|
+
|
39
|
+
def current_namespace
|
40
|
+
scope = current_scope
|
41
|
+
scope = scope.parent if @scope_stack.size == 1 && @sexp.type != :program
|
42
|
+
scope.lexical_target
|
43
|
+
end
|
44
|
+
|
45
|
+
def push_scope(scope)
|
46
|
+
@scope_stack.push scope
|
47
|
+
end
|
48
|
+
|
49
|
+
def current_scope
|
50
|
+
@scope_stack.last
|
51
|
+
end
|
52
|
+
|
53
|
+
def pop_scope
|
54
|
+
@scope_stack.pop
|
55
|
+
end
|
56
|
+
|
57
|
+
def with_scope(scope)
|
58
|
+
push_scope scope
|
59
|
+
yield
|
60
|
+
ensure
|
61
|
+
pop_scope
|
62
|
+
end
|
63
|
+
|
64
|
+
def push_self(obj)
|
65
|
+
copy_instruct(@self_register, obj)
|
66
|
+
@self_stack.push obj
|
67
|
+
end
|
68
|
+
|
69
|
+
def current_self
|
70
|
+
@self_stack.last
|
71
|
+
end
|
72
|
+
|
73
|
+
def pop_self
|
74
|
+
@self_stack.pop
|
75
|
+
copy_instruct(@self_register, current_self)
|
76
|
+
end
|
77
|
+
|
78
|
+
def with_self(namespace)
|
79
|
+
push_self namespace
|
80
|
+
yield
|
81
|
+
ensure
|
82
|
+
pop_self
|
83
|
+
end
|
84
|
+
|
85
|
+
def query_self
|
86
|
+
call_instruct(ClassRegistry['Laser#Magic'].binding,
|
87
|
+
:current_self, value: true, raise: false)
|
88
|
+
end
|
89
|
+
|
90
|
+
def reobserve_current_exception
|
91
|
+
cur_exception = call_instruct(ClassRegistry['Laser#Magic'].binding,
|
92
|
+
:current_exception, value: true, raise: false)
|
93
|
+
copy_instruct(@exception_register, cur_exception)
|
94
|
+
end
|
95
|
+
|
96
|
+
def observe_just_raised_exception
|
97
|
+
cur_exception = call_instruct(ClassRegistry['Laser#Magic'].binding,
|
98
|
+
:get_just_raised_exception, value: true, raise: false)
|
99
|
+
copy_instruct(@exception_register, cur_exception)
|
100
|
+
end
|
101
|
+
|
102
|
+
def build_exception_blocks
|
103
|
+
@return_register = create_temporary('t#return_value')
|
104
|
+
@graph.final_return = create_temporary('t#final_return')
|
105
|
+
@exception_register = create_temporary('t#exception_value')
|
106
|
+
@graph.final_exception = create_temporary('t#exit_exception')
|
107
|
+
@current_return = create_block(ControlFlowGraph::RETURN_POSTDOMINATOR_NAME)
|
108
|
+
@current_rescue = create_block(ControlFlowGraph::EXCEPTION_POSTDOMINATOR_NAME)
|
109
|
+
@current_yield_fail = create_block(ControlFlowGraph::YIELD_POSTDOMINATOR_NAME)
|
110
|
+
joined = create_block(ControlFlowGraph::FAILURE_POSTDOMINATOR_NAME)
|
111
|
+
with_current_basic_block(@current_rescue) do
|
112
|
+
uncond_instruct joined, flags: RGL::ControlFlowGraph::EDGE_ABNORMAL
|
113
|
+
end
|
114
|
+
with_current_basic_block(@current_yield_fail) do
|
115
|
+
uncond_instruct joined, flags: RGL::ControlFlowGraph::EDGE_ABNORMAL
|
116
|
+
end
|
117
|
+
with_current_basic_block(joined) do
|
118
|
+
copy_instruct(@graph.final_exception, @exception_register)
|
119
|
+
add_instruction(:raise, @graph.final_exception)
|
120
|
+
uncond_instruct @exit, flags: RGL::ControlFlowGraph::EDGE_ABNORMAL, jump_instruct: false
|
121
|
+
end
|
122
|
+
with_current_basic_block(@current_return) do
|
123
|
+
copy_instruct(@graph.final_return, @return_register)
|
124
|
+
add_instruction(:return, @graph.final_return)
|
125
|
+
uncond_instruct @exit, jump_instruct: false
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def reset_visibility_stack
|
130
|
+
initial = call_instruct(ClassRegistry['Array'].binding, :[], const_instruct(:private), value: true, raise: false)
|
131
|
+
copy_instruct(Bootstrap::VISIBILITY_STACK, initial)
|
132
|
+
end
|
133
|
+
|
134
|
+
def build_prologue
|
135
|
+
uncond_instruct create_block
|
136
|
+
if @sexp.type == :program
|
137
|
+
push_self(Scope::GlobalScope.self_ptr)
|
138
|
+
else
|
139
|
+
push_self(query_self)
|
140
|
+
end
|
141
|
+
reset_visibility_stack
|
142
|
+
build_exception_blocks
|
143
|
+
if @sexp.type != :program
|
144
|
+
dynamic_context = ClosedScope.new(current_scope, current_self)
|
145
|
+
@scope_stack = [dynamic_context]
|
146
|
+
@block_arg = call_instruct(ClassRegistry['Laser#Magic'].binding,
|
147
|
+
:current_block, value: true, raise: false)
|
148
|
+
@block_arg.name = 't#current_block'
|
149
|
+
@graph.block_register = @block_arg
|
150
|
+
reobserve_current_exception
|
151
|
+
build_formal_args(@formals, block_arg: @block_arg) unless @formals.empty?
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def formal_arg_range(start, size)
|
156
|
+
call_instruct(ClassRegistry['Laser#Magic'].binding, :current_argument_range,
|
157
|
+
start, size, value: true, raise: false)
|
158
|
+
end
|
159
|
+
|
160
|
+
def formal_arg_at(idx)
|
161
|
+
call_instruct(ClassRegistry['Laser#Magic'].binding, :current_argument, idx,
|
162
|
+
value: true, raise: false)
|
163
|
+
end
|
164
|
+
|
165
|
+
def copy_positionals(args)
|
166
|
+
args.each_with_index do |pos, idx|
|
167
|
+
copy_instruct(pos, formal_arg_at(const_instruct(idx)))
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
def copy_positionals_with_offset(args, offset)
|
172
|
+
args.each_with_index do |pos, idx|
|
173
|
+
dynamic_idx = idx.zero? ? offset : call_instruct(const_instruct(idx), :+, offset,
|
174
|
+
value: true, raise: false)
|
175
|
+
copy_instruct(pos, formal_arg_at(dynamic_idx))
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
def build_formal_args(formals, opts={})
|
180
|
+
formals.each { |formal| current_scope.add_binding!(formal) }
|
181
|
+
|
182
|
+
nb_formals = formals.last.is_block? ? formals[0..-2] : formals
|
183
|
+
has_rest = rest_arg = nb_formals.find(&:is_rest?)
|
184
|
+
min_arity = nb_formals.count(&:is_positional?)
|
185
|
+
optionals = nb_formals.select(&:is_optional?)
|
186
|
+
num_optionals = optionals.size
|
187
|
+
min_nonrest_arity = min_arity + num_optionals
|
188
|
+
# zero dynamic args = easy and more efficient case
|
189
|
+
if !rest_arg && optionals.empty?
|
190
|
+
copy_positionals(nb_formals)
|
191
|
+
else
|
192
|
+
# pre-dynamic positionals
|
193
|
+
cur_arity = call_instruct(ClassRegistry['Laser#Magic'].binding,
|
194
|
+
:current_arity, value: true, raise: false)
|
195
|
+
pre_dynamic_positional = nb_formals.take_while(&:is_positional?)
|
196
|
+
num_pre_dynamics = pre_dynamic_positional.size
|
197
|
+
copy_positionals(pre_dynamic_positional)
|
198
|
+
# optional args
|
199
|
+
previous_optional_block = nil
|
200
|
+
optionals.each_with_index do |argument, index|
|
201
|
+
max_arity_indicating_missing = const_instruct(index + min_arity)
|
202
|
+
has_arg, no_arg = create_blocks 2
|
203
|
+
if previous_optional_block
|
204
|
+
with_current_basic_block(previous_optional_block) do
|
205
|
+
uncond_instruct no_arg
|
206
|
+
end
|
207
|
+
end
|
208
|
+
cond_result = call_instruct(cur_arity, :<, max_arity_indicating_missing,
|
209
|
+
value: true, raise: false)
|
210
|
+
cond_instruct cond_result, no_arg, has_arg
|
211
|
+
|
212
|
+
start_block no_arg
|
213
|
+
arg_value = walk_node(argument.default_value_sexp, value: true)
|
214
|
+
copy_instruct(argument, arg_value)
|
215
|
+
previous_optional_block = @current_block
|
216
|
+
|
217
|
+
start_block has_arg
|
218
|
+
copy_instruct(argument, formal_arg_at(const_instruct(index + num_pre_dynamics)))
|
219
|
+
end
|
220
|
+
|
221
|
+
optionals_done = create_block
|
222
|
+
# rest args
|
223
|
+
if has_rest
|
224
|
+
rest_start = const_instruct(num_pre_dynamics + num_optionals)
|
225
|
+
rest_size = call_instruct(cur_arity, :-, const_instruct(min_nonrest_arity), value: true, raise: false)
|
226
|
+
copy_instruct(rest_arg, formal_arg_range(rest_start, rest_size))
|
227
|
+
end
|
228
|
+
uncond_instruct optionals_done
|
229
|
+
|
230
|
+
if previous_optional_block
|
231
|
+
with_current_basic_block(previous_optional_block) do
|
232
|
+
# at this point, if there was a rest arg, it's empty.
|
233
|
+
if has_rest
|
234
|
+
empty_rest = call_instruct(ClassRegistry['Array'].binding, :[], value: true, raise: false)
|
235
|
+
copy_instruct(rest_arg, empty_rest)
|
236
|
+
end
|
237
|
+
uncond_instruct optionals_done
|
238
|
+
end
|
239
|
+
end
|
240
|
+
start_block optionals_done
|
241
|
+
|
242
|
+
# post-dynamic conditionals
|
243
|
+
post_dynamic_positional = nb_formals[num_pre_dynamics..-1].select(&:is_positional?)
|
244
|
+
post_dynamic_start = call_instruct(cur_arity, :-,
|
245
|
+
const_instruct(post_dynamic_positional.size), value: true, raise: false)
|
246
|
+
copy_positionals_with_offset(post_dynamic_positional, post_dynamic_start)
|
247
|
+
end
|
248
|
+
block_arg = formals.find(&:is_block?)
|
249
|
+
if block_arg
|
250
|
+
if opts[:block_arg]
|
251
|
+
the_block = opts[:block_arg]
|
252
|
+
else
|
253
|
+
the_block = call_instruct(ClassRegistry['Laser#Magic'].binding,
|
254
|
+
:current_block, value: true, raise: false)
|
255
|
+
end
|
256
|
+
copy_instruct(block_arg, the_block)
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
# Creates a new block that jumps to the given target upon completion.
|
261
|
+
# Very useful for building branches.
|
262
|
+
def build_block_with_jump(target = nil, name = nil)
|
263
|
+
new_block = name ? create_block(name) : create_block
|
264
|
+
with_current_basic_block(new_block) do
|
265
|
+
yield
|
266
|
+
uncond_instruct target if target
|
267
|
+
end
|
268
|
+
new_block
|
269
|
+
end
|
270
|
+
|
271
|
+
# yields with the current basic block set to the provided basic block.
|
272
|
+
# useful for quickly adding an edge without directly touching the
|
273
|
+
# graph object.
|
274
|
+
def with_current_basic_block(basic_block)
|
275
|
+
old_block, @current_block = @current_block, basic_block
|
276
|
+
yield
|
277
|
+
ensure
|
278
|
+
@current_block = old_block
|
279
|
+
end
|
280
|
+
|
281
|
+
def with_current_node(node)
|
282
|
+
old_node, @current_node = @current_node, node
|
283
|
+
@current_node.scope = current_scope
|
284
|
+
yield
|
285
|
+
ensure
|
286
|
+
@current_node = old_node
|
287
|
+
end
|
288
|
+
|
289
|
+
# Walks the node differently based on whether the value is needed.
|
290
|
+
def walk_node(node, opts={})
|
291
|
+
with_current_node(node) do
|
292
|
+
case node.type
|
293
|
+
when :bodystmt
|
294
|
+
bodystmt_walk node
|
295
|
+
when :class
|
296
|
+
class_name, superclass, body = node.children
|
297
|
+
class_instruct(class_name, superclass, body, opts)
|
298
|
+
when :module
|
299
|
+
module_name, body = node.children
|
300
|
+
module_instruct(module_name, body, opts)
|
301
|
+
when :sclass
|
302
|
+
receiver, body = node.children
|
303
|
+
singleton_class_instruct receiver, body, opts
|
304
|
+
when :def
|
305
|
+
name, args, body = node.children
|
306
|
+
name = const_instruct(name.expanded_identifier.to_sym)
|
307
|
+
parsed_args = Signature.arg_list_for_arglist(args)
|
308
|
+
def_instruct(current_namespace, name, parsed_args, body, opts)
|
309
|
+
when :defs
|
310
|
+
recv, _, name, args, body = node.children
|
311
|
+
name = const_instruct(name.expanded_identifier)
|
312
|
+
receiver = walk_node(recv, value: true)
|
313
|
+
singleton = call_instruct(receiver, :singleton_class, value: true)
|
314
|
+
parsed_args = Signature.arg_list_for_arglist(args)
|
315
|
+
def_instruct(singleton, name, parsed_args, body, opts)
|
316
|
+
when :alias
|
317
|
+
lhs, rhs = node.children
|
318
|
+
lhs_val = const_instruct(lhs[1].expanded_identifier.to_sym)
|
319
|
+
rhs_val = const_instruct(rhs[1].expanded_identifier.to_sym)
|
320
|
+
call_instruct(current_namespace, :alias_method, lhs_val, rhs_val,
|
321
|
+
value: false, ignore_privacy: true)
|
322
|
+
when :undef
|
323
|
+
undeffed = node.children.first
|
324
|
+
undeffed.each do |method|
|
325
|
+
method_name = const_instruct(method[1].expanded_identifier.to_sym)
|
326
|
+
call_instruct(current_namespace, :undef_method, method_name,
|
327
|
+
value: false, raise: true)
|
328
|
+
end
|
329
|
+
nil
|
330
|
+
when :assign
|
331
|
+
lhs, rhs = node.children
|
332
|
+
single_assign_instruct(lhs, rhs, opts)
|
333
|
+
when :massign
|
334
|
+
lhs, rhs = node.children
|
335
|
+
multiple_assign_instruct(lhs, rhs, opts)
|
336
|
+
when :begin
|
337
|
+
walk_node node[1], opts
|
338
|
+
when :paren
|
339
|
+
walk_body node[1], opts
|
340
|
+
when :while
|
341
|
+
condition, body = node.children
|
342
|
+
while_instruct(condition, body, opts)
|
343
|
+
when :while_mod
|
344
|
+
condition, body_stmt = node.children
|
345
|
+
while_instruct(condition, [body_stmt], opts)
|
346
|
+
when :until
|
347
|
+
condition, body = node.children
|
348
|
+
until_instruct(condition, body, opts)
|
349
|
+
when :until_mod
|
350
|
+
condition, body_stmt = node.children
|
351
|
+
until_instruct(condition, [body_stmt], opts)
|
352
|
+
when :if
|
353
|
+
if_instruct(node, false, opts)
|
354
|
+
when :unless
|
355
|
+
condition, body, else_block = node.children
|
356
|
+
unless_instruct(condition, body, else_block, opts)
|
357
|
+
when :if_mod
|
358
|
+
if_instruct(node, true, opts)
|
359
|
+
when :unless_mod
|
360
|
+
condition, body = node.children
|
361
|
+
unless_instruct(condition, [body], nil, opts)
|
362
|
+
when :rescue_mod
|
363
|
+
rescue_expr, guarded_expr = node.children
|
364
|
+
rescue_mod_instruct(rescue_expr, guarded_expr, opts)
|
365
|
+
when :unary
|
366
|
+
op, receiver = node.children
|
367
|
+
receiver = walk_node(receiver, value: true)
|
368
|
+
call_instruct(receiver, op, opts)
|
369
|
+
when :binary
|
370
|
+
# If someone makes an overloaded operator that mutates something....
|
371
|
+
# we have to run it (maybe), even if we hate them.
|
372
|
+
lhs, op, rhs = node.children
|
373
|
+
binary_instruct(lhs, op, rhs, opts)
|
374
|
+
when :ifop
|
375
|
+
cond, if_true, if_false = node.children
|
376
|
+
ternary_instruct(cond, if_true, if_false, opts)
|
377
|
+
when :const_path_ref
|
378
|
+
lhs, const = node.children
|
379
|
+
lhs_value = walk_node lhs, value: true
|
380
|
+
ident = const_instruct(const.expanded_identifier)
|
381
|
+
call_instruct(lhs_value, :const_get, ident, opts)
|
382
|
+
when :call, :command, :command_call, :aref, :method_add_arg, :vcall
|
383
|
+
issue_call node, opts
|
384
|
+
when :method_add_block
|
385
|
+
# need: the receiver, the method name, the arguments, and the block body
|
386
|
+
method_call = node.method_call
|
387
|
+
receiver = if method_call.receiver_node
|
388
|
+
then walk_node(method_call.receiver_node, value: true)
|
389
|
+
else self_instruct
|
390
|
+
end
|
391
|
+
arg_node = method_call.arg_node
|
392
|
+
arg_node = arg_node[1] if arg_node && arg_node.type == :arg_paren
|
393
|
+
block_arg_bindings = node[2][1] ? Signature.arg_list_for_arglist(node[2][1][1]) : []
|
394
|
+
body_sexp = node[2][2]
|
395
|
+
case node[1].type
|
396
|
+
when :super
|
397
|
+
arg_node = arg_node[1] if arg_node.type == :args_add_block
|
398
|
+
call_method_with_block(
|
399
|
+
receiver, method_call.method_name, arg_node,
|
400
|
+
block_arg_bindings, body_sexp, opts)
|
401
|
+
when :zsuper
|
402
|
+
call_zsuper_with_block(node[1], block_arg_bindings, body_sexp, opts)
|
403
|
+
else
|
404
|
+
opts = opts.merge(ignore_privacy: true) if method_call.implicit_receiver?
|
405
|
+
call_method_with_block(
|
406
|
+
receiver, method_call.method_name, arg_node, block_arg_bindings, body_sexp, opts)
|
407
|
+
end
|
408
|
+
when :super
|
409
|
+
issue_super_call(node)
|
410
|
+
when :zsuper
|
411
|
+
# TODO(adgar): blocks in args & style
|
412
|
+
block = rb_check_convert_type(@block_arg, ClassRegistry['Proc'].binding, :to_proc)
|
413
|
+
invoke_super_with_block(*compute_zsuper_arguments, block, opts)
|
414
|
+
when :yield
|
415
|
+
yield_instruct(node[1], opts)
|
416
|
+
when :yield0
|
417
|
+
yield_instruct(nil, opts)
|
418
|
+
when :return
|
419
|
+
return_instruct node
|
420
|
+
const_instruct(nil) if opts[:value]
|
421
|
+
when :return0
|
422
|
+
return0_instruct
|
423
|
+
const_instruct(nil) if opts[:value]
|
424
|
+
when :break
|
425
|
+
break_instruct(node[1])
|
426
|
+
const_instruct(nil) if opts[:value]
|
427
|
+
when :next
|
428
|
+
next_instruct(node[1])
|
429
|
+
const_instruct(nil) if opts[:value]
|
430
|
+
when :redo
|
431
|
+
redo_instruct
|
432
|
+
const_instruct(nil) if opts[:value]
|
433
|
+
when :void_stmt
|
434
|
+
const_instruct(nil) if opts[:value]
|
435
|
+
when :program
|
436
|
+
uncond_instruct create_block
|
437
|
+
walk_body node[1], value: false
|
438
|
+
when :dot3
|
439
|
+
start, stop = node.children
|
440
|
+
start_val = walk_node(start, value: true)
|
441
|
+
stop_val = walk_node(stop, value: true)
|
442
|
+
true_val = const_instruct(true)
|
443
|
+
call_instruct(ClassRegistry['Range'].binding, :new, start_val, stop_val, true_val, opts)
|
444
|
+
when :dot2
|
445
|
+
start, stop = node.children
|
446
|
+
start_val = walk_node(start, value: true)
|
447
|
+
stop_val = walk_node(stop, value: true)
|
448
|
+
false_val = const_instruct(false)
|
449
|
+
call_instruct(ClassRegistry['Range'].binding, :new, start_val, stop_val, false_val, opts)
|
450
|
+
else
|
451
|
+
opts[:value] ? value_walk(node) : novalue_walk(node)
|
452
|
+
end
|
453
|
+
end
|
454
|
+
end
|
455
|
+
|
456
|
+
# Walks the node expecting that the expression's return value will be discarded.
|
457
|
+
# Since everything is an expression in Ruby, knowing when to ignore return
|
458
|
+
# values is nice.
|
459
|
+
def novalue_walk(node)
|
460
|
+
with_current_node(node) do
|
461
|
+
case node.type
|
462
|
+
when :void_stmt
|
463
|
+
# Do nothing.
|
464
|
+
when :massign
|
465
|
+
lhs, rhs = node.children
|
466
|
+
multiple_assign_instruct(lhs, rhs, value: false)
|
467
|
+
when :opassign
|
468
|
+
lhs, op, rhs = node.children
|
469
|
+
op = op.expanded_identifier[0..-2].to_sym
|
470
|
+
if lhs.type == :field
|
471
|
+
receiver = walk_node lhs[1], value: true
|
472
|
+
method_name = lhs[3].expanded_identifier
|
473
|
+
# Receiver is ONLY EVALUATED ONCE
|
474
|
+
# (on ruby 1.9.2p136 (2010-12-25 revision 30365) [x86_64-darwin10.6.0])
|
475
|
+
current_val = call_instruct(receiver, method_name.to_sym, block: false, value: true)
|
476
|
+
if op == :"||"
|
477
|
+
false_block, after = create_blocks 2
|
478
|
+
cond_instruct(current_val, after, false_block)
|
479
|
+
|
480
|
+
start_block false_block
|
481
|
+
rhs_value = walk_node rhs, value: true
|
482
|
+
call_instruct(receiver, "#{method_name}=".to_sym, rhs_value, block: false, value: false)
|
483
|
+
uncond_instruct after
|
484
|
+
|
485
|
+
start_block after
|
486
|
+
elsif op == :"&&"
|
487
|
+
true_block, after = create_blocks 2
|
488
|
+
cond_instruct(current_val, true_block, after)
|
489
|
+
|
490
|
+
start_block true_block
|
491
|
+
rhs_value = walk_node rhs, value: true
|
492
|
+
call_instruct(receiver, "#{method_name}=".to_sym, rhs_value, block: false, value: false)
|
493
|
+
uncond_instruct after
|
494
|
+
|
495
|
+
start_block after
|
496
|
+
else
|
497
|
+
rhs_value = walk_node rhs, value: true
|
498
|
+
temp_result = call_instruct(current_val, op, rhs_value, block: false, value: true)
|
499
|
+
call_instruct(receiver, "#{method_name}=".to_sym, temp_result, block: false, value: false)
|
500
|
+
end
|
501
|
+
# TODO(adgar): aref_field
|
502
|
+
else
|
503
|
+
result = binary_instruct(lhs, op, rhs, value: true)
|
504
|
+
single_assign_instruct(lhs, result)
|
505
|
+
end
|
506
|
+
when :case
|
507
|
+
after = create_block
|
508
|
+
argument, body = node.children
|
509
|
+
argument_value = walk_node argument, value: true
|
510
|
+
|
511
|
+
while body && body.type == :when
|
512
|
+
when_opts, when_body, body = body.children
|
513
|
+
when_body_block = create_block
|
514
|
+
when_opts.each do |opt|
|
515
|
+
after_fail = create_block
|
516
|
+
condition_result = call_instruct(walk_node(opt, value: true), :===, argument_value, value: true)
|
517
|
+
cond_instruct(condition_result, when_body_block, after_fail)
|
518
|
+
start_block after_fail
|
519
|
+
end
|
520
|
+
all_fail = @current_block
|
521
|
+
|
522
|
+
start_block when_body_block
|
523
|
+
walk_body when_body, value: false
|
524
|
+
uncond_instruct after
|
525
|
+
|
526
|
+
start_block all_fail
|
527
|
+
end
|
528
|
+
if body && body.type == :else
|
529
|
+
walk_body body[1], value: false
|
530
|
+
end
|
531
|
+
uncond_instruct after
|
532
|
+
when :var_ref
|
533
|
+
nil
|
534
|
+
when :for
|
535
|
+
lhs, receiver, body = node.children
|
536
|
+
receiver_value = walk_node receiver, value: true
|
537
|
+
if Symbol === lhs[0]
|
538
|
+
# field or var_ref/const_ref
|
539
|
+
case lhs.type
|
540
|
+
when :field
|
541
|
+
# TODO(adgar): generate calls
|
542
|
+
else
|
543
|
+
# just get the value
|
544
|
+
arg_bindings = [lhs.binding]
|
545
|
+
call_method_with_block(receiver_value, :each, [], arg_bindings, body, value: false)
|
546
|
+
end
|
547
|
+
else
|
548
|
+
# TODO(adgar): multiple assign
|
549
|
+
end
|
550
|
+
when :string_embexpr
|
551
|
+
node[1].each { |elt| walk_node(elt, value: false) }
|
552
|
+
when :@CHAR, :@tstring_content, :@int, :@float, :@regexp_end, :symbol,
|
553
|
+
:@label, :symbol_literal, :defined
|
554
|
+
# do nothing
|
555
|
+
when :string_literal
|
556
|
+
content_nodes = node[1].children
|
557
|
+
content_nodes.each do |node|
|
558
|
+
walk_node node, value: false
|
559
|
+
end
|
560
|
+
when :xstring_literal
|
561
|
+
body = build_string_instruct(node[1])
|
562
|
+
call_instruct(self_register, :`, body, value: false)
|
563
|
+
when :regexp_literal
|
564
|
+
node[1].each { |part| walk_node node, value: false }
|
565
|
+
when :dyna_symbol
|
566
|
+
content_nodes = node[1].children
|
567
|
+
content_nodes.each { |node| walk_node node, value: false }
|
568
|
+
when :array
|
569
|
+
receiver = ClassRegistry['Array'].binding
|
570
|
+
generic_call_instruct(receiver, :[], node[1], false, value: false)
|
571
|
+
when :hash
|
572
|
+
if node[1]
|
573
|
+
walk_node node[1], value: false
|
574
|
+
else
|
575
|
+
const_instruct({}, value: false)
|
576
|
+
end
|
577
|
+
when :assoclist_from_args, :bare_assoc_hash
|
578
|
+
pairs = node[1]
|
579
|
+
key_value_paired = pairs.map {|a, b| [walk_node(a, value: true), walk_node(b, value: true)] }.flatten
|
580
|
+
receiver = ClassRegistry['Hash'].binding
|
581
|
+
call_instruct(receiver, :[], *key_value_paired, block: false, value: false, raise: false)
|
582
|
+
else
|
583
|
+
raise ArgumentError.new("Unknown AST node type #{node.type.inspect}")
|
584
|
+
end
|
585
|
+
end
|
586
|
+
end
|
587
|
+
|
588
|
+
# Walks the node with the expectation that the return value will be used.
|
589
|
+
def value_walk(node)
|
590
|
+
with_current_node(node) do
|
591
|
+
case node.type
|
592
|
+
when :opassign
|
593
|
+
lhs, op, rhs = node.children
|
594
|
+
op = op.expanded_identifier[0..-2].to_sym
|
595
|
+
if lhs.type == :field
|
596
|
+
receiver = walk_node lhs[1], value: true
|
597
|
+
method_name = lhs[3].expanded_identifier
|
598
|
+
# Receiver is ONLY EVALUATED ONCE
|
599
|
+
# (on ruby 1.9.2p136 (2010-12-25 revision 30365) [x86_64-darwin10.6.0])
|
600
|
+
current_val = call_instruct(receiver, method_name.to_sym, block: false, value: true)
|
601
|
+
if op == :"||"
|
602
|
+
result = create_temporary
|
603
|
+
true_block, false_block, after = create_blocks 3
|
604
|
+
cond_instruct(current_val, true_block, false_block)
|
605
|
+
|
606
|
+
start_block true_block
|
607
|
+
copy_instruct result, current_val
|
608
|
+
uncond_instruct after
|
609
|
+
|
610
|
+
start_block false_block
|
611
|
+
rhs_value = walk_node rhs, value: true
|
612
|
+
call_instruct(receiver, "#{method_name}=".to_sym, rhs_value, block: false, value: false)
|
613
|
+
copy_instruct result, rhs_value
|
614
|
+
uncond_instruct after
|
615
|
+
|
616
|
+
start_block after
|
617
|
+
result
|
618
|
+
elsif op == :"&&"
|
619
|
+
result = create_temporary
|
620
|
+
true_block, false_block, after = create_blocks 3
|
621
|
+
cond_instruct(current_val, true_block, false_block)
|
622
|
+
|
623
|
+
start_block true_block
|
624
|
+
rhs_value = walk_node rhs, value: true
|
625
|
+
call_instruct(receiver, "#{method_name}=".to_sym, rhs_value, block: false, value: false)
|
626
|
+
copy_instruct result, rhs_value
|
627
|
+
uncond_instruct after
|
628
|
+
|
629
|
+
start_block false_block
|
630
|
+
copy_instruct result, current_val
|
631
|
+
uncond_instruct after
|
632
|
+
|
633
|
+
start_block after
|
634
|
+
result
|
635
|
+
else
|
636
|
+
rhs_value = walk_node rhs, value: true
|
637
|
+
temp_result = call_instruct(current_val, op, rhs_value, block: false, value: true)
|
638
|
+
call_instruct(receiver, "#{method_name}=".to_sym, temp_result, block: false, value: false)
|
639
|
+
temp_result
|
640
|
+
end
|
641
|
+
# TODO(adgar): aref_field
|
642
|
+
else
|
643
|
+
result = binary_instruct(lhs, op, rhs, value: true)
|
644
|
+
single_assign_instruct(lhs, result)
|
645
|
+
result
|
646
|
+
end
|
647
|
+
when :var_field
|
648
|
+
variable_instruct(node)
|
649
|
+
when :var_ref
|
650
|
+
if node[1].type == :@const
|
651
|
+
const_lookup(node[1].expanded_identifier)
|
652
|
+
elsif node[1].type == :@ident || node[1].expanded_identifier == 'self'
|
653
|
+
variable_instruct(node)
|
654
|
+
elsif node[1].type == :@kw
|
655
|
+
const_instruct(node.constant_value)
|
656
|
+
elsif node[1].type == :@ivar
|
657
|
+
call_instruct(current_self, :instance_variable_get,
|
658
|
+
const_instruct(node.expanded_identifier), value: true, ignore_privacy: true)
|
659
|
+
elsif node[1].type == :@gvar
|
660
|
+
call_instruct(ClassRegistry['Laser#Magic'].binding, :get_global,
|
661
|
+
const_instruct(node.expanded_identifier), raise: false, value: true)
|
662
|
+
end
|
663
|
+
when :top_const_ref
|
664
|
+
const = node[1]
|
665
|
+
ident = const_instruct(const.expanded_identifier)
|
666
|
+
call_instruct(ClassRegistry['Object'].binding,
|
667
|
+
:const_get, ident, value: true)
|
668
|
+
when :for
|
669
|
+
lhs, receiver, body = node.children
|
670
|
+
receiver_value = walk_node receiver, value: true
|
671
|
+
if Symbol === lhs[0]
|
672
|
+
# field or var_ref/const_ref
|
673
|
+
case lhs.type
|
674
|
+
when :field
|
675
|
+
# call
|
676
|
+
else
|
677
|
+
# just get the value
|
678
|
+
arg_bindings = [lhs.binding]
|
679
|
+
call_method_with_block(receiver_value, :each, [], arg_bindings, body, value: true)
|
680
|
+
end
|
681
|
+
# TODO(adgar): aref_field
|
682
|
+
else
|
683
|
+
# TODO(adgar): multiple assign
|
684
|
+
end
|
685
|
+
when :case
|
686
|
+
after = create_block
|
687
|
+
result = create_temporary
|
688
|
+
argument, body = node.children
|
689
|
+
argument_value = walk_node argument, value: true
|
690
|
+
|
691
|
+
while body && body.type == :when
|
692
|
+
when_opts, when_body, body = body.children
|
693
|
+
when_body_block = create_block
|
694
|
+
when_opts.each do |opt|
|
695
|
+
after_fail = create_block
|
696
|
+
condition_result = call_instruct(walk_node(opt, value: true), :===, argument_value, value: true)
|
697
|
+
cond_instruct(condition_result, when_body_block, after_fail)
|
698
|
+
start_block after_fail
|
699
|
+
end
|
700
|
+
all_fail = @current_block
|
701
|
+
|
702
|
+
start_block when_body_block
|
703
|
+
when_body_result = walk_body when_body, value: true
|
704
|
+
copy_instruct(result, when_body_result)
|
705
|
+
uncond_instruct after
|
706
|
+
|
707
|
+
start_block all_fail
|
708
|
+
end
|
709
|
+
if body.nil?
|
710
|
+
copy_instruct(result, nil)
|
711
|
+
uncond_instruct after
|
712
|
+
elsif body.type == :else
|
713
|
+
else_body_result = walk_body body[1], value: true
|
714
|
+
copy_instruct(result, else_body_result)
|
715
|
+
uncond_instruct after
|
716
|
+
end
|
717
|
+
|
718
|
+
start_block after
|
719
|
+
result
|
720
|
+
when :@CHAR, :@tstring_content, :@int, :@float, :@regexp_end, :symbol,
|
721
|
+
:@label, :symbol_literal
|
722
|
+
const_instruct(node.constant_value)
|
723
|
+
when :string_literal
|
724
|
+
content_nodes = node[1].children
|
725
|
+
build_string_instruct(content_nodes)
|
726
|
+
when :string_embexpr
|
727
|
+
final = walk_body node[1], value: true
|
728
|
+
call_instruct(final, :to_s, value: true)
|
729
|
+
when :xstring_literal
|
730
|
+
body = build_string_instruct(node[1])
|
731
|
+
call_instruct(self_register, :`, body, value: true)
|
732
|
+
when :regexp_literal
|
733
|
+
body = build_string_instruct(node[1])
|
734
|
+
options = const_instruct(node[2].constant_value)
|
735
|
+
receiver = ClassRegistry['Regexp'].binding
|
736
|
+
call_instruct(receiver, :new, body, options, value: true)
|
737
|
+
when :dyna_symbol
|
738
|
+
content_nodes = node[1].children
|
739
|
+
string_version = build_string_instruct(content_nodes)
|
740
|
+
call_instruct(string_version, :to_sym, value: true, raise: false)
|
741
|
+
when :array
|
742
|
+
receiver = ClassRegistry['Array'].binding
|
743
|
+
generic_call_instruct(receiver, :[], node[1], false, value: true)
|
744
|
+
when :hash
|
745
|
+
if node[1]
|
746
|
+
walk_node node[1], value: true
|
747
|
+
else
|
748
|
+
const_instruct({})
|
749
|
+
end
|
750
|
+
when :assoclist_from_args, :bare_assoc_hash
|
751
|
+
pairs = node[1].map { |_, k, v| [k, v] }
|
752
|
+
key_value_paired = pairs.map {|a, b| [walk_node(a, value: true), walk_node(b, value: true)] }.flatten
|
753
|
+
receiver = ClassRegistry['Hash'].binding
|
754
|
+
call_instruct(receiver, :[], *key_value_paired, block: false, value: true)
|
755
|
+
when :defined
|
756
|
+
defined_op_instruct(node[1])
|
757
|
+
else
|
758
|
+
raise ArgumentError.new("Unknown AST node type #{node.type.inspect}")
|
759
|
+
end
|
760
|
+
end
|
761
|
+
end
|
762
|
+
|
763
|
+
private
|
764
|
+
|
765
|
+
def initialize_graph
|
766
|
+
@graph = ControlFlowGraph.new(@formals)
|
767
|
+
@graph.root = @sexp
|
768
|
+
@block_counter = 0
|
769
|
+
@enter = @graph.enter
|
770
|
+
@exit = @graph.exit
|
771
|
+
@temporary_counter = 0
|
772
|
+
@current_break = @current_next = @current_redo = @current_return = @current_rescue = nil
|
773
|
+
start_block @enter
|
774
|
+
end
|
775
|
+
|
776
|
+
# Redirects break, next, redo, and return to the given Sexp for each
|
777
|
+
# target to redirect.
|
778
|
+
def with_jumps_redirected(targets={})
|
779
|
+
new_targets = targets.merge(targets) do |key, redirect|
|
780
|
+
current = send("current_#{key}")
|
781
|
+
next nil unless current
|
782
|
+
build_block_with_jump(current) do
|
783
|
+
walk_body redirect, value: false
|
784
|
+
end
|
785
|
+
end.delete_if { |k, v| v.nil? }
|
786
|
+
with_jump_targets(new_targets) do
|
787
|
+
yield
|
788
|
+
end
|
789
|
+
end
|
790
|
+
|
791
|
+
# Yields with jump targets specified. Since a number of jump targets
|
792
|
+
# require temporary specification in a stack-like fashion during CFG construction,
|
793
|
+
# I use the call stack to simulate the explicit one suggested by Morgan.
|
794
|
+
def with_jump_targets(targets={})
|
795
|
+
old_break, old_next, old_redo, old_return, old_rescue, old_yield_fail =
|
796
|
+
@current_break, @current_next, @current_redo, @current_return, @current_rescue, @current_yield_fail
|
797
|
+
@current_break = targets[:break] if targets.has_key?(:break)
|
798
|
+
@current_next = targets[:next] if targets.has_key?(:next)
|
799
|
+
@current_redo = targets[:redo] if targets.has_key?(:redo)
|
800
|
+
@current_return = targets[:return] if targets.has_key?(:return)
|
801
|
+
@current_rescue = targets[:rescue] if targets.has_key?(:rescue)
|
802
|
+
@current_yield_fail = targets[:yield_fail] if targets.has_key?(:yield_fail)
|
803
|
+
yield
|
804
|
+
ensure
|
805
|
+
@current_break, @current_next, @current_redo, @current_return, @current_rescue, @current_yield_fail =
|
806
|
+
old_break, old_next, old_redo, old_return, old_rescue, old_yield_fail
|
807
|
+
end
|
808
|
+
|
809
|
+
# Walks over a series of statements, ignoring the return value of
|
810
|
+
# everything except the last statement. Stores the result of the
|
811
|
+
# last statement in the result parameter.
|
812
|
+
def walk_body(body, opts={})
|
813
|
+
opts = {value: true}.merge(opts)
|
814
|
+
if opts[:value]
|
815
|
+
body[0..-2].each { |elt| walk_node(elt, value: false) }
|
816
|
+
if body.any?
|
817
|
+
walk_node(body.last, value: true)
|
818
|
+
else
|
819
|
+
const_instruct(nil)
|
820
|
+
end
|
821
|
+
else
|
822
|
+
body.each { |node| walk_node node, value: false }
|
823
|
+
end
|
824
|
+
end
|
825
|
+
|
826
|
+
def raise_instruct(arg, opts)
|
827
|
+
target = opts[:target]
|
828
|
+
call_instruct(ClassRegistry['Laser#Magic'].binding, :push_exception, arg, raise: false)
|
829
|
+
copy_instruct(@exception_register, arg)
|
830
|
+
uncond_instruct target, flags: RGL::ControlFlowGraph::EDGE_ABNORMAL
|
831
|
+
start_block target
|
832
|
+
end
|
833
|
+
|
834
|
+
def raise_instance_of_instruct(klass, *args)
|
835
|
+
opts = {target: current_rescue}
|
836
|
+
opts.merge!(args.pop) if Hash === args.last
|
837
|
+
instance = call_instruct(klass, :new, *args, value: true, raise: false)
|
838
|
+
raise_instruct instance, opts
|
839
|
+
end
|
840
|
+
|
841
|
+
# Yields with an explicit block being wrapped around the execution of the
|
842
|
+
# user's block. The basic block object created is provided as a parameter to the
|
843
|
+
# caller's operations which have the possibility of invoking the block.
|
844
|
+
def call_with_explicit_block(block_arg_bindings, block_sexp)
|
845
|
+
body_block = create_block
|
846
|
+
body_value, body_proc = create_block_temporary block_arg_bindings, block_sexp, @current_block
|
847
|
+
call_instruct(body_value, :lexical_self=, self_instruct, raise: false, value: false)
|
848
|
+
@graph.add_edge(@current_block, body_block,
|
849
|
+
ControlFlowGraph::EDGE_BLOCK_TAKEN | ControlFlowGraph::EDGE_ABNORMAL)
|
850
|
+
result = yield(body_value)
|
851
|
+
after = @current_block # new block caused by method call
|
852
|
+
block_exit = build_block_exit_block body_block, after
|
853
|
+
body_proc.exit_block = block_exit
|
854
|
+
walk_block_body block_arg_bindings, body_block, block_exit, block_sexp, after
|
855
|
+
start_block after
|
856
|
+
result
|
857
|
+
end
|
858
|
+
|
859
|
+
def build_block_exit_block(body_block, after)
|
860
|
+
build_block_with_jump(nil, body_block.name + '-Exit') do
|
861
|
+
@graph.add_edge(@current_block, body_block,
|
862
|
+
RGL::ControlFlowGraph::EDGE_ABNORMAL | RGL::ControlFlowGraph::EDGE_BLOCK_TAKEN)
|
863
|
+
@graph.add_edge(@current_block, after)
|
864
|
+
add_potential_raise_edge(false)
|
865
|
+
end
|
866
|
+
end
|
867
|
+
|
868
|
+
def call_zsuper_with_block(node, block_arg_bindings, block_sexp, opts={})
|
869
|
+
opts = {value: true, raise: true}.merge(opts)
|
870
|
+
call_with_explicit_block(block_arg_bindings, block_sexp) do |body_value|
|
871
|
+
invoke_super_with_block *compute_zsuper_arguments, body_value, opts
|
872
|
+
end
|
873
|
+
end
|
874
|
+
|
875
|
+
def call_method_with_block(receiver, method, args, block_arg_bindings, block_sexp, opts={})
|
876
|
+
opts = {value: true, raise: true}.merge(opts)
|
877
|
+
call_with_explicit_block(block_arg_bindings, block_sexp) do |body_value|
|
878
|
+
generic_call_instruct receiver, method, args, body_value, opts
|
879
|
+
end
|
880
|
+
end
|
881
|
+
|
882
|
+
def invoke_super_with_block(args, is_vararg, body_block, opts={})
|
883
|
+
opts = {value: true, raise: true}.merge(opts)
|
884
|
+
# TODO(adgar): blocks in args & style
|
885
|
+
if is_vararg
|
886
|
+
then super_vararg_instruct(args, {block: body_block}.merge(opts))
|
887
|
+
else super_instruct(*args, {block: body_block}.merge(opts))
|
888
|
+
end
|
889
|
+
end
|
890
|
+
|
891
|
+
# Walks the block with it's new next/etc. boundaries set based on the block's
|
892
|
+
# scope
|
893
|
+
def walk_block_body(block_arg_bindings, body_block, block_exit, body, after)
|
894
|
+
block_exception = create_temporary(body_block.name + '_final_exception')
|
895
|
+
rescue_block = build_block_with_jump(nil, body_block.name + '-Failure') do
|
896
|
+
copy_instruct(block_exception, @exception_register)
|
897
|
+
add_instruction(:raise, block_exception)
|
898
|
+
uncond_instruct block_exit, flags: RGL::ControlFlowGraph::EDGE_ABNORMAL, jump_instruct: false
|
899
|
+
end
|
900
|
+
with_jump_targets(rescue: rescue_block) do
|
901
|
+
with_current_basic_block(body_block) do
|
902
|
+
body_result = nil
|
903
|
+
with_self(query_self) do
|
904
|
+
build_formal_args(block_arg_bindings)
|
905
|
+
body_result = walk_body body, value: true
|
906
|
+
end
|
907
|
+
add_instruction(:return, body_result)
|
908
|
+
uncond_instruct(block_exit, jump_instruct: false)
|
909
|
+
end
|
910
|
+
end
|
911
|
+
end
|
912
|
+
|
913
|
+
# Terminates the current block with a jump to the target block.
|
914
|
+
def uncond_instruct(target, opts = {})
|
915
|
+
opts = {jump_instruct: true, flags: RGL::ControlFlowGraph::EDGE_NORMAL}.merge(opts)
|
916
|
+
add_instruction(:jump, target.name) if opts[:jump_instruct]
|
917
|
+
@graph.add_edge(@current_block, target, opts[:flags])
|
918
|
+
start_block target
|
919
|
+
end
|
920
|
+
|
921
|
+
# Creates an unconditional branch from the current block, based on the given
|
922
|
+
# value, to either the true block or the false block.
|
923
|
+
def cond_instruct(val, true_block, false_block, opts = {branch_instruct: true})
|
924
|
+
if opts[:branch_instruct]
|
925
|
+
add_instruction(:branch, val, true_block.name, false_block.name)
|
926
|
+
end
|
927
|
+
@graph.add_edge(@current_block, true_block)
|
928
|
+
@graph.add_edge(@current_block, false_block)
|
929
|
+
end
|
930
|
+
|
931
|
+
# Performs a no-arg return.
|
932
|
+
def return0_instruct
|
933
|
+
return_uncond_jump_instruct(nil)
|
934
|
+
end
|
935
|
+
|
936
|
+
def return_instruct(node)
|
937
|
+
result = evaluate_args_into_array node[1]
|
938
|
+
return_uncond_jump_instruct result
|
939
|
+
end
|
940
|
+
|
941
|
+
def return_uncond_jump_instruct(result)
|
942
|
+
copy_instruct(@return_register, result)
|
943
|
+
uncond_instruct @current_return
|
944
|
+
start_block create_block
|
945
|
+
result
|
946
|
+
end
|
947
|
+
|
948
|
+
# Performs a yield of the given value, capturing the return
|
949
|
+
# value.
|
950
|
+
def yield_instruct(arg_node, opts={})
|
951
|
+
opts = {raise: true, value: true}.merge(opts)
|
952
|
+
# this is: if @block_arg; @block_arg.call(args)
|
953
|
+
# else raise LocalJumpError.new(...)
|
954
|
+
if_block, no_block = create_blocks 2
|
955
|
+
cond_instruct(@block_arg, if_block, no_block)
|
956
|
+
|
957
|
+
start_block no_block
|
958
|
+
message = const_instruct('no block given (yield)')
|
959
|
+
file_name = const_instruct(@current_node.file_name)
|
960
|
+
line_number = const_instruct(@current_node.line_number || 0)
|
961
|
+
raise_instance_of_instruct(
|
962
|
+
ClassRegistry['LocalJumpError'].binding, message, file_name, line_number,
|
963
|
+
target: current_yield_fail)
|
964
|
+
|
965
|
+
start_block if_block
|
966
|
+
if arg_node.nil?
|
967
|
+
result = call_instruct(@block_arg, :call, opts)
|
968
|
+
else
|
969
|
+
arg_node = arg_node[1] if arg_node[0] == :paren
|
970
|
+
result = generic_call_instruct(@block_arg, :call, arg_node[1], nil, opts)
|
971
|
+
end
|
972
|
+
result
|
973
|
+
end
|
974
|
+
|
975
|
+
def yield_instruct_with_arg(node, opts={})
|
976
|
+
opts = {raise: true, value: true}.merge(opts)
|
977
|
+
result = evaluate_args_into_array node[1]
|
978
|
+
yield_instruct result, opts
|
979
|
+
end
|
980
|
+
|
981
|
+
# Takes an argument node and evaluates it into an array. used by
|
982
|
+
# return and yield, as they always pass along 1 argument.
|
983
|
+
def evaluate_args_into_array(args)
|
984
|
+
if args[0] == :args_add_star
|
985
|
+
# if there's a splat, always return an actual array object of all the arguments.
|
986
|
+
compute_varargs(args)
|
987
|
+
elsif args[0] == :paren
|
988
|
+
evaluate_args_into_array(args[1])
|
989
|
+
elsif args[0] == :args_add_block
|
990
|
+
evaluate_args_into_array(args[1])
|
991
|
+
elsif args.size > 1
|
992
|
+
# if there's more than 1 argument, but no splats, then we just pack
|
993
|
+
# them into an array and return that array.
|
994
|
+
arg_temps = args.map { |arg| walk_node arg, value: true }
|
995
|
+
result = call_instruct(ClassRegistry['Array'].binding, :[], *arg_temps,
|
996
|
+
value: true, raise: false)
|
997
|
+
else
|
998
|
+
# Otherwise, just 1 simple argument: return it.
|
999
|
+
walk_node args[0], value: true
|
1000
|
+
end
|
1001
|
+
end
|
1002
|
+
|
1003
|
+
attr_reader :current_break, :current_next, :current_redo
|
1004
|
+
attr_reader :current_return, :current_rescue, :current_yield_fail
|
1005
|
+
|
1006
|
+
# TODO(adgar): ARGUMENTS
|
1007
|
+
def break_instruct(args)
|
1008
|
+
uncond_instruct @current_break
|
1009
|
+
start_block create_block
|
1010
|
+
end
|
1011
|
+
|
1012
|
+
# TODO(adgar): ARGUMENTS
|
1013
|
+
def next_instruct(args)
|
1014
|
+
uncond_instruct @current_next
|
1015
|
+
start_block create_block
|
1016
|
+
end
|
1017
|
+
|
1018
|
+
def redo_instruct
|
1019
|
+
add_fake_edge @current_block, @graph.exit
|
1020
|
+
uncond_instruct @current_redo
|
1021
|
+
start_block create_block
|
1022
|
+
add_fake_edge @graph.enter, @current_block
|
1023
|
+
end
|
1024
|
+
|
1025
|
+
# Walks a body statement.
|
1026
|
+
def bodystmt_walk(node)
|
1027
|
+
# Pretty fucking compact encapsulation of the :bodystmt block. Damn, mother
|
1028
|
+
# fucker.
|
1029
|
+
result = create_temporary
|
1030
|
+
|
1031
|
+
body, rescue_body, else_body, ensure_body = node.children
|
1032
|
+
body_block, after = create_blocks 2
|
1033
|
+
uncond_instruct body_block
|
1034
|
+
|
1035
|
+
if ensure_body
|
1036
|
+
ensure_block = create_block
|
1037
|
+
|
1038
|
+
# Generate the body with redirects to the ensure block, so no jumps get away without
|
1039
|
+
# running the ensure block
|
1040
|
+
with_jumps_redirected(break: ensure_body[1], redo: ensure_body[1], next: ensure_body[1],
|
1041
|
+
return: ensure_body[1], rescue: ensure_body[1],
|
1042
|
+
yield_fail: ensure_body[1]) do
|
1043
|
+
rescue_target, yield_fail_target =
|
1044
|
+
build_rescue_target(node, result, rescue_body, ensure_block,
|
1045
|
+
current_rescue, current_yield_fail)
|
1046
|
+
walk_body_with_rescue_target(result, body, body_block, rescue_target, yield_fail_target)
|
1047
|
+
end
|
1048
|
+
uncond_instruct ensure_block
|
1049
|
+
walk_body(ensure_body[1], value: false)
|
1050
|
+
uncond_instruct after
|
1051
|
+
else
|
1052
|
+
# Generate the body with redirects to the ensure block, so no jumps get away without
|
1053
|
+
# running the ensure block
|
1054
|
+
rescue_target, yield_fail_target =
|
1055
|
+
build_rescue_target(node, result, rescue_body, after,
|
1056
|
+
current_rescue, current_yield_fail)
|
1057
|
+
walk_body_with_rescue_target(result, body, body_block, rescue_target, yield_fail_target)
|
1058
|
+
uncond_instruct after
|
1059
|
+
end
|
1060
|
+
result
|
1061
|
+
end
|
1062
|
+
|
1063
|
+
# Builds the rescue block(s) for the given rescue_body, if there is one,
|
1064
|
+
# and returns the block to jump to when an exception is raised.
|
1065
|
+
def build_rescue_target(node, result, rescue_body, destination, rescue_fail, yield_fail_target)
|
1066
|
+
if rescue_body
|
1067
|
+
then rescue_instruct(node, result, rescue_body, destination, rescue_fail, yield_fail_target)
|
1068
|
+
else [rescue_fail, yield_fail_target]
|
1069
|
+
end
|
1070
|
+
end
|
1071
|
+
|
1072
|
+
# Walks the body of code with its result copied and its rescue target set.
|
1073
|
+
def walk_body_with_rescue_target(result, body, body_block, rescue_target, yield_fail_target)
|
1074
|
+
with_jump_targets(rescue: rescue_target, yield_fail: yield_fail_target) do
|
1075
|
+
start_block body_block
|
1076
|
+
body_result = walk_body body, value: true
|
1077
|
+
copy_instruct(result, body_result)
|
1078
|
+
end
|
1079
|
+
end
|
1080
|
+
|
1081
|
+
def rescue_instruct(node, enclosing_body_result, rescue_body, ensure_block, rescue_fail, yield_fail)
|
1082
|
+
rescue_target, yield_fail_target = create_blocks 2
|
1083
|
+
catchers = [rescue_target, yield_fail_target]
|
1084
|
+
start_block rescue_target
|
1085
|
+
while rescue_body
|
1086
|
+
rhs, exception_name, handler_body, rescue_body = rescue_body.children
|
1087
|
+
rhs ||= Sexp.new([[:var_ref, [:@const, 'StandardError', [0, 0]]]])
|
1088
|
+
handler_block = create_block
|
1089
|
+
|
1090
|
+
# for everything in rescue_body[1]
|
1091
|
+
# check if === $!, if so, go to handler_block, if not, keep checking.
|
1092
|
+
catchers.map! do |catcher|
|
1093
|
+
with_current_basic_block(catcher) do
|
1094
|
+
failure_block = nil
|
1095
|
+
foreach_on_rhs(rhs) do |temp|
|
1096
|
+
result = call_instruct(temp, :===, @exception_register, value: true)
|
1097
|
+
failure_block = create_block
|
1098
|
+
cond_instruct(result, handler_block, failure_block)
|
1099
|
+
start_block failure_block
|
1100
|
+
end
|
1101
|
+
@current_block
|
1102
|
+
end
|
1103
|
+
end
|
1104
|
+
|
1105
|
+
# Build the handler block.
|
1106
|
+
start_block handler_block
|
1107
|
+
# Assign to $! if there is a requested name for the exception
|
1108
|
+
if exception_name
|
1109
|
+
var_name = exception_name.expanded_identifier
|
1110
|
+
copy_instruct(current_scope.lookup_or_create_local(var_name), @exception_register)
|
1111
|
+
end
|
1112
|
+
body_result = walk_body handler_body, value: true
|
1113
|
+
copy_instruct(enclosing_body_result, body_result)
|
1114
|
+
call_instruct(ClassRegistry['Laser#Magic'].binding, :pop_exception, raise: false)
|
1115
|
+
uncond_instruct ensure_block
|
1116
|
+
end
|
1117
|
+
# All rescues failed.
|
1118
|
+
else_body = node[3]
|
1119
|
+
rescue_else_instruct(else_body, catchers, [rescue_fail, yield_fail]) # else_body
|
1120
|
+
[rescue_target, yield_fail_target]
|
1121
|
+
end
|
1122
|
+
|
1123
|
+
# Builds a rescue-else body.
|
1124
|
+
def rescue_else_instruct(else_body, catchers, fail_targets)
|
1125
|
+
catchers.zip(fail_targets).each do |catcher, fail_target|
|
1126
|
+
with_current_basic_block(catcher) do
|
1127
|
+
if else_body
|
1128
|
+
else_block = create_block
|
1129
|
+
uncond_instruct else_block
|
1130
|
+
start_block else_block
|
1131
|
+
walk_body else_body[1], value: false
|
1132
|
+
end
|
1133
|
+
uncond_instruct fail_target, flags: RGL::ControlFlowGraph::EDGE_ABNORMAL
|
1134
|
+
end
|
1135
|
+
end
|
1136
|
+
end
|
1137
|
+
|
1138
|
+
# Generates the code for the 'expr rescue expr' construct, which
|
1139
|
+
# catches StandardError exceptions. Offers a nice optimization opportunity
|
1140
|
+
# for yield-failure.
|
1141
|
+
def rescue_mod_instruct(guarded_expr, rescue_expr, opts={value: true})
|
1142
|
+
# first off, yield failure ALWAYS caught, so that's optimized
|
1143
|
+
# no ensure, else, etc
|
1144
|
+
# StandardError is the caught exception
|
1145
|
+
result_temporary = create_temporary if opts[:value]
|
1146
|
+
rescue_check_block, after = create_blocks 2
|
1147
|
+
is_caught_block = build_block_with_jump(after) do
|
1148
|
+
caught_result = walk_node(rescue_expr, opts)
|
1149
|
+
copy_instruct(result_temporary, caught_result) if opts[:value]
|
1150
|
+
end
|
1151
|
+
with_current_basic_block(rescue_check_block) do
|
1152
|
+
caught = call_instruct(ClassRegistry['StandardError'].binding, :===,
|
1153
|
+
@exception_register, value: true, raise: false)
|
1154
|
+
cond_instruct(caught, is_caught_block, current_rescue)
|
1155
|
+
end
|
1156
|
+
with_jump_targets(rescue: rescue_check_block, yield_fail: is_caught_block) do
|
1157
|
+
normal_result = walk_node(guarded_expr, opts)
|
1158
|
+
copy_instruct(result_temporary, normal_result) if opts[:value]
|
1159
|
+
uncond_instruct(after)
|
1160
|
+
end
|
1161
|
+
result_temporary
|
1162
|
+
end
|
1163
|
+
|
1164
|
+
def class_instruct(class_name, superclass, body, opts={value: true})
|
1165
|
+
# first: calculate receiver to perform a check if
|
1166
|
+
# the class already exists
|
1167
|
+
the_class_holder = create_temporary
|
1168
|
+
case class_name.type
|
1169
|
+
when :const_ref
|
1170
|
+
receiver_val = current_namespace
|
1171
|
+
name_as_string = class_name.expanded_identifier
|
1172
|
+
when :const_path_ref
|
1173
|
+
receiver_val = walk_node(class_name[1], value: true)
|
1174
|
+
name_as_string = class_name[2].expanded_identifier
|
1175
|
+
when :top_const_ref
|
1176
|
+
receiver_val = ClassRegistry['Object'].binding
|
1177
|
+
name_as_string = class_name[1].expanded_identifier
|
1178
|
+
end
|
1179
|
+
actual_name = const_instruct(name_as_string)
|
1180
|
+
|
1181
|
+
if superclass
|
1182
|
+
superclass_val = walk_node(superclass, value: true)
|
1183
|
+
need_confirm_superclass = true
|
1184
|
+
else
|
1185
|
+
superclass_val = lookup_or_create_temporary(:var, '::Object')
|
1186
|
+
copy_instruct(superclass_val, ClassRegistry['Object'])
|
1187
|
+
need_confirm_superclass = false
|
1188
|
+
end
|
1189
|
+
|
1190
|
+
already_exists = call_instruct(receiver_val, :const_defined?, actual_name, const_instruct(false), value: true, raise: false)
|
1191
|
+
if_exists_block, if_noexists_block, after_exists_check = create_blocks 3
|
1192
|
+
cond_instruct(already_exists, if_exists_block, if_noexists_block)
|
1193
|
+
|
1194
|
+
############### LOOKING UP AND VERIFYING CLASS BRANCH ###########
|
1195
|
+
|
1196
|
+
start_block if_exists_block
|
1197
|
+
the_class = call_instruct(receiver_val, :const_get, actual_name, const_instruct(false), value: true, raise: false)
|
1198
|
+
copy_instruct(the_class_holder, the_class)
|
1199
|
+
# check if it's actually a module
|
1200
|
+
is_module_block, after_conflict_check = create_blocks 2
|
1201
|
+
is_class_cond_val = call_instruct(ClassRegistry['Class'].binding, :===, the_class, value: true, raise: false)
|
1202
|
+
cond_instruct(is_class_cond_val, after_conflict_check, is_module_block)
|
1203
|
+
|
1204
|
+
# Unconditionally raise if it is not a class! The error is a TypeError
|
1205
|
+
start_block is_module_block
|
1206
|
+
raise_instance_of_instruct(ClassRegistry['LaserReopenedModuleAsClassError'].binding,
|
1207
|
+
const_instruct("#{name_as_string} is not a class"))
|
1208
|
+
|
1209
|
+
start_block after_conflict_check
|
1210
|
+
# Now, compare superclasses if provided superclass is not Object
|
1211
|
+
if need_confirm_superclass
|
1212
|
+
should_not_confirm_superclass = call_instruct(superclass_val, :equal?,
|
1213
|
+
ClassRegistry['Object'].binding, value: true, raise: false)
|
1214
|
+
validate_superclass_mismatch_block = create_block
|
1215
|
+
cond_instruct(should_not_confirm_superclass, after_exists_check, validate_superclass_mismatch_block)
|
1216
|
+
|
1217
|
+
start_block validate_superclass_mismatch_block
|
1218
|
+
old_superclass_val = call_instruct(the_class, :superclass, value: true, raise: false)
|
1219
|
+
superclass_is_equal_cond = call_instruct(old_superclass_val, :equal?, superclass_val, value: true, raise: false)
|
1220
|
+
superclass_conflict_block = create_block
|
1221
|
+
cond_instruct(superclass_is_equal_cond, after_exists_check, superclass_conflict_block)
|
1222
|
+
|
1223
|
+
start_block superclass_conflict_block
|
1224
|
+
raise_instance_of_instruct(ClassRegistry['LaserSuperclassMismatchError'].binding,
|
1225
|
+
const_instruct("superclass mismatch for class #{name_as_string}"))
|
1226
|
+
else
|
1227
|
+
uncond_instruct(after_exists_check)
|
1228
|
+
end
|
1229
|
+
|
1230
|
+
############### CREATING CLASS BRANCH ###############################
|
1231
|
+
|
1232
|
+
start_block if_noexists_block
|
1233
|
+
# only confirm superclass if it's not defaulting to Object!
|
1234
|
+
if need_confirm_superclass
|
1235
|
+
is_not_class_block, after_is_class_check = create_blocks 2
|
1236
|
+
is_class_cond_val = call_instruct(ClassRegistry['Class'].binding, :===, superclass_val, value: true, raise: false)
|
1237
|
+
cond_instruct(is_class_cond_val, after_is_class_check, is_not_class_block)
|
1238
|
+
|
1239
|
+
start_block is_not_class_block
|
1240
|
+
raise_instance_of_instruct ClassRegistry['TypeError'].binding
|
1241
|
+
|
1242
|
+
start_block after_is_class_check
|
1243
|
+
end
|
1244
|
+
# create the class and assign
|
1245
|
+
the_class = call_instruct(ClassRegistry['Class'].binding, :new, superclass_val, value: true, raise: false)
|
1246
|
+
call_instruct(receiver_val, :const_set, actual_name, the_class, value: false, raise: false)
|
1247
|
+
copy_instruct(the_class_holder, the_class)
|
1248
|
+
uncond_instruct after_exists_check
|
1249
|
+
|
1250
|
+
start_block after_exists_check
|
1251
|
+
call_instruct(Bootstrap::VISIBILITY_STACK, :push, const_instruct(:public), raise: false, value: false)
|
1252
|
+
# use this namespace!
|
1253
|
+
module_eval_instruct(the_class_holder, body, opts)
|
1254
|
+
call_instruct(Bootstrap::VISIBILITY_STACK, :pop, raise: false, value: false)
|
1255
|
+
end
|
1256
|
+
|
1257
|
+
def module_instruct(module_name, body, opts={value: true})
|
1258
|
+
# first: calculate receiver to perform a check if
|
1259
|
+
# the class already exists
|
1260
|
+
the_module_holder = create_temporary
|
1261
|
+
case module_name.type
|
1262
|
+
when :const_ref
|
1263
|
+
receiver_val = current_namespace
|
1264
|
+
name_as_string = module_name.expanded_identifier
|
1265
|
+
when :const_path_ref
|
1266
|
+
receiver_val = walk_node(module_name[1], value: true)
|
1267
|
+
name_as_string = module_name[2].expanded_identifier
|
1268
|
+
when :top_const_ref
|
1269
|
+
receiver_val = ClassRegistry['Object'].binding
|
1270
|
+
name_as_string = module_name[1].expanded_identifier
|
1271
|
+
end
|
1272
|
+
actual_name = const_instruct(name_as_string)
|
1273
|
+
|
1274
|
+
already_exists = call_instruct(receiver_val, :const_defined?, actual_name, const_instruct(false), value: true, raise: false)
|
1275
|
+
if_exists_block, if_noexists_block, after_exists_check = create_blocks 3
|
1276
|
+
cond_instruct(already_exists, if_exists_block, if_noexists_block)
|
1277
|
+
|
1278
|
+
start_block if_exists_block
|
1279
|
+
the_module = call_instruct(receiver_val, :const_get, actual_name, const_instruct(false), value: true, raise: false)
|
1280
|
+
copy_instruct(the_module_holder, the_module)
|
1281
|
+
# check if it's actually a class
|
1282
|
+
is_class_block, after_conflict_check = create_blocks 2
|
1283
|
+
is_class_cond_val = call_instruct(ClassRegistry['Class'].binding, :===, the_module, value: true, raise: false)
|
1284
|
+
cond_instruct(is_class_cond_val, is_class_block, after_exists_check)
|
1285
|
+
|
1286
|
+
# Unconditionally raise if it is a class! The error is a TypeError
|
1287
|
+
start_block is_class_block
|
1288
|
+
raise_instance_of_instruct(ClassRegistry['LaserReopenedClassAsModuleError'].binding,
|
1289
|
+
const_instruct("#{name_as_string} is not a module"))
|
1290
|
+
|
1291
|
+
start_block if_noexists_block
|
1292
|
+
# create the class and assign
|
1293
|
+
the_module = call_instruct(ClassRegistry['Module'].binding, :new, value: true, raise: false)
|
1294
|
+
call_instruct(receiver_val, :const_set, actual_name, the_module, value: false, raise: false)
|
1295
|
+
copy_instruct(the_module_holder, the_module)
|
1296
|
+
uncond_instruct after_exists_check
|
1297
|
+
|
1298
|
+
start_block after_exists_check
|
1299
|
+
|
1300
|
+
call_instruct(Bootstrap::VISIBILITY_STACK, :push, const_instruct(:public), raise: false, value: false)
|
1301
|
+
module_eval_instruct(the_module_holder, body, opts)
|
1302
|
+
call_instruct(Bootstrap::VISIBILITY_STACK, :pop, raise: false, value: false)
|
1303
|
+
end
|
1304
|
+
|
1305
|
+
def singleton_class_instruct(receiver, body, opts={value: false})
|
1306
|
+
receiver_val = walk_node receiver, value: true
|
1307
|
+
|
1308
|
+
maybe_symbol, no_singleton, has_singleton = create_blocks 3
|
1309
|
+
cond_result = call_instruct(ClassRegistry['Fixnum'].binding, :===, receiver_val, value: true)
|
1310
|
+
cond_instruct(cond_result, no_singleton, maybe_symbol)
|
1311
|
+
|
1312
|
+
start_block maybe_symbol
|
1313
|
+
cond_result = call_instruct(ClassRegistry['Symbol'].binding, :===, receiver_val, value: true)
|
1314
|
+
cond_instruct(cond_result, no_singleton, has_singleton)
|
1315
|
+
|
1316
|
+
start_block no_singleton
|
1317
|
+
raise_instance_of_instruct ClassRegistry['TypeError'].binding
|
1318
|
+
|
1319
|
+
start_block has_singleton
|
1320
|
+
singleton = call_instruct(receiver_val, :singleton_class, value: true, raise: false)
|
1321
|
+
module_eval_instruct(singleton, body, opts)
|
1322
|
+
end
|
1323
|
+
|
1324
|
+
def module_eval_instruct(new_self, body, opts)
|
1325
|
+
with_scope(ClosedScope.new(current_scope, new_self)) do
|
1326
|
+
with_self new_self do
|
1327
|
+
walk_node body, opts
|
1328
|
+
end
|
1329
|
+
end
|
1330
|
+
end
|
1331
|
+
|
1332
|
+
# Defines a method on the current lexically-enclosing class/module.
|
1333
|
+
def def_instruct(receiver, name, args, body, opts = {})
|
1334
|
+
opts = {value: false}.merge(opts)
|
1335
|
+
body.scope = current_scope
|
1336
|
+
block, new_proc = create_block_temporary(args, body)
|
1337
|
+
new_proc.annotations = body.parent.comment.annotation_map if body.parent.comment
|
1338
|
+
call_instruct(receiver, 'define_method', name, block, raise: false)
|
1339
|
+
const_instruct(nil) if opts[:value]
|
1340
|
+
end
|
1341
|
+
|
1342
|
+
# Creates a temporary, assigns it a constant value, and returns it.
|
1343
|
+
def const_instruct(val)
|
1344
|
+
result = lookup_or_create_temporary(:const, val)
|
1345
|
+
copy_instruct result, val
|
1346
|
+
result
|
1347
|
+
end
|
1348
|
+
|
1349
|
+
def self_instruct
|
1350
|
+
@self_register
|
1351
|
+
end
|
1352
|
+
|
1353
|
+
# Copies one register to another.
|
1354
|
+
def copy_instruct(lhs, rhs)
|
1355
|
+
add_instruction(:assign, lhs, rhs)
|
1356
|
+
end
|
1357
|
+
|
1358
|
+
def evaluate_if_needed(node, opts={})
|
1359
|
+
if Bindings::Base === node
|
1360
|
+
node
|
1361
|
+
else
|
1362
|
+
walk_node(node, opts)
|
1363
|
+
end
|
1364
|
+
end
|
1365
|
+
|
1366
|
+
# Does a single assignment between an LHS node and an RHS node, both unevaluated.
|
1367
|
+
# This can be used by massign calls! In fact, it's very important to structure
|
1368
|
+
# this code to handle such cases.
|
1369
|
+
def single_assign_instruct(lhs, rhs, opts={})
|
1370
|
+
opts = {value: true}.merge(opts)
|
1371
|
+
case lhs.type
|
1372
|
+
when :field
|
1373
|
+
# In 1.9.2, receiver is evaulated first, then the arguments
|
1374
|
+
receiver = walk_node lhs[1], value: true
|
1375
|
+
method_name = lhs[3].expanded_identifier
|
1376
|
+
rhs_val = evaluate_if_needed(rhs, value: true)
|
1377
|
+
call_instruct(receiver, "#{method_name}=".to_sym, rhs_val, {block: false}.merge(opts))
|
1378
|
+
rhs_val
|
1379
|
+
when :aref_field
|
1380
|
+
generic_aref_instruct(walk_node(lhs[1], value: true), lhs[2][1], rhs, opts)
|
1381
|
+
when :const_path_field
|
1382
|
+
receiver, const = lhs.children
|
1383
|
+
receiver_val = walk_node(receiver, value: true)
|
1384
|
+
const_name_val = const_instruct(const.expanded_identifier)
|
1385
|
+
rhs_val = evaluate_if_needed(rhs, value: true)
|
1386
|
+
# never raises!
|
1387
|
+
call_instruct(receiver_val, :const_set, const_name_val, rhs_val, value: false, raise: false)
|
1388
|
+
rhs_val
|
1389
|
+
when :mlhs_paren
|
1390
|
+
# rhs may or may not be evaluated, and we're okay with that
|
1391
|
+
multiple_assign_instruct(lhs[1], rhs, opts)
|
1392
|
+
else
|
1393
|
+
if Bindings::Base === rhs
|
1394
|
+
rhs_val = rhs
|
1395
|
+
elsif rhs.type == :mrhs_new_from_args || rhs.type == :args_add_star ||
|
1396
|
+
rhs.type == :mrhs_add_star
|
1397
|
+
fixed, varying = compute_fixed_and_varying_rhs(rhs)
|
1398
|
+
rhs_val = combine_fixed_and_varying(fixed, varying)
|
1399
|
+
else
|
1400
|
+
rhs_val = walk_node rhs, value: true
|
1401
|
+
end
|
1402
|
+
|
1403
|
+
var_name = lhs.expanded_identifier
|
1404
|
+
if lhs.type == :@ident || lhs[1].type == :@ident
|
1405
|
+
lhs.binding = current_scope.lookup_or_create_local(var_name)
|
1406
|
+
copy_instruct lhs.binding, rhs_val
|
1407
|
+
elsif lhs[1].type == :@const
|
1408
|
+
call_instruct(current_namespace, :const_set, const_instruct(var_name), rhs_val, value: false, raise: false)
|
1409
|
+
elsif lhs[1].type == :@ivar
|
1410
|
+
call_instruct(current_self, :instance_variable_set, const_instruct(var_name),
|
1411
|
+
rhs_val, value: true, ignore_privacy: true)
|
1412
|
+
elsif lhs[1].type == :@gvar
|
1413
|
+
call_instruct(ClassRegistry['Laser#Magic'].binding, :set_global,
|
1414
|
+
const_instruct(var_name), rhs_val, raise: false, value: true)
|
1415
|
+
end
|
1416
|
+
rhs_val
|
1417
|
+
end
|
1418
|
+
end
|
1419
|
+
|
1420
|
+
def wasted_val(val)
|
1421
|
+
val.add_error(DiscardedRHSError.new("RHS value being discarded", val))
|
1422
|
+
end
|
1423
|
+
|
1424
|
+
def wasted_var(var)
|
1425
|
+
if var.type == :field || var.type == :aref_field || var.type == :mlhs_paren
|
1426
|
+
var.add_error(UnassignedLHSError.new("LHS never assigned - defaults to nil", var))
|
1427
|
+
else
|
1428
|
+
var.add_error(UnassignedLHSError.new("LHS (#{var.expanded_identifier}) never assigned - defaults to nil", var))
|
1429
|
+
end
|
1430
|
+
end
|
1431
|
+
|
1432
|
+
def wasted_lhs_splat(var, attachment_node)
|
1433
|
+
if var.nil?
|
1434
|
+
attachment_node.add_error(UnassignedLHSError.new("Unnamed LHS splat is always empty ([]) - not useful", attachment_node))
|
1435
|
+
elsif var.type == :field || var.type == :aref_field || var.type == :mlhs_paren
|
1436
|
+
var.add_error(UnassignedLHSError.new("LHS splat is always empty ([]) - not useful", var))
|
1437
|
+
else
|
1438
|
+
var.add_error(UnassignedLHSError.new("LHS splat (#{var.expanded_identifier}) is always empty ([]) - not useful", var))
|
1439
|
+
end
|
1440
|
+
end
|
1441
|
+
|
1442
|
+
def wasted_rhs_splat(val)
|
1443
|
+
val.add_error(DiscardedRHSError.new("RHS splat expanded and then discarded", val))
|
1444
|
+
end
|
1445
|
+
|
1446
|
+
def issue_wasted_val_warnings(list)
|
1447
|
+
list.each { |val| wasted_val(val) }
|
1448
|
+
end
|
1449
|
+
|
1450
|
+
def issue_wasted_var_warnings(list)
|
1451
|
+
list.each { |val| wasted_var(val) }
|
1452
|
+
end
|
1453
|
+
|
1454
|
+
# Expands a multiple-assignment as optimally as possible. Without any
|
1455
|
+
# splats, will expand to sequential assignments.
|
1456
|
+
def multiple_assign_instruct(lhs, rhs, opts={})
|
1457
|
+
rhs_has_star = rhs.find_type(:mrhs_add_star) || rhs.find_type(:args_add_star)
|
1458
|
+
# a, b = c, d, e
|
1459
|
+
if lhs.type != :mlhs_add_star && !rhs_has_star
|
1460
|
+
# a, b = c, d, e
|
1461
|
+
# tries to generate maximally efficient code: computes
|
1462
|
+
# precise assignments to improve analysis.
|
1463
|
+
if Sexp === lhs[0] && rhs.type == :mrhs_new_from_args
|
1464
|
+
rhs = rhs[1] + [rhs[2]] # i assume some parser silliness does this
|
1465
|
+
# issue warnings
|
1466
|
+
if lhs.size < rhs.size
|
1467
|
+
issue_wasted_val_warnings(rhs[lhs.size..-1])
|
1468
|
+
elsif lhs.size > rhs.size
|
1469
|
+
issue_wasted_var_warnings(lhs[rhs.size..-1])
|
1470
|
+
end
|
1471
|
+
# pair them up. Enumerable#zip in 1.9.2 doesn't support unmatched lengths
|
1472
|
+
|
1473
|
+
pairs = (0...[lhs.size, rhs.size].max).map do |idx|
|
1474
|
+
need_temp = opts[:value] || (lhs[idx] && rhs[idx])
|
1475
|
+
{ lhs: lhs[idx], rhs: rhs[idx], need_temp: need_temp }
|
1476
|
+
end
|
1477
|
+
# compute the rhs node
|
1478
|
+
pairs_with_vals = pairs.map do |hash|
|
1479
|
+
# only walk rhs if it exists
|
1480
|
+
result = hash[:rhs] && walk_node(hash[:rhs], value: hash[:need_temp])
|
1481
|
+
hash.merge(value: result)
|
1482
|
+
end
|
1483
|
+
# perform necessary assignments
|
1484
|
+
pairs_with_vals.each do |hash|
|
1485
|
+
if hash[:lhs]
|
1486
|
+
hash[:value] ||= const_instruct(nil)
|
1487
|
+
new_value = hash[:value]
|
1488
|
+
single_assign_instruct(hash[:lhs], new_value, value: false)
|
1489
|
+
end
|
1490
|
+
end
|
1491
|
+
# if we need the value, we need to return all the RHS in an array.
|
1492
|
+
if opts[:value]
|
1493
|
+
result_temps = pairs_with_vals.map { |hash| hash[:value] }
|
1494
|
+
call_instruct(ClassRegistry['Array'].binding, :[], *result_temps,
|
1495
|
+
value: true, raise: false)
|
1496
|
+
end
|
1497
|
+
# a, b, c = foo
|
1498
|
+
# no star on RHS means implicit conversion: to_ary.
|
1499
|
+
elsif Sexp === lhs[0] && rhs.type != :mrhs_new_from_args
|
1500
|
+
rhs_val = walk_node(rhs, value: true)
|
1501
|
+
rhs_array = rb_ary_to_ary(rhs_val)
|
1502
|
+
declare_instruct(:expect_tuple_size, :==, lhs.size, rhs_array)
|
1503
|
+
lhs.each_with_index do |node, idx|
|
1504
|
+
assigned_val = call_instruct(rhs_array, :[], const_instruct(idx),
|
1505
|
+
value: true, raise: false)
|
1506
|
+
single_assign_instruct(node, assigned_val)
|
1507
|
+
end
|
1508
|
+
rhs_array
|
1509
|
+
else
|
1510
|
+
raise ArgumentError.new("Unexpected non-starred massign node:\n" +
|
1511
|
+
" lhs = #{lhs.inspect}\n rhs = #{rhs.inspect}")
|
1512
|
+
end
|
1513
|
+
# a, *b, c = [1, 2, 3]
|
1514
|
+
# implicit #to_ary
|
1515
|
+
elsif lhs.type == :mlhs_add_star && !rhs_has_star && rhs.type != :mrhs_new_from_args
|
1516
|
+
# calculate RHS: array of unknown length
|
1517
|
+
rhs_val = walk_node(rhs, value: true)
|
1518
|
+
rhs_array = rb_ary_to_ary(rhs_val)
|
1519
|
+
assign_splat_mlhs_to_varying(lhs[1], lhs[2], lhs[3] || [], rhs_array)
|
1520
|
+
# a, *b, c = 1, 2, 3, 4, 5
|
1521
|
+
# also easy/precise
|
1522
|
+
elsif lhs.type == :mlhs_add_star && !rhs_has_star
|
1523
|
+
# RHS = :mrhs_new_from_args
|
1524
|
+
# single_val not handled yet.
|
1525
|
+
rhs_arr = rhs[1] + [rhs[2]]
|
1526
|
+
|
1527
|
+
# Calculate pre-star, star, and post-star bindings and boundaries
|
1528
|
+
pre_star = lhs[1] # [], if empty
|
1529
|
+
star_node = lhs[2] # could be nil
|
1530
|
+
post_star = lhs[3] || []
|
1531
|
+
|
1532
|
+
star_start = pre_star.size
|
1533
|
+
star_end = [star_start, rhs_arr.size - post_star.size].max
|
1534
|
+
star_range = star_start...star_end
|
1535
|
+
|
1536
|
+
if star_range.entries.empty?
|
1537
|
+
wasted_lhs_splat(star_node, lhs)
|
1538
|
+
end
|
1539
|
+
|
1540
|
+
# all RHS are ALWAYS consumed. But if the star is unnamed, star values can be
|
1541
|
+
# discarded.
|
1542
|
+
rhs_vals = rhs_arr.map.with_index do |node, idx|
|
1543
|
+
if !opts[:value] && star_node.nil? && star_range.include?(idx)
|
1544
|
+
walk_node(node, value: false)
|
1545
|
+
nil
|
1546
|
+
else
|
1547
|
+
walk_node(node, value: true)
|
1548
|
+
end
|
1549
|
+
end
|
1550
|
+
|
1551
|
+
# do pre-star nodes
|
1552
|
+
pre_star.each_with_index do |lhs_node, idx|
|
1553
|
+
single_assign_instruct(lhs_node, rhs_vals[idx] || const_instruct(nil))
|
1554
|
+
end
|
1555
|
+
# do star assignment if star_node != nil
|
1556
|
+
if star_node
|
1557
|
+
star_parts = rhs_vals[star_range]
|
1558
|
+
star_arr = call_instruct(ClassRegistry['Array'].binding, :[], *star_parts,
|
1559
|
+
value: true, raise: false)
|
1560
|
+
single_assign_instruct star_node, star_arr
|
1561
|
+
end
|
1562
|
+
# do post-star nodes
|
1563
|
+
post_star.each_with_index do |lhs_node, idx|
|
1564
|
+
single_assign_instruct(lhs_node, rhs_vals[star_end + idx] || const_instruct(nil))
|
1565
|
+
end
|
1566
|
+
if opts[:value]
|
1567
|
+
call_instruct(ClassRegistry['Array'].binding, :[], *rhs_vals,
|
1568
|
+
value: true, raise: false)
|
1569
|
+
end
|
1570
|
+
# a, b, c = 1, *foo
|
1571
|
+
elsif lhs.type != :mlhs_add_star && rhs_has_star
|
1572
|
+
# for building the final array
|
1573
|
+
lhs_size = lhs.size
|
1574
|
+
fixed, varying = compute_fixed_and_varying_rhs(rhs)
|
1575
|
+
fixed_size = fixed.size
|
1576
|
+
if fixed_size >= lhs_size
|
1577
|
+
wasted_rhs_splat(rhs)
|
1578
|
+
else
|
1579
|
+
declare_instruct(:expect_tuple_size, :==, lhs_size - fixed_size, varying)
|
1580
|
+
end
|
1581
|
+
fixed[0...lhs_size].each_with_index do |val, idx|
|
1582
|
+
single_assign_instruct(lhs[idx], val)
|
1583
|
+
end
|
1584
|
+
fixed_size.upto(lhs_size - 1) do |idx|
|
1585
|
+
looked_up = call_instruct(varying, :[], const_instruct(idx - fixed_size), value: true, raise: false)
|
1586
|
+
single_assign_instruct(lhs[idx], looked_up)
|
1587
|
+
end
|
1588
|
+
if fixed.empty?
|
1589
|
+
result = varying
|
1590
|
+
else
|
1591
|
+
fixed_as_arr = call_instruct(ClassRegistry['Array'].binding, :[], *fixed, value: true, raise: false)
|
1592
|
+
result = call_instruct(fixed_as_arr, :+, varying, value: true, raise: false)
|
1593
|
+
end
|
1594
|
+
result
|
1595
|
+
# a, *b, c = d, *e
|
1596
|
+
elsif lhs.type == :mlhs_add_star && rhs_has_star
|
1597
|
+
# Calculate pre-star, star, and post-star bindings and boundaries
|
1598
|
+
fixed, varying = compute_fixed_and_varying_rhs(rhs)
|
1599
|
+
# inefficient: ignore fixed stuff
|
1600
|
+
# TODO(adgar): optimize this
|
1601
|
+
rhs_array = combine_fixed_and_varying(fixed, varying)
|
1602
|
+
assign_splat_mlhs_to_varying(lhs[1], lhs[2], lhs[3] || [], rhs_array)
|
1603
|
+
else
|
1604
|
+
raise ArgumentError.new("Unexpected :massign node:\n " +
|
1605
|
+
"lhs = #{lhs.inspect}\n rhs = #{rhs.inspect}")
|
1606
|
+
end
|
1607
|
+
end
|
1608
|
+
|
1609
|
+
def assign_splat_mlhs_to_varying(pre_star, star_node, post_star, rhs_array)
|
1610
|
+
# pre-star is easy: how they are extracted is deterministic
|
1611
|
+
pre_star.each_with_index do |lhs_node, idx|
|
1612
|
+
assigned_val = call_instruct(rhs_array, :[], const_instruct(idx),
|
1613
|
+
value: true, raise: false)
|
1614
|
+
single_assign_instruct(lhs_node, assigned_val)
|
1615
|
+
end
|
1616
|
+
|
1617
|
+
# next, extract star_node. run-time version of below
|
1618
|
+
star_start = const_instruct(pre_star.size)
|
1619
|
+
fixed_size = const_instruct(pre_star.size + post_star.size)
|
1620
|
+
declare_instruct(:expect_tuple_size, :>, pre_star.size + post_star.size, rhs_array)
|
1621
|
+
# calculate star_end at runtime without loops
|
1622
|
+
rhs_arr_size = call_instruct(rhs_array, :size,
|
1623
|
+
value: true, raise: false)
|
1624
|
+
star_size = call_instruct(rhs_arr_size, :-, fixed_size,
|
1625
|
+
value: true, raise: false)
|
1626
|
+
|
1627
|
+
after = create_block
|
1628
|
+
|
1629
|
+
if_nonempty = build_block_with_jump(after) do
|
1630
|
+
if star_node
|
1631
|
+
star_subarray = call_instruct(rhs_array, :[], star_start, star_size,
|
1632
|
+
value: true, raise: false)
|
1633
|
+
single_assign_instruct(star_node, star_subarray)
|
1634
|
+
end
|
1635
|
+
if post_star.any?
|
1636
|
+
post_star_start = call_instruct(star_start, :+, star_size,
|
1637
|
+
value: true, raise: false)
|
1638
|
+
post_star_array = call_instruct(rhs_array, :[], post_star_start,
|
1639
|
+
const_instruct(post_star.size), value: true, raise: false)
|
1640
|
+
post_star.each_with_index do |lhs_node, idx|
|
1641
|
+
assigned_val = call_instruct(post_star_array, :[], const_instruct(idx),
|
1642
|
+
value: true, raise: false)
|
1643
|
+
single_assign_instruct(lhs_node, assigned_val)
|
1644
|
+
end
|
1645
|
+
end
|
1646
|
+
end
|
1647
|
+
|
1648
|
+
if_empty = build_block_with_jump(after) do
|
1649
|
+
single_assign_instruct(star_node, const_instruct([])) if star_node
|
1650
|
+
post_star.each_with_index do |lhs_node, idx|
|
1651
|
+
assigned_val = call_instruct(rhs_array, :[], const_instruct(pre_star.size + idx),
|
1652
|
+
value: true, raise: false)
|
1653
|
+
single_assign_instruct(lhs_node, assigned_val)
|
1654
|
+
end
|
1655
|
+
end
|
1656
|
+
|
1657
|
+
cond_value = call_instruct(star_size, :>, const_instruct(0),
|
1658
|
+
value: true, raise: false)
|
1659
|
+
cond_instruct(cond_value, if_nonempty, if_empty)
|
1660
|
+
|
1661
|
+
start_block after
|
1662
|
+
rhs_array
|
1663
|
+
end
|
1664
|
+
|
1665
|
+
# Computes the fixed portion of the RHS, and the varying portion of the
|
1666
|
+
# RHS. The fixed portion will be an array of temporaries, the varying
|
1667
|
+
# portion will be an array temporary.
|
1668
|
+
def compute_fixed_and_varying_rhs(node)
|
1669
|
+
case node[0]
|
1670
|
+
when :mrhs_add_star, :args_add_star
|
1671
|
+
pre_star, star = node[1], node[2]
|
1672
|
+
post_star = node[3..-1]
|
1673
|
+
# pre_star could have more stars!
|
1674
|
+
fixed, varying = compute_fixed_and_varying_rhs(pre_star)
|
1675
|
+
# varying had better be []
|
1676
|
+
star_pre_conv = walk_node(star, value: true)
|
1677
|
+
star_vals = rb_check_convert_type(star_pre_conv, ClassRegistry['Array'].binding, :to_a)
|
1678
|
+
# if we had varying parts, then we append the star, otherwise, the star IS
|
1679
|
+
# the varying part
|
1680
|
+
if varying
|
1681
|
+
varying = call_instruct(varying, :+, star_vals, value: true, raise: false)
|
1682
|
+
else
|
1683
|
+
varying = star_vals
|
1684
|
+
end
|
1685
|
+
# if we have a post-star section, append to varying
|
1686
|
+
if post_star && !post_star.empty?
|
1687
|
+
post_star_vals = build_array_instruct(post_star)
|
1688
|
+
varying = call_instruct(varying, :+, post_star_vals, value: true, raise: false)
|
1689
|
+
end
|
1690
|
+
when :mrhs_new_from_args
|
1691
|
+
# random spare node when mrhs is just used for fixed rhs
|
1692
|
+
fixed, varying = compute_fixed_and_varying_rhs(node[1])
|
1693
|
+
if node[2]
|
1694
|
+
if varying.nil?
|
1695
|
+
then fixed << walk_node(node[2], value: true)
|
1696
|
+
else varying = call_instruct(varying, :+, build_array_instruct([node[2]]), value: true, raise: false)
|
1697
|
+
end
|
1698
|
+
end
|
1699
|
+
when Sexp, NilClass
|
1700
|
+
fixed = node.map { |val| walk_node(val, value: true) }
|
1701
|
+
varying = nil
|
1702
|
+
end
|
1703
|
+
[fixed, varying]
|
1704
|
+
end
|
1705
|
+
|
1706
|
+
# Combines an array of fixed temporaries and an array temporary that contains
|
1707
|
+
# an unknown number of temporaries.
|
1708
|
+
def combine_fixed_and_varying(fixed, varying)
|
1709
|
+
if fixed.empty?
|
1710
|
+
varying || const_instruct([])
|
1711
|
+
else
|
1712
|
+
fixed_ary = call_instruct(ClassRegistry['Array'].binding, :[], *fixed, value: true, raise: false)
|
1713
|
+
if varying
|
1714
|
+
call_instruct(fixed_ary, :+, varying, value: true, raise: false)
|
1715
|
+
else
|
1716
|
+
fixed_ary
|
1717
|
+
end
|
1718
|
+
end
|
1719
|
+
end
|
1720
|
+
|
1721
|
+
def foreach_on_rhs(node, &blk)
|
1722
|
+
case node[0]
|
1723
|
+
when :mrhs_add_star, :args_add_star
|
1724
|
+
foreach_on_rhs(node[1], &blk)
|
1725
|
+
array_to_iterate = walk_node node[2], value: true
|
1726
|
+
counter = lookup_or_create_temporary(:rescue_iterator)
|
1727
|
+
copy_instruct(counter, 0)
|
1728
|
+
max = call_instruct(array_to_iterate, :size, value: true, raise: false)
|
1729
|
+
|
1730
|
+
loop_start_block, after = create_blocks 2
|
1731
|
+
|
1732
|
+
uncond_instruct loop_start_block
|
1733
|
+
cond_result = call_instruct(counter, :<, max, value: true, raise: false)
|
1734
|
+
|
1735
|
+
check_block = build_block_with_jump(loop_start_block) do
|
1736
|
+
current_val = call_instruct(array_to_iterate, :[], counter, value: true, raise: false)
|
1737
|
+
yield current_val
|
1738
|
+
next_counter = call_instruct(counter, :+, 1, value: true, raise: false)
|
1739
|
+
copy_instruct(counter, next_counter)
|
1740
|
+
end
|
1741
|
+
|
1742
|
+
cond_instruct(cond_result, check_block, after)
|
1743
|
+
start_block after
|
1744
|
+
when :mrhs_new_from_args
|
1745
|
+
foreach_on_rhs(node[1], &blk)
|
1746
|
+
yield walk_node(node[2], value: true) if node[2]
|
1747
|
+
when Sexp
|
1748
|
+
node.each { |val_node| yield walk_node(val_node, value: true) }
|
1749
|
+
end
|
1750
|
+
end
|
1751
|
+
|
1752
|
+
# Implicit conversion protocol to an array.
|
1753
|
+
def rb_ary_to_ary(value)
|
1754
|
+
result = lookup_or_create_temporary(:ary_to_ary, value)
|
1755
|
+
try_conv = rb_check_convert_type(value, ClassRegistry['Array'].binding, :to_ary)
|
1756
|
+
|
1757
|
+
after = create_block
|
1758
|
+
if_conv_succ = build_block_with_jump(after) do
|
1759
|
+
copy_instruct(result, try_conv)
|
1760
|
+
end
|
1761
|
+
if_conv_fail = build_block_with_jump(after) do
|
1762
|
+
new_result = call_instruct(ClassRegistry['Array'].binding, :[], value,
|
1763
|
+
value: true, raise: false)
|
1764
|
+
copy_instruct(result, new_result)
|
1765
|
+
end
|
1766
|
+
|
1767
|
+
cond_instruct(try_conv, if_conv_succ, if_conv_fail)
|
1768
|
+
|
1769
|
+
start_block after
|
1770
|
+
result
|
1771
|
+
end
|
1772
|
+
|
1773
|
+
#TODO(adgar): RAISES HERE!
|
1774
|
+
def rb_check_convert_type(value, klass, method)
|
1775
|
+
result = lookup_or_create_temporary(:convert, value, klass, method)
|
1776
|
+
after = create_block
|
1777
|
+
|
1778
|
+
comparison_result = call_instruct(klass, :===, value, value: true)
|
1779
|
+
|
1780
|
+
if_not_klass_block = build_block_with_jump do
|
1781
|
+
# TODO(adgar): if method does not exist, return nil.
|
1782
|
+
has_method = call_instruct(ClassRegistry['Laser#Magic'].binding, :responds?,
|
1783
|
+
value, const_instruct(method), value: true, raise: false)
|
1784
|
+
|
1785
|
+
if_has_method_block = build_block_with_jump(after) do
|
1786
|
+
conversion_result = call_instruct(value, method, value: true)
|
1787
|
+
copy_instruct result, conversion_result
|
1788
|
+
end
|
1789
|
+
if_no_method_block = build_block_with_jump(after) do
|
1790
|
+
copy_instruct result, const_instruct(nil)
|
1791
|
+
declare_instruct(:alias, result, value)
|
1792
|
+
end
|
1793
|
+
cond_instruct(has_method, if_has_method_block, if_no_method_block)
|
1794
|
+
end
|
1795
|
+
if_klass_block = build_block_with_jump(after) do
|
1796
|
+
copy_instruct(result, value)
|
1797
|
+
end
|
1798
|
+
|
1799
|
+
cond_instruct(comparison_result, if_klass_block, if_not_klass_block)
|
1800
|
+
|
1801
|
+
start_block after
|
1802
|
+
result
|
1803
|
+
end
|
1804
|
+
|
1805
|
+
def explicit_block_arg(method_call)
|
1806
|
+
if (to_proc = method_call.arguments.block_arg)
|
1807
|
+
to_proc_val = walk_node(to_proc, value: true)
|
1808
|
+
rb_check_convert_type(to_proc_val, ClassRegistry['Proc'].binding, :to_proc)
|
1809
|
+
end
|
1810
|
+
end
|
1811
|
+
|
1812
|
+
def issue_call(node, opts={})
|
1813
|
+
opts = {value: true}.merge(opts)
|
1814
|
+
method_call = node.method_call
|
1815
|
+
opts = opts.merge(ignore_privacy: true) if method_call.implicit_receiver?
|
1816
|
+
receiver = receiver_instruct node
|
1817
|
+
block = explicit_block_arg(method_call)
|
1818
|
+
generic_call_instruct(receiver, method_call.method_name,
|
1819
|
+
method_call.arg_node, block, opts)
|
1820
|
+
end
|
1821
|
+
|
1822
|
+
def issue_super_call(node, opts={})
|
1823
|
+
method_call = node.method_call
|
1824
|
+
opts = opts.merge(ignore_privacy: true)
|
1825
|
+
block = explicit_block_arg(method_call)
|
1826
|
+
generic_super_instruct(method_call.arg_node, block, opts)
|
1827
|
+
end
|
1828
|
+
|
1829
|
+
def receiver_instruct(node)
|
1830
|
+
method_call = node.method_call
|
1831
|
+
if method_call.receiver_node
|
1832
|
+
then walk_node method_call.receiver_node, value: true
|
1833
|
+
else self_instruct
|
1834
|
+
end
|
1835
|
+
end
|
1836
|
+
|
1837
|
+
# Given a receiver, a method, a method_add_arg node, and a block value,
|
1838
|
+
# issue a call instruction. This will involve computing the arguments,
|
1839
|
+
# potentially issuing a vararg call (if splats are used). The return
|
1840
|
+
# value is captured and returned to the caller of this method.
|
1841
|
+
def generic_call_instruct(receiver, method, args, block, opts={})
|
1842
|
+
opts = {value: true}.merge(opts)
|
1843
|
+
args = [] if args.nil?
|
1844
|
+
args = args[1] if args[0] == :args_add_block
|
1845
|
+
if args[0] == :args_add_star
|
1846
|
+
arg_array = compute_varargs(args)
|
1847
|
+
call_vararg_instruct(receiver, method, arg_array, {block: block}.merge(opts))
|
1848
|
+
else
|
1849
|
+
arg_temps = args.map { |arg| walk_node(arg, value: true) }
|
1850
|
+
call_instruct(receiver, method, *arg_temps, {block: block}.merge(opts))
|
1851
|
+
end
|
1852
|
+
end
|
1853
|
+
|
1854
|
+
# Given a receiver, a method, a method_add_arg node, and a block value,
|
1855
|
+
# issue a super instruction. This will involve computing the arguments,
|
1856
|
+
# potentially issuing a vararg super (if splats are used). The return
|
1857
|
+
# value is captured and returned to the superer of this method.
|
1858
|
+
def generic_super_instruct(args, block, opts={})
|
1859
|
+
opts = {value: true, raise: true}.merge(opts)
|
1860
|
+
args = args[1] if args[0] == :args_add_block
|
1861
|
+
if args[0] == :args_add_star
|
1862
|
+
arg_array = compute_varargs(args)
|
1863
|
+
super_vararg_instruct(arg_array, {block: block}.merge(opts))
|
1864
|
+
else
|
1865
|
+
arg_temps = args.map { |arg| walk_node(arg, value: true) }
|
1866
|
+
super_instruct(*arg_temps, {block: block}.merge(opts))
|
1867
|
+
end
|
1868
|
+
end
|
1869
|
+
|
1870
|
+
# Given a receiver, a method, a method_add_arg node, and a block value,
|
1871
|
+
# issue a call instruction. This will involve computing the arguments,
|
1872
|
+
# potentially issuing a vararg call (if splats are used). The return
|
1873
|
+
# value is captured and returned to the caller of this method.
|
1874
|
+
def generic_aref_instruct(receiver, args, val, opts={})
|
1875
|
+
opts = {value: true, raise: true}.merge(opts)
|
1876
|
+
args = [] if args.nil?
|
1877
|
+
args = args[1] if args[0] == :args_add_block
|
1878
|
+
if args[0] == :args_add_star
|
1879
|
+
arg_array = compute_varargs(args)
|
1880
|
+
call_instruct(arg_array, :<<, walk_node(val, value: true), value: false)
|
1881
|
+
call_vararg_instruct(receiver, :[]=, arg_array, false, opts)
|
1882
|
+
else
|
1883
|
+
arg_temps = (args + [val]).map { |arg| evaluate_if_needed(arg, value: true) }
|
1884
|
+
call_instruct(receiver, :[]=, *arg_temps, {block: false}.merge(opts))
|
1885
|
+
end
|
1886
|
+
end
|
1887
|
+
|
1888
|
+
# Computes the arguments to a zsuper call at the given node. Also returns
|
1889
|
+
# whether the resulting argument expansion is of variable arity.
|
1890
|
+
# This is different from normal splatting because we are computing based
|
1891
|
+
# upon the argument list of the method, not a normal arg_ node.
|
1892
|
+
#
|
1893
|
+
# returns: (Bindings::Base | [Bindings::Base], Boolean)
|
1894
|
+
def compute_zsuper_arguments
|
1895
|
+
args_to_walk = @formals
|
1896
|
+
is_vararg = args_to_walk.any? { |arg| arg.kind == :rest }
|
1897
|
+
if is_vararg
|
1898
|
+
index_of_star = args_to_walk.index { |arg| arg.kind == :rest }
|
1899
|
+
# re-splatting vararg super call. assholes
|
1900
|
+
|
1901
|
+
pre_star = args_to_walk[0...index_of_star]
|
1902
|
+
result = call_instruct(ClassRegistry['Array'].binding, :[], *pre_star,
|
1903
|
+
value: true, raise: false)
|
1904
|
+
result = call_instruct(result, :+, args_to_walk[index_of_star],
|
1905
|
+
value: true, raise: false)
|
1906
|
+
|
1907
|
+
post_star = args_to_walk[index_of_star+1 .. -1]
|
1908
|
+
post_star_temp = call_instruct(ClassRegistry['Array'].binding, :[], *post_star,
|
1909
|
+
value: true, raise: false)
|
1910
|
+
result = call_instruct(result, :+, post_star_temp, value: true, raise: false)
|
1911
|
+
[result, is_vararg]
|
1912
|
+
else
|
1913
|
+
[args_to_walk, is_vararg]
|
1914
|
+
end
|
1915
|
+
end
|
1916
|
+
|
1917
|
+
# Computes a splatting node (:args_add_star)
|
1918
|
+
def compute_varargs(args)
|
1919
|
+
result = call_instruct(ClassRegistry['Array'].binding, :new, value: true, raise: false)
|
1920
|
+
if args[1][0] == :args_add_star || args[1].children.any?
|
1921
|
+
prefix = if args[1][0] == :args_add_star
|
1922
|
+
then compute_varargs(args[1])
|
1923
|
+
else prefix = build_array_instruct(args[1].children)
|
1924
|
+
end
|
1925
|
+
result = call_instruct(result, :+, prefix, value: true, raise: false)
|
1926
|
+
end
|
1927
|
+
starred = walk_node args[2], value: true
|
1928
|
+
starred_converted = rb_check_convert_type(starred, ClassRegistry['Array'].binding, :to_a)
|
1929
|
+
result = call_instruct(result, :+, starred_converted, value: true, raise: false)
|
1930
|
+
if args[3..-1].any?
|
1931
|
+
suffix = build_array_instruct(args[3..-1])
|
1932
|
+
result = call_instruct(result, :+, suffix, value: true, raise: false)
|
1933
|
+
end
|
1934
|
+
result
|
1935
|
+
end
|
1936
|
+
|
1937
|
+
# Adds a generic method call instruction.
|
1938
|
+
def call_instruct(receiver, method, *args)
|
1939
|
+
opts = {raise: true}
|
1940
|
+
opts.merge!(args.last) if Hash === args.last
|
1941
|
+
result = create_result_if_needed opts
|
1942
|
+
call_opts = { ignore_privacy: opts[:ignore_privacy] }
|
1943
|
+
add_instruction_with_opts(:call, result, receiver, method.to_sym, *args, call_opts)
|
1944
|
+
add_potential_raise_edge if opts[:raise]
|
1945
|
+
result
|
1946
|
+
end
|
1947
|
+
|
1948
|
+
# Adds a generic method call instruction.
|
1949
|
+
def call_vararg_instruct(receiver, method, args, opts={})
|
1950
|
+
opts = {raise: true, value: true}.merge(opts)
|
1951
|
+
result = create_result_if_needed opts
|
1952
|
+
call_opts = { ignore_privacy: opts[:ignore_privacy] }
|
1953
|
+
add_instruction_with_opts(:call_vararg, result, receiver, method.to_sym, args, opts, call_opts)
|
1954
|
+
add_potential_raise_edge if opts[:raise]
|
1955
|
+
result
|
1956
|
+
end
|
1957
|
+
|
1958
|
+
# Adds a generic method super instruction.
|
1959
|
+
def super_instruct(*args)
|
1960
|
+
opts = (Hash === args.last) ? args.last : {}
|
1961
|
+
result = create_result_if_needed opts
|
1962
|
+
|
1963
|
+
add_instruction(:super, result, *args)
|
1964
|
+
add_potential_raise_edge
|
1965
|
+
result
|
1966
|
+
end
|
1967
|
+
|
1968
|
+
# Adds a generic method super instruction.
|
1969
|
+
def super_vararg_instruct(args, block, opts={})
|
1970
|
+
opts = {raise: true, value: true}.merge(opts)
|
1971
|
+
result = create_result_if_needed opts
|
1972
|
+
add_instruction(:super_vararg, result, args, block)
|
1973
|
+
add_potential_raise_edge if opts[:raise]
|
1974
|
+
result
|
1975
|
+
end
|
1976
|
+
|
1977
|
+
# Adds an edge from the current block to the current rescue target,
|
1978
|
+
# while creating a new block for the "natural" exit.
|
1979
|
+
def add_potential_raise_edge(add_success_branch = true)
|
1980
|
+
fail_block = create_block
|
1981
|
+
@graph.add_edge(@current_block, fail_block, RGL::ControlFlowGraph::EDGE_ABNORMAL)
|
1982
|
+
with_current_basic_block(fail_block) do
|
1983
|
+
observe_just_raised_exception
|
1984
|
+
uncond_instruct current_rescue, flags: RGL::ControlFlowGraph::EDGE_ABNORMAL
|
1985
|
+
end
|
1986
|
+
uncond_instruct create_block, jump_instruct: false if add_success_branch
|
1987
|
+
end
|
1988
|
+
|
1989
|
+
# Looks up the value of a variable and assigns it to a new temporary
|
1990
|
+
def variable_instruct(var_ref)
|
1991
|
+
return self_register if var_ref.expanded_identifier == 'self'
|
1992
|
+
var_named_instruct var_ref.expanded_identifier
|
1993
|
+
end
|
1994
|
+
|
1995
|
+
def var_named_instruct(var_name)
|
1996
|
+
binding = current_scope.lookup_or_create_local(var_name)
|
1997
|
+
result = lookup_or_create_temporary(:var, binding)
|
1998
|
+
copy_instruct result, binding
|
1999
|
+
result
|
2000
|
+
end
|
2001
|
+
|
2002
|
+
# Performs constant lookup.
|
2003
|
+
# TODO(adgar): FULL CONSTANT LOOKUP w/ SUPERCLASS CHECKING ETC
|
2004
|
+
def const_lookup(const_name)
|
2005
|
+
ident = const_instruct(const_name)
|
2006
|
+
call_instruct(current_namespace,
|
2007
|
+
:const_get, ident, value: true, ignore_privacy: true)
|
2008
|
+
end
|
2009
|
+
|
2010
|
+
# Performs constant definition checking.
|
2011
|
+
# TODO(adgar): FULL CONSTANT LOOKUP w/ SUPERCLASS CHECKING ETC
|
2012
|
+
def const_defined?(const_name)
|
2013
|
+
ident = const_instruct(const_name)
|
2014
|
+
call_instruct(current_namespace,
|
2015
|
+
:const_defined?, ident, value: true, ignore_privacy: true)
|
2016
|
+
end
|
2017
|
+
|
2018
|
+
def binary_instruct(lhs, op, rhs, opts={})
|
2019
|
+
opts = {value: true}.merge(opts)
|
2020
|
+
if op == :or || op == :"||"
|
2021
|
+
return or_instruct(lhs, rhs, opts)
|
2022
|
+
elsif op == :and || op == :"&&"
|
2023
|
+
return and_instruct(lhs, rhs, opts)
|
2024
|
+
end
|
2025
|
+
|
2026
|
+
lhs_result = walk_node lhs, value: true
|
2027
|
+
rhs_result = walk_node rhs, value: true
|
2028
|
+
call_instruct(lhs_result, op, rhs_result, opts)
|
2029
|
+
end
|
2030
|
+
|
2031
|
+
def ternary_instruct(cond, if_true, if_false, opts={})
|
2032
|
+
opts = {value: true}.merge(opts)
|
2033
|
+
if_true_block, if_false_block, after = create_blocks 3
|
2034
|
+
cond_result = walk_node cond, value: true
|
2035
|
+
cond_instruct(cond_result, if_true_block, if_false_block)
|
2036
|
+
|
2037
|
+
start_block if_true_block
|
2038
|
+
if_true_result = walk_node if_true, opts
|
2039
|
+
if_true_block = @current_block
|
2040
|
+
|
2041
|
+
start_block if_false_block
|
2042
|
+
if_false_result = walk_node if_false, opts
|
2043
|
+
if_false_block = @current_block
|
2044
|
+
|
2045
|
+
# generate temporary if necessary
|
2046
|
+
result = opts[:value] ?
|
2047
|
+
lookup_or_create_temporary(:ternary, cond_result, if_true_result, if_false_result) :
|
2048
|
+
nil
|
2049
|
+
|
2050
|
+
start_block if_true_block
|
2051
|
+
copy_instruct(result, if_true_result) if opts[:value]
|
2052
|
+
uncond_instruct(after)
|
2053
|
+
|
2054
|
+
start_block if_false_block
|
2055
|
+
copy_instruct(result, if_false_result) if opts[:value]
|
2056
|
+
uncond_instruct(after)
|
2057
|
+
|
2058
|
+
start_block after
|
2059
|
+
result
|
2060
|
+
end
|
2061
|
+
|
2062
|
+
# Runs the list of operations in body while the condition is true.
|
2063
|
+
# Then returns nil.
|
2064
|
+
def while_instruct(condition, body, opts={})
|
2065
|
+
opts = {value: true}.merge(opts)
|
2066
|
+
body_block, after_block, precond_block = create_blocks 3
|
2067
|
+
|
2068
|
+
with_jump_targets(break: after_block, redo: body_block, next: precond_block) do
|
2069
|
+
uncond_instruct precond_block
|
2070
|
+
start_block precond_block
|
2071
|
+
|
2072
|
+
cond_result = walk_node condition, value: true
|
2073
|
+
cond_instruct(cond_result, body_block, after_block)
|
2074
|
+
|
2075
|
+
start_block body_block
|
2076
|
+
walk_body body, value: false
|
2077
|
+
cond_result = walk_node condition, value: true
|
2078
|
+
cond_instruct(cond_result, body_block, after_block)
|
2079
|
+
end
|
2080
|
+
|
2081
|
+
start_block after_block
|
2082
|
+
const_instruct(nil) if opts[:value]
|
2083
|
+
end
|
2084
|
+
|
2085
|
+
# Runs the list of operations in body until the condition is true.
|
2086
|
+
# Then returns nil.
|
2087
|
+
def until_instruct(condition, body, opts={})
|
2088
|
+
opts = {value: true}.merge(opts)
|
2089
|
+
body_block, after_block, precond_block = create_blocks 3
|
2090
|
+
|
2091
|
+
with_jump_targets(break: after_block, redo: body_block, next: precond_block) do
|
2092
|
+
uncond_instruct precond_block
|
2093
|
+
start_block precond_block
|
2094
|
+
|
2095
|
+
cond_result = walk_node condition, value: true
|
2096
|
+
cond_instruct(cond_result, after_block, body_block)
|
2097
|
+
|
2098
|
+
start_block body_block
|
2099
|
+
|
2100
|
+
walk_body body, value: false
|
2101
|
+
cond_result = walk_node condition, value: true
|
2102
|
+
cond_instruct(cond_result, after_block, body_block)
|
2103
|
+
end
|
2104
|
+
|
2105
|
+
start_block after_block
|
2106
|
+
const_instruct(nil) if opts[:value]
|
2107
|
+
end
|
2108
|
+
|
2109
|
+
# Performs an OR operation, with short circuiting that must save
|
2110
|
+
# the result of the operation.
|
2111
|
+
def or_instruct(lhs, rhs, opts={})
|
2112
|
+
opts = {value: true}.merge(opts)
|
2113
|
+
if opts[:value]
|
2114
|
+
then or_instruct_value(lhs, rhs)
|
2115
|
+
else or_instruct_novalue(lhs, rhs)
|
2116
|
+
end
|
2117
|
+
end
|
2118
|
+
|
2119
|
+
# Performs a short-circuit OR operation while retaining the resulting value.
|
2120
|
+
def or_instruct_value(lhs, rhs)
|
2121
|
+
after = create_block
|
2122
|
+
|
2123
|
+
lhs_result = walk_node lhs, value: true
|
2124
|
+
|
2125
|
+
result = nil
|
2126
|
+
false_block = build_block_with_jump(after) do
|
2127
|
+
rhs_result = walk_node rhs, value: true
|
2128
|
+
result = lookup_or_create_temporary(:or_short_circuit, lhs_result, rhs_result)
|
2129
|
+
copy_instruct(result, rhs_result)
|
2130
|
+
end
|
2131
|
+
true_block = build_block_with_jump(after) do
|
2132
|
+
copy_instruct(result, lhs_result)
|
2133
|
+
end
|
2134
|
+
|
2135
|
+
cond_instruct(lhs_result, true_block, false_block)
|
2136
|
+
|
2137
|
+
start_block(after)
|
2138
|
+
result
|
2139
|
+
end
|
2140
|
+
|
2141
|
+
# Performs a short-circuit OR operation while discarding the resulting value.
|
2142
|
+
def or_instruct_novalue(lhs, rhs)
|
2143
|
+
after = create_block
|
2144
|
+
|
2145
|
+
lhs_result = walk_node lhs, value: true
|
2146
|
+
|
2147
|
+
false_block = build_block_with_jump(after) do
|
2148
|
+
walk_node rhs, value: false
|
2149
|
+
end
|
2150
|
+
cond_instruct(lhs_result, after, false_block)
|
2151
|
+
|
2152
|
+
start_block(after)
|
2153
|
+
end
|
2154
|
+
|
2155
|
+
# Performs an AND operation, with short circuiting, that must save
|
2156
|
+
# the result of the operation.
|
2157
|
+
def and_instruct(lhs, rhs, opts={})
|
2158
|
+
opts = {value: true}.merge(opts)
|
2159
|
+
if opts[:value]
|
2160
|
+
then and_instruct_value(lhs, rhs)
|
2161
|
+
else and_instruct_novalue(lhs, rhs)
|
2162
|
+
end
|
2163
|
+
end
|
2164
|
+
|
2165
|
+
# Performs a short-circuit AND operation while retaining the resulting value.
|
2166
|
+
def and_instruct_value(lhs, rhs)
|
2167
|
+
after = create_block
|
2168
|
+
|
2169
|
+
lhs_result = walk_node lhs, value: true
|
2170
|
+
|
2171
|
+
result = nil
|
2172
|
+
true_block = build_block_with_jump(after) do
|
2173
|
+
rhs_result = walk_node rhs, value: true
|
2174
|
+
result = lookup_or_create_temporary(:and_short_circuit, lhs_result, rhs_result)
|
2175
|
+
copy_instruct(result, rhs_result)
|
2176
|
+
end
|
2177
|
+
false_block = build_block_with_jump(after) do
|
2178
|
+
copy_instruct(result, lhs_result)
|
2179
|
+
end
|
2180
|
+
cond_instruct(lhs_result, true_block, false_block)
|
2181
|
+
|
2182
|
+
start_block(after)
|
2183
|
+
result
|
2184
|
+
end
|
2185
|
+
|
2186
|
+
# Performs a short-circuit AND operation while discarding the resulting value.
|
2187
|
+
def and_instruct_novalue(lhs, rhs)
|
2188
|
+
after = create_block
|
2189
|
+
|
2190
|
+
lhs_result = walk_node lhs, value: true
|
2191
|
+
|
2192
|
+
true_block = build_block_with_jump(after) do
|
2193
|
+
walk_node rhs, value: false
|
2194
|
+
end
|
2195
|
+
cond_instruct(lhs_result, true_block, after)
|
2196
|
+
start_block(after)
|
2197
|
+
end
|
2198
|
+
|
2199
|
+
# Performs a value-capturing if instruction, with unlimited else-ifs
|
2200
|
+
# and a potential else block.
|
2201
|
+
#
|
2202
|
+
# condition: Sexp
|
2203
|
+
# body: [Sexp]
|
2204
|
+
# else_block: Sexp | NilClass
|
2205
|
+
def if_instruct(node, is_mod=false, opts={})
|
2206
|
+
opts = {value: true}.merge(opts)
|
2207
|
+
if opts[:value]
|
2208
|
+
then if_instruct_value(node, is_mod)
|
2209
|
+
else if_instruct_novalue(node, is_mod)
|
2210
|
+
end
|
2211
|
+
end
|
2212
|
+
|
2213
|
+
def if_instruct_value(node, is_mod=false)
|
2214
|
+
result = create_temporary
|
2215
|
+
after = create_block
|
2216
|
+
current = node
|
2217
|
+
next_block = nil
|
2218
|
+
|
2219
|
+
while current
|
2220
|
+
if current.type == :else
|
2221
|
+
true_block = next_block
|
2222
|
+
body, next_block, else_block = current[1], after, nil
|
2223
|
+
else
|
2224
|
+
true_block = create_block
|
2225
|
+
condition, body, else_block = current.children
|
2226
|
+
next_block = create_block
|
2227
|
+
|
2228
|
+
cond_result = walk_node condition, value: true
|
2229
|
+
cond_instruct(cond_result, true_block, next_block)
|
2230
|
+
end
|
2231
|
+
|
2232
|
+
start_block true_block
|
2233
|
+
body = [body] if is_mod
|
2234
|
+
body_result = walk_body body, value: true
|
2235
|
+
copy_instruct(result, body_result)
|
2236
|
+
uncond_instruct(after)
|
2237
|
+
|
2238
|
+
start_block next_block
|
2239
|
+
# check: is there no else at all, and we're about to break out of the loop?
|
2240
|
+
if current.type != :else && else_block.nil?
|
2241
|
+
copy_instruct(result, nil)
|
2242
|
+
uncond_instruct(after)
|
2243
|
+
start_block after
|
2244
|
+
end
|
2245
|
+
current = else_block
|
2246
|
+
end
|
2247
|
+
result
|
2248
|
+
end
|
2249
|
+
|
2250
|
+
# Performs an if instruction that ignores result values, with unlimited else-ifs
|
2251
|
+
# and a potential else block.
|
2252
|
+
#
|
2253
|
+
# condition: Sexp
|
2254
|
+
# body: [Sexp]
|
2255
|
+
# else_block: Sexp | NilClass
|
2256
|
+
def if_instruct_novalue(node, is_mod=false)
|
2257
|
+
current = node
|
2258
|
+
after = create_block
|
2259
|
+
next_block = nil
|
2260
|
+
|
2261
|
+
while current
|
2262
|
+
if current.type == :else
|
2263
|
+
true_block = next_block
|
2264
|
+
body, next_block, else_block = current[1], after, nil
|
2265
|
+
else
|
2266
|
+
true_block = create_block
|
2267
|
+
condition, body, else_block = current.children
|
2268
|
+
next_block = else_block ? create_block : after
|
2269
|
+
|
2270
|
+
cond_result = walk_node condition, value: true
|
2271
|
+
cond_instruct(cond_result, true_block, next_block)
|
2272
|
+
end
|
2273
|
+
|
2274
|
+
start_block true_block
|
2275
|
+
body = [body] if is_mod
|
2276
|
+
walk_body body, value: false
|
2277
|
+
uncond_instruct(after)
|
2278
|
+
|
2279
|
+
start_block next_block
|
2280
|
+
current = else_block
|
2281
|
+
end
|
2282
|
+
end
|
2283
|
+
|
2284
|
+
def unless_instruct(condition, body, else_block, opts={})
|
2285
|
+
opts = {value: true}.merge(opts)
|
2286
|
+
if opts[:value]
|
2287
|
+
then unless_instruct_value(condition, body, else_block)
|
2288
|
+
else unless_instruct_novalue(condition, body, else_block)
|
2289
|
+
end
|
2290
|
+
end
|
2291
|
+
|
2292
|
+
# Performs a value-capturing unless instruction.
|
2293
|
+
#
|
2294
|
+
# condition: Sexp
|
2295
|
+
# body: [Sexp]
|
2296
|
+
# else_block: Sexp | NilClass
|
2297
|
+
def unless_instruct_value(condition, body, else_block)
|
2298
|
+
result = create_temporary
|
2299
|
+
after = create_block
|
2300
|
+
|
2301
|
+
cond_result = walk_node condition, value: true
|
2302
|
+
|
2303
|
+
true_block = build_block_with_jump(after) do
|
2304
|
+
body_result = walk_body body, value: true
|
2305
|
+
copy_instruct(result, body_result)
|
2306
|
+
end
|
2307
|
+
|
2308
|
+
next_block = build_block_with_jump(after) do
|
2309
|
+
body_result = if else_block
|
2310
|
+
then walk_body else_block[1], value: true
|
2311
|
+
else const_instruct nil
|
2312
|
+
end
|
2313
|
+
copy_instruct result, body_result
|
2314
|
+
end
|
2315
|
+
|
2316
|
+
cond_instruct(cond_result, next_block, true_block)
|
2317
|
+
|
2318
|
+
start_block after
|
2319
|
+
result
|
2320
|
+
end
|
2321
|
+
|
2322
|
+
# Performs an unless instruction, ignoring the potential that its value
|
2323
|
+
# is saved.
|
2324
|
+
#
|
2325
|
+
# condition: Sexp
|
2326
|
+
# body: [Sexp]
|
2327
|
+
# else_block: Sexp | NilClass
|
2328
|
+
def unless_instruct_novalue(condition, body, else_block)
|
2329
|
+
after, true_block = create_blocks 2
|
2330
|
+
next_block = else_block ? create_block : after
|
2331
|
+
|
2332
|
+
cond_result = walk_node condition, value: true
|
2333
|
+
cond_instruct(cond_result, next_block, true_block)
|
2334
|
+
|
2335
|
+
start_block true_block
|
2336
|
+
walk_body body, value: false
|
2337
|
+
uncond_instruct(after)
|
2338
|
+
|
2339
|
+
if else_block
|
2340
|
+
start_block next_block
|
2341
|
+
else_block[1].each { |elt| walk_node(elt, value: false) }
|
2342
|
+
uncond_instruct(after)
|
2343
|
+
end
|
2344
|
+
|
2345
|
+
start_block after
|
2346
|
+
end
|
2347
|
+
|
2348
|
+
def defined_op_instruct(expression)
|
2349
|
+
case expression.type
|
2350
|
+
when :yield0, :yield
|
2351
|
+
# give 'yield' if there is a block, otherwise nil
|
2352
|
+
result = lookup_or_create_temporary(:defined, :yield)
|
2353
|
+
after = create_block
|
2354
|
+
with_block_block = build_block_with_jump(after) do
|
2355
|
+
copy_instruct(result, const_instruct('yield'))
|
2356
|
+
end
|
2357
|
+
without_block_block = build_block_with_jump(after) do
|
2358
|
+
copy_instruct(result, const_instruct(nil))
|
2359
|
+
end
|
2360
|
+
cond_instruct(@block_arg, with_block_block, without_block_block)
|
2361
|
+
start_block after
|
2362
|
+
result
|
2363
|
+
when :assign, :massign
|
2364
|
+
const_instruct('assignment')
|
2365
|
+
when :var_ref
|
2366
|
+
case expression[1].type
|
2367
|
+
when :@ident
|
2368
|
+
const_instruct('local-variable')
|
2369
|
+
when :@kw
|
2370
|
+
case expression[1][1]
|
2371
|
+
when 'nil' then const_instruct('nil')
|
2372
|
+
when 'self' then const_instruct('self')
|
2373
|
+
when 'true' then const_instruct('true')
|
2374
|
+
when 'false' then const_instruct('false')
|
2375
|
+
when '__FILE__', '__LINE__', '__ENCODING__' then const_instruct('expression')
|
2376
|
+
else
|
2377
|
+
raise NotImplementedError.new("defined? on keyword #{expression[1][1]}")
|
2378
|
+
end
|
2379
|
+
when :@const
|
2380
|
+
const_defined?(expression.expanded_identifier)
|
2381
|
+
when :@ivar
|
2382
|
+
|
2383
|
+
when :@cvar
|
2384
|
+
|
2385
|
+
when :@gvar
|
2386
|
+
end
|
2387
|
+
when :string_literal, :and, :or, :@int, :@float, :symbol_literal
|
2388
|
+
const_instruct('expression')
|
2389
|
+
else
|
2390
|
+
raise NotImplementedError.new("Unimplemented defined? case: #{expression.type}")
|
2391
|
+
end
|
2392
|
+
end
|
2393
|
+
|
2394
|
+
# Takes a set of either :@tstring_content or :string_embexpr nodes
|
2395
|
+
# and constructs a string out of them. (In other words, this computes
|
2396
|
+
# the contents of possibly-interpolated strings).
|
2397
|
+
def build_string_instruct(components)
|
2398
|
+
temp = const_instruct('')
|
2399
|
+
components.each do |node|
|
2400
|
+
as_string = walk_node node, value: true
|
2401
|
+
temp = call_instruct(temp, :+, as_string, value: true, raise: false)
|
2402
|
+
end
|
2403
|
+
temp
|
2404
|
+
end
|
2405
|
+
|
2406
|
+
# Takes a set of nodes, finds their values, and builds a temporary holding
|
2407
|
+
# the array containing them.
|
2408
|
+
def build_array_instruct(components)
|
2409
|
+
args = components.map { |arg| walk_node(arg, value: true) }
|
2410
|
+
call_instruct(ClassRegistry['Array'].binding, :[], *args, value: true, raise: false)
|
2411
|
+
end
|
2412
|
+
|
2413
|
+
# CL-style compiler hint generation.
|
2414
|
+
def declare_instruct(kind, *args)
|
2415
|
+
verify_declare_instruction(kind, args)
|
2416
|
+
add_instruction(:declare, kind, *args)
|
2417
|
+
end
|
2418
|
+
|
2419
|
+
def verify_declare_instruction(kind, args)
|
2420
|
+
case kind
|
2421
|
+
when :alias
|
2422
|
+
if args.size != 2
|
2423
|
+
raise ArgumentError.new("declare :alias requires 2 args")
|
2424
|
+
end
|
2425
|
+
when :expect_tuple_size
|
2426
|
+
if args.size != 3
|
2427
|
+
raise ArgumentError.new('declare :expect_tuple_size requires 3 args')
|
2428
|
+
end
|
2429
|
+
end
|
2430
|
+
end
|
2431
|
+
|
2432
|
+
def create_result_if_needed(opts)
|
2433
|
+
value = opts.delete(:value)
|
2434
|
+
value ? create_temporary : nil
|
2435
|
+
end
|
2436
|
+
|
2437
|
+
def add_fake_edge(from, to)
|
2438
|
+
@graph.add_edge(from, to, RGL::ControlFlowGraph::EDGE_FAKE)
|
2439
|
+
end
|
2440
|
+
|
2441
|
+
# Returns the name of the current temporary.
|
2442
|
+
def current_temporary(prefix='t')
|
2443
|
+
"%#{prefix}#{@temporary_counter}"
|
2444
|
+
end
|
2445
|
+
|
2446
|
+
# Creates a temporary variable with an unused name.
|
2447
|
+
def create_temporary(name = nil)
|
2448
|
+
unless name
|
2449
|
+
@temporary_counter += 1
|
2450
|
+
name = current_temporary
|
2451
|
+
end
|
2452
|
+
Bindings::TemporaryBinding.new(name, nil)
|
2453
|
+
end
|
2454
|
+
|
2455
|
+
# Creates a block temporary variable with an unused name.
|
2456
|
+
def create_block_temporary(args, body, callsite_block=nil)
|
2457
|
+
@temporary_counter += 1
|
2458
|
+
name = current_temporary('B-')
|
2459
|
+
if callsite_block
|
2460
|
+
new_proc = LaserProc.new(args, body, @graph, callsite_block)
|
2461
|
+
else
|
2462
|
+
new_proc = LaserProc.new(args, body)
|
2463
|
+
end
|
2464
|
+
binding = Bindings::BlockBinding.new(name, nil)
|
2465
|
+
add_instruction(:assign, binding, new_proc)
|
2466
|
+
[binding, new_proc]
|
2467
|
+
end
|
2468
|
+
|
2469
|
+
def lookup_or_create_temporary(*keys)
|
2470
|
+
@temporary_table[keys]
|
2471
|
+
end
|
2472
|
+
|
2473
|
+
# Adds a simple instruction to the current basic block.
|
2474
|
+
def add_instruction(*args)
|
2475
|
+
@current_block.instructions << Instruction.new(args, node: @current_node,
|
2476
|
+
block: @current_block)
|
2477
|
+
end
|
2478
|
+
|
2479
|
+
# Adds a simple instruction to the current basic block.
|
2480
|
+
def add_instruction_with_opts(*args, opts)
|
2481
|
+
opts = {node: @current_node, block: @current_block}.merge(opts)
|
2482
|
+
i = Instruction.new(args, opts)
|
2483
|
+
@current_block.instructions << Instruction.new(args, opts)
|
2484
|
+
end
|
2485
|
+
|
2486
|
+
# Creates the given number of blocks.
|
2487
|
+
def create_blocks(count)
|
2488
|
+
(0...count).to_a.map { create_block }
|
2489
|
+
end
|
2490
|
+
|
2491
|
+
# Creates a new basic block for flow analysis.
|
2492
|
+
def create_block(name = 'B' + (@block_counter += 1).to_s)
|
2493
|
+
result = BasicBlock.new(name)
|
2494
|
+
@graph.add_vertex result
|
2495
|
+
result
|
2496
|
+
end
|
2497
|
+
|
2498
|
+
# Sets the current block to be the given block.
|
2499
|
+
def start_block(block)
|
2500
|
+
@current_block = block
|
2501
|
+
end
|
2502
|
+
end
|
2503
|
+
end
|
2504
|
+
end
|
2505
|
+
end
|