Content-Length: 548697 | pFad | http://github.com/jruby/jruby/commit/#start-of-content

68725548 Merge pull request #8908 from headius/inspect_hash_keys · jruby/jruby@a80335b · GitHub
Skip to content

Commit a80335b

Browse files
authored
Merge pull request #8908 from headius/inspect_hash_keys
Update Hash#inspect logic for multibyte keys
2 parents 25dc482 + 2ca2938 commit a80335b

File tree

4 files changed

+80
-16
lines changed

4 files changed

+80
-16
lines changed

core/src/main/java/org/jruby/RubyHash.java

Lines changed: 55 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -950,20 +950,66 @@ protected RubyString inspectHash(final ThreadContext context) {
950950

951951
private static final VisitorWithState<RubyString> InspectVisitor = new VisitorWithState<RubyString>() {
952952
@Override
953+
// MRI: inspect_i for inspect_hash in hash.c
953954
public void visit(ThreadContext context, RubyHash self, IRubyObject key, IRubyObject value, int index, RubyString str) {
954-
RubyString keyStr = inspect(context, key);
955-
RubyString valStr = inspect(context, value);
955+
RubyString keyStr;
956956

957-
final ByteList bytes = str.getByteList();
958-
bytes.ensure(2 + keyStr.size() + 2 + valStr.size());
957+
boolean isSymbol = key instanceof RubySymbol;
958+
boolean quote = false;
959+
if (key instanceof RubySymbol symbol) {
960+
keyStr = symbol.fstring();
961+
quote = symbolKeyNeedsQuote(context, symbol);
962+
} else {
963+
keyStr = inspect(context, key);
964+
}
965+
966+
if (str.size() > 1) {
967+
str.catString(", ");
968+
} else {
969+
str.setEncoding(keyStr.getEncoding());
970+
}
959971

960-
if (index > 0) bytes.append((byte) ',').append((byte) ' ');
972+
if (quote) {
973+
str.append(keyStr.inspect(context));
974+
} else {
975+
str.append(keyStr);
976+
}
961977

962-
boolean keyIsSymbol = key instanceof RubySymbol;
978+
str.catString(isSymbol ? ": " : " => ");
963979

964-
str.catWithCodeRange(keyIsSymbol ? (RubyString) keyStr.substr(context, 1, keyStr.length() - 1) : keyStr);
965-
self.appendAssociation(keyIsSymbol, bytes);
966-
str.catWithCodeRange(valStr);
980+
RubyString valStr = inspect(context, value);
981+
str.append(valStr);
982+
}
983+
984+
// MRI: symbol_key_needs_quote
985+
boolean symbolKeyNeedsQuote(ThreadContext context, RubySymbol sym) {
986+
RubyString str = sym.fstring();
987+
int len = str.size();
988+
if (len == 0 || !sym.isSimpleName(context)) return true;
989+
ByteList bytes = str.getByteList();
990+
int first = bytes.get(0);
991+
if (first == '@' || first == '$' || first == '!') return true;
992+
if (!RubyString.atCharBoundary(bytes.unsafeBytes(), bytes.begin(), bytes.begin() + bytes.getRealSize() - 1, bytes.begin() + bytes.realSize(), bytes.getEncoding())) return false;
993+
switch (bytes.get(len - 1)) {
994+
case '+':
995+
case '-':
996+
case '*':
997+
case '/':
998+
case '`':
999+
case '%':
1000+
case '^':
1001+
case '&':
1002+
case '|':
1003+
case ']':
1004+
case '<':
1005+
case '=':
1006+
case '>':
1007+
case '~':
1008+
case '@':
1009+
return true;
1010+
default:
1011+
return false;
1012+
}
9671013
}
9681014
};
9691015

core/src/main/java/org/jruby/RubyString.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4178,7 +4178,7 @@ private void ensureBytePosition(ThreadContext context, int pos) {
41784178
}
41794179

41804180
// MRI: at_char_boundary
4181-
private static boolean atCharBoundary(byte[] bytes, int s, int p, int e, Encoding enc) {
4181+
public static boolean atCharBoundary(byte[] bytes, int s, int p, int e, Encoding enc) {
41824182
// our version checks if p == bytes.length, where CRuby would have a \0 character and not reposition
41834183
return p == bytes.length || enc.leftAdjustCharHead(bytes, s, p, e) == p;
41844184
}

core/src/main/java/org/jruby/RubySymbol.java

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -485,14 +485,9 @@ final RubyString inspect(final Ruby runtime) {
485485

486486
@JRubyMethod(name = "inspect")
487487
public IRubyObject inspect(ThreadContext context) {
488-
// TODO: 1.9 rb_enc_symname_p
489-
Encoding resenc = context.runtime.getDefaultInternalEncoding();
490-
if (resenc == null) resenc = context.runtime.getDefaultExternalEncoding();
491-
492488
RubyString str = newString(context, getBytes());
493489

494-
if (!(isPrintable(context) && (resenc.equals(getBytes().getEncoding()) || str.isAsciiOnly()) &&
495-
isSymbolName(symbol))) {
490+
if (!isSimpleName(context)) {
496491
str = (RubyString) str.inspect(context);
497492
}
498493

@@ -504,6 +499,24 @@ public IRubyObject inspect(ThreadContext context) {
504499
return newString(context, result);
505500
}
506501

502+
// MRI: rb_str_symname_p
503+
public boolean isSimpleName(ThreadContext context) {
504+
int len;
505+
Ruby runtime = context.runtime;
506+
Encoding resenc = runtime.getDefaultInternalEncoding();
507+
if (resenc == null) resenc = runtime.getDefaultExternalEncoding();
508+
509+
RubyString str = fstring;
510+
ByteList bytes = fstring.getByteList();
511+
Encoding enc = bytes.getEncoding();
512+
513+
if ((resenc != enc && !str.isAsciiOnly()) /*|| len != (long)strlen(ptr)*/ ||
514+
!isSymbolName(symbol) || !isPrintable(context)) {
515+
return false;
516+
}
517+
return true;
518+
}
519+
507520
@Override
508521
@JRubyMethod(name = "to_s")
509522
public IRubyObject to_s(ThreadContext context) {
@@ -874,6 +887,7 @@ private boolean isPrintable(ThreadContext context) {
874887
return true;
875888
}
876889

890+
// MRI: rb_enc_symname_p, roughly
877891
private static boolean isSymbolName(final String str) {
878892
if (str == null || str.isEmpty()) return false;
879893

spec/ruby/core/hash/shared/to_s.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,10 @@
8585
{ true => false }.to_s.should == expected
8686
end
8787

88+
it "leaves printable multi-byte symbolic keys unescaped" do
89+
{"\u3042": 1}.inspect.should == "{あ: 1}"
90+
end
91+
8892
ruby_version_is "3.4" do
8993
it "adds quotes to symbol keys that are not valid symbol literals" do
9094
{ "needs-quotes": 1 }.send(@method).should == '{"needs-quotes": 1}'

0 commit comments

Comments
 (0)








ApplySandwichStrip

pFad - (p)hone/(F)rame/(a)nonymizer/(d)eclutterfier!      Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

Fetched URL: http://github.com/jruby/jruby/commit/#start-of-content

Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy