From 4aba3156f267391847c228171771977a44825c49 Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Thu, 25 Apr 2024 17:23:12 +0200 Subject: [PATCH 1/2] Use MethodEntry Assumptions to track whether CoreMethodAssumptions still hold * This does not invalidate on prepend but only when redefining/overriding that specific method. * Fixes https://github.com/oracle/truffleruby/issues/3546 --- CHANGELOG.md | 2 + ...redefining_optimized_core_methods_tags.txt | 2 + .../with_else_branch.yaml | 4 +- .../with_multiple_values.yaml | 4 +- .../with_single_value.yaml | 4 +- .../with_string_literal_in_when_clause.yaml | 2 +- .../with_rational_and_imaginary_parts.yaml | 2 +- .../method_calls/special_cases/method_%.yaml | 2 +- .../method_calls/special_cases/method_&.yaml | 2 +- .../method_calls/special_cases/method_*.yaml | 2 +- .../method_calls/special_cases/method_+.yaml | 2 +- .../method_calls/special_cases/method_-.yaml | 2 +- .../method_calls/special_cases/method_-@.yaml | 2 +- .../method_calls/special_cases/method_<.yaml | 2 +- .../method_calls/special_cases/method_<<.yaml | 2 +- .../method_calls/special_cases/method_<=.yaml | 2 +- .../method_calls/special_cases/method_==.yaml | 4 +- .../special_cases/method_===.yaml | 2 +- .../method_calls/special_cases/method_>.yaml | 2 +- .../method_calls/special_cases/method_>=.yaml | 2 +- .../method_calls/special_cases/method_>>.yaml | 2 +- .../special_cases/method_nil?.yaml | 2 +- .../special_cases/method_slash.yaml | 2 +- .../method_calls/special_cases/method_|.yaml | 2 +- .../operators/+=/attribute_assignment.yaml | 2 +- ...ssignment_with_explicit_self_receiver.yaml | 2 +- ...ignment_with_safe_navigation_operator.yaml | 2 +- .../operators/+=/reference_assignment.yaml | 2 +- ...erence_assignment_with_block_argument.yaml | 2 +- ...ssignment_with_explicit_self_receiver.yaml | 2 +- ...ence_assignment_with_multiple_indexes.yaml | 2 +- ...ignment_with_nested_splatted_argument.yaml | 2 +- ...nce_assignment_with_splatted_argument.yaml | 2 +- .../variable_assignments/class_variable.yaml | 2 +- .../+=/variable_assignments/constant.yaml | 2 +- .../constant_fully_qualified.yaml | 2 +- ..._fully_qualified_with_implicit_parent.yaml | 2 +- .../variable_assignments/global_variable.yaml | 2 +- .../instance_variable.yaml | 2 +- .../redefining_optimized_core_methods_spec.rb | 33 +++++- .../java/org/truffleruby/RubyContext.java | 1 + .../org/truffleruby/core/CoreLibrary.java | 8 +- .../core/inlined/CoreMethodAssumptions.java | 20 ++-- .../truffleruby/core/inlined/CoreMethods.java | 2 - .../truffleruby/core/method/MethodEntry.java | 25 ++++- .../truffleruby/core/module/ModuleFields.java | 101 ++++++++---------- 46 files changed, 162 insertions(+), 114 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2836d7f22aa..8bf6cd46b75 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,8 @@ Changes: Memory Footprint: +* Use inlined core method nodes even when modules are prepended to core classes (#3546, @eregon). + # 24.0.0 New features: diff --git a/spec/tags/truffle/redefining_optimized_core_methods_tags.txt b/spec/tags/truffle/redefining_optimized_core_methods_tags.txt index 9c08118b18c..3374b8fe30f 100644 --- a/spec/tags/truffle/redefining_optimized_core_methods_tags.txt +++ b/spec/tags/truffle/redefining_optimized_core_methods_tags.txt @@ -1,2 +1,4 @@ slow:Redefining optimized core methods emits performance warning for redefining core classes methods slow:Prepending a module into a class with optimised methods emits performance warning +slow:Prepending a module into a class with optimised methods keeps the inlined nodes if no optimised method is overridden +slow:Prepending a module into a class with optimised methods emits performance warning if that module overrides an optimised method diff --git a/spec/truffle/parsing/fixtures/case/with_expression_and_when/with_else_branch.yaml b/spec/truffle/parsing/fixtures/case/with_expression_and_when/with_else_branch.yaml index 3d791da6696..78a97522d22 100644 --- a/spec/truffle/parsing/fixtures/case/with_expression_and_when/with_else_branch.yaml +++ b/spec/truffle/parsing/fixtures/case/with_expression_and_when/with_else_branch.yaml @@ -78,7 +78,7 @@ ast: | attributes: assumptions = [Assumption(valid, name=set_trace_func is not used)] flags = 0 - integerCaseEqualAssumption = Assumption(valid, name=inlined Integer#===) + integerCaseEqualAssumption = Assumption(valid, name=core method is not overridden:) parameters = RubyCallNodeParameters{methodName='===', descriptor=NoKeywordArgumentsDescriptor, isSplatted=false, ignoreVisibility=true, isVCall=false, isSafeNavigation=false, isAttrAssign=false} sourceCharIndex = -1 sourceLength = 0 @@ -110,7 +110,7 @@ ast: | attributes: assumptions = [Assumption(valid, name=set_trace_func is not used)] flags = 0 - integerCaseEqualAssumption = Assumption(valid, name=inlined Integer#===) + integerCaseEqualAssumption = Assumption(valid, name=core method is not overridden:) parameters = RubyCallNodeParameters{methodName='===', descriptor=NoKeywordArgumentsDescriptor, isSplatted=false, ignoreVisibility=true, isVCall=false, isSafeNavigation=false, isAttrAssign=false} sourceCharIndex = -1 sourceLength = 0 diff --git a/spec/truffle/parsing/fixtures/case/with_expression_and_when/with_multiple_values.yaml b/spec/truffle/parsing/fixtures/case/with_expression_and_when/with_multiple_values.yaml index 36ed07276dd..bdcc1b97a3a 100644 --- a/spec/truffle/parsing/fixtures/case/with_expression_and_when/with_multiple_values.yaml +++ b/spec/truffle/parsing/fixtures/case/with_expression_and_when/with_multiple_values.yaml @@ -78,7 +78,7 @@ ast: | attributes: assumptions = [Assumption(valid, name=set_trace_func is not used)] flags = 0 - integerCaseEqualAssumption = Assumption(valid, name=inlined Integer#===) + integerCaseEqualAssumption = Assumption(valid, name=core method is not overridden:) parameters = RubyCallNodeParameters{methodName='===', descriptor=NoKeywordArgumentsDescriptor, isSplatted=false, ignoreVisibility=true, isVCall=false, isSafeNavigation=false, isAttrAssign=false} sourceCharIndex = -1 sourceLength = 0 @@ -103,7 +103,7 @@ ast: | attributes: assumptions = [Assumption(valid, name=set_trace_func is not used)] flags = 0 - integerCaseEqualAssumption = Assumption(valid, name=inlined Integer#===) + integerCaseEqualAssumption = Assumption(valid, name=core method is not overridden:) parameters = RubyCallNodeParameters{methodName='===', descriptor=NoKeywordArgumentsDescriptor, isSplatted=false, ignoreVisibility=true, isVCall=false, isSafeNavigation=false, isAttrAssign=false} sourceCharIndex = -1 sourceLength = 0 diff --git a/spec/truffle/parsing/fixtures/case/with_expression_and_when/with_single_value.yaml b/spec/truffle/parsing/fixtures/case/with_expression_and_when/with_single_value.yaml index cbf22cecb15..108e4e57f4f 100644 --- a/spec/truffle/parsing/fixtures/case/with_expression_and_when/with_single_value.yaml +++ b/spec/truffle/parsing/fixtures/case/with_expression_and_when/with_single_value.yaml @@ -77,7 +77,7 @@ ast: | attributes: assumptions = [Assumption(valid, name=set_trace_func is not used)] flags = 0 - integerCaseEqualAssumption = Assumption(valid, name=inlined Integer#===) + integerCaseEqualAssumption = Assumption(valid, name=core method is not overridden:) parameters = RubyCallNodeParameters{methodName='===', descriptor=NoKeywordArgumentsDescriptor, isSplatted=false, ignoreVisibility=true, isVCall=false, isSafeNavigation=false, isAttrAssign=false} sourceCharIndex = -1 sourceLength = 0 @@ -109,7 +109,7 @@ ast: | attributes: assumptions = [Assumption(valid, name=set_trace_func is not used)] flags = 0 - integerCaseEqualAssumption = Assumption(valid, name=inlined Integer#===) + integerCaseEqualAssumption = Assumption(valid, name=core method is not overridden:) parameters = RubyCallNodeParameters{methodName='===', descriptor=NoKeywordArgumentsDescriptor, isSplatted=false, ignoreVisibility=true, isVCall=false, isSafeNavigation=false, isAttrAssign=false} sourceCharIndex = -1 sourceLength = 0 diff --git a/spec/truffle/parsing/fixtures/case/with_expression_and_when/with_string_literal_in_when_clause.yaml b/spec/truffle/parsing/fixtures/case/with_expression_and_when/with_string_literal_in_when_clause.yaml index c2a12d22a12..0fec07b7103 100644 --- a/spec/truffle/parsing/fixtures/case/with_expression_and_when/with_string_literal_in_when_clause.yaml +++ b/spec/truffle/parsing/fixtures/case/with_expression_and_when/with_string_literal_in_when_clause.yaml @@ -20,7 +20,7 @@ ast: | attributes: assumptions = [Assumption(valid, name=set_trace_func is not used)] flags = 0 - integerCaseEqualAssumption = Assumption(valid, name=inlined Integer#===) + integerCaseEqualAssumption = Assumption(valid, name=core method is not overridden:) parameters = RubyCallNodeParameters{methodName='===', descriptor=NoKeywordArgumentsDescriptor, isSplatted=false, ignoreVisibility=true, isVCall=false, isSafeNavigation=false, isAttrAssign=false} sourceCharIndex = -1 sourceLength = 0 diff --git a/spec/truffle/parsing/fixtures/complex_numbers/with_rational_and_imaginary_parts.yaml b/spec/truffle/parsing/fixtures/complex_numbers/with_rational_and_imaginary_parts.yaml index 5330135d2ad..926e02d19f9 100644 --- a/spec/truffle/parsing/fixtures/complex_numbers/with_rational_and_imaginary_parts.yaml +++ b/spec/truffle/parsing/fixtures/complex_numbers/with_rational_and_imaginary_parts.yaml @@ -8,7 +8,7 @@ ruby: | ast: | InlinedAddNodeGen attributes: - assumptions = [Assumption(valid, name=set_trace_func is not used), Assumption(valid, name=inlined Integer#+), Assumption(valid, name=inlined Float#+)] + assumptions = [Assumption(valid, name=set_trace_func is not used), Assumption(valid, name=core method is not overridden:), Assumption(valid, name=core method is not overridden:)] flags = 1 parameters = RubyCallNodeParameters{methodName='+', descriptor=NoKeywordArgumentsDescriptor, isSplatted=false, ignoreVisibility=false, isVCall=false, isSafeNavigation=false, isAttrAssign=false} sourceCharIndex = 0 diff --git a/spec/truffle/parsing/fixtures/method_calls/special_cases/method_%.yaml b/spec/truffle/parsing/fixtures/method_calls/special_cases/method_%.yaml index 2dcbcdf7767..30a0434820d 100644 --- a/spec/truffle/parsing/fixtures/method_calls/special_cases/method_%.yaml +++ b/spec/truffle/parsing/fixtures/method_calls/special_cases/method_%.yaml @@ -6,7 +6,7 @@ ruby: | ast: | InlinedModNodeGen attributes: - assumptions = [Assumption(valid, name=set_trace_func is not used), Assumption(valid, name=inlined Integer#%), Assumption(valid, name=inlined Float#%)] + assumptions = [Assumption(valid, name=set_trace_func is not used), Assumption(valid, name=core method is not overridden:), Assumption(valid, name=core method is not overridden:)] flags = 1 parameters = RubyCallNodeParameters{methodName='%', descriptor=NoKeywordArgumentsDescriptor, isSplatted=false, ignoreVisibility=false, isVCall=false, isSafeNavigation=false, isAttrAssign=false} sourceCharIndex = 0 diff --git a/spec/truffle/parsing/fixtures/method_calls/special_cases/method_&.yaml b/spec/truffle/parsing/fixtures/method_calls/special_cases/method_&.yaml index 9e57b49570b..4fcb98d8c4a 100644 --- a/spec/truffle/parsing/fixtures/method_calls/special_cases/method_&.yaml +++ b/spec/truffle/parsing/fixtures/method_calls/special_cases/method_&.yaml @@ -6,7 +6,7 @@ ruby: | ast: | InlinedBitAndNodeGen attributes: - assumptions = [Assumption(valid, name=set_trace_func is not used), Assumption(valid, name=inlined Integer#&)] + assumptions = [Assumption(valid, name=set_trace_func is not used), Assumption(valid, name=core method is not overridden:)] flags = 1 parameters = RubyCallNodeParameters{methodName='&', descriptor=NoKeywordArgumentsDescriptor, isSplatted=false, ignoreVisibility=false, isVCall=false, isSafeNavigation=false, isAttrAssign=false} sourceCharIndex = 0 diff --git a/spec/truffle/parsing/fixtures/method_calls/special_cases/method_*.yaml b/spec/truffle/parsing/fixtures/method_calls/special_cases/method_*.yaml index cf58ae11446..6137e6cc2d5 100644 --- a/spec/truffle/parsing/fixtures/method_calls/special_cases/method_*.yaml +++ b/spec/truffle/parsing/fixtures/method_calls/special_cases/method_*.yaml @@ -6,7 +6,7 @@ ruby: | ast: | InlinedMulNodeGen attributes: - assumptions = [Assumption(valid, name=set_trace_func is not used), Assumption(valid, name=inlined Integer#*), Assumption(valid, name=inlined Float#*)] + assumptions = [Assumption(valid, name=set_trace_func is not used), Assumption(valid, name=core method is not overridden:), Assumption(valid, name=core method is not overridden:)] flags = 1 parameters = RubyCallNodeParameters{methodName='*', descriptor=NoKeywordArgumentsDescriptor, isSplatted=false, ignoreVisibility=false, isVCall=false, isSafeNavigation=false, isAttrAssign=false} sourceCharIndex = 0 diff --git a/spec/truffle/parsing/fixtures/method_calls/special_cases/method_+.yaml b/spec/truffle/parsing/fixtures/method_calls/special_cases/method_+.yaml index e6b9d9dffc5..4919904bc66 100644 --- a/spec/truffle/parsing/fixtures/method_calls/special_cases/method_+.yaml +++ b/spec/truffle/parsing/fixtures/method_calls/special_cases/method_+.yaml @@ -6,7 +6,7 @@ ruby: | ast: | InlinedAddNodeGen attributes: - assumptions = [Assumption(valid, name=set_trace_func is not used), Assumption(valid, name=inlined Integer#+), Assumption(valid, name=inlined Float#+)] + assumptions = [Assumption(valid, name=set_trace_func is not used), Assumption(valid, name=core method is not overridden:), Assumption(valid, name=core method is not overridden:)] flags = 1 parameters = RubyCallNodeParameters{methodName='+', descriptor=NoKeywordArgumentsDescriptor, isSplatted=false, ignoreVisibility=false, isVCall=false, isSafeNavigation=false, isAttrAssign=false} sourceCharIndex = 0 diff --git a/spec/truffle/parsing/fixtures/method_calls/special_cases/method_-.yaml b/spec/truffle/parsing/fixtures/method_calls/special_cases/method_-.yaml index 3686280658b..f080ac40ce1 100644 --- a/spec/truffle/parsing/fixtures/method_calls/special_cases/method_-.yaml +++ b/spec/truffle/parsing/fixtures/method_calls/special_cases/method_-.yaml @@ -6,7 +6,7 @@ ruby: | ast: | InlinedSubNodeGen attributes: - assumptions = [Assumption(valid, name=set_trace_func is not used), Assumption(valid, name=inlined Integer#-), Assumption(valid, name=inlined Float#-)] + assumptions = [Assumption(valid, name=set_trace_func is not used), Assumption(valid, name=core method is not overridden:), Assumption(valid, name=core method is not overridden:)] flags = 1 parameters = RubyCallNodeParameters{methodName='-', descriptor=NoKeywordArgumentsDescriptor, isSplatted=false, ignoreVisibility=false, isVCall=false, isSafeNavigation=false, isAttrAssign=false} sourceCharIndex = 0 diff --git a/spec/truffle/parsing/fixtures/method_calls/special_cases/method_-@.yaml b/spec/truffle/parsing/fixtures/method_calls/special_cases/method_-@.yaml index 6a8151ebef1..e46dd54e19d 100644 --- a/spec/truffle/parsing/fixtures/method_calls/special_cases/method_-@.yaml +++ b/spec/truffle/parsing/fixtures/method_calls/special_cases/method_-@.yaml @@ -7,7 +7,7 @@ ruby: | ast: | InlinedNegNodeGen attributes: - assumptions = [Assumption(valid, name=set_trace_func is not used), Assumption(valid, name=inlined Integer#-@), Assumption(valid, name=inlined Float#-@)] + assumptions = [Assumption(valid, name=set_trace_func is not used), Assumption(valid, name=core method is not overridden:), Assumption(valid, name=core method is not overridden:)] flags = 1 parameters = RubyCallNodeParameters{methodName='-@', descriptor=NoKeywordArgumentsDescriptor, isSplatted=false, ignoreVisibility=false, isVCall=false, isSafeNavigation=false, isAttrAssign=false} sourceCharIndex = 14 diff --git a/spec/truffle/parsing/fixtures/method_calls/special_cases/method_<.yaml b/spec/truffle/parsing/fixtures/method_calls/special_cases/method_<.yaml index 10e6fe73647..abe162d6479 100644 --- a/spec/truffle/parsing/fixtures/method_calls/special_cases/method_<.yaml +++ b/spec/truffle/parsing/fixtures/method_calls/special_cases/method_<.yaml @@ -6,7 +6,7 @@ ruby: | ast: | InlinedLessThanNodeGen attributes: - assumptions = [Assumption(valid, name=set_trace_func is not used), Assumption(valid, name=inlined Integer#<), Assumption(valid, name=inlined Float#<)] + assumptions = [Assumption(valid, name=set_trace_func is not used), Assumption(valid, name=core method is not overridden:), Assumption(valid, name=core method is not overridden:)] flags = 1 parameters = RubyCallNodeParameters{methodName='<', descriptor=NoKeywordArgumentsDescriptor, isSplatted=false, ignoreVisibility=false, isVCall=false, isSafeNavigation=false, isAttrAssign=false} sourceCharIndex = 0 diff --git a/spec/truffle/parsing/fixtures/method_calls/special_cases/method_<<.yaml b/spec/truffle/parsing/fixtures/method_calls/special_cases/method_<<.yaml index 4f1a87561ab..a9cf3b32a65 100644 --- a/spec/truffle/parsing/fixtures/method_calls/special_cases/method_<<.yaml +++ b/spec/truffle/parsing/fixtures/method_calls/special_cases/method_<<.yaml @@ -6,7 +6,7 @@ ruby: | ast: | InlinedLeftShiftNodeGen attributes: - assumptions = [Assumption(valid, name=set_trace_func is not used), Assumption(valid, name=inlined Integer#<<)] + assumptions = [Assumption(valid, name=set_trace_func is not used), Assumption(valid, name=core method is not overridden:)] flags = 1 parameters = RubyCallNodeParameters{methodName='<<', descriptor=NoKeywordArgumentsDescriptor, isSplatted=false, ignoreVisibility=false, isVCall=false, isSafeNavigation=false, isAttrAssign=false} sourceCharIndex = 0 diff --git a/spec/truffle/parsing/fixtures/method_calls/special_cases/method_<=.yaml b/spec/truffle/parsing/fixtures/method_calls/special_cases/method_<=.yaml index 4d48b51d630..43ea1613123 100644 --- a/spec/truffle/parsing/fixtures/method_calls/special_cases/method_<=.yaml +++ b/spec/truffle/parsing/fixtures/method_calls/special_cases/method_<=.yaml @@ -6,7 +6,7 @@ ruby: | ast: | InlinedLessOrEqualNodeGen attributes: - assumptions = [Assumption(valid, name=set_trace_func is not used), Assumption(valid, name=inlined Integer#<=), Assumption(valid, name=inlined Float#<=)] + assumptions = [Assumption(valid, name=set_trace_func is not used), Assumption(valid, name=core method is not overridden:), Assumption(valid, name=core method is not overridden:)] flags = 1 parameters = RubyCallNodeParameters{methodName='<=', descriptor=NoKeywordArgumentsDescriptor, isSplatted=false, ignoreVisibility=false, isVCall=false, isSafeNavigation=false, isAttrAssign=false} sourceCharIndex = 0 diff --git a/spec/truffle/parsing/fixtures/method_calls/special_cases/method_==.yaml b/spec/truffle/parsing/fixtures/method_calls/special_cases/method_==.yaml index 0cdb0a2e78f..134be3efa46 100644 --- a/spec/truffle/parsing/fixtures/method_calls/special_cases/method_==.yaml +++ b/spec/truffle/parsing/fixtures/method_calls/special_cases/method_==.yaml @@ -8,8 +8,8 @@ ast: | attributes: assumptions = [Assumption(valid, name=set_trace_func is not used)] flags = 1 - floatEqualAssumption = Assumption(valid, name=inlined Float#==) - integerEqualAssumption = Assumption(valid, name=inlined Integer#==) + floatEqualAssumption = Assumption(valid, name=core method is not overridden:) + integerEqualAssumption = Assumption(valid, name=core method is not overridden:) parameters = RubyCallNodeParameters{methodName='==', descriptor=NoKeywordArgumentsDescriptor, isSplatted=false, ignoreVisibility=false, isVCall=false, isSafeNavigation=false, isAttrAssign=false} sourceCharIndex = 0 sourceLength = 6 diff --git a/spec/truffle/parsing/fixtures/method_calls/special_cases/method_===.yaml b/spec/truffle/parsing/fixtures/method_calls/special_cases/method_===.yaml index fccf885155e..4829c7aafa5 100644 --- a/spec/truffle/parsing/fixtures/method_calls/special_cases/method_===.yaml +++ b/spec/truffle/parsing/fixtures/method_calls/special_cases/method_===.yaml @@ -8,7 +8,7 @@ ast: | attributes: assumptions = [Assumption(valid, name=set_trace_func is not used)] flags = 1 - integerCaseEqualAssumption = Assumption(valid, name=inlined Integer#===) + integerCaseEqualAssumption = Assumption(valid, name=core method is not overridden:) parameters = RubyCallNodeParameters{methodName='===', descriptor=NoKeywordArgumentsDescriptor, isSplatted=false, ignoreVisibility=false, isVCall=false, isSafeNavigation=false, isAttrAssign=false} sourceCharIndex = 0 sourceLength = 7 diff --git a/spec/truffle/parsing/fixtures/method_calls/special_cases/method_>.yaml b/spec/truffle/parsing/fixtures/method_calls/special_cases/method_>.yaml index a7f2db0e7bf..6ea19b3322d 100644 --- a/spec/truffle/parsing/fixtures/method_calls/special_cases/method_>.yaml +++ b/spec/truffle/parsing/fixtures/method_calls/special_cases/method_>.yaml @@ -6,7 +6,7 @@ ruby: | ast: | InlinedGreaterThanNodeGen attributes: - assumptions = [Assumption(valid, name=set_trace_func is not used), Assumption(valid, name=inlined Integer#>), Assumption(valid, name=inlined Float#>)] + assumptions = [Assumption(valid, name=set_trace_func is not used), Assumption(valid, name=core method is not overridden:), Assumption(valid, name=core method is not overridden:)] flags = 1 parameters = RubyCallNodeParameters{methodName='>', descriptor=NoKeywordArgumentsDescriptor, isSplatted=false, ignoreVisibility=false, isVCall=false, isSafeNavigation=false, isAttrAssign=false} sourceCharIndex = 0 diff --git a/spec/truffle/parsing/fixtures/method_calls/special_cases/method_>=.yaml b/spec/truffle/parsing/fixtures/method_calls/special_cases/method_>=.yaml index 28ba4d29369..17caa8d469c 100644 --- a/spec/truffle/parsing/fixtures/method_calls/special_cases/method_>=.yaml +++ b/spec/truffle/parsing/fixtures/method_calls/special_cases/method_>=.yaml @@ -6,7 +6,7 @@ ruby: | ast: | InlinedGreaterOrEqualNodeGen attributes: - assumptions = [Assumption(valid, name=set_trace_func is not used), Assumption(valid, name=inlined Integer#>=), Assumption(valid, name=inlined Float#>=)] + assumptions = [Assumption(valid, name=set_trace_func is not used), Assumption(valid, name=core method is not overridden:), Assumption(valid, name=core method is not overridden:)] flags = 1 parameters = RubyCallNodeParameters{methodName='>=', descriptor=NoKeywordArgumentsDescriptor, isSplatted=false, ignoreVisibility=false, isVCall=false, isSafeNavigation=false, isAttrAssign=false} sourceCharIndex = 0 diff --git a/spec/truffle/parsing/fixtures/method_calls/special_cases/method_>>.yaml b/spec/truffle/parsing/fixtures/method_calls/special_cases/method_>>.yaml index 354f6c40fb1..8dd4cf03ad4 100644 --- a/spec/truffle/parsing/fixtures/method_calls/special_cases/method_>>.yaml +++ b/spec/truffle/parsing/fixtures/method_calls/special_cases/method_>>.yaml @@ -6,7 +6,7 @@ ruby: | ast: | InlinedRightShiftNodeGen attributes: - assumptions = [Assumption(valid, name=set_trace_func is not used), Assumption(valid, name=inlined Integer#>>)] + assumptions = [Assumption(valid, name=set_trace_func is not used), Assumption(valid, name=core method is not overridden:)] flags = 1 parameters = RubyCallNodeParameters{methodName='>>', descriptor=NoKeywordArgumentsDescriptor, isSplatted=false, ignoreVisibility=false, isVCall=false, isSafeNavigation=false, isAttrAssign=false} sourceCharIndex = 0 diff --git a/spec/truffle/parsing/fixtures/method_calls/special_cases/method_nil?.yaml b/spec/truffle/parsing/fixtures/method_calls/special_cases/method_nil?.yaml index 5deceed44b4..44865a2e14c 100644 --- a/spec/truffle/parsing/fixtures/method_calls/special_cases/method_nil?.yaml +++ b/spec/truffle/parsing/fixtures/method_calls/special_cases/method_nil?.yaml @@ -7,7 +7,7 @@ ruby: | ast: | InlinedIsNilNodeGen attributes: - assumptions = [Assumption(valid, name=set_trace_func is not used), Assumption(valid, name=inlined Nil#nil?)] + assumptions = [Assumption(valid, name=set_trace_func is not used), Assumption(valid, name=core method is not overridden:)] flags = 1 parameters = RubyCallNodeParameters{methodName='nil?', descriptor=NoKeywordArgumentsDescriptor, isSplatted=false, ignoreVisibility=false, isVCall=false, isSafeNavigation=false, isAttrAssign=false} sourceCharIndex = 14 diff --git a/spec/truffle/parsing/fixtures/method_calls/special_cases/method_slash.yaml b/spec/truffle/parsing/fixtures/method_calls/special_cases/method_slash.yaml index df2d58fa7e2..508c79fa6d9 100644 --- a/spec/truffle/parsing/fixtures/method_calls/special_cases/method_slash.yaml +++ b/spec/truffle/parsing/fixtures/method_calls/special_cases/method_slash.yaml @@ -6,7 +6,7 @@ ruby: | ast: | InlinedDivNodeGen attributes: - assumptions = [Assumption(valid, name=set_trace_func is not used), Assumption(valid, name=inlined Integer#/), Assumption(valid, name=inlined Float#/)] + assumptions = [Assumption(valid, name=set_trace_func is not used), Assumption(valid, name=core method is not overridden:), Assumption(valid, name=core method is not overridden:)] flags = 1 parameters = RubyCallNodeParameters{methodName='/', descriptor=NoKeywordArgumentsDescriptor, isSplatted=false, ignoreVisibility=false, isVCall=false, isSafeNavigation=false, isAttrAssign=false} sourceCharIndex = 0 diff --git a/spec/truffle/parsing/fixtures/method_calls/special_cases/method_|.yaml b/spec/truffle/parsing/fixtures/method_calls/special_cases/method_|.yaml index c5e80445f24..36f5d4cf476 100644 --- a/spec/truffle/parsing/fixtures/method_calls/special_cases/method_|.yaml +++ b/spec/truffle/parsing/fixtures/method_calls/special_cases/method_|.yaml @@ -6,7 +6,7 @@ ruby: | ast: | InlinedBitOrNodeGen attributes: - assumptions = [Assumption(valid, name=set_trace_func is not used), Assumption(valid, name=inlined Integer#|)] + assumptions = [Assumption(valid, name=set_trace_func is not used), Assumption(valid, name=core method is not overridden:)] flags = 1 parameters = RubyCallNodeParameters{methodName='|', descriptor=NoKeywordArgumentsDescriptor, isSplatted=false, ignoreVisibility=false, isVCall=false, isSafeNavigation=false, isAttrAssign=false} sourceCharIndex = 0 diff --git a/spec/truffle/parsing/fixtures/operators/+=/attribute_assignment.yaml b/spec/truffle/parsing/fixtures/operators/+=/attribute_assignment.yaml index 92bbb64c99e..9dba50d0aa6 100644 --- a/spec/truffle/parsing/fixtures/operators/+=/attribute_assignment.yaml +++ b/spec/truffle/parsing/fixtures/operators/+=/attribute_assignment.yaml @@ -104,7 +104,7 @@ ast: | arguments = [ InlinedAddNodeGen attributes: - assumptions = [Assumption(valid, name=set_trace_func is not used), Assumption(valid, name=inlined Integer#+), Assumption(valid, name=inlined Float#+)] + assumptions = [Assumption(valid, name=set_trace_func is not used), Assumption(valid, name=core method is not overridden:), Assumption(valid, name=core method is not overridden:)] flags = 0 parameters = RubyCallNodeParameters{methodName='+', descriptor=NoKeywordArgumentsDescriptor, isSplatted=false, ignoreVisibility=false, isVCall=false, isSafeNavigation=false, isAttrAssign=false} sourceCharIndex = 0 diff --git a/spec/truffle/parsing/fixtures/operators/+=/attribute_assignment_with_explicit_self_receiver.yaml b/spec/truffle/parsing/fixtures/operators/+=/attribute_assignment_with_explicit_self_receiver.yaml index ee9d0325ede..cde263ac9ad 100644 --- a/spec/truffle/parsing/fixtures/operators/+=/attribute_assignment_with_explicit_self_receiver.yaml +++ b/spec/truffle/parsing/fixtures/operators/+=/attribute_assignment_with_explicit_self_receiver.yaml @@ -64,7 +64,7 @@ ast: | arguments = [ InlinedAddNodeGen attributes: - assumptions = [Assumption(valid, name=set_trace_func is not used), Assumption(valid, name=inlined Integer#+), Assumption(valid, name=inlined Float#+)] + assumptions = [Assumption(valid, name=set_trace_func is not used), Assumption(valid, name=core method is not overridden:), Assumption(valid, name=core method is not overridden:)] flags = 0 parameters = RubyCallNodeParameters{methodName='+', descriptor=NoKeywordArgumentsDescriptor, isSplatted=false, ignoreVisibility=false, isVCall=false, isSafeNavigation=false, isAttrAssign=false} sourceCharIndex = 0 diff --git a/spec/truffle/parsing/fixtures/operators/+=/attribute_assignment_with_safe_navigation_operator.yaml b/spec/truffle/parsing/fixtures/operators/+=/attribute_assignment_with_safe_navigation_operator.yaml index 9841515d8ac..76b29c62531 100644 --- a/spec/truffle/parsing/fixtures/operators/+=/attribute_assignment_with_safe_navigation_operator.yaml +++ b/spec/truffle/parsing/fixtures/operators/+=/attribute_assignment_with_safe_navigation_operator.yaml @@ -136,7 +136,7 @@ ast: | arguments = [ InlinedAddNodeGen attributes: - assumptions = [Assumption(valid, name=set_trace_func is not used), Assumption(valid, name=inlined Integer#+), Assumption(valid, name=inlined Float#+)] + assumptions = [Assumption(valid, name=set_trace_func is not used), Assumption(valid, name=core method is not overridden:), Assumption(valid, name=core method is not overridden:)] flags = 0 parameters = RubyCallNodeParameters{methodName='+', descriptor=NoKeywordArgumentsDescriptor, isSplatted=false, ignoreVisibility=false, isVCall=false, isSafeNavigation=false, isAttrAssign=false} sourceCharIndex = 0 diff --git a/spec/truffle/parsing/fixtures/operators/+=/reference_assignment.yaml b/spec/truffle/parsing/fixtures/operators/+=/reference_assignment.yaml index 76085591230..3edf3e8e4c2 100644 --- a/spec/truffle/parsing/fixtures/operators/+=/reference_assignment.yaml +++ b/spec/truffle/parsing/fixtures/operators/+=/reference_assignment.yaml @@ -119,7 +119,7 @@ ast: | operand2Node_ = InlinedAddNodeGen attributes: - assumptions = [Assumption(valid, name=set_trace_func is not used), Assumption(valid, name=inlined Integer#+), Assumption(valid, name=inlined Float#+)] + assumptions = [Assumption(valid, name=set_trace_func is not used), Assumption(valid, name=core method is not overridden:), Assumption(valid, name=core method is not overridden:)] flags = 0 parameters = RubyCallNodeParameters{methodName='+', descriptor=NoKeywordArgumentsDescriptor, isSplatted=false, ignoreVisibility=false, isVCall=false, isSafeNavigation=false, isAttrAssign=false} sourceCharIndex = 0 diff --git a/spec/truffle/parsing/fixtures/operators/+=/reference_assignment_with_block_argument.yaml b/spec/truffle/parsing/fixtures/operators/+=/reference_assignment_with_block_argument.yaml index 1584bfc8a2f..f245e8d18a5 100644 --- a/spec/truffle/parsing/fixtures/operators/+=/reference_assignment_with_block_argument.yaml +++ b/spec/truffle/parsing/fixtures/operators/+=/reference_assignment_with_block_argument.yaml @@ -159,7 +159,7 @@ ast: | type = FRAME_LOCAL InlinedAddNodeGen attributes: - assumptions = [Assumption(valid, name=set_trace_func is not used), Assumption(valid, name=inlined Integer#+), Assumption(valid, name=inlined Float#+)] + assumptions = [Assumption(valid, name=set_trace_func is not used), Assumption(valid, name=core method is not overridden:), Assumption(valid, name=core method is not overridden:)] flags = 0 parameters = RubyCallNodeParameters{methodName='+', descriptor=NoKeywordArgumentsDescriptor, isSplatted=false, ignoreVisibility=false, isVCall=false, isSafeNavigation=false, isAttrAssign=false} sourceCharIndex = 0 diff --git a/spec/truffle/parsing/fixtures/operators/+=/reference_assignment_with_explicit_self_receiver.yaml b/spec/truffle/parsing/fixtures/operators/+=/reference_assignment_with_explicit_self_receiver.yaml index c52cba222ba..44796922e96 100644 --- a/spec/truffle/parsing/fixtures/operators/+=/reference_assignment_with_explicit_self_receiver.yaml +++ b/spec/truffle/parsing/fixtures/operators/+=/reference_assignment_with_explicit_self_receiver.yaml @@ -83,7 +83,7 @@ ast: | operand2Node_ = InlinedAddNodeGen attributes: - assumptions = [Assumption(valid, name=set_trace_func is not used), Assumption(valid, name=inlined Integer#+), Assumption(valid, name=inlined Float#+)] + assumptions = [Assumption(valid, name=set_trace_func is not used), Assumption(valid, name=core method is not overridden:), Assumption(valid, name=core method is not overridden:)] flags = 0 parameters = RubyCallNodeParameters{methodName='+', descriptor=NoKeywordArgumentsDescriptor, isSplatted=false, ignoreVisibility=false, isVCall=false, isSafeNavigation=false, isAttrAssign=false} sourceCharIndex = 0 diff --git a/spec/truffle/parsing/fixtures/operators/+=/reference_assignment_with_multiple_indexes.yaml b/spec/truffle/parsing/fixtures/operators/+=/reference_assignment_with_multiple_indexes.yaml index 5962cc10cc3..4b6bb4242d3 100644 --- a/spec/truffle/parsing/fixtures/operators/+=/reference_assignment_with_multiple_indexes.yaml +++ b/spec/truffle/parsing/fixtures/operators/+=/reference_assignment_with_multiple_indexes.yaml @@ -162,7 +162,7 @@ ast: | type = FRAME_LOCAL InlinedAddNodeGen attributes: - assumptions = [Assumption(valid, name=set_trace_func is not used), Assumption(valid, name=inlined Integer#+), Assumption(valid, name=inlined Float#+)] + assumptions = [Assumption(valid, name=set_trace_func is not used), Assumption(valid, name=core method is not overridden:), Assumption(valid, name=core method is not overridden:)] flags = 0 parameters = RubyCallNodeParameters{methodName='+', descriptor=NoKeywordArgumentsDescriptor, isSplatted=false, ignoreVisibility=false, isVCall=false, isSafeNavigation=false, isAttrAssign=false} sourceCharIndex = 0 diff --git a/spec/truffle/parsing/fixtures/operators/+=/reference_assignment_with_nested_splatted_argument.yaml b/spec/truffle/parsing/fixtures/operators/+=/reference_assignment_with_nested_splatted_argument.yaml index b2376183510..699b2d90c3e 100644 --- a/spec/truffle/parsing/fixtures/operators/+=/reference_assignment_with_nested_splatted_argument.yaml +++ b/spec/truffle/parsing/fixtures/operators/+=/reference_assignment_with_nested_splatted_argument.yaml @@ -172,7 +172,7 @@ ast: | values = [ InlinedAddNodeGen attributes: - assumptions = [Assumption(valid, name=set_trace_func is not used), Assumption(valid, name=inlined Integer#+), Assumption(valid, name=inlined Float#+)] + assumptions = [Assumption(valid, name=set_trace_func is not used), Assumption(valid, name=core method is not overridden:), Assumption(valid, name=core method is not overridden:)] flags = 0 parameters = RubyCallNodeParameters{methodName='+', descriptor=NoKeywordArgumentsDescriptor, isSplatted=false, ignoreVisibility=false, isVCall=false, isSafeNavigation=false, isAttrAssign=false} sourceCharIndex = 0 diff --git a/spec/truffle/parsing/fixtures/operators/+=/reference_assignment_with_splatted_argument.yaml b/spec/truffle/parsing/fixtures/operators/+=/reference_assignment_with_splatted_argument.yaml index 2959b6b2f4a..3648b9eb0b2 100644 --- a/spec/truffle/parsing/fixtures/operators/+=/reference_assignment_with_splatted_argument.yaml +++ b/spec/truffle/parsing/fixtures/operators/+=/reference_assignment_with_splatted_argument.yaml @@ -170,7 +170,7 @@ ast: | values = [ InlinedAddNodeGen attributes: - assumptions = [Assumption(valid, name=set_trace_func is not used), Assumption(valid, name=inlined Integer#+), Assumption(valid, name=inlined Float#+)] + assumptions = [Assumption(valid, name=set_trace_func is not used), Assumption(valid, name=core method is not overridden:), Assumption(valid, name=core method is not overridden:)] flags = 0 parameters = RubyCallNodeParameters{methodName='+', descriptor=NoKeywordArgumentsDescriptor, isSplatted=false, ignoreVisibility=false, isVCall=false, isSafeNavigation=false, isAttrAssign=false} sourceCharIndex = 0 diff --git a/spec/truffle/parsing/fixtures/operators/+=/variable_assignments/class_variable.yaml b/spec/truffle/parsing/fixtures/operators/+=/variable_assignments/class_variable.yaml index 52c5a49fac9..ba72d196f23 100644 --- a/spec/truffle/parsing/fixtures/operators/+=/variable_assignments/class_variable.yaml +++ b/spec/truffle/parsing/fixtures/operators/+=/variable_assignments/class_variable.yaml @@ -25,7 +25,7 @@ ast: | rhs = InlinedAddNodeGen attributes: - assumptions = [Assumption(valid, name=set_trace_func is not used), Assumption(valid, name=inlined Integer#+), Assumption(valid, name=inlined Float#+)] + assumptions = [Assumption(valid, name=set_trace_func is not used), Assumption(valid, name=core method is not overridden:), Assumption(valid, name=core method is not overridden:)] flags = 0 parameters = RubyCallNodeParameters{methodName='+', descriptor=NoKeywordArgumentsDescriptor, isSplatted=false, ignoreVisibility=false, isVCall=false, isSafeNavigation=false, isAttrAssign=false} sourceCharIndex = 0 diff --git a/spec/truffle/parsing/fixtures/operators/+=/variable_assignments/constant.yaml b/spec/truffle/parsing/fixtures/operators/+=/variable_assignments/constant.yaml index 82c79b8e3af..c2b2d3dd61f 100644 --- a/spec/truffle/parsing/fixtures/operators/+=/variable_assignments/constant.yaml +++ b/spec/truffle/parsing/fixtures/operators/+=/variable_assignments/constant.yaml @@ -23,7 +23,7 @@ ast: | valueNode = InlinedAddNodeGen attributes: - assumptions = [Assumption(valid, name=set_trace_func is not used), Assumption(valid, name=inlined Integer#+), Assumption(valid, name=inlined Float#+)] + assumptions = [Assumption(valid, name=set_trace_func is not used), Assumption(valid, name=core method is not overridden:), Assumption(valid, name=core method is not overridden:)] flags = 0 parameters = RubyCallNodeParameters{methodName='+', descriptor=NoKeywordArgumentsDescriptor, isSplatted=false, ignoreVisibility=false, isVCall=false, isSafeNavigation=false, isAttrAssign=false} sourceCharIndex = 0 diff --git a/spec/truffle/parsing/fixtures/operators/+=/variable_assignments/constant_fully_qualified.yaml b/spec/truffle/parsing/fixtures/operators/+=/variable_assignments/constant_fully_qualified.yaml index b84bc713f72..a1087b8921e 100644 --- a/spec/truffle/parsing/fixtures/operators/+=/variable_assignments/constant_fully_qualified.yaml +++ b/spec/truffle/parsing/fixtures/operators/+=/variable_assignments/constant_fully_qualified.yaml @@ -74,7 +74,7 @@ ast: | valueNode = InlinedAddNodeGen attributes: - assumptions = [Assumption(valid, name=set_trace_func is not used), Assumption(valid, name=inlined Integer#+), Assumption(valid, name=inlined Float#+)] + assumptions = [Assumption(valid, name=set_trace_func is not used), Assumption(valid, name=core method is not overridden:), Assumption(valid, name=core method is not overridden:)] flags = 0 parameters = RubyCallNodeParameters{methodName='+', descriptor=NoKeywordArgumentsDescriptor, isSplatted=false, ignoreVisibility=false, isVCall=false, isSafeNavigation=false, isAttrAssign=false} sourceCharIndex = 0 diff --git a/spec/truffle/parsing/fixtures/operators/+=/variable_assignments/constant_fully_qualified_with_implicit_parent.yaml b/spec/truffle/parsing/fixtures/operators/+=/variable_assignments/constant_fully_qualified_with_implicit_parent.yaml index 6f049304a5a..eaff5a8c009 100644 --- a/spec/truffle/parsing/fixtures/operators/+=/variable_assignments/constant_fully_qualified_with_implicit_parent.yaml +++ b/spec/truffle/parsing/fixtures/operators/+=/variable_assignments/constant_fully_qualified_with_implicit_parent.yaml @@ -22,7 +22,7 @@ ast: | valueNode = InlinedAddNodeGen attributes: - assumptions = [Assumption(valid, name=set_trace_func is not used), Assumption(valid, name=inlined Integer#+), Assumption(valid, name=inlined Float#+)] + assumptions = [Assumption(valid, name=set_trace_func is not used), Assumption(valid, name=core method is not overridden:), Assumption(valid, name=core method is not overridden:)] flags = 0 parameters = RubyCallNodeParameters{methodName='+', descriptor=NoKeywordArgumentsDescriptor, isSplatted=false, ignoreVisibility=false, isVCall=false, isSafeNavigation=false, isAttrAssign=false} sourceCharIndex = 0 diff --git a/spec/truffle/parsing/fixtures/operators/+=/variable_assignments/global_variable.yaml b/spec/truffle/parsing/fixtures/operators/+=/variable_assignments/global_variable.yaml index d185309fe6e..cbfb6719711 100644 --- a/spec/truffle/parsing/fixtures/operators/+=/variable_assignments/global_variable.yaml +++ b/spec/truffle/parsing/fixtures/operators/+=/variable_assignments/global_variable.yaml @@ -21,7 +21,7 @@ ast: | valueNode_ = InlinedAddNodeGen attributes: - assumptions = [Assumption(valid, name=set_trace_func is not used), Assumption(valid, name=inlined Integer#+), Assumption(valid, name=inlined Float#+)] + assumptions = [Assumption(valid, name=set_trace_func is not used), Assumption(valid, name=core method is not overridden:), Assumption(valid, name=core method is not overridden:)] flags = 0 parameters = RubyCallNodeParameters{methodName='+', descriptor=NoKeywordArgumentsDescriptor, isSplatted=false, ignoreVisibility=false, isVCall=false, isSafeNavigation=false, isAttrAssign=false} sourceCharIndex = 0 diff --git a/spec/truffle/parsing/fixtures/operators/+=/variable_assignments/instance_variable.yaml b/spec/truffle/parsing/fixtures/operators/+=/variable_assignments/instance_variable.yaml index bad0a006d76..69b4d10c397 100644 --- a/spec/truffle/parsing/fixtures/operators/+=/variable_assignments/instance_variable.yaml +++ b/spec/truffle/parsing/fixtures/operators/+=/variable_assignments/instance_variable.yaml @@ -21,7 +21,7 @@ ast: | rhs = InlinedAddNodeGen attributes: - assumptions = [Assumption(valid, name=set_trace_func is not used), Assumption(valid, name=inlined Integer#+), Assumption(valid, name=inlined Float#+)] + assumptions = [Assumption(valid, name=set_trace_func is not used), Assumption(valid, name=core method is not overridden:), Assumption(valid, name=core method is not overridden:)] flags = 0 parameters = RubyCallNodeParameters{methodName='+', descriptor=NoKeywordArgumentsDescriptor, isSplatted=false, ignoreVisibility=false, isVCall=false, isSafeNavigation=false, isAttrAssign=false} sourceCharIndex = 0 diff --git a/spec/truffle/redefining_optimized_core_methods_spec.rb b/spec/truffle/redefining_optimized_core_methods_spec.rb index 7f1a90ead2f..49048bbcd78 100644 --- a/spec/truffle/redefining_optimized_core_methods_spec.rb +++ b/spec/truffle/redefining_optimized_core_methods_spec.rb @@ -189,7 +189,15 @@ def to_proc(...) end CODE - code = [prolog, code_for_integer_class, code_for_float_class, code_for_nil_class, code_for_symbol_class].join("\n") + code_for_refining = <<~CODE + module R + refine Integer do + def +; end + end + end + CODE + + code = [prolog, code_for_integer_class, code_for_float_class, code_for_nil_class, code_for_symbol_class, code_for_refining].join("\n") output = ruby_exe(code, args: "2>&1") output.should.include?("warning: Redefining 'Integer#+' disables interpreter and JIT optimizations") @@ -226,15 +234,34 @@ def to_proc(...) output.should.include?("warning: Redefining 'NilClass#nil?' disables interpreter and JIT optimizations") output.should.include?("warning: Redefining 'Symbol#to_proc' disables interpreter and JIT optimizations") + + output.should.include?("warning: Refining 'Integer#+' disables interpreter and JIT optimizations") end end describe "Prepending a module into a class with optimised methods" do - it "emits performance warning" do + it "keeps the inlined nodes if no optimised method is overridden" do + code = <<~CODE + Warning[:performance] = true + + module M + end + + class Integer + prepend M + end + CODE + + ruby_exe(code, args: "2>&1").should == '' + end + + it "emits performance warning if that module overrides an optimised method" do code = <<~CODE Warning[:performance] = true module M + def <=> + end end class Integer @@ -242,6 +269,6 @@ class Integer end CODE - ruby_exe(code, args: "2>&1").should.include?("warning: Prepending a module to Integer disables interpreter and JIT optimizations") + ruby_exe(code, args: "2>&1").should.include?("warning: Redefining 'Integer#<=>' disables interpreter and JIT optimizations") end end diff --git a/src/main/java/org/truffleruby/RubyContext.java b/src/main/java/org/truffleruby/RubyContext.java index 88450263de8..81b813df144 100644 --- a/src/main/java/org/truffleruby/RubyContext.java +++ b/src/main/java/org/truffleruby/RubyContext.java @@ -221,6 +221,7 @@ public RubyContext(RubyLanguage language, TruffleLanguage.Env env) { coreLibrary = new CoreLibrary(this, language); nativeConfiguration = NativeConfiguration.loadNativeConfiguration(this); coreLibrary.initialize(); + language.coreMethodAssumptions.registerAssumptions(coreLibrary); valueWrapperManager = new ValueWrapperManager(); Metrics.printTime("after-create-core-library"); diff --git a/src/main/java/org/truffleruby/core/CoreLibrary.java b/src/main/java/org/truffleruby/core/CoreLibrary.java index f0237051014..5853b2f1e77 100644 --- a/src/main/java/org/truffleruby/core/CoreLibrary.java +++ b/src/main/java/org/truffleruby/core/CoreLibrary.java @@ -268,13 +268,16 @@ private static SourceSection initCoreSourceSection() { return source.createUnavailableSection(); } - private enum State { + public enum State { + CREATED, + /** Loading methods defined in Java */ INITIALIZING, + /** Loading methods defined in Ruby */ LOADING_RUBY_CORE, LOADED } - private State state = State.INITIALIZING; + public State state = State.CREATED; private final SingletonClassNode node; @@ -601,6 +604,7 @@ public void initialize() { } public void loadCoreNodes() { + state = State.INITIALIZING; final CoreMethodNodeManager coreMethodNodeManager = new CoreMethodNodeManager(context); coreMethodNodeManager.loadCoreMethodNodes(); diff --git a/src/main/java/org/truffleruby/core/inlined/CoreMethodAssumptions.java b/src/main/java/org/truffleruby/core/inlined/CoreMethodAssumptions.java index 79b50e5fb36..26443c20bff 100644 --- a/src/main/java/org/truffleruby/core/inlined/CoreMethodAssumptions.java +++ b/src/main/java/org/truffleruby/core/inlined/CoreMethodAssumptions.java @@ -15,15 +15,19 @@ import com.oracle.truffle.api.Assumption; +import com.oracle.truffle.api.nodes.Node; +import org.truffleruby.RubyContext; import org.truffleruby.RubyLanguage; import org.truffleruby.core.CoreLibrary; import org.truffleruby.core.klass.RubyClass; +import org.truffleruby.core.method.MethodEntry; import org.truffleruby.core.module.ModuleFields; import org.truffleruby.language.RubyContextSourceNode; import org.truffleruby.language.RubyNode; import org.truffleruby.language.dispatch.RubyCallNode; import org.truffleruby.language.dispatch.RubyCallNodeParameters; import org.truffleruby.language.methods.BlockDefinitionNode; +import org.truffleruby.language.methods.InternalMethod; /** We inline basic operations directly in the AST (instead of a method call) as it makes little sense to compile them * in isolation without the surrounding method and it delays more interesting compilations by filling the compilation @@ -45,8 +49,7 @@ * Two strategies are used to check method re-definition. *
  • If the class is a leaf class (there cannot be instances of a subclass of that class), then we only need to check * the receiver is an instance of that class and register an Assumption for the given method name (see - * {@link ModuleFields#registerAssumption(String, com.oracle.truffle.api.Assumption)}). In such cases the method must be - * public as we do not check visibility.
  • + * {@link ModuleFields#registerAssumption}). In such cases the method must be public as we do not check visibility. *
  • Otherwise, we need to do a method lookup and verify the method that would be called is the standard definition we * expect.
  • *

    @@ -129,7 +132,6 @@ public CoreMethodAssumptions(RubyLanguage language) { nilClassIsNilAssumption = registerAssumption((cl) -> cl.nilClass, "Nil", "nil?"); symbolToProcAssumption = registerAssumption((cl) -> cl.symbolClass, "Symbol", "to_proc"); - } @FunctionalInterface @@ -138,14 +140,18 @@ public interface ContextGetClass { } private Assumption registerAssumption(ContextGetClass classGetter, String className, String methodName) { - final Assumption assumption = Assumption.create("inlined " + className + "#" + methodName); - classAssumptionsToRegister.add((cl) -> classGetter.apply(cl).fields.registerAssumption(methodName, assumption)); + final Assumption assumption = Assumption.create(MethodEntry.CORE_METHOD_IS_NOT_OVERRIDDEN); + classAssumptionsToRegister.add((cl) -> { + classGetter.apply(cl).fields.registerAssumption(cl, methodName, assumption); + }); return assumption; } + /** Must be called before any method is defined, otherwise these assumptions will not properly be reused in + * {@link ModuleFields#addMethod(RubyContext, Node, InternalMethod)}. */ public void registerAssumptions(CoreLibrary coreLibrary) { - for (Consumer registerers : classAssumptionsToRegister) { - registerers.accept(coreLibrary); + for (Consumer registerer : classAssumptionsToRegister) { + registerer.accept(coreLibrary); } } diff --git a/src/main/java/org/truffleruby/core/inlined/CoreMethods.java b/src/main/java/org/truffleruby/core/inlined/CoreMethods.java index 0b28e85babb..dcc73242992 100644 --- a/src/main/java/org/truffleruby/core/inlined/CoreMethods.java +++ b/src/main/java/org/truffleruby/core/inlined/CoreMethods.java @@ -53,8 +53,6 @@ public CoreMethods(RubyLanguage language, RubyContext context) { ARRAY_AT = getMethod(arrayClass, "at"); ARRAY_INDEX_GET = getMethod(arrayClass, "[]"); ARRAY_INDEX_SET = getMethod(arrayClass, "[]="); - - language.coreMethodAssumptions.registerAssumptions(context.getCoreLibrary()); } private InternalMethod getMethod(RubyModule module, String name) { diff --git a/src/main/java/org/truffleruby/core/method/MethodEntry.java b/src/main/java/org/truffleruby/core/method/MethodEntry.java index 5b8ffa4c226..2f37e323c6f 100644 --- a/src/main/java/org/truffleruby/core/method/MethodEntry.java +++ b/src/main/java/org/truffleruby/core/method/MethodEntry.java @@ -9,20 +9,32 @@ */ package org.truffleruby.core.method; +import com.oracle.truffle.api.nodes.Node; +import org.truffleruby.RubyContext; import org.truffleruby.core.module.RubyModule; +import org.truffleruby.core.string.StringUtils; +import org.truffleruby.language.PerformanceWarningNode; import org.truffleruby.language.methods.InternalMethod; import com.oracle.truffle.api.Assumption; import org.truffleruby.language.methods.SharedMethodInfo; +import java.util.Objects; + public final class MethodEntry { + public static final String CORE_METHOD_IS_NOT_OVERRIDDEN = "core method is not overridden:"; + private final Assumption assumption; private final InternalMethod method; public MethodEntry(InternalMethod method) { + this(method, Assumption.create("method is not overridden:")); + } + + public MethodEntry(InternalMethod method, Assumption assumption) { assert method != null; - this.assumption = Assumption.create("method is not overridden:"); + this.assumption = Objects.requireNonNull(assumption); this.method = method; } @@ -47,8 +59,17 @@ public InternalMethod getMethod() { return method; } - public void invalidate(RubyModule module, String methodName) { + public void invalidate(RubyContext context, RubyModule module, String methodName, Node node) { assumption.invalidate(SharedMethodInfo.moduleAndMethodName(module, methodName)); + + if (assumption.getName() == CORE_METHOD_IS_NOT_OVERRIDDEN) { + PerformanceWarningNode.warn(context, + StringUtils.format("Redefining '%s#%s' disables interpreter and JIT optimizations", + module.getName(), + methodName), + node); + } + } } diff --git a/src/main/java/org/truffleruby/core/module/ModuleFields.java b/src/main/java/org/truffleruby/core/module/ModuleFields.java index 724cb622e58..4363a22f950 100644 --- a/src/main/java/org/truffleruby/core/module/ModuleFields.java +++ b/src/main/java/org/truffleruby/core/module/ModuleFields.java @@ -30,6 +30,7 @@ import org.truffleruby.RubyLanguage; import org.truffleruby.collections.ConcurrentOperations; import org.truffleruby.collections.ConcurrentWeakSet; +import org.truffleruby.core.CoreLibrary; import org.truffleruby.core.encoding.Encodings; import org.truffleruby.core.encoding.TStringUtils; import org.truffleruby.core.kernel.KernelNodes; @@ -49,6 +50,7 @@ import org.truffleruby.language.control.RaiseException; import org.truffleruby.language.loader.ReentrantLockFreeingMap; import org.truffleruby.language.methods.InternalMethod; +import org.truffleruby.language.methods.SharedMethodInfo; import org.truffleruby.language.objects.IsFrozenNodeGen; import org.truffleruby.language.objects.ObjectGraph; import org.truffleruby.language.objects.ObjectGraphNode; @@ -308,7 +310,7 @@ public void include(RubyContext context, Node currentNode, RubyModule module) { if (ModuleOperations.includesModule(rubyModule, ancestor)) { if (isIncludedModuleBeforeSuperClass(ancestor)) { // Include the modules at the appropriate inclusionPoint - performIncludes(inclusionPoint, modulesToInclude); + performIncludes(context, inclusionPoint, modulesToInclude, currentNode); assert modulesToInclude.isEmpty(); // We need to include the others after that module @@ -324,12 +326,13 @@ public void include(RubyContext context, Node currentNode, RubyModule module) { } } - performIncludes(inclusionPoint, modulesToInclude); + performIncludes(context, inclusionPoint, modulesToInclude, currentNode); - newHierarchyVersion(context, currentNode); + newHierarchyVersion(); } - private void performIncludes(ModuleChain inclusionPoint, Deque moduleAncestors) { + private void performIncludes(RubyContext context, ModuleChain inclusionPoint, Deque moduleAncestors, + Node node) { while (!moduleAncestors.isEmpty()) { RubyModule toInclude = moduleAncestors.pop(); inclusionPoint.insertAfter(toInclude); @@ -340,7 +343,7 @@ private void performIncludes(ModuleChain inclusionPoint, Deque modul newConstantsVersion(toInclude.fields.getConstantNames()); if (rubyModule instanceof RubyClass) { // M.include(N) just registers N but does nothing for methods until C.include/prepend(M) - newMethodsVersion(toInclude.fields.getMethodNames()); + newMethodsVersion(context, toInclude.fields.getMethodNames(), node); } } } @@ -391,7 +394,7 @@ public void prepend(RubyContext context, Node currentNode, RubyModule module) { moduleToInvalidate.fields.newConstantsVersion(constantsToInvalidate); if (isClass) { // M.prepend(N) just registers N but does nothing for methods until C.prepend/include(M) - moduleToInvalidate.fields.newMethodsVersion(methodsToInvalidate); + moduleToInvalidate.fields.newMethodsVersion(context, methodsToInvalidate, currentNode); } } @@ -402,9 +405,7 @@ public void prepend(RubyContext context, Node currentNode, RubyModule module) { } // If there were already prepended modules, invalidate the first of them - newHierarchyVersion(context, currentNode); - - invalidateBuiltinsAssumptions(context, currentNode); + newHierarchyVersion(); } private List getPrependedModulesAndSelf() { @@ -526,12 +527,14 @@ public RubyConstant removeConstant(RubyContext context, Node currentNode, String @TruffleBoundary public void addMethod(RubyContext context, Node currentNode, InternalMethod method) { + assert context.getCoreLibrary().state != CoreLibrary.State.CREATED : "should not add methods yet"; assert ModuleOperations.canBindMethodTo(method, rubyModule) || ModuleOperations.assignableTo(context.getCoreLibrary().objectClass, method.getDeclaringModule()) || // TODO (pitr-ch 24-Jul-2016): find out why undefined methods sometimes do not match above assertion // e.g. "block in _routes route_set.rb:525" in rails/actionpack/lib/action_dispatch/routing/ (method.isUndefined() && methods.get(method.getName()) != null); + final String name = method.getName(); checkFrozen(context, currentNode); method = method.withOwner(rubyModule); @@ -544,21 +547,27 @@ public void addMethod(RubyContext context, Node currentNode, InternalMethod meth } } - MethodEntry previousMethodEntry = methods.put(method.getName(), new MethodEntry(method)); + MethodEntry methodEntry; + Assumption assumption; + if (context.getCoreLibrary().isInitializing() && (assumption = inlinedBuiltinsAssumptions.get(name)) != null) { + methodEntry = new MethodEntry(method, assumption); + } else { + methodEntry = new MethodEntry(method); + } + + MethodEntry previousMethodEntry = methods.put(name, methodEntry); if (!context.getCoreLibrary().isInitializing()) { if (previousMethodEntry != null) { - previousMethodEntry.invalidate(rubyModule, method.getName()); + previousMethodEntry.invalidate(context, rubyModule, name, currentNode); } if (includedBy != null) { - invalidateMethodIncludedBy(method.getName()); + invalidateMethodIncludedBy(context, name, currentNode); } - // invalidate assumptions to not use an AST-inlined methods - changedMethod(context, method.getName(), currentNode); if (refinedModule != null) { - refinedModule.fields.changedMethod(context, method.getName(), currentNode); + refinedModule.fields.refinedMethod(context, method.getName(), currentNode); } } @@ -569,7 +578,7 @@ public void addMethod(RubyContext context, Node currentNode, InternalMethod meth if (previousMethodEntry == null || previousMethodEntry.getMethod() == null || previousMethodEntry.getMethod().getSharedMethodInfo() != method.getSharedMethodInfo()) { - final RubySymbol methodSymbol = context.getLanguageSlow().getSymbol(method.getName()); + final RubySymbol methodSymbol = context.getLanguageSlow().getSymbol(name); if (RubyGuards.isSingletonClass(rubyModule)) { RubyDynamicObject receiver = ((RubyClass) rubyModule).attached; RubyContext.send(currentNode, receiver, "singleton_method_added", methodSymbol); @@ -580,13 +589,13 @@ public void addMethod(RubyContext context, Node currentNode, InternalMethod meth } // track if ever a custom Module.const_add callback is defined and ignore a default one - if (context.getCoreLibrary().isLoaded() && method.getName().equals("const_added")) { - RubyLanguage.getCurrentContext().constAddedIsDefined(); + if (context.getCoreLibrary().isLoaded() && name.equals("const_added")) { + context.constAddedIsDefined(); } } @TruffleBoundary - public boolean removeMethod(RubyContext context, String methodName, Node currentNode) { + public boolean removeMethod(RubyContext context, String methodName, Node node) { final InternalMethod method = getMethod(methodName); if (method == null) { return false; @@ -594,10 +603,9 @@ public boolean removeMethod(RubyContext context, String methodName, Node current MethodEntry removedEntry = methods.remove(methodName); if (removedEntry != null) { - removedEntry.invalidate(rubyModule, methodName); + removedEntry.invalidate(context, rubyModule, methodName, node); } - changedMethod(context, methodName, currentNode); return true; } @@ -842,19 +850,15 @@ public String toString() { return super.toString() + "(" + getName() + ")"; } - public void newHierarchyVersion(RubyContext context, Node currentNode) { + public void newHierarchyVersion() { if (!isClass()) { hierarchyUnmodifiedAssumption.invalidate(getName()); } - - if (isRefinement()) { - getRefinedModule().fields.invalidateBuiltinsAssumptions(context, currentNode); - } } - private void invalidateMethodIncludedBy(String method) { + private void invalidateMethodIncludedBy(RubyContext context, String method, Node node) { for (RubyModule module : includedBy) { - module.fields.newMethodVersion(method); + module.fields.newMethodVersion(context, method, node); } } @@ -884,20 +888,20 @@ public void newConstantVersion(String constantToInvalidate) { } } - public void newMethodsVersion(Collection methodsToInvalidate) { + public void newMethodsVersion(RubyContext context, Collection methodsToInvalidate, Node node) { for (String name : methodsToInvalidate) { - newMethodVersion(name); + newMethodVersion(context, name, node); } } - private void newMethodVersion(String methodToInvalidate) { + private void newMethodVersion(RubyContext context, String methodToInvalidate, Node node) { while (true) { final MethodEntry methodEntry = methods.get(methodToInvalidate); if (methodEntry == null) { return; } else { if (methods.replace(methodToInvalidate, methodEntry, methodEntry.withNewAssumption())) { - methodEntry.invalidate(rubyModule, methodToInvalidate); + methodEntry.invalidate(context, rubyModule, methodToInvalidate, node); return; } } @@ -1152,37 +1156,20 @@ public SourceSection getSourceSection() { } /** Registers an Assumption for a given method name, which is invalidated when a method with same name is defined or - * undefined in this class or when a module is prepended to this class. This does not check re-definitions in - * subclasses. */ - public void registerAssumption(String methodName, Assumption assumption) { - assert RubyLanguage.getCurrentContext().getCoreLibrary().isInitializing(); + * undefined in this class or in a prepended module. This does not check re-definitions in subclasses. */ + public void registerAssumption(CoreLibrary coreLibrary, String methodName, Assumption assumption) { + assert coreLibrary.state == CoreLibrary.State.CREATED; Assumption old = inlinedBuiltinsAssumptions.put(methodName, assumption); assert old == null; } - private void changedMethod(RubyContext context, String methodName, Node currentNode) { + private void refinedMethod(RubyContext context, String methodName, Node currentNode) { Assumption assumption = inlinedBuiltinsAssumptions.get(methodName); if (assumption != null) { - assumption.invalidate(); - - PerformanceWarningNode.warn( - context, - StringUtils.format("Redefining '%s#%s' disables interpreter and JIT optimizations", getName(), - methodName), - currentNode); - } - } - - private void invalidateBuiltinsAssumptions(RubyContext context, Node currentNode) { - if (!inlinedBuiltinsAssumptions.isEmpty()) { - for (Assumption assumption : inlinedBuiltinsAssumptions.values()) { - assumption.invalidate(); - } - - PerformanceWarningNode.warn( - context, - StringUtils.format("Prepending a module to %s disables interpreter and JIT optimizations", - getName()), + var moduleAndMethodName = SharedMethodInfo.moduleAndMethodName(rubyModule, methodName); + assumption.invalidate("method is refined: " + moduleAndMethodName); + PerformanceWarningNode.warn(context, + StringUtils.format("Refining '%s' disables interpreter and JIT optimizations", moduleAndMethodName), currentNode); } } From a608e54fdf3857e5b09226839ab67d02bac10472 Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Thu, 25 Apr 2024 17:45:46 +0200 Subject: [PATCH 2/2] Add spec for overriding an optimized method through an already-prepended module --- spec/ruby/core/module/prepend_spec.rb | 20 ++++++++++++++++++++ spec/tags/core/module/prepend_tags.txt | 1 + 2 files changed, 21 insertions(+) diff --git a/spec/ruby/core/module/prepend_spec.rb b/spec/ruby/core/module/prepend_spec.rb index c90fa9700ec..bce4d6f7e67 100644 --- a/spec/ruby/core/module/prepend_spec.rb +++ b/spec/ruby/core/module/prepend_spec.rb @@ -75,6 +75,26 @@ def foo foo.call.should == 'm' end + it "updates the optimized method when a prepended module is updated" do + out = ruby_exe(<<~RUBY) + module M; end + class Integer + prepend M + end + l = -> { 1 + 2 } + p l.call + M.module_eval do + def +(o) + $called = true + super(o) + end + end + p l.call + p $called + RUBY + out.should == "3\n3\ntrue\n" + end + it "updates the method when there is a base included method and the prepended module overrides it" do base_module = Module.new do def foo diff --git a/spec/tags/core/module/prepend_tags.txt b/spec/tags/core/module/prepend_tags.txt index 9de3da58582..b92f114163b 100644 --- a/spec/tags/core/module/prepend_tags.txt +++ b/spec/tags/core/module/prepend_tags.txt @@ -1,3 +1,4 @@ fails:Module#prepend uses only new module when dupping the module fails:Module#prepend prepends a module if it is included in a super class fails:Module#prepend when module already exists in ancestor chain modifies the ancestor chain +slow:Module#prepend updates the optimized method when a prepended module is updated