diff --git a/CHANGELOG.md b/CHANGELOG.md index 4477ac80e93d..8b44851d9d61 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,7 @@ Compatibility: * Add `Enumerator#product` (#3039, @itarato). * Add `Module#const_added` (#3039, @itarato). * Show the pointer size information (if available) in `FFI::Pointer#inspect` (@nirvdrum). +* Implement performance warnings (`Warning[:performance]`) like in CRuby 3.3 (@eregon). Performance: diff --git a/spec/ruby/core/warning/element_reference_spec.rb b/spec/ruby/core/warning/element_reference_spec.rb index 41129e533abb..bd024d19b49c 100644 --- a/spec/ruby/core/warning/element_reference_spec.rb +++ b/spec/ruby/core/warning/element_reference_spec.rb @@ -3,8 +3,8 @@ describe "Warning.[]" do ruby_version_is '2.7.2' do it "returns default values for categories :deprecated and :experimental" do - ruby_exe('p Warning[:deprecated]').chomp.should == "false" - ruby_exe('p Warning[:experimental]').chomp.should == "true" + ruby_exe('p [Warning[:deprecated], Warning[:experimental]]').chomp.should == "[false, true]" + ruby_exe('p [Warning[:deprecated], Warning[:experimental]]', options: "-w").chomp.should == "[true, true]" end end diff --git a/spec/ruby/core/warning/element_set_spec.rb b/spec/ruby/core/warning/element_set_spec.rb index f439ffde0a13..d59a7d4c9e13 100644 --- a/spec/ruby/core/warning/element_set_spec.rb +++ b/spec/ruby/core/warning/element_set_spec.rb @@ -17,6 +17,18 @@ end end + ruby_version_is '3.3' do + it "enables or disables performance warnings" do + original = Warning[:performance] + begin + Warning[:performance] = !original + Warning[:performance].should == !original + ensure + Warning[:performance] = original + end + end + end + it "raises for unknown category" do -> { Warning[:noop] = false }.should raise_error(ArgumentError, /unknown category: noop/) end diff --git a/spec/tags/core/warning/element_reference_tags.txt b/spec/tags/core/warning/element_reference_tags.txt index 27b4c08d53b4..9eb23d826cc2 100644 --- a/spec/tags/core/warning/element_reference_tags.txt +++ b/spec/tags/core/warning/element_reference_tags.txt @@ -1 +1,2 @@ slow:Warning.[] returns default values for categories :deprecated and :experimental +slow:Warning.[] returns default values for :performance category diff --git a/spec/truffleruby.next-specs b/spec/truffleruby.next-specs index c20856c33576..06f5e903e33f 100644 --- a/spec/truffleruby.next-specs +++ b/spec/truffleruby.next-specs @@ -9,35 +9,5 @@ # Use spec/ruby/core/nil/nil_spec.rb as a dummy file to avoid being empty (what causes mspec to error) spec/ruby/core/nil/nil_spec.rb -spec/ruby/core/array/slice_spec.rb -spec/ruby/core/array/element_reference_spec.rb - -spec/ruby/core/hash/shift_spec.rb -spec/ruby/core/range/size_spec.rb - -spec/ruby/core/string/dedup_spec.rb - -spec/ruby/core/string/bytesplice_spec.rb - -spec/ruby/core/string/byteindex_spec.rb -spec/ruby/core/string/byterindex_spec.rb - -spec/ruby/core/queue/deq_spec.rb -spec/ruby/core/queue/pop_spec.rb -spec/ruby/core/queue/shift_spec.rb - -spec/ruby/core/sizedqueue/deq_spec.rb -spec/ruby/core/sizedqueue/pop_spec.rb -spec/ruby/core/sizedqueue/shift_spec.rb -spec/ruby/core/sizedqueue/append_spec.rb -spec/ruby/core/sizedqueue/enq_spec.rb -spec/ruby/core/sizedqueue/push_spec.rb - -spec/ruby/core/module/const_added_spec.rb -spec/ruby/core/module/refinements_spec.rb -spec/ruby/core/module/undefined_instance_methods_spec.rb -spec/ruby/core/refinement/refined_class_spec.rb -spec/ruby/core/module/used_refinements_spec.rb - -spec/ruby/core/thread/each_caller_location_spec.rb -spec/ruby/core/enumerator/product_spec.rb +spec/ruby/core/warning/element_reference_spec.rb +spec/ruby/core/warning/element_set_spec.rb diff --git a/src/launcher/java/org/truffleruby/launcher/CommandLineParser.java b/src/launcher/java/org/truffleruby/launcher/CommandLineParser.java index 0f13abcd1b9e..585c4a5dd79c 100644 --- a/src/launcher/java/org/truffleruby/launcher/CommandLineParser.java +++ b/src/launcher/java/org/truffleruby/launcher/CommandLineParser.java @@ -361,6 +361,12 @@ private void processArgument() throws CommandLineException { case ":no-experimental": config.setOption(OptionsCatalog.WARN_EXPERIMENTAL, false); break; + case ":performance": + config.setOption(OptionsCatalog.WARN_PERFORMANCE, true); + break; + case ":no-performance": + config.setOption(OptionsCatalog.WARN_PERFORMANCE, false); + break; default: LOGGER.warning("unknown warning category: `" + temp.substring(1) + "'"); break; @@ -494,6 +500,7 @@ private void processArgument() throws CommandLineException { private void setAllWarningCategories(boolean value) { config.setOption(OptionsCatalog.WARN_DEPRECATED, value); config.setOption(OptionsCatalog.WARN_EXPERIMENTAL, value); + // WARN_PERFORMANCE is excluded here, it is not set by -w/-W2 on CRuby } private void enableDisableFeature(String name, boolean enable) { diff --git a/src/launcher/java/org/truffleruby/launcher/RubyLauncher.java b/src/launcher/java/org/truffleruby/launcher/RubyLauncher.java index a14748d9362c..50c40fcf8308 100644 --- a/src/launcher/java/org/truffleruby/launcher/RubyLauncher.java +++ b/src/launcher/java/org/truffleruby/launcher/RubyLauncher.java @@ -437,6 +437,7 @@ private static void printHelp(PrintStream out) { out.println("Warning categories:"); out.println(" deprecated deprecated features"); out.println(" experimental experimental features"); + out.println(" performance performance issues"); } // Same as above, but with "ruby -h" diff --git a/src/main/java/org/truffleruby/RubyContext.java b/src/main/java/org/truffleruby/RubyContext.java index c46d231a4819..c24cf2840bdb 100644 --- a/src/main/java/org/truffleruby/RubyContext.java +++ b/src/main/java/org/truffleruby/RubyContext.java @@ -166,6 +166,7 @@ public final class RubyContext { private final AssumedValue warningCategoryDeprecated; private final AssumedValue warningCategoryExperimental; + private final AssumedValue warningCategoryPerformance; private ImmutableRubyString mainScriptName; @@ -189,6 +190,7 @@ public RubyContext(RubyLanguage language, TruffleLanguage.Env env) { warningCategoryDeprecated = new AssumedValue<>(options.WARN_DEPRECATED); warningCategoryExperimental = new AssumedValue<>(options.WARN_EXPERIMENTAL); + warningCategoryPerformance = new AssumedValue<>(options.WARN_PERFORMANCE); safepointManager = new SafepointManager(this); coreExceptions = new CoreExceptions(this, language); @@ -305,6 +307,9 @@ protected boolean patch(Env newEnv) { if (newOptions.WARN_EXPERIMENTAL != oldOptions.WARN_EXPERIMENTAL) { warningCategoryExperimental.set(newOptions.WARN_EXPERIMENTAL); } + if (newOptions.WARN_PERFORMANCE != oldOptions.WARN_PERFORMANCE) { + warningCategoryPerformance.set(newOptions.WARN_PERFORMANCE); + } // Re-read the value of $TZ as it can be different in the new process GetTimeZoneNode.invalidateTZ(); @@ -758,6 +763,10 @@ public AssumedValue getWarningCategoryExperimental() { return warningCategoryExperimental; } + public AssumedValue getWarningCategoryPerformance() { + return warningCategoryPerformance; + } + public PrintStream getEnvOutStream() { return outStream; } diff --git a/src/main/java/org/truffleruby/core/kernel/KernelNodes.java b/src/main/java/org/truffleruby/core/kernel/KernelNodes.java index 7480530ba641..927742a4339c 100644 --- a/src/main/java/org/truffleruby/core/kernel/KernelNodes.java +++ b/src/main/java/org/truffleruby/core/kernel/KernelNodes.java @@ -1851,6 +1851,11 @@ protected boolean getCategoryExperimental(RubySymbol category) { return getContext().getWarningCategoryExperimental().get(); } + @Specialization(guards = "category == coreSymbols().PERFORMANCE") + protected boolean getCategoryPerformance(RubySymbol category) { + return getContext().getWarningCategoryPerformance().get(); + } + } @Primitive(name = "warning_set_category") @@ -1864,6 +1869,8 @@ protected boolean setCategory(RubySymbol category, boolean newValue) { existingValue = getContext().getWarningCategoryDeprecated(); } else if (category == coreSymbols().EXPERIMENTAL) { existingValue = getContext().getWarningCategoryExperimental(); + } else if (category == coreSymbols().PERFORMANCE) { + existingValue = getContext().getWarningCategoryPerformance(); } else { throw CompilerDirectives.shouldNotReachHere("unexpected warning category"); } diff --git a/src/main/java/org/truffleruby/core/symbol/CoreSymbols.java b/src/main/java/org/truffleruby/core/symbol/CoreSymbols.java index 66e065cda5d8..87a7d1d1536d 100644 --- a/src/main/java/org/truffleruby/core/symbol/CoreSymbols.java +++ b/src/main/java/org/truffleruby/core/symbol/CoreSymbols.java @@ -40,6 +40,7 @@ public final class CoreSymbols { public final RubySymbol ON_BLOCKING = createRubySymbol("on_blocking"); public final RubySymbol DEPRECATED = createRubySymbol("deprecated"); public final RubySymbol EXPERIMENTAL = createRubySymbol("experimental"); + public final RubySymbol PERFORMANCE = createRubySymbol("performance"); public final RubySymbol BIG = createRubySymbol("big"); public final RubySymbol LITTLE = createRubySymbol("little"); public final RubySymbol NATIVE = createRubySymbol("native"); diff --git a/src/main/java/org/truffleruby/language/NotOptimizedWarningNode.java b/src/main/java/org/truffleruby/language/NotOptimizedWarningNode.java index 5b2e079812b0..0cbad2da47af 100644 --- a/src/main/java/org/truffleruby/language/NotOptimizedWarningNode.java +++ b/src/main/java/org/truffleruby/language/NotOptimizedWarningNode.java @@ -57,6 +57,11 @@ protected void warnOnce(String message) throws Warned { return; } + // Only warn if Warning[:performance] is true + if (!getContext().getWarningCategoryPerformance().get()) { + return; + } + log(message); throw new Warned(); } diff --git a/src/main/java/org/truffleruby/options/Options.java b/src/main/java/org/truffleruby/options/Options.java index 583cf4479821..6fec5990104e 100644 --- a/src/main/java/org/truffleruby/options/Options.java +++ b/src/main/java/org/truffleruby/options/Options.java @@ -131,6 +131,8 @@ public final class Options { public final boolean WARN_DEPRECATED; /** --warn-experimental=true */ public final boolean WARN_EXPERIMENTAL; + /** --warn-performance=false */ + public final boolean WARN_PERFORMANCE; /** --use-truffle-regex=true */ public final boolean USE_TRUFFLE_REGEX; /** --warn-truffle-regex-compile-fallback=false */ @@ -262,6 +264,7 @@ public Options(Env env, OptionValues options, LanguageOptions languageOptions) { CEXTS_LOG_WARNINGS = options.get(OptionsCatalog.CEXTS_LOG_WARNINGS_KEY); WARN_DEPRECATED = options.get(OptionsCatalog.WARN_DEPRECATED_KEY); WARN_EXPERIMENTAL = options.get(OptionsCatalog.WARN_EXPERIMENTAL_KEY); + WARN_PERFORMANCE = options.get(OptionsCatalog.WARN_PERFORMANCE_KEY); USE_TRUFFLE_REGEX = options.get(OptionsCatalog.USE_TRUFFLE_REGEX_KEY); WARN_TRUFFLE_REGEX_COMPILE_FALLBACK = options.get(OptionsCatalog.WARN_TRUFFLE_REGEX_COMPILE_FALLBACK_KEY); WARN_TRUFFLE_REGEX_MATCH_FALLBACK = options.get(OptionsCatalog.WARN_TRUFFLE_REGEX_MATCH_FALLBACK_KEY); @@ -410,6 +413,8 @@ public Object fromDescriptor(OptionDescriptor descriptor) { return WARN_DEPRECATED; case "ruby.warn-experimental": return WARN_EXPERIMENTAL; + case "ruby.warn-performance": + return WARN_PERFORMANCE; case "ruby.use-truffle-regex": return USE_TRUFFLE_REGEX; case "ruby.warn-truffle-regex-compile-fallback": diff --git a/src/main/ruby/truffleruby/core/truffle/warning_operations.rb b/src/main/ruby/truffleruby/core/truffle/warning_operations.rb index 61a169421439..16419ba7633e 100644 --- a/src/main/ruby/truffleruby/core/truffle/warning_operations.rb +++ b/src/main/ruby/truffleruby/core/truffle/warning_operations.rb @@ -11,7 +11,7 @@ module Truffle module WarningOperations def self.check_category(category) - return if category == :deprecated || category == :experimental + return if category == :deprecated || category == :experimental || category == :performance raise ArgumentError, "unknown category: #{category}" end diff --git a/src/main/ruby/truffleruby/core/warning.rb b/src/main/ruby/truffleruby/core/warning.rb index e33733c1b183..d3b016336d96 100644 --- a/src/main/ruby/truffleruby/core/warning.rb +++ b/src/main/ruby/truffleruby/core/warning.rb @@ -42,6 +42,8 @@ def self.[](category) Primitive.warning_get_category(:deprecated) when :experimental Primitive.warning_get_category(:experimental) + when :performance + Primitive.warning_get_category(:performance) else raise ArgumentError, "unknown category: #{category}" end @@ -55,6 +57,8 @@ def self.[]=(category, value) Primitive.warning_set_category(:deprecated, Primitive.as_boolean(value)) when :experimental Primitive.warning_set_category(:experimental, Primitive.as_boolean(value)) + when :performance + Primitive.warning_set_category(:performance, Primitive.as_boolean(value)) else raise ArgumentError, "unknown category: #{category}" end diff --git a/src/options.yml b/src/options.yml index 4b8b8de6f3ba..ab003c04fc1f 100644 --- a/src/options.yml +++ b/src/options.yml @@ -142,8 +142,9 @@ EXPERT: CEXTS_LOG_LOAD: [cexts-log-load, boolean, false, Log loading of cexts] CEXTS_LOG_WARNINGS: [cexts-log-warnings, boolean, false, Log cexts warnings] - WARN_DEPRECATED: [[warn-deprecated, -W], boolean, false, 'Sets deprecated Warning category'] - WARN_EXPERIMENTAL: [[warn-experimental, -W], boolean, true, 'Sets experimental Warning category'] + WARN_DEPRECATED: [[warn-deprecated, -W], boolean, false, 'Sets the deprecated Warning category'] + WARN_EXPERIMENTAL: [[warn-experimental, -W], boolean, true, 'Sets the experimental Warning category'] + WARN_PERFORMANCE: [[warn-performance, -W], boolean, false, 'Sets the performance Warning category'] # Controlling the regular expression engines USE_TRUFFLE_REGEX: [use-truffle-regex, boolean, true, 'Use the Truffle regular expression engine when possible and fallback to Joni otherwise'] diff --git a/src/shared/java/org/truffleruby/shared/options/OptionsCatalog.java b/src/shared/java/org/truffleruby/shared/options/OptionsCatalog.java index af7f38ff12f2..47d28946dcd8 100644 --- a/src/shared/java/org/truffleruby/shared/options/OptionsCatalog.java +++ b/src/shared/java/org/truffleruby/shared/options/OptionsCatalog.java @@ -83,6 +83,7 @@ public final class OptionsCatalog { public static final OptionKey CEXTS_LOG_WARNINGS_KEY = new OptionKey<>(false); public static final OptionKey WARN_DEPRECATED_KEY = new OptionKey<>(false); public static final OptionKey WARN_EXPERIMENTAL_KEY = new OptionKey<>(true); + public static final OptionKey WARN_PERFORMANCE_KEY = new OptionKey<>(false); public static final OptionKey USE_TRUFFLE_REGEX_KEY = new OptionKey<>(true); public static final OptionKey WARN_TRUFFLE_REGEX_COMPILE_FALLBACK_KEY = new OptionKey<>(false); public static final OptionKey WARN_TRUFFLE_REGEX_MATCH_FALLBACK_KEY = new OptionKey<>(false); @@ -654,7 +655,7 @@ public final class OptionsCatalog { public static final OptionDescriptor WARN_DEPRECATED = OptionDescriptor .newBuilder(WARN_DEPRECATED_KEY, "ruby.warn-deprecated") - .help("Sets deprecated Warning category (configured by the -W Ruby option)") + .help("Sets the deprecated Warning category (configured by the -W Ruby option)") .category(OptionCategory.EXPERT) .stability(OptionStability.EXPERIMENTAL) .usageSyntax("") @@ -662,7 +663,15 @@ public final class OptionsCatalog { public static final OptionDescriptor WARN_EXPERIMENTAL = OptionDescriptor .newBuilder(WARN_EXPERIMENTAL_KEY, "ruby.warn-experimental") - .help("Sets experimental Warning category (configured by the -W Ruby option)") + .help("Sets the experimental Warning category (configured by the -W Ruby option)") + .category(OptionCategory.EXPERT) + .stability(OptionStability.EXPERIMENTAL) + .usageSyntax("") + .build(); + + public static final OptionDescriptor WARN_PERFORMANCE = OptionDescriptor + .newBuilder(WARN_PERFORMANCE_KEY, "ruby.warn-performance") + .help("Sets the performance Warning category (configured by the -W Ruby option)") .category(OptionCategory.EXPERT) .stability(OptionStability.EXPERIMENTAL) .usageSyntax("") @@ -1436,6 +1445,8 @@ public static OptionDescriptor fromName(String name) { return WARN_DEPRECATED; case "ruby.warn-experimental": return WARN_EXPERIMENTAL; + case "ruby.warn-performance": + return WARN_PERFORMANCE; case "ruby.use-truffle-regex": return USE_TRUFFLE_REGEX; case "ruby.warn-truffle-regex-compile-fallback": @@ -1666,6 +1677,7 @@ public static OptionDescriptor[] allDescriptors() { CEXTS_LOG_WARNINGS, WARN_DEPRECATED, WARN_EXPERIMENTAL, + WARN_PERFORMANCE, USE_TRUFFLE_REGEX, WARN_TRUFFLE_REGEX_COMPILE_FALLBACK, WARN_TRUFFLE_REGEX_MATCH_FALLBACK, diff --git a/test/mri/excludes/TestFiddle.rb b/test/mri/excludes/TestFiddle.rb index 73981b929f9c..a7737fc570ea 100644 --- a/test/mri/excludes/TestFiddle.rb +++ b/test/mri/excludes/TestFiddle.rb @@ -1,2 +1,3 @@ exclude :test_nil_true_etc, "NameError: uninitialized constant Fiddle::Qtrue" exclude :test_dlopen_linker_script_group_linux, "NotImplementedError: NotImplementedError" +exclude :test_dlopen_linker_script_input_linux, "NotImplementedError: NotImplementedError" diff --git a/tool/generate-core-symbols.rb b/tool/generate-core-symbols.rb index 867d541f9535..ea1c23330eaf 100755 --- a/tool/generate-core-symbols.rb +++ b/tool/generate-core-symbols.rb @@ -59,6 +59,7 @@ public final RubySymbol ON_BLOCKING = createRubySymbol("on_blocking"); public final RubySymbol DEPRECATED = createRubySymbol("deprecated"); public final RubySymbol EXPERIMENTAL = createRubySymbol("experimental"); + public final RubySymbol PERFORMANCE = createRubySymbol("performance"); public final RubySymbol BIG = createRubySymbol("big"); public final RubySymbol LITTLE = createRubySymbol("little"); public final RubySymbol NATIVE = createRubySymbol("native");