Skip to content

Commit

Permalink
Showing 81 changed files with 3,063 additions and 1,212 deletions.
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
9.0.5.0
9.1.0.0-SNAPSHOT
2 changes: 1 addition & 1 deletion bin/jruby.bash
Original file line number Diff line number Diff line change
@@ -383,7 +383,7 @@ if [ "$VERIFY_JRUBY" != "" ]; then
"-Djruby.home=$JRUBY_HOME" \
"-Djruby.lib=$JRUBY_HOME/lib" -Djruby.script=jruby \
"-Djruby.shell=$JRUBY_SHELL" \
$java_class $JRUBY_OPTS "$@"
$java_class $mode "$@"

# Record the exit status immediately, or it will be overridden.
JRUBY_STATUS=$?
2 changes: 1 addition & 1 deletion core/pom.xml
Original file line number Diff line number Diff line change
@@ -12,7 +12,7 @@ DO NOT MODIFIY - GENERATED CODE
<parent>
<groupId>org.jruby</groupId>
<artifactId>jruby-parent</artifactId>
<version>9.0.5.0</version>
<version>9.1.0.0-SNAPSHOT</version>
</parent>
<artifactId>jruby-core</artifactId>
<name>JRuby Core</name>
6 changes: 3 additions & 3 deletions core/src/main/java/org/jruby/util/StringSupport.java
Original file line number Diff line number Diff line change
@@ -319,7 +319,7 @@ public static long strLengthWithCodeRange(Encoding enc, byte[]bytes, int p, int
}
}

private static long strLengthWithCodeRangeAsciiCompatible(Encoding enc, byte[]bytes, int p, int end) {
public static long strLengthWithCodeRangeAsciiCompatible(Encoding enc, byte[]bytes, int p, int end) {
int cr = 0, c = 0;
while (p < end) {
if (Encoding.isAscii(bytes[p])) {
@@ -341,7 +341,7 @@ private static long strLengthWithCodeRangeAsciiCompatible(Encoding enc, byte[]by
return pack(c, cr == 0 ? CR_7BIT : cr);
}

private static long strLengthWithCodeRangeNonAsciiCompatible(Encoding enc, byte[]bytes, int p, int end) {
public static long strLengthWithCodeRangeNonAsciiCompatible(Encoding enc, byte[]bytes, int p, int end) {
int cr = 0, c = 0;
for (c = 0; p < end; c++) {
int cl = preciseLength(enc, bytes, p, end);
@@ -365,7 +365,7 @@ public static long strLengthWithCodeRange(ByteList bytes, Encoding enc) {
}

// arg cannot be negative
static long pack(int result, int arg) {
public static long pack(int result, int arg) {
return ((long)arg << 31) | result;
}

8 changes: 4 additions & 4 deletions core/src/main/java/org/jruby/util/io/EncodingUtils.java
Original file line number Diff line number Diff line change
@@ -1842,12 +1842,12 @@ public static Encoding getEncoding(ByteList str) {

// MRI: get_actual_encoding
public static Encoding getActualEncoding(Encoding enc, ByteList byteList) {
return getActualEncoding(enc, byteList.getUnsafeBytes(), byteList.begin(), byteList.begin() + byteList.realSize());
}

public static Encoding getActualEncoding(Encoding enc, byte[] bytes, int p, int end) {
if (enc.isDummy() && enc instanceof UnicodeEncoding) {
// handle dummy UTF-16 and UTF-32 by scanning for BOM, as in MRI
byte[] bytes = byteList.unsafeBytes();
int p = byteList.begin();
int end = p + byteList.getRealSize();

if (enc == UTF16Dummy && end - p >= 2) {
int c0 = bytes[p] & 0xff;
int c1 = bytes[p + 1] & 0xff;
4 changes: 2 additions & 2 deletions lib/pom.xml
Original file line number Diff line number Diff line change
@@ -12,7 +12,7 @@ DO NOT MODIFIY - GENERATED CODE
<parent>
<groupId>org.jruby</groupId>
<artifactId>jruby-parent</artifactId>
<version>9.0.5.0</version>
<version>9.1.0.0-SNAPSHOT</version>
</parent>
<artifactId>jruby-stdlib</artifactId>
<name>JRuby Lib Setup</name>
@@ -28,7 +28,7 @@ DO NOT MODIFIY - GENERATED CODE
<dependency>
<groupId>org.jruby</groupId>
<artifactId>jruby-core</artifactId>
<version>9.0.5.0</version>
<version>9.1.0.0-SNAPSHOT</version>
<scope>test</scope>
</dependency>
<dependency>
71 changes: 33 additions & 38 deletions maven/jruby/src/it/extended/src/test/java/org/example/BaseTest.java
Original file line number Diff line number Diff line change
@@ -1,19 +1,13 @@
package org.example;

import static org.junit.Assert.assertEquals;

import java.util.Arrays;
import java.io.File;
import java.io.StringWriter;
import java.net.URL;
import java.net.URLClassLoader;

import org.jruby.embed.LocalContextScope;
import org.jruby.embed.ScriptingContainer;

import org.junit.Before;
import org.junit.Test;

public class BaseTest {

private final String basedir = new File( System.getProperty("basedir"), "../../../../../" ).getAbsolutePath();
@@ -31,38 +25,39 @@ private void runTests(ScriptingContainer container) throws Exception {
container.terminate();
}

private void collectTests(ScriptingContainer container, String index) throws Exception {
container.runScriptlet("File.open(File.join('test', '" + index + ".index')) do |f|\n" +
" f.each_line.each do |line|\n" +
" next if line =~ /^#/ or line.strip.empty?\n" +
" filename = \"test/mri/#{line.chomp}\"\n" +
" filename = \"test/jruby/#{line.chomp}.rb\" unless File.exist? filename\n" +
" filename = \"test/#{line.chomp}.rb\" unless File.exist? filename\n" +
" next unless File.file? filename\n" +
" next if filename =~ /mri\\/net\\/http\\//\n" +
" next if filename =~ /mri\\/ruby\\/test_class/\n" +
" next if filename =~ /mri\\/ruby\\/test_io/\n" +
" next if filename =~ /mri\\/ruby\\/test_econv/\n" +
// TODO find a way to obey the minitest/excludes and get those back
" next if filename =~ /psych\\/test_encoding.rb/\n" +
" next if filename =~ /psych\\/test_parser.rb/\n" +
" next if filename =~ /psych\\/test_psych.rb/\n" +
" next if filename =~ /psych\\/visitors\\/test_yaml_tree.rb/\n" +
" next if filename =~ /psych\\/test_document.rb/\n" +
" next if filename =~ /psych\\/test_tree_builder.rb/\n" +
" next if filename =~ /psych\\/test_date_time.rb/\n" +
" next if filename =~ /psych\\/test_nil.rb/\n" +
// TODO file an issue or so
" next if filename =~ /test_load_compiled_ruby.rb/\n" +
" next if filename =~ /compiler\\/test_jrubyc.rb/\n" +
// TODO remove the following after fix of #2215
" next if filename =~ /test_jar_on_load_path.rb/\n" +
" next if filename =~ /test_file.rb/\n" +
" filename.sub!( /.*\\/test\\//, 'test/' )\n" +
" puts filename\n" +
" require filename\n" +
" end\n" +
" end");
protected void collectTests(ScriptingContainer container, String index) throws Exception {
container.runScriptlet(
"File.open(File.join('test', '" + index + ".index')) do |f|\n" +
" f.each_line do |line|\n" +
" next if line =~ /^#/ or line.strip.empty?\n" +
" filename = \"test/mri/#{line.chomp}\"\n" +
" filename = \"test/jruby/#{line.chomp}.rb\" unless File.exist? filename\n" +
" filename = \"test/#{line.chomp}.rb\" unless File.exist? filename\n" +
" next unless File.file? filename\n" +
" next if filename =~ /mri\\/net\\/http\\//\n" +
" next if filename =~ /mri\\/ruby\\/test_class/\n" +
" next if filename =~ /mri\\/ruby\\/test_io/\n" +
" next if filename =~ /mri\\/ruby\\/test_econv/\n" +
// TODO find a way to obey the minitest/excludes and get those back
" next if filename =~ /psych\\/test_encoding.rb/\n" +
" next if filename =~ /psych\\/test_parser.rb/\n" +
" next if filename =~ /psych\\/test_psych.rb/\n" +
" next if filename =~ /psych\\/visitors\\/test_yaml_tree.rb/\n" +
" next if filename =~ /psych\\/test_document.rb/\n" +
" next if filename =~ /psych\\/test_tree_builder.rb/\n" +
" next if filename =~ /psych\\/test_date_time.rb/\n" +
" next if filename =~ /psych\\/test_nil.rb/\n" +
// TODO file an issue or so
" next if filename =~ /test_load_compiled_ruby.rb/\n" +
" next if filename =~ /compiler\\/test_jrubyc.rb/\n" +
// TODO remove the following after fix of #2215
" next if filename =~ /test_jar_on_load_path.rb/\n" +
" next if filename =~ /test_file.rb/\n" +
" filename.sub!( /.*\\/test\\//, 'test/' )\n" +
" puts filename\n" +
" require filename\n" +
" end\n" +
" end" );
}

void runIt(String index) throws Exception {
Original file line number Diff line number Diff line change
@@ -1,23 +1,37 @@
package org.example;

import static org.junit.Assert.assertEquals;

import java.util.Arrays;
import java.io.File;
import java.io.StringWriter;
import java.net.URL;
import java.net.URLClassLoader;
import org.junit.Test;

import org.jruby.embed.LocalContextScope;
import org.jruby.embed.ScriptingContainer;

import org.junit.Before;
import org.junit.Test;

public class JRubyTest extends BaseTest {

@Test
public void testJRuby() throws Exception {
runIt("jruby");
}

protected void collectTests(ScriptingContainer container, String index) {
container.runScriptlet(
"File.open(File.join('test', '" + index + ".index')) do |f|\n" +
" f.each_line do |line|\n" +
" next if line =~ /^#/ or line.strip.empty?\n" +
" filename = \"test/jruby/#{line.chomp}.rb\"\n" +
" filename = \"test/#{line.chomp}.rb\" unless File.exist? filename\n" +
" next unless File.file? filename\n" +

// TODO file an issue or so
" next if filename =~ /test_load_compiled_ruby.rb/\n" +
" next if filename =~ /compiler\\/test_jrubyc.rb/\n" +
// TODO remove the following after fix of #2215
" next if filename =~ /test_jar_on_load_path.rb/\n" +
" next if filename =~ /test_file.rb/\n" +

" filename.sub!( /.*\\/test\\//, 'test/' )\n" +
" puts filename\n" +
" require filename\n" +
" end\n" +
" end" );
}

}
43 changes: 32 additions & 11 deletions maven/jruby/src/it/extended/src/test/java/org/example/MRITest.java
Original file line number Diff line number Diff line change
@@ -1,23 +1,44 @@
package org.example;

import static org.junit.Assert.assertEquals;

import java.util.Arrays;
import java.io.File;
import java.io.StringWriter;
import java.net.URL;
import java.net.URLClassLoader;
import org.junit.Test;

import org.jruby.embed.LocalContextScope;
import org.jruby.embed.ScriptingContainer;

import org.junit.Before;
import org.junit.Test;

public class MRITest extends BaseTest {

@Test
public void testMRI() throws Exception {
runIt("mri", "ENV['EXCLUDE_DIR']='test/mri/excludes';");
}

protected void collectTests(ScriptingContainer container, String index) {
container.runScriptlet(
"File.open(File.join('test', '" + index + ".index')) do |f|\n" +
" f.each_line do |line|\n" +
" next if line =~ /^#/ or line.strip.empty?\n" +
" filename = \"test/mri/#{line.chomp}\"\n" +
" filename = \"test/#{line.chomp}.rb\" unless File.exist? filename\n" +
" next unless File.file? filename\n" +

" next if filename =~ /mri\\/net\\/http\\//\n" +
" next if filename =~ /mri\\/ruby\\/test_class/\n" +
" next if filename =~ /mri\\/ruby\\/test_io/\n" +
" next if filename =~ /mri\\/ruby\\/test_econv/\n" +
// TODO find a way to obey the minitest/excludes and get those back
" next if filename =~ /psych\\/test_encoding.rb/\n" +
" next if filename =~ /psych\\/test_parser.rb/\n" +
" next if filename =~ /psych\\/test_psych.rb/\n" +
" next if filename =~ /psych\\/visitors\\/test_yaml_tree.rb/\n" +
" next if filename =~ /psych\\/test_document.rb/\n" +
" next if filename =~ /psych\\/test_tree_builder.rb/\n" +
" next if filename =~ /psych\\/test_date_time.rb/\n" +
" next if filename =~ /psych\\/test_nil.rb/\n" +

" filename.sub!( /.*\\/test\\//, 'test/' )\n" +
" puts filename\n" +
" require filename\n" +
" end\n" +
" end" );
}

}
Original file line number Diff line number Diff line change
@@ -1,17 +1,5 @@
package org.example;

import static org.junit.Assert.assertEquals;

import java.util.Arrays;
import java.io.File;
import java.io.StringWriter;
import java.net.URL;
import java.net.URLClassLoader;

import org.jruby.embed.LocalContextScope;
import org.jruby.embed.ScriptingContainer;

import org.junit.Before;
import org.junit.Test;

public class ObjectspaceTest extends BaseTest {
Original file line number Diff line number Diff line change
@@ -1,17 +1,5 @@
package org.example;

import static org.junit.Assert.assertEquals;

import java.util.Arrays;
import java.io.File;
import java.io.StringWriter;
import java.net.URL;
import java.net.URLClassLoader;

import org.jruby.embed.LocalContextScope;
import org.jruby.embed.ScriptingContainer;

import org.junit.Before;
import org.junit.Test;

public class SlowTest extends BaseTest {
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
@@ -16,7 +16,7 @@ DO NOT MODIFIY - GENERATED CODE
</parent>
<groupId>org.jruby</groupId>
<artifactId>jruby-parent</artifactId>
<version>9.0.5.0</version>
<version>9.1.0.0-SNAPSHOT</version>
<packaging>pom</packaging>
<name>JRuby</name>
<description>JRuby is the effort to recreate the Ruby (http://www.ruby-lang.org) interpreter in Java.</description>
23 changes: 22 additions & 1 deletion test/truffle/compiler/pe/core/string_pe.rb
Original file line number Diff line number Diff line change
@@ -12,4 +12,25 @@
example "Truffle::Primitive.create_simple_string.getbyte(0)", simple_string.getbyte(0)

example "'abc'.length", 3
tagged_example "'abc' == 'abc'", true # seems to fail sometimes
example "'こにちわ'.length", 4

example "'abc'.bytesize", 3
example "'こにちわ'.bytesize", 12

example "'abc' == 'abc'", true
example "x = 'abc'; x == x", true
example "x = 'abc'; x == x.dup", true
example "x = 'abc'; 'abc' == x.dup", true

example "'abc'.ascii_only?", true
example "'こにちわ'.ascii_only?", false

example "''.ascii?", false
example "'abc'.ascii?", true

example "'abc'.valid_encoding?", true
example "'こにちわ'.valid_encoding?", true

example "''.empty?", true
example "'abc'.empty?", false
example "'こにちわ'.empty?", false
2 changes: 1 addition & 1 deletion truffle/pom.xml
Original file line number Diff line number Diff line change
@@ -12,7 +12,7 @@ DO NOT MODIFIY - GENERATED CODE
<parent>
<groupId>org.jruby</groupId>
<artifactId>jruby-parent</artifactId>
<version>9.0.5.0</version>
<version>9.1.0.0-SNAPSHOT</version>
</parent>
<artifactId>jruby-truffle</artifactId>
<name>JRuby Truffle</name>
Original file line number Diff line number Diff line change
@@ -20,6 +20,7 @@
import org.jruby.truffle.format.nodes.SourceNode;
import org.jruby.truffle.format.runtime.exceptions.FormatException;
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.core.StringOperations;
import org.jruby.truffle.runtime.layouts.Layouts;
import org.jruby.util.ByteList;
import org.jruby.util.Pack;
@@ -168,7 +169,7 @@ public Object read(VirtualFrame frame, byte[] source) {
final ByteList result = new ByteList(lElem, 0, index, encoding, false);
setSourcePosition(frame, encode.position());

return Layouts.STRING.createString(getContext().getCoreLibrary().getStringFactory(), result, StringSupport.CR_UNKNOWN, null);
return Layouts.STRING.createString(getContext().getCoreLibrary().getStringFactory(), StringOperations.ropeFromByteList(result, StringSupport.CR_UNKNOWN), null);
}

}
Original file line number Diff line number Diff line change
@@ -18,6 +18,7 @@
import org.jruby.truffle.format.nodes.SourceNode;
import org.jruby.truffle.format.runtime.MissingValue;
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.core.StringOperations;
import org.jruby.truffle.runtime.layouts.Layouts;
import org.jruby.util.ByteList;
import org.jruby.util.StringSupport;
@@ -109,7 +110,7 @@ public Object read(VirtualFrame frame, byte[] source) {

setSourcePosition(frame, start + length);

return Layouts.STRING.createString(getContext().getCoreLibrary().getStringFactory(), result, StringSupport.CR_UNKNOWN, null);
return Layouts.STRING.createString(getContext().getCoreLibrary().getStringFactory(), StringOperations.ropeFromByteList(result, StringSupport.CR_UNKNOWN), null);
}

}
Original file line number Diff line number Diff line change
@@ -17,6 +17,7 @@
import org.jruby.truffle.format.nodes.PackNode;
import org.jruby.truffle.format.nodes.SourceNode;
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.core.StringOperations;
import org.jruby.truffle.runtime.layouts.Layouts;
import org.jruby.util.ByteList;
import org.jruby.util.StringSupport;
@@ -83,7 +84,7 @@ public Object read(VirtualFrame frame, byte[] source) {
final ByteList result = new ByteList(lElem, encoding, false);
setSourcePosition(frame, encode.position());

return Layouts.STRING.createString(getContext().getCoreLibrary().getStringFactory(), result, StringSupport.CR_UNKNOWN, null);
return Layouts.STRING.createString(getContext().getCoreLibrary().getStringFactory(), StringOperations.ropeFromByteList(result, StringSupport.CR_UNKNOWN), null);
}

}
Original file line number Diff line number Diff line change
@@ -17,6 +17,7 @@
import org.jruby.truffle.format.nodes.PackNode;
import org.jruby.truffle.format.nodes.SourceNode;
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.core.StringOperations;
import org.jruby.truffle.runtime.layouts.Layouts;
import org.jruby.util.ByteList;
import org.jruby.util.Pack;
@@ -84,7 +85,7 @@ public Object read(VirtualFrame frame, byte[] source) {
final ByteList result = new ByteList(lElem, encoding, false);
setSourcePosition(frame, encode.position());

return Layouts.STRING.createString(getContext().getCoreLibrary().getStringFactory(), result, StringSupport.CR_UNKNOWN, null);
return Layouts.STRING.createString(getContext().getCoreLibrary().getStringFactory(), StringOperations.ropeFromByteList(result, StringSupport.CR_UNKNOWN), null);
}

}
Original file line number Diff line number Diff line change
@@ -18,6 +18,7 @@
import org.jruby.truffle.format.nodes.PackNode;
import org.jruby.truffle.format.nodes.SourceNode;
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.core.StringOperations;
import org.jruby.truffle.runtime.layouts.Layouts;
import org.jruby.util.ByteList;
import org.jruby.util.Pack;
@@ -75,7 +76,7 @@ public Object read(VirtualFrame frame, byte[] source) {
final ByteList result = new ByteList(lElem, 0, index, encoding, false);
setSourcePosition(frame, encode.position());

return Layouts.STRING.createString(getContext().getCoreLibrary().getStringFactory(), result, StringSupport.CR_UNKNOWN, null);
return Layouts.STRING.createString(getContext().getCoreLibrary().getStringFactory(), StringOperations.ropeFromByteList(result, StringSupport.CR_UNKNOWN), null);
}

}
Original file line number Diff line number Diff line change
@@ -21,6 +21,7 @@
import org.jruby.truffle.format.nodes.SourceNode;
import org.jruby.truffle.format.runtime.exceptions.NoImplicitConversionException;
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.core.StringOperations;
import org.jruby.truffle.runtime.layouts.Layouts;
import org.jruby.util.ByteList;
import org.jruby.util.Pack;
@@ -156,7 +157,7 @@ else if (encode.hasRemaining()) {

setSourcePosition(frame, encode.position());

return Layouts.STRING.createString(getContext().getCoreLibrary().getStringFactory(), result, StringSupport.CR_UNKNOWN, null);
return Layouts.STRING.createString(getContext().getCoreLibrary().getStringFactory(), StringOperations.ropeFromByteList(result, StringSupport.CR_UNKNOWN), null);
}

private static int safeGet(ByteBuffer encode) {
Original file line number Diff line number Diff line change
@@ -99,7 +99,7 @@ public ByteList toStringString(VirtualFrame frame, DynamicObject string) {
setTainted(frame);
}

return StringOperations.getByteList(string);
return StringOperations.getByteListReadOnly(string);
}

@Specialization(guards = "isRubyArray(array)")
@@ -116,7 +116,7 @@ public ByteList toString(VirtualFrame frame, DynamicObject array) {
setTainted(frame);
}

return StringOperations.getByteList((DynamicObject) value);
return StringOperations.getByteListReadOnly((DynamicObject) value);
}

CompilerDirectives.transferToInterpreter();
@@ -142,7 +142,7 @@ public ByteList toString(VirtualFrame frame, Object object) {
setTainted(frame);
}

return StringOperations.getByteList((DynamicObject) value);
return StringOperations.getByteListReadOnly((DynamicObject) value);
}

if (inspectOnConversionFailure) {
@@ -152,7 +152,7 @@ public ByteList toString(VirtualFrame frame, Object object) {
getEncapsulatingSourceSection(), new RubyNode[]{null}));
}

return StringOperations.getByteList(inspectNode.toS(frame, object));
return StringOperations.getByteListReadOnly(inspectNode.toS(frame, object));
}

CompilerDirectives.transferToInterpreter();
9 changes: 9 additions & 0 deletions truffle/src/main/java/org/jruby/truffle/nodes/RubyNode.java
Original file line number Diff line number Diff line change
@@ -24,6 +24,7 @@
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.core.StringOperations;
import org.jruby.truffle.runtime.layouts.Layouts;
import org.jruby.truffle.runtime.rope.Rope;
import org.jruby.truffle.runtime.sockets.NativeSockets;
import org.jruby.util.ByteList;

@@ -156,6 +157,10 @@ public DynamicObject getSymbol(ByteList name) {
return getContext().getSymbol(name);
}

public DynamicObject getSymbol(Rope name) {
return getContext().getSymbol(name);
}

/** Creates a String from the ByteList, with unknown CR */
protected DynamicObject createString(ByteList bytes) {
return StringOperations.createString(getContext(), bytes);
@@ -166,6 +171,10 @@ protected DynamicObject create7BitString(ByteList bytes) {
return StringOperations.create7BitString(getContext(), bytes);
}

protected DynamicObject createString(Rope rope) {
return StringOperations.createString(getContext(), rope);
}

protected POSIX posix() {
return getContext().getPosix();
}
Original file line number Diff line number Diff line change
@@ -12,33 +12,38 @@

import com.oracle.truffle.api.object.DynamicObject;
import org.jruby.truffle.runtime.core.StringOperations;
import org.jruby.truffle.runtime.layouts.Layouts;
import org.jruby.truffle.runtime.rope.Rope;
import org.jruby.util.ByteList;

public abstract class StringCachingGuards {

public static ByteList privatizeByteList(DynamicObject string) {
public static Rope privatizeRope(DynamicObject string) {
if (RubyGuards.isRubyString(string)) {
return StringOperations.getByteList(string).dup();
// TODO (nirvdrum 25-Jan-16) Should we flatten the rope to avoid caching a potentially deep rope tree?
return StringOperations.rope(string);
} else {
return null;
}
}

public static boolean byteListsEqual(DynamicObject string, ByteList byteList) {
public static boolean ropesEqual(DynamicObject string, Rope rope) {
if (RubyGuards.isRubyString(string)) {
final Rope stringRope = StringOperations.rope(string);

// equal below does not check encoding
if (StringOperations.getByteList(string).getEncoding() != byteList.getEncoding()) {
if (stringRope.getEncoding() != rope.getEncoding()) {
return false;
}
// TODO CS 8-Nov-15 this code goes off into the woods - need to break it apart and branch profile it
return StringOperations.getByteList(string).equal(byteList);

return stringRope.equals(rope);
} else {
return false;
}
}

public static int byteListLength(ByteList byteList) {
return byteList.length();
public static int ropeLength(Rope rope) {
return rope.byteLength();
}

}
Original file line number Diff line number Diff line change
@@ -36,7 +36,7 @@ protected DynamicObject toSymbolSymbol(DynamicObject symbol) {

@Specialization(guards = "isRubyString(string)")
protected DynamicObject toSymbolString(DynamicObject string) {
return getSymbol(StringOperations.getByteList(string));
return getSymbol(StringOperations.rope(string));
}

@Specialization
Original file line number Diff line number Diff line change
@@ -152,7 +152,7 @@ public InstanceEvalNode(RubyContext context, SourceSection sourceSection) {
@CompilerDirectives.TruffleBoundary
@Specialization(guards = "isRubyString(string)")
public Object instanceEval(Object receiver, DynamicObject string, NotProvided block) {
return getContext().instanceEval(StringOperations.getByteList(string), receiver, "(eval)", this);
return getContext().instanceEval(StringOperations.getByteListReadOnly(string), receiver, "(eval)", this);
}

@Specialization
Original file line number Diff line number Diff line change
@@ -32,7 +32,9 @@
import org.jruby.truffle.runtime.core.EncodingOperations;
import org.jruby.truffle.runtime.core.StringOperations;
import org.jruby.truffle.runtime.layouts.Layouts;
import org.jruby.truffle.runtime.rope.Rope;
import org.jruby.util.ByteList;
import org.jruby.util.StringSupport;

import java.nio.charset.StandardCharsets;
import java.util.HashMap;
@@ -128,7 +130,7 @@ public DynamicObject isCompatibleStringStringCached(DynamicObject first, Dynamic
"isRubyString(first)", "isRubyString(second)"
}, contains = "isCompatibleStringStringCached")
public DynamicObject isCompatibleStringStringUncached(DynamicObject first, DynamicObject second) {
final Encoding compatibleEncoding = areCompatible(first, second);
final Encoding compatibleEncoding = compatibleEncodingForStrings(first, second);

if (compatibleEncoding != null) {
return getEncoding(compatibleEncoding);
@@ -154,7 +156,7 @@ public Object isCompatibleEncodingEncoding(DynamicObject first, DynamicObject se
@TruffleBoundary
@Specialization(guards = {"isRubyString(first)", "isRubyRegexp(second)"})
public Object isCompatibleStringRegexp(DynamicObject first, DynamicObject second) {
final Encoding compatibleEncoding = org.jruby.RubyEncoding.areCompatible(StringOperations.getByteList(first).getEncoding(), Layouts.REGEXP.getRegex(second).getEncoding());
final Encoding compatibleEncoding = org.jruby.RubyEncoding.areCompatible(Layouts.STRING.getRope(first).getEncoding(), Layouts.REGEXP.getRegex(second).getEncoding());

if (compatibleEncoding != null) {
return getEncoding(compatibleEncoding);
@@ -166,7 +168,7 @@ public Object isCompatibleStringRegexp(DynamicObject first, DynamicObject second
@TruffleBoundary
@Specialization(guards = {"isRubyRegexp(first)", "isRubyString(second)"})
public Object isCompatibleRegexpString(DynamicObject first, DynamicObject second) {
final Encoding compatibleEncoding = org.jruby.RubyEncoding.areCompatible(Layouts.REGEXP.getRegex(first).getEncoding(), StringOperations.getByteList(second).getEncoding());
final Encoding compatibleEncoding = org.jruby.RubyEncoding.areCompatible(Layouts.REGEXP.getRegex(first).getEncoding(), Layouts.STRING.getRope(second).getEncoding());

if (compatibleEncoding != null) {
return getEncoding(compatibleEncoding);
@@ -190,7 +192,7 @@ public Object isCompatibleRegexpRegexp(DynamicObject first, DynamicObject second
@TruffleBoundary
@Specialization(guards = {"isRubyRegexp(first)", "isRubySymbol(second)"})
public Object isCompatibleRegexpSymbol(DynamicObject first, DynamicObject second) {
final Encoding compatibleEncoding = org.jruby.RubyEncoding.areCompatible(Layouts.REGEXP.getRegex(first).getEncoding(), Layouts.SYMBOL.getByteList(second).getEncoding());
final Encoding compatibleEncoding = org.jruby.RubyEncoding.areCompatible(Layouts.REGEXP.getRegex(first).getEncoding(), Layouts.SYMBOL.getRope(second).getEncoding());

if (compatibleEncoding != null) {
return getEncoding(compatibleEncoding);
@@ -202,7 +204,7 @@ public Object isCompatibleRegexpSymbol(DynamicObject first, DynamicObject second
@TruffleBoundary
@Specialization(guards = {"isRubySymbol(first)", "isRubyRegexp(second)"})
public Object isCompatibleSymbolRegexp(DynamicObject first, DynamicObject second) {
final Encoding compatibleEncoding = org.jruby.RubyEncoding.areCompatible(Layouts.SYMBOL.getByteList(first).getEncoding(), Layouts.REGEXP.getRegex(second).getEncoding());
final Encoding compatibleEncoding = org.jruby.RubyEncoding.areCompatible(Layouts.SYMBOL.getRope(first).getEncoding(), Layouts.REGEXP.getRegex(second).getEncoding());

if (compatibleEncoding != null) {
return getEncoding(compatibleEncoding);
@@ -214,7 +216,7 @@ public Object isCompatibleSymbolRegexp(DynamicObject first, DynamicObject second
@TruffleBoundary
@Specialization(guards = {"isRubyString(first)", "isRubySymbol(second)"})
public Object isCompatibleStringSymbol(DynamicObject first, DynamicObject second) {
final Encoding compatibleEncoding = org.jruby.RubyEncoding.areCompatible(StringOperations.getCodeRangeable(first), SymbolNodes.getCodeRangeable(second));
final Encoding compatibleEncoding = org.jruby.RubyEncoding.areCompatible(StringOperations.getCodeRangeableReadOnly(first), SymbolNodes.getCodeRangeable(second));

if (compatibleEncoding != null) {
return getEncoding(compatibleEncoding);
@@ -238,7 +240,7 @@ public Object isCompatibleSymbolSymbol(DynamicObject first, DynamicObject second
@TruffleBoundary
@Specialization(guards = {"isRubyString(first)", "isRubyEncoding(second)"})
public Object isCompatibleStringEncoding(DynamicObject first, DynamicObject second) {
final Encoding compatibleEncoding = org.jruby.RubyEncoding.areCompatible(StringOperations.getByteList(first).getEncoding(), EncodingOperations.getEncoding(second));
final Encoding compatibleEncoding = org.jruby.RubyEncoding.areCompatible(Layouts.STRING.getRope(first).getEncoding(), EncodingOperations.getEncoding(second));

if (compatibleEncoding != null) {
return getEncoding(compatibleEncoding);
@@ -247,26 +249,47 @@ public Object isCompatibleStringEncoding(DynamicObject first, DynamicObject seco
}
}

private Encoding areCompatible(DynamicObject first, DynamicObject second) {
@TruffleBoundary
public static Encoding compatibleEncodingForStrings(DynamicObject first, DynamicObject second) {
// Taken from org.jruby.RubyEncoding#areCompatible.

assert RubyGuards.isRubyString(first);
assert RubyGuards.isRubyString(second);

final ByteList firstByteList = StringOperations.getByteList(first);
final ByteList secondByteList = StringOperations.getByteList(second);
final Rope firstRope = StringOperations.rope(first);
final Rope secondRope = StringOperations.rope(second);

final Encoding firstEncoding = firstRope.getEncoding();
final Encoding secondEncoding = secondRope.getEncoding();

if (firstEncoding == null || secondEncoding == null) return null;
if (firstEncoding == secondEncoding) return firstEncoding;

if (secondRope.isEmpty()) return firstEncoding;
if (firstRope.isEmpty()) {
return firstEncoding.isAsciiCompatible() && isAsciiOnly(secondRope) ? firstEncoding : secondEncoding;
}

final Encoding firstEncoding = firstByteList.getEncoding();
final Encoding secondEncoding = secondByteList.getEncoding();
if (!firstEncoding.isAsciiCompatible() || !secondEncoding.isAsciiCompatible()) return null;

if (firstEncoding == secondEncoding) {
return firstEncoding;
if (firstRope.getCodeRange() != secondRope.getCodeRange()) {
if (firstRope.getCodeRange() == StringSupport.CR_7BIT) return secondEncoding;
if (secondRope.getCodeRange() == StringSupport.CR_7BIT) return firstEncoding;
}
if (secondRope.getCodeRange() == StringSupport.CR_7BIT) return firstEncoding;
if (firstRope.getCodeRange() == StringSupport.CR_7BIT) return secondEncoding;

return org.jruby.RubyEncoding.areCompatible(StringOperations.getCodeRangeable(first), StringOperations.getCodeRangeable(second));
return null;
}

@TruffleBoundary
private static boolean isAsciiOnly(Rope rope) {
return rope.getEncoding().isAsciiCompatible() && rope.getCodeRange() == StringSupport.CR_7BIT;
}

protected Encoding extractEncoding(DynamicObject string) {
if (RubyGuards.isRubyString(string)) {
return StringOperations.getByteList(string).getEncoding();
return Layouts.STRING.getRope(string).getEncoding();
}

return null;
Original file line number Diff line number Diff line change
@@ -19,6 +19,8 @@
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.core.StringOperations;
import org.jruby.truffle.runtime.layouts.Layouts;
import org.jruby.truffle.runtime.rope.Rope;
import org.jruby.truffle.runtime.rope.RopeOperations;
import org.jruby.truffle.translator.BodyTranslator;
import org.jruby.util.RegexpOptions;

@@ -43,18 +45,20 @@ public Object execute(VirtualFrame frame) {

for (int n = 0; n < children.length; n++) {
final Object child = children[n].execute(frame);
strings[n] = org.jruby.RubyString.newString(getContext().getRuntime(), StringOperations.getByteList((DynamicObject) toS.call(frame, child, "to_s", null)));
strings[n] = org.jruby.RubyString.newString(getContext().getRuntime(), StringOperations.getByteListReadOnly((DynamicObject) toS.call(frame, child, "to_s", null)));
}

final org.jruby.RubyString preprocessed = org.jruby.RubyRegexp.preprocessDRegexp(getContext().getRuntime(), strings, options);

final DynamicObject regexp = RegexpNodes.createRubyRegexp(getContext(), this, getContext().getCoreLibrary().getRegexpClass(), preprocessed.getByteList(), options);
final DynamicObject regexp = RegexpNodes.createRubyRegexp(getContext(), this, getContext().getCoreLibrary().getRegexpClass(), StringOperations.ropeFromByteList(preprocessed.getByteList()), options);

if (options.isEncodingNone()) {
final Rope source = Layouts.REGEXP.getSource(regexp);

if (!BodyTranslator.all7Bit(preprocessed.getByteList().bytes())) {
Layouts.REGEXP.getSource(regexp).setEncoding(getContext().getRuntime().getEncodingService().getAscii8bitEncoding());
Layouts.REGEXP.setSource(regexp, RopeOperations.withEncoding(source, getContext().getRuntime().getEncodingService().getAscii8bitEncoding()));
} else {
Layouts.REGEXP.getSource(regexp).setEncoding(getContext().getRuntime().getEncodingService().getUSAsciiEncoding());
Layouts.REGEXP.setSource(regexp, RopeOperations.withEncoding(source, getContext().getRuntime().getEncodingService().getUSAsciiEncoding()));
}
}

Original file line number Diff line number Diff line change
@@ -31,7 +31,7 @@ public final class InterpolatedStringNode extends RubyNode {

@Children private final ToSNode[] children;

@Child private CallDispatchHeadNode concatNode;
@Child private CallDispatchHeadNode appendNode;
@Child private CallDispatchHeadNode dupNode;
@Child private IsTaintedNode isTaintedNode;
@Child private TaintNode taintNode;
@@ -41,7 +41,7 @@ public final class InterpolatedStringNode extends RubyNode {
public InterpolatedStringNode(RubyContext context, SourceSection sourceSection, ToSNode[] children) {
super(context, sourceSection);
this.children = children;
concatNode = DispatchHeadNodeFactory.createMethodCall(context);
appendNode = DispatchHeadNodeFactory.createMethodCall(context);
dupNode = DispatchHeadNodeFactory.createMethodCall(context);
isTaintedNode = IsTaintedNodeGen.create(context, sourceSection, null);
taintNode = TaintNodeGen.create(context, sourceSection, null);
@@ -74,13 +74,14 @@ private Object concat(VirtualFrame frame, Object[] strings) {

Object builder = null;

// TODO (nirvdrum 11-Jan-16) Rewrite to avoid massively unbalanced trees.
for (Object string : strings) {
assert RubyGuards.isRubyString(string);

if (builder == null) {
builder = dupNode.call(frame, string, "dup", null);
} else {
builder = concatNode.call(frame, builder, "concat", null, string);
builder = appendNode.call(frame, builder, "append", null, string);
}
}

Original file line number Diff line number Diff line change
@@ -72,6 +72,7 @@
import org.jruby.truffle.runtime.loader.FeatureLoader;
import org.jruby.truffle.runtime.methods.InternalMethod;
import org.jruby.truffle.runtime.methods.SharedMethodInfo;
import org.jruby.truffle.runtime.rope.Rope;
import org.jruby.truffle.runtime.subsystems.ThreadManager.BlockingAction;
import org.jruby.truffle.translator.TranslatorDriver;
import org.jruby.truffle.translator.TranslatorDriver.ParserContext;
@@ -511,7 +512,7 @@ public RubyRootNode getRootNode() {

@Specialization(guards = {
"isRubyString(source)",
"byteListsEqual(source, cachedSource)",
"ropesEqual(source, cachedSource)",
"!parseDependsOnDeclarationFrame(cachedRootNode)"
}, limit = "getCacheLimit()")
public Object evalNoBindingCached(
@@ -520,7 +521,7 @@ public Object evalNoBindingCached(
NotProvided binding,
NotProvided filename,
NotProvided lineNumber,
@Cached("privatizeByteList(source)") ByteList cachedSource,
@Cached("privatizeRope(source)") Rope cachedSource,
@Cached("compileSource(frame, source)") RootNodeWrapper cachedRootNode,
@Cached("createCallTarget(cachedRootNode)") CallTarget cachedCallTarget,
@Cached("create(cachedCallTarget)") DirectCallNode callNode
@@ -621,7 +622,7 @@ public Object evalBadBinding(DynamicObject source, DynamicObject badBinding, Not

@TruffleBoundary
private Object doEval(DynamicObject source, DynamicObject binding, String filename, boolean ownScopeForAssignments) {
final Object result = getContext().eval(ParserContext.EVAL, StringOperations.getByteList(source), binding, ownScopeForAssignments, filename, this);
final Object result = getContext().eval(ParserContext.EVAL, StringOperations.getByteListReadOnly(source), binding, ownScopeForAssignments, filename, this);
assert result != null;
return result;
}
@@ -632,7 +633,7 @@ protected RootNodeWrapper compileSource(VirtualFrame frame, DynamicObject source
final DynamicObject callerBinding = getCallerBinding(frame);
final MaterializedFrame parentFrame = Layouts.BINDING.getFrame(callerBinding);

final Encoding encoding = StringOperations.getByteList(sourceText).getEncoding();
final Encoding encoding = Layouts.STRING.getRope(sourceText).getEncoding();
final Source source = Source.fromText(sourceText.toString(), "(eval)");

final TranslatorDriver translator = new TranslatorDriver(getContext());
@@ -1870,13 +1871,13 @@ public SprintfNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}

@Specialization(guards = { "isRubyString(format)", "byteListsEqual(format, cachedFormat)" })
@Specialization(guards = { "isRubyString(format)", "ropesEqual(format, cachedFormat)" })
public DynamicObject formatCached(
VirtualFrame frame,
DynamicObject format,
Object[] arguments,
@Cached("privatizeByteList(format)") ByteList cachedFormat,
@Cached("byteListLength(cachedFormat)") int cachedFormatLength,
@Cached("privatizeRope(format)") Rope cachedFormat,
@Cached("ropeLength(cachedFormat)") int cachedFormatLength,
@Cached("create(compileFormat(format))") DirectCallNode callPackNode) {
final PackResult result;

@@ -1905,7 +1906,7 @@ public DynamicObject formatUncached(
throw handleException(e);
}

return finishFormat(StringOperations.getByteList(format).length(), result);
return finishFormat(Layouts.STRING.getRope(format).byteLength(), result);
}

private RuntimeException handleException(PackException exception) {
@@ -1964,7 +1965,7 @@ protected CallTarget compileFormat(DynamicObject format) {
assert RubyGuards.isRubyString(format);

try {
return new PrintfCompiler(getContext(), this).compile(StringOperations.getByteList(format));
return new PrintfCompiler(getContext(), this).compile(StringOperations.getByteListReadOnly(format));
} catch (FormatException e) {
CompilerDirectives.transferToInterpreter();
throw new RaiseException(getContext().getCoreLibrary().argumentError(e.getMessage(), this));
Original file line number Diff line number Diff line change
@@ -33,6 +33,7 @@
import org.jruby.truffle.runtime.core.ArrayOperations;
import org.jruby.truffle.runtime.core.StringOperations;
import org.jruby.truffle.runtime.layouts.Layouts;
import org.jruby.truffle.runtime.rope.Rope;
import org.jruby.util.ByteList;
import org.jruby.util.StringSupport;

@@ -154,7 +155,7 @@ public static Region getCharOffsets(DynamicObject matchData) {

@TruffleBoundary
private static Region createCharOffsets(DynamicObject matchData) {
final ByteList source = StringOperations.getByteList(Layouts.MATCH_DATA.getSource(matchData));
final ByteList source = StringOperations.getByteListReadOnly(Layouts.MATCH_DATA.getSource(matchData));
final Encoding enc = source.getEncoding();
final Region charOffsets = getCharOffsetsManyRegs(matchData, source, enc);
Layouts.MATCH_DATA.setCharOffsets(matchData, charOffsets);
@@ -197,8 +198,8 @@ public Object getIndex(DynamicObject matchData, int index, int length) {
@Specialization(guards = "isRubySymbol(index)")
public Object getIndexSymbol(DynamicObject matchData, DynamicObject index, NotProvided length) {
try {
ByteList value = Layouts.SYMBOL.getByteList(index);
final int i = Layouts.REGEXP.getRegex(Layouts.MATCH_DATA.getRegexp(matchData)).nameToBackrefNumber(value.getUnsafeBytes(), value.getBegin(), value.getBegin() + value.getRealSize(), Layouts.MATCH_DATA.getRegion(matchData));
final Rope value = Layouts.SYMBOL.getRope(index);
final int i = Layouts.REGEXP.getRegex(Layouts.MATCH_DATA.getRegexp(matchData)).nameToBackrefNumber(value.getBytes(), value.getBegin(), value.getBegin() + value.getRealSize(), Layouts.MATCH_DATA.getRegion(matchData));

return getIndex(matchData, i, NotProvided.INSTANCE);
} catch (final ValueException e) {
@@ -213,7 +214,7 @@ public Object getIndexSymbol(DynamicObject matchData, DynamicObject index, NotPr
@Specialization(guards = "isRubyString(index)")
public Object getIndexString(DynamicObject matchData, DynamicObject index, NotProvided length) {
try {
ByteList value = StringOperations.getByteList(index);
ByteList value = StringOperations.getByteListReadOnly(index);
final int i = Layouts.REGEXP.getRegex(Layouts.MATCH_DATA.getRegexp(matchData)).nameToBackrefNumber(value.getUnsafeBytes(), value.getBegin(), value.getBegin() + value.getRealSize(), Layouts.MATCH_DATA.getRegion(matchData));

return getIndex(matchData, i, NotProvided.INSTANCE);
@@ -421,7 +422,7 @@ public ToSNode(RubyContext context, SourceSection sourceSection) {
@CompilerDirectives.TruffleBoundary
@Specialization
public DynamicObject toS(DynamicObject matchData) {
final ByteList bytes = StringOperations.getByteList(Layouts.MATCH_DATA.getGlobal(matchData)).dup();
final ByteList bytes = StringOperations.getByteListReadOnly(Layouts.MATCH_DATA.getGlobal(matchData)).dup();
return createString(bytes);
}
}
Original file line number Diff line number Diff line change
@@ -641,7 +641,7 @@ private Object classEvalSource(DynamicObject module, DynamicObject code, String

final MaterializedFrame callerFrame = RubyCallStack.getCallerFrame(getContext())
.getFrame(FrameInstance.FrameAccess.MATERIALIZE, true).materialize();
Encoding encoding = StringOperations.getByteList(code).getEncoding();
Encoding encoding = Layouts.STRING.getRope(code).getEncoding();

CompilerDirectives.transferToInterpreter();
// TODO (pitr 15-Oct-2015): fix this ugly hack, required for AS
Original file line number Diff line number Diff line change
@@ -26,7 +26,7 @@ public static boolean isRegexpLiteral(DynamicObject regexp) {
}

public static boolean isValidEncoding(DynamicObject string) {
return StringOperations.scanForCodeRange(string) != StringSupport.CR_BROKEN;
return StringOperations.codeRange(string) != StringSupport.CR_BROKEN;
}

}
90 changes: 51 additions & 39 deletions truffle/src/main/java/org/jruby/truffle/nodes/core/RegexpNodes.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, 2016 Oracle and/or its affiliates. All rights reserved. This
* Copyright (c) 2013, 2015, 2016 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:
*
@@ -39,6 +39,8 @@
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.core.StringOperations;
import org.jruby.truffle.runtime.layouts.Layouts;
import org.jruby.truffle.runtime.rope.Rope;
import org.jruby.truffle.runtime.rope.RopeOperations;
import org.jruby.util.*;

import java.nio.charset.StandardCharsets;
@@ -54,25 +56,25 @@ public static Object matchCommon(RubyContext context, DynamicObject regexp, Dyna
assert RubyGuards.isRubyRegexp(regexp);
assert RubyGuards.isRubyString(source);

final ByteList sourceByteList = StringOperations.getByteList(source);
final Rope sourceRope = StringOperations.rope(source);

final ByteList bl = Layouts.REGEXP.getSource(regexp);
final Encoding enc = checkEncoding(regexp, StringOperations.getCodeRangeable(source), true);
final ByteList preprocessed = RegexpSupport.preprocess(context.getRuntime(), bl, enc, new Encoding[] { null }, RegexpSupport.ErrorMode.RAISE);
final Rope regexpSourceRope = Layouts.REGEXP.getSource(regexp);
final Encoding enc = checkEncoding(regexp, sourceRope, true);
final ByteList preprocessed = RegexpSupport.preprocess(context.getRuntime(), regexpSourceRope.getUnsafeByteList(), enc, new Encoding[] { null }, RegexpSupport.ErrorMode.RAISE);

final Regex r = new Regex(preprocessed.getUnsafeBytes(), preprocessed.getBegin(), preprocessed.getBegin() + preprocessed.getRealSize(), Layouts.REGEXP.getOptions(regexp).toJoniOptions(), checkEncoding(regexp, StringOperations.getCodeRangeable(source), true));
final Matcher matcher = r.matcher(sourceByteList.unsafeBytes(), sourceByteList.begin(), sourceByteList.begin() + sourceByteList.realSize());
int range = sourceByteList.begin() + sourceByteList.realSize();
final Regex r = new Regex(preprocessed.getUnsafeBytes(), preprocessed.getBegin(), preprocessed.getBegin() + preprocessed.getRealSize(), Layouts.REGEXP.getOptions(regexp).toJoniOptions(), checkEncoding(regexp, sourceRope, true));
final Matcher matcher = r.matcher(sourceRope.getBytes(), sourceRope.begin(), sourceRope.begin() + sourceRope.realSize());
int range = sourceRope.begin() + sourceRope.realSize();

return matchCommon(context, regexp, source, operator, setNamedCaptures, matcher, sourceByteList.begin() + startPos, range);
return matchCommon(context, regexp, source, operator, setNamedCaptures, matcher, sourceRope.begin() + startPos, range);
}

@TruffleBoundary
public static Object matchCommon(RubyContext context, DynamicObject regexp, DynamicObject source, boolean operator, boolean setNamedCaptures, Matcher matcher, int startPos, int range) {
assert RubyGuards.isRubyRegexp(regexp);
assert RubyGuards.isRubyString(source);

final ByteList bytes = StringOperations.getByteList(source);
final ByteList bytes = StringOperations.getByteListReadOnly(source);

final int match = matcher.search(startPos, range, Option.DEFAULT);

@@ -176,10 +178,8 @@ public static Object matchCommon(RubyContext context, DynamicObject regexp, Dyna
private static DynamicObject createSubstring(DynamicObject source, int start, int length) {
assert RubyGuards.isRubyString(source);

final ByteList bytes = new ByteList(StringOperations.getByteList(source), start, length);
final DynamicObject ret = Layouts.STRING.createString(Layouts.CLASS.getInstanceFactory(Layouts.BASIC_OBJECT.getLogicalClass(source)), bytes, StringSupport.CR_UNKNOWN, null);

Layouts.STRING.setCodeRange(ret, Layouts.STRING.getCodeRange(source));
final ByteList bytes = new ByteList(StringOperations.getByteListReadOnly(source), start, length);
final DynamicObject ret = Layouts.STRING.createString(Layouts.CLASS.getInstanceFactory(Layouts.BASIC_OBJECT.getLogicalClass(source)), StringOperations.ropeFromByteList(bytes, StringOperations.getCodeRange(source)), null);

return ret;
}
@@ -198,7 +198,7 @@ private static void setLocalVariable(Frame frame, String name, Object value) {
}
}

public static ByteList shimModifiers(ByteList bytes) {
public static Rope shimModifiers(Rope bytes) {
// Joni doesn't support (?u) etc but we can shim some common cases

String bytesString = bytes.toString();
@@ -224,14 +224,15 @@ public static ByteList shimModifiers(ByteList bytes) {
throw new UnsupportedOperationException();
}

bytes = ByteList.create(bytesString);
// TODO (nirvdrum 25-Jan-16): We probably just want a way to create a Rope from a java.lang.String.
bytes = StringOperations.ropeFromByteList(ByteList.create(bytesString));
}

return bytes;
}

@TruffleBoundary
public static Regex compile(Node currentNode, RubyContext context, ByteList bytes, RegexpOptions options) {
public static Regex compile(Node currentNode, RubyContext context, Rope bytes, RegexpOptions options) {
bytes = shimModifiers(bytes);

try {
@@ -250,13 +251,14 @@ public static Regex compile(Node currentNode, RubyContext context, ByteList byte
}
*/

final ByteList byteList = bytes.getUnsafeByteList();
Encoding enc = bytes.getEncoding();
Encoding[] fixedEnc = new Encoding[]{null};
ByteList unescaped = RegexpSupport.preprocess(context.getRuntime(), bytes, enc, fixedEnc, RegexpSupport.ErrorMode.RAISE);
ByteList unescaped = RegexpSupport.preprocess(context.getRuntime(), byteList, enc, fixedEnc, RegexpSupport.ErrorMode.RAISE);
if (fixedEnc[0] != null) {
if ((fixedEnc[0] != enc && options.isFixed()) ||
(fixedEnc[0] != ASCIIEncoding.INSTANCE && options.isEncodingNone())) {
RegexpSupport.raiseRegexpError19(context.getRuntime(), bytes, enc, options, "incompatible character encoding");
RegexpSupport.raiseRegexpError19(context.getRuntime(), byteList, enc, options, "incompatible character encoding");
}
if (fixedEnc[0] != ASCIIEncoding.INSTANCE) {
options.setFixed(true);
@@ -269,10 +271,8 @@ public static Regex compile(Node currentNode, RubyContext context, ByteList byte
if (fixedEnc[0] != null) options.setFixed(true);
//if (regexpOptions.isEncodingNone()) setEncodingNone();

bytes.setEncoding(enc);

Regex ret = new Regex(unescaped.getUnsafeBytes(), unescaped.getBegin(), unescaped.getBegin() + unescaped.getRealSize(), options.toJoniOptions(), enc, Syntax.RUBY);
ret.setUserObject(bytes);
ret.setUserObject(RopeOperations.withEncoding(bytes, enc));

return ret;
} catch (ValueException e) {
@@ -294,7 +294,7 @@ public static void setRegex(DynamicObject regexp, Regex regex) {
Layouts.REGEXP.setRegex(regexp, regex);
}

public static void setSource(DynamicObject regexp, ByteList source) {
public static void setSource(DynamicObject regexp, Rope source) {
Layouts.REGEXP.setSource(regexp, source);
}

@@ -303,7 +303,7 @@ public static void setOptions(DynamicObject regexp, RegexpOptions options) {
}

// TODO (nirvdrum 03-June-15) Unify with JRuby in RegexpSupport.
public static Encoding checkEncoding(DynamicObject regexp, CodeRangeable str, boolean warn) {
public static Encoding checkEncoding(DynamicObject regexp, Rope str, boolean warn) {
assert RubyGuards.isRubyRegexp(regexp);

final Regex pattern = Layouts.REGEXP.getRegex(regexp);
@@ -314,7 +314,7 @@ public static Encoding checkEncoding(DynamicObject regexp, CodeRangeable str, bo
}
*/
//check();
Encoding enc = str.getByteList().getEncoding();
Encoding enc = str.getEncoding();
if (!enc.isAsciiCompatible()) {
if (enc != pattern.getEncoding()) {
//encodingMatchError(getRuntime(), pattern, enc);
@@ -337,31 +337,42 @@ public static Encoding checkEncoding(DynamicObject regexp, CodeRangeable str, bo
return enc;
}

public static void initialize(RubyContext context, DynamicObject regexp, Node currentNode, ByteList setSource, int options) {
public static void initialize(RubyContext context, DynamicObject regexp, Node currentNode, Rope setSource, int options) {
assert RubyGuards.isRubyRegexp(regexp);
setSource(regexp, setSource);
setOptions(regexp, RegexpOptions.fromEmbeddedOptions(options));
setRegex(regexp, compile(currentNode, context, setSource, Layouts.REGEXP.getOptions(regexp)));
final RegexpOptions regexpOptions = RegexpOptions.fromEmbeddedOptions(options);
final Regex regex = compile(currentNode, context, setSource, regexpOptions);

// The RegexpNodes.compile operation may modify the encoding of the source rope. This modified copy is stored
// in the Regex object as the "user object". Since ropes are immutable, we need to take this updated copy when
// constructing the final regexp.
setSource(regexp, (Rope) regex.getUserObject());
setOptions(regexp, regexpOptions);
setRegex(regexp, regex);
}

public static void initialize(DynamicObject regexp, Regex setRegex, ByteList setSource) {
public static void initialize(DynamicObject regexp, Regex setRegex, Rope setSource) {
assert RubyGuards.isRubyRegexp(regexp);
setRegex(regexp, setRegex);
setSource(regexp, setSource);
}

public static DynamicObject createRubyRegexp(RubyContext context, Node currentNode, DynamicObject regexpClass, ByteList regex, RegexpOptions options) {
return Layouts.REGEXP.createRegexp(Layouts.CLASS.getInstanceFactory(regexpClass), RegexpNodes.compile(currentNode, context, regex, options), regex, options, null);
public static DynamicObject createRubyRegexp(RubyContext context, Node currentNode, DynamicObject regexpClass, Rope source, RegexpOptions options) {
final Regex regexp = RegexpNodes.compile(currentNode, context, source, options);

// The RegexpNodes.compile operation may modify the encoding of the source rope. This modified copy is stored
// in the Regex object as the "user object". Since ropes are immutable, we need to take this updated copy when
// constructing the final regexp.
return Layouts.REGEXP.createRegexp(Layouts.CLASS.getInstanceFactory(regexpClass), regexp, (Rope) regexp.getUserObject(), options, null);
}

public static DynamicObject createRubyRegexp(DynamicObject regexpClass, Regex regex, ByteList source, RegexpOptions options) {
public static DynamicObject createRubyRegexp(DynamicObject regexpClass, Regex regex, Rope source, RegexpOptions options) {
final DynamicObject regexp = Layouts.REGEXP.createRegexp(Layouts.CLASS.getInstanceFactory(regexpClass), null, null, RegexpOptions.NULL_OPTIONS, null);
RegexpNodes.setOptions(regexp, options);
RegexpNodes.initialize(regexp, regex, source);
return regexp;
}

public static DynamicObject createRubyRegexp(DynamicObject regexpClass, Regex regex, ByteList source) {
public static DynamicObject createRubyRegexp(DynamicObject regexpClass, Regex regex, Rope source) {
final DynamicObject regexp = Layouts.REGEXP.createRegexp(Layouts.CLASS.getInstanceFactory(regexpClass), null, null, RegexpOptions.NULL_OPTIONS, null);
RegexpNodes.initialize(regexp, regex, source);
return regexp;
@@ -419,7 +430,7 @@ public EscapeNode(RubyContext context, SourceSection sourceSection) {
@TruffleBoundary
@Specialization(guards = "isRubyString(pattern)")
public DynamicObject escape(DynamicObject pattern) {
return createString(StringOperations.encodeByteList(org.jruby.RubyRegexp.quote19(new ByteList(StringOperations.getByteList(pattern)), true).toString(), UTF8Encoding.INSTANCE));
return createString(StringOperations.encodeByteList(org.jruby.RubyRegexp.quote19(new ByteList(StringOperations.getByteListReadOnly(pattern)), true).toString(), UTF8Encoding.INSTANCE));
}

}
@@ -468,8 +479,9 @@ public QuoteNode(RubyContext context, SourceSection sourceSection) {
@TruffleBoundary
@Specialization(guards = "isRubyString(raw)")
public DynamicObject quoteString(DynamicObject raw) {
boolean isAsciiOnly = StringOperations.getByteList(raw).getEncoding().isAsciiCompatible() && StringOperations.scanForCodeRange(raw) == CR_7BIT;
return createString(org.jruby.RubyRegexp.quote19(StringOperations.getByteList(raw), isAsciiOnly));
final Rope rope = StringOperations.rope(raw);
boolean isAsciiOnly = rope.getEncoding().isAsciiCompatible() && rope.getCodeRange() == CR_7BIT;
return createString(org.jruby.RubyRegexp.quote19(StringOperations.getByteListReadOnly(raw), isAsciiOnly));
}

@Specialization(guards = "isRubySymbol(raw)")
@@ -502,7 +514,7 @@ public SourceNode(RubyContext context, SourceSection sourceSection) {

@Specialization
public DynamicObject source(DynamicObject regexp) {
return createString(Layouts.REGEXP.getSource(regexp).dup());
return createString(Layouts.REGEXP.getSource(regexp));
}

}
@@ -517,7 +529,7 @@ public ToSNode(RubyContext context, SourceSection sourceSection) {
@TruffleBoundary
@Specialization
public DynamicObject toS(DynamicObject regexp) {
return createString(((org.jruby.RubyString) org.jruby.RubyRegexp.newRegexp(getContext().getRuntime(), Layouts.REGEXP.getSource(regexp), Layouts.REGEXP.getRegex(regexp).getOptions()).to_s()).getByteList());
return createString(((org.jruby.RubyString) org.jruby.RubyRegexp.newRegexp(getContext().getRuntime(), Layouts.REGEXP.getSource(regexp).getUnsafeByteList(), Layouts.REGEXP.getRegex(regexp).getOptions()).to_s()).getByteList());
}

}
438 changes: 438 additions & 0 deletions truffle/src/main/java/org/jruby/truffle/nodes/core/RopeNodes.java

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2015 Oracle and/or its affiliates. All rights reserved. This
* Copyright (c) 2015, 2016 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:
*
@@ -15,49 +15,62 @@
import org.jruby.truffle.nodes.RubyGuards;
import org.jruby.truffle.runtime.core.StringOperations;
import org.jruby.truffle.runtime.layouts.Layouts;
import org.jruby.truffle.runtime.rope.Rope;
import org.jruby.util.CodeRangeSupport;
import org.jruby.util.StringSupport;

public class StringGuards {

public static boolean isSingleByteOptimizable(DynamicObject string) {
assert RubyGuards.isRubyString(string);
return Layouts.STRING.getCodeRange(string) == StringSupport.CR_7BIT || StringOperations.getByteList(string).getEncoding().maxLength() == 1;
return Layouts.STRING.getRope(string).isSingleByteOptimizable();
}

public static boolean is7Bit(DynamicObject string) {
assert RubyGuards.isRubyString(string);
return Layouts.STRING.getCodeRange(string) == StringSupport.CR_7BIT;
return StringOperations.getCodeRange(string) == StringSupport.CR_7BIT;
}

public static boolean isAsciiCompatible(DynamicObject string) {
assert RubyGuards.isRubyString(string);
return StringOperations.getByteList(string).getEncoding().isAsciiCompatible();
return Layouts.STRING.getRope(string).getEncoding().isAsciiCompatible();
}

public static boolean isSingleByteOptimizableOrAsciiOnly(DynamicObject string) {
assert RubyGuards.isRubyString(string);
// TODO (nirvdrnum 08-Jun-15) Rubinius tracks whether a String is ASCII-only via a field in the String.
// TODO (nirvdrum 08-Jun-15) Rubinius tracks whether a String is ASCII-only via a field in the String.
return isSingleByteOptimizable(string);
}

public static boolean isSingleByte(DynamicObject string) {
assert RubyGuards.isRubyString(string);
return StringOperations.getByteList(string).getEncoding().isSingleByte();
return Layouts.STRING.getRope(string).getEncoding().isSingleByte();
}

public static boolean isValidOr7BitEncoding(DynamicObject string) {
assert RubyGuards.isRubyString(string);
return StringOperations.isCodeRangeValid(string) || CodeRangeSupport.isCodeRangeAsciiOnly(StringOperations.getCodeRangeable(string));
final Rope rope = StringOperations.rope(string);

return (rope.getCodeRange() == StringSupport.CR_VALID) || (rope.getCodeRange() == StringSupport.CR_7BIT);
}

public static boolean isFixedWidthEncoding(DynamicObject string) {
assert RubyGuards.isRubyString(string);
return StringOperations.getByteList(string).getEncoding().isFixedWidth();
return Layouts.STRING.getRope(string).getEncoding().isFixedWidth();
}

public static boolean isValidUtf8(DynamicObject string) {
assert RubyGuards.isRubyString(string);
return StringOperations.isCodeRangeValid(string) && StringOperations.getByteList(string).getEncoding() instanceof UTF8Encoding;
return StringOperations.isCodeRangeValid(string) && Layouts.STRING.getRope(string).getEncoding() instanceof UTF8Encoding;
}

public static boolean isEmpty(DynamicObject string) {
assert RubyGuards.isRubyString(string);
return Layouts.STRING.getRope(string).isEmpty();
}

public static boolean isBrokenCodeRange(DynamicObject string) {
assert RubyGuards.isRubyString(string);
return StringOperations.codeRange(string) == StringSupport.CR_BROKEN;
}
}
1,146 changes: 662 additions & 484 deletions truffle/src/main/java/org/jruby/truffle/nodes/core/StringNodes.java

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -94,7 +94,7 @@ public EncodingNode(RubyContext context, SourceSection sourceSection) {

@Specialization
public DynamicObject encoding(DynamicObject symbol) {
return EncodingNodes.getEncoding(Layouts.SYMBOL.getByteList(symbol).getEncoding());
return EncodingNodes.getEncoding(Layouts.SYMBOL.getRope(symbol).getEncoding());
}

}
@@ -176,7 +176,7 @@ public ToSNode(RubyContext context, SourceSection sourceSection) {

@Specialization
public DynamicObject toS(DynamicObject symbol) {
return createString(Layouts.SYMBOL.getByteList(symbol).dup());
return createString(Layouts.SYMBOL.getRope(symbol));
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2014, 2015 Oracle and/or its affiliates. All rights reserved. This
* Copyright (c) 2014, 2015, 2016 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:
*
@@ -26,7 +26,10 @@
import com.oracle.truffle.api.source.SourceSection;
import org.jruby.truffle.nodes.StringCachingGuards;
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.core.StringOperations;
import org.jruby.truffle.runtime.layouts.Layouts;
import org.jruby.truffle.runtime.rope.Rope;
import org.jruby.truffle.runtime.rope.RopeOperations;
import org.jruby.util.ByteList;
import org.jruby.util.StringSupport;

@@ -232,11 +235,11 @@ public Object readProperty(VirtualFrame frame,
return ForeignAccess.execute(readNode, frame, receiver, identifierString);
}

@Specialization(guards = {"isRubyString(identifier)", "byteListsEqual(identifier, cachedIdentifier)"})
@Specialization(guards = {"isRubyString(identifier)", "ropesEqual(identifier, cachedIdentifier)"})
public Object readProperty(VirtualFrame frame,
TruffleObject receiver,
DynamicObject identifier,
@Cached("privatizeByteList(identifier)") ByteList cachedIdentifier,
@Cached("privatizeRope(identifier)") Rope cachedIdentifier,
@Cached("identifier.toString()") String identifierString,
@Cached("createReadNode()") Node readNode) {
return ForeignAccess.execute(readNode, frame, receiver, identifierString);
@@ -280,12 +283,12 @@ public Object writeProperty(VirtualFrame frame,
return ForeignAccess.execute(writeNode, frame, receiver, identifierString, value);
}

@Specialization(guards = {"isRubyString(identifier)", "byteListsEqual(identifier, cachedIdentifier)"})
@Specialization(guards = {"isRubyString(identifier)", "ropesEqual(identifier, cachedIdentifier)"})
public Object writeProperty(VirtualFrame frame,
TruffleObject receiver,
DynamicObject identifier,
Object value,
@Cached("privatizeByteList(identifier)") ByteList cachedIdentifier,
@Cached("privatizeRope(identifier)") Rope cachedIdentifier,
@Cached("identifier.toString()") String identifierString,
@Cached("createWriteNode()") Node writeNode) {
return ForeignAccess.execute(writeNode, frame, receiver, identifierString, value);
@@ -341,7 +344,7 @@ public double unbox(VirtualFrame frame, double receiver) {
public DynamicObject executeForeign(VirtualFrame frame, CharSequence receiver) {
// TODO CS-21-Dec-15 this shouldn't be needed - we need to convert j.l.String to Ruby's String automatically

return Layouts.STRING.createString(getContext().getCoreLibrary().getStringFactory(), ByteList.create(receiver), StringSupport.CR_UNKNOWN, null);
return Layouts.STRING.createString(getContext().getCoreLibrary().getStringFactory(), StringOperations.ropeFromByteList(ByteList.create(receiver)), null);
}

@Specialization
@@ -437,15 +440,15 @@ public EvalNode(RubyContext context, SourceSection sourceSection) {
@Specialization(guards = {
"isRubyString(mimeType)",
"isRubyString(source)",
"byteListsEqual(mimeType, cachedMimeType)",
"byteListsEqual(source, cachedSource)"
"ropesEqual(mimeType, cachedMimeType)",
"ropesEqual(source, cachedSource)"
}, limit = "getCacheLimit()")
public Object evalCached(
VirtualFrame frame,
DynamicObject mimeType,
DynamicObject source,
@Cached("privatizeByteList(mimeType)") ByteList cachedMimeType,
@Cached("privatizeByteList(source)") ByteList cachedSource,
@Cached("privatizeRope(mimeType)") Rope cachedMimeType,
@Cached("privatizeRope(source)") Rope cachedSource,
@Cached("create(parse(mimeType, source))") DirectCallNode callNode
) {
return callNode.call(frame, new Object[]{});
@@ -486,7 +489,7 @@ public JavaStringToRubyNode(RubyContext context, SourceSection sourceSection) {
@Specialization
@TruffleBoundary
public DynamicObject javaStringToRuby(String string) {
return Layouts.STRING.createString(getContext().getCoreLibrary().getStringFactory(), ByteList.create(string), StringSupport.CR_UNKNOWN, null);
return Layouts.STRING.createString(getContext().getCoreLibrary().getStringFactory(), StringOperations.ropeFromByteList(ByteList.create(string), StringSupport.CR_UNKNOWN), null);
}

}
Original file line number Diff line number Diff line change
@@ -43,9 +43,12 @@
import org.jruby.truffle.runtime.hash.BucketsStrategy;
import org.jruby.truffle.runtime.layouts.Layouts;
import org.jruby.truffle.runtime.methods.InternalMethod;
import org.jruby.truffle.runtime.rope.Rope;
import org.jruby.truffle.runtime.rope.RopeOperations;
import org.jruby.truffle.runtime.subsystems.SimpleShell;
import org.jruby.util.ByteList;
import org.jruby.util.Memo;
import org.jruby.util.StringSupport;
import org.jruby.util.unsafe.UnsafeHolder;

import java.io.IOException;
@@ -214,7 +217,7 @@ public DumpStringNode(RubyContext context, SourceSection sourceSection) {
public DynamicObject dumpString(DynamicObject string) {
final StringBuilder builder = new StringBuilder();

final ByteList byteList = StringOperations.getByteList(string);
final ByteList byteList = StringOperations.getByteListReadOnly(string);

for (int i = 0; i < byteList.length(); i++) {
builder.append(String.format("\\x%02x", byteList.get(i)));
@@ -454,6 +457,57 @@ public DynamicObject debugPrint(DynamicObject string) {

}

@CoreMethod(names = "debug_print_rope", onSingleton = true, required = 1, optional = 1)
public abstract static class DebugPrintRopeNode extends CoreMethodArrayArgumentsNode {

@Child private RopeNodes.DebugPrintRopeNode debugPrintRopeNode;

public DebugPrintRopeNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
debugPrintRopeNode = RopeNodesFactory.DebugPrintRopeNodeGen.create(context, sourceSection, null, null, null);
}

@TruffleBoundary
@Specialization(guards = "isRubyString(string)")
public DynamicObject debugPrintDefault(DynamicObject string, NotProvided printString) {
return debugPrint(string, true);
}

@TruffleBoundary
@Specialization(guards = "isRubyString(string)")
public DynamicObject debugPrint(DynamicObject string, boolean printString) {
System.err.println("Legend: ");
System.err.println("BN = Bytes Null? (byte[] not yet populated)");
System.err.println("BL = Byte Length");
System.err.println("CL = Character Length");
System.err.println("CR = Code Range");
System.err.println("O = Offset (SubstringRope only)");
System.err.println("D = Depth");
System.err.println("LD = Left Depth (ConcatRope only)");
System.err.println("RD = Right Depth (ConcatRope only)");

return debugPrintRopeNode.executeDebugPrint(StringOperations.rope(string), 0, printString);
}

}

@CoreMethod(names = "flatten_rope", onSingleton = true, required = 1)
public abstract static class FlattenRopeNode extends CoreMethodArrayArgumentsNode {

public FlattenRopeNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}

@TruffleBoundary
@Specialization(guards = "isRubyString(string)")
public DynamicObject debugPrint(DynamicObject string) {
final Rope flattened = RopeOperations.flatten(StringOperations.rope(string));

return createString(flattened);
}

}

@CoreMethod(names = "jruby_home_directory", onSingleton = true)
public abstract static class JRubyHomeDirectoryNode extends CoreMethodNode {

@@ -809,7 +863,7 @@ public CreateSimpleStringNode(RubyContext context, SourceSection sourceSection)

@Specialization
public DynamicObject createSimpleString() {
return createString(new ByteList(new byte[]{'t', 'e', 's', 't'}, false));
return createString(RopeOperations.create(new byte[]{'t', 'e', 's', 't'}, UTF8Encoding.INSTANCE, StringSupport.CR_7BIT));
}
}

Original file line number Diff line number Diff line change
@@ -62,6 +62,7 @@
import org.jruby.truffle.runtime.methods.Arity;
import org.jruby.truffle.runtime.methods.InternalMethod;
import org.jruby.truffle.runtime.methods.SharedMethodInfo;
import org.jruby.truffle.runtime.rope.Rope;
import org.jruby.util.ByteList;
import org.jruby.util.Memo;

@@ -2403,13 +2404,13 @@ public PackNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}

@Specialization(guards = {"isRubyString(format)", "byteListsEqual(format, cachedFormat)"}, limit = "getCacheLimit()")
@Specialization(guards = {"isRubyString(format)", "ropesEqual(format, cachedFormat)"}, limit = "getCacheLimit()")
public DynamicObject packCached(
VirtualFrame frame,
DynamicObject array,
DynamicObject format,
@Cached("privatizeByteList(format)") ByteList cachedFormat,
@Cached("byteListLength(cachedFormat)") int cachedFormatLength,
@Cached("privatizeRope(format)") Rope cachedFormat,
@Cached("ropeLength(cachedFormat)") int cachedFormatLength,
@Cached("create(compileFormat(format))") DirectCallNode callPackNode) {
final PackResult result;

@@ -2438,7 +2439,7 @@ public DynamicObject packUncached(
throw handleException(e);
}

return finishPack(StringOperations.getByteList(format).length(), result);
return finishPack(Layouts.STRING.getRope(format).byteLength(), result);
}

private RuntimeException handleException(PackException exception) {
Original file line number Diff line number Diff line change
@@ -44,7 +44,7 @@ public CachedDispatchNode(
if (RubyGuards.isRubySymbol(cachedName)) {
cachedNameAsSymbol = (DynamicObject) cachedName;
} else if (RubyGuards.isRubyString(cachedName)) {
cachedNameAsSymbol = context.getSymbol(StringOperations.getByteList((DynamicObject) cachedName));
cachedNameAsSymbol = context.getSymbol(StringOperations.rope((DynamicObject) cachedName));
} else if (cachedName instanceof String) {
cachedNameAsSymbol = context.getSymbol((String) cachedName);
} else {
@@ -72,7 +72,7 @@ protected final boolean guardName(Object methodName) {
// TODO(CS, 11-Jan-15) this just repeats the above guard...
return cachedName == methodName;
} else if (RubyGuards.isRubyString(cachedName)) {
return (RubyGuards.isRubyString(methodName)) && StringOperations.getByteList((DynamicObject) cachedName).equal(StringOperations.getByteList((DynamicObject) methodName));
return (RubyGuards.isRubyString(methodName)) && StringOperations.rope((DynamicObject) cachedName).equals(StringOperations.rope((DynamicObject) methodName));
} else {
throw new UnsupportedOperationException();
}
Original file line number Diff line number Diff line change
@@ -21,6 +21,7 @@
import org.jruby.truffle.runtime.core.StringOperations;
import org.jruby.truffle.runtime.layouts.Layouts;
import org.jruby.truffle.runtime.layouts.ext.DigestLayoutImpl;
import org.jruby.truffle.runtime.rope.Rope;
import org.jruby.util.ByteList;

import java.security.MessageDigest;
@@ -146,8 +147,9 @@ public UpdateNode(RubyContext context, SourceSection sourceSection) {
@TruffleBoundary
@Specialization(guards = "isRubyString(message)")
public DynamicObject update(DynamicObject digestObject, DynamicObject message) {
final ByteList bytes = StringOperations.getByteList(message);
DigestLayoutImpl.INSTANCE.getDigest(digestObject).update(bytes.getUnsafeBytes(), bytes.begin(), bytes.length());
final Rope rope = StringOperations.rope(message);

DigestLayoutImpl.INSTANCE.getDigest(digestObject).update(rope.getBytes(), rope.begin(), rope.byteLength());
return digestObject;
}

@@ -221,8 +223,9 @@ public BubbleBabbleNode(RubyContext context, SourceSection sourceSection) {
@TruffleBoundary
@Specialization(guards = "isRubyString(message)")
public DynamicObject bubblebabble(DynamicObject message) {
final ByteList byteList = StringOperations.getByteList(message);
return createString(BubbleBabble.bubblebabble(byteList.unsafeBytes(), byteList.begin(), byteList.length()));
final Rope rope = StringOperations.rope(message);

return createString(BubbleBabble.bubblebabble(rope.getBytes(), rope.begin(), rope.byteLength()));
}

}
Original file line number Diff line number Diff line change
@@ -70,7 +70,7 @@ public int memsizeOfHash(DynamicObject object) {

@Specialization(guards = "isRubyString(object)")
public int memsizeOfString(DynamicObject object) {
return 1 + object.getShape().getPropertyListInternal(false).size() + StringOperations.getByteList(object).getRealSize();
return 1 + object.getShape().getPropertyListInternal(false).size() + StringOperations.rope(object).byteLength();
}

@Specialization(guards = "isRubyMatchData(object)")
Original file line number Diff line number Diff line change
@@ -215,7 +215,7 @@ private StreamReader readerFor(VirtualFrame frame, DynamicObject yaml) {
return new StreamReader(new InputStreamReader(new InputStreamAdapter(getContext(), yaml), charset));
}

ByteList byteList = StringOperations.getByteList(toStrNode.coerceObject(frame, yaml));
ByteList byteList = StringOperations.getByteListReadOnly(toStrNode.coerceObject(frame, yaml));
Encoding enc = byteList.getEncoding();

// if not unicode, transcode to UTF8
Original file line number Diff line number Diff line change
@@ -261,7 +261,7 @@ public InteropStringIsBoxed(RubyContext context, SourceSection sourceSection) {
@Override
public Object execute(VirtualFrame frame) {
Object o = ForeignAccess.getReceiver(frame);
return RubyGuards.isRubyString(o) && StringOperations.getByteList(((DynamicObject) o)).length() == 1;
return RubyGuards.isRubyString(o) && StringOperations.rope((DynamicObject) o).byteLength() == 1;
}
}

@@ -273,7 +273,7 @@ public InteropStringUnboxNode(RubyContext context, SourceSection sourceSection)

@Override
public Object execute(VirtualFrame frame) {
return StringOperations.getByteList(((DynamicObject) ForeignAccess.getReceiver(frame))).get(0);
return StringOperations.getByteListReadOnly(((DynamicObject) ForeignAccess.getReceiver(frame))).get(0);
}
}

@@ -367,10 +367,10 @@ public Object execute(VirtualFrame frame) {
if (RubyGuards.isRubyString(ForeignAccess.getReceiver(frame))) {
final DynamicObject string = (DynamicObject) ForeignAccess.getReceiver(frame);
final int index = (int) ForeignAccess.getArguments(frame).get(labelIndex);
if (index >= StringOperations.getByteList(string).length()) {
if (index >= Layouts.STRING.getRope(string).byteLength()) {
return 0;
} else {
return (byte) StringOperations.getByteList(string).get(index);
return (byte) StringOperations.getByteListReadOnly(string).get(index);
}
} else {
CompilerDirectives.transferToInterpreter();
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, 2015 Oracle and/or its affiliates. All rights reserved. This
* Copyright (c) 2013, 2015, 2016 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:
*
@@ -9,41 +9,32 @@
*/
package org.jruby.truffle.nodes.literal;

import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.object.DynamicObject;
import com.oracle.truffle.api.source.SourceSection;
import org.jcodings.Encoding;
import org.jruby.truffle.nodes.RubyNode;
import org.jruby.truffle.nodes.objects.AllocateObjectNode;
import org.jruby.truffle.nodes.objects.AllocateObjectNodeGen;
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.rope.Rope;
import org.jruby.util.ByteList;

import java.util.Arrays;

public class StringLiteralNode extends RubyNode {

private @CompilationFinal byte[] bytes; // deeply immutable
private final Encoding encoding;
private final int codeRange;
private final Rope rope;

@Child private AllocateObjectNode allocateObjectNode;

public StringLiteralNode(RubyContext context, SourceSection sourceSection, ByteList byteList, int codeRange) {
super(context, sourceSection);
assert byteList != null;
this.bytes = byteList.bytes();
this.encoding = byteList.getEncoding();
this.codeRange = codeRange;
this.rope = context.getRopeTable().getRope(byteList.bytes(), byteList.getEncoding(), codeRange);
allocateObjectNode = AllocateObjectNodeGen.create(context, sourceSection, false, null, null);
}

@Override
public DynamicObject execute(VirtualFrame frame) {
final byte[] copy = Arrays.copyOf(bytes, bytes.length);
final ByteList byteList = new ByteList(copy, encoding, false);
return allocateObjectNode.allocate(getContext().getCoreLibrary().getStringClass(), byteList, codeRange, null);
return allocateObjectNode.allocate(getContext().getCoreLibrary().getStringClass(), rope, null);
}

}
Original file line number Diff line number Diff line change
@@ -11,17 +11,20 @@

import com.oracle.truffle.api.CompilerDirectives;
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.VirtualFrame;
import com.oracle.truffle.api.object.DynamicObject;
import com.oracle.truffle.api.object.DynamicObjectFactory;
import com.oracle.truffle.api.source.SourceSection;
import com.oracle.truffle.api.utilities.ConditionProfile;
import org.jruby.truffle.nodes.RubyNode;
import org.jruby.truffle.nodes.core.*;
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.control.RaiseException;
import org.jruby.truffle.runtime.core.StringOperations;
import org.jruby.truffle.runtime.layouts.Layouts;
import org.jruby.truffle.runtime.rope.Rope;
import org.jruby.util.ByteList;

@CoreClass(name = "Rubinius::ByteArray")
@@ -39,8 +42,21 @@ public GetByteNode(RubyContext context, SourceSection sourceSection) {
}

@Specialization
public int getByte(DynamicObject bytes, int index) {
return Layouts.BYTE_ARRAY.getBytes(bytes).get(index) & 0xff;
public int getByte(DynamicObject bytes, int index,
@Cached("createBinaryProfile()") ConditionProfile nullByteIndexProfile) {
final ByteList byteList = Layouts.BYTE_ARRAY.getBytes(bytes);

// Handling out-of-bounds issues like this is non-standard. In Rubinius, it would raise an exception instead.
// We're modifying the semantics to address a primary use case for this class: Rubinius's @data array
// in the String class. Rubinius Strings are NULL-terminated and their code working with Strings takes
// advantage of that fact. So, where they expect to receive a NULL byte, we'd be out-of-bounds and raise
// an exception. Simply appending a NULL byte may trigger a full copy of the original byte[], which we
// want to avoid. The compromise is bending on the semantics here.
if (nullByteIndexProfile.profile(index == byteList.realSize())) {
return 0;
}

return byteList.get(index) & 0xff;
}

}
@@ -54,11 +70,12 @@ public PrependNode(RubyContext context, SourceSection sourceSection) {

@Specialization(guards = "isRubyString(string)")
public DynamicObject prepend(DynamicObject bytes, DynamicObject string) {
final int prependLength = StringOperations.getByteList(string).getUnsafeBytes().length;
final Rope rope = StringOperations.rope(string);
final int prependLength = rope.byteLength();
final int originalLength = Layouts.BYTE_ARRAY.getBytes(bytes).getUnsafeBytes().length;
final int newLength = prependLength + originalLength;
final byte[] prependedBytes = new byte[newLength];
System.arraycopy(StringOperations.getByteList(string).getUnsafeBytes(), 0, prependedBytes, 0, prependLength);
System.arraycopy(rope.getBytes(), 0, prependedBytes, 0, prependLength);
System.arraycopy(Layouts.BYTE_ARRAY.getBytes(bytes).getUnsafeBytes(), 0, prependedBytes, prependLength, originalLength);
return ByteArrayNodes.createByteArray(getContext().getCoreLibrary().getByteArrayFactory(), new ByteList(prependedBytes));
}
@@ -102,21 +119,18 @@ public int size(DynamicObject bytes) {
@CoreMethod(names = "locate", required = 3, lowerFixnumParameters = {1, 2})
public abstract static class LocateNode extends CoreMethodArrayArgumentsNode {

@Child private StringNodes.SizeNode sizeNode;

public LocateNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
sizeNode = StringNodesFactory.SizeNodeFactory.create(context, sourceSection, new RubyNode[] {});
}

@Specialization(guards = "isRubyString(pattern)")
public Object getByte(VirtualFrame frame, DynamicObject bytes, DynamicObject pattern, int start, int length) {
final int index = new ByteList(Layouts.BYTE_ARRAY.getBytes(bytes), start, length).indexOf(StringOperations.getByteList(pattern));
public Object getByte(DynamicObject bytes, DynamicObject pattern, int start, int length) {
final int index = new ByteList(Layouts.BYTE_ARRAY.getBytes(bytes), start, length).indexOf(StringOperations.getByteListReadOnly(pattern));

if (index == -1) {
return nil();
} else {
return start + index + sizeNode.executeInteger(frame, pattern);
return start + index + StringOperations.rope(pattern).characterLength();
}
}

Original file line number Diff line number Diff line change
@@ -23,14 +23,19 @@
import org.jcodings.transcode.EConvResult;
import org.jruby.truffle.nodes.RubyGuards;
import org.jruby.truffle.nodes.core.EncodingConverterNodes;
import org.jruby.truffle.nodes.core.RopeNodes;
import org.jruby.truffle.nodes.core.RopeNodesFactory;
import org.jruby.truffle.nodes.dispatch.CallDispatchHeadNode;
import org.jruby.truffle.nodes.dispatch.DispatchHeadNodeFactory;
import org.jruby.truffle.runtime.NotProvided;
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.control.RaiseException;
import org.jruby.truffle.runtime.core.StringOperations;
import org.jruby.truffle.runtime.layouts.Layouts;
import org.jruby.truffle.runtime.rope.Rope;
import org.jruby.truffle.runtime.rope.RopeOperations;
import org.jruby.util.ByteList;
import static org.jruby.truffle.runtime.core.StringOperations.rope;

/**
* Rubinius primitives associated with the Ruby {@code Encoding::Converter} class..
@@ -54,10 +59,11 @@ public Object encodingConverterAllocate(DynamicObject encodingConverterClass, No
@RubiniusPrimitive(name = "encoding_converter_primitive_convert")
public static abstract class PrimitiveConvertNode extends RubiniusPrimitiveNode {

private final ConditionProfile nonNullSourceProfile = ConditionProfile.createBinaryProfile();
@Child private RopeNodes.MakeSubstringNode makeSubstringNode;

public PrimitiveConvertNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
makeSubstringNode = RopeNodesFactory.MakeSubstringNodeGen.create(context, sourceSection, null, null, null);
}

@Specialization(guards = {"isRubyString(source)", "isRubyString(target)", "isRubyHash(options)"})
@@ -69,7 +75,7 @@ public Object encodingConverterPrimitiveConvert(DynamicObject encodingConverter,
@Specialization(guards = {"isNil(source)", "isRubyString(target)"})
public Object primitiveConvertNilSource(DynamicObject encodingConverter, DynamicObject source,
DynamicObject target, int offset, int size, int options) {
return primitiveConvertHelper(encodingConverter, new ByteList(), source, target, offset, size, options);
return primitiveConvertHelper(encodingConverter, source, target, offset, size, options);
}

@Specialization(guards = {"isRubyString(source)", "isRubyString(target)"})
@@ -78,23 +84,18 @@ public Object encodingConverterPrimitiveConvert(DynamicObject encodingConverter,

// Taken from org.jruby.RubyConverter#primitive_convert.

StringOperations.modify(source);
StringOperations.clearCodeRange(source);

return primitiveConvertHelper(encodingConverter, StringOperations.getByteList(source), source, target, offset, size, options);
return primitiveConvertHelper(encodingConverter, source, target, offset, size, options);
}

@TruffleBoundary
private Object primitiveConvertHelper(DynamicObject encodingConverter, ByteList inBytes, DynamicObject source,
private Object primitiveConvertHelper(DynamicObject encodingConverter, DynamicObject source,
DynamicObject target, int offset, int size, int options) {
// Taken from org.jruby.RubyConverter#primitive_convert.

final boolean nonNullSource = source != nil();

StringOperations.modify(target);
StringOperations.clearCodeRange(target);

final ByteList outBytes = StringOperations.getByteList(target);
Rope sourceRope = nonNullSource ? rope(source) : RopeOperations.EMPTY_UTF8_ROPE;
final Rope targetRope = rope(target);
final ByteList outBytes = targetRope.toByteListCopy();

final Ptr inPtr = new Ptr();
final Ptr outPtr = new Ptr();
@@ -107,9 +108,9 @@ private Object primitiveConvertHelper(DynamicObject encodingConverter, ByteList
if (size == -1) {
size = 16; // in MRI, this is RSTRING_EMBED_LEN_MAX

if (nonNullSourceProfile.profile(nonNullSource)) {
if (size < StringOperations.getByteList(source).getRealSize()) {
size = StringOperations.getByteList(source).getRealSize();
if (nonNullSource) {
if (size < sourceRope.byteLength()) {
size = sourceRope.byteLength();
}
}
}
@@ -137,16 +138,16 @@ private Object primitiveConvertHelper(DynamicObject encodingConverter, ByteList

outBytes.ensure((int) outputByteEnd);

inPtr.p = inBytes.getBegin();
outPtr.p = outBytes.getBegin() + offset;
inPtr.p = 0;
outPtr.p = offset;
int os = outPtr.p + size;
EConvResult res = ec.convert(inBytes.getUnsafeBytes(), inPtr, inBytes.getRealSize() + inPtr.p, outBytes.getUnsafeBytes(), outPtr, os, options);
EConvResult res = ec.convert(sourceRope.getBytes(), inPtr, sourceRope.byteLength() + inPtr.p, outBytes.getUnsafeBytes(), outPtr, os, options);

outBytes.setRealSize(outPtr.p - outBytes.begin());

if (nonNullSourceProfile.profile(nonNullSource)) {
StringOperations.getByteList(source).setRealSize(inBytes.getRealSize() - (inPtr.p - inBytes.getBegin()));
StringOperations.getByteList(source).setBegin(inPtr.p);
if (nonNullSource) {
sourceRope = makeSubstringNode.executeMake(sourceRope, inPtr.p, sourceRope.byteLength() - inPtr.p);
StringOperations.setRope(source, sourceRope);
}

if (growOutputBuffer && res == EConvResult.DestinationBufferFull) {
@@ -163,6 +164,8 @@ private Object primitiveConvertHelper(DynamicObject encodingConverter, ByteList
outBytes.setEncoding(ec.destinationEncoding);
}

StringOperations.setRope(target, StringOperations.ropeFromByteList(outBytes));

return getSymbol(res.symbolicName());
}
}
Original file line number Diff line number Diff line change
@@ -31,12 +31,12 @@ public EncodingGetObjectEncodingNode(RubyContext context, SourceSection sourceSe

@Specialization(guards = "isRubyString(string)")
public DynamicObject encodingGetObjectEncodingString(DynamicObject string) {
return EncodingNodes.getEncoding(StringOperations.getByteList(string).getEncoding());
return EncodingNodes.getEncoding(Layouts.STRING.getRope(string).getEncoding());
}

@Specialization(guards = "isRubySymbol(symbol)")
public DynamicObject encodingGetObjectEncodingSymbol(DynamicObject symbol) {
return EncodingNodes.getEncoding(Layouts.SYMBOL.getByteList(symbol).getEncoding());
return EncodingNodes.getEncoding(Layouts.SYMBOL.getRope(symbol).getEncoding());
}

@Specialization(guards = "isRubyEncoding(encoding)")
Original file line number Diff line number Diff line change
@@ -51,6 +51,7 @@
import org.jruby.truffle.runtime.control.RaiseException;
import org.jruby.truffle.runtime.core.StringOperations;
import org.jruby.truffle.runtime.layouts.Layouts;
import org.jruby.truffle.runtime.rope.Rope;
import org.jruby.util.ByteList;

public abstract class IOBufferPrimitiveNodes {
@@ -90,8 +91,8 @@ public IOBufferUnshiftPrimitiveNode(RubyContext context, SourceSection sourceSec
public int unshift(VirtualFrame frame, DynamicObject ioBuffer, DynamicObject string, int startPosition) {
Layouts.IO_BUFFER.setWriteSynced(ioBuffer, false);

final ByteList byteList = StringOperations.getByteList(string);
int stringSize = byteList.realSize() - startPosition;
final Rope rope = StringOperations.rope(string);
int stringSize = rope.byteLength() - startPosition;
final int usedSpace = Layouts.IO_BUFFER.getUsed(ioBuffer);
final int availableSpace = IOBUFFER_SIZE - usedSpace;

@@ -102,7 +103,7 @@ public int unshift(VirtualFrame frame, DynamicObject ioBuffer, DynamicObject str
ByteList storage = Layouts.BYTE_ARRAY.getBytes(Layouts.IO_BUFFER.getStorage(ioBuffer));

// Data is copied here - can we do something COW?
System.arraycopy(byteList.unsafeBytes(), byteList.begin() + startPosition, storage.getUnsafeBytes(), storage.begin() + usedSpace, stringSize);
System.arraycopy(rope.getBytes(), startPosition, storage.getUnsafeBytes(), storage.begin() + usedSpace, stringSize);

Layouts.IO_BUFFER.setUsed(ioBuffer, usedSpace + stringSize);

Original file line number Diff line number Diff line change
@@ -205,8 +205,8 @@ public IOFNMatchPrimitiveNode(RubyContext context, SourceSection sourceSection)
@TruffleBoundary
@Specialization(guards = {"isRubyString(pattern)", "isRubyString(path)"})
public boolean fnmatch(DynamicObject pattern, DynamicObject path, int flags) {
final ByteList patternBytes = StringOperations.getByteList(pattern);
final ByteList pathBytes = StringOperations.getByteList(path);
final ByteList patternBytes = StringOperations.getByteListReadOnly(pattern);
final ByteList pathBytes = StringOperations.getByteListReadOnly(path);
return Dir.fnmatch(patternBytes.getUnsafeBytes(),
patternBytes.getBegin(),
patternBytes.getBegin() + patternBytes.getRealSize(),
@@ -404,7 +404,7 @@ public IOWritePrimitiveNode(RubyContext context, SourceSection sourceSection) {
public int write(DynamicObject file, DynamicObject string) {
final int fd = Layouts.IO.getDescriptor(file);

final ByteList byteList = StringOperations.getByteList(string);
final ByteList byteList = StringOperations.getByteListReadOnly(string);

if (getContext().getDebugStandardOut() != null && fd == STDOUT) {
getContext().getDebugStandardOut().write(byteList.unsafeBytes(), byteList.begin(), byteList.length());
Original file line number Diff line number Diff line change
@@ -24,6 +24,7 @@
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.core.StringOperations;
import org.jruby.truffle.runtime.layouts.Layouts;
import org.jruby.truffle.runtime.rope.Rope;
import org.jruby.truffle.runtime.rubinius.RubiniusTypes;
import org.jruby.util.ByteList;
import org.jruby.util.unsafe.UnsafeHolder;
@@ -338,9 +339,9 @@ public PointerWriteStringPrimitiveNode(RubyContext context, SourceSection source

@Specialization(guards = "isRubyString(string)")
public DynamicObject address(DynamicObject pointer, DynamicObject string, int maxLength) {
final ByteList bytes = StringOperations.getByteList(string);
final int length = Math.min(bytes.length(), maxLength);
Layouts.POINTER.getPointer(pointer).put(0, bytes.unsafeBytes(), bytes.begin(), length);
final Rope rope = StringOperations.rope(string);
final int length = Math.min(rope.byteLength(), maxLength);
Layouts.POINTER.getPointer(pointer).put(0, rope.getBytes(), 0, length);
return pointer;
}

Original file line number Diff line number Diff line change
@@ -17,18 +17,21 @@
import jnr.constants.platform.Fcntl;
import jnr.ffi.Pointer;
import org.jcodings.specific.UTF8Encoding;
import org.jruby.RubyEncoding;
import org.jruby.platform.Platform;
import org.jruby.truffle.nodes.core.CoreClass;
import org.jruby.truffle.nodes.core.CoreMethod;
import org.jruby.truffle.nodes.core.CoreMethodArrayArgumentsNode;
import org.jruby.truffle.nodes.core.RopeNodes;
import org.jruby.truffle.nodes.core.RopeNodesFactory;
import org.jruby.truffle.nodes.objects.AllocateObjectNode;
import org.jruby.truffle.nodes.objects.AllocateObjectNodeGen;
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.control.RaiseException;
import org.jruby.truffle.runtime.core.StringOperations;
import org.jruby.truffle.runtime.layouts.Layouts;
import org.jruby.util.ByteList;
import org.jruby.truffle.runtime.rope.RopeOperations;
import org.jruby.util.StringSupport;
import static org.jruby.truffle.runtime.core.StringOperations.decodeUTF8;

import java.nio.charset.StandardCharsets;

@@ -45,7 +48,7 @@ public AccessNode(RubyContext context, SourceSection sourceSection) {
@CompilerDirectives.TruffleBoundary
@Specialization(guards = "isRubyString(path)")
public int access(DynamicObject path, int mode) {
final String pathString = RubyEncoding.decodeUTF8(StringOperations.getByteList(path).getUnsafeBytes(), StringOperations.getByteList(path).getBegin(), StringOperations.getByteList(path).getRealSize());
final String pathString = decodeUTF8(path);
return posix().access(pathString, mode);
}

@@ -61,7 +64,7 @@ public ChmodNode(RubyContext context, SourceSection sourceSection) {
@CompilerDirectives.TruffleBoundary
@Specialization(guards = "isRubyString(path)")
public int chmod(DynamicObject path, int mode) {
return posix().chmod(path.toString(), mode);
return posix().chmod(decodeUTF8(path), mode);
}

}
@@ -76,7 +79,7 @@ public ChownNode(RubyContext context, SourceSection sourceSection) {
@CompilerDirectives.TruffleBoundary
@Specialization(guards = "isRubyString(path)")
public int chown(DynamicObject path, int owner, int group) {
return posix().chown(path.toString(), owner, group);
return posix().chown(decodeUTF8(path), owner, group);
}

}
@@ -179,9 +182,7 @@ public GetenvNode(RubyContext context, SourceSection sourceSection) {
@CompilerDirectives.TruffleBoundary
@Specialization(guards = "isRubyString(name)")
public DynamicObject getenv(DynamicObject name) {
final String nameString = RubyEncoding.decodeUTF8(StringOperations.getByteList(name).getUnsafeBytes(), StringOperations.getByteList(name).getBegin(), StringOperations.getByteList(name).getRealSize());

Object result = posix().getenv(nameString);
Object result = posix().getenv(decodeUTF8(name));

if (result == null) {
return nil();
@@ -330,10 +331,7 @@ public ReadlinkNode(RubyContext context, SourceSection sourceSection) {
@CompilerDirectives.TruffleBoundary
@Specialization(guards = {"isRubyString(path)", "isRubyPointer(pointer)"})
public int readlink(DynamicObject path, DynamicObject pointer, int bufsize) {
final ByteList byteList = StringOperations.getByteList(path);
final String pathString = RubyEncoding.decodeUTF8(byteList.unsafeBytes(), byteList.begin(), byteList.length());

final int result = posix().readlink(pathString, Layouts.POINTER.getPointer(pointer), bufsize);
final int result = posix().readlink(decodeUTF8(path), Layouts.POINTER.getPointer(pointer), bufsize);
if (result == -1) {
CompilerDirectives.transferToInterpreter();
throw new RaiseException(getContext().getCoreLibrary().errnoError(posix().errno(), this));
@@ -354,10 +352,7 @@ public SetenvNode(RubyContext context, SourceSection sourceSection) {
@CompilerDirectives.TruffleBoundary
@Specialization(guards = { "isRubyString(name)", "isRubyString(value)" })
public int setenv(DynamicObject name, DynamicObject value, int overwrite) {
final String nameString = RubyEncoding.decodeUTF8(StringOperations.getByteList(name).getUnsafeBytes(), StringOperations.getByteList(name).getBegin(), StringOperations.getByteList(name).getRealSize());
final String valueString = RubyEncoding.decodeUTF8(StringOperations.getByteList(value).getUnsafeBytes(), StringOperations.getByteList(value).getBegin(), StringOperations.getByteList(value).getRealSize());

return posix().setenv(nameString, valueString, overwrite);
return posix().setenv(decodeUTF8(name), decodeUTF8(value), overwrite);
}

}
@@ -372,9 +367,7 @@ public LinkNode(RubyContext context, SourceSection sourceSection) {
@CompilerDirectives.TruffleBoundary
@Specialization(guards = {"isRubyString(path)", "isRubyString(other)"})
public int link(DynamicObject path, DynamicObject other) {
final String pathString = RubyEncoding.decodeUTF8(StringOperations.getByteList(path).getUnsafeBytes(), StringOperations.getByteList(path).getBegin(), StringOperations.getByteList(path).getRealSize());
final String otherString = RubyEncoding.decodeUTF8(StringOperations.getByteList(other).getUnsafeBytes(), StringOperations.getByteList(other).getBegin(), StringOperations.getByteList(other).getRealSize());
return posix().link(pathString, otherString);
return posix().link(decodeUTF8(path), decodeUTF8(other));
}

}
@@ -389,8 +382,7 @@ public UnlinkNode(RubyContext context, SourceSection sourceSection) {
@CompilerDirectives.TruffleBoundary
@Specialization(guards = "isRubyString(path)")
public int unlink(DynamicObject path) {
final ByteList byteList = StringOperations.getByteList(path);
return posix().unlink(RubyEncoding.decodeUTF8(byteList.getUnsafeBytes(), byteList.getBegin(), byteList.getRealSize()));
return posix().unlink(decodeUTF8(path));
}

}
@@ -419,10 +411,7 @@ public UnsetenvNode(RubyContext context, SourceSection sourceSection) {
@CompilerDirectives.TruffleBoundary
@Specialization(guards = "isRubyString(name)")
public int unsetenv(DynamicObject name) {
final ByteList byteList = StringOperations.getByteList(name);
final String nameString = RubyEncoding.decodeUTF8(byteList.getUnsafeBytes(), byteList.getBegin(), byteList.getRealSize());

return posix().unsetenv(nameString);
return posix().unsetenv(decodeUTF8(name));
}

}
@@ -437,10 +426,7 @@ public UtimesNode(RubyContext context, SourceSection sourceSection) {
@CompilerDirectives.TruffleBoundary
@Specialization(guards = {"isRubyString(path)", "isRubyPointer(pointer)"})
public int utimes(DynamicObject path, DynamicObject pointer) {
final ByteList byteList = StringOperations.getByteList(path);
final String pathString = RubyEncoding.decodeUTF8(byteList.getUnsafeBytes(), byteList.getBegin(), byteList.getRealSize());

final int result = posix().utimes(pathString, Layouts.POINTER.getPointer(pointer));
final int result = posix().utimes(decodeUTF8(path), Layouts.POINTER.getPointer(pointer));
if (result == -1) {
CompilerDirectives.transferToInterpreter();
throw new RaiseException(getContext().getCoreLibrary().errnoError(posix().errno(), this));
@@ -691,9 +677,7 @@ public RenameNode(RubyContext context, SourceSection sourceSection) {
@CompilerDirectives.TruffleBoundary
@Specialization(guards = {"isRubyString(path)", "isRubyString(other)"})
public int rename(DynamicObject path, DynamicObject other) {
final String pathString = RubyEncoding.decodeUTF8(StringOperations.getByteList(path).getUnsafeBytes(), StringOperations.getByteList(path).getBegin(), StringOperations.getByteList(path).getRealSize());
final String otherString = RubyEncoding.decodeUTF8(StringOperations.getByteList(other).getUnsafeBytes(), StringOperations.getByteList(other).getBegin(), StringOperations.getByteList(other).getRealSize());
return posix().rename(pathString, otherString);
return posix().rename(decodeUTF8(path), decodeUTF8(other));
}

}
@@ -716,8 +700,11 @@ public int rmdir(DynamicObject path) {
@CoreMethod(names = "getcwd", isModuleFunction = true, required = 2)
public abstract static class GetcwdNode extends CoreMethodArrayArgumentsNode {

@Child private RopeNodes.MakeLeafRopeNode makeLeafRopeNode;

public GetcwdNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
makeLeafRopeNode = RopeNodesFactory.MakeLeafRopeNodeGen.create(context, sourceSection, null, null, null);
}

@CompilerDirectives.TruffleBoundary
@@ -726,7 +713,7 @@ public DynamicObject getcwd(DynamicObject resultPath, int maxSize) {
// We just ignore maxSize - I think this is ok

final String path = getContext().getRuntime().getCurrentDirectory();
StringOperations.getByteList(resultPath).replace(path.getBytes(StandardCharsets.UTF_8));
StringOperations.setRope(resultPath, makeLeafRopeNode.executeMake(path.getBytes(StandardCharsets.UTF_8), Layouts.STRING.getRope(resultPath).getEncoding(), StringSupport.CR_UNKNOWN));
return resultPath;
}

@@ -870,8 +857,8 @@ public int getaddrinfoNil(DynamicObject hostName, DynamicObject serviceName, Dyn
@Specialization(guards = {"isRubyString(hostName)", "isRubyString(serviceName)", "isRubyPointer(hintsPointer)", "isRubyPointer(resultsPointer)"})
public int getaddrinfoString(DynamicObject hostName, DynamicObject serviceName, DynamicObject hintsPointer, DynamicObject resultsPointer) {
return nativeSockets().getaddrinfo(
StringOperations.getByteList(hostName),
StringOperations.getByteList(serviceName),
StringOperations.getByteListReadOnly(hostName),
StringOperations.getByteListReadOnly(serviceName),
Layouts.POINTER.getPointer(hintsPointer),
Layouts.POINTER.getPointer(resultsPointer));
}
@@ -880,7 +867,7 @@ public int getaddrinfoString(DynamicObject hostName, DynamicObject serviceName,
@Specialization(guards = {"isRubyString(hostName)", "isNil(serviceName)", "isRubyPointer(hintsPointer)", "isRubyPointer(resultsPointer)"})
public int getaddrinfo(DynamicObject hostName, DynamicObject serviceName, DynamicObject hintsPointer, DynamicObject resultsPointer) {
return nativeSockets().getaddrinfo(
StringOperations.getByteList(hostName),
StringOperations.getByteListReadOnly(hostName),
null,
Layouts.POINTER.getPointer(hintsPointer),
Layouts.POINTER.getPointer(resultsPointer));
Original file line number Diff line number Diff line change
@@ -24,6 +24,7 @@
import org.jruby.truffle.runtime.control.RaiseException;
import org.jruby.truffle.runtime.core.StringOperations;
import org.jruby.truffle.runtime.layouts.Layouts;
import org.jruby.truffle.runtime.rope.Rope;
import org.jruby.util.ByteList;
import org.jruby.util.RegexpSupport;

@@ -69,7 +70,7 @@ public DynamicObject initializeAlreadyInitialized(DynamicObject regexp, DynamicO

@Specialization(guards = {"!isRegexpLiteral(regexp)", "!isInitialized(regexp)", "isRubyString(pattern)"})
public DynamicObject initialize(DynamicObject regexp, DynamicObject pattern, int options) {
RegexpNodes.initialize(getContext(), regexp, this, StringOperations.getByteList(pattern), options);
RegexpNodes.initialize(getContext(), regexp, this, StringOperations.rope(pattern), options);
return regexp;
}

@@ -129,26 +130,26 @@ public Object searchRegionNotInitialized(DynamicObject regexp, DynamicObject str
public Object searchRegionInvalidEncoding(DynamicObject regexp, DynamicObject string, int start, int end, boolean forward) {
CompilerDirectives.transferToInterpreter();
throw new RaiseException(getContext().getCoreLibrary().argumentError(
String.format("invalid byte sequence in %s", StringOperations.getByteList(string).getEncoding()), this));
String.format("invalid byte sequence in %s", Layouts.STRING.getRope(string).getEncoding()), this));
}

@TruffleBoundary
@Specialization(guards = {"isInitialized(regexp)", "isRubyString(string)", "isValidEncoding(string)"})
public Object searchRegion(DynamicObject regexp, DynamicObject string, int start, int end, boolean forward) {
final ByteList stringBl = StringOperations.getByteList(string);
final ByteList bl = Layouts.REGEXP.getSource(regexp);
final Encoding enc = RegexpNodes.checkEncoding(regexp, StringOperations.getCodeRangeable(string), true);
ByteList preprocessed = RegexpSupport.preprocess(getContext().getRuntime(), bl, enc, new Encoding[]{null}, RegexpSupport.ErrorMode.RAISE);
preprocessed = RegexpNodes.shimModifiers(preprocessed);
final Regex r = new Regex(preprocessed.getUnsafeBytes(), preprocessed.getBegin(), preprocessed.getBegin() + preprocessed.getRealSize(), Layouts.REGEXP.getRegex(regexp).getOptions(), RegexpNodes.checkEncoding(regexp, StringOperations.getCodeRangeable(string), true));
final Matcher matcher = r.matcher(stringBl.getUnsafeBytes(), stringBl.begin(), stringBl.begin() + stringBl.realSize());
final Rope stringRope = StringOperations.rope(string);
final Rope regexpSourceRope = Layouts.REGEXP.getSource(regexp);
final Encoding enc = RegexpNodes.checkEncoding(regexp, stringRope, true);
ByteList preprocessed = RegexpSupport.preprocess(getContext().getRuntime(), regexpSourceRope.getUnsafeByteList(), enc, new Encoding[]{null}, RegexpSupport.ErrorMode.RAISE);
Rope preprocessedRope = RegexpNodes.shimModifiers(StringOperations.ropeFromByteList(preprocessed));
final Regex r = new Regex(preprocessedRope.getBytes(), preprocessedRope.getBegin(), preprocessedRope.getBegin() + preprocessedRope.getRealSize(), Layouts.REGEXP.getRegex(regexp).getOptions(), RegexpNodes.checkEncoding(regexp, stringRope, true));
final Matcher matcher = r.matcher(stringRope.getBytes(), stringRope.begin(), stringRope.begin() + stringRope.realSize());

if (forward) {
// Search forward through the string.
return RegexpNodes.matchCommon(getContext(), regexp, string, false, false, matcher, start + stringBl.begin(), end + stringBl.begin());
return RegexpNodes.matchCommon(getContext(), regexp, string, false, false, matcher, start + stringRope.begin(), end + stringRope.begin());
} else {
// Search backward through the string.
return RegexpNodes.matchCommon(getContext(), regexp, string, false, false, matcher, end + stringBl.begin(), start + stringBl.begin());
return RegexpNodes.matchCommon(getContext(), regexp, string, false, false, matcher, end + stringRope.begin(), start + stringRope.begin());
}
}

Original file line number Diff line number Diff line change
@@ -172,9 +172,7 @@ public StatStatPrimitiveNode(RubyContext context, SourceSection sourceSection) {
@Specialization(guards = "isRubyString(path)")
public int stat(DynamicObject rubyStat, DynamicObject path) {
final FileStat stat = posix().allocateStat();
final ByteList byteList = StringOperations.getByteList(path);
final String pathString = RubyEncoding.decodeUTF8(byteList.getUnsafeBytes(), byteList.getBegin(), byteList.getRealSize());
final int code = posix().stat(pathString, stat);
final int code = posix().stat(StringOperations.decodeUTF8(path), stat);

if (code == 0) {
writeStatNode.execute(rubyStat, stat);
Loading

0 comments on commit e4d7808

Please sign in to comment.