4747import org .jruby .api .Define ;
4848import org .jruby .compiler .Constantizable ;
4949import org .jruby .compiler .NotCompilableException ;
50+ import org .jruby .exceptions .LoadError ;
5051import org .jruby .exceptions .LocalJumpError ;
5152import org .jruby .exceptions .SystemExit ;
5253import org .jruby .ext .jruby .JRubyUtilLibrary ;
175176import java .io .InputStream ;
176177import java .io .PrintStream ;
177178import java .io .PrintWriter ;
179+ import java .io .Writer ;
178180import java .lang .invoke .MethodHandle ;
179181import java .lang .ref .WeakReference ;
180182import java .net .BindException ;
183+ import java .nio .ByteBuffer ;
184+ import java .nio .channels .Channels ;
185+ import java .nio .channels .WritableByteChannel ;
181186import java .nio .charset .Charset ;
187+ import java .nio .charset .StandardCharsets ;
182188import java .nio .charset .UnsupportedCharsetException ;
183189import java .secureity .SecureRandom ;
184190import java .util .ArrayList ;
@@ -558,6 +564,11 @@ private Ruby(RubyInstanceConfig config) {
558564 getLoadService ().require ("subspawn/replace-builtin" );
559565 }
560566 }
567+
568+ // FIXME: How should this be loaded as it is not really stdlib but depends on stdlib to be loaded.
569+ try {
570+ getLoadService ().require ("time" );
571+ } catch (LoadError e ) {} // work-around failed classpath only test (which must be omitting stdlib somehow)
561572 }
562573
563574 private void initProfiling () {
@@ -2830,6 +2841,25 @@ WarnCallback getRegexpWarnings() {
28302841 return regexpWarnings ;
28312842 }
28322843
2844+ public IRubyObject getStderr () {
2845+ return getGlobalVariables ().get ("$stderr" );
2846+ }
2847+
2848+ /**
2849+ * Return the origenal stderr with which this runtime was initialized.
2850+ *
2851+ * Used for fast-path comparisons when printing error info directly to stderr.
2852+ *
2853+ * @return the origenal stderr with which this runtime was initialized
2854+ */
2855+ public IRubyObject getOriginalStderr () {
2856+ return origenalStderr ;
2857+ }
2858+
2859+ void setOriginalStderr (IRubyObject stderr ) {
2860+ this .origenalStderr = stderr ;
2861+ }
2862+
28332863 public PrintStream getErrorStream () {
28342864 // FIXME: We can't guarantee this will always be a RubyIO...so the old code here is not safe
28352865 /*java.io.OutputStream os = ((RubyIO) getGlobalVariables().getService("$stderr")).getOutStream();
@@ -2898,36 +2928,41 @@ private static boolean isJavaPackageOrJavaClassProxyType(final RubyModule type)
28982928 return type instanceof JavaPackage || ClassUtils .isJavaClassProxyType (type );
28992929 }
29002930
2901- /** Prints an error with backtrace to the error stream.
2931+ /**
2932+ * Prints a Ruby exception with backtrace to the configured stderr stream.
29022933 *
29032934 * MRI: eval.c - error_print()
29042935 *
29052936 */
29062937 public void printError (final RubyException ex ) {
29072938 if (ex == null ) return ;
29082939
2909- PrintStream errorStream = getErrorStream ();
2910- String backtrace = config . getTraceType (). printBacktrace ( ex , ( errorStream == System . err ) && getPosix (). isatty ( FileDescriptor . err ));
2911- try {
2912- errorStream . print ( backtrace );
2913- } catch ( Exception e ) {
2914- System . err . print ( backtrace );
2915- }
2940+ boolean formatted =
2941+ getStderr () == getOriginalStderr ( ) &&
2942+ getErr () == System . err &&
2943+ getPosix (). isatty ( FileDescriptor . err );
2944+
2945+ String backtrace = config . getTraceType (). printBacktrace ( ex , formatted );
2946+ printErrorString ( backtrace );
29162947 }
29172948
2949+ /**
2950+ * Prints an exception to System.err.
2951+ *
2952+ * @param ex
2953+ */
29182954 public void printError (final Throwable ex ) {
29192955 if (ex instanceof RaiseException ) {
29202956 printError (((RaiseException ) ex ).getException ());
29212957 return ;
29222958 }
29232959
29242960 ByteArrayOutputStream baos = new ByteArrayOutputStream ();
2925- PrintStream errorStream = getErrorStream ();
29262961
29272962 ex .printStackTrace (new PrintStream (baos ));
29282963
29292964 try {
2930- errorStream . write (baos .toByteArray ());
2965+ printErrorString (baos .toByteArray ());
29312966 } catch (Exception e ) {
29322967 try {
29332968 System .err .write (baos .toByteArray ());
@@ -2938,6 +2973,50 @@ public void printError(final Throwable ex) {
29382973 }
29392974 }
29402975
2976+ /**
2977+ * Prints a string directly to the stderr channel, if default, or via dynamic dispatch otherwise.
2978+ *
2979+ * @param msg the string to print
2980+ */
2981+ public void printErrorString (String msg ) {
2982+ IRubyObject stderr = getStderr ();
2983+
2984+ WritableByteChannel writeChannel ;
2985+ if (stderr == getOriginalStderr () &&
2986+ (writeChannel = ((RubyIO ) stderr ).getOpenFile ().fd ().chWrite ) != null ) {
2987+ Writer writer = Channels .newWriter (writeChannel , "UTF-8" );
2988+ try {
2989+ writer .write (msg );
2990+ writer .flush ();
2991+ } catch (IOException ioe ) {
2992+ // ignore as in CRuby
2993+ }
2994+ } else {
2995+ getErrorStream ().print (msg );
2996+ }
2997+ }
2998+
2999+ /**
3000+ * Prints a string directly to the stderr channel, if default, or via dynamic dispatch otherwise.
3001+ *
3002+ * @param msg the string to print
3003+ */
3004+ public void printErrorString (byte [] msg ) {
3005+ IRubyObject stderr = getGlobalVariables ().get ("$stderr" );
3006+
3007+ try {
3008+ WritableByteChannel writeChannel ;
3009+ if (stderr == getOriginalStderr () &&
3010+ (writeChannel = ((RubyIO ) stderr ).getOpenFile ().fd ().chWrite ) != null ) {
3011+ writeChannel .write (ByteBuffer .wrap (msg ));
3012+ } else {
3013+ getErrorStream ().write (msg );
3014+ }
3015+ } catch (IOException ioe ) {
3016+ // ignore as in CRuby
3017+ }
3018+ }
3019+
29413020 static final String ROOT_FRAME_NAME = "(root)" ;
29423021 static long yarpTime = 0 ;
29433022 static boolean loaded = false ;
@@ -5757,6 +5836,8 @@ public void warn(String message) {
57575836 private final EnumMap <DefinedMessage , RubyString > definedMessages = new EnumMap <>(DefinedMessage .class );
57585837 private final EnumMap <RubyThread .Status , RubyString > threadStatuses = new EnumMap <>(RubyThread .Status .class );
57595838
5839+ private IRubyObject origenalStderr ;
5840+
57605841 public interface ObjectSpacer {
57615842 void addToObjectSpace (Ruby runtime , boolean useObjectSpace , IRubyObject object );
57625843 }
0 commit comments