Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

On eval of Time#strftime read from file: Java::JavaLang::Error: Unknown special char: Y #5179

Closed
Confusion opened this issue May 20, 2018 · 6 comments

Comments

@Confusion
Copy link

Confusion commented May 20, 2018

Environment

Current JRuby head on Ubuntu 16.04

$ bin/jruby -v
jruby 9.2.0.0-SNAPSHOT (2.5.0) 2018-05-19 7845e58 OpenJDK 64-Bit Server VM 25.171-b11 on 1.8.0_171-8u171-b11-0ubuntu0.16.04.1-b11 +jit [linux-x86_64]

$ bin/jruby -S bundle -v
Bundler version 1.16.2

$ uname -a
Linux photon 4.4.0-122-generic #146-Ubuntu SMP Mon Apr 23 15:34:04 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux

Gemfile:

gem 'foo', path: '.'

foo.gemspec:

Time.now.strftime('%Y-%m-%d')

Expected Behavior

This is a reduced case from an original setup that works correctly using JRuby 9.1.17.0, which I wanted to test using current JRuby head. Therefore the expected behavior as shown by MRI may seems silly, but what matters is that the JRuby output differs. MRI 2.5.0 with bundler 1.16.2 returns:

$ ruby -S bundle
[..]
NoMethodError: undefined method `loaded_from=' for "2018-05-20":String

because the gemspec does not contain a valid Gem::Specification, but returns a String. This is also what JRuby 9.1.17.0 returns.

Actual Behavior

$ bin/jruby -S bundle
 [ .. snip bundler error reporting template .. ]

Java::JavaLang::Error: Unknown special char: Y
  org.jruby.util.RubyDateFormatter.compilePattern(RubyDateFormatter.java:343)
  org.jruby.util.RubyDateFormatter.compilePattern(RubyDateFormatter.java:259)
  org.jruby.util.RubyDateFormatter.compileAndFormat(RubyDateFormatter.java:373)
  org.jruby.RubyTime.strftime(RubyTime.java:569)
  [..]
  org.jruby.Ruby.runScript(Ruby.java:852)
  org.jruby.Ruby.runNormally(Ruby.java:771)
  org.jruby.Ruby.runNormally(Ruby.java:789)
  org.jruby.Ruby.runFromMain(Ruby.java:601)
  org.jruby.Main.doRunFromMain(Main.java:415)
  org.jruby.Main.internalRun(Main.java:307)
  org.jruby.Main.run(Main.java:234)
  org.jruby.Main.main(Main.java:206)

 [ .. snip more of the bundler error reporting template .. ]

If you run with the debugger enabled, add a debugger statement on the first line of the foo.gemspec and manually execute the command, then you see it actually returns the correct answer (the current date in ISO8601 format) and subsequently the output becomes the same as the MRI output. But immediately continuing execution in the debugger session causes the original error.

@Confusion
Copy link
Author

Confusion commented May 20, 2018

Shorter reproduction:

$ bin/jruby -e "Gem.activate_bin_path('bundler', 'bundle', '1.16.2'); \
                require 'bundler'; \
                Bundler.load_gemspec_uncached('/tmp/foo.gemspec')"

No Gemfile needed.

The next reproduction, excluding more code, would be

$ bin/jruby -e "Gem.activate_bin_path('bundler', 'bundle', '1.16.2'); \
               require 'bundler'; \
               Bundler::SharedHelpers.chdir('/tmp') { \
                 Bundler.send(:eval_gemspec, \
                                       Pathname.new('/tmp/foo.gemspec'), \
                                       %(Time.now.strftime('%Y-%m-%d')\n))}"

but that doesn't reproduce the issue anymore.

@kares
Copy link
Member

kares commented May 21, 2018

wanted to look into this but (yet) seems Bundler 1.16.2 specific ... which must be a recent release 😞

bin/jruby -e "Gem.activate_bin_path('bundler', 'bundle', '1.16.1'); \
                require 'bundler'; \
                Bundler.load_gemspec_uncached('./foo.gemspec')"

NoMethodError: undefined method `loaded_from=' for "2018-05-21":String
  load_gemspec_uncached at /home/kares/workspace/oss/jruby/lib/ruby/gems/shared/gems/bundler-1.16.1/lib/bundler.rb:441
                 <main> at -e:1

@Confusion
Copy link
Author

Confusion commented May 21, 2018

Problem turns out to be independent of bundler. Smaller reproduction:

bin/jruby -e "eval File.read('/tmp/foo.rb', mode: 'rb')"

where foo.rb contains the aforementioned Time.now.strftime('%Y-%m-%d'). Replacing 'rb' by 'r' makes the error go away. Replacing 'rb' by 'r:ASCII-8BIT' reproduces the error. Replacing 'rb' by 'r:UTF-8' reproduces the error. Removing the eval makes the error go away.

Passes on JRuby 9.1.17.0.

@Confusion Confusion changed the title On Time#strftime in gemspec: Java::JavaLang::Error: Unknown special char: Y On eval of Time#strftime read from file: Java::JavaLang::Error: Unknown special char: Y May 21, 2018
@enebo
Copy link
Member

enebo commented May 21, 2018

A quick glance and some notes. default will be UTF-8 for eval and that works and so does even more uncommon encodings which are ascii compatible like EUC-JP. If I force_encoding of the format string I can see that we also work. So ASCII-BINARY strftime format string is our problem. Reduced more:

Time.now.strftime('%Y-%m-%d'.force_encoding('ASCII-8BIT'))

@headius
Copy link
Member

headius commented May 21, 2018

The following patch fixes it. Without calling into this line, the Format class never initializes properly, which leaves the conversion token mapping array unpopulated, so all tokens come out null.

This isn't the right fix; the Format enumeration needs to be allowed to initialize properly before the parsing proceeds. It doesn't without this code because all subsequent logic goes directly for the conversion array, which doesn't trigger the class initialization.

diff --git a/core/src/main/java/org/jruby/util/RubyDateFormatter.java b/core/src/main/java/org/jruby/util/RubyDateFormatter.java
index fb9618e086..dc1193dee0 100644
--- a/core/src/main/java/org/jruby/util/RubyDateFormatter.java
+++ b/core/src/main/java/org/jruby/util/RubyDateFormatter.java
@@ -266,9 +266,9 @@ public class RubyDateFormatter {
         }
 
         final List<Token> compiledPattern = new LinkedList<Token>();
-        if (enc != ASCIIEncoding.INSTANCE) { // default for ByteList
+//        if (enc != ASCIIEncoding.INSTANCE) { // default for ByteList
             compiledPattern.add(new Token(Format.FORMAT_ENCODING, enc));
-        }
+//        }
 
         ByteArrayInputStream in = new ByteArrayInputStream(pattern.getUnsafeBytes(), pattern.getBegin(), pattern.getRealSize());
         Reader reader = new InputStreamReader(in, runtime.getEncodingService().charsetForEncoding(pattern.getEncoding()));

@headius
Copy link
Member

headius commented May 21, 2018

The bug was introduced at 7b8221e by commenting out an access of one of the Format enum values. This forced the class, and the associated conversion array, to be initialized.

I have a more complete fix in process.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants