From 8907ae931e51836ca6989e9912a636e8416f4f1b Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Mon, 12 Sep 2022 16:16:06 +0000 Subject: [PATCH 01/11] [GR-40738] Add JDK 19 to the CI, remove the deprecated --fiber-pool option and cleanups based on that PullRequest: truffleruby/3473 (cherry picked from commit 149f675badfecdbafe8d71729c0b20c3770c0d9f) --- ci.jsonnet | 14 +++ .../launcher/CommandLineException.java | 3 +- src/main/.checkstyle_checks.xml | 4 + .../java/org/truffleruby/RubyLanguage.java | 86 +++++++++----- .../java/org/truffleruby/cext/CExtNodes.java | 22 ++-- .../truffleruby/cext/ValueWrapperManager.java | 2 +- .../core/DataObjectFinalizationService.java | 3 +- .../truffleruby/core/FinalizationService.java | 2 +- .../org/truffleruby/core/MarkingService.java | 2 +- .../truffleruby/core/MarkingServiceNodes.java | 2 +- .../core/exception/GetBacktraceException.java | 3 +- .../core/exception/RubySyntaxError.java | 11 +- .../truffleruby/core/fiber/FiberManager.java | 58 +++------- .../truffleruby/core/fiber/FiberNodes.java | 46 +++----- .../core/fiber/FiberPoolThread.java | 23 ---- .../org/truffleruby/core/fiber/RubyFiber.java | 4 + .../CantCompressNegativeException.java | 4 +- .../exceptions/CantConvertException.java | 3 +- .../format/exceptions/FormatException.java | 3 +- .../exceptions/InvalidFormatException.java | 3 +- .../NoImplicitConversionException.java | 3 +- .../exceptions/OutsideOfStringException.java | 4 +- .../format/exceptions/RangeException.java | 3 +- .../exceptions/TooFewArgumentsException.java | 4 +- ...otConvertBinaryRubyStringToJavaString.java | 3 +- .../truffleruby/core/string/ConvertBytes.java | 3 +- .../core/string/DoubleConverter.java | 3 +- .../core/thread/ThreadManager.java | 109 ++++-------------- .../truffleruby/core/thread/ThreadNodes.java | 2 +- .../core/thread/TruffleThreadNodes.java | 2 +- .../truffleruby/debug/TruffleDebugNodes.java | 2 +- .../language/NotOptimizedWarningNode.java | 2 +- .../truffleruby/language/SafepointAction.java | 4 +- .../language/SafepointManager.java | 4 +- .../language/constants/AutoloadException.java | 26 ----- .../language/control/BreakException.java | 3 +- .../control/DeferredRaiseException.java | 3 +- .../control/DynamicReturnException.java | 3 +- .../language/control/ExitException.java | 3 +- .../language/control/KillException.java | 4 +- .../control/LocalReturnException.java | 3 +- .../language/control/NextException.java | 3 +- .../language/control/RaiseException.java | 3 +- .../language/control/RedoException.java | 4 +- .../language/control/RetryException.java | 4 +- .../control/TerminationException.java | 3 +- .../language/control/ThrowException.java | 3 +- .../language/loader/FeatureLoader.java | 4 +- .../language/loader/RequireNode.java | 4 +- .../ThreadAndFrameLocalStorage.java | 7 +- .../java/org/truffleruby/options/Options.java | 5 - .../options/UnknownOptionException.java | 27 ----- .../parser/lexer/SyntaxException.java | 3 +- src/options.yml | 1 - .../shared/options/OptionTypeException.java | 27 ----- .../shared/options/OptionsCatalog.java | 12 -- src/test/java/org/truffleruby/LeakTest.java | 3 +- src/test/java/org/truffleruby/MiscTest.java | 1 + tool/jt.rb | 17 ++- 59 files changed, 202 insertions(+), 420 deletions(-) delete mode 100644 src/main/java/org/truffleruby/core/fiber/FiberPoolThread.java delete mode 100644 src/main/java/org/truffleruby/language/constants/AutoloadException.java delete mode 100644 src/main/java/org/truffleruby/options/UnknownOptionException.java delete mode 100644 src/shared/java/org/truffleruby/shared/options/OptionTypeException.java diff --git a/ci.jsonnet b/ci.jsonnet index d486846ece72..5ea919af79c5 100644 --- a/ci.jsonnet +++ b/ci.jsonnet @@ -238,6 +238,12 @@ local part_definitions = { JAVA_HOME: common.jdks["labsjdk-ce-17"], }, }, + + v19: with_path { + downloads+: { + JAVA_HOME: common.jdks["labsjdk-ce-19"], + }, + }, }, platform: { @@ -537,10 +543,13 @@ local composition_environment = utils.add_inclusion_tracking(part_definitions, " # Order: platform, jdk, mx_env. Keep aligned for an easy visual comparison. "ruby-test-specs-linux-11": $.platform.linux + $.jdk.v11 + $.env.jvm + gate_no_build + $.use.build + $.run.test_unit_tck + native_config + $.run.test_specs + { timelimit: "01:20:00" }, "ruby-test-specs-linux-17": $.platform.linux + $.jdk.v17 + $.env.jvm + gate_no_build + $.use.build + $.run.test_unit_tck + native_config + $.run.test_specs + { timelimit: "01:20:00" }, + "ruby-test-specs-linux-19": $.platform.linux + $.jdk.v19 + $.env.jvm + gate_no_build + $.use.build + $.run.test_unit_tck + native_config + $.run.test_specs + { timelimit: "01:20:00" }, "ruby-test-specs-darwin-amd64-11": $.platform.darwin_amd64 + $.jdk.v11 + $.env.jvm + gate_no_build + $.use.build + $.run.test_unit_tck + native_config + $.run.test_specs + { timelimit: "01:40:00" }, "ruby-test-specs-darwin-amd64-17": $.platform.darwin_amd64 + $.jdk.v17 + $.env.jvm + gate_no_build + $.use.build + $.run.test_unit_tck + native_config + $.run.test_specs + { timelimit: "01:40:00" }, + "ruby-test-specs-darwin-amd64-19": $.platform.darwin_amd64 + $.jdk.v19 + $.env.jvm + gate_no_build + $.use.build + $.run.test_unit_tck + native_config + $.run.test_specs + { timelimit: "01:40:00" }, "ruby-test-specs-darwin-aarch64-11": $.platform.darwin_aarch64 + $.jdk.v11 + $.env.jvm + gate_no_build + $.use.build + $.run.test_unit_tck + native_config + $.run.test_specs + { timelimit: "01:40:00" }, "ruby-test-specs-darwin-aarch64-17": $.platform.darwin_aarch64 + $.jdk.v17 + $.env.jvm + gate_no_build + $.use.build + $.run.test_unit_tck + native_config + $.run.test_specs + { timelimit: "01:40:00" }, + "ruby-test-specs-darwin-aarch64-19": $.platform.darwin_aarch64 + $.jdk.v19 + $.env.jvm + gate_no_build + $.use.build + $.run.test_unit_tck + native_config + $.run.test_specs + { timelimit: "01:40:00" }, "ruby-test-fast-linux-aarch64": $.platform.linux_aarch64 + $.jdk.v11 + $.env.jvm + gate + $.run.test_fast + native_config + { timelimit: "45:00" }, "ruby-test-fast-linux": $.platform.linux + $.jdk.v11 + $.env.jvm + gate + $.run.test_fast + { timelimit: "45:00" }, # To catch missing slow tags "ruby-test-mri-linux": $.platform.linux + $.jdk.v11 + $.env.native + gate + $.run.test_mri + { timelimit: "01:20:00" }, @@ -559,15 +568,20 @@ local composition_environment = utils.add_inclusion_tracking(part_definitions, " "ruby-test-compiler-graal-core-11": $.platform.linux + $.jdk.v11 + $.env.jvm_ce + gate + $.use.truffleruby + $.run.test_compiler, "ruby-test-compiler-graal-core-17": $.platform.linux + $.jdk.v17 + $.env.jvm_ce + gate + $.use.truffleruby + $.run.test_compiler, + "ruby-test-compiler-graal-core-19": $.platform.linux + $.jdk.v19 + $.env.jvm_ce + gate + $.use.truffleruby + $.run.test_compiler, "ruby-test-compiler-graal-enterprise-11": $.platform.linux + $.jdk.v11 + $.env.jvm_ee + gate + $.use.truffleruby + $.run.test_compiler, "ruby-test-compiler-graal-enterprise-17": $.platform.linux + $.jdk.v17 + $.env.jvm_ee + gate + $.use.truffleruby + $.run.test_compiler, + "ruby-test-compiler-graal-enterprise-19": $.platform.linux + $.jdk.v19 + $.env.jvm_ee + gate + $.use.truffleruby + $.run.test_compiler, "ruby-test-svm-graal-core-linux-11": $.platform.linux + $.jdk.v11 + $.env.native + $.env.gdb_svm + gate + native_tests, "ruby-test-svm-graal-core-linux-17": $.platform.linux + $.jdk.v17 + $.env.native + $.env.gdb_svm + gate + native_tests, + "ruby-test-svm-graal-core-linux-19": $.platform.linux + $.jdk.v19 + $.env.native + $.env.gdb_svm + gate + native_tests, "ruby-test-svm-graal-core-darwin-amd64-11": $.platform.darwin_amd64 + $.jdk.v11 + $.env.native + $.env.gdb_svm + gate + native_tests, "ruby-test-svm-graal-core-darwin-amd64-17": $.platform.darwin_amd64 + $.jdk.v17 + $.env.native + $.env.gdb_svm + gate + native_tests, + "ruby-test-svm-graal-core-darwin-amd64-19": $.platform.darwin_amd64 + $.jdk.v19 + $.env.native + $.env.gdb_svm + gate + native_tests, "ruby-test-svm-graal-core-darwin-aarch64-11": $.platform.darwin_aarch64 + $.jdk.v11 + $.env.native + gate + native_tests, "ruby-test-svm-graal-core-darwin-aarch64-17": $.platform.darwin_aarch64 + $.jdk.v17 + $.env.native + gate + native_tests, + "ruby-test-svm-graal-core-darwin-aarch64-19": $.platform.darwin_aarch64 + $.jdk.v19 + $.env.native + gate + native_tests, "ruby-test-svm-graal-enterprise-linux": $.platform.linux + $.jdk.v11 + $.env.native_ee + $.env.gdb_svm + gate + native_tests, "ruby-test-svm-graal-enterprise-darwin-aarch64 ": $.platform.darwin_aarch64 + $.jdk.v11 + $.env.native_ee + gate + native_tests, }, diff --git a/src/launcher/java/org/truffleruby/launcher/CommandLineException.java b/src/launcher/java/org/truffleruby/launcher/CommandLineException.java index b4360c3577dd..8fa2a8a59959 100644 --- a/src/launcher/java/org/truffleruby/launcher/CommandLineException.java +++ b/src/launcher/java/org/truffleruby/launcher/CommandLineException.java @@ -27,10 +27,9 @@ ***** END LICENSE BLOCK *****/ package org.truffleruby.launcher; +@SuppressWarnings("serial") public class CommandLineException extends Exception { - private static final long serialVersionUID = -8585821821150293755L; - private final boolean usageError; public CommandLineException(String message) { diff --git a/src/main/.checkstyle_checks.xml b/src/main/.checkstyle_checks.xml index 824fcbb1f5c4..90754e502294 100644 --- a/src/main/.checkstyle_checks.xml +++ b/src/main/.checkstyle_checks.xml @@ -251,6 +251,10 @@ + + + + diff --git a/src/main/java/org/truffleruby/RubyLanguage.java b/src/main/java/org/truffleruby/RubyLanguage.java index 667ba50dda9f..7ecdba40920a 100644 --- a/src/main/java/org/truffleruby/RubyLanguage.java +++ b/src/main/java/org/truffleruby/RubyLanguage.java @@ -13,8 +13,10 @@ import java.io.IOException; import java.lang.ref.Cleaner; import java.util.Arrays; +import java.util.Map; import java.util.Objects; import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicLong; import java.util.logging.Level; @@ -50,7 +52,6 @@ import org.truffleruby.core.exception.RubySyntaxError; import org.truffleruby.core.exception.RubySystemCallError; import org.truffleruby.core.exception.RubySystemExit; -import org.truffleruby.core.fiber.FiberPoolThread; import org.truffleruby.core.fiber.RubyFiber; import org.truffleruby.core.hash.RubyHash; import org.graalvm.options.OptionValues; @@ -185,16 +186,44 @@ public final class RubyLanguage extends TruffleLanguage { * {@link TranslatorEnvironment#newFrameDescriptorBuilder(org.truffleruby.parser.ParentFrameDescriptor, boolean)}. */ public static final FrameDescriptor EMPTY_FRAME_DESCRIPTOR = new FrameDescriptor(Nil.INSTANCE); - /** We need an extra indirection added to ContextThreadLocal due to multiple Fibers of different Ruby Threads - * sharing the same Java Thread when using the fiber pool. */ - public static final class ThreadLocalState { - public RubyThread rubyThread; + private RubyThread getOrCreateForeignThread(RubyContext context, Thread thread) { + RubyThread foreignThread = rubyThreadInitMap.remove(thread); + if (foreignThread == null) { + foreignThread = context.getThreadManager().createForeignThread(); + rubyThreadInitMap.put(thread, foreignThread); + } + return foreignThread; } - private final ContextThreadLocal threadLocalState = createContextThreadLocal( - (context, thread) -> thread instanceof FiberPoolThread - ? ((FiberPoolThread) thread).threadLocalState - : new ThreadLocalState()); + public final Map rubyThreadInitMap = new ConcurrentHashMap<>(); + private final ContextThreadLocal rubyThread = createContextThreadLocal( + (context, thread) -> { + if (thread == context.getThreadManager().getOrInitializeRootJavaThread()) { + // Already initialized when creating the context + return context.getThreadManager().getRootThread(); + } + + if (context.getThreadManager().isRubyManagedThread(thread)) { + return Objects.requireNonNull(rubyThreadInitMap.remove(thread)); + } + + return getOrCreateForeignThread(context, thread); + }); + + public final Map rubyFiberInitMap = new ConcurrentHashMap<>(); + private final ContextThreadLocal rubyFiber = createContextThreadLocal( + (context, thread) -> { + if (thread == context.getThreadManager().getOrInitializeRootJavaThread()) { + // Already initialized when creating the context + return context.getThreadManager().getRootThread().getRootFiber(); + } + + if (context.getThreadManager().isRubyManagedThread(thread)) { + return Objects.requireNonNull(rubyFiberInitMap.remove(thread)); + } + + return getOrCreateForeignThread(context, thread).getRootFiber(); + }); private final CyclicAssumption tracingCyclicAssumption = new CyclicAssumption("object-space-tracing"); @CompilationFinal private volatile Assumption tracingAssumption = tracingCyclicAssumption.getAssumption(); @@ -330,18 +359,11 @@ public RubyLanguage() { } public RubyThread getCurrentThread() { - final RubyThread rubyThread = threadLocalState.get().rubyThread; - if (rubyThread == null) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - throw CompilerDirectives.shouldNotReachHere( - "No Ruby Thread is associated with current Java Thread: " + Thread.currentThread()); - } - return rubyThread; + return rubyThread.get(); } - public void setupCurrentThread(Thread javaThread, RubyThread rubyThread) { - final ThreadLocalState threadLocalState = this.threadLocalState.get(javaThread); - threadLocalState.rubyThread = rubyThread; + public RubyFiber getCurrentFiber() { + return rubyFiber.get(); } @TruffleBoundary @@ -553,39 +575,34 @@ protected boolean isThreadAccessAllowed(Thread thread, boolean singleThreaded) { @Override public void initializeThread(RubyContext context, Thread thread) { - LOGGER.fine(() -> "initializeThread(#" + thread.getId() + " " + thread + ")"); + LOGGER.fine(() -> "initializeThread(#" + getThreadId(thread) + " " + thread + ")"); if (thread == context.getThreadManager().getOrInitializeRootJavaThread()) { // Already initialized when creating the context - setupCurrentThread(thread, context.getThreadManager().getRootThread()); return; } if (context.getThreadManager().isRubyManagedThread(thread)) { - final RubyThread rubyThread = context.getThreadManager().getCurrentThreadOrNull(); - if (rubyThread != null && rubyThread.thread == thread) { // new Ruby Thread + final RubyThread rubyThread = getCurrentThread(); + if (rubyThread.thread == thread) { // new Ruby Thread if (thread != Thread.currentThread()) { throw CompilerDirectives .shouldNotReachHere("Ruby threads should be initialized on their Java thread"); } context.getThreadManager().start(rubyThread, thread); - setupCurrentThread(thread, rubyThread); } else { // Fiber } return; } - final RubyThread foreignThread = context.getThreadManager().createForeignThread(); + final RubyThread foreignThread = getCurrentThread(); context.getThreadManager().startForeignThread(foreignThread, thread); - setupCurrentThread(thread, foreignThread); } @Override public void disposeThread(RubyContext context, Thread thread) { - LOGGER.fine( - () -> "disposeThread(#" + thread.getId() + " " + thread + " on " + - context.getThreadManager().getCurrentThreadOrNull() + ")"); + LOGGER.fine(() -> "disposeThread(#" + getThreadId(thread) + " " + thread + " on " + getCurrentThread() + ")"); if (thread == context.getThreadManager().getRootJavaThread()) { if (context.getEnv().isPreInitialization()) { @@ -604,8 +621,8 @@ public void disposeThread(RubyContext context, Thread thread) { } if (context.getThreadManager().isRubyManagedThread(thread)) { - final RubyThread rubyThread = context.getThreadManager().getCurrentThreadOrNull(); - if (rubyThread != null && rubyThread.thread == thread) { // Thread + final RubyThread rubyThread = getCurrentThread(); + if (rubyThread.thread == thread) { // Thread if (thread != Thread.currentThread()) { throw CompilerDirectives.shouldNotReachHere("Ruby threads should be disposed on their Java thread"); } @@ -617,7 +634,7 @@ public void disposeThread(RubyContext context, Thread thread) { } // A foreign Thread, its Fibers are considered isRubyManagedThread() - final RubyThread rubyThread = context.getThreadManager().getRubyThreadForJavaThread(thread); + final RubyThread rubyThread = this.rubyThread.get(thread); context.getThreadManager().cleanup(rubyThread, thread); } @@ -848,4 +865,9 @@ private static String buildCoreLoadPath(String coreLoadPath) { throw CompilerDirectives.shouldNotReachHere(e); } } + + @SuppressWarnings("deprecation") // deprecated on JDK19 by Thread#threadId, but that's added in JDK19 + public static long getThreadId(Thread thread) { + return thread.getId(); + } } diff --git a/src/main/java/org/truffleruby/cext/CExtNodes.java b/src/main/java/org/truffleruby/cext/CExtNodes.java index 3d3d71a34db4..6552907f32b5 100644 --- a/src/main/java/org/truffleruby/cext/CExtNodes.java +++ b/src/main/java/org/truffleruby/cext/CExtNodes.java @@ -171,9 +171,7 @@ public abstract static class CallWithCExtLockAndFrameNode extends PrimitiveArray @Specialization protected Object callWithCExtLockAndFrame( VirtualFrame frame, Object receiver, RubyArray argsArray, Object specialVariables, Object block) { - final ExtensionCallStack extensionStack = getLanguage() - .getCurrentThread() - .getCurrentFiber().extensionCallStack; + final ExtensionCallStack extensionStack = getLanguage().getCurrentFiber().extensionCallStack; final boolean keywordsGiven = RubyArguments.getDescriptor(frame) instanceof KeywordArgumentsDescriptor; extensionStack.push(keywordsGiven, specialVariables, block); try { @@ -195,9 +193,7 @@ protected Object callWithCExtLockAndFrame( @Cached TranslateInteropExceptionNode translateInteropExceptionNode, @Cached ConditionProfile ownedProfile, @Cached UnwrapNode unwrapNode) { - final ExtensionCallStack extensionStack = getLanguage() - .getCurrentThread() - .getCurrentFiber().extensionCallStack; + final ExtensionCallStack extensionStack = getLanguage().getCurrentFiber().extensionCallStack; final boolean keywordsGiven = RubyArguments.getDescriptor(frame) instanceof KeywordArgumentsDescriptor; extensionStack.push(keywordsGiven, specialVariables, block); try { @@ -845,7 +841,7 @@ public abstract static class RbKeywordGivenNode extends CoreMethodArrayArguments @Specialization protected boolean keywordGiven() { - return getLanguage().getCurrentThread().getCurrentFiber().extensionCallStack.areKeywordsGiven(); + return getLanguage().getCurrentFiber().extensionCallStack.areKeywordsGiven(); } } @@ -854,7 +850,7 @@ public abstract static class BlockProcNode extends CoreMethodArrayArgumentsNode @Specialization protected Object block() { - return getLanguage().getCurrentThread().getCurrentFiber().extensionCallStack.getBlock(); + return getLanguage().getCurrentFiber().extensionCallStack.getBlock(); } } @@ -863,7 +859,7 @@ public abstract static class VarsFromStackNode extends PrimitiveArrayArgumentsNo @Specialization protected Object variables() { - return getLanguage().getCurrentThread().getCurrentFiber().extensionCallStack.getSpecialVariables(); + return getLanguage().getCurrentFiber().extensionCallStack.getSpecialVariables(); } } @@ -1379,9 +1375,7 @@ public abstract static class StoreException extends YieldingCoreMethodNode { @Specialization protected Object storeException(CapturedException captured) { - final ExtensionCallStack extensionStack = getLanguage() - .getCurrentThread() - .getCurrentFiber().extensionCallStack; + final ExtensionCallStack extensionStack = getLanguage().getCurrentFiber().extensionCallStack; extensionStack.setException(captured); return nil; } @@ -1392,9 +1386,7 @@ public abstract static class RetrieveException extends YieldingCoreMethodNode { @Specialization protected Object retrieveException() { - final ExtensionCallStack extensionStack = getLanguage() - .getCurrentThread() - .getCurrentFiber().extensionCallStack; + final ExtensionCallStack extensionStack = getLanguage().getCurrentFiber().extensionCallStack; return extensionStack.getException(); } } diff --git a/src/main/java/org/truffleruby/cext/ValueWrapperManager.java b/src/main/java/org/truffleruby/cext/ValueWrapperManager.java index 9c27c71dc645..22a68af84a80 100644 --- a/src/main/java/org/truffleruby/cext/ValueWrapperManager.java +++ b/src/main/java/org/truffleruby/cext/ValueWrapperManager.java @@ -64,7 +64,7 @@ public class ValueWrapperManager { private volatile HandleBlockWeakReference[] blockMap = new HandleBlockWeakReference[0]; public static HandleBlockHolder getBlockHolder(RubyContext context, RubyLanguage language) { - return language.getCurrentThread().getCurrentFiber().handleData; + return language.getCurrentFiber().handleData; } /* We keep a map of long wrappers that have been generated because various C extensions assume that any given fixnum diff --git a/src/main/java/org/truffleruby/core/DataObjectFinalizationService.java b/src/main/java/org/truffleruby/core/DataObjectFinalizationService.java index 1e702dfc1fdf..9925219b5528 100644 --- a/src/main/java/org/truffleruby/core/DataObjectFinalizationService.java +++ b/src/main/java/org/truffleruby/core/DataObjectFinalizationService.java @@ -81,8 +81,7 @@ private void runFinalizer(DataObjectFinalizerReference ref) throws Error { if (!getContext().isFinalizing()) { Object data = ref.dataHolder.getPointer(); if (!nullNode.isNull(data)) { - final ExtensionCallStack stack = getLanguage().getCurrentThread() - .getCurrentFiber().extensionCallStack; + final ExtensionCallStack stack = getLanguage().getCurrentFiber().extensionCallStack; stack.push(false, stack.getSpecialVariables(), stack.getBlock()); try { callNode.execute(ref.callable, data); diff --git a/src/main/java/org/truffleruby/core/FinalizationService.java b/src/main/java/org/truffleruby/core/FinalizationService.java index f60fe912ec76..ab11d50acd84 100644 --- a/src/main/java/org/truffleruby/core/FinalizationService.java +++ b/src/main/java/org/truffleruby/core/FinalizationService.java @@ -95,7 +95,7 @@ protected void processReference(RubyContext context, RubyLanguage language, protected void processReferenceInternal(RubyContext context, RubyLanguage language, FinalizerReference finalizerReference) { - final ExtensionCallStack stack = language.getCurrentThread().getCurrentFiber().extensionCallStack; + final ExtensionCallStack stack = language.getCurrentFiber().extensionCallStack; stack.push(stack.areKeywordsGiven(), stack.getSpecialVariables(), stack.getBlock()); try { while (!context.isFinalizing()) { diff --git a/src/main/java/org/truffleruby/core/MarkingService.java b/src/main/java/org/truffleruby/core/MarkingService.java index ab51b1647f8f..50f9679240af 100644 --- a/src/main/java/org/truffleruby/core/MarkingService.java +++ b/src/main/java/org/truffleruby/core/MarkingService.java @@ -87,7 +87,7 @@ protected void processReference(RubyContext context, RubyLanguage language, Proc @TruffleBoundary public void runAllMarkers(RubyContext context, RubyLanguage language) { - final ExtensionCallStack stack = language.getCurrentThread().getCurrentFiber().extensionCallStack; + final ExtensionCallStack stack = language.getCurrentFiber().extensionCallStack; stack.push(stack.areKeywordsGiven(), stack.getSpecialVariables(), stack.getBlock()); try { // TODO (eregon, 15 Sept 2020): there seems to be no synchronization here while walking the list of diff --git a/src/main/java/org/truffleruby/core/MarkingServiceNodes.java b/src/main/java/org/truffleruby/core/MarkingServiceNodes.java index d7ca989298b7..d550fba3f8bd 100644 --- a/src/main/java/org/truffleruby/core/MarkingServiceNodes.java +++ b/src/main/java/org/truffleruby/core/MarkingServiceNodes.java @@ -27,7 +27,7 @@ public abstract static class KeepAliveNode extends RubyBaseNode { @Specialization protected void keepObjectAlive(Object object) { - ExtensionCallStack stack = getLanguage().getCurrentThread().getCurrentFiber().extensionCallStack; + ExtensionCallStack stack = getLanguage().getCurrentFiber().extensionCallStack; addToList(stack.getKeptObjects(), object); } diff --git a/src/main/java/org/truffleruby/core/exception/GetBacktraceException.java b/src/main/java/org/truffleruby/core/exception/GetBacktraceException.java index 42611d6347f2..4a89a7b46c18 100644 --- a/src/main/java/org/truffleruby/core/exception/GetBacktraceException.java +++ b/src/main/java/org/truffleruby/core/exception/GetBacktraceException.java @@ -12,10 +12,9 @@ import com.oracle.truffle.api.exception.AbstractTruffleException; import com.oracle.truffle.api.nodes.Node; +@SuppressWarnings("serial") public class GetBacktraceException extends AbstractTruffleException { - private static final long serialVersionUID = 2633487517169337464L; - public static final int UNLIMITED = AbstractTruffleException.UNLIMITED_STACK_TRACE; public GetBacktraceException(Node location, int limit) { diff --git a/src/main/java/org/truffleruby/core/exception/RubySyntaxError.java b/src/main/java/org/truffleruby/core/exception/RubySyntaxError.java index e29fe777d425..06bf02917c2b 100644 --- a/src/main/java/org/truffleruby/core/exception/RubySyntaxError.java +++ b/src/main/java/org/truffleruby/core/exception/RubySyntaxError.java @@ -10,6 +10,7 @@ package org.truffleruby.core.exception; import org.truffleruby.core.klass.RubyClass; +import org.truffleruby.language.RubyGuards; import org.truffleruby.language.backtrace.Backtrace; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; @@ -20,6 +21,7 @@ import com.oracle.truffle.api.library.ExportMessage; import com.oracle.truffle.api.object.Shape; import com.oracle.truffle.api.source.SourceSection; +import org.truffleruby.language.library.RubyStringLibrary; @ExportLibrary(InteropLibrary.class) public class RubySyntaxError extends RubyException { @@ -44,9 +46,16 @@ public ExceptionType getExceptionType() { return ExceptionType.PARSE_ERROR; } + @TruffleBoundary @ExportMessage public boolean isExceptionIncompleteSource() { - return false; // Unknown + if (RubyStringLibrary.getUncached().isRubyString(message)) { + String messageString = RubyGuards.getJavaString(message); + return messageString.endsWith(" unexpected end-of-file") || + messageString.endsWith(" meets end of file"); + } else { + return false; + } } @ExportMessage diff --git a/src/main/java/org/truffleruby/core/fiber/FiberManager.java b/src/main/java/org/truffleruby/core/fiber/FiberManager.java index 8e2161f2e8c2..bfdba507c027 100644 --- a/src/main/java/org/truffleruby/core/fiber/FiberManager.java +++ b/src/main/java/org/truffleruby/core/fiber/FiberManager.java @@ -64,16 +64,12 @@ public void initialize(RubyFiber fiber, boolean blocking, RubyProc block, Node c final TruffleContext truffleContext = context.getEnv().getContext(); - ThreadManager.FIBER_BEING_SPAWNED.set(fiber); - try { - context.getThreadManager().leaveAndEnter(truffleContext, currentNode, () -> { - context.getThreadManager().spawnFiber(fiber, () -> fiberMain(context, fiber, block, currentNode)); - waitForInitialization(context, fiber, currentNode); - return BlockingAction.SUCCESS; - }); - } finally { - ThreadManager.FIBER_BEING_SPAWNED.remove(); - } + context.getThreadManager().leaveAndEnter(truffleContext, currentNode, () -> { + context.getThreadManager().spawnFiber(fiber, sourceSection, + () -> fiberMain(context, fiber, block, currentNode)); + waitForInitialization(context, fiber, currentNode); + return BlockingAction.SUCCESS; + }); } /** Wait for full initialization of the new fiber */ @@ -98,10 +94,8 @@ private void fiberMain(RubyContext context, RubyFiber fiber, RubyProc block, Nod assert !fiber.isRootFiber() : "Root Fibers execute threadMain() and not fiberMain()"; assertNotEntered("Fibers should start unentered to avoid triggering multithreading"); - final FiberPoolThread thread = (FiberPoolThread) Thread.currentThread(); - final SourceSection sourceSection = block.getSharedMethodInfo().getSourceSection(); - final String oldName = thread.getName(); - thread.setName(NAME_PREFIX + " id=" + thread.getId() + " from " + context.fileLine(sourceSection)); + final Thread thread = Thread.currentThread(); + final TruffleContext truffleContext = context.getEnv().getContext(); start(fiber, thread); @@ -109,15 +103,9 @@ private void fiberMain(RubyContext context, RubyFiber fiber, RubyProc block, Nod fiber.initializedLatch.countDown(); final FiberMessage message = waitMessage(fiber, currentNode); - final TruffleContext truffleContext = context.getEnv().getContext(); - - /* We need to setup the ThreadLocalState before enter(), as that polls and the current thread is needed for - * guest safepoints. It can't just be set in RubyLanguage#initializeThread() as this java.lang.Thread is reused - * for multiple Fibers and multiple Ruby Threads due to the Fiber pool, but initializeThread() is only called - * once per thread. */ fiber.rubyThread.setCurrentFiber(fiber); - thread.threadLocalState.rubyThread = fiber.rubyThread; + // enter() polls so we need the current Fiber to be set before enter() final Object prev = truffleContext.enter(currentNode); FiberMessage lastMessage = null; @@ -153,9 +141,7 @@ private void fiberMain(RubyContext context, RubyFiber fiber, RubyProc block, Nod fiber.status = FiberStatus.TERMINATED; // Leave context before addToMessageQueue() -> parent Fiber starts executing truffleContext.leave(currentNode, prev); - thread.threadLocalState.rubyThread = null; cleanup(fiber, thread); - thread.setName(oldName); if (lastMessage != null) { addToMessageQueue(returnFiber, lastMessage); @@ -164,7 +150,7 @@ private void fiberMain(RubyContext context, RubyFiber fiber, RubyProc block, Nod } public RubyFiber getReturnFiber(RubyFiber currentFiber, Node currentNode, BranchProfile errorProfile) { - assert currentFiber == currentFiber.rubyThread.getCurrentFiber(); + assert currentFiber.isActive(); final RubyFiber rootFiber = currentFiber.rubyThread.getRootFiber(); @@ -329,17 +315,9 @@ public void safepoint(RubyFiber fromFiber, RubyFiber fiber, SafepointAction acti } public void start(RubyFiber fiber, Thread javaThread) { - final ThreadManager threadManager = context.getThreadManager(); - - if (Thread.currentThread() == javaThread) { - context.getThreadManager().rubyFiber.set(fiber); - } - context.getThreadManager().javaThreadToRubyFiber.put(javaThread, fiber); - fiber.thread = javaThread; final RubyThread rubyThread = fiber.rubyThread; - threadManager.initializeValuesForJavaThread(rubyThread, javaThread); // share RubyFiber as its fiberLocals might be accessed by other threads with Thread#[] SharedObjects.propagate(language, rubyThread, fiber); @@ -347,23 +325,14 @@ public void start(RubyFiber fiber, Thread javaThread) { } public void cleanup(RubyFiber fiber, Thread javaThread) { - final ThreadManager threadManager = context.getThreadManager(); - context.getValueWrapperManager().cleanup(context, fiber.handleData); fiber.status = FiberStatus.TERMINATED; - threadManager.cleanupValuesForJavaThread(javaThread); - fiber.rubyThread.runningFibers.remove(fiber); fiber.thread = null; - if (Thread.currentThread() == javaThread) { - threadManager.rubyFiber.remove(); - } - threadManager.javaThreadToRubyFiber.remove(javaThread); - fiber.finishedLatch.countDown(); } @@ -425,14 +394,14 @@ public String getFiberDebugInfo(RubyThread rubyThread) { if (thread == null) { builder.append(" (no Java thread)"); } else { - builder.append(" #").append(thread.getId()).append(' ').append(thread); + builder.append(" #").append(RubyLanguage.getThreadId(thread)).append(' ').append(thread); } if (fiber.isRootFiber()) { builder.append(" (root)"); } - if (fiber == fiber.rubyThread.getCurrentFiber()) { + if (fiber.isActive()) { builder.append(" (current)"); } @@ -510,9 +479,8 @@ private FiberSafepointMessage(RubyFiber sendingFiber, SafepointAction action) { /** Used to cleanup and terminate Fibers when the parent Thread dies. */ // TODO: should not be an AbstractTruffleException and not run ensure, like in CRuby + @SuppressWarnings("serial") private static final class FiberShutdownException extends TerminationException { - private static final long serialVersionUID = 1522270454305076317L; - public FiberShutdownException(Node location) { super("terminate Fiber", location); } diff --git a/src/main/java/org/truffleruby/core/fiber/FiberNodes.java b/src/main/java/org/truffleruby/core/fiber/FiberNodes.java index 341657f42f72..40bc6cfd97f5 100644 --- a/src/main/java/org/truffleruby/core/fiber/FiberNodes.java +++ b/src/main/java/org/truffleruby/core/fiber/FiberNodes.java @@ -63,32 +63,31 @@ public Object singleValue(Object[] args) { return singleValueCastNode.executeSingleValue(args); } - public abstract Object executeTransferControlTo(RubyThread currentThread, RubyFiber currentFiber, - RubyFiber fiber, FiberOperation operation, ArgumentsDescriptor descriptor, Object[] args); + public abstract Object execute(RubyFiber currentFiber, RubyFiber toFiber, FiberOperation operation, + ArgumentsDescriptor descriptor, Object[] args); @Specialization protected Object transfer( - RubyThread currentThread, RubyFiber currentFiber, - RubyFiber fiber, + RubyFiber toFiber, FiberOperation operation, ArgumentsDescriptor descriptor, Object[] args, @Cached BranchProfile errorProfile) { - if (fiber.isTerminated()) { + if (toFiber.isTerminated()) { errorProfile.enter(); throw new RaiseException(getContext(), coreExceptions().deadFiberCalledError(this)); } - if (fiber.rubyThread != currentThread) { + if (toFiber.rubyThread != currentFiber.rubyThread) { errorProfile.enter(); throw new RaiseException( getContext(), coreExceptions().fiberError("fiber called across threads", this)); } - var descriptorAndArgs = getContext().fiberManager.transferControlTo(currentFiber, fiber, operation, + var descriptorAndArgs = getContext().fiberManager.transferControlTo(currentFiber, toFiber, operation, descriptor, args, this); // Ignore the descriptor like CRuby here, see https://bugs.ruby-lang.org/issues/18621 return singleValue(descriptorAndArgs.args); @@ -124,7 +123,6 @@ public abstract static class InitializeNode extends PrimitiveArrayArgumentsNode @TruffleBoundary @Specialization protected Object initialize(RubyFiber fiber, boolean blocking, RubyProc block) { - final RubyThread thread = getLanguage().getCurrentThread(); getContext().fiberManager.initialize(fiber, blocking, block, this); return nil; } @@ -158,17 +156,15 @@ protected Object transfer(VirtualFrame frame, RubyFiber toFiber, Object[] rawArg .fiberError("attempt to transfer to a yielding fiber", this)); } - final RubyThread currentThread = getLanguage().getCurrentThread(); - final RubyFiber currentFiber = currentThread.getCurrentFiber(); + final RubyFiber currentFiber = getLanguage().getCurrentFiber(); if (sameFiberProfile.profile(currentFiber == toFiber)) { // A Fiber can transfer to itself return fiberTransferNode.singleValue(rawArgs); } - return fiberTransferNode - .executeTransferControlTo(currentThread, currentFiber, toFiber, FiberOperation.TRANSFER, - RubyArguments.getDescriptor(frame), rawArgs); + return fiberTransferNode.execute(currentFiber, toFiber, FiberOperation.TRANSFER, + RubyArguments.getDescriptor(frame), rawArgs); } } @@ -190,8 +186,7 @@ protected Object resume( FiberOperation operation, RubyFiber toFiber, ArgumentsDescriptor descriptor, Object[] args, @Cached BranchProfile errorProfile) { - final RubyThread currentThread = getLanguage().getCurrentThread(); - final RubyFiber currentFiber = currentThread.getCurrentFiber(); + final RubyFiber currentFiber = getLanguage().getCurrentFiber(); if (toFiber.isTerminated()) { errorProfile.enter(); @@ -221,8 +216,7 @@ protected Object resume( coreExceptions().fiberError("attempt to resume a transferring fiber", this)); } - return fiberTransferNode - .executeTransferControlTo(currentThread, currentFiber, toFiber, operation, descriptor, args); + return fiberTransferNode.execute(currentFiber, toFiber, operation, descriptor, args); } } @@ -250,10 +244,8 @@ protected Object raise(RubyFiber fiber, RubyException exception, } if (fiber.status == FiberStatus.SUSPENDED && !fiber.yielding) { - final RubyThread currentThread = getLanguage().getCurrentThread(); - final RubyFiber currentFiber = currentThread.getCurrentFiber(); - return getTransferNode().executeTransferControlTo( - currentThread, + final RubyFiber currentFiber = getLanguage().getCurrentFiber(); + return getTransferNode().execute( currentFiber, fiber, FiberOperation.RAISE, @@ -305,14 +297,12 @@ public abstract static class YieldNode extends CoreMethodArrayArgumentsNode { protected Object fiberYield(VirtualFrame frame, Object[] rawArgs, @Cached BranchProfile errorProfile) { - final RubyThread currentThread = getLanguage().getCurrentThread(); - final RubyFiber currentFiber = currentThread.getCurrentFiber(); + final RubyFiber currentFiber = getLanguage().getCurrentFiber(); final RubyFiber fiberYieldedTo = getContext().fiberManager .getReturnFiber(currentFiber, this, errorProfile); - return fiberTransferNode.executeTransferControlTo( - currentThread, + return fiberTransferNode.execute( currentFiber, fiberYieldedTo, FiberOperation.YIELD, @@ -337,7 +327,7 @@ public abstract static class CurrentNode extends CoreMethodNode { @Specialization protected RubyFiber current() { - return getLanguage().getCurrentThread().getCurrentFiber(); + return getLanguage().getCurrentFiber(); } } @@ -373,7 +363,7 @@ public abstract static class FiberGetCatchTagsNode extends PrimitiveArrayArgumen @Specialization protected RubyArray getCatchTags() { - final RubyFiber currentFiber = getLanguage().getCurrentThread().getCurrentFiber(); + final RubyFiber currentFiber = getLanguage().getCurrentFiber(); return currentFiber.catchTags; } } @@ -394,7 +384,7 @@ public abstract static class IsBlockingNode extends CoreMethodArrayArgumentsNode @Specialization protected Object isBlocking() { - RubyFiber currentFiber = getLanguage().getCurrentThread().getCurrentFiber(); + RubyFiber currentFiber = getLanguage().getCurrentFiber(); if (currentFiber.blocking) { return 1; } else { diff --git a/src/main/java/org/truffleruby/core/fiber/FiberPoolThread.java b/src/main/java/org/truffleruby/core/fiber/FiberPoolThread.java deleted file mode 100644 index 6735889c5e6d..000000000000 --- a/src/main/java/org/truffleruby/core/fiber/FiberPoolThread.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. This - * code is released under a tri EPL/GPL/LGPL license. You can use it, - * redistribute it and/or modify it under the terms of the: - * - * Eclipse Public License version 2.0, or - * GNU General Public License version 2, or - * GNU Lesser General Public License version 2.1. - */ -package org.truffleruby.core.fiber; - -import org.truffleruby.RubyLanguage.ThreadLocalState; - -public final class FiberPoolThread extends Thread { - - public final ThreadLocalState threadLocalState; - - public FiberPoolThread(Runnable target) { - super(target); - threadLocalState = new ThreadLocalState(); - } - -} diff --git a/src/main/java/org/truffleruby/core/fiber/RubyFiber.java b/src/main/java/org/truffleruby/core/fiber/RubyFiber.java index c57aa4cc9f72..07e291f750e6 100644 --- a/src/main/java/org/truffleruby/core/fiber/RubyFiber.java +++ b/src/main/java/org/truffleruby/core/fiber/RubyFiber.java @@ -115,6 +115,10 @@ public boolean isRootFiber() { return rubyThread.getRootFiber() == this; } + public boolean isActive() { + return this == rubyThread.getCurrentFiber(); + } + public boolean isTerminated() { return status == FiberStatus.TERMINATED; } diff --git a/src/main/java/org/truffleruby/core/format/exceptions/CantCompressNegativeException.java b/src/main/java/org/truffleruby/core/format/exceptions/CantCompressNegativeException.java index 6cb39cdb3def..a967f4d03d08 100644 --- a/src/main/java/org/truffleruby/core/format/exceptions/CantCompressNegativeException.java +++ b/src/main/java/org/truffleruby/core/format/exceptions/CantCompressNegativeException.java @@ -9,8 +9,6 @@ */ package org.truffleruby.core.format.exceptions; +@SuppressWarnings("serial") public class CantCompressNegativeException extends FormatException { - - private static final long serialVersionUID = 3584862629702748827L; - } diff --git a/src/main/java/org/truffleruby/core/format/exceptions/CantConvertException.java b/src/main/java/org/truffleruby/core/format/exceptions/CantConvertException.java index afffc2ca94f0..d5d7a56e0ebe 100644 --- a/src/main/java/org/truffleruby/core/format/exceptions/CantConvertException.java +++ b/src/main/java/org/truffleruby/core/format/exceptions/CantConvertException.java @@ -9,10 +9,9 @@ */ package org.truffleruby.core.format.exceptions; +@SuppressWarnings("serial") public class CantConvertException extends FormatException { - private static final long serialVersionUID = -1748812990145250644L; - public CantConvertException(String message) { super(message); } diff --git a/src/main/java/org/truffleruby/core/format/exceptions/FormatException.java b/src/main/java/org/truffleruby/core/format/exceptions/FormatException.java index 048a8f69a960..96b3ab95cfce 100644 --- a/src/main/java/org/truffleruby/core/format/exceptions/FormatException.java +++ b/src/main/java/org/truffleruby/core/format/exceptions/FormatException.java @@ -11,10 +11,9 @@ import com.oracle.truffle.api.nodes.ControlFlowException; +@SuppressWarnings("serial") public class FormatException extends ControlFlowException { - private static final long serialVersionUID = -6570764260422083237L; - private final String message; public FormatException() { diff --git a/src/main/java/org/truffleruby/core/format/exceptions/InvalidFormatException.java b/src/main/java/org/truffleruby/core/format/exceptions/InvalidFormatException.java index b4fa718c664a..835696a6d1fa 100644 --- a/src/main/java/org/truffleruby/core/format/exceptions/InvalidFormatException.java +++ b/src/main/java/org/truffleruby/core/format/exceptions/InvalidFormatException.java @@ -9,10 +9,9 @@ */ package org.truffleruby.core.format.exceptions; +@SuppressWarnings("serial") public class InvalidFormatException extends FormatException { - private static final long serialVersionUID = -6689191131430505495L; - public InvalidFormatException(String message) { super(message); } diff --git a/src/main/java/org/truffleruby/core/format/exceptions/NoImplicitConversionException.java b/src/main/java/org/truffleruby/core/format/exceptions/NoImplicitConversionException.java index 7310913240ab..4cc661e1fb36 100644 --- a/src/main/java/org/truffleruby/core/format/exceptions/NoImplicitConversionException.java +++ b/src/main/java/org/truffleruby/core/format/exceptions/NoImplicitConversionException.java @@ -9,10 +9,9 @@ */ package org.truffleruby.core.format.exceptions; +@SuppressWarnings("serial") public class NoImplicitConversionException extends FormatException { - private static final long serialVersionUID = -2509958825294561087L; - private final Object object; private final String target; diff --git a/src/main/java/org/truffleruby/core/format/exceptions/OutsideOfStringException.java b/src/main/java/org/truffleruby/core/format/exceptions/OutsideOfStringException.java index 43a44c95f212..99a74ae8b5b0 100644 --- a/src/main/java/org/truffleruby/core/format/exceptions/OutsideOfStringException.java +++ b/src/main/java/org/truffleruby/core/format/exceptions/OutsideOfStringException.java @@ -9,8 +9,6 @@ */ package org.truffleruby.core.format.exceptions; +@SuppressWarnings("serial") public class OutsideOfStringException extends FormatException { - - private static final long serialVersionUID = -4122128522293680018L; - } diff --git a/src/main/java/org/truffleruby/core/format/exceptions/RangeException.java b/src/main/java/org/truffleruby/core/format/exceptions/RangeException.java index 20e582c672aa..e225d70b1b4e 100644 --- a/src/main/java/org/truffleruby/core/format/exceptions/RangeException.java +++ b/src/main/java/org/truffleruby/core/format/exceptions/RangeException.java @@ -9,10 +9,9 @@ */ package org.truffleruby.core.format.exceptions; +@SuppressWarnings("serial") public class RangeException extends FormatException { - private static final long serialVersionUID = -6312025908880011931L; - public RangeException(String message) { super(message); } diff --git a/src/main/java/org/truffleruby/core/format/exceptions/TooFewArgumentsException.java b/src/main/java/org/truffleruby/core/format/exceptions/TooFewArgumentsException.java index d3f38f9547c7..0e953843245c 100644 --- a/src/main/java/org/truffleruby/core/format/exceptions/TooFewArgumentsException.java +++ b/src/main/java/org/truffleruby/core/format/exceptions/TooFewArgumentsException.java @@ -9,8 +9,6 @@ */ package org.truffleruby.core.format.exceptions; +@SuppressWarnings("serial") public class TooFewArgumentsException extends FormatException { - - private static final long serialVersionUID = 425804306436596616L; - } diff --git a/src/main/java/org/truffleruby/core/string/CannotConvertBinaryRubyStringToJavaString.java b/src/main/java/org/truffleruby/core/string/CannotConvertBinaryRubyStringToJavaString.java index a2bd4ff30f11..6aec6d51e90f 100644 --- a/src/main/java/org/truffleruby/core/string/CannotConvertBinaryRubyStringToJavaString.java +++ b/src/main/java/org/truffleruby/core/string/CannotConvertBinaryRubyStringToJavaString.java @@ -9,10 +9,9 @@ */ package org.truffleruby.core.string; +@SuppressWarnings("serial") public class CannotConvertBinaryRubyStringToJavaString extends RuntimeException { - private static final long serialVersionUID = 3484665101786967474L; - private final int nonAsciiCharacter; public CannotConvertBinaryRubyStringToJavaString(int nonAsciiCharacter) { diff --git a/src/main/java/org/truffleruby/core/string/ConvertBytes.java b/src/main/java/org/truffleruby/core/string/ConvertBytes.java index 44abd91508a2..2410c477da11 100644 --- a/src/main/java/org/truffleruby/core/string/ConvertBytes.java +++ b/src/main/java/org/truffleruby/core/string/ConvertBytes.java @@ -511,9 +511,8 @@ private BigInteger stringToBig(String str) { return digits[0]; } + @SuppressWarnings("serial") public static class ERange extends RuntimeException { - private static final long serialVersionUID = 3393153027217708024L; - public enum Kind { Overflow, Underflow diff --git a/src/main/java/org/truffleruby/core/string/DoubleConverter.java b/src/main/java/org/truffleruby/core/string/DoubleConverter.java index 0d2ab6277dac..84f7c7046f94 100644 --- a/src/main/java/org/truffleruby/core/string/DoubleConverter.java +++ b/src/main/java/org/truffleruby/core/string/DoubleConverter.java @@ -153,9 +153,8 @@ private double completeCalculation() { return SafeDoubleParser.parseDouble(new String(chars, 0, charsIndex)); } + @SuppressWarnings("serial") static class LightweightNumberFormatException extends NumberFormatException { - private static final long serialVersionUID = 8405843059834590L; - public LightweightNumberFormatException(String message) { super(message); } diff --git a/src/main/java/org/truffleruby/core/thread/ThreadManager.java b/src/main/java/org/truffleruby/core/thread/ThreadManager.java index 8996c058560e..60f9d1522a7a 100644 --- a/src/main/java/org/truffleruby/core/thread/ThreadManager.java +++ b/src/main/java/org/truffleruby/core/thread/ThreadManager.java @@ -10,20 +10,16 @@ package org.truffleruby.core.thread; import java.util.Collections; -import java.util.Map; import java.util.Set; import java.util.Timer; import java.util.WeakHashMap; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CountDownLatch; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; import java.util.concurrent.locks.ReentrantLock; import java.util.function.Consumer; import java.util.function.Supplier; import com.oracle.truffle.api.CompilerAsserts; -import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives.ValueType; import com.oracle.truffle.api.TruffleContext; import com.oracle.truffle.api.TruffleSafepoint; @@ -31,9 +27,9 @@ import com.oracle.truffle.api.TruffleSafepoint.Interrupter; import com.oracle.truffle.api.interop.InteropLibrary; import com.oracle.truffle.api.object.DynamicObjectLibrary; +import com.oracle.truffle.api.source.SourceSection; import org.truffleruby.RubyContext; import org.truffleruby.RubyLanguage; -import org.truffleruby.SuppressFBWarnings; import org.truffleruby.collections.Memo; import org.truffleruby.core.DummyNode; import org.truffleruby.core.InterruptMode; @@ -41,7 +37,6 @@ import org.truffleruby.core.exception.RubyException; import org.truffleruby.core.exception.RubySystemExit; import org.truffleruby.core.fiber.FiberManager; -import org.truffleruby.core.fiber.FiberPoolThread; import org.truffleruby.core.fiber.RubyFiber; import org.truffleruby.core.klass.RubyClass; import org.truffleruby.core.string.StringUtils; @@ -76,34 +71,24 @@ public class ThreadManager { private final RubyThread rootThread; @CompilationFinal private Thread rootJavaThread; - private final Map javaThreadToRubyThread = new ConcurrentHashMap<>(); - private final Set runningRubyThreads = ConcurrentHashMap.newKeySet(); /** The set of Java threads TruffleRuby created, and is responsible to exit in {@link #killAndWaitOtherThreads()}. * Needs to be weak because {@link RubyLanguage#disposeThread} is called late as Fibers use a "host thread" - * (disposeThread is only called on Context#close for those), and there might be multiple Fibers per such thread - * with the pool. If a Thread is unreachable we do not need to wait for it in {@link #killAndWaitOtherThreads()}, - * but otherwise we need to and we can never remove from this Set as otherwise we cannot guarantee we wait until the - * Thread truly finishes execution. */ + * (disposeThread is only called on Context#close for those). If a Thread is unreachable we do not need to wait for + * it in {@link #killAndWaitOtherThreads()}, but otherwise we need to and we can never remove from this Set as + * otherwise we cannot guarantee we wait until the Thread truly finishes execution. */ private final Set rubyManagedThreads = Collections .newSetFromMap(Collections.synchronizedMap(new WeakHashMap<>())); - public final Map javaThreadToRubyFiber = new ConcurrentHashMap<>(); - public final ThreadLocal rubyFiber = ThreadLocal - .withInitial(() -> javaThreadToRubyFiber.get(Thread.currentThread())); - private boolean nativeInterrupt; private Timer nativeInterruptTimer; private ThreadLocal nativeCallInterrupter; - private final ExecutorService fiberPool; - public ThreadManager(RubyContext context, RubyLanguage language) { this.context = context; this.language = language; this.rootThread = createBootThread("main"); - this.fiberPool = Executors.newCachedThreadPool(this::createFiberJavaThread); } public void initialize() { @@ -148,29 +133,24 @@ public void restartMainThread(Thread mainJavaThread) { PRNGRandomizerNodes.resetSeed(context, rootThread.randomizer); } - // spawning Thread => Fiber object - public static final ThreadLocal FIBER_BEING_SPAWNED = new ThreadLocal<>(); - - private Thread createFiberJavaThread(Runnable runnable) { - final RubyFiber fiber = FIBER_BEING_SPAWNED.get(); - assert fiber != null; - + private Thread createFiberJavaThread(RubyFiber fiber, SourceSection sourceSection, Runnable runnable) { if (context.isPreInitializing()) { throw new UnsupportedOperationException("fibers should not be created while pre-initializing the context"); } - final Thread thread = new FiberPoolThread(runnable); // context.getEnv().createUnenteredThread(runnable); - thread.setName("Ruby-FiberPool-" + thread.getName()); + final Thread thread = new Thread(runnable); // context.getEnv().createUnenteredThread(runnable); + + language.rubyThreadInitMap.put(thread, fiber.rubyThread); + language.rubyFiberInitMap.put(thread, fiber); + thread.setName(FiberManager.NAME_PREFIX + " id=" + RubyLanguage.getThreadId(thread) + " from " + + context.fileLine(sourceSection)); thread.setDaemon(true); // GR-33255 rubyManagedThreads.add(thread); // need to be set before initializeThread() - thread.setUncaughtExceptionHandler((javaThread, throwable) -> { - System.err.println("Throwable escaped Fiber pool thread:"); - throwable.printStackTrace(); - }); + thread.setUncaughtExceptionHandler(uncaughtExceptionHandler(fiber)); return thread; } - private Thread createJavaThread(Runnable runnable, RubyThread rubyThread) { + private Thread createJavaThread(Runnable runnable, RubyThread rubyThread, String info) { if (context.getOptions().SINGLE_THREADED) { throw new RaiseException( context, @@ -182,8 +162,11 @@ private Thread createJavaThread(Runnable runnable, RubyThread rubyThread) { } final Thread thread = context.getEnv().createThread(runnable); + + language.rubyThreadInitMap.put(thread, rubyThread); + language.rubyFiberInitMap.put(thread, rubyThread.getRootFiber()); + thread.setName(NAME_PREFIX + " id=" + RubyLanguage.getThreadId(thread) + " from " + info); rubyManagedThreads.add(thread); // need to be set before initializeThread() - javaThreadToRubyThread.put(thread, rubyThread); // need to be set before initializeThread() thread.setUncaughtExceptionHandler(uncaughtExceptionHandler(rubyThread.getRootFiber())); return thread; } @@ -208,23 +191,8 @@ private static Thread.UncaughtExceptionHandler uncaughtExceptionHandler(RubyFibe }; } - @SuppressFBWarnings("RV") - public void spawnFiber(RubyFiber fiber, Runnable task) { - final Runnable body = () -> { - try { - task.run(); - } catch (Throwable t) { - // Fibers are run on a thread-pool, so make sure any exception escaping - // is handled here as the thread pool ignores exceptions. - uncaughtExceptionHandler(fiber).uncaughtException(Thread.currentThread(), t); - } - }; - - if (context.getOptions().FIBER_POOL) { - fiberPool.submit(body); - } else { - createFiberJavaThread(body).start(); - } + public void spawnFiber(RubyFiber fiber, SourceSection sourceSection, Runnable task) { + createFiberJavaThread(fiber, sourceSection, task).start(); } /** Whether the thread was created by TruffleRuby. */ @@ -291,10 +259,8 @@ public void initialize(RubyThread rubyThread, Node currentNode, String info, Str rubyThread.sourceLocation = info; final RubyFiber rootFiber = rubyThread.getRootFiber(); - final Thread thread = createJavaThread(() -> threadMain(rubyThread, currentNode, task), rubyThread); - thread.setName(NAME_PREFIX + " id=" + thread.getId() + " from " + info); + final Thread thread = createJavaThread(() -> threadMain(rubyThread, currentNode, task), rubyThread, info); rubyThread.thread = thread; - javaThreadToRubyThread.put(thread, rubyThread); thread.start(); @@ -621,34 +587,6 @@ Interrupter getNativeCallInterrupter() { } } - public void initializeValuesForJavaThread(RubyThread rubyThread, Thread thread) { - javaThreadToRubyThread.put(thread, rubyThread); - } - - public void cleanupValuesForJavaThread(Thread thread) { - javaThreadToRubyThread.remove(thread); - } - - @TruffleBoundary - public RubyThread getRubyThreadForJavaThread(Thread thread) { - final RubyThread rubyThread = javaThreadToRubyThread.get(thread); - if (rubyThread == null) { - throw CompilerDirectives.shouldNotReachHere( - "No Ruby Thread is associated with Java Thread: " + thread); - } - return rubyThread; - } - - @TruffleBoundary - public RubyThread getCurrentThreadOrNull() { - return javaThreadToRubyThread.get(Thread.currentThread()); - } - - @TruffleBoundary - public RubyFiber getRubyFiberFromCurrentJavaThread() { - return rubyFiber.get(); - } - public void registerThread(RubyThread thread) { if (!runningRubyThreads.add(thread)) { throw new UnsupportedOperationException(thread + " was already registered"); @@ -671,9 +609,6 @@ public void checkNoRunningThreads() { @TruffleBoundary public void killAndWaitOtherThreads() { - // Disallow new Fibers to be created - fiberPool.shutdown(); - // Kill all Ruby Threads and Fibers // The logic below avoids using the SafepointManager if there is @@ -716,7 +651,7 @@ public void killAndWaitOtherThreads() { private void doKillOtherThreads() { final Thread initiatingJavaThread = Thread.currentThread(); SafepointPredicate predicate = (context, thread, action) -> Thread.currentThread() != initiatingJavaThread && - getRubyFiberFromCurrentJavaThread() == thread.getCurrentFiber(); + language.getCurrentFiber() == thread.getCurrentFiber(); context.getSafepointManager().pauseAllThreadsAndExecute( DummyNode.INSTANCE, @@ -752,7 +687,7 @@ public String getThreadDebugInfo() { } // cannot use getCurrentThread() as it might have been cleared - if (thread == getCurrentThreadOrNull()) { + if (thread == language.getCurrentThread()) { builder.append(" (current)"); } diff --git a/src/main/java/org/truffleruby/core/thread/ThreadNodes.java b/src/main/java/org/truffleruby/core/thread/ThreadNodes.java index d14964d1a549..e82c8512fe26 100644 --- a/src/main/java/org/truffleruby/core/thread/ThreadNodes.java +++ b/src/main/java/org/truffleruby/core/thread/ThreadNodes.java @@ -198,7 +198,7 @@ public void run(RubyThread rubyThread, Node currentNode) { final Backtrace backtrace = getContext().getCallStack().getBacktrace(currentNode, 0); backtrace.getStackTrace(); // must be done on the thread - final RubyFiber fiber = getContext().getThreadManager().getRubyFiberFromCurrentJavaThread(); + final RubyFiber fiber = getLanguage().getCurrentFiber(); synchronized (backtraces) { backtraces.add(Pair.create(fiber, backtrace)); diff --git a/src/main/java/org/truffleruby/core/thread/TruffleThreadNodes.java b/src/main/java/org/truffleruby/core/thread/TruffleThreadNodes.java index 57f2a507b315..4a904550524f 100644 --- a/src/main/java/org/truffleruby/core/thread/TruffleThreadNodes.java +++ b/src/main/java/org/truffleruby/core/thread/TruffleThreadNodes.java @@ -66,7 +66,7 @@ protected Object findRubyCaller(RubyArray modules, } else { CallerDataReadingNode.notifyCallerToSendData(getContext(), data.callNode, this); Object variables = storageNode.execute(data.frame.materialize()); - getLanguage().getCurrentThread().getCurrentFiber().extensionCallStack.setSpecialVariables(variables); + getLanguage().getCurrentFiber().extensionCallStack.setSpecialVariables(variables); return variables; } } diff --git a/src/main/java/org/truffleruby/debug/TruffleDebugNodes.java b/src/main/java/org/truffleruby/debug/TruffleDebugNodes.java index 561a678c92ab..afe732b7948e 100644 --- a/src/main/java/org/truffleruby/debug/TruffleDebugNodes.java +++ b/src/main/java/org/truffleruby/debug/TruffleDebugNodes.java @@ -1280,7 +1280,7 @@ protected RubyString parseName(InternalMethod method) { } - /** Creates a Truffle thread that is no {@link ThreadManager#isRubyManagedThread(java.lang.Thread)}}. */ + /** Creates a Truffle thread which is not {@link ThreadManager#isRubyManagedThread(java.lang.Thread)}}. */ @CoreMethod(names = "create_polyglot_thread", onSingleton = true, required = 1) public abstract static class CreatePolyglotThread extends CoreMethodArrayArgumentsNode { @Specialization diff --git a/src/main/java/org/truffleruby/language/NotOptimizedWarningNode.java b/src/main/java/org/truffleruby/language/NotOptimizedWarningNode.java index e95768881932..a920c50fac29 100644 --- a/src/main/java/org/truffleruby/language/NotOptimizedWarningNode.java +++ b/src/main/java/org/truffleruby/language/NotOptimizedWarningNode.java @@ -35,8 +35,8 @@ public static NotOptimizedWarningNode create() { return NotOptimizedWarningNodeGen.create(); } + @SuppressWarnings("serial") protected static class Warned extends ControlFlowException { - private static final long serialVersionUID = 6760505091509903491L; } public final void warn(String message) { diff --git a/src/main/java/org/truffleruby/language/SafepointAction.java b/src/main/java/org/truffleruby/language/SafepointAction.java index adce68c81efb..eab8d659a189 100644 --- a/src/main/java/org/truffleruby/language/SafepointAction.java +++ b/src/main/java/org/truffleruby/language/SafepointAction.java @@ -24,8 +24,8 @@ public abstract class SafepointAction extends ThreadLocalAction { private static final SafepointPredicate CURRENT_FIBER_OF_THREAD = // - (context, thread, action) -> thread == action.getTargetThread() && context.getThreadManager() - .getRubyFiberFromCurrentJavaThread() == action.getTargetThread().getCurrentFiber(); + (context, thread, action) -> thread == action.getTargetThread() && + context.getLanguageSlow().getCurrentFiber() == action.getTargetThread().getCurrentFiber(); private final boolean publicSynchronous; private final String reason; diff --git a/src/main/java/org/truffleruby/language/SafepointManager.java b/src/main/java/org/truffleruby/language/SafepointManager.java index 9a6eaa0f6eae..198fe431fba5 100644 --- a/src/main/java/org/truffleruby/language/SafepointManager.java +++ b/src/main/java/org/truffleruby/language/SafepointManager.java @@ -35,8 +35,8 @@ public void pauseRubyThreadAndExecute(Node currentNode, SafepointAction action) final ThreadManager threadManager = context.getThreadManager(); final RubyThread rubyThread = action.getTargetThread(); - if (threadManager.getCurrentThreadOrNull() == rubyThread) { - if (threadManager.getRubyFiberFromCurrentJavaThread() != rubyThread.getCurrentFiber()) { + if (context.getEnv().getContext().isEntered() && context.getLanguageSlow().getCurrentThread() == rubyThread) { + if (context.getLanguageSlow().getCurrentFiber() != rubyThread.getCurrentFiber()) { throw CompilerDirectives.shouldNotReachHere( "The currently executing Java thread does not correspond to the currently active fiber for the current Ruby thread"); } diff --git a/src/main/java/org/truffleruby/language/constants/AutoloadException.java b/src/main/java/org/truffleruby/language/constants/AutoloadException.java deleted file mode 100644 index a54a499ceb86..000000000000 --- a/src/main/java/org/truffleruby/language/constants/AutoloadException.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. This - * code is released under a tri EPL/GPL/LGPL license. You can use it, - * redistribute it and/or modify it under the terms of the: - * - * Eclipse Public License version 2.0, or - * GNU General Public License version 2, or - * GNU Lesser General Public License version 2.1. - */ -package org.truffleruby.language.constants; - -import com.oracle.truffle.api.exception.AbstractTruffleException; -import org.truffleruby.language.control.RaiseException; - -/** Wraps a {@link RaiseException} occuring during a module autoload, whenever the autoloaded constant is itself the - * module for another constant read. */ -public class AutoloadException extends AbstractTruffleException { - - private static final long serialVersionUID = 1L; - - public final RaiseException raiseException; - - public AutoloadException(RaiseException raiseException) { - this.raiseException = raiseException; - } -} diff --git a/src/main/java/org/truffleruby/language/control/BreakException.java b/src/main/java/org/truffleruby/language/control/BreakException.java index 3284ec7db801..b17ba0d8b010 100644 --- a/src/main/java/org/truffleruby/language/control/BreakException.java +++ b/src/main/java/org/truffleruby/language/control/BreakException.java @@ -11,10 +11,9 @@ import com.oracle.truffle.api.nodes.ControlFlowException; +@SuppressWarnings("serial") public final class BreakException extends ControlFlowException { - private static final long serialVersionUID = -8260344464830705773L; - private final BreakID breakID; private final Object result; diff --git a/src/main/java/org/truffleruby/language/control/DeferredRaiseException.java b/src/main/java/org/truffleruby/language/control/DeferredRaiseException.java index 10e876cded1d..415b3becfaec 100644 --- a/src/main/java/org/truffleruby/language/control/DeferredRaiseException.java +++ b/src/main/java/org/truffleruby/language/control/DeferredRaiseException.java @@ -13,10 +13,9 @@ import org.truffleruby.RubyContext; import org.truffleruby.core.exception.RubyException; +@SuppressWarnings("serial") public class DeferredRaiseException extends Exception { - private static final long serialVersionUID = -9202513314613691124L; - public final ExceptionGetter exceptionGetter; public DeferredRaiseException(ExceptionGetter exceptionGetter) { diff --git a/src/main/java/org/truffleruby/language/control/DynamicReturnException.java b/src/main/java/org/truffleruby/language/control/DynamicReturnException.java index 64a95bb0f18a..0d333ee5dc8a 100644 --- a/src/main/java/org/truffleruby/language/control/DynamicReturnException.java +++ b/src/main/java/org/truffleruby/language/control/DynamicReturnException.java @@ -11,10 +11,9 @@ import com.oracle.truffle.api.nodes.ControlFlowException; +@SuppressWarnings("serial") public final class DynamicReturnException extends ControlFlowException { - private static final long serialVersionUID = -45053969587014940L; - private final ReturnID returnID; private final Object value; diff --git a/src/main/java/org/truffleruby/language/control/ExitException.java b/src/main/java/org/truffleruby/language/control/ExitException.java index 370ddfb109b5..d55e3cb2a4c5 100644 --- a/src/main/java/org/truffleruby/language/control/ExitException.java +++ b/src/main/java/org/truffleruby/language/control/ExitException.java @@ -13,10 +13,9 @@ /** Exception sent by the hard Kernel#exit! */ // TODO: should not be an AbstractTruffleException and not run ensure, like in CRuby +@SuppressWarnings("serial") public final class ExitException extends TerminationException { - private static final long serialVersionUID = 8152389017577849952L; - private final int code; public ExitException(int code, Node location) { diff --git a/src/main/java/org/truffleruby/language/control/KillException.java b/src/main/java/org/truffleruby/language/control/KillException.java index 343e84955065..76a1a72c19bb 100644 --- a/src/main/java/org/truffleruby/language/control/KillException.java +++ b/src/main/java/org/truffleruby/language/control/KillException.java @@ -12,10 +12,8 @@ import com.oracle.truffle.api.nodes.Node; /** Used by Thread#kill and to terminate threads. */ +@SuppressWarnings("serial") public final class KillException extends TerminationException { - - private static final long serialVersionUID = 4546683467567415385L; - public KillException(Node location) { super("Thread#kill", location); } diff --git a/src/main/java/org/truffleruby/language/control/LocalReturnException.java b/src/main/java/org/truffleruby/language/control/LocalReturnException.java index 53740bde1201..ce3b9a255014 100644 --- a/src/main/java/org/truffleruby/language/control/LocalReturnException.java +++ b/src/main/java/org/truffleruby/language/control/LocalReturnException.java @@ -11,10 +11,9 @@ import com.oracle.truffle.api.nodes.ControlFlowException; +@SuppressWarnings("serial") public final class LocalReturnException extends ControlFlowException { - private static final long serialVersionUID = -98757896543565476L; - private final Object value; public LocalReturnException(Object value) { diff --git a/src/main/java/org/truffleruby/language/control/NextException.java b/src/main/java/org/truffleruby/language/control/NextException.java index 5577eef1033b..e95e5c1fb786 100644 --- a/src/main/java/org/truffleruby/language/control/NextException.java +++ b/src/main/java/org/truffleruby/language/control/NextException.java @@ -11,10 +11,9 @@ import com.oracle.truffle.api.nodes.ControlFlowException; +@SuppressWarnings("serial") public final class NextException extends ControlFlowException { - private static final long serialVersionUID = 1354120079185876952L; - private final Object result; public NextException(Object result) { diff --git a/src/main/java/org/truffleruby/language/control/RaiseException.java b/src/main/java/org/truffleruby/language/control/RaiseException.java index 0ad543b40e70..c091ff60144f 100644 --- a/src/main/java/org/truffleruby/language/control/RaiseException.java +++ b/src/main/java/org/truffleruby/language/control/RaiseException.java @@ -21,11 +21,10 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; /** A ControlFlowException holding a Ruby exception. */ +@SuppressWarnings("serial") @ExportLibrary(value = InteropLibrary.class, delegateTo = "exception") public final class RaiseException extends AbstractTruffleException { - private static final long serialVersionUID = -4128190563044417424L; - protected final RubyException exception; public RaiseException(RubyContext context, RubyException exception) { diff --git a/src/main/java/org/truffleruby/language/control/RedoException.java b/src/main/java/org/truffleruby/language/control/RedoException.java index 3b5fc9027d62..60f33a6480c4 100644 --- a/src/main/java/org/truffleruby/language/control/RedoException.java +++ b/src/main/java/org/truffleruby/language/control/RedoException.java @@ -11,8 +11,6 @@ import com.oracle.truffle.api.nodes.ControlFlowException; +@SuppressWarnings("serial") public final class RedoException extends ControlFlowException { - - private static final long serialVersionUID = 7279456434679526564L; - } diff --git a/src/main/java/org/truffleruby/language/control/RetryException.java b/src/main/java/org/truffleruby/language/control/RetryException.java index bfe27c25a84a..d31df9ebd492 100644 --- a/src/main/java/org/truffleruby/language/control/RetryException.java +++ b/src/main/java/org/truffleruby/language/control/RetryException.java @@ -11,8 +11,6 @@ import com.oracle.truffle.api.nodes.ControlFlowException; +@SuppressWarnings("serial") public final class RetryException extends ControlFlowException { - - private static final long serialVersionUID = -6020762812635743383L; - } diff --git a/src/main/java/org/truffleruby/language/control/TerminationException.java b/src/main/java/org/truffleruby/language/control/TerminationException.java index d7116bf724ec..df5abe247ee0 100644 --- a/src/main/java/org/truffleruby/language/control/TerminationException.java +++ b/src/main/java/org/truffleruby/language/control/TerminationException.java @@ -17,11 +17,10 @@ import com.oracle.truffle.api.library.ExportMessage; import com.oracle.truffle.api.nodes.Node; +@SuppressWarnings("serial") @ExportLibrary(InteropLibrary.class) public abstract class TerminationException extends AbstractTruffleException { - private static final long serialVersionUID = 2170201891607264355L; - @TruffleBoundary private static RuntimeException javaStacktrace() { return new RuntimeException(); diff --git a/src/main/java/org/truffleruby/language/control/ThrowException.java b/src/main/java/org/truffleruby/language/control/ThrowException.java index e6023f7e904b..d89728fbea25 100644 --- a/src/main/java/org/truffleruby/language/control/ThrowException.java +++ b/src/main/java/org/truffleruby/language/control/ThrowException.java @@ -11,10 +11,9 @@ import com.oracle.truffle.api.nodes.ControlFlowException; +@SuppressWarnings("serial") public class ThrowException extends ControlFlowException { - private static final long serialVersionUID = 5996793715653695919L; - private final Object tag; private final Object value; diff --git a/src/main/java/org/truffleruby/language/loader/FeatureLoader.java b/src/main/java/org/truffleruby/language/loader/FeatureLoader.java index 2071be83d896..e67b68d9525a 100644 --- a/src/main/java/org/truffleruby/language/loader/FeatureLoader.java +++ b/src/main/java/org/truffleruby/language/loader/FeatureLoader.java @@ -462,7 +462,7 @@ public void ensureCExtImplementationLoaded(String feature, RequireNode requireNo final Object initFunction = findFunctionInLibrary(library, "rb_tr_init", rubyLibPath); final InteropLibrary interop = InteropLibrary.getFactory().getUncached(); - language.getCurrentThread().getCurrentFiber().extensionCallStack.push(false, nil, nil); + language.getCurrentFiber().extensionCallStack.push(false, nil, nil); try { // rb_tr_init(Truffle::CExt) interop.execute(initFunction, truffleCExt); @@ -471,7 +471,7 @@ public void ensureCExtImplementationLoaded(String feature, RequireNode requireNo } catch (InteropException e) { throw TranslateInteropExceptionNode.getUncached().execute(e); } finally { - language.getCurrentThread().getCurrentFiber().extensionCallStack.pop(); + language.getCurrentFiber().extensionCallStack.pop(); } } finally { Metrics.printTime("after-load-cext-support"); diff --git a/src/main/java/org/truffleruby/language/loader/RequireNode.java b/src/main/java/org/truffleruby/language/loader/RequireNode.java index 3d799ed1e8ea..6da12391862e 100644 --- a/src/main/java/org/truffleruby/language/loader/RequireNode.java +++ b/src/main/java/org/truffleruby/language/loader/RequireNode.java @@ -285,7 +285,7 @@ private void requireCExtension(String feature, String expandedPath, Node current requireMetric("before-execute-" + feature); ValueWrapperManager.allocateNewBlock(getContext(), getLanguage()); - getLanguage().getCurrentThread().getCurrentFiber().extensionCallStack.push(false, nil, nil); + getLanguage().getCurrentFiber().extensionCallStack.push(false, nil, nil); try { InteropNodes .execute( @@ -294,7 +294,7 @@ private void requireCExtension(String feature, String expandedPath, Node current initFunctionInteropLibrary, TranslateInteropExceptionNode.getUncached()); } finally { - getLanguage().getCurrentThread().getCurrentFiber().extensionCallStack.pop(); + getLanguage().getCurrentFiber().extensionCallStack.pop(); ValueWrapperManager.allocateNewBlock(getContext(), getLanguage()); requireMetric("after-execute-" + feature); } diff --git a/src/main/java/org/truffleruby/language/threadlocal/ThreadAndFrameLocalStorage.java b/src/main/java/org/truffleruby/language/threadlocal/ThreadAndFrameLocalStorage.java index fe3364cd627d..fe200631f144 100644 --- a/src/main/java/org/truffleruby/language/threadlocal/ThreadAndFrameLocalStorage.java +++ b/src/main/java/org/truffleruby/language/threadlocal/ThreadAndFrameLocalStorage.java @@ -10,6 +10,7 @@ package org.truffleruby.language.threadlocal; import org.truffleruby.RubyContext; +import org.truffleruby.RubyLanguage; import org.truffleruby.language.Nil; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; @@ -26,12 +27,12 @@ public class ThreadAndFrameLocalStorage { public ThreadAndFrameLocalStorage(RubyContext context) { // Cannot store a Thread id while pre-initializing - originalThreadId = context.isPreInitializing() ? 0 : Thread.currentThread().getId(); + originalThreadId = context.isPreInitializing() ? 0 : RubyLanguage.getThreadId(Thread.currentThread()); originalThreadValue = Nil.INSTANCE; } public Object get(ConditionProfile sameThreadProfile) { - if (sameThreadProfile.profile(Thread.currentThread().getId() == originalThreadId)) { + if (sameThreadProfile.profile(RubyLanguage.getThreadId(Thread.currentThread()) == originalThreadId)) { return originalThreadValue; } else { return fallbackGet(); @@ -60,7 +61,7 @@ private Object fallbackGet() { } public void set(Object value, ConditionProfile sameThreadProfile) { - if (sameThreadProfile.profile(Thread.currentThread().getId() == originalThreadId)) { + if (sameThreadProfile.profile(RubyLanguage.getThreadId(Thread.currentThread()) == originalThreadId)) { originalThreadValue = value; } else { fallbackSet(value); diff --git a/src/main/java/org/truffleruby/options/Options.java b/src/main/java/org/truffleruby/options/Options.java index 4810cbd077de..2f776424f16b 100644 --- a/src/main/java/org/truffleruby/options/Options.java +++ b/src/main/java/org/truffleruby/options/Options.java @@ -72,8 +72,6 @@ public class Options { public final boolean PATCHING; /** --hashing-deterministic=false */ public final boolean HASHING_DETERMINISTIC; - /** --fiber-pool=false */ - public final boolean FIBER_POOL; /** --log-subprocess=false */ public final boolean LOG_SUBPROCESS; /** --warn-locale=true */ @@ -234,7 +232,6 @@ public Options(Env env, OptionValues options, LanguageOptions languageOptions) { PATTERN_MATCHING = options.get(OptionsCatalog.PATTERN_MATCHING_KEY); PATCHING = options.get(OptionsCatalog.PATCHING_KEY); HASHING_DETERMINISTIC = options.get(OptionsCatalog.HASHING_DETERMINISTIC_KEY); - FIBER_POOL = options.get(OptionsCatalog.FIBER_POOL_KEY); LOG_SUBPROCESS = options.get(OptionsCatalog.LOG_SUBPROCESS_KEY); WARN_LOCALE = options.get(OptionsCatalog.WARN_LOCALE_KEY); EXCEPTIONS_STORE_JAVA = options.get(OptionsCatalog.EXCEPTIONS_STORE_JAVA_KEY); @@ -354,8 +351,6 @@ public Object fromDescriptor(OptionDescriptor descriptor) { return PATCHING; case "ruby.hashing-deterministic": return HASHING_DETERMINISTIC; - case "ruby.fiber-pool": - return FIBER_POOL; case "ruby.log-subprocess": return LOG_SUBPROCESS; case "ruby.warn-locale": diff --git a/src/main/java/org/truffleruby/options/UnknownOptionException.java b/src/main/java/org/truffleruby/options/UnknownOptionException.java deleted file mode 100644 index c924526c562c..000000000000 --- a/src/main/java/org/truffleruby/options/UnknownOptionException.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (c) 2016, 2019 Oracle and/or its affiliates. All rights reserved. This - * code is released under a tri EPL/GPL/LGPL license. You can use it, - * redistribute it and/or modify it under the terms of the: - * - * Eclipse Public License version 2.0, or - * GNU General Public License version 2, or - * GNU Lesser General Public License version 2.1. - */ -package org.truffleruby.options; - -public class UnknownOptionException extends UnsupportedOperationException { - - private static final long serialVersionUID = 94889894853948L; - - private final String name; - - public UnknownOptionException(String name) { - super(String.format("Unknown option %s", name)); - this.name = name; - } - - public String getName() { - return name; - } - -} diff --git a/src/main/java/org/truffleruby/parser/lexer/SyntaxException.java b/src/main/java/org/truffleruby/parser/lexer/SyntaxException.java index d724d8493e7a..8f63e1472c56 100644 --- a/src/main/java/org/truffleruby/parser/lexer/SyntaxException.java +++ b/src/main/java/org/truffleruby/parser/lexer/SyntaxException.java @@ -28,6 +28,7 @@ ***** END LICENSE BLOCK *****/ package org.truffleruby.parser.lexer; +@SuppressWarnings("serial") public class SyntaxException extends RuntimeException { public enum PID { BAD_HEX_NUMBER("BAD_HEX_NUMBER"), @@ -70,8 +71,6 @@ public String getID() { } } - private static final long serialVersionUID = -2130930815167932274L; - private String file; private int line; private PID pid; diff --git a/src/options.yml b/src/options.yml index 8f9d74a3b700..ceed5aa73e87 100644 --- a/src/options.yml +++ b/src/options.yml @@ -102,7 +102,6 @@ EXPERT: LAZY_TRANSLATION_USER: [lazy-translation-user, boolean, LAZY_CALLTARGETS, 'Lazily translation of stdlib, gem and user source files'] PATCHING: [patching, boolean, true, Use patching] HASHING_DETERMINISTIC: [hashing-deterministic, boolean, false, Produce deterministic hash values] - FIBER_POOL: [fiber-pool, boolean, false, 'Use a thread pool to speed up creating Fibers'] LOG_SUBPROCESS: [log-subprocess, boolean, false, 'Log whenever a subprocess is created'] # Also see --log-process-args WARN_LOCALE: [warn-locale, boolean, true, 'Warn when the system locale is not set properly'] diff --git a/src/shared/java/org/truffleruby/shared/options/OptionTypeException.java b/src/shared/java/org/truffleruby/shared/options/OptionTypeException.java deleted file mode 100644 index 8a1e911ba798..000000000000 --- a/src/shared/java/org/truffleruby/shared/options/OptionTypeException.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (c) 2016, 2019 Oracle and/or its affiliates. All rights reserved. This - * code is released under a tri EPL/GPL/LGPL license. You can use it, - * redistribute it and/or modify it under the terms of the: - * - * Eclipse Public License version 2.0, or - * GNU General Public License version 2, or - * GNU Lesser General Public License version 2.1. - */ -package org.truffleruby.shared.options; - -public class OptionTypeException extends UnsupportedOperationException { - - private static final long serialVersionUID = 9479324724903L; - - private final String name; - - public OptionTypeException(String name, String value) { - super(String.format("Unsupported value '%s' for option %s", value, name)); - this.name = name; - } - - public String getName() { - return name; - } - -} diff --git a/src/shared/java/org/truffleruby/shared/options/OptionsCatalog.java b/src/shared/java/org/truffleruby/shared/options/OptionsCatalog.java index 47e4263582cc..8701c296d722 100644 --- a/src/shared/java/org/truffleruby/shared/options/OptionsCatalog.java +++ b/src/shared/java/org/truffleruby/shared/options/OptionsCatalog.java @@ -53,7 +53,6 @@ public class OptionsCatalog { public static final OptionKey LAZY_TRANSLATION_USER_KEY = new OptionKey<>(LAZY_CALLTARGETS_KEY.getDefaultValue()); public static final OptionKey PATCHING_KEY = new OptionKey<>(true); public static final OptionKey HASHING_DETERMINISTIC_KEY = new OptionKey<>(false); - public static final OptionKey FIBER_POOL_KEY = new OptionKey<>(false); public static final OptionKey LOG_SUBPROCESS_KEY = new OptionKey<>(false); public static final OptionKey WARN_LOCALE_KEY = new OptionKey<>(true); public static final OptionKey EXCEPTIONS_STORE_JAVA_KEY = new OptionKey<>(false); @@ -428,14 +427,6 @@ public class OptionsCatalog { .usageSyntax("") .build(); - public static final OptionDescriptor FIBER_POOL = OptionDescriptor - .newBuilder(FIBER_POOL_KEY, "ruby.fiber-pool") - .help("Use a thread pool to speed up creating Fibers") - .category(OptionCategory.EXPERT) - .stability(OptionStability.EXPERIMENTAL) - .usageSyntax("") - .build(); - public static final OptionDescriptor LOG_SUBPROCESS = OptionDescriptor .newBuilder(LOG_SUBPROCESS_KEY, "ruby.log-subprocess") .help("Log whenever a subprocess is created") @@ -1376,8 +1367,6 @@ public static OptionDescriptor fromName(String name) { return PATCHING; case "ruby.hashing-deterministic": return HASHING_DETERMINISTIC; - case "ruby.fiber-pool": - return FIBER_POOL; case "ruby.log-subprocess": return LOG_SUBPROCESS; case "ruby.warn-locale": @@ -1636,7 +1625,6 @@ public static OptionDescriptor[] allDescriptors() { LAZY_TRANSLATION_USER, PATCHING, HASHING_DETERMINISTIC, - FIBER_POOL, LOG_SUBPROCESS, WARN_LOCALE, EXCEPTIONS_STORE_JAVA, diff --git a/src/test/java/org/truffleruby/LeakTest.java b/src/test/java/org/truffleruby/LeakTest.java index fba7cffaeca8..3e77790c5d4c 100644 --- a/src/test/java/org/truffleruby/LeakTest.java +++ b/src/test/java/org/truffleruby/LeakTest.java @@ -69,9 +69,8 @@ public static void main(String[] args) { private String code; private List forbiddenClasses = new ArrayList<>(); + @SuppressWarnings("serial") private final class SystemExit extends RuntimeException { - private static final long serialVersionUID = 1L; - public SystemExit() { super(null, null); } diff --git a/src/test/java/org/truffleruby/MiscTest.java b/src/test/java/org/truffleruby/MiscTest.java index 9250d2ace9bf..1b43aebc5462 100644 --- a/src/test/java/org/truffleruby/MiscTest.java +++ b/src/test/java/org/truffleruby/MiscTest.java @@ -132,6 +132,7 @@ public void testForeignThread() throws Throwable { Runnable polyglotThreadBody = () -> { context.eval("ruby", "$queue.pop"); assertTrue(context.eval("ruby", "Thread.current").toString().contains("")); + assertTrue(context.eval("ruby", "Thread.current.equal?(Thread.current)").asBoolean()); }; Thread polyglotThread = context .eval("ruby", "Truffle::Debug") diff --git a/tool/jt.rb b/tool/jt.rb index fcdc9d9297e7..441c11c4a51e 100755 --- a/tool/jt.rb +++ b/tool/jt.rb @@ -63,6 +63,8 @@ # Expand GEM_HOME relative to cwd so it cannot be misinterpreted later. ENV['GEM_HOME'] = File.expand_path(ENV['GEM_HOME']) if ENV['GEM_HOME'] +JDK_VERSIONS = [11, 17, 19] + MRI_TEST_RELATIVE_PREFIX = 'test/mri/tests' MRI_TEST_PREFIX = "#{TRUFFLERUBY_DIR}/#{MRI_TEST_RELATIVE_PREFIX}" MRI_TEST_CEXT_DIR = "#{MRI_TEST_PREFIX}/cext-c" @@ -748,7 +750,7 @@ def help The default can be changed with `export RUBY_BIN=RUBY_SELECTOR` --silent|-q Does not print the command and which Ruby is used --verbose|-v Print more details and commands - --jdk Specifies which version of the JDK should be used: 11 (default) or 17 + --jdk Specifies which version of the JDK should be used: #{JDK_VERSIONS.join(' or ')} jt build [graalvm|parser|options|core-symbols] ... by default it builds graalvm jt build parser|options|core-symbols @@ -860,7 +862,7 @@ def help OPENSSL_PREFIX Where to find OpenSSL headers and libraries ECLIPSE_EXE Where to find Eclipse SYSTEM_RUBY The Ruby interpreter to run 'jt' itself, when using 'bin/jt' - JT_JDK The default JDK version to use: 11 (default) or 17 + JT_JDK The default JDK version to use: #{JDK_VERSIONS.join(' or ')} JT_ENV The default value for 'jt build --env JT_ENV' and for 'jt --use JT_ENV' JT_PROFILE_SUBCOMMANDS Print the time each subprocess takes on stderr JT_SPECS_COMPILATION Controls whether Graal will be used when running specs (default: 'true'). Only affects JVM with Graal. Set to 'false' to disable for running specs faster when developing with jvm-ce. @@ -2231,13 +2233,8 @@ def install(name, *options) end private def install_jvmci(download_message, ee, jdk_version: @jdk_version) - if jdk_version == 11 - jdk_name = ee ? 'labsjdk-ee-11' : 'labsjdk-ce-11' - elsif jdk_version == 17 - jdk_name = ee ? 'labsjdk-ee-17' : 'labsjdk-ce-17' - else - raise "Unknown JDK version: #{jdk_version}" - end + raise "Unknown JDK version: #{jdk_version}" unless JDK_VERSIONS.include?(jdk_version) + jdk_name = ee ? "labsjdk-ee-#{jdk_version}" : "labsjdk-ce-#{jdk_version}" java_home = "#{JDKS_CACHE_DIR}/#{jdk_name}-#{jvmci_version}" unless File.directory?(java_home) @@ -3097,7 +3094,7 @@ def process_pre_args(args) end end - raise "Invalid JDK version: #{@jdk_version}" unless [11, 17].include?(@jdk_version) + raise "Invalid JDK version: #{@jdk_version}" unless JDK_VERSIONS.include?(@jdk_version) if needs_rebuild rebuild From 9735585483085b4b79eaee6e409f588a5fea56d9 Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Mon, 12 Sep 2022 19:42:19 +0000 Subject: [PATCH 02/11] [GR-32001] Replace ReadCallerFrameNode by AlwaysInlinedMethodNode PullRequest: truffleruby/3458 (cherry picked from commit 47b44a47ff3114607f34e3a2edb40e7f3953fdbb) --- CHANGELOG.md | 3 + .../core/basicobject/instance_eval_spec.rb | 68 +++- spec/ruby/core/module/define_method_spec.rb | 43 ++- spec/ruby/core/module/instance_method_spec.rb | 34 +- spec/ruby/core/module/shared/class_eval.rb | 21 +- .../core/basicobject/instance_eval_tags.txt | 1 + spec/tags/core/module/class_eval_tags.txt | 1 + spec/tags/core/module/module_eval_tags.txt | 1 + spec/tags/core/proc/new_tags.txt | 4 - spec/truffle/always_inlined_spec.rb | 36 ++ spec/truffle/caller_data_spec.rb | 26 -- src/main/.checkstyle_checks.xml | 2 +- .../core/basicobject/BasicObjectNodes.java | 148 ++++---- .../core/binding/BindingNodes.java | 26 -- .../core/exception/CoreExceptions.java | 77 +---- .../core/inlined/AlwaysInlinedMethodNode.java | 5 + .../truffleruby/core/module/ModuleNodes.java | 318 +++++++++--------- .../org/truffleruby/core/proc/ProcNodes.java | 41 +-- .../truffleruby/core/symbol/SymbolNodes.java | 38 +-- .../FrameAndVariablesSendingNode.java | 8 +- .../arguments/ReadCallerFrameNode.java | 43 --- 21 files changed, 453 insertions(+), 491 deletions(-) delete mode 100644 src/main/java/org/truffleruby/language/arguments/ReadCallerFrameNode.java diff --git a/CHANGELOG.md b/CHANGELOG.md index e648e7f00ba0..d64503ab4fd2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,6 +38,9 @@ Compatibility: * Implement `rb_ivar_foreach` to iterate over instance and class variables like in CRuby (#2701, @aardvark179). * Fix the absolute path of the main script after chdir (#2709, @eregon). * Fix exception for `Fiddle::Handle.new` with a missing library (#2714, @eregon). +* Fix arguments implicit type conversion for `BasicObject#instance_eval`, `Module#class_eval`, `Module#module_eval`, `Module#define_method` (@andrykonchin). +* Raise `ArgumentError` unconditionally when `Proc.new` is called without a block argument (@andrykonchin). + Performance: diff --git a/spec/ruby/core/basicobject/instance_eval_spec.rb b/spec/ruby/core/basicobject/instance_eval_spec.rb index b6a146095d83..350b08a30eab 100644 --- a/spec/ruby/core/basicobject/instance_eval_spec.rb +++ b/spec/ruby/core/basicobject/instance_eval_spec.rb @@ -20,12 +20,18 @@ a.instance_eval('self').equal?(a).should be_true end - it "expects a block with no arguments" do - -> { "hola".instance_eval }.should raise_error(ArgumentError) + it "raises an ArgumentError when no arguments and no block are given" do + -> { "hola".instance_eval }.should raise_error(ArgumentError, "wrong number of arguments (given 0, expected 1..3)") end - it "takes no arguments with a block" do - -> { "hola".instance_eval(4, 5) {|a,b| a + b } }.should raise_error(ArgumentError) + it "raises an ArgumentError when a block and normal arguments are given" do + -> { "hola".instance_eval(4, 5) {|a,b| a + b } }.should raise_error(ArgumentError, "wrong number of arguments (given 2, expected 0)") + end + + it "raises an ArgumentError when more than 3 arguments are given" do + -> { + "hola".instance_eval("1 + 1", "some file", 0, "bogus") + }.should raise_error(ArgumentError, "wrong number of arguments (given 4, expected 1..3)") end it "yields the object to the block" do @@ -185,4 +191,58 @@ def meth(arg); arg; end x.should == :value end + + it "converts string argument with #to_str method" do + source_code = Object.new + def source_code.to_str() "1" end + + a = BasicObject.new + a.instance_eval(source_code).should == 1 + end + + it "raises ArgumentError if returned value is not String" do + source_code = Object.new + def source_code.to_str() :symbol end + + a = BasicObject.new + -> { a.instance_eval(source_code) }.should raise_error(TypeError, /can't convert Object to String/) + end + + it "converts filename argument with #to_str method" do + filename = Object.new + def filename.to_str() "file.rb" end + + err = begin + Object.new.instance_eval("raise", filename) + rescue => e + e + end + err.backtrace.first.split(":")[0].should == "file.rb" + end + + it "raises ArgumentError if returned value is not String" do + filename = Object.new + def filename.to_str() :symbol end + + -> { Object.new.instance_eval("raise", filename) }.should raise_error(TypeError, /can't convert Object to String/) + end + + it "converts lineno argument with #to_int method" do + lineno = Object.new + def lineno.to_int() 15 end + + err = begin + Object.new.instance_eval("raise", "file.rb", lineno) + rescue => e + e + end + err.backtrace.first.split(":")[1].should == "15" + end + + it "raises ArgumentError if returned value is not Integer" do + lineno = Object.new + def lineno.to_int() :symbol end + + -> { Object.new.instance_eval("raise", "file.rb", lineno) }.should raise_error(TypeError, /can't convert Object to Integer/) + end end diff --git a/spec/ruby/core/module/define_method_spec.rb b/spec/ruby/core/module/define_method_spec.rb index c65b30de2450..ce94436bfd68 100644 --- a/spec/ruby/core/module/define_method_spec.rb +++ b/spec/ruby/core/module/define_method_spec.rb @@ -219,18 +219,55 @@ class DefineMethodSpecClass o.block_test2.should == o end + it "raises TypeError if name cannot converted to String" do + -> { + Class.new { define_method(1001, -> {}) } + }.should raise_error(TypeError, /is not a symbol nor a string/) + + -> { + Class.new { define_method([], -> {}) } + }.should raise_error(TypeError, /is not a symbol nor a string/) + end + + it "converts non-String name to String with #to_str" do + obj = Object.new + def obj.to_str() "foo" end + + new_class = Class.new { define_method(obj, -> { :called }) } + new_class.new.foo.should == :called + end + + it "raises TypeError when #to_str called on non-String name returns non-String value" do + obj = Object.new + def obj.to_str() [] end + + -> { + Class.new { define_method(obj, -> {}) } + }.should raise_error(TypeError, /can't convert Object to String/) + end + it "raises a TypeError when the given method is no Method/Proc" do -> { Class.new { define_method(:test, "self") } - }.should raise_error(TypeError) + }.should raise_error(TypeError, "wrong argument type String (expected Proc/Method/UnboundMethod)") -> { Class.new { define_method(:test, 1234) } - }.should raise_error(TypeError) + }.should raise_error(TypeError, "wrong argument type Integer (expected Proc/Method/UnboundMethod)") -> { Class.new { define_method(:test, nil) } - }.should raise_error(TypeError) + }.should raise_error(TypeError, "wrong argument type NilClass (expected Proc/Method/UnboundMethod)") + end + + it "uses provided Method/Proc even if block is specified" do + new_class = Class.new do + define_method(:test, -> { :method_is_called }) do + :block_is_called + end + end + + new_class.new.test.should == :method_is_called end it "raises an ArgumentError when no block is given" do diff --git a/spec/ruby/core/module/instance_method_spec.rb b/spec/ruby/core/module/instance_method_spec.rb index b4d6a0d8c803..30e27bb18095 100644 --- a/spec/ruby/core/module/instance_method_spec.rb +++ b/spec/ruby/core/module/instance_method_spec.rb @@ -51,14 +51,36 @@ @mod_um.inspect.should =~ /\bModuleSpecs::InstanceMethChild\b/ end - it "raises a TypeError if not passed a symbol" do - -> { Object.instance_method([]) }.should raise_error(TypeError) - -> { Object.instance_method(0) }.should raise_error(TypeError) + it "raises a TypeError if the given name is not a String/Symbol" do + -> { Object.instance_method([]) }.should raise_error(TypeError, /is not a symbol nor a string/) + -> { Object.instance_method(0) }.should raise_error(TypeError, /is not a symbol nor a string/) + -> { Object.instance_method(nil) }.should raise_error(TypeError, /is not a symbol nor a string/) + -> { Object.instance_method(mock('x')) }.should raise_error(TypeError, /is not a symbol nor a string/) end - it "raises a TypeError if the given name is not a string/symbol" do - -> { Object.instance_method(nil) }.should raise_error(TypeError) - -> { Object.instance_method(mock('x')) }.should raise_error(TypeError) + it "accepts String name argument" do + method = ModuleSpecs::InstanceMeth.instance_method(:foo) + method.should be_kind_of(UnboundMethod) + end + + it "accepts Symbol name argument" do + method = ModuleSpecs::InstanceMeth.instance_method("foo") + method.should be_kind_of(UnboundMethod) + end + + it "converts non-String name by calling #to_str method" do + obj = Object.new + def obj.to_str() "foo" end + + method = ModuleSpecs::InstanceMeth.instance_method(obj) + method.should be_kind_of(UnboundMethod) + end + + it "raises TypeError when passed non-String name and #to_str returns non-String value" do + obj = Object.new + def obj.to_str() [] end + + -> { ModuleSpecs::InstanceMeth.instance_method(obj) }.should raise_error(TypeError, /can't convert Object to String/) end it "raises a NameError if the method has been undefined" do diff --git a/spec/ruby/core/module/shared/class_eval.rb b/spec/ruby/core/module/shared/class_eval.rb index 224078ae548a..9ef7b5be44c5 100644 --- a/spec/ruby/core/module/shared/class_eval.rb +++ b/spec/ruby/core/module/shared/class_eval.rb @@ -55,40 +55,49 @@ def foo it "converts a non-string filename to a string using to_str" do (file = mock(__FILE__)).should_receive(:to_str).and_return(__FILE__) ModuleSpecs.send(@method, "1+1", file) + + (file = mock(__FILE__)).should_receive(:to_str).and_return(__FILE__) + ModuleSpecs.send(@method, "1+1", file, 15) end it "raises a TypeError when the given filename can't be converted to string using to_str" do (file = mock('123')).should_receive(:to_str).and_return(123) - -> { ModuleSpecs.send(@method, "1+1", file) }.should raise_error(TypeError) + -> { ModuleSpecs.send(@method, "1+1", file) }.should raise_error(TypeError, /can't convert MockObject to String/) end it "converts non string eval-string to string using to_str" do (o = mock('1 + 1')).should_receive(:to_str).and_return("1 + 1") ModuleSpecs.send(@method, o).should == 2 + + (o = mock('1 + 1')).should_receive(:to_str).and_return("1 + 1") + ModuleSpecs.send(@method, o, "file.rb").should == 2 + + (o = mock('1 + 1')).should_receive(:to_str).and_return("1 + 1") + ModuleSpecs.send(@method, o, "file.rb", 15).should == 2 end it "raises a TypeError when the given eval-string can't be converted to string using to_str" do o = mock('x') - -> { ModuleSpecs.send(@method, o) }.should raise_error(TypeError) + -> { ModuleSpecs.send(@method, o) }.should raise_error(TypeError, "no implicit conversion of MockObject into String") (o = mock('123')).should_receive(:to_str).and_return(123) - -> { ModuleSpecs.send(@method, o) }.should raise_error(TypeError) + -> { ModuleSpecs.send(@method, o) }.should raise_error(TypeError, /can't convert MockObject to String/) end it "raises an ArgumentError when no arguments and no block are given" do - -> { ModuleSpecs.send(@method) }.should raise_error(ArgumentError) + -> { ModuleSpecs.send(@method) }.should raise_error(ArgumentError, "wrong number of arguments (given 0, expected 1..3)") end it "raises an ArgumentError when more than 3 arguments are given" do -> { ModuleSpecs.send(@method, "1 + 1", "some file", 0, "bogus") - }.should raise_error(ArgumentError) + }.should raise_error(ArgumentError, "wrong number of arguments (given 4, expected 1..3)") end it "raises an ArgumentError when a block and normal arguments are given" do -> { ModuleSpecs.send(@method, "1 + 1") { 1 + 1 } - }.should raise_error(ArgumentError) + }.should raise_error(ArgumentError, "wrong number of arguments (given 1, expected 0)") end # This case was found because Rubinius was caching the compiled diff --git a/spec/tags/core/basicobject/instance_eval_tags.txt b/spec/tags/core/basicobject/instance_eval_tags.txt index 67762604be84..7da91df6984d 100644 --- a/spec/tags/core/basicobject/instance_eval_tags.txt +++ b/spec/tags/core/basicobject/instance_eval_tags.txt @@ -1 +1,2 @@ fails:BasicObject#instance_eval gets constants in the receiver if a string given +fails:BasicObject#instance_eval raises an ArgumentError when more than 3 arguments are given diff --git a/spec/tags/core/module/class_eval_tags.txt b/spec/tags/core/module/class_eval_tags.txt index bde031d4fa0c..3d52505ebf70 100644 --- a/spec/tags/core/module/class_eval_tags.txt +++ b/spec/tags/core/module/class_eval_tags.txt @@ -1,2 +1,3 @@ fails:Module#class_eval activates refinements from the eval scope fails:Module#class_eval activates refinements from the eval scope with block +fails:Module#class_eval raises an ArgumentError when more than 3 arguments are given diff --git a/spec/tags/core/module/module_eval_tags.txt b/spec/tags/core/module/module_eval_tags.txt index 3a04a737a55f..50252653a24c 100644 --- a/spec/tags/core/module/module_eval_tags.txt +++ b/spec/tags/core/module/module_eval_tags.txt @@ -1,2 +1,3 @@ fails:Module#module_eval activates refinements from the eval scope fails:Module#module_eval activates refinements from the eval scope with block +fails:Module#module_eval raises an ArgumentError when more than 3 arguments are given diff --git a/spec/tags/core/proc/new_tags.txt b/spec/tags/core/proc/new_tags.txt index 245975003703..f254d769fccf 100644 --- a/spec/tags/core/proc/new_tags.txt +++ b/spec/tags/core/proc/new_tags.txt @@ -1,5 +1 @@ fails:Proc.new with an associated block raises a LocalJumpError when context of the block no longer exists -fails:Proc.new without a block can be created if invoked from within a method with a block -fails:Proc.new without a block can be created if invoked on a subclass from within a method with a block -fails:Proc.new without a block can be create when called with no block -fails:Proc.new without a block raises an ArgumentError when passed no block diff --git a/spec/truffle/always_inlined_spec.rb b/spec/truffle/always_inlined_spec.rb index b8773182440a..dd445c0c55be 100644 --- a/spec/truffle/always_inlined_spec.rb +++ b/spec/truffle/always_inlined_spec.rb @@ -87,6 +87,42 @@ }.should raise_error(ArgumentError) { |e| e.backtrace_locations[0].label.should == 'initialize_copy' } end + it "for Symbol#to_proc" do + -> { + :a.to_proc(:wrong) + }.should raise_error(ArgumentError) { |e| e.backtrace_locations[0].label.should == 'to_proc' } + end + + it "for BasicObject#instance_eval" do + -> { + BasicObject.new.instance_eval + }.should raise_error(ArgumentError) { |e| e.backtrace_locations[0].label.should == 'instance_eval' } + end + + it "for Module#class_eval" do + -> { + Module.new.class_eval + }.should raise_error(ArgumentError) { |e| e.backtrace_locations[0].label.should == 'class_eval' } + end + + it "for Module#module_eval" do + -> { + Module.new.module_eval + }.should raise_error(ArgumentError) { |e| e.backtrace_locations[0].label.should == 'module_eval' } + end + + it "for Module#define_method" do + -> { + Module.new.define_method(:wrong) + }.should raise_error(ArgumentError) { |e| e.backtrace_locations[0].label.should == 'define_method' } + end + + it "for Module#instance_method" do + -> { + Module.new.instance_method([]) + }.should raise_error(TypeError) { |e| e.backtrace_locations[0].label.should == 'instance_method' } + end + guard -> { RUBY_ENGINE != "ruby" } do it "for #send" do -> { diff --git a/spec/truffle/caller_data_spec.rb b/spec/truffle/caller_data_spec.rb index 7663e1892778..e34e53b99cb6 100644 --- a/spec/truffle/caller_data_spec.rb +++ b/spec/truffle/caller_data_spec.rb @@ -20,13 +20,6 @@ def self.last_match_set(match) Primitive.regexp_last_match_set(Primitive.caller_special_variables, match) match end - - def self.caller_binding_and_variables(last_line, last_match) - b = Primitive.caller_binding - Primitive.io_last_line_set(Primitive.caller_special_variables, last_line) - Primitive.regexp_last_match_set(Primitive.caller_special_variables, last_match) - b - end end describe "A caller" do @@ -48,23 +41,4 @@ def self.caller_binding_and_variables(last_line, last_match) $_.should == last_line $~.should == md end - - it "can have its special variables and frame read by the same method" do - last_line = "Hello!" - md = Primitive.matchdata_create_single_group(/o/, "Hello", 4, 5) - b = TruffleCallerSpecFixtures.caller_binding_and_variables(last_line, md) - $_.should == last_line - $~.should == md - b.local_variable_get(:md).should == md - end - - it "can have its special variables and frame read by the same method through an intermediate #send" do - last_line = "Hello!" - md = Primitive.matchdata_create_single_group(/o/, "Hello", 4, 5) - b = TruffleCallerSpecFixtures.send(:caller_binding_and_variables, last_line, md) - $_.should == last_line - $~.should == md - b.local_variable_get(:md).should == md - end - end diff --git a/src/main/.checkstyle_checks.xml b/src/main/.checkstyle_checks.xml index 90754e502294..55a5792091e7 100644 --- a/src/main/.checkstyle_checks.xml +++ b/src/main/.checkstyle_checks.xml @@ -208,7 +208,7 @@ - + diff --git a/src/main/java/org/truffleruby/core/basicobject/BasicObjectNodes.java b/src/main/java/org/truffleruby/core/basicobject/BasicObjectNodes.java index cd07c43a4982..975966ca4a31 100644 --- a/src/main/java/org/truffleruby/core/basicobject/BasicObjectNodes.java +++ b/src/main/java/org/truffleruby/core/basicobject/BasicObjectNodes.java @@ -13,6 +13,7 @@ import com.oracle.truffle.api.interop.InteropLibrary; import com.oracle.truffle.api.interop.UnsupportedMessageException; import com.oracle.truffle.api.object.Shape; +import com.oracle.truffle.api.profiles.BranchProfile; import com.oracle.truffle.api.strings.AbstractTruffleString; import org.truffleruby.Layouts; import org.truffleruby.builtins.CoreMethod; @@ -23,6 +24,8 @@ import org.truffleruby.core.basicobject.BasicObjectNodesFactory.ReferenceEqualNodeFactory; import org.truffleruby.core.cast.BooleanCastNode; import org.truffleruby.core.cast.NameToJavaStringNode; +import org.truffleruby.core.cast.ToIntNode; +import org.truffleruby.core.cast.ToStrNode; import org.truffleruby.core.encoding.RubyEncoding; import org.truffleruby.core.exception.ExceptionOperations.ExceptionFormatter; import org.truffleruby.core.exception.RubyException; @@ -39,12 +42,14 @@ import org.truffleruby.language.LexicalScope; import org.truffleruby.language.Nil; import org.truffleruby.language.NotProvided; +import org.truffleruby.language.RubyBaseNode; import org.truffleruby.language.RubyDynamicObject; import org.truffleruby.language.RubyGuards; import org.truffleruby.language.RubyNode; import org.truffleruby.language.RubySourceNode; import org.truffleruby.language.Visibility; -import org.truffleruby.language.arguments.ReadCallerFrameNode; +import org.truffleruby.language.arguments.ArgumentsDescriptor; +import org.truffleruby.language.arguments.EmptyArgumentsDescriptor; import org.truffleruby.language.arguments.RubyArguments; import org.truffleruby.language.control.RaiseException; import org.truffleruby.language.dispatch.DispatchNode; @@ -316,94 +321,66 @@ protected Object initialize(Frame callerFrame, Object self, Object[] rubyArgs, R } } - @CoreMethod(names = "instance_eval", needsBlock = true, optional = 3, lowerFixnum = 3) - public abstract static class InstanceEvalNode extends CoreMethodArrayArgumentsNode { - - @Specialization(guards = { "strings.isRubyString(string)", "stringsFileName.isRubyString(fileName)" }, - limit = "1") - protected Object instanceEval( - VirtualFrame frame, Object receiver, Object string, Object fileName, int line, Nil block, - @Cached RubyStringLibrary strings, - @Cached RubyStringLibrary stringsFileName, - @Cached ToJavaStringNode toJavaStringNode, - @Cached ReadCallerFrameNode callerFrameNode, - @Cached IndirectCallNode callNode) { - final MaterializedFrame callerFrame = callerFrameNode.execute(frame); + @GenerateUncached + @CoreMethod(names = "instance_eval", needsBlock = true, optional = 3, alwaysInlined = true) + public abstract static class InstanceEvalNode extends AlwaysInlinedMethodNode { + + @Specialization(guards = "isBlockProvided(rubyArgs)") + protected Object evalWithBlock(Frame callerFrame, Object self, Object[] rubyArgs, RootCallTarget target, + @Cached InstanceExecBlockNode instanceExecNode, + @Cached BranchProfile errorProfile) { + final int count = RubyArguments.getPositionalArgumentsCount(rubyArgs, false); + + if (count > 0) { + errorProfile.enter(); + throw new RaiseException(getContext(), coreExceptions().argumentError(count, 0, this)); + } - return instanceEvalHelper( - callerFrame, - receiver, - strings.getTString(string), - strings.getEncoding(string), - toJavaStringNode.executeToJavaString(fileName), - line, - callNode); + final Object block = RubyArguments.getBlock(rubyArgs); + return instanceExecNode.execute(EmptyArgumentsDescriptor.INSTANCE, self, new Object[]{ self }, + (RubyProc) block); } - @Specialization(guards = { "strings.isRubyString(string)", "stringsFileName.isRubyString(fileName)" }, - limit = "1") - protected Object instanceEval( - VirtualFrame frame, Object receiver, Object string, Object fileName, NotProvided line, Nil block, + @Specialization(guards = "!isBlockProvided(rubyArgs)") + protected Object evalWithString(Frame callerFrame, Object self, Object[] rubyArgs, RootCallTarget target, + @Cached BranchProfile errorProfile, @Cached RubyStringLibrary strings, - @Cached RubyStringLibrary stringsFileName, @Cached ToJavaStringNode toJavaStringNode, - @Cached ReadCallerFrameNode callerFrameNode, + @Cached ToStrNode toStrNode, + @Cached ToIntNode toIntNode, @Cached IndirectCallNode callNode) { - final MaterializedFrame callerFrame = callerFrameNode.execute(frame); + final Object sourceCode; + String fileName = coreStrings().EVAL_FILENAME_STRING.toString(); + int line = 1; + int count = RubyArguments.getPositionalArgumentsCount(rubyArgs, false); + + if (count == 0) { + errorProfile.enter(); + throw new RaiseException(getContext(), coreExceptions().argumentError(0, 1, 2, this)); + } - return instanceEvalHelper( - callerFrame, - receiver, - strings.getTString(string), - strings.getEncoding(string), - toJavaStringNode.executeToJavaString(fileName), - 1, - callNode); - } + sourceCode = toStrNode.execute(RubyArguments.getArgument(rubyArgs, 0)); - @Specialization(guards = "strings.isRubyString(string)", limit = "1") - protected Object instanceEval( - VirtualFrame frame, Object receiver, Object string, NotProvided fileName, NotProvided line, Nil block, - @Cached RubyStringLibrary strings, - @Cached ReadCallerFrameNode callerFrameNode, - @Cached IndirectCallNode callNode) { - final MaterializedFrame callerFrame = callerFrameNode.execute(frame); + if (count >= 2) { + fileName = toJavaStringNode + .executeToJavaString(toStrNode.execute(RubyArguments.getArgument(rubyArgs, 1))); + } + + if (count >= 3) { + line = toIntNode.execute(RubyArguments.getArgument(rubyArgs, 2)); + } + needCallerFrame(callerFrame, target); return instanceEvalHelper( - callerFrame, - receiver, - strings.getTString(string), - strings.getEncoding(string), - coreStrings().EVAL_FILENAME_STRING.toString(), - 1, + callerFrame.materialize(), + self, + strings.getTString(sourceCode), + strings.getEncoding(sourceCode), + fileName, + line, callNode); } - @Specialization - protected Object instanceEval( - VirtualFrame frame, - Object receiver, - NotProvided string, - NotProvided fileName, - NotProvided line, - RubyProc block, - @Cached InstanceExecNode instanceExecNode) { - return instanceExecNode.executeInstanceExec(frame, receiver, new Object[]{ receiver }, block); - } - - @Specialization - protected Object noArgsNoBlock( - Object receiver, NotProvided string, NotProvided fileName, NotProvided line, Nil block) { - throw new RaiseException(getContext(), coreExceptions().argumentError(0, 1, 2, this)); - } - - @Specialization(guards = "wasProvided(string)") - protected Object argsAndBlock( - Object receiver, Object string, Object maybeFileName, Object maybeLine, RubyProc block) { - final int passed = RubyGuards.wasProvided(maybeLine) ? 3 : RubyGuards.wasProvided(maybeFileName) ? 2 : 1; - throw new RaiseException(getContext(), coreExceptions().argumentError(passed, 0, this)); - } - @TruffleBoundary private Object instanceEvalHelper(MaterializedFrame callerFrame, Object receiver, AbstractTruffleString code, RubyEncoding encoding, @@ -466,6 +443,25 @@ protected Object instanceExec(Object receiver, Object[] arguments, Nil block) { } + @GenerateUncached + public abstract static class InstanceExecBlockNode extends RubyBaseNode { + + public abstract Object execute(ArgumentsDescriptor descriptor, Object self, Object[] args, RubyProc block); + + @Specialization + protected Object instanceExec(ArgumentsDescriptor descriptor, Object self, Object[] arguments, RubyProc block, + @Cached CallBlockNode callBlockNode) { + final DeclarationContext declarationContext = new DeclarationContext( + Visibility.PUBLIC, + new SingletonClassOfSelfDefaultDefinee(self), + block.declarationContext.getRefinements()); + + return callBlockNode.executeCallBlock( + declarationContext, block, self, nil, descriptor, arguments, null); + } + + } + @CoreMethod(names = "method_missing", needsBlock = true, rest = true, optional = 1, visibility = Visibility.PRIVATE, split = Split.NEVER) public abstract static class MethodMissingNode extends CoreMethodArrayArgumentsNode { diff --git a/src/main/java/org/truffleruby/core/binding/BindingNodes.java b/src/main/java/org/truffleruby/core/binding/BindingNodes.java index 7ddcad668aa3..f589c0eec9d5 100644 --- a/src/main/java/org/truffleruby/core/binding/BindingNodes.java +++ b/src/main/java/org/truffleruby/core/binding/BindingNodes.java @@ -17,9 +17,7 @@ import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.dsl.GenerateNodeFactory; import com.oracle.truffle.api.dsl.GenerateUncached; -import com.oracle.truffle.api.frame.FrameInstance.FrameAccess; import com.oracle.truffle.api.frame.FrameSlotKind; -import com.oracle.truffle.api.profiles.ConditionProfile; import com.oracle.truffle.api.strings.TruffleString; import org.truffleruby.Layouts; import org.truffleruby.RubyContext; @@ -35,13 +33,11 @@ import org.truffleruby.core.encoding.Encodings; import org.truffleruby.core.klass.RubyClass; import org.truffleruby.core.string.RubyString; -import org.truffleruby.language.CallStackManager; import org.truffleruby.language.RubyBaseNode; import org.truffleruby.language.RubyBaseNodeWithExecute; import org.truffleruby.language.RubyNode; import org.truffleruby.language.RubySourceNode; import org.truffleruby.language.Visibility; -import org.truffleruby.language.arguments.ReadCallerFrameNode; import org.truffleruby.language.arguments.RubyArguments; import org.truffleruby.language.control.RaiseException; import org.truffleruby.language.locals.FindDeclarationVariableNodes; @@ -500,28 +496,6 @@ protected Object allocate(RubyClass rubyClass) { } - @Primitive(name = "caller_binding") - public abstract static class CallerBindingNode extends PrimitiveArrayArgumentsNode { - - @Child ReadCallerFrameNode callerFrameNode = new ReadCallerFrameNode(); - - @Specialization - protected RubyBinding binding(VirtualFrame frame, - @Cached ConditionProfile javaCoreMethodProfile) { - MaterializedFrame callerFrame = callerFrameNode.execute(frame); - - if (javaCoreMethodProfile.profile(CallStackManager.isJavaCore(RubyArguments.tryGetMethod(callerFrame)))) { - // we are called from a Java core method, e.g., Method#call, we need to find the actual caller - callerFrame = getContext() - .getCallStack() - .getNonJavaCoreCallerFrame(FrameAccess.MATERIALIZE) - .materialize(); - } - - return BindingNodes.createBinding(getContext(), getLanguage(), callerFrame); - } - } - @Primitive(name = "create_empty_binding") public abstract static class CreateEmptyBindingNode extends PrimitiveArrayArgumentsNode { @Specialization diff --git a/src/main/java/org/truffleruby/core/exception/CoreExceptions.java b/src/main/java/org/truffleruby/core/exception/CoreExceptions.java index 18453fff80ad..f21cc02f2c81 100644 --- a/src/main/java/org/truffleruby/core/exception/CoreExceptions.java +++ b/src/main/java/org/truffleruby/core/exception/CoreExceptions.java @@ -42,7 +42,6 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.interop.InvalidArrayIndexException; import com.oracle.truffle.api.interop.UnknownIdentifierException; -import com.oracle.truffle.api.interop.UnsupportedMessageException; import com.oracle.truffle.api.interop.UnsupportedTypeException; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.object.DynamicObjectLibrary; @@ -165,20 +164,10 @@ public RubyException argumentErrorNegativeArraySize(RubyBaseNode currentNode) { null); } - public RubyException argumentErrorTooLargeString(Node currentNode) { - return argumentError( - "result of string concatenation exceeds the system maximum string length (2^31-1 bytes)", - currentNode); - } - public RubyException argumentErrorCharacterRequired(Node currentNode) { return argumentError("%c requires a character", currentNode); } - public RubyException argumentErrorCantOmitPrecision(Node currentNode) { - return argumentError("can't omit precision for a Float.", currentNode); - } - @TruffleBoundary public RubyException argumentErrorUnknownKeywords(Object[] keys, Node currentNode) { if (keys.length == 1) { @@ -567,10 +556,6 @@ public RubyException typeErrorCantDefineSingleton(Node currentNode) { return typeError("can't define singleton", currentNode); } - public RubyException typeErrorCantBeCastedToBigDecimal(Node currentNode) { - return typeError("could not be casted to BigDecimal", currentNode); - } - @TruffleBoundary public RubyException typeErrorCantConvertTo(Object from, String toClass, String methodUsed, Object result, Node currentNode) { @@ -682,6 +667,14 @@ public RubyException typeErrorRescueInvalidClause(Node currentNode) { return typeError("class or module required for rescue clause", currentNode); } + @TruffleBoundary + public RubyException typeErrorExpectedProcOrMethodOrUnboundMethod(Object object, Node currentNode) { + String badClassName = LogicalClassNode.getUncached().execute(object).fields.getName(); + return typeError( + StringUtils.format("wrong argument type %s (expected Proc/Method/UnboundMethod)", badClassName), + currentNode); + } + @TruffleBoundary public RubyException typeError(String message, Node currentNode, Throwable javaThrowable) { RubyClass exceptionClass = context.getCoreLibrary().typeErrorClass; @@ -698,11 +691,6 @@ public RubyException typeErrorUnsupportedTypeException(UnsupportedTypeException return typeError("unsupported type " + formattedValues, currentNode); } - @TruffleBoundary - public RubyException typeErrorUninitializedRegexp(Node currentNode) { - return typeError("uninitialized Regexp", currentNode); - } - // NameError @TruffleBoundary @@ -822,24 +810,6 @@ public RubyNameError nameErrorImportNotFound(String name, Node currentNode) { return nameError(StringUtils.format("import '%s' not found", name), null, name, currentNode); } - @TruffleBoundary - public RubyNameError nameErrorUnknownIdentifier(Object receiver, Object name, UnknownIdentifierException exception, - Node currentNode) { - return nameError(exception.getMessage(), receiver, name.toString(), currentNode); - } - - @TruffleBoundary - public RubyNameError nameErrorUnknownIdentifier(Object receiver, Object name, InvalidArrayIndexException exception, - Node currentNode) { - return nameError(exception.getMessage(), receiver, name.toString(), currentNode); - } - - @TruffleBoundary - public RubyNameError nameErrorUnsuportedMessage(Object receiver, Object name, UnsupportedMessageException exception, - Node currentNode) { - return nameError(exception.getMessage(), receiver, name.toString(), currentNode); - } - @TruffleBoundary public RubyNameError nameErrorUnknownIdentifierException( UnknownIdentifierException exception, Object receiver, Node currentNode) { @@ -1057,22 +1027,6 @@ public RubyException floatDomainError(String value, Node currentNode) { return ExceptionOperations.createRubyException(context, exceptionClass, errorMessage, currentNode, null); } - public RubyException floatDomainErrorResultsToNaN(Node currentNode) { - return floatDomainError("Computation results to 'NaN'(Not a Number)", currentNode); - } - - public RubyException floatDomainErrorResultsToInfinity(Node currentNode) { - return floatDomainError("Computation results to 'Infinity'", currentNode); - } - - public RubyException floatDomainErrorResultsToNegInfinity(Node currentNode) { - return floatDomainError("Computation results to '-Infinity'", currentNode); - } - - public RubyException floatDomainErrorSqrtNegative(Node currentNode) { - return floatDomainError("(VpSqrt) SQRT(negative value)", currentNode); - } - // IOError @TruffleBoundary @@ -1263,21 +1217,6 @@ public RubyException securityError(String message, Node currentNode) { return ExceptionOperations.createRubyException(context, exceptionClass, errorMessage, currentNode, null); } - // SystemCallError - - @TruffleBoundary - public RubySystemCallError systemCallError(String message, int errno, Backtrace backtrace) { - RubyClass exceptionClass = context.getCoreLibrary().systemCallErrorClass; - Object errorMessage; - if (message == null) { - errorMessage = Nil.INSTANCE; - } else { - errorMessage = StringOperations.createUTF8String(context, language, message); - } - return ExceptionOperations - .createSystemCallError(context, exceptionClass, errorMessage, errno, backtrace); - } - // FFI::NullPointerError @TruffleBoundary diff --git a/src/main/java/org/truffleruby/core/inlined/AlwaysInlinedMethodNode.java b/src/main/java/org/truffleruby/core/inlined/AlwaysInlinedMethodNode.java index 621af3504ccb..57dcd54a4837 100644 --- a/src/main/java/org/truffleruby/core/inlined/AlwaysInlinedMethodNode.java +++ b/src/main/java/org/truffleruby/core/inlined/AlwaysInlinedMethodNode.java @@ -16,6 +16,7 @@ import com.oracle.truffle.api.frame.Frame; import org.truffleruby.language.CallStackManager; import org.truffleruby.language.RubyBaseNode; +import org.truffleruby.language.arguments.RubyArguments; import org.truffleruby.language.control.RaiseException; /** A core method that should always be executed inline, without going through a CallTarget. That enables accessing the @@ -66,4 +67,8 @@ private RaiseException buildException(String method) { getNode())); } + public static boolean isBlockProvided(Object[] rubyArgs) { + return RubyArguments.getBlock(rubyArgs) != nil; + } + } diff --git a/src/main/java/org/truffleruby/core/module/ModuleNodes.java b/src/main/java/org/truffleruby/core/module/ModuleNodes.java index 6d424ff5a39c..ba9c624038d3 100644 --- a/src/main/java/org/truffleruby/core/module/ModuleNodes.java +++ b/src/main/java/org/truffleruby/core/module/ModuleNodes.java @@ -46,6 +46,7 @@ import org.truffleruby.core.array.RubyArray; import org.truffleruby.core.cast.BooleanCastWithDefaultNode; import org.truffleruby.core.cast.NameToJavaStringNode; +import org.truffleruby.core.cast.ToIntNode; import org.truffleruby.core.cast.ToPathNodeGen; import org.truffleruby.core.cast.ToStrNode; import org.truffleruby.core.cast.ToStringOrSymbolNodeGen; @@ -58,7 +59,6 @@ import org.truffleruby.core.method.MethodFilter; import org.truffleruby.core.method.RubyMethod; import org.truffleruby.core.method.RubyUnboundMethod; -import org.truffleruby.core.module.ModuleNodesFactory.ClassExecNodeFactory; import org.truffleruby.core.module.ModuleNodesFactory.ConstSetNodeFactory; import org.truffleruby.core.module.ModuleNodesFactory.ConstSetUncheckedNodeGen; import org.truffleruby.core.module.ModuleNodesFactory.GeneratedReaderNodeFactory; @@ -75,7 +75,6 @@ import org.truffleruby.interop.ToJavaStringNode; import org.truffleruby.language.LexicalScope; import org.truffleruby.language.Nil; -import org.truffleruby.language.NotProvided; import org.truffleruby.language.RubyBaseNode; import org.truffleruby.language.RubyBaseNodeWithExecute; import org.truffleruby.language.RubyConstant; @@ -89,7 +88,6 @@ import org.truffleruby.language.WarningNode.UncachedWarningNode; import org.truffleruby.language.arguments.ArgumentsDescriptor; import org.truffleruby.language.arguments.EmptyArgumentsDescriptor; -import org.truffleruby.language.arguments.ReadCallerFrameNode; import org.truffleruby.language.arguments.RubyArguments; import org.truffleruby.language.backtrace.BacktraceFormatter; import org.truffleruby.language.constants.ConstantEntry; @@ -104,7 +102,6 @@ import org.truffleruby.language.library.RubyStringLibrary; import org.truffleruby.language.loader.CodeLoader; import org.truffleruby.language.methods.Arity; -import org.truffleruby.language.methods.CanBindMethodToModuleNode; import org.truffleruby.language.methods.DeclarationContext; import org.truffleruby.language.methods.DeclarationContext.FixedDefaultDefinee; import org.truffleruby.language.methods.InternalMethod; @@ -662,89 +659,78 @@ protected Object isAutoload(RubyModule module, String name, boolean inherit) { } } - @CoreMethod(names = { "class_eval", "module_eval" }, optional = 3, lowerFixnum = 3, needsBlock = true) - public abstract static class ClassEvalNode extends CoreMethodArrayArgumentsNode { + @GenerateUncached + @CoreMethod(names = { "class_eval", "module_eval" }, optional = 3, needsBlock = true, alwaysInlined = true) + public abstract static class ClassEvalNode extends AlwaysInlinedMethodNode { - @Child private ReadCallerFrameNode readCallerFrameNode = ReadCallerFrameNode.create(); + @Specialization(guards = "isBlockProvided(rubyArgs)") + protected Object evalWithBlock(Frame callerFrame, RubyModule self, Object[] rubyArgs, RootCallTarget target, + @Cached BranchProfile errorProfile, + @Cached ClassExecBlockNode classExecNode) { + final int count = RubyArguments.getPositionalArgumentsCount(rubyArgs, false); - @Specialization(guards = "libCode.isRubyString(code)", limit = "1") - protected Object classEval( - VirtualFrame frame, RubyModule module, Object code, NotProvided file, NotProvided line, Nil block, - @Cached IndirectCallNode callNode, - @Cached RubyStringLibrary libCode) { - return classEvalSource(frame, module, code, "(eval)", callNode); - } + if (count > 0) { + errorProfile.enter(); + throw new RaiseException(getContext(), coreExceptions().argumentError(count, 0, this)); + } - @Specialization(guards = { "libCode.isRubyString(code)", "libFile.isRubyString(file)" }, limit = "1") - protected Object classEval( - VirtualFrame frame, RubyModule module, Object code, Object file, NotProvided line, Nil block, - @Cached IndirectCallNode callNode, - @Cached RubyStringLibrary libCode, - @Cached RubyStringLibrary libFile, - @Cached ToJavaStringNode toJavaStringNode) { - return classEvalSource(frame, module, code, toJavaStringNode.executeToJavaString(file), callNode); + final Object block = RubyArguments.getBlock(rubyArgs); + return classExecNode.execute(EmptyArgumentsDescriptor.INSTANCE, self, new Object[]{ self }, + (RubyProc) block); } - @Specialization(guards = { "libCode.isRubyString(code)", "wasProvided(file)" }, limit = "1") - protected Object classEval(VirtualFrame frame, RubyModule module, Object code, Object file, int line, Nil block, - @Cached IndirectCallNode callNode, - @Cached RubyStringLibrary libCode, - @Cached RubyStringLibrary libFile, - @Cached ToJavaStringNode toJavaStringNode) { - final CodeLoader.DeferredCall deferredCall = classEvalSource( - frame, - module, - code, - toJavaStringNode.executeToJavaString(file), - line); - return deferredCall.call(callNode); - } + @Specialization(guards = "!isBlockProvided(rubyArgs)") + protected Object evalWithString(Frame callerFrame, RubyModule self, Object[] rubyArgs, RootCallTarget target, + @Cached BranchProfile errorProfile, + @Cached ToJavaStringNode toJavaStringNode, + @Cached ToStrNode toStrNode, + @Cached ToIntNode toIntNode, + @Cached IndirectCallNode callNode) { + final Object sourceCode; + String fileName = coreStrings().EVAL_FILENAME_STRING.toString(); + int line = 1; - @Specialization(guards = "wasProvided(code)") - protected Object classEval( - VirtualFrame frame, RubyModule module, Object code, NotProvided file, NotProvided line, Nil block, - @Cached IndirectCallNode callNode, - @Cached ToStrNode toStrNode) { - return classEvalSource(frame, module, toStrNode.execute(code), "(eval)", callNode); - } + int count = RubyArguments.getPositionalArgumentsCount(rubyArgs, false); - @Specialization(guards = { "libCode.isRubyString(code)", "wasProvided(file)" }, limit = "1") - protected Object classEval( - VirtualFrame frame, RubyModule module, Object code, Object file, NotProvided line, Nil block, - @Cached RubyStringLibrary stringLibrary, - @Cached ToJavaStringNode toJavaStringNode, - @Cached IndirectCallNode callNode, - @Cached RubyStringLibrary libCode, - @Cached ToStrNode toStrNode) { - final String javaString = toJavaStringNode.executeToJavaString(toStrNode.execute(file)); - return classEvalSource(frame, module, code, javaString, callNode); - } + if (count == 0) { + errorProfile.enter(); + throw new RaiseException(getContext(), coreExceptions().argumentError(0, 1, 2, this)); + } - private Object classEvalSource(VirtualFrame frame, RubyModule module, Object code, String file, - @Cached IndirectCallNode callNode) { - final CodeLoader.DeferredCall deferredCall = classEvalSource(frame, module, code, file, 1); - return deferredCall.call(callNode); - } + sourceCode = toStrNode.execute(RubyArguments.getArgument(rubyArgs, 0)); - private CodeLoader.DeferredCall classEvalSource(VirtualFrame frame, RubyModule module, - Object rubySource, String file, int line) { + if (count >= 2) { + fileName = toJavaStringNode + .executeToJavaString(toStrNode.execute(RubyArguments.getArgument(rubyArgs, 1))); + } - final MaterializedFrame callerFrame = readCallerFrameNode.execute(frame); + if (count >= 3) { + line = toIntNode.execute(RubyArguments.getArgument(rubyArgs, 2)); + } - return classEvalSourceInternal(module, rubySource, file, line, callerFrame); + needCallerFrame(callerFrame, target); + return classEvalSource( + callerFrame.materialize(), + self, + sourceCode, + fileName, + line, + callNode); } @TruffleBoundary - private CodeLoader.DeferredCall classEvalSourceInternal(RubyModule module, Object rubySource, - String file, int line, MaterializedFrame callerFrame) { + private Object classEvalSource(MaterializedFrame callerFrame, RubyModule module, Object sourceCode, String file, + int line, + IndirectCallNode callNode) { final RubySource source = EvalLoader.createEvalSource( getContext(), - RubyStringLibrary.getUncached().getTString(rubySource), - RubyStringLibrary.getUncached().getEncoding(rubySource), + RubyStringLibrary.getUncached().getTString(sourceCode), + RubyStringLibrary.getUncached().getEncoding(sourceCode), "class/module_eval", file, line, this); + final LexicalScope lexicalScope = new LexicalScope( RubyArguments.getMethod(callerFrame).getLexicalScope(), module); @@ -756,7 +742,7 @@ private CodeLoader.DeferredCall classEvalSourceInternal(RubyModule module, Objec lexicalScope, this); - return getContext().getCodeLoader().prepareExecute( + final CodeLoader.DeferredCall deferredCall = getContext().getCodeLoader().prepareExecute( callTarget, ParserContext.MODULE, new DeclarationContext( @@ -766,23 +752,8 @@ private CodeLoader.DeferredCall classEvalSourceInternal(RubyModule module, Objec callerFrame, module, lexicalScope); - } - - @Specialization - protected Object classEval( - RubyModule self, NotProvided code, NotProvided file, NotProvided line, RubyProc block, - @Cached ClassExecNode classExecNode) { - return classExecNode.classExec(EmptyArgumentsDescriptor.INSTANCE, self, new Object[]{ self }, block); - } - @Specialization - protected Object classEval(RubyModule self, NotProvided code, NotProvided file, NotProvided line, Nil block) { - throw new RaiseException(getContext(), coreExceptions().argumentError(0, 1, 2, this)); - } - - @Specialization(guards = "wasProvided(code)") - protected Object classEval(RubyModule self, Object code, NotProvided file, NotProvided line, RubyProc block) { - throw new RaiseException(getContext(), coreExceptions().argumentError(1, 0, this)); + return deferredCall.call(callNode); } } @@ -790,23 +761,26 @@ protected Object classEval(RubyModule self, Object code, NotProvided file, NotPr @CoreMethod(names = { "class_exec", "module_exec" }, rest = true, needsBlock = true) public abstract static class ClassExecNode extends CoreMethodArrayArgumentsNode { - public static ClassExecNode create() { - return ClassExecNodeFactory.create(null); - } - - @Child private CallBlockNode callBlockNode = CallBlockNode.create(); - @Specialization - protected Object withBlock(VirtualFrame frame, RubyModule self, Object[] args, RubyProc block) { - return classExec(RubyArguments.getDescriptor(frame), self, args, block); + protected Object withBlock(VirtualFrame frame, RubyModule self, Object[] args, RubyProc block, + @Cached ClassExecBlockNode classExecBlockNode) { + return classExecBlockNode.execute(RubyArguments.getDescriptor(frame), self, args, block); } @Specialization protected Object noBlock(RubyModule self, Object[] args, Nil block) { throw new RaiseException(getContext(), coreExceptions().noBlockGiven(this)); } + } + + @GenerateUncached + public abstract static class ClassExecBlockNode extends RubyBaseNode { + + public abstract Object execute(ArgumentsDescriptor descriptor, RubyModule self, Object[] args, RubyProc block); - public Object classExec(ArgumentsDescriptor descriptor, RubyModule self, Object[] args, RubyProc block) { + @Specialization + protected Object classExec(ArgumentsDescriptor descriptor, RubyModule self, Object[] args, RubyProc block, + @Cached CallBlockNode callBlockNode) { final DeclarationContext declarationContext = new DeclarationContext( Visibility.PUBLIC, new FixedDefaultDefinee(self), @@ -1250,52 +1224,83 @@ private void warnAlreadyInitializedConstant(RubyModule module, String name, } + @GenerateUncached @CoreMethod( names = "define_method", needsBlock = true, required = 1, optional = 1, - split = Split.NEVER, - argumentNames = { "name", "proc_or_method", "block" }) - @NodeChild(value = "module", type = RubyNode.class) - @NodeChild(value = "name", type = RubyBaseNodeWithExecute.class) - @NodeChild(value = "proc", type = RubyNode.class) - @NodeChild(value = "block", type = RubyNode.class) - public abstract static class DefineMethodNode extends CoreMethodNode { + argumentNames = { "name", "proc_or_method", "block" }, + alwaysInlined = true) + @ImportStatic(RubyArguments.class) + public abstract static class DefineMethodNode extends AlwaysInlinedMethodNode { - @Child private ReadCallerFrameNode readCallerFrame = ReadCallerFrameNode.create(); + @Specialization(guards = { "isMethodParameterProvided(rubyArgs)", "isRubyMethod(getArgument(rubyArgs, 1))" }) + protected RubySymbol defineMethodWithMethod( + Frame callerFrame, RubyModule module, Object[] rubyArgs, RootCallTarget target, + @Cached NameToJavaStringNode nameToJavaStringNode) { + final String name = nameToJavaStringNode.execute(RubyArguments.getArgument(rubyArgs, 0)); + final Object method = RubyArguments.getArgument(rubyArgs, 1); - @CreateCast("name") - protected RubyBaseNodeWithExecute coerceToString(RubyBaseNodeWithExecute name) { - return NameToJavaStringNode.create(name); + return addMethod(module, name, (RubyMethod) method); } - @TruffleBoundary - @Specialization - protected RubySymbol defineMethod(RubyModule module, String name, NotProvided proc, Nil block) { - throw new RaiseException(getContext(), coreExceptions().argumentError("needs either proc or block", this)); + @Specialization(guards = { "isMethodParameterProvided(rubyArgs)", "isRubyProc(getArgument(rubyArgs, 1))" }) + protected RubySymbol defineMethodWithProc( + Frame callerFrame, RubyModule module, Object[] rubyArgs, RootCallTarget target, + @Cached NameToJavaStringNode nameToJavaStringNode) { + final String name = nameToJavaStringNode.execute(RubyArguments.getArgument(rubyArgs, 0)); + final Object method = RubyArguments.getArgument(rubyArgs, 1); + + needCallerFrame(callerFrame, target); + return addProc(module, name, (RubyProc) method, callerFrame.materialize()); } - @Specialization - protected RubySymbol defineMethodBlock( - VirtualFrame frame, RubyModule module, String name, NotProvided proc, RubyProc block) { - return defineMethodProc(frame, module, name, block, nil); + @Specialization( + guards = { "isMethodParameterProvided(rubyArgs)", "isRubyUnboundMethod(getArgument(rubyArgs, 1))" }) + protected RubySymbol defineMethodWithUnboundMethod( + Frame callerFrame, RubyModule module, Object[] rubyArgs, RootCallTarget target, + @Cached NameToJavaStringNode nameToJavaStringNode) { + final String name = nameToJavaStringNode.execute(RubyArguments.getArgument(rubyArgs, 0)); + final Object method = RubyArguments.getArgument(rubyArgs, 1); + + needCallerFrame(callerFrame, target); + return addUnboundMethod(module, name, (RubyUnboundMethod) method, callerFrame.materialize()); } - @Specialization - protected RubySymbol defineMethodProc( - VirtualFrame frame, RubyModule module, String name, RubyProc proc, Nil block) { - return defineMethod(module, name, proc, readCallerFrame.execute(frame)); + @Specialization(guards = { + "isMethodParameterProvided(rubyArgs)", + "!isExpectedMethodParameterType(getArgument(rubyArgs, 1))" }) + protected RubySymbol defineMethodWithUnexpectedMethodParameterType( + Frame callerFrame, RubyModule module, Object[] rubyArgs, RootCallTarget target) { + final Object method = RubyArguments.getArgument(rubyArgs, 1); + throw new RaiseException(getContext(), + coreExceptions().typeErrorExpectedProcOrMethodOrUnboundMethod(method, this)); + } + + @Specialization(guards = { "!isMethodParameterProvided(rubyArgs)", "isBlockProvided(rubyArgs)" }) + protected RubySymbol defineMethodWithBlock( + Frame callerFrame, RubyModule module, Object[] rubyArgs, RootCallTarget target, + @Cached NameToJavaStringNode nameToJavaStringNode) { + final String name = nameToJavaStringNode.execute(RubyArguments.getArgument(rubyArgs, 0)); + final Object block = RubyArguments.getBlock(rubyArgs); + + needCallerFrame(callerFrame, target); + return addProc(module, name, (RubyProc) block, callerFrame.materialize()); + } + + @Specialization(guards = { "!isMethodParameterProvided(rubyArgs)", "!isBlockProvided(rubyArgs)" }) + protected RubySymbol defineMethodWithoutMethodAndBlock( + Frame callerFrame, RubyModule nodule, Object[] rubyArgs, RootCallTarget target) { + throw new RaiseException(getContext(), coreExceptions().argumentErrorProcWithoutBlock(this)); } @TruffleBoundary - @Specialization - protected RubySymbol defineMethodMethod(RubyModule module, String name, RubyMethod methodObject, Nil block, - @Cached CanBindMethodToModuleNode canBindMethodToModuleNode) { - final InternalMethod method = methodObject.method; + private RubySymbol addMethod(RubyModule module, String name, RubyMethod method) { + final InternalMethod internalMethod = method.method; - if (!canBindMethodToModuleNode.executeCanBindMethodToModule(method, module)) { - final RubyModule declaringModule = method.getDeclaringModule(); + if (!ModuleOperations.canBindMethodTo(internalMethod, module)) { + final RubyModule declaringModule = internalMethod.getDeclaringModule(); if (RubyGuards.isSingletonClass(declaringModule)) { throw new RaiseException(getContext(), coreExceptions().typeError( "can't bind singleton method to a different class", @@ -1307,20 +1312,13 @@ protected RubySymbol defineMethodMethod(RubyModule module, String name, RubyMeth } } - module.fields.addMethod(getContext(), this, method.withName(name)); + module.fields.addMethod(getContext(), this, internalMethod.withName(name)); return getSymbol(name); } - @Specialization - protected RubySymbol defineMethod( - VirtualFrame frame, RubyModule module, String name, RubyUnboundMethod method, Nil block) { - final MaterializedFrame callerFrame = readCallerFrame.execute(frame); - return defineMethodInternal(module, name, method, callerFrame); - } - @TruffleBoundary - private RubySymbol defineMethodInternal(RubyModule module, String name, RubyUnboundMethod method, - final MaterializedFrame callerFrame) { + private RubySymbol addUnboundMethod(RubyModule module, String name, RubyUnboundMethod method, + MaterializedFrame callerFrame) { final InternalMethod internalMethod = method.method; if (!ModuleOperations.canBindMethodTo(internalMethod, module)) { final RubyModule declaringModule = internalMethod.getDeclaringModule(); @@ -1338,12 +1336,11 @@ private RubySymbol defineMethodInternal(RubyModule module, String name, RubyUnbo } } - return addMethod(module, name, internalMethod, callerFrame); + return addInternalMethod(module, name, internalMethod, callerFrame); } @TruffleBoundary - private RubySymbol defineMethod(RubyModule module, String name, RubyProc proc, - MaterializedFrame callerFrame) { + private RubySymbol addProc(RubyModule module, String name, RubyProc proc, MaterializedFrame callerFrame) { final RootCallTarget callTargetForLambda = proc.callTargets.getCallTargetForLambda(); final RubyLambdaRootNode rootNode = RubyLambdaRootNode.of(callTargetForLambda); final SharedMethodInfo info = proc.getSharedMethodInfo().forDefineMethod(module, name, proc); @@ -1354,7 +1351,7 @@ private RubySymbol defineMethod(RubyModule module, String name, RubyProc proc, final RubyLambdaRootNode newRootNode = rootNode.copyRootNode(info, newBody); final RootCallTarget newCallTarget = newRootNode.getCallTarget(); - final InternalMethod method = InternalMethod.fromProc( + final InternalMethod internalMethod = InternalMethod.fromProc( getContext(), info, proc.declarationContext, @@ -1363,7 +1360,7 @@ private RubySymbol defineMethod(RubyModule module, String name, RubyProc proc, Visibility.PUBLIC, proc, newCallTarget); - return addMethod(module, name, method, callerFrame); + return addInternalMethod(module, name, internalMethod, callerFrame); } private static class CallMethodWithLambdaBody extends RubyContextSourceNode { @@ -1396,7 +1393,7 @@ public Object execute(VirtualFrame frame) { } @TruffleBoundary - private RubySymbol addMethod(RubyModule module, String name, InternalMethod method, + private RubySymbol addInternalMethod(RubyModule module, String name, InternalMethod method, MaterializedFrame callerFrame) { method = method.withName(name); @@ -1406,6 +1403,16 @@ private RubySymbol addMethod(RubyModule module, String name, InternalMethod meth return getSymbol(method.getName()); } + protected boolean isMethodParameterProvided(Object[] rubyArgs) { + final int count = RubyArguments.getPositionalArgumentsCount(rubyArgs, false); + return count >= 2; + } + + protected boolean isExpectedMethodParameterType(Object method) { + return RubyGuards.isRubyMethod(method) || RubyGuards.isRubyUnboundMethod(method) || + RubyGuards.isRubyProc(method); + } + } @CoreMethod(names = "extend_object", required = 1, visibility = Visibility.PRIVATE) @@ -1444,26 +1451,17 @@ protected Object extended(RubyModule module, Object object) { @CoreMethod(names = "initialize", needsBlock = true) // Ideally should not split if no block given public abstract static class InitializeNode extends CoreMethodArrayArgumentsNode { - @Child private ClassExecNode classExecNode; - public abstract RubyModule executeInitialize(RubyModule module, Object block); - void classEval(RubyModule module, RubyProc block) { - if (classExecNode == null) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - classExecNode = insert(ClassExecNode.create()); - } - classExecNode.classExec(EmptyArgumentsDescriptor.INSTANCE, module, new Object[]{ module }, block); - } - @Specialization protected RubyModule initialize(RubyModule module, Nil block) { return module; } @Specialization - protected RubyModule initialize(RubyModule module, RubyProc block) { - classEval(module, block); + protected RubyModule initialize(RubyModule module, RubyProc block, + @Cached ClassExecBlockNode classExecBlockNode) { + classExecBlockNode.execute(EmptyArgumentsDescriptor.INSTANCE, module, new Object[]{ module }, block); return module; } @@ -1934,22 +1932,18 @@ protected RubyArray instanceMethods(RubyModule module, boolean includeAncestors) } } - @CoreMethod(names = "instance_method", required = 1) - @NodeChild(value = "module", type = RubyNode.class) - @NodeChild(value = "name", type = RubyBaseNodeWithExecute.class) - public abstract static class InstanceMethodNode extends CoreMethodNode { - @Child private ReadCallerFrameNode readCallerFrame = ReadCallerFrameNode.create(); - - @CreateCast("name") - protected RubyBaseNodeWithExecute coerceToString(RubyBaseNodeWithExecute name) { - return NameToJavaStringNode.create(name); - } + @GenerateUncached + @CoreMethod(names = "instance_method", required = 1, alwaysInlined = true) + public abstract static class InstanceMethodNode extends AlwaysInlinedMethodNode { @Specialization - protected RubyUnboundMethod instanceMethod(VirtualFrame frame, RubyModule module, String name, + protected RubyUnboundMethod instanceMethod( + Frame callerFrame, RubyModule module, Object[] rubyArgs, RootCallTarget target, + @Cached NameToJavaStringNode nameToJavaStringNode, @Cached BranchProfile errorProfile) { - final Frame callerFrame = readCallerFrame.execute(frame); + needCallerFrame(callerFrame, target); final DeclarationContext declarationContext = RubyArguments.getDeclarationContext(callerFrame); + final String name = nameToJavaStringNode.execute(RubyArguments.getArgument(rubyArgs, 0)); // TODO(CS, 11-Jan-15) cache this lookup final InternalMethod method = ModuleOperations.lookupMethodUncached(module, name, declarationContext); diff --git a/src/main/java/org/truffleruby/core/proc/ProcNodes.java b/src/main/java/org/truffleruby/core/proc/ProcNodes.java index 976fe0d08db0..d96b336008d5 100644 --- a/src/main/java/org/truffleruby/core/proc/ProcNodes.java +++ b/src/main/java/org/truffleruby/core/proc/ProcNodes.java @@ -31,26 +31,21 @@ import org.truffleruby.core.symbol.SymbolNodes; import org.truffleruby.language.Nil; import org.truffleruby.language.Visibility; -import org.truffleruby.language.WarnNode; import org.truffleruby.language.arguments.ArgumentDescriptorUtils; -import org.truffleruby.language.arguments.ReadCallerFrameNode; import org.truffleruby.language.arguments.RubyArguments; import org.truffleruby.language.control.RaiseException; import org.truffleruby.language.dispatch.DispatchNode; -import org.truffleruby.language.locals.FindDeclarationVariableNodes.FindAndReadDeclarationVariableNode; import org.truffleruby.language.methods.Arity; import org.truffleruby.language.objects.AllocationTracing; import org.truffleruby.language.objects.LogicalClassNode; import org.truffleruby.language.yield.CallBlockNode; import org.truffleruby.parser.ArgumentDescriptor; -import org.truffleruby.parser.TranslatorEnvironment; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.frame.MaterializedFrame; import com.oracle.truffle.api.frame.VirtualFrame; -import com.oracle.truffle.api.object.Shape; import com.oracle.truffle.api.profiles.ConditionProfile; import com.oracle.truffle.api.source.SourceSection; @@ -75,32 +70,8 @@ public static ProcNewNode create() { public abstract RubyProc executeProcNew(VirtualFrame frame, RubyClass procClass, Object[] args, Object block); @Specialization - protected RubyProc proc(VirtualFrame frame, RubyClass procClass, Object[] args, Nil block, - @Cached FindAndReadDeclarationVariableNode readNode, - @Cached ReadCallerFrameNode readCaller, - @Cached ProcNewNode recurseNode, - @Cached("new()") WarnNode warnNode) { - final MaterializedFrame parentFrame = readCaller.execute(frame); - - Object parentBlock = readNode.execute(parentFrame, TranslatorEnvironment.METHOD_BLOCK_NAME, nil); - - if (parentBlock == nil) { - throw new RaiseException(getContext(), coreExceptions().argumentErrorProcWithoutBlock(this)); - } else { - if (warnNode.shouldWarnForDeprecation()) { - warnNode.warningMessage( - getContext().getCallStack().getTopMostUserSourceSection(), - "Capturing the given block using Kernel#proc is deprecated; use `&block` instead"); - } - - final RubyProc proc = (RubyProc) parentBlock; - return recurseNode.executeProcNew(frame, procClass, args, proc); - } - } - - @Specialization(guards = { "procClass == getProcClass()", "block.getShape() == getProcShape()" }) - protected RubyProc procNormalOptimized(RubyClass procClass, Object[] args, RubyProc block) { - return block; + protected RubyProc proc(VirtualFrame frame, RubyClass procClass, Object[] args, Nil block) { + throw new RaiseException(getContext(), coreExceptions().argumentErrorProcWithoutBlock(this)); } @Specialization(guards = "procClass == metaClass(block)") @@ -132,14 +103,6 @@ protected RubyProc procSpecial(VirtualFrame frame, RubyClass procClass, Object[] return proc; } - protected RubyClass getProcClass() { - return coreLibrary().procClass; - } - - protected Shape getProcShape() { - return getLanguage().procShape; - } - protected RubyClass metaClass(RubyProc object) { return object.getMetaClass(); } diff --git a/src/main/java/org/truffleruby/core/symbol/SymbolNodes.java b/src/main/java/org/truffleruby/core/symbol/SymbolNodes.java index bfb4ef65d3a2..91cea034032c 100644 --- a/src/main/java/org/truffleruby/core/symbol/SymbolNodes.java +++ b/src/main/java/org/truffleruby/core/symbol/SymbolNodes.java @@ -11,6 +11,7 @@ import com.oracle.truffle.api.dsl.GenerateUncached; import com.oracle.truffle.api.dsl.ImportStatic; +import com.oracle.truffle.api.frame.Frame; import org.graalvm.collections.Pair; import org.truffleruby.RubyContext; import org.truffleruby.RubyLanguage; @@ -21,6 +22,7 @@ import org.truffleruby.collections.ConcurrentOperations; import org.truffleruby.core.CoreLibrary; import org.truffleruby.core.array.RubyArray; +import org.truffleruby.core.inlined.AlwaysInlinedMethodNode; import org.truffleruby.core.klass.RubyClass; import org.truffleruby.core.module.RubyModule; import org.truffleruby.core.proc.ProcCallTargets; @@ -35,7 +37,6 @@ import org.truffleruby.language.RubyRootNode; import org.truffleruby.language.Visibility; import org.truffleruby.language.arguments.EmptyArgumentsDescriptor; -import org.truffleruby.language.arguments.ReadCallerFrameNode; import org.truffleruby.language.arguments.RubyArguments; import org.truffleruby.language.control.BreakID; import org.truffleruby.language.control.RaiseException; @@ -55,7 +56,6 @@ import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.frame.FrameDescriptor; import com.oracle.truffle.api.frame.MaterializedFrame; -import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.source.SourceSection; import java.util.Map; @@ -126,29 +126,26 @@ protected long hash(RubySymbol symbol, } } - @CoreMethod(names = "to_proc") + @GenerateUncached + @CoreMethod(names = "to_proc", alwaysInlined = true) @ImportStatic(DeclarationContext.class) - public abstract static class ToProcNode extends CoreMethodArrayArgumentsNode { + public abstract static class ToProcNode extends AlwaysInlinedMethodNode { public static final Arity ARITY = new Arity(1, 0, true); public static ToProcNode create() { - return SymbolNodesFactory.ToProcNodeFactory.create(null); + return SymbolNodesFactory.ToProcNodeFactory.create(); } - public abstract RubyProc execute(VirtualFrame frame, RubySymbol symbol); - - @Child private ReadCallerFrameNode readCallerFrame = ReadCallerFrameNode.create(); - @Specialization( guards = { "isSingleContext()", "symbol == cachedSymbol", - "getRefinements(frame) == cachedRefinements" }, + "getRefinements(callerFrame) == cachedRefinements" }, limit = "getIdentityCacheLimit()") - protected RubyProc toProcCached(VirtualFrame frame, RubySymbol symbol, + protected RubyProc toProcCached(Frame callerFrame, RubySymbol symbol, Object[] rubyArgs, RootCallTarget target, @Cached("symbol") RubySymbol cachedSymbol, - @Cached("getRefinements(frame)") Map cachedRefinements, + @Cached("getRefinements(callerFrame)") Map cachedRefinements, @Cached("getOrCreateCallTarget(getContext(), getLanguage(), cachedSymbol, cachedRefinements)") RootCallTarget callTarget, @Cached("createProc(getContext(), getLanguage(), cachedRefinements, callTarget)") RubyProc cachedProc) { return cachedProc; @@ -157,17 +154,19 @@ protected RubyProc toProcCached(VirtualFrame frame, RubySymbol symbol, @Specialization( guards = { "symbol == cachedSymbol", - "getRefinements(frame) == NO_REFINEMENTS" }, + "getRefinements(callerFrame) == NO_REFINEMENTS" }, limit = "getIdentityCacheLimit()") - protected RubyProc toProcCachedNoRefinements(VirtualFrame frame, RubySymbol symbol, + protected RubyProc toProcCachedNoRefinements( + Frame callerFrame, RubySymbol symbol, Object[] rubyArgs, RootCallTarget target, @Cached("symbol") RubySymbol cachedSymbol, @Cached("getOrCreateCallTarget(getContext(), getLanguage(), cachedSymbol, NO_REFINEMENTS)") RootCallTarget callTarget) { return createProc(getContext(), getLanguage(), DeclarationContext.NO_REFINEMENTS, callTarget); } @Specialization(replaces = { "toProcCached", "toProcCachedNoRefinements" }) - protected RubyProc toProcUncached(VirtualFrame frame, RubySymbol symbol) { - final Map refinements = getRefinements(frame); + protected RubyProc toProcUncached( + Frame callerFrame, RubySymbol symbol, Object[] rubyArgs, RootCallTarget target) { + final Map refinements = getRefinements(callerFrame); final RootCallTarget callTarget = getOrCreateCallTarget(getContext(), getLanguage(), symbol, refinements); return createProc(getContext(), getLanguage(), refinements, callTarget); } @@ -246,12 +245,7 @@ public static RootCallTarget createCallTarget(RubyLanguage language, RubySymbol return rootNode.getCallTarget(); } - protected InternalMethod getMethod(VirtualFrame frame) { - return RubyArguments.getMethod(frame); - } - - protected Map getRefinements(VirtualFrame frame) { - final MaterializedFrame callerFrame = readCallerFrame.execute(frame); + protected Map getRefinements(Frame callerFrame) { final DeclarationContext declarationContext = RubyArguments.tryGetDeclarationContext(callerFrame); return declarationContext != null ? declarationContext.getRefinements() diff --git a/src/main/java/org/truffleruby/language/FrameAndVariablesSendingNode.java b/src/main/java/org/truffleruby/language/FrameAndVariablesSendingNode.java index 7685d068bae0..f84d295c1ca6 100644 --- a/src/main/java/org/truffleruby/language/FrameAndVariablesSendingNode.java +++ b/src/main/java/org/truffleruby/language/FrameAndVariablesSendingNode.java @@ -12,17 +12,17 @@ import com.oracle.truffle.api.frame.Frame; import org.truffleruby.SuppressFBWarnings; import org.truffleruby.core.binding.RubyBinding; +import org.truffleruby.core.inlined.AlwaysInlinedMethodNode; import org.truffleruby.core.kernel.TruffleKernelNodes.GetSpecialVariableStorage; -import org.truffleruby.language.arguments.ReadCallerFrameNode; import org.truffleruby.language.arguments.ReadCallerVariablesNode; import org.truffleruby.language.methods.DeclarationContext; import org.truffleruby.language.threadlocal.SpecialVariableStorage; /** Some Ruby methods need access to the caller frame (the frame active when the method call was made) or to the storage - * of special variables within that frame: see usages of {@link ReadCallerFrameNode} and {@link ReadCallerVariablesNode} - * . This is notably used to get hold of instances of {@link DeclarationContext} and {@link RubyBinding} and methods - * which need to access the last regexp MatchData or the last IO line. + * of special variables within that frame: see usages of {@link AlwaysInlinedMethodNode} and + * {@link ReadCallerVariablesNode}. This is notably used to get hold of instances of {@link DeclarationContext} and + * {@link RubyBinding} and methods which need to access the last regexp MatchData or the last IO line. * *

* This means that when making a method call, we might need to pass down its {@link Frame} or diff --git a/src/main/java/org/truffleruby/language/arguments/ReadCallerFrameNode.java b/src/main/java/org/truffleruby/language/arguments/ReadCallerFrameNode.java deleted file mode 100644 index 48bc2f4bb9b4..000000000000 --- a/src/main/java/org/truffleruby/language/arguments/ReadCallerFrameNode.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2015, 2021 Oracle and/or its affiliates. All rights reserved. This - * code is released under a tri EPL/GPL/LGPL license. You can use it, - * redistribute it and/or modify it under the terms of the: - * - * Eclipse Public License version 2.0, or - * GNU General Public License version 2, or - * GNU Lesser General Public License version 2.1. - */ -package org.truffleruby.language.arguments; - -import com.oracle.truffle.api.frame.Frame; -import org.truffleruby.language.FrameAndVariablesSendingNode; - -import com.oracle.truffle.api.frame.MaterializedFrame; - -public class ReadCallerFrameNode extends ReadCallerDataNode { - - public static ReadCallerFrameNode create() { - return new ReadCallerFrameNode(); - } - - @Override - public MaterializedFrame execute(Frame frame) { - return (MaterializedFrame) super.execute(frame); - } - - @Override - protected MaterializedFrame getData(Frame frame) { - return RubyArguments.getCallerFrame(frame); - } - - @Override - public void startSending(FrameAndVariablesSendingNode node) { - node.startSendingOwnFrame(); - } - - @Override - protected Object getDataFromFrame(MaterializedFrame frame) { - return frame; - } - -} From 7273f3d9ae083db1eaae281e5f7cd79b5cea719a Mon Sep 17 00:00:00 2001 From: Duncan MacGregor Date: Wed, 14 Sep 2022 14:26:09 +0000 Subject: [PATCH 03/11] [GR-17457] Patch recent versions of nokogiri with system libraries. PullRequest: truffleruby/3488 (cherry picked from commit e24dc0c632bbed72a30a2b7930afa490b4e5675f) --- lib/truffle/truffle/patches/nokogiri_patches.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/truffle/truffle/patches/nokogiri_patches.rb b/lib/truffle/truffle/patches/nokogiri_patches.rb index 4f2cfd5efd14..743e6d6cb0b0 100644 --- a/lib/truffle/truffle/patches/nokogiri_patches.rb +++ b/lib/truffle/truffle/patches/nokogiri_patches.rb @@ -79,6 +79,11 @@ def self.using_system_libraries? replacement: "VALUE thing = Qnil;\nVALUE errors = rb_ary_new();", predicate: -> { using_system_libraries? } }, + { + match: 'VALUE retVal = Qnil;', + replacement: "VALUE retVal = Qnil;\nVALUE errors = rb_ary_new();", + predicate: -> { using_system_libraries? } + }, { match: 'xmlSetStructuredErrorFunc(NULL, Nokogiri_error_raise);', replacement: 'xmlSetStructuredErrorFunc(errors, Nokogiri_error_array_pusher);', From 7812286f09258ab225279f8ee979c3469614f8ca Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Mon, 26 Sep 2022 17:48:19 +0000 Subject: [PATCH 04/11] [GR-41105] Fix IO#{wait,wait_readable,wait_writable} with a timeout > INT_MAX seconds PullRequest: truffleruby/3495 (cherry picked from commit 26078927db016cbc3438f296a5ed32d71616ab7c) --- CHANGELOG.md | 1 + lib/truffle/io/wait.rb | 5 ++-- .../library/io-wait/wait_readable_spec.rb | 25 +++++++++++++++++++ .../library/io-wait/wait_writable_spec.rb | 18 +++++++++++++ .../truffleruby/core/truffle/io_operations.rb | 13 +++++++--- 5 files changed, 56 insertions(+), 6 deletions(-) create mode 100644 spec/ruby/library/io-wait/wait_readable_spec.rb create mode 100644 spec/ruby/library/io-wait/wait_writable_spec.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index d64503ab4fd2..2a558b57c425 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ Bug fixes: * Disallow the marshaling of polyglot exceptions since we can't properly reconstruct them (@nirvdrum). * Fix `String#split` missing a value in its return array when called with a pattern of `" "` and a _limit_ value > 0 on a string with trailing whitespace where the limit hasn't been met (@nirvdrum). * Fix `Kernel#sleep` and `Mutex#sleep` for durations smaller than 1 millisecond (#2716, @eregon). +* Fix `IO#{wait,wait_readable,wait_writable}` with a timeout > INT_MAX seconds (@eregon). Compatibility: diff --git a/lib/truffle/io/wait.rb b/lib/truffle/io/wait.rb index 153e44b31abb..2c665f4f37f0 100644 --- a/lib/truffle/io/wait.rb +++ b/lib/truffle/io/wait.rb @@ -17,12 +17,11 @@ def ready? Truffle::IOOperations.poll(self, Truffle::IOOperations::POLLIN, 0) end - def wait(timeout = nil) + def wait_readable(timeout = nil) ensure_open_and_readable Truffle::IOOperations.poll(self, Truffle::IOOperations::POLLIN, timeout) ? self : nil end - - alias_method :wait_readable, :wait + alias_method :wait, :wait_readable def wait_writable(timeout = nil) ensure_open_and_writable diff --git a/spec/ruby/library/io-wait/wait_readable_spec.rb b/spec/ruby/library/io-wait/wait_readable_spec.rb new file mode 100644 index 000000000000..48e0b4ef7d2f --- /dev/null +++ b/spec/ruby/library/io-wait/wait_readable_spec.rb @@ -0,0 +1,25 @@ +require_relative '../../spec_helper' + +require 'io/wait' + +describe "IO#wait_readable" do + before :each do + @io = File.new(__FILE__ ) + end + + after :each do + @io.close + end + + it "waits for the IO to become readable with no timeout" do + @io.wait_readable.should == @io + end + + it "waits for the IO to become readable with the given timeout" do + @io.wait_readable(1).should == @io + end + + it "waits for the IO to become readable with the given large timeout" do + @io.wait_readable(365 * 24 * 60 * 60).should == @io + end +end diff --git a/spec/ruby/library/io-wait/wait_writable_spec.rb b/spec/ruby/library/io-wait/wait_writable_spec.rb new file mode 100644 index 000000000000..5900a0c5663d --- /dev/null +++ b/spec/ruby/library/io-wait/wait_writable_spec.rb @@ -0,0 +1,18 @@ +require_relative '../../spec_helper' + +require 'io/wait' + +describe "IO#wait_writable" do + it "waits for the IO to become writable with no timeout" do + STDOUT.wait_writable.should == STDOUT + end + + it "waits for the IO to become writable with the given timeout" do + STDOUT.wait_writable(1).should == STDOUT + end + + it "waits for the IO to become writable with the given large timeout" do + # Represents one year and is larger than a 32-bit int + STDOUT.wait_writable(365 * 24 * 60 * 60).should == STDOUT + end +end diff --git a/src/main/ruby/truffleruby/core/truffle/io_operations.rb b/src/main/ruby/truffleruby/core/truffle/io_operations.rb index ca9de1c42454..38c471e0b5d4 100644 --- a/src/main/ruby/truffleruby/core/truffle/io_operations.rb +++ b/src/main/ruby/truffleruby/core/truffle/io_operations.rb @@ -212,9 +212,16 @@ def self.poll(io, event_mask, timeout) raise ArgumentError, 'timeout must be positive' if timeout < 0 # Milliseconds, rounded down - timeout = remaining_timeout = (timeout * 1_000).to_i + timeout_ms = Primitive.rb_to_int((timeout * 1_000).to_i) + while timeout_ms > 2147483647 # INT_MAX + timeout_ms -= 2147483000 + ret = poll(io, event_mask, 2147483) + return ret unless ret == false + end + + remaining_timeout = timeout_ms start = Process.clock_gettime(Process::CLOCK_MONOTONIC, :millisecond) - deadline = start + timeout + deadline = start + timeout_ms else remaining_timeout = -1 end @@ -225,7 +232,7 @@ def self.poll(io, event_mask, timeout) if primitive_result < 0 errno = Errno.errno if errno == Errno::EINTR::Errno - if timeout + if timeout_ms # Update timeout now = Process.clock_gettime(Process::CLOCK_MONOTONIC, :millisecond) if now >= deadline From 1a12d2a30a149dda127a692b8542fa739baa64cd Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Tue, 27 Sep 2022 02:40:49 +0000 Subject: [PATCH 05/11] [GR-40769] Check for native access when creating a Pointer PullRequest: truffleruby/3474 (cherry picked from commit 788cb4c1ad98bcd275277aa59f53b029423af828) --- CHANGELOG.md | 4 + .../java/org/truffleruby/cext/CExtNodes.java | 34 +++++---- .../java/org/truffleruby/cext/DataHolder.java | 8 +- .../truffleruby/core/VMPrimitiveNodes.java | 6 +- .../truffleruby/core/array/ArrayNodes.java | 2 +- .../array/library/NativeArrayStorage.java | 6 +- .../core/encoding/EncodingManager.java | 2 +- .../read/bytes/ReadStringPointerNode.java | 2 +- .../core/string/ImmutableRubyString.java | 5 +- .../truffleruby/core/string/StringNodes.java | 2 +- .../org/truffleruby/core/support/IONodes.java | 10 ++- .../truffleruby/core/thread/RubyThread.java | 8 +- .../core/thread/ThreadLocalBuffer.java | 20 ++--- .../core/thread/ThreadManager.java | 6 +- .../org/truffleruby/extra/ffi/Pointer.java | 64 +++++++++++----- .../truffleruby/extra/ffi/PointerNodes.java | 76 +++++++++---------- .../language/loader/FeatureLoader.java | 5 +- .../truffleruby/core/truffle/ffi/pointer.rb | 7 +- .../truffleruby/ContextPermissionsTest.java | 28 ++++++- 19 files changed, 186 insertions(+), 109 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a558b57c425..841bf869f1b1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -58,6 +58,10 @@ Changes: * Refactored internals of `rb_sprintf` to simplify handling of `VALUE`s in common cases (@aardvark179). * Refactored sharing of array objects between threads using new `SharedArrayStorage` (@aardvark179). +Security: + +* The native access permission is now properly checked before any native pointer (e.g. `Truffle::FFI::Pointer`) is created (@eregon). + # 22.2.0 New features: diff --git a/src/main/java/org/truffleruby/cext/CExtNodes.java b/src/main/java/org/truffleruby/cext/CExtNodes.java index 6552907f32b5..2cdbedb69fd8 100644 --- a/src/main/java/org/truffleruby/cext/CExtNodes.java +++ b/src/main/java/org/truffleruby/cext/CExtNodes.java @@ -22,6 +22,7 @@ import org.jcodings.Encoding; import org.jcodings.IntHolder; import org.truffleruby.Layouts; +import org.truffleruby.RubyContext; import org.truffleruby.RubyLanguage; import org.truffleruby.builtins.CoreMethod; import org.truffleruby.builtins.CoreMethodArrayArgumentsNode; @@ -149,9 +150,9 @@ public class CExtNodes { public static final int RUBY_TAG_THROW = 0x7; public static final int RUBY_TAG_FATAL = 0x8; - public static Pointer newNativeStringPointer(int capacity, RubyLanguage language) { + public static Pointer newNativeStringPointer(RubyLanguage language, RubyContext context, int capacity) { // We need up to 4 \0 bytes for UTF-32. Always use 4 for speed rather than checking the encoding min length. - Pointer pointer = Pointer.mallocAutoRelease(capacity + 4, language); + Pointer pointer = Pointer.mallocAutoRelease(language, context, capacity + 4); pointer.writeInt(capacity, 0); return pointer; } @@ -714,7 +715,7 @@ public abstract static class RbStrNewNulNode extends CoreMethodArrayArgumentsNod @Specialization protected RubyString rbStrNewNul(int byteLength, @Cached MutableTruffleString.FromNativePointerNode fromNativePointerNode) { - final Pointer pointer = Pointer.callocAutoRelease(byteLength + 1, getLanguage()); + final Pointer pointer = Pointer.callocAutoRelease(getLanguage(), getContext(), byteLength + 1); var nativeTString = fromNativePointerNode.execute(pointer, 0, byteLength, Encodings.BINARY.tencoding, false); return createMutableString(nativeTString, Encodings.BINARY); @@ -791,8 +792,8 @@ protected RubyString rbStrResize(RubyString string, int newByteLength, string.clearCodeRange(); return string; } else { - var newNativeTString = TrStrCapaResizeNode.resize(pointer, newByteLength, newByteLength, tencoding, - fromNativePointerNode, getLanguage()); + var newNativeTString = TrStrCapaResizeNode.resize(getLanguage(), getContext(), pointer, newByteLength, + newByteLength, tencoding, fromNativePointerNode); string.setTString(newNativeTString); // Like MRI's rb_str_resize() @@ -818,18 +819,18 @@ protected RubyString trStrCapaResize(RubyString string, int newCapacity, return string; } else { int byteLength = string.tstring.byteLength(tencoding); - var newNativeTString = resize(pointer, newCapacity, byteLength, tencoding, fromNativePointerNode, - getLanguage()); + var newNativeTString = resize(getLanguage(), getContext(), pointer, newCapacity, byteLength, tencoding, + fromNativePointerNode); string.setTString(newNativeTString); return string; } } - static MutableTruffleString resize(Pointer pointer, int newCapacity, int newByteLength, - TruffleString.Encoding tencoding, MutableTruffleString.FromNativePointerNode fromNativePointerNode, - RubyLanguage language) { - final Pointer newPointer = newNativeStringPointer(newCapacity, language); + static MutableTruffleString resize(RubyLanguage language, RubyContext context, Pointer pointer, int newCapacity, + int newByteLength, TruffleString.Encoding tencoding, + MutableTruffleString.FromNativePointerNode fromNativePointerNode) { + final Pointer newPointer = newNativeStringPointer(language, context, newCapacity); newPointer.writeBytes(0, pointer, 0, Math.min(pointer.getSize(), newCapacity)); return fromNativePointerNode.execute(newPointer, 0, newByteLength, tencoding, false); @@ -1234,8 +1235,8 @@ protected Pointer toNative(RubyString string, pointer = (Pointer) getInternalNativePointerNode.execute(tstring, tencoding); } else { int byteLength = tstring.byteLength(tencoding); - pointer = allocateAndCopyToNative(tstring, tencoding, byteLength, copyToNativeMemoryNode, - getLanguage()); + pointer = allocateAndCopyToNative(getLanguage(), getContext(), tstring, tencoding, byteLength, + copyToNativeMemoryNode); var nativeTString = fromNativePointerNode.execute(pointer, 0, byteLength, tencoding, false); string.setTString(nativeTString); @@ -1249,9 +1250,10 @@ protected Pointer toNativeImmutable(ImmutableRubyString string) { return string.getNativeString(getLanguage()); } - public static Pointer allocateAndCopyToNative(AbstractTruffleString tstring, TruffleString.Encoding tencoding, - int capacity, TruffleString.CopyToNativeMemoryNode copyToNativeMemoryNode, RubyLanguage language) { - final Pointer pointer = newNativeStringPointer(capacity, language); + public static Pointer allocateAndCopyToNative(RubyLanguage language, RubyContext context, + AbstractTruffleString tstring, TruffleString.Encoding tencoding, int capacity, + TruffleString.CopyToNativeMemoryNode copyToNativeMemoryNode) { + final Pointer pointer = newNativeStringPointer(language, context, capacity); copyToNativeMemoryNode.execute(tstring, 0, pointer, 0, capacity, tencoding); return pointer; } diff --git a/src/main/java/org/truffleruby/cext/DataHolder.java b/src/main/java/org/truffleruby/cext/DataHolder.java index 4b46ec035b6b..56f1185b6ce4 100644 --- a/src/main/java/org/truffleruby/cext/DataHolder.java +++ b/src/main/java/org/truffleruby/cext/DataHolder.java @@ -25,16 +25,16 @@ public final class DataHolder implements TruffleObject { private Object pointer; - public DataHolder(Object address) { - this.pointer = address; + public DataHolder(Object pointer) { + this.pointer = pointer; } public Object getPointer() { return pointer; } - public void setPointer(Object address) { - this.pointer = address; + public void setPointer(Object pointer) { + this.pointer = pointer; } @ExportMessage diff --git a/src/main/java/org/truffleruby/core/VMPrimitiveNodes.java b/src/main/java/org/truffleruby/core/VMPrimitiveNodes.java index 3cfb2e74c3b2..02baa8ede22f 100644 --- a/src/main/java/org/truffleruby/core/VMPrimitiveNodes.java +++ b/src/main/java/org/truffleruby/core/VMPrimitiveNodes.java @@ -643,9 +643,9 @@ protected long argvLength() { } int argc = getContext().nativeArgc; - Pointer argv = new Pointer(getContext().nativeArgv, argc * Pointer.SIZE); - Pointer first = argv.readPointer(0); - Pointer last = argv.readPointer((argc - 1) * Pointer.SIZE); + Pointer argv = new Pointer(getContext(), getContext().nativeArgv, argc * Pointer.SIZE); + Pointer first = argv.readPointer(getContext(), 0); + Pointer last = argv.readPointer(getContext(), (argc - 1) * Pointer.SIZE); long lastByte = last.getAddress() + last.findNullByte(getContext(), InteropLibrary.getUncached(), 0); nativeArgvLength = lastByte - first.getAddress(); diff --git a/src/main/java/org/truffleruby/core/array/ArrayNodes.java b/src/main/java/org/truffleruby/core/array/ArrayNodes.java index 5598c10db169..f05aec4a3946 100644 --- a/src/main/java/org/truffleruby/core/array/ArrayNodes.java +++ b/src/main/java/org/truffleruby/core/array/ArrayNodes.java @@ -2325,7 +2325,7 @@ protected RubyArray storeToNative(RubyArray array, @Cached IsSharedNode isSharedNode, @Cached ConditionProfile sharedProfile) { final int size = arraySizeProfile.profile(array.size); - Pointer pointer = Pointer.mallocAutoRelease(size * Pointer.SIZE, getLanguage()); + Pointer pointer = Pointer.mallocAutoRelease(getLanguage(), getContext(), size * Pointer.SIZE); Object newStore = new NativeArrayStorage(pointer, size); stores.copyContents(store, 0, newStore, 0, size); getContext().getMarkingService().addMarker( diff --git a/src/main/java/org/truffleruby/core/array/library/NativeArrayStorage.java b/src/main/java/org/truffleruby/core/array/library/NativeArrayStorage.java index a5eeaa4bd88c..477bc8370a1d 100644 --- a/src/main/java/org/truffleruby/core/array/library/NativeArrayStorage.java +++ b/src/main/java/org/truffleruby/core/array/library/NativeArrayStorage.java @@ -17,6 +17,7 @@ import com.oracle.truffle.api.TruffleSafepoint; import com.oracle.truffle.api.profiles.ConditionProfile; import com.oracle.truffle.api.profiles.LoopConditionProfile; +import org.truffleruby.RubyContext; import org.truffleruby.cext.UnwrapNode; import org.truffleruby.cext.UnwrapNodeGen.UnwrapNativeNodeGen; import org.truffleruby.cext.ValueWrapper; @@ -121,9 +122,10 @@ protected int capacity() { } @ExportMessage - protected NativeArrayStorage expand(int newCapacity) { + protected NativeArrayStorage expand(int newCapacity, + @CachedLibrary("this") ArrayStoreLibrary node) { final int capacity = this.length; - Pointer newPointer = Pointer.malloc(capacity); + Pointer newPointer = Pointer.malloc(RubyContext.get(node), capacity); newPointer.writeBytes(0, pointer, 0, capacity); newPointer.writeBytes(capacity, newCapacity - capacity, (byte) 0); /* We copy the contents of the marked objects to ensure the references will be kept alive even if the old store diff --git a/src/main/java/org/truffleruby/core/encoding/EncodingManager.java b/src/main/java/org/truffleruby/core/encoding/EncodingManager.java index 8705df3a04a8..5871a886aafa 100644 --- a/src/main/java/org/truffleruby/core/encoding/EncodingManager.java +++ b/src/main/java/org/truffleruby/core/encoding/EncodingManager.java @@ -153,7 +153,7 @@ private void initializeLocaleEncoding(TruffleNFIPlatform nfi, NativeConfiguratio } catch (InteropException e) { throw CompilerDirectives.shouldNotReachHere(e); } - final byte[] bytes = new Pointer(address).readZeroTerminatedByteArray( + final byte[] bytes = new Pointer(context, address).readZeroTerminatedByteArray( context, InteropLibrary.getUncached(), 0); diff --git a/src/main/java/org/truffleruby/core/format/read/bytes/ReadStringPointerNode.java b/src/main/java/org/truffleruby/core/format/read/bytes/ReadStringPointerNode.java index 1ac4e6f622ea..6616629c0259 100644 --- a/src/main/java/org/truffleruby/core/format/read/bytes/ReadStringPointerNode.java +++ b/src/main/java/org/truffleruby/core/format/read/bytes/ReadStringPointerNode.java @@ -48,7 +48,7 @@ protected MissingValue decode(Nil nil) { protected RubyString read(VirtualFrame frame, long address, @CachedLibrary(limit = "getRubyLibraryCacheLimit()") RubyLibrary rubyLibrary, @CachedLibrary(limit = "1") InteropLibrary interop) { - final Pointer pointer = new Pointer(address); + final Pointer pointer = new Pointer(getContext(), address); checkAssociated( (Pointer[]) frame.getObject(FormatFrameDescriptor.SOURCE_ASSOCIATED_SLOT), pointer); diff --git a/src/main/java/org/truffleruby/core/string/ImmutableRubyString.java b/src/main/java/org/truffleruby/core/string/ImmutableRubyString.java index 8d1378d18fde..0bd891fed255 100644 --- a/src/main/java/org/truffleruby/core/string/ImmutableRubyString.java +++ b/src/main/java/org/truffleruby/core/string/ImmutableRubyString.java @@ -84,8 +84,9 @@ private synchronized Pointer createNativeString(RubyLanguage language) { if (nativeString == null) { var tencoding = getEncodingUncached().tencoding; int byteLength = tstring.byteLength(tencoding); - nativeString = CExtNodes.StringToNativeNode.allocateAndCopyToNative(tstring, tencoding, byteLength, - TruffleString.CopyToNativeMemoryNode.getUncached(), language); + nativeString = CExtNodes.StringToNativeNode.allocateAndCopyToNative(language, + RubyLanguage.getCurrentContext(), tstring, tencoding, byteLength, + TruffleString.CopyToNativeMemoryNode.getUncached()); } return nativeString; } diff --git a/src/main/java/org/truffleruby/core/string/StringNodes.java b/src/main/java/org/truffleruby/core/string/StringNodes.java index 8a051badcbce..9bf33151c26e 100644 --- a/src/main/java/org/truffleruby/core/string/StringNodes.java +++ b/src/main/java/org/truffleruby/core/string/StringNodes.java @@ -1589,7 +1589,7 @@ protected Object initializeCopyNative(RubyString self, RubyString from, var tencoding = encoding.tencoding; final Pointer fromPointer = (Pointer) getInternalNativePointerNode.execute(tstring, tencoding); - final Pointer newPointer = Pointer.mallocAutoRelease(fromPointer.getSize(), getLanguage()); + final Pointer newPointer = Pointer.mallocAutoRelease(getLanguage(), getContext(), fromPointer.getSize()); newPointer.writeBytes(0, fromPointer, 0, fromPointer.getSize()); // TODO (eregon, 2022): should we have the copy be native too, or rather take the opportunity of having to copy to be managed? diff --git a/src/main/java/org/truffleruby/core/support/IONodes.java b/src/main/java/org/truffleruby/core/support/IONodes.java index d35b41f52a9c..17fd4d57f3b6 100644 --- a/src/main/java/org/truffleruby/core/support/IONodes.java +++ b/src/main/java/org/truffleruby/core/support/IONodes.java @@ -67,6 +67,7 @@ import java.util.Arrays; import com.oracle.truffle.api.strings.TruffleString; +import org.truffleruby.RubyContext; import org.truffleruby.builtins.CoreMethod; import org.truffleruby.builtins.CoreMethodArrayArgumentsNode; import org.truffleruby.builtins.CoreModule; @@ -515,13 +516,14 @@ protected RubyPointer getThreadBuffer(long size, final RubyPointer instance = new RubyPointer( coreLibrary().truffleFFIPointerClass, getLanguage().truffleFFIPointerShape, - getBuffer(thread, size, sizeProfile)); + getBuffer(getContext(), thread, size, sizeProfile)); AllocationTracing.trace(instance, this); return instance; } - public static Pointer getBuffer(RubyThread rubyThread, long size, ConditionProfile sizeProfile) { - return rubyThread.ioBuffer.allocate(rubyThread, size, sizeProfile); + public static Pointer getBuffer(RubyContext context, RubyThread rubyThread, long size, + ConditionProfile sizeProfile) { + return rubyThread.getIoBuffer(context).allocate(context, rubyThread, size, sizeProfile); } } @@ -532,7 +534,7 @@ public abstract static class IOThreadBufferFreeNode extends PrimitiveArrayArgume protected Object getThreadBuffer(RubyPointer pointer, @Cached ConditionProfile freeProfile) { RubyThread thread = getLanguage().getCurrentThread(); - thread.ioBuffer.free(thread, pointer.pointer, freeProfile); + thread.getIoBuffer(getContext()).free(thread, pointer.pointer, freeProfile); return nil; } } diff --git a/src/main/java/org/truffleruby/core/thread/RubyThread.java b/src/main/java/org/truffleruby/core/thread/RubyThread.java index b923d9be9811..f08b56b0fd88 100644 --- a/src/main/java/org/truffleruby/core/thread/RubyThread.java +++ b/src/main/java/org/truffleruby/core/thread/RubyThread.java @@ -31,6 +31,7 @@ import org.truffleruby.core.support.PRNGRandomizerNodes; import org.truffleruby.core.support.RubyPRNGRandomizer; import org.truffleruby.core.tracepoint.TracePointState; +import org.truffleruby.extra.ffi.Pointer; import org.truffleruby.language.Nil; import org.truffleruby.language.RubyDynamicObject; import org.truffleruby.language.objects.ObjectGraph; @@ -62,7 +63,7 @@ public final class RubyThread extends RubyDynamicObject implements ObjectGraphNo volatile Object value = null; public final AtomicBoolean wakeUp = new AtomicBoolean(false); volatile int priority = Thread.NORM_PRIORITY; - public ThreadLocalBuffer ioBuffer = ThreadLocalBuffer.NULL_BUFFER; + ThreadLocalBuffer ioBuffer; Object threadGroup; public String sourceLocation; Object name = Nil.INSTANCE; @@ -132,6 +133,11 @@ public void setCurrentFiber(RubyFiber fiber) { currentFiber = fiber; } + public ThreadLocalBuffer getIoBuffer(RubyContext context) { + Pointer.checkNativeAccess(context); + return ioBuffer; + } + @Override public void getAdjacentObjects(Set reachable) { ObjectGraph.addProperty(reachable, threadLocalVariables); diff --git a/src/main/java/org/truffleruby/core/thread/ThreadLocalBuffer.java b/src/main/java/org/truffleruby/core/thread/ThreadLocalBuffer.java index f32e442baecb..5f383c39fd33 100644 --- a/src/main/java/org/truffleruby/core/thread/ThreadLocalBuffer.java +++ b/src/main/java/org/truffleruby/core/thread/ThreadLocalBuffer.java @@ -12,11 +12,11 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.profiles.ConditionProfile; +import org.truffleruby.RubyContext; import org.truffleruby.extra.ffi.Pointer; public final class ThreadLocalBuffer { - public static final ThreadLocalBuffer NULL_BUFFER = new ThreadLocalBuffer(new Pointer(0, 0), null); private static final long ALIGNMENT = 8L; private static final long ALIGNMENT_MASK = ALIGNMENT - 1; @@ -24,7 +24,7 @@ public final class ThreadLocalBuffer { long remaining; private final ThreadLocalBuffer parent; - private ThreadLocalBuffer(Pointer start, ThreadLocalBuffer parent) { + public ThreadLocalBuffer(Pointer start, ThreadLocalBuffer parent) { this.start = start; this.remaining = start.getSize(); this.parent = parent; @@ -61,14 +61,14 @@ public void free(RubyThread thread, Pointer ptr, ConditionProfile freeProfile) { public void freeAll(RubyThread thread) { ThreadLocalBuffer current = this; - thread.ioBuffer = NULL_BUFFER; + thread.ioBuffer = null; while (current != null) { current.freeMemory(); current = current.parent; } } - public Pointer allocate(RubyThread thread, long size, ConditionProfile allocationProfile) { + public Pointer allocate(RubyContext context, RubyThread thread, long size, ConditionProfile allocationProfile) { /* If there is space in the thread's existing buffer then we will return a pointer to that and reduce the * remaining space count. Otherwise we will either allocate a new buffer, or (if no space is currently being * used in the existing buffer) replace it with a larger one. */ @@ -77,13 +77,13 @@ public Pointer allocate(RubyThread thread, long size, ConditionProfile allocatio * or reallocating a buffer that we technically have a pointer to. */ final long allocationSize = alignUp(size); if (allocationProfile.profile(remaining >= allocationSize)) { - final Pointer pointer = new Pointer(cursor(), allocationSize); + final Pointer pointer = new Pointer(context, cursor(), allocationSize); remaining -= allocationSize; assert invariants(); return pointer; } else { - final ThreadLocalBuffer newBuffer = allocateNewBlock(thread, allocationSize); - final Pointer pointer = new Pointer(newBuffer.start.getAddress(), allocationSize); + final ThreadLocalBuffer newBuffer = allocateNewBlock(context, thread, allocationSize); + final Pointer pointer = new Pointer(context, newBuffer.start.getAddress(), allocationSize); newBuffer.remaining -= allocationSize; assert newBuffer.invariants(); return pointer; @@ -95,7 +95,7 @@ private static long alignUp(long size) { } @TruffleBoundary - private ThreadLocalBuffer allocateNewBlock(RubyThread thread, long size) { + private ThreadLocalBuffer allocateNewBlock(RubyContext context, RubyThread thread, long size) { // Allocate a new buffer. Chain it if we aren't the default thread buffer, otherwise make a new default buffer. final long blockSize = Math.max(size, 1024); final ThreadLocalBuffer newBuffer; @@ -103,9 +103,9 @@ private ThreadLocalBuffer allocateNewBlock(RubyThread thread, long size) { // Free the old block freeMemory(); // Create new bigger block - newBuffer = new ThreadLocalBuffer(Pointer.malloc(blockSize), null); + newBuffer = new ThreadLocalBuffer(Pointer.malloc(context, blockSize), null); } else { - newBuffer = new ThreadLocalBuffer(Pointer.malloc(blockSize), this); + newBuffer = new ThreadLocalBuffer(Pointer.malloc(context, blockSize), this); } thread.ioBuffer = newBuffer; return newBuffer; diff --git a/src/main/java/org/truffleruby/core/thread/ThreadManager.java b/src/main/java/org/truffleruby/core/thread/ThreadManager.java index 60f9d1522a7a..f0bd8129b756 100644 --- a/src/main/java/org/truffleruby/core/thread/ThreadManager.java +++ b/src/main/java/org/truffleruby/core/thread/ThreadManager.java @@ -41,6 +41,7 @@ import org.truffleruby.core.klass.RubyClass; import org.truffleruby.core.string.StringUtils; import org.truffleruby.core.support.PRNGRandomizerNodes; +import org.truffleruby.extra.ffi.Pointer; import org.truffleruby.interop.InteropNodes; import org.truffleruby.interop.TranslateInteropExceptionNode; import org.truffleruby.language.Nil; @@ -375,6 +376,7 @@ public void startForeignThread(RubyThread rubyThread, Thread javaThread) { public void start(RubyThread thread, Thread javaThread) { thread.thread = javaThread; + thread.ioBuffer = context.getEnv().isNativeAccessAllowed() ? Pointer.getNullBuffer(context) : null; registerThread(thread); final RubyFiber rootFiber = thread.getRootFiber(); @@ -397,7 +399,9 @@ private void cleanupKillOtherFibers(RubyThread thread) { public void cleanupThreadState(RubyThread thread, Thread javaThread) { context.fiberManager.cleanup(thread.getRootFiber(), javaThread); - thread.ioBuffer.freeAll(thread); + if (thread.ioBuffer != null) { + thread.ioBuffer.freeAll(thread); + } unregisterThread(thread); thread.thread = null; diff --git a/src/main/java/org/truffleruby/extra/ffi/Pointer.java b/src/main/java/org/truffleruby/extra/ffi/Pointer.java index 05a7ce406f4e..0350f0434477 100644 --- a/src/main/java/org/truffleruby/extra/ffi/Pointer.java +++ b/src/main/java/org/truffleruby/extra/ffi/Pointer.java @@ -23,39 +23,62 @@ import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import org.truffleruby.core.thread.ThreadLocalBuffer; +import org.truffleruby.language.control.RaiseException; import sun.misc.Unsafe; @ExportLibrary(InteropLibrary.class) public final class Pointer implements AutoCloseable, TruffleObject { - public static final Pointer NULL = new Pointer(0); + private static final Pointer NULL = new Pointer(); + private static final ThreadLocalBuffer NULL_BUFFER = new ThreadLocalBuffer(NULL, null); + public static final long SIZE = Long.BYTES; public static final long UNBOUNDED = Long.MAX_VALUE; public static final Pointer[] EMPTY_ARRAY = new Pointer[0]; - /** Allocates memory and produces a pointer to it. Does not clear or initialize the memory, so it will contain - * arbitrary values. Use {@link #calloc} to get cleared memory. */ - public static Pointer malloc(long size) { - return new Pointer(UNSAFE.allocateMemory(size), size); + public static void checkNativeAccess(RubyContext context) { + if (!context.getEnv().isNativeAccessAllowed()) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + throw new RaiseException( + context, + context.getCoreExceptions().securityError("native access is not allowed", null)); + } + } + + public static Pointer getNullPointer(RubyContext context) { + checkNativeAccess(context); + return NULL; + } + + public static ThreadLocalBuffer getNullBuffer(RubyContext context) { + checkNativeAccess(context); + return NULL_BUFFER; + } + + public static Pointer malloc(RubyContext context, long size) { + checkNativeAccess(context); + return new Pointer(context, UNSAFE.allocateMemory(size), size); } /** Includes {@link #enableAutorelease(RubyLanguage)} and avoids locking for it */ - public static Pointer mallocAutoRelease(long size, RubyLanguage language) { - return new Pointer(UNSAFE.allocateMemory(size), size, language); + public static Pointer mallocAutoRelease(RubyLanguage language, RubyContext context, long size) { + checkNativeAccess(context); + return new Pointer(language, context, UNSAFE.allocateMemory(size), size); } /** Allocates memory and produces a pointer to it. Clears the memory before returning it. Use {@link #malloc} if you * do not need the memory to be cleared. */ - public static Pointer calloc(long size) { - final Pointer pointer = malloc(size); + public static Pointer calloc(RubyContext context, long size) { + final Pointer pointer = malloc(context, size); pointer.writeBytes(0, size, (byte) 0); return pointer; } /** Includes {@link #enableAutorelease(RubyLanguage)} and avoids locking for it */ - public static Pointer callocAutoRelease(long size, RubyLanguage language) { - final Pointer pointer = mallocAutoRelease(size, language); + public static Pointer callocAutoRelease(RubyLanguage language, RubyContext context, long size) { + final Pointer pointer = mallocAutoRelease(language, context, size); pointer.writeBytes(0, size, (byte) 0); return pointer; } @@ -89,16 +112,23 @@ public void markFreed() { } } - public Pointer(long address) { - this(address, UNBOUNDED); + private Pointer() { + this.address = 0L; + this.size = 0L; + } + + public Pointer(RubyContext context, long address) { + this(context, address, UNBOUNDED); } - public Pointer(long address, long size) { + public Pointer(RubyContext context, long address, long size) { + checkNativeAccess(context); this.address = address; this.size = size; } - private Pointer(long address, long size, RubyLanguage language) { + private Pointer(RubyLanguage language, RubyContext context, long address, long size) { + checkNativeAccess(context); this.address = address; this.size = size; enableAutoreleaseUnsynchronized(language); @@ -294,8 +324,8 @@ private int checkStringSize(long size) { return (int) size; } - public Pointer readPointer(long offset) { - return new Pointer(readLong(offset)); + public Pointer readPointer(RubyContext context, long offset) { + return new Pointer(context, readLong(offset)); } @TruffleBoundary diff --git a/src/main/java/org/truffleruby/extra/ffi/PointerNodes.java b/src/main/java/org/truffleruby/extra/ffi/PointerNodes.java index 233ec191d1f6..213d8bb7c2d6 100644 --- a/src/main/java/org/truffleruby/extra/ffi/PointerNodes.java +++ b/src/main/java/org/truffleruby/extra/ffi/PointerNodes.java @@ -79,7 +79,7 @@ public abstract static class AllocateNode extends UnaryCoreMethodNode { @Specialization protected RubyPointer allocate(RubyClass pointerClass) { final Shape shape = getLanguage().truffleFFIPointerShape; - final RubyPointer instance = new RubyPointer(pointerClass, shape, Pointer.NULL); + final RubyPointer instance = new RubyPointer(pointerClass, shape, Pointer.getNullPointer(getContext())); AllocationTracing.trace(instance, this); return instance; } @@ -141,7 +141,7 @@ public abstract static class PointerSetAddressNode extends CoreMethodArrayArgume @Specialization protected long setAddress(RubyPointer pointer, long address) { - pointer.pointer = new Pointer(address); + pointer.pointer = new Pointer(getContext(), address); return address; } @@ -173,7 +173,7 @@ public abstract static class PointerSetSizeNode extends CoreMethodArrayArguments @Specialization protected long setSize(RubyPointer pointer, long size) { final Pointer old = pointer.pointer; - pointer.pointer = new Pointer(old.getAddress(), size); + pointer.pointer = new Pointer(getContext(), old.getAddress(), size); return size; } @@ -211,7 +211,7 @@ public abstract static class PointerMallocPrimitiveNode extends PrimitiveArrayAr @Specialization protected RubyPointer malloc(RubyPointer pointer, long size) { - pointer.pointer = Pointer.malloc(size); + pointer.pointer = Pointer.malloc(getContext(), size); return pointer; } @@ -244,9 +244,9 @@ public abstract static class PointerCopyMemoryNode extends PointerPrimitiveArray @Specialization protected Object copyMemory(long to, long from, long size) { - final Pointer ptr = new Pointer(to); + final Pointer ptr = new Pointer(getContext(), to); checkNull(ptr); - ptr.writeBytes(0, new Pointer(from), 0, size); + ptr.writeBytes(0, new Pointer(getContext(), from), 0, size); return nil; } @@ -266,7 +266,7 @@ protected RubyString readNullPointer(long address, long limit) { protected RubyString readStringToNull(long address, long limit, @Cached TruffleString.FromByteArrayNode fromByteArrayNode, @CachedLibrary(limit = "1") InteropLibrary interop) { - final Pointer ptr = new Pointer(address); + final Pointer ptr = new Pointer(getContext(), address); checkNull(ptr); final byte[] bytes = ptr.readZeroTerminatedByteArray(getContext(), interop, 0, limit); return createString(fromByteArrayNode, bytes, Encodings.BINARY); @@ -276,7 +276,7 @@ protected RubyString readStringToNull(long address, long limit, protected RubyString readStringToNull(long address, Nil limit, @CachedLibrary(limit = "1") InteropLibrary interop, @Cached TruffleString.FromByteArrayNode fromByteArrayNode) { - final Pointer ptr = new Pointer(address); + final Pointer ptr = new Pointer(getContext(), address); checkNull(ptr); final byte[] bytes = ptr.readZeroTerminatedByteArray(getContext(), interop, 0); return createString(fromByteArrayNode, bytes, Encodings.BINARY); @@ -290,7 +290,7 @@ public abstract static class PointerReadBytesToArrayNode extends PointerPrimitiv @Specialization protected Object readBytes(RubyByteArray array, int arrayOffset, long address, int length, @Cached ConditionProfile zeroProfile) { - final Pointer ptr = new Pointer(address); + final Pointer ptr = new Pointer(getContext(), address); if (zeroProfile.profile(length == 0)) { // No need to check the pointer address if we read nothing return nil; @@ -311,7 +311,7 @@ public abstract static class PointerReadBytesNode extends PointerPrimitiveArrayA protected RubyString readBytes(long address, int length, @Cached ConditionProfile zeroProfile, @Cached TruffleString.FromByteArrayNode fromByteArrayNode) { - final Pointer ptr = new Pointer(address); + final Pointer ptr = new Pointer(getContext(), address); if (zeroProfile.profile(length == 0)) { // No need to check the pointer address if we read nothing return createString(TStringConstants.EMPTY_BINARY, Encodings.BINARY); @@ -333,7 +333,7 @@ protected Object writeBytes(long address, Object string, int index, int length, @Cached ConditionProfile nonZeroProfile, @Cached TruffleString.CopyToNativeMemoryNode copyToNativeMemoryNode, @Cached RubyStringLibrary libString) { - Pointer ptr = new Pointer(address); + Pointer ptr = new Pointer(getContext(), address); var tstring = libString.getTString(string); var encoding = libString.getTEncoding(string); @@ -358,7 +358,7 @@ public abstract static class PointerReadCharNode extends PointerPrimitiveArrayAr @Specialization protected int readCharSigned(long address) { - final Pointer ptr = new Pointer(address); + final Pointer ptr = new Pointer(getContext(), address); checkNull(ptr); return ptr.readByte(0); } @@ -370,7 +370,7 @@ public abstract static class PointerReadUCharNode extends PointerPrimitiveArrayA @Specialization protected int readCharUnsigned(long address) { - final Pointer ptr = new Pointer(address); + final Pointer ptr = new Pointer(getContext(), address); checkNull(ptr); return Byte.toUnsignedInt(ptr.readByte(0)); } @@ -382,7 +382,7 @@ public abstract static class PointerReadShortNode extends PointerPrimitiveArrayA @Specialization protected int readShortSigned(long address) { - final Pointer ptr = new Pointer(address); + final Pointer ptr = new Pointer(getContext(), address); checkNull(ptr); return ptr.readShort(0); } @@ -393,7 +393,7 @@ public abstract static class PointerReadUShortNode extends PointerPrimitiveArray @Specialization protected int readShortUnsigned(long address) { - final Pointer ptr = new Pointer(address); + final Pointer ptr = new Pointer(getContext(), address); checkNull(ptr); return Short.toUnsignedInt(ptr.readShort(0)); } @@ -405,7 +405,7 @@ public abstract static class PointerReadIntNode extends PointerPrimitiveArrayArg @Specialization protected int readIntSigned(long address) { - final Pointer ptr = new Pointer(address); + final Pointer ptr = new Pointer(getContext(), address); checkNull(ptr); return ptr.readInt(0); } @@ -417,7 +417,7 @@ public abstract static class PointerReadUIntNode extends PointerPrimitiveArrayAr @Specialization protected long readIntUnsigned(long address) { - final Pointer ptr = new Pointer(address); + final Pointer ptr = new Pointer(getContext(), address); checkNull(ptr); return Integer.toUnsignedLong(ptr.readInt(0)); } @@ -429,7 +429,7 @@ public abstract static class PointerReadLongNode extends PointerPrimitiveArrayAr @Specialization protected long readLongSigned(long address) { - final Pointer ptr = new Pointer(address); + final Pointer ptr = new Pointer(getContext(), address); checkNull(ptr); return ptr.readLong(0); } @@ -441,7 +441,7 @@ public abstract static class PointerReadULongNode extends PointerPrimitiveArrayA @Specialization protected Object readLongUnsigned(long address) { - final Pointer ptr = new Pointer(address); + final Pointer ptr = new Pointer(getContext(), address); checkNull(ptr); return readUnsignedLong(ptr, 0); } @@ -459,7 +459,7 @@ public abstract static class PointerReadFloatNode extends PointerPrimitiveArrayA // must return double so Ruby nodes can deal with it @Specialization protected double readFloat(long address) { - final Pointer ptr = new Pointer(address); + final Pointer ptr = new Pointer(getContext(), address); checkNull(ptr); return ptr.readFloat(0); } @@ -471,7 +471,7 @@ public abstract static class PointerReadDoubleNode extends PointerPrimitiveArray @Specialization protected double readDouble(long address) { - final Pointer ptr = new Pointer(address); + final Pointer ptr = new Pointer(getContext(), address); checkNull(ptr); return ptr.readDouble(0); } @@ -483,9 +483,9 @@ public abstract static class PointerReadPointerNode extends PointerPrimitiveArra @Specialization protected RubyPointer readPointer(long address) { - final Pointer ptr = new Pointer(address); + final Pointer ptr = new Pointer(getContext(), address); checkNull(ptr); - final Pointer readPointer = ptr.readPointer(0); + final Pointer readPointer = ptr.readPointer(getContext(), 0); final RubyPointer instance = new RubyPointer( coreLibrary().truffleFFIPointerClass, getLanguage().truffleFFIPointerShape, @@ -502,7 +502,7 @@ public abstract static class PointerWriteCharNode extends PointerPrimitiveArrayA @Specialization(guards = { "MIN_VALUE <= value", "value <= MAX_VALUE" }) protected Object writeChar(long address, int value) { - final Pointer ptr = new Pointer(address); + final Pointer ptr = new Pointer(getContext(), address); checkNull(ptr); byte byteValue = (byte) value; ptr.writeByte(0, byteValue); @@ -517,7 +517,7 @@ public abstract static class PointerWriteUCharNode extends PointerPrimitiveArray @Specialization(guards = { "0 <= value", "value <= MAX_VALUE" }) protected Object writeChar(long address, int value) { - final Pointer ptr = new Pointer(address); + final Pointer ptr = new Pointer(getContext(), address); checkNull(ptr); byte byteValue = (byte) value; ptr.writeByte(0, byteValue); @@ -526,7 +526,7 @@ protected Object writeChar(long address, int value) { @Specialization(guards = { "value > MAX_VALUE", "value < 256" }) protected Object writeUnsignedChar(long address, int value) { - final Pointer ptr = new Pointer(address); + final Pointer ptr = new Pointer(getContext(), address); checkNull(ptr); byte signed = (byte) value; // Same as value - 2^8 ptr.writeByte(0, signed); @@ -541,7 +541,7 @@ public abstract static class PointerWriteShortNode extends PointerPrimitiveArray @Specialization(guards = { "MIN_VALUE <= value", "value <= MAX_VALUE" }) protected Object writeShort(long address, int value) { - final Pointer ptr = new Pointer(address); + final Pointer ptr = new Pointer(getContext(), address); checkNull(ptr); short shortValue = (short) value; ptr.writeShort(0, shortValue); @@ -556,7 +556,7 @@ public abstract static class PointerWriteUShortNode extends PointerPrimitiveArra @Specialization(guards = { "0 <= value", "value <= MAX_VALUE" }) protected Object writeShort(long address, int value) { - final Pointer ptr = new Pointer(address); + final Pointer ptr = new Pointer(getContext(), address); checkNull(ptr); short shortValue = (short) value; ptr.writeShort(0, shortValue); @@ -565,7 +565,7 @@ protected Object writeShort(long address, int value) { @Specialization(guards = { "value > MAX_VALUE", "value < 65536" }) protected Object writeUnsignedSort(long address, int value) { - final Pointer ptr = new Pointer(address); + final Pointer ptr = new Pointer(getContext(), address); checkNull(ptr); short signed = (short) value; // Same as value - 2^16 ptr.writeShort(0, signed); @@ -579,7 +579,7 @@ public abstract static class PointerWriteIntNode extends PointerPrimitiveArrayAr @Specialization protected Object writeInt(long address, int value) { - final Pointer ptr = new Pointer(address); + final Pointer ptr = new Pointer(getContext(), address); checkNull(ptr); ptr.writeInt(0, value); return nil; @@ -595,7 +595,7 @@ public abstract static class PointerWriteUIntNode extends PointerPrimitiveArrayA @Specialization(guards = "value >= 0") protected Object writeInt(long address, int value) { - final Pointer ptr = new Pointer(address); + final Pointer ptr = new Pointer(getContext(), address); checkNull(ptr); ptr.writeInt(0, value); return nil; @@ -603,7 +603,7 @@ protected Object writeInt(long address, int value) { @Specialization(guards = { "value > MAX_VALUE", "value < MAX_UNSIGNED_INT_PLUS_ONE" }) protected Object writeUnsignedInt(long address, long value) { - final Pointer ptr = new Pointer(address); + final Pointer ptr = new Pointer(getContext(), address); checkNull(ptr); int signed = (int) value; // Same as value - 2^32 ptr.writeInt(0, signed); @@ -617,7 +617,7 @@ public abstract static class PointerWriteLongNode extends PointerPrimitiveArrayA @Specialization protected Object writeLong(long address, long value) { - final Pointer ptr = new Pointer(address); + final Pointer ptr = new Pointer(getContext(), address); checkNull(ptr); ptr.writeLong(0, value); return nil; @@ -630,7 +630,7 @@ public abstract static class PointerWriteULongNode extends PointerPrimitiveArray @Specialization(guards = "value >= 0") protected Object writeLong(long address, long value) { - final Pointer ptr = new Pointer(address); + final Pointer ptr = new Pointer(getContext(), address); checkNull(ptr); ptr.writeLong(0, value); return nil; @@ -638,7 +638,7 @@ protected Object writeLong(long address, long value) { @Specialization protected Object writeUnsignedLong(long address, RubyBignum value) { - final Pointer ptr = new Pointer(address); + final Pointer ptr = new Pointer(getContext(), address); checkNull(ptr); writeUnsignedLong(ptr, 0, value); return nil; @@ -660,7 +660,7 @@ public abstract static class PointerWriteFloatNode extends PointerPrimitiveArray @Specialization protected Object writeFloat(long address, double value) { - final Pointer ptr = new Pointer(address); + final Pointer ptr = new Pointer(getContext(), address); checkNull(ptr); ptr.writeFloat(0, (float) value); return nil; @@ -673,7 +673,7 @@ public abstract static class PointerWriteDoubleNode extends PointerPrimitiveArra @Specialization protected Object writeDouble(long address, double value) { - final Pointer ptr = new Pointer(address); + final Pointer ptr = new Pointer(getContext(), address); checkNull(ptr); ptr.writeDouble(0, value); return nil; @@ -686,7 +686,7 @@ public abstract static class PointerWritePointerNode extends PointerPrimitiveArr @Specialization protected Object writePointer(long address, long value) { - final Pointer ptr = new Pointer(address); + final Pointer ptr = new Pointer(getContext(), address); checkNull(ptr); ptr.writePointer(0, value); return nil; diff --git a/src/main/java/org/truffleruby/language/loader/FeatureLoader.java b/src/main/java/org/truffleruby/language/loader/FeatureLoader.java index e67b68d9525a..6f27ef04eb10 100644 --- a/src/main/java/org/truffleruby/language/loader/FeatureLoader.java +++ b/src/main/java/org/truffleruby/language/loader/FeatureLoader.java @@ -203,10 +203,11 @@ private String initializeWorkingDirectory() { // The current working cannot change if there are no native calls return context.getEnv().getCurrentWorkingDirectory().getPath(); } + final int bufferSize = PATH_MAX; final RubyThread rubyThread = language.getCurrentThread(); final Pointer buffer = IOThreadBufferAllocateNode - .getBuffer(rubyThread, bufferSize, ConditionProfile.getUncached()); + .getBuffer(context, rubyThread, bufferSize, ConditionProfile.getUncached()); try { final long address; try { @@ -224,7 +225,7 @@ private String initializeWorkingDirectory() { final Encoding localeEncoding = context.getEncodingManager().getLocaleEncoding().jcoding; return new String(bytes, EncodingManager.charsetForEncoding(localeEncoding)); } finally { - rubyThread.ioBuffer.free(rubyThread, buffer, ConditionProfile.getUncached()); + rubyThread.getIoBuffer(context).free(rubyThread, buffer, ConditionProfile.getUncached()); } } diff --git a/src/main/ruby/truffleruby/core/truffle/ffi/pointer.rb b/src/main/ruby/truffleruby/core/truffle/ffi/pointer.rb index 1d2dc79611b3..50064aae1476 100644 --- a/src/main/ruby/truffleruby/core/truffle/ffi/pointer.rb +++ b/src/main/ruby/truffleruby/core/truffle/ffi/pointer.rb @@ -230,7 +230,12 @@ def put(type, offset, value) end SIZE = 8 - NULL = Pointer.new(0x0) + + Truffle::Boot.delay do + if Truffle::Boot.get_option 'platform-native' + NULL = Pointer.new(0x0) + end + end end class MemoryPointer < Pointer diff --git a/src/test/java/org/truffleruby/ContextPermissionsTest.java b/src/test/java/org/truffleruby/ContextPermissionsTest.java index 287025457f87..e5121077315d 100644 --- a/src/test/java/org/truffleruby/ContextPermissionsTest.java +++ b/src/test/java/org/truffleruby/ContextPermissionsTest.java @@ -31,6 +31,24 @@ public void testNoPermissions() throws Throwable { } } + @Test + public void testPointerNoNative() throws Throwable { + try (Context context = Context.newBuilder("ruby").build()) { + Assert.assertEquals(3, context.eval("ruby", "1 + 2").asInt()); + + Assert.assertTrue(context.eval("ruby", "defined?(Truffle::FFI::Pointer::NULL).nil?").asBoolean()); + RubyTest.assertThrows( + () -> context.eval("ruby", "Truffle::FFI::Pointer.allocate"), + e -> assertEquals("native access is not allowed (SecurityError)", e.getMessage())); + RubyTest.assertThrows( + () -> context.eval("ruby", "Truffle::FFI::Pointer.new(4)"), + e -> assertEquals("native access is not allowed (SecurityError)", e.getMessage())); + RubyTest.assertThrows( + () -> context.eval("ruby", "Truffle::FFI::MemoryPointer.new(4)"), + e -> assertEquals("native access is not allowed (SecurityError)", e.getMessage())); + } + } + @Test public void testNativeNoThreads() throws Throwable { try (Context context = Context.newBuilder("ruby").allowNativeAccess(true).build()) { @@ -61,8 +79,9 @@ public void testThreadsNoNative() throws Throwable { Assert.assertEquals(7, context.eval("ruby", "Thread.new { 3 + 4 }.value").asInt()); - String code = "begin; File.stat('.'); rescue SecurityError => e; e.message; end"; - Assert.assertEquals("native access is not allowed", context.eval("ruby", code).asString()); + RubyTest.assertThrows( + () -> context.eval("ruby", "File.stat('.')"), + e -> assertEquals("native access is not allowed (SecurityError)", e.getMessage())); } } @@ -76,8 +95,9 @@ public void testNoThreadsEnforcesSingleThreadedOption() throws Throwable { String option = "Truffle::Boot.get_option('single-threaded')"; Assert.assertEquals(true, context.eval("ruby", option).asBoolean()); - String code = "begin; Thread.new {}.join; rescue SecurityError => e; e.message; end"; - Assert.assertEquals("threads not allowed in single-threaded mode", context.eval("ruby", code).asString()); + RubyTest.assertThrows( + () -> context.eval("ruby", "Thread.new {}.join"), + e -> assertEquals("threads not allowed in single-threaded mode (SecurityError)", e.getMessage())); } } From 3fbcfae1e2b386288c9c5556530153c3c151a31a Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Tue, 27 Sep 2022 17:22:29 +0000 Subject: [PATCH 06/11] [GR-17457] Prepare changelog for 23.0 and cleanup 22.3 changelog PullRequest: truffleruby/3498 (cherry picked from commit e306ad33f2d66f5eca6bcd2344fac0160fb38cf8) --- CHANGELOG.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 841bf869f1b1..af0e8869b414 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ New features: -* Foreign strings now have all methods of Ruby `String`. They are treated as `#frozen?` UTF-8 Ruby Strings. +* Foreign strings now have all methods of Ruby `String`. They are treated as `#frozen?` UTF-8 Ruby Strings (@eregon). * Add `Java.add_to_classpath` method to add jar paths at runtime (#2693, @bjfish). * Add support for Ruby 3.1's Hash shorthand/punning syntax (@nirvdrum). * Add support for Ruby 3.1's anonymous block forwarding syntax (@nirvdrum). @@ -42,7 +42,6 @@ Compatibility: * Fix arguments implicit type conversion for `BasicObject#instance_eval`, `Module#class_eval`, `Module#module_eval`, `Module#define_method` (@andrykonchin). * Raise `ArgumentError` unconditionally when `Proc.new` is called without a block argument (@andrykonchin). - Performance: * Replace a call of `-"string"` with frozen string literal at parse time (@andrykonchin). @@ -52,7 +51,7 @@ Performance: Changes: -* No more conversion between Java Strings and Ruby Strings at the interop boundary. +* No more conversion between Java Strings and Ruby Strings at the interop boundary (@eregon). * Removed `Truffle::Interop.{import_without_conversion,export_without_conversion}` (use `Polyglot.{import,export}` instead). * Removed `Truffle::Interop.members_without_conversion` (use `Truffle::Interop.members` instead). * Refactored internals of `rb_sprintf` to simplify handling of `VALUE`s in common cases (@aardvark179). From 3043215fc32e00c35b5c739c31d9ef5bf94eb560 Mon Sep 17 00:00:00 2001 From: Andrii Konchyn Date: Wed, 28 Sep 2022 13:03:36 +0000 Subject: [PATCH 07/11] [GR-18163] UnboundMethod#hash is the same for the same method in subclass and superclass PullRequest: truffleruby/3493 (cherry picked from commit f2d0b66df81d07c8c9eccd573c04b41c8f70e547) --- CHANGELOG.md | 1 + spec/ruby/core/unboundmethod/fixtures/classes.rb | 10 ++++++++++ spec/ruby/core/unboundmethod/hash_spec.rb | 7 +++++++ .../truffleruby/core/method/UnboundMethodNodes.java | 1 - 4 files changed, 18 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index af0e8869b414..173538ca04d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,6 +41,7 @@ Compatibility: * Fix exception for `Fiddle::Handle.new` with a missing library (#2714, @eregon). * Fix arguments implicit type conversion for `BasicObject#instance_eval`, `Module#class_eval`, `Module#module_eval`, `Module#define_method` (@andrykonchin). * Raise `ArgumentError` unconditionally when `Proc.new` is called without a block argument (@andrykonchin). +* Fix `UnboundMethod#hash` to not depend on a module it was retrieved from (#2728, @andrykonchin). Performance: diff --git a/spec/ruby/core/unboundmethod/fixtures/classes.rb b/spec/ruby/core/unboundmethod/fixtures/classes.rb index 46b1c51669a2..1f466e39d86e 100644 --- a/spec/ruby/core/unboundmethod/fixtures/classes.rb +++ b/spec/ruby/core/unboundmethod/fixtures/classes.rb @@ -84,4 +84,14 @@ def overridden; end class C < B def overridden; end end + + module HashSpecs + class SuperClass + def foo + end + end + + class SubClass < SuperClass + end + end end diff --git a/spec/ruby/core/unboundmethod/hash_spec.rb b/spec/ruby/core/unboundmethod/hash_spec.rb index 12dce0020fe2..6888675bc111 100644 --- a/spec/ruby/core/unboundmethod/hash_spec.rb +++ b/spec/ruby/core/unboundmethod/hash_spec.rb @@ -12,4 +12,11 @@ to_s, inspect = Array.instance_method(:to_s), Array.instance_method(:inspect) to_s.hash.should == inspect.hash end + + it "equals a hash of the same method in the superclass" do + foo_in_superclass = UnboundMethodSpecs::HashSpecs::SuperClass.instance_method(:foo) + foo = UnboundMethodSpecs::HashSpecs::SubClass.instance_method(:foo) + + foo.hash.should == foo_in_superclass.hash + end end diff --git a/src/main/java/org/truffleruby/core/method/UnboundMethodNodes.java b/src/main/java/org/truffleruby/core/method/UnboundMethodNodes.java index db54560350e8..10ea2fa9d237 100644 --- a/src/main/java/org/truffleruby/core/method/UnboundMethodNodes.java +++ b/src/main/java/org/truffleruby/core/method/UnboundMethodNodes.java @@ -121,7 +121,6 @@ public abstract static class HashNode extends CoreMethodArrayArgumentsNode { protected long hash(RubyUnboundMethod rubyMethod) { final InternalMethod method = rubyMethod.method; long h = getContext().getHashing(this).start(method.getDeclaringModule().hashCode()); - h = Hashing.update(h, rubyMethod.origin.hashCode()); h = Hashing.update(h, MethodNodes.hashInternalMethod(method)); return Hashing.end(h); } From 8c556ce7a96feb72a576a3e0cda15a95005a054b Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Thu, 29 Sep 2022 23:07:14 +0000 Subject: [PATCH 08/11] [GR-33155] Implement interop exception messages on RubyException PullRequest: truffleruby/3497 (cherry picked from commit 7cc607338c2d83cb834f3f5afe0016af6e523f08) --- doc/contributor/interop_details.md | 68 +++++++++++++++-- spec/truffle/interop/matrix_spec.rb | 75 ++++++++++++++++++- .../core/exception/ExceptionNodes.java | 15 ++++ .../core/exception/ExceptionOperations.java | 4 +- .../core/exception/RubyException.java | 49 ++++++++++++ .../language/control/RaiseException.java | 8 +- .../core/truffle/exception_operations.rb | 25 ++++--- .../truffleruby/ContextPermissionsTest.java | 25 +++++-- .../org/truffleruby/JSR223InteropTest.java | 2 +- .../org/truffleruby/PolyglotInteropTest.java | 4 +- 10 files changed, 240 insertions(+), 35 deletions(-) diff --git a/doc/contributor/interop_details.md b/doc/contributor/interop_details.md index 5905121d566c..1cb2c4681654 100644 --- a/doc/contributor/interop_details.md +++ b/doc/contributor/interop_details.md @@ -16,6 +16,8 @@ - **a `Class`** - **a `Hash`** - **an `Array`** +- **an `Exception`** +- **an `Exception` with a cause** - **`proc {...}`** - **`lambda {...}`** - **a `Method`** @@ -307,11 +309,11 @@ When interop message `getHashValuesIterator` is sent ## Members related messages (incomplete) When interop message `readMember` is sent -- to any non-immediate `Object` like **`nil`**, **`:symbol`**, **a `String`**, **a `BigDecimal`**, **an `Object`**, **a frozen `Object`**, **a `StructWithValue`**, **a `Class`**, **a `Hash`**, **an `Array`**, **`proc {...}`**, **`lambda {...}`**, **a `Method`**, **a `Truffle::FFI::Pointer`**, **polyglot pointer**, **polyglot array** or **polyglot hash** +- to any non-immediate `Object` like **`nil`**, **`:symbol`**, **a `String`**, **a `BigDecimal`**, **an `Object`**, **a frozen `Object`**, **a `StructWithValue`**, **a `Class`**, **a `Hash`**, **an `Array`**, **an `Exception`**, **an `Exception` with a cause**, **`proc {...}`**, **`lambda {...}`**, **a `Method`**, **a `Truffle::FFI::Pointer`**, **polyglot pointer**, **polyglot array** or **polyglot hash** it returns a method with the given name when the method is defined. -- to any non-immediate `Object` like **`nil`**, **`:symbol`**, **a `String`**, **a `BigDecimal`**, **an `Object`**, **a frozen `Object`**, **a `StructWithValue`**, **a `Class`**, **a `Hash`**, **an `Array`**, **`proc {...}`**, **`lambda {...}`**, **a `Method`**, **a `Truffle::FFI::Pointer`**, **polyglot pointer**, **polyglot array** or **polyglot hash** +- to any non-immediate `Object` like **`nil`**, **`:symbol`**, **a `String`**, **a `BigDecimal`**, **an `Object`**, **a frozen `Object`**, **a `StructWithValue`**, **a `Class`**, **a `Hash`**, **an `Array`**, **an `Exception`**, **an `Exception` with a cause**, **`proc {...}`**, **`lambda {...}`**, **a `Method`**, **a `Truffle::FFI::Pointer`**, **polyglot pointer**, **polyglot array** or **polyglot hash** it fails with `UnknownIdentifierException` when the method is not defined. -- to any non-immediate `Object` like **a `String`**, **an `Object`**, **a `StructWithValue`**, **a `Class`**, **a `Hash`**, **an `Array`**, **`proc {...}`**, **`lambda {...}`**, **a `Method`**, **a `Truffle::FFI::Pointer`**, **polyglot pointer**, **polyglot array** or **polyglot hash** +- to any non-immediate `Object` like **a `String`**, **an `Object`**, **a `StructWithValue`**, **a `Class`**, **a `Hash`**, **an `Array`**, **an `Exception`**, **an `Exception` with a cause**, **`proc {...}`**, **`lambda {...}`**, **a `Method`**, **a `Truffle::FFI::Pointer`**, **polyglot pointer**, **polyglot array** or **polyglot hash** it reads the given instance variable. - to **polyglot members** it reads the value stored with the given name. @@ -321,7 +323,7 @@ When interop message `readMember` is sent it fails with `UnsupportedMessageError`. When interop message `writeMember` is sent -- to any non-immediate non-frozen `Object` like **a `String`**, **an `Object`**, **a `StructWithValue`**, **a `Class`**, **a `Hash`**, **an `Array`**, **`proc {...}`**, **`lambda {...}`**, **a `Method`**, **a `Truffle::FFI::Pointer`**, **polyglot pointer**, **polyglot array** or **polyglot hash** +- to any non-immediate non-frozen `Object` like **a `String`**, **an `Object`**, **a `StructWithValue`**, **a `Class`**, **a `Hash`**, **an `Array`**, **an `Exception`**, **an `Exception` with a cause**, **`proc {...}`**, **`lambda {...}`**, **a `Method`**, **a `Truffle::FFI::Pointer`**, **polyglot pointer**, **polyglot array** or **polyglot hash** it writes the given instance variable. - to **polyglot members** it writes the given value under the given name. @@ -332,10 +334,64 @@ When interop message `writeMember` is sent - otherwise it fails with `UnsupportedMessageError`. +## Exception related messages + +When interop message `isException` is sent +- to **an `Exception`** or **an `Exception` with a cause** + it returns true. +- otherwise + it returns false. + +When interop message `throwException` is sent +- to **an `Exception`** or **an `Exception` with a cause** + it throws the exception. +- otherwise + it fails with `UnsupportedMessageError`. + +When interop message `getExceptionType` is sent +- to **an `Exception`** or **an `Exception` with a cause** + it returns the exception type. +- otherwise + it fails with `UnsupportedMessageError`. + +When interop message `hasExceptionMessage` is sent +- to **an `Exception`** or **an `Exception` with a cause** + it returns true. +- otherwise + it returns false. + +When interop message `getExceptionMessage` is sent +- to **an `Exception`** or **an `Exception` with a cause** + it returns the message of the exception. +- otherwise + it fails with `UnsupportedMessageError`. + +When interop message `hasExceptionStackTrace` is sent +- to **an `Exception`** or **an `Exception` with a cause** + it returns true. +- otherwise + it returns false. + +When interop message `getExceptionStackTrace` is sent +- to **an `Exception`** or **an `Exception` with a cause** + it returns the stacktrace of the exception. +- otherwise + it fails with `UnsupportedMessageError`. + +When interop message `hasExceptionCause` is sent +- to **an `Exception` with a cause** + it returns true. +- otherwise + it returns false. + +When interop message `getExceptionCause` is sent +- to **an `Exception` with a cause** + it returns the cause of the exception. +- otherwise + it fails with `UnsupportedMessageError`. + ## Number related messages (missing) ## Instantiation related messages (missing) -## Exception related messages (missing) - ## Time related messages (unimplemented) diff --git a/spec/truffle/interop/matrix_spec.rb b/spec/truffle/interop/matrix_spec.rb index 60440f756d0f..c623a308a778 100644 --- a/spec/truffle/interop/matrix_spec.rb +++ b/spec/truffle/interop/matrix_spec.rb @@ -1,3 +1,5 @@ +# truffleruby_primitives: true + # Copyright (c) 2018, 2021 Oracle and/or its affiliates. All rights reserved. This # code is released under a tri EPL/GPL/LGPL license. You can use it, # redistribute it and/or modify it under the terms of the: @@ -243,6 +245,33 @@ def spec_it(subject) module: Subject.(name: AN_INSTANCE) { Module.new }, hash: Subject.(name: AN_INSTANCE, doc: true) { {} }, array: Subject.(name: AN_INSTANCE, doc: true) { [] }, + # raise & rescue to give it a backtrace + exception: Subject.(name: "an `Exception`", doc: true) do + begin + raise "the exception message" + rescue => e + e + end + end, + exception_with_cause: Subject.(name: "an `Exception` with a cause", doc: true) do + begin + raise "the cause" + rescue + begin + raise "the exception message" + rescue => e + e + end + end + end, + # also test RaiseException since it is what other languages see when they catch an exception from Ruby + raise_exception: Subject.(name: AN_INSTANCE) do + begin + raise "the exception message" + rescue => e + Primitive.exception_get_raise_exception(e) + end + end, proc: Subject.(proc { |v| v }, name: code("proc {...}"), doc: true), lambda: Subject.(-> v { v }, name: code("lambda {...}"), doc: true), @@ -286,6 +315,7 @@ def spec_it(subject) immediate_subjects = [:false, :true, :zero, :small_integer, :zero_float, :small_float] non_immediate_subjects = SUBJECTS.keys - immediate_subjects frozen_subjects = [:big_decimal, :nil, :symbol, :strange_symbol, :frozen_object] + exception_subjects = [:exception, :exception_with_cause, :raise_exception] # not part of the standard matrix, not considered in last rest case EXTRA_SUBJECTS = { @@ -298,7 +328,7 @@ def spec_it(subject) def predicate(name, is, *message_args, &setup) -> subject do setup.call subject if setup - Truffle::Interop.send(name, subject, *message_args).send(is ? :should : :should_not, be_true) + Truffle::Interop.send(name, subject, *message_args).should == is end end @@ -580,7 +610,7 @@ def array_element_predicate(message, predicate, insert_on_true_case) Delimiter["Members related messages (incomplete)"], Message[:readMember, Test.new("returns a method with the given name when the method is defined", "any non-immediate `Object`", - *non_immediate_subjects - [:polyglot_object]) do |subject| + *non_immediate_subjects - [:polyglot_object, :raise_exception]) do |subject| Truffle::Interop.read_member(subject, 'to_s').should == subject.method(:to_s) end, Test.new("fails with `UnknownIdentifierException` when the method is not defined", "any non-immediate `Object`", @@ -626,9 +656,48 @@ def array_element_predicate(message, predicate, insert_on_true_case) end, unsupported_test { |subject| Truffle::Interop.write_member(subject, :something, 'val') }], + Delimiter["Exception related messages"], + Message[:isException, + Test.new("returns true", *exception_subjects, &predicate(:exception?, true)), + Test.new("returns false", &predicate(:exception?, false))], + Message[:throwException, + Test.new("throws the exception", *exception_subjects) do |subject| + -> { Truffle::Interop.throw_exception(subject) }.should raise_error { |e| e.should.equal?(subject) } + end, + unsupported_test { |subject| Truffle::Interop.throw_exception(subject) }], + Message[:getExceptionType, + Test.new("returns the exception type", *exception_subjects) do |subject| + Truffle::Interop.exception_type(subject).should == :RUNTIME_ERROR + end, + unsupported_test { |subject| Truffle::Interop.exception_type(subject) }], + Message[:hasExceptionMessage, + Test.new("returns true", *exception_subjects, &predicate(:has_exception_message?, true)), + Test.new("returns false", &predicate(:has_exception_message?, false))], + Message[:getExceptionMessage, + Test.new("returns the message of the exception", *exception_subjects) do |subject| + Truffle::Interop.exception_message(subject).should == "the exception message" + end, + unsupported_test { |subject| Truffle::Interop.exception_message(subject) }], + Message[:hasExceptionStackTrace, + Test.new("returns true", *exception_subjects, &predicate(:has_exception_stack_trace?, true)), + Test.new("returns false", &predicate(:has_exception_stack_trace?, false))], + Message[:getExceptionStackTrace, + Test.new("returns the stacktrace of the exception", *exception_subjects) do |subject| + stacktrace = Truffle::Interop.exception_stack_trace(subject) + Truffle::Interop.should.has_array_elements?(stacktrace) + end, + unsupported_test { |subject| Truffle::Interop.exception_stack_trace(subject) }], + Message[:hasExceptionCause, + Test.new("returns true", :exception_with_cause, &predicate(:has_exception_cause?, true)), + Test.new("returns false", &predicate(:has_exception_cause?, false))], + Message[:getExceptionCause, + Test.new("returns the cause of the exception", :exception_with_cause) do |subject| + Truffle::Interop.exception_cause(subject).should == subject.cause + end, + unsupported_test { |subject| Truffle::Interop.exception_cause(subject) }], + Delimiter["Number related messages (missing)"], Delimiter["Instantiation related messages (missing)"], - Delimiter["Exception related messages (missing)"], Delimiter["Time related messages (unimplemented)"], ] diff --git a/src/main/java/org/truffleruby/core/exception/ExceptionNodes.java b/src/main/java/org/truffleruby/core/exception/ExceptionNodes.java index 28338ecff060..d6f037605cc5 100644 --- a/src/main/java/org/truffleruby/core/exception/ExceptionNodes.java +++ b/src/main/java/org/truffleruby/core/exception/ExceptionNodes.java @@ -353,4 +353,19 @@ protected int limit() { } + @Primitive(name = "exception_get_raise_exception") + public abstract static class GetRaiseExceptionNode extends CoreMethodArrayArgumentsNode { + + @Specialization + protected Object getRaiseException(RubyException exception) { + RaiseException raiseException = exception.backtrace.getRaiseException(); + if (raiseException != null) { + return raiseException; + } else { + return nil; + } + } + + } + } diff --git a/src/main/java/org/truffleruby/core/exception/ExceptionOperations.java b/src/main/java/org/truffleruby/core/exception/ExceptionOperations.java index 7bcfa6b25908..9efbf436fcf1 100644 --- a/src/main/java/org/truffleruby/core/exception/ExceptionOperations.java +++ b/src/main/java/org/truffleruby/core/exception/ExceptionOperations.java @@ -97,7 +97,7 @@ public static String getMessage(Throwable throwable) { } @TruffleBoundary - private static String messageFieldToString(RubyException exception) { + public static String messageFieldToString(RubyException exception) { Object message = exception.message; RubyStringLibrary strings = RubyStringLibrary.getUncached(); if (message == null || message == Nil.INSTANCE) { @@ -115,7 +115,7 @@ public static String messageToString(RubyException exception) { Object messageObject = null; try { messageObject = DispatchNode.getUncached().call(exception, "message"); - } catch (Throwable e) { + } catch (RaiseException e) { // Fall back to the internal message field } if (messageObject != null && RubyStringLibrary.getUncached().isRubyString(messageObject)) { diff --git a/src/main/java/org/truffleruby/core/exception/RubyException.java b/src/main/java/org/truffleruby/core/exception/RubyException.java index dd044a8ba67b..4c17f08dbfad 100644 --- a/src/main/java/org/truffleruby/core/exception/RubyException.java +++ b/src/main/java/org/truffleruby/core/exception/RubyException.java @@ -11,14 +11,18 @@ import java.util.Set; +import com.oracle.truffle.api.TruffleStackTraceElement; import com.oracle.truffle.api.interop.ExceptionType; import com.oracle.truffle.api.interop.InteropLibrary; +import com.oracle.truffle.api.interop.UnsupportedMessageException; import com.oracle.truffle.api.library.CachedLibrary; import com.oracle.truffle.api.library.ExportLibrary; import com.oracle.truffle.api.library.ExportMessage; import com.oracle.truffle.api.nodes.Node; import org.truffleruby.RubyContext; +import org.truffleruby.RubyLanguage; import org.truffleruby.core.VMPrimitiveNodes.VMRaiseExceptionNode; +import org.truffleruby.core.array.ArrayHelpers; import org.truffleruby.core.array.RubyArray; import org.truffleruby.core.klass.RubyClass; import org.truffleruby.core.proc.RubyProc; @@ -33,6 +37,8 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.object.Shape; +import static org.truffleruby.language.RubyBaseNode.nil; + @ExportLibrary(InteropLibrary.class) public class RubyException extends RubyDynamicObject implements ObjectGraphNode { @@ -96,6 +102,49 @@ public RuntimeException throwException( public ExceptionType getExceptionType() { return ExceptionType.RUNTIME_ERROR; } + + @ExportMessage + public boolean hasExceptionCause() { + return this.cause != nil; + } + + @ExportMessage + public Object getExceptionCause() throws UnsupportedMessageException { + if (!hasExceptionCause()) { + throw UnsupportedMessageException.create(); + } + return this.cause; + } + + @ExportMessage + public boolean hasExceptionMessage() { + return true; + } + + @ExportMessage + public Object getExceptionMessage() { + return ExceptionOperations.messageToString(this); + } + + @ExportMessage + public boolean hasExceptionStackTrace() { + return this.backtrace != null; + } + + @TruffleBoundary + @ExportMessage + public Object getExceptionStackTrace() throws UnsupportedMessageException { + if (!hasExceptionStackTrace()) { + throw UnsupportedMessageException.create(); + } + + TruffleStackTraceElement[] stackTrace = this.backtrace.getStackTrace(); + Object[] items = new Object[stackTrace.length]; + for (int i = 0; i < items.length; i++) { + items[i] = stackTrace[i].getGuestObject(); + } + return ArrayHelpers.createArray(RubyContext.get(null), RubyLanguage.get(null), items); + } // endregion } diff --git a/src/main/java/org/truffleruby/language/control/RaiseException.java b/src/main/java/org/truffleruby/language/control/RaiseException.java index c091ff60144f..d0761e5618bf 100644 --- a/src/main/java/org/truffleruby/language/control/RaiseException.java +++ b/src/main/java/org/truffleruby/language/control/RaiseException.java @@ -15,11 +15,8 @@ import org.truffleruby.RubyContext; import org.truffleruby.core.exception.ExceptionOperations; import org.truffleruby.core.exception.RubyException; -import org.truffleruby.core.module.ModuleFields; import org.truffleruby.language.backtrace.Backtrace; -import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; - /** A ControlFlowException holding a Ruby exception. */ @SuppressWarnings("serial") @ExportLibrary(value = InteropLibrary.class, delegateTo = "exception") @@ -55,11 +52,8 @@ public RubyException getException() { } @Override - @TruffleBoundary public String getMessage() { - final ModuleFields exceptionClass = exception.getLogicalClass().fields; - final String message = ExceptionOperations.messageToString(exception); - return String.format("%s (%s)", message, exceptionClass.getName()); + return ExceptionOperations.messageFieldToString(exception); } } diff --git a/src/main/ruby/truffleruby/core/truffle/exception_operations.rb b/src/main/ruby/truffleruby/core/truffle/exception_operations.rb index 638935ff40e0..d3c9ad1522a6 100644 --- a/src/main/ruby/truffleruby/core/truffle/exception_operations.rb +++ b/src/main/ruby/truffleruby/core/truffle/exception_operations.rb @@ -94,8 +94,14 @@ def self.receiver_string(receiver) def self.message_and_class(exception, highlight) message = StringValue exception.message.to_s + klass = exception.class.to_s + if Primitive.object_kind_of?(exception, Polyglot::ForeignException) and + Truffle::Interop.has_meta_object?(exception) + klass = "#{klass}: #{Truffle::Interop.meta_qualified_name Truffle::Interop.meta_object(exception)}" + end + if highlight - highlighted_class = " (\e[1;4m#{exception.class}\e[m\e[1m)" + highlighted_class = " (\e[1;4m#{klass}\e[m\e[1m)" if message.include?("\n") first = true result = +'' @@ -113,9 +119,9 @@ def self.message_and_class(exception, highlight) end else if i = message.index("\n") - "#{message[0...i]} (#{exception.class})#{message[i..-1]}" + "#{message[0...i]} (#{klass})#{message[i..-1]}" else - "#{message} (#{exception.class})" + "#{message} (#{klass})" end end end @@ -187,24 +193,25 @@ def self.backtrace?(exc) end def self.append_causes(str, err, causes, reverse, highlight) - if !Primitive.nil?(err.cause) && Exception === err.cause && !causes.has_key?(err.cause) - causes[err.cause] = true + cause = err.cause + if !Primitive.nil?(cause) && Exception === cause && !causes.has_key?(cause) + causes[cause] = true if reverse - append_causes(str, err.cause, causes, reverse, highlight) - backtrace_message = backtrace_message(highlight, reverse, err.cause.backtrace, err.cause) + append_causes(str, cause, causes, reverse, highlight) + backtrace_message = backtrace_message(highlight, reverse, cause.backtrace, cause) if backtrace_message.empty? str << message_and_class(err, highlight) else str << backtrace_message end else - backtrace_message = backtrace_message(highlight, reverse, err.cause.backtrace, err.cause) + backtrace_message = backtrace_message(highlight, reverse, cause.backtrace, cause) if backtrace_message.empty? str << message_and_class(err, highlight) else str << backtrace_message end - append_causes(str, err.cause, causes, reverse, highlight) + append_causes(str, cause, causes, reverse, highlight) end end end diff --git a/src/test/java/org/truffleruby/ContextPermissionsTest.java b/src/test/java/org/truffleruby/ContextPermissionsTest.java index e5121077315d..f8e546101013 100644 --- a/src/test/java/org/truffleruby/ContextPermissionsTest.java +++ b/src/test/java/org/truffleruby/ContextPermissionsTest.java @@ -39,13 +39,22 @@ public void testPointerNoNative() throws Throwable { Assert.assertTrue(context.eval("ruby", "defined?(Truffle::FFI::Pointer::NULL).nil?").asBoolean()); RubyTest.assertThrows( () -> context.eval("ruby", "Truffle::FFI::Pointer.allocate"), - e -> assertEquals("native access is not allowed (SecurityError)", e.getMessage())); + e -> { + assertEquals("native access is not allowed", e.getMessage()); + assertEquals("SecurityError", e.getGuestObject().getMetaObject().getMetaQualifiedName()); + }); RubyTest.assertThrows( () -> context.eval("ruby", "Truffle::FFI::Pointer.new(4)"), - e -> assertEquals("native access is not allowed (SecurityError)", e.getMessage())); + e -> { + assertEquals("native access is not allowed", e.getMessage()); + assertEquals("SecurityError", e.getGuestObject().getMetaObject().getMetaQualifiedName()); + }); RubyTest.assertThrows( () -> context.eval("ruby", "Truffle::FFI::MemoryPointer.new(4)"), - e -> assertEquals("native access is not allowed (SecurityError)", e.getMessage())); + e -> { + assertEquals("native access is not allowed", e.getMessage()); + assertEquals("SecurityError", e.getGuestObject().getMetaObject().getMetaQualifiedName()); + }); } } @@ -81,7 +90,10 @@ public void testThreadsNoNative() throws Throwable { RubyTest.assertThrows( () -> context.eval("ruby", "File.stat('.')"), - e -> assertEquals("native access is not allowed (SecurityError)", e.getMessage())); + e -> { + assertEquals("native access is not allowed", e.getMessage()); + assertEquals("SecurityError", e.getGuestObject().getMetaObject().getMetaQualifiedName()); + }); } } @@ -97,7 +109,10 @@ public void testNoThreadsEnforcesSingleThreadedOption() throws Throwable { RubyTest.assertThrows( () -> context.eval("ruby", "Thread.new {}.join"), - e -> assertEquals("threads not allowed in single-threaded mode (SecurityError)", e.getMessage())); + e -> { + assertEquals("threads not allowed in single-threaded mode", e.getMessage()); + assertEquals("SecurityError", e.getGuestObject().getMetaObject().getMetaQualifiedName()); + }); } } diff --git a/src/test/java/org/truffleruby/JSR223InteropTest.java b/src/test/java/org/truffleruby/JSR223InteropTest.java index fd58d0a29f9c..bea535c4fd33 100644 --- a/src/test/java/org/truffleruby/JSR223InteropTest.java +++ b/src/test/java/org/truffleruby/JSR223InteropTest.java @@ -69,7 +69,7 @@ public void testNoPermissionsByDefault() { scriptEngine.eval("Process.pid"); fail("should have thrown"); } catch (ScriptException scriptException) { - assertEquals("org.graalvm.polyglot.PolyglotException: native access is not allowed (SecurityError)", + assertEquals("org.graalvm.polyglot.PolyglotException: native access is not allowed", scriptException.getMessage()); } } diff --git a/src/test/java/org/truffleruby/PolyglotInteropTest.java b/src/test/java/org/truffleruby/PolyglotInteropTest.java index 147fb2bf81e2..1d947958f1de 100644 --- a/src/test/java/org/truffleruby/PolyglotInteropTest.java +++ b/src/test/java/org/truffleruby/PolyglotInteropTest.java @@ -200,7 +200,7 @@ public void testTopScopes() { // create a method to test search order context.eval("ruby", "def forty_two; :method; end"); assertEquals("method", context.eval(interactiveSource("forty_two")).asString()); - assertEquals("Method", bindings.getMember("forty_two").getMetaObject().getMetaSimpleName()); + assertEquals("Method", bindings.getMember("forty_two").getMetaObject().getMetaQualifiedName()); bindings.putMember("forty_two", 42); assertEquals(42, bindings.getMember("forty_two").asInt()); @@ -211,7 +211,7 @@ public void testTopScopes() { bindings.putMember("local_var", 42); RubyTest.assertThrows( () -> context.eval("ruby", "local_var"), // non-interactive source - e -> assertEquals("NameError", e.getGuestObject().getMetaObject().getMetaSimpleName())); + e -> assertEquals("NameError", e.getGuestObject().getMetaObject().getMetaQualifiedName())); context.eval(interactiveSource("new_eval_local_var = 88")); assertEquals(88, context.eval(interactiveSource("new_eval_local_var")).asInt()); From e7816b0ecb49520fcdcc54e4022e365921125aaa Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Fri, 30 Sep 2022 14:33:12 +0000 Subject: [PATCH 09/11] [GR-17457] Fix initializeThread() when called on a different thread than the passed thread PullRequest: truffleruby/3501 (cherry picked from commit c4730d70978d86c654944611279cd2b4f9c32d57) --- .../java/org/truffleruby/RubyLanguage.java | 28 +++++++++++-------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/src/main/java/org/truffleruby/RubyLanguage.java b/src/main/java/org/truffleruby/RubyLanguage.java index 7ecdba40920a..cbf5e5b6a7f8 100644 --- a/src/main/java/org/truffleruby/RubyLanguage.java +++ b/src/main/java/org/truffleruby/RubyLanguage.java @@ -436,7 +436,7 @@ public RubyContext createContext(Env env) { } setRubyHomeTruffleFile(env, newHome); - LOGGER.fine("createContext()"); + LOGGER.fine("createContext() on " + Thread.currentThread()); Metrics.printTime("before-create-context"); final RubyContext context = new RubyContext(this, env); Metrics.printTime("after-create-context"); @@ -450,7 +450,7 @@ public RubyContext createContext(Env env) { @Override protected void initializeContext(RubyContext context) { - LOGGER.fine("initializeContext()"); + LOGGER.fine("initializeContext() on " + Thread.currentThread()); try { Metrics.printTime("before-initialize-context"); @@ -479,7 +479,7 @@ protected boolean patchContext(RubyContext context, Env newEnv) { // We need to initialize the Metrics class of the language classloader Metrics.initializeOption(); - LOGGER.fine("patchContext()"); + LOGGER.fine("patchContext() on " + Thread.currentThread() + ")"); Metrics.printTime("before-patch-context"); final LanguageOptions oldOptions = Objects.requireNonNull(this.options); final LanguageOptions newOptions = new LanguageOptions(newEnv, newEnv.getOptions(), singleContext); @@ -496,13 +496,13 @@ protected boolean patchContext(RubyContext context, Env newEnv) { @Override protected void finalizeContext(RubyContext context) { - LOGGER.fine("finalizeContext()"); + LOGGER.fine("finalizeContext() on " + Thread.currentThread()); context.finalizeContext(); } @Override protected void disposeContext(RubyContext context) { - LOGGER.fine("disposeContext()"); + LOGGER.fine("disposeContext() on " + Thread.currentThread()); context.disposeContext(); if (options.COVERAGE_GLOBAL) { @@ -575,7 +575,7 @@ protected boolean isThreadAccessAllowed(Thread thread, boolean singleThreaded) { @Override public void initializeThread(RubyContext context, Thread thread) { - LOGGER.fine(() -> "initializeThread(#" + getThreadId(thread) + " " + thread + ")"); + LOGGER.fine(() -> "initializeThread(" + showThread(thread) + ") on " + Thread.currentThread()); if (thread == context.getThreadManager().getOrInitializeRootJavaThread()) { // Already initialized when creating the context @@ -583,7 +583,7 @@ public void initializeThread(RubyContext context, Thread thread) { } if (context.getThreadManager().isRubyManagedThread(thread)) { - final RubyThread rubyThread = getCurrentThread(); + final RubyThread rubyThread = this.rubyThread.get(thread); if (rubyThread.thread == thread) { // new Ruby Thread if (thread != Thread.currentThread()) { throw CompilerDirectives @@ -596,13 +596,13 @@ public void initializeThread(RubyContext context, Thread thread) { return; } - final RubyThread foreignThread = getCurrentThread(); + final RubyThread foreignThread = this.rubyThread.get(thread); context.getThreadManager().startForeignThread(foreignThread, thread); } @Override public void disposeThread(RubyContext context, Thread thread) { - LOGGER.fine(() -> "disposeThread(#" + getThreadId(thread) + " " + thread + " on " + getCurrentThread() + ")"); + LOGGER.fine(() -> "disposeThread(" + showThread(thread) + ") on " + Thread.currentThread()); if (thread == context.getThreadManager().getRootJavaThread()) { if (context.getEnv().isPreInitialization()) { @@ -621,7 +621,7 @@ public void disposeThread(RubyContext context, Thread thread) { } if (context.getThreadManager().isRubyManagedThread(thread)) { - final RubyThread rubyThread = getCurrentThread(); + final RubyThread rubyThread = this.rubyThread.get(thread); if (rubyThread.thread == thread) { // Thread if (thread != Thread.currentThread()) { throw CompilerDirectives.shouldNotReachHere("Ruby threads should be disposed on their Java thread"); @@ -634,8 +634,12 @@ public void disposeThread(RubyContext context, Thread thread) { } // A foreign Thread, its Fibers are considered isRubyManagedThread() - final RubyThread rubyThread = this.rubyThread.get(thread); - context.getThreadManager().cleanup(rubyThread, thread); + final RubyThread foreignThread = this.rubyThread.get(thread); + context.getThreadManager().cleanup(foreignThread, thread); + } + + private String showThread(Thread thread) { + return "#" + getThreadId(thread) + " " + thread + " = " + this.rubyThread.get(thread); } @Override From ccc0590df489022f202b286a4ae6d4d6b8e34ca8 Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Sat, 10 Sep 2022 13:03:04 +0200 Subject: [PATCH 10/11] Skip transient tests (cherry picked from commit 08dd8d8c3bb7798f52a521e20dd117615ed9239a) --- test/mri/excludes/FTPTest.rb | 1 + test/mri/excludes/TestSleep.rb | 1 + 2 files changed, 2 insertions(+) create mode 100644 test/mri/excludes/TestSleep.rb diff --git a/test/mri/excludes/FTPTest.rb b/test/mri/excludes/FTPTest.rb index b636b6c0207b..398126d35e3a 100644 --- a/test/mri/excludes/FTPTest.rb +++ b/test/mri/excludes/FTPTest.rb @@ -2,3 +2,4 @@ exclude :test_getbinaryfile_command_injection, "needs investigation" exclude :test_putbinaryfile_command_injection, "hangs" exclude :test_list_read_timeout_exceeded, "transient" +exclude :test_read_timeout_exceeded, "transient" diff --git a/test/mri/excludes/TestSleep.rb b/test/mri/excludes/TestSleep.rb new file mode 100644 index 000000000000..9f05dc556975 --- /dev/null +++ b/test/mri/excludes/TestSleep.rb @@ -0,0 +1 @@ +exclude :test_sleep_5sec, "transient" From 9a4bf72c60013b053ba878b8ed09625fa431ce00 Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Mon, 3 Oct 2022 15:09:51 +0200 Subject: [PATCH 11/11] Update to graal's release branch --- common.json | 32 ++++++++++++++++---------------- mx.truffleruby/suite.py | 4 ++-- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/common.json b/common.json index 0640fe997bce..30366b4ffef5 100644 --- a/common.json +++ b/common.json @@ -4,26 +4,26 @@ "jdks": { "openjdk11": {"name": "jpg-jdk", "version": "11.0.11", "build_id": "9", "open": true, "release": true, "platformspecific": true }, "oraclejdk11": {"name": "jpg-jdk", "version": "11.0.11", "build_id": "9", "release": true, "platformspecific": true, "extrabundles": ["static-libs"] }, - "labsjdk-ce-11": {"name": "labsjdk", "version": "ce-11.0.17+5-jvmci-22.3-b05", "platformspecific": true }, - "labsjdk-ce-11-llvm": {"name": "labsjdk", "version": "ce-11.0.17+5-jvmci-22.3-b05-sulong", "platformspecific": true }, - "labsjdk-ee-11": {"name": "labsjdk", "version": "ee-11.0.17+9-jvmci-22.3-b05", "platformspecific": true }, - "labsjdk-ee-11-llvm": {"name": "labsjdk", "version": "ee-11.0.17+9-jvmci-22.3-b05-sulong", "platformspecific": true }, + "labsjdk-ce-11": {"name": "labsjdk", "version": "ce-11.0.17+5-jvmci-22.3-b06", "platformspecific": true }, + "labsjdk-ce-11-llvm": {"name": "labsjdk", "version": "ce-11.0.17+5-jvmci-22.3-b06-sulong", "platformspecific": true }, + "labsjdk-ee-11": {"name": "labsjdk", "version": "ee-11.0.17+10-jvmci-22.3-b06", "platformspecific": true }, + "labsjdk-ee-11-llvm": {"name": "labsjdk", "version": "ee-11.0.17+10-jvmci-22.3-b06-sulong", "platformspecific": true }, "oraclejdk17": {"name": "jpg-jdk", "version": "17.0.1", "build_id": "12", "release": true, "platformspecific": true, "extrabundles": ["static-libs"]}, - "labsjdk-ce-17": {"name": "labsjdk", "version": "ce-17.0.5+5-jvmci-22.3-b05", "platformspecific": true }, - "labsjdk-ce-17Debug": {"name": "labsjdk", "version": "ce-17.0.5+5-jvmci-22.3-b05-debug", "platformspecific": true }, - "labsjdk-ce-17-llvm": {"name": "labsjdk", "version": "ce-17.0.5+5-jvmci-22.3-b05-sulong", "platformspecific": true }, - "labsjdk-ee-17": {"name": "labsjdk", "version": "ee-17.0.5+8-jvmci-22.3-b05", "platformspecific": true }, - "labsjdk-ee-17Debug": {"name": "labsjdk", "version": "ee-17.0.5+8-jvmci-22.3-b05-debug", "platformspecific": true }, - "labsjdk-ee-17-llvm": {"name": "labsjdk", "version": "ee-17.0.5+8-jvmci-22.3-b05-sulong", "platformspecific": true }, + "labsjdk-ce-17": {"name": "labsjdk", "version": "ce-17.0.5+5-jvmci-22.3-b06", "platformspecific": true }, + "labsjdk-ce-17Debug": {"name": "labsjdk", "version": "ce-17.0.5+5-jvmci-22.3-b06-debug", "platformspecific": true }, + "labsjdk-ce-17-llvm": {"name": "labsjdk", "version": "ce-17.0.5+5-jvmci-22.3-b06-sulong", "platformspecific": true }, + "labsjdk-ee-17": {"name": "labsjdk", "version": "ee-17.0.5+9-jvmci-22.3-b06", "platformspecific": true }, + "labsjdk-ee-17Debug": {"name": "labsjdk", "version": "ee-17.0.5+9-jvmci-22.3-b06-debug", "platformspecific": true }, + "labsjdk-ee-17-llvm": {"name": "labsjdk", "version": "ee-17.0.5+9-jvmci-22.3-b06-sulong", "platformspecific": true }, "oraclejdk19": {"name": "jpg-jdk", "version": "19", "build_id": "26", "release": true, "platformspecific": true, "extrabundles": ["static-libs"]}, - "labsjdk-ce-19": {"name": "labsjdk", "version": "ce-19+36-jvmci-22.3-b05", "platformspecific": true }, - "labsjdk-ce-19Debug": {"name": "labsjdk", "version": "ce-19+36-jvmci-22.3-b05-debug", "platformspecific": true }, - "labsjdk-ce-19-llvm": {"name": "labsjdk", "version": "ce-19+36-jvmci-22.3-b05-sulong", "platformspecific": true }, - "labsjdk-ee-19": {"name": "labsjdk", "version": "ee-19.0.1+9-jvmci-22.3-b05", "platformspecific": true }, - "labsjdk-ee-19Debug": {"name": "labsjdk", "version": "ee-19.0.1+9-jvmci-22.3-b05-debug", "platformspecific": true }, - "labsjdk-ee-19-llvm": {"name": "labsjdk", "version": "ee-19.0.1+9-jvmci-22.3-b05-sulong", "platformspecific": true } + "labsjdk-ce-19": {"name": "labsjdk", "version": "ce-19+36-jvmci-22.3-b06", "platformspecific": true }, + "labsjdk-ce-19Debug": {"name": "labsjdk", "version": "ce-19+36-jvmci-22.3-b06-debug", "platformspecific": true }, + "labsjdk-ce-19-llvm": {"name": "labsjdk", "version": "ce-19+36-jvmci-22.3-b06-sulong", "platformspecific": true }, + "labsjdk-ee-19": {"name": "labsjdk", "version": "ee-19.0.1+10-jvmci-22.3-b06", "platformspecific": true }, + "labsjdk-ee-19Debug": {"name": "labsjdk", "version": "ee-19.0.1+10-jvmci-22.3-b06--debug", "platformspecific": true }, + "labsjdk-ee-19-llvm": {"name": "labsjdk", "version": "ee-19.0.1+10-jvmci-22.3-b06-sulong", "platformspecific": true } }, "COMMENT.devkits" : "The devkits versions reflect those used to build the JVMCI JDKs (e.g., see devkit_platform_revisions in /make/conf/jib-profiles.js)", diff --git a/mx.truffleruby/suite.py b/mx.truffleruby/suite.py index 7cf026248b6a..4c6b6833bb76 100644 --- a/mx.truffleruby/suite.py +++ b/mx.truffleruby/suite.py @@ -7,7 +7,7 @@ { "name": "regex", "subdir": True, - "version": "46235c96a132a7bb776a396fc26e31f5b5852c8c", + "version": "552ddc7f51fdf520ef68c876a27f8c62a4f65a1e", "urls": [ {"url": "https://github.com/oracle/graal.git", "kind": "git"}, {"url": "https://curio.ssw.jku.at/nexus/content/repositories/snapshots", "kind": "binary"}, @@ -16,7 +16,7 @@ { "name": "sulong", "subdir": True, - "version": "46235c96a132a7bb776a396fc26e31f5b5852c8c", + "version": "552ddc7f51fdf520ef68c876a27f8c62a4f65a1e", "urls": [ {"url": "https://github.com/oracle/graal.git", "kind": "git"}, {"url": "https://curio.ssw.jku.at/nexus/content/repositories/snapshots", "kind": "binary"},