Skip to content

Commit

Permalink
Showing 26 changed files with 368 additions and 151 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -71,6 +71,7 @@ test/prawn
test/rails
test/testapp/testapp
test/truffle/*.methods
test/truffle/integration/gem-testing
tool/nailgun/Makefile
tool/nailgun/config.log
tool/nailgun/config.status
2 changes: 0 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -25,7 +25,6 @@ env:
- JAVA_OPTS="-XX:+TieredCompilation -XX:TieredStopAtLevel=1"
- MALLOC_ARENA_MAX=2
matrix:
- JTRCI='lib/ruby/truffle/jruby+truffle/gem_ci/travis.txt'
- PHASE='-Ptest'
- PHASE='-Prake -Dtask=test:jruby'
- PHASE='-Prake -Dtask=test:jruby:fullint'
@@ -81,7 +80,6 @@ matrix:
- env: JT=check_ambiguous_arguments
jdk: oraclejdk8
- env: JT='test mri'
- env: JTRCI='lib/ruby/truffle/jruby+truffle/gem_ci/travis.txt'
- env: PHASE='-Pmain'
jdk: oraclejdk8
- env: PHASE='-Pj2ee'
12 changes: 5 additions & 7 deletions core/src/main/java/org/jruby/ir/IRBuilder.java
Original file line number Diff line number Diff line change
@@ -1016,12 +1016,10 @@ public Operand buildCall(CallNode callNode) {
return new FrozenString(asString.getValue(), asString.getCodeRange(), asString.getPosition().getFile(), asString.getPosition().getLine());
}

// Though you might be tempted to move this build into the CallInstr as:
// new Callinstr( ... , build(receiverNode, s), ...)
// that is incorrect IR because the receiver has to be built *before* call arguments are built
// The receiver has to be built *before* call arguments are built
// to preserve expected code execution order
Operand receiver = buildWithOrder(receiverNode, callNode.containsVariableAssignment());
Variable callResult = createTemporaryVariable();
Variable callResult = createTemporaryVariable();

ArrayNode argsAry;
if (
@@ -1042,10 +1040,10 @@ public Operand buildCall(CallNode callNode) {
addInstr(new BNilInstr(lazyLabel, receiver));
}

Operand[] args = setupCallArgs(callArgsNode);
Operand block = setupCallClosure(callNode.getIterNode());
Operand[] args = setupCallArgs(callArgsNode);
Operand block = setupCallClosure(callNode.getIterNode());

CallInstr callInstr = CallInstr.create(scope, callResult, callNode.getName(), receiver, args, block);
CallInstr callInstr = CallInstr.create(scope, callResult, callNode.getName(), receiver, args, block);

// This is to support the ugly Proc.new with no block, which must see caller's frame
if ( callNode.getName().equals("new") &&
5 changes: 2 additions & 3 deletions lib/ruby/truffle/jruby+truffle/gem_ci/activesupport.rb
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
subdir 'activesupport'
repository_name 'rails'

unless File.exists? repository_dir
git_clone 'https://github.com/rails/rails.git', branch: '4-2-stable'
end
git_clone 'https://github.com/rails/rails.git' unless File.exists? repository_dir
git_checkout git_tag('4.2.5.2')

use_only_https_git_paths!

Original file line number Diff line number Diff line change
@@ -54,22 +54,16 @@
LoggerTest: [:test_buffer_multibyte],
MultibyteCharsExtrasTest: [:test_titleize_should_be_unicode_aware,
:test_titleize_should_not_affect_characters_that_do_not_case_fold],
TimeZoneTest: [:test_now_enforces_spring_dst_rules],
TransliterateTest: [:test_transliterate_should_allow_a_custom_replacement_char,
:test_transliterate_should_approximate_ascii,
:test_transliterate_should_work_with_custom_i18n_rules_and_uncomposed_utf8],
DurationTest: [:test_eql],
ModuleTest: [:test_delegation_exception_backtrace,
:test_delegation_exception_backtrace_with_allow_nil],
StringInflectionsTest: [:test_string_parameterized_no_separator,
:test_string_parameterized_normal,
:test_string_parameterized_underscore,
:test_titleize],
TimeExtCalculationsTest: [:test_seconds_since_midnight_at_daylight_savings_time_end,
:test_seconds_until_end_of_day_at_daylight_savings_time_end,
:test_time_created_with_local_constructor_cannot_represent_times_during_hour_skipped_by_dst],
TimeWithZoneTest: [:test_change,
:test_no_method_error_has_proper_context] }
:test_seconds_until_end_of_day_at_daylight_savings_time_end],
TimeWithZoneTest: [:test_no_method_error_has_proper_context] }
undefine = if ENV['TRAVIS']
@@ -104,3 +98,4 @@
- date
- bigdecimal
- pathname
- openssl-stubs
23 changes: 13 additions & 10 deletions lib/ruby/truffle/jruby+truffle/lib/runner.rb
Original file line number Diff line number Diff line change
@@ -233,8 +233,10 @@ def initialize(argv = ARGV)
@option_parsers = build_option_parsers

@subcommand, *argv_after_global = @option_parsers[:global].order argv
@called_from_dir = Dir.pwd
@options[:global][:dir] = File.expand_path(@options[:global][:dir] || @called_from_dir)

Dir.chdir dir do
Dir.chdir @options[:global][:dir] do
puts "pwd: #{Dir.pwd}" if verbose?

load_gem_configuration
@@ -257,7 +259,7 @@ def initialize(argv = ARGV)
end

def run
Dir.chdir dir do
Dir.chdir @options[:global][:dir] do
send "subcommand_#{@subcommand}", @argv_after_subcommand
end
end
@@ -271,10 +273,6 @@ def print_options

private

def dir
@options[:global][:dir] || Dir.pwd
end

def verbose?
@options[:global][:verbose]
end
@@ -495,7 +493,12 @@ def subcommand_ci(rest)
batch = if path =~ /^in|stdin$/
$stdin.read
else
File.read(path)
path = Pathname(path)
if path.absolute?
File.read(path)
else
File.read(Pathname(@called_from_dir).join(path))
end
end

results = batch.each_line.map do |line|
@@ -506,13 +509,13 @@ def subcommand_ci(rest)
rest = option_parser.order line.split

gem_name = rest.first
CIEnvironment.new(dir, gem_name, rest[1..-1]).success?
CIEnvironment.new(@options[:global][:dir], gem_name, rest[1..-1]).success?
end

results.all?
else
gem_name = rest.first
ci = CIEnvironment.new dir, gem_name, rest[1..-1], definition: options[:ci][:definition]
ci = CIEnvironment.new @options[:global][:dir], gem_name, rest[1..-1], definition: options[:ci][:definition]
ci.success?
end
end
@@ -631,7 +634,7 @@ def git_clone(url)

def git_checkout(target)
return if target.nil?
execute_cmd %W[git checkout #{target}], dir: repository_dir, print: true
execute_cmd %W[git checkout --force #{target}], dir: repository_dir, print: true
end

def git_tag(version)
94 changes: 94 additions & 0 deletions lib/ruby/truffle/shims/openssl-stubs.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# Copyright (c) 2015 Oracle and/or its affiliates. All rights reserved. This
# code is released under a tri EPL/GPL/LGPL license. You can use it,
# redistribute it and/or modify it under the terms of the:
#
# Eclipse Public License version 1.0
# GNU General Public License version 2
# GNU Lesser General Public License version 2.1

# This is shimmed specifically for RubySpec's marshal specs, Rails, and ActiveSupport tests

require 'digest'

module OpenSSL
module X509
class Name

def to_a
raise "not implemented"
end

end
end

module Cipher
CipherError = Class.new StandardError

class Cipher
attr_accessor :key, :iv

def initialize(cipher)
@encrypt = nil
@iv = nil
end

def encrypt
@encrypt = true
end

def decrypt
@encrypt = false
end

def random_iv
@iv = (format('%10s', rand(16**10).to_s(16))*4).gsub(' ', '0')
end

def update(data)
raise CipherError if @iv.nil?

data.each_byte.each_with_index.map do |byte, i|
iv_i = i % 20
iv_byte = @iv[(iv_i*2)..(iv_i*2+1)].to_i(16)
(byte ^ iv_byte).chr
end.join
end

def final
''
end

end
end

module PKCS5
def self.pbkdf2_hmac_sha1(secret, salt, iterations, key_size)
(salt * (key_size / salt.size + 1))[0...key_size]
end
end

module Digest
%i[SHA1 SHA256 SHA384 SHA512].each do |name|
klass = Class.new(::Digest.const_get(name)) do
def name
self.class.name
end
end

const_set name, klass
end
end

module HMAC
def self.hexdigest(digest, key, data)
# AS test data
if data == "BAh7BjoIZm9vbzonTWVzc2FnZVZlcmlmaWVyVGVzdDo6QXV0b2xvYWRDbGFzcwY6CUBmb29JIghmb28GOgZFVA=="
return "f3ef39a5241c365083770566dc7a9eb5d6ace914"
end
Digest::SHA1.hexdigest(data)
end
end
end

# make it look as openssl is loaded
$" << __FILE__.gsub(/-stubs\.rb/, '.rb')
77 changes: 6 additions & 71 deletions lib/ruby/truffle/shims/openssl.rb
Original file line number Diff line number Diff line change
@@ -6,76 +6,11 @@
# GNU General Public License version 2
# GNU Lesser General Public License version 2.1

# This is shimmed specifically for RubySpec's marshal specs, Rails, and ActiveSupport tests
# If loaded directly simulate as it was not found, it can added only
# explicitly by loading openssl-stubs which makes it look like
# openssl was loaded.

require 'digest'
load_error = LoadError.new("cannot load such file -- openssl")
load_error.instance_variable_set :@path, 'openssl'
raise load_error

module OpenSSL
module X509
class Name

def to_a
raise "not implemented"
end

end
end

module Cipher
CipherError = Class.new StandardError

class Cipher
attr_accessor :key, :iv

def initialize(cipher)
@encrypt = nil
@iv = nil
end

def encrypt
@encrypt = true
end

def decrypt
@encrypt = false
end

def random_iv
@iv = (format('%10s', rand(16**10).to_s(16))*4).gsub(' ', '0')
end

def update(data)
raise CipherError if @iv.nil?

data.each_byte.each_with_index.map do |byte, i|
iv_i = i % 20
iv_byte = @iv[(iv_i*2)..(iv_i*2+1)].to_i(16)
(byte ^ iv_byte).chr
end.join
end

def final
''
end

end
end

module PKCS5
def self.pbkdf2_hmac_sha1(secret, salt, iterations, key_size)
(salt * (key_size / salt.size + 1))[0...key_size]
end
end

Digest = ::Digest

module HMAC
def self.hexdigest(digest, key, data)
# AS test data
if data == "BAh7BjoIZm9vbzonTWVzc2FnZVZlcmlmaWVyVGVzdDo6QXV0b2xvYWRDbGFzcwY6CUBmb29JIghmb28GOgZFVA=="
return "f3ef39a5241c365083770566dc7a9eb5d6ace914"
end
Digest::SHA1.hexdigest(data)
end
end
end
23 changes: 23 additions & 0 deletions spec/jrubyc/java/files/hashy_kwargs.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
module HashyKwargs

DEFAULT_ARGS = { 'str' => 1, sym: 2 }
private_constant :DEFAULT_ARGS

def generic(*args, **kwargs, &block)
[ args, kwargs, block ]
end

def self.kwargs1(sym: 1, sec: 2)
sym || DEFAULT_ARGS[:sym]
end

def self.kwargs2(req:, **opts); [ req, opts ] end

DEFAULT_ARGS['foo'] || DEFAULT_ARGS['str']

Hash.new.tap do |hash|
hash['one'] = 11 / 10; hash['two'] = 22 / 10
@@hash_one = hash['tri'] || hash['one']
end

end
25 changes: 22 additions & 3 deletions spec/jrubyc/java/loading_spec.rb
Original file line number Diff line number Diff line change
@@ -25,7 +25,7 @@ def compile_files(files)
it "loads double_rescue.class" do
load File.join(FILES_DIR, 'double_rescue.class')

expect( defined?(DoubleRescue) ).to be_truthy
expect( Object.const_defined?(:DoubleRescue) ).to be true
DoubleRescue.new._call

expect( DoubleRescue.re_raise_return ).to be_a LoadError
@@ -35,15 +35,34 @@ def compile_files(files)
it "loads sample_block.class" do
load File.join(FILES_DIR, 'sample_block.class')

expect( defined?(SampleBlock) ).to be_truthy
expect( Object.const_defined?(:SampleBlock) ).to be true
expect( SampleBlock.class_variable_get :@@func ).to eql '11'
end

it "deserializes symbol_proc.class" do
load File.join(FILES_DIR, 'symbol_proc.class')

expect( $symbol_proc_result ).to be_truthy
expect( $symbol_proc_result ).to_not be nil
expect( $symbol_proc_result ).to eql [ 1, 2, 3 ]
end

it "compiles hashy_kwargs.class correctly" do
load File.join(FILES_DIR, 'hashy_kwargs.class')

expect( Object.const_defined?(:HashyKwargs) ).to be true
klass = Class.new { include HashyKwargs }
res = klass.new.generic('0', 111, arg: 1) { 'block' }
pending 'FIXME if you wonder!'
expect( res[0] ).to eql [ '0', 111 ]
expect( res[1] ).to eql({ :arg => 1 })
expect( res[2].call ).to eql 'block'
expect( res.size ).to be 3

expect( HashyKwargs.kwargs1(sec: '2') ).to eql 1
expect( HashyKwargs.kwargs1(sym: false) ).to eql 2

res = HashyKwargs.kwargs2(foo: :bar, baz: 0, req: true)
expect( res ).to eql [ true, { :foo => :bar, :baz => 0 }]
end

end
9 changes: 9 additions & 0 deletions spec/truffle/truffle.mspec
Original file line number Diff line number Diff line change
@@ -49,6 +49,15 @@ class MSpecScript
# Load issues with 'delegate'.
"^spec/ruby/library/delegate/delegate_class/instance_method_spec.rb",
"^spec/ruby/library/delegate/delegator/protected_methods_spec.rb",

# Openssl not yet supported
"^spec/ruby/library/openssl/cipher_spec.rb",
"^spec/ruby/library/openssl/config/freeze_spec.rb",
"^spec/ruby/library/openssl/hmac/digest_spec.rb",
"^spec/ruby/library/openssl/hmac/hexdigest_spec.rb",
"^spec/ruby/library/openssl/random/pseudo_bytes_spec.rb",
"^spec/ruby/library/openssl/random/random_bytes_spec.rb",
"^spec/ruby/library/openssl/x509/name/parse_spec.rb"
]

set :truffle, [
3 changes: 2 additions & 1 deletion test/pom.rb
Original file line number Diff line number Diff line change
@@ -76,7 +76,8 @@
'showWarnings' => 'true',
'showDeprecation' => 'true',
'source' => '${base.java.version}',
'target' => '${base.java.version}' )
'target' => '${base.java.version}',
'testExcludes' => ['truffle/**/*.java'] )
plugin :dependency do
execute_goals( 'copy',
:id => 'copy jars for testing',
6 changes: 5 additions & 1 deletion test/truffle/integration/backtraces/backtraces.rb
Original file line number Diff line number Diff line change
@@ -29,14 +29,18 @@ def check(file)

success = true

print = []
expected.zip(actual).each do |e, a|
unless a.end_with?(e)
puts "Expected #{e}, actually #{a}"
print << "E #{a} Expected: #{e}"
success = false
else
print << ". #{a}"
end
end

unless success
puts print.join("\n")
exit 1
end
end
11 changes: 7 additions & 4 deletions test/truffle/integration/backtraces/eval.backtrace
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
/eval.rb:18:in `<main>'
(eval):1:in `<main>'
/eval.rb:22:in `eval'
/eval.rb:22:in `m3'
a_m3_calling_source.rb:42:in `<main>'
/eval.rb:18:in `eval'
/eval.rb:18:in `m2'
/eval.rb:14:in `<main>'
(eval):1:in `<main>'
/eval.rb:14:in `eval'
/eval.rb:14:in `m1'
/eval.rb:22:in `block in <main>'
/eval.rb:26:in `block in <main>'
/backtraces.rb:17:in `check'
/eval.rb:21:in `<main>'
/eval.rb:25:in `<main>'
4 changes: 4 additions & 0 deletions test/truffle/integration/backtraces/eval.rb
Original file line number Diff line number Diff line change
@@ -15,6 +15,10 @@ def m1
end

def m2
eval 'm3', binding, 'a_m3_calling_source.rb', 42
end

def m3
eval "raise 'message'"
end

7 changes: 7 additions & 0 deletions test/truffle/integration/gem-testing.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/usr/bin/env bash

gem install bundler

mkdir -p test/truffle/integration/gem-testing
jruby+truffle --dir test/truffle/integration/gem-testing ci --batch lib/ruby/truffle/jruby+truffle/gem_ci/travis.txt

File renamed without changes.
1 change: 1 addition & 0 deletions test/truffle/integration/rails/.jruby+truffle.yaml
Original file line number Diff line number Diff line change
@@ -48,4 +48,5 @@

:run:
:require:
- openssl-stubs
- stubs
8 changes: 0 additions & 8 deletions tool/travis_runner.sh
Original file line number Diff line number Diff line change
@@ -21,11 +21,3 @@ if [[ -v JT ]]
then
ruby tool/jt.rb $JT
fi

if [[ -v JTRCI ]]
then
gem install bundler
mkdir gem-testing
cd gem-testing
jruby+truffle ci --batch ../$JTRCI
fi
Original file line number Diff line number Diff line change
@@ -605,7 +605,7 @@ public Object evalNoBindingUncached(VirtualFrame frame, DynamicObject source, No
final DynamicObject binding = getCallerBinding(frame);
final MaterializedFrame topFrame = Layouts.BINDING.getFrame(binding);
RubyArguments.setSelf(topFrame.getArguments(), RubyArguments.getSelf(frame));
final CodeLoader.DeferredCall deferredCall = doEvalX(source, binding, "(eval)", true);
final CodeLoader.DeferredCall deferredCall = doEvalX(source, binding, "(eval)", 1, true);
return callNode.call(frame, deferredCall.getCallTarget(), deferredCall.getArguments());

}
@@ -626,7 +626,7 @@ public Object evalNilBinding(VirtualFrame frame, DynamicObject source, DynamicOb
})
public Object evalBinding(VirtualFrame frame, DynamicObject source, DynamicObject binding, NotProvided filename,
NotProvided lineNumber, @Cached("create()") IndirectCallNode callNode) {
final CodeLoader.DeferredCall deferredCall = doEvalX(source, binding, "(eval)", false);
final CodeLoader.DeferredCall deferredCall = doEvalX(source, binding, "(eval)", 1, false);
return callNode.call(frame, deferredCall.getCallTarget(), deferredCall.getArguments());
}

@@ -665,7 +665,7 @@ public Object evalBindingFilename(VirtualFrame frame, DynamicObject source, Dyna
"isRubyString(filename)" })
public Object evalBindingFilenameLine(VirtualFrame frame, DynamicObject source, DynamicObject binding, DynamicObject filename,
int lineNumber, @Cached("create()") IndirectCallNode callNode) {
final CodeLoader.DeferredCall deferredCall = doEvalX(source, binding, filename.toString(), false);
final CodeLoader.DeferredCall deferredCall = doEvalX(source, binding, filename.toString(), lineNumber, false);
return callNode.call(frame, deferredCall.getCallTarget(), deferredCall.getArguments());
}

@@ -679,13 +679,23 @@ public Object evalBadBinding(DynamicObject source, DynamicObject badBinding, Not
}

@TruffleBoundary
private CodeLoader.DeferredCall doEvalX(DynamicObject source, DynamicObject binding, String filename, boolean ownScopeForAssignments) {
ByteList code = StringOperations.getByteListReadOnly(source);
final Source source1 = Source.fromText(code, filename);
private CodeLoader.DeferredCall doEvalX(DynamicObject rubySource,
DynamicObject binding,
String filename,
int line,
boolean ownScopeForAssignments) {
ByteList code = StringOperations.getByteListReadOnly(rubySource);

// TODO (pitr 15-Oct-2015): fix this ugly hack, required for AS, copy-paste
final String space = new String(new char[Math.max(line - 1, 0)]).replace("\0", "\n");
final Source source = Source.fromText(space + code, filename);

final MaterializedFrame frame = Layouts.BINDING.getFrame(binding);
final DeclarationContext declarationContext = RubyArguments.getDeclarationContext(frame);
final RubyRootNode rootNode = getContext().getCodeLoader().parse(source1, code.getEncoding(), ParserContext.EVAL, frame, ownScopeForAssignments, this);
return getContext().getCodeLoader().prepareExecute(ParserContext.EVAL, declarationContext, rootNode, frame, RubyArguments.getSelf(frame));
final RubyRootNode rootNode = getContext().getCodeLoader().parse(
source, code.getEncoding(), ParserContext.EVAL, frame, ownScopeForAssignments, this);
return getContext().getCodeLoader().prepareExecute(
ParserContext.EVAL, declarationContext, rootNode, frame, RubyArguments.getSelf(frame));
}

protected RootNodeWrapper compileSource(VirtualFrame frame, DynamicObject sourceText) {
Original file line number Diff line number Diff line change
@@ -93,6 +93,7 @@
import org.jruby.truffle.language.parser.ParserContext;
import org.jruby.truffle.language.parser.jruby.Translator;
import org.jruby.truffle.language.yield.YieldNode;
import org.jruby.util.ByteList;
import org.jruby.util.IdUtil;

import java.util.ArrayList;
@@ -666,17 +667,17 @@ private Object classEvalSource(VirtualFrame frame, DynamicObject module, Dynamic
}

@TruffleBoundary
private CodeLoader.DeferredCall classEvalSource(DynamicObject module, DynamicObject code, String file, int line) {
assert RubyGuards.isRubyString(code);
private CodeLoader.DeferredCall classEvalSource(DynamicObject module, DynamicObject rubySource, String file, int line) {
assert RubyGuards.isRubyString(rubySource);
ByteList code = StringOperations.getByteListReadOnly(rubySource);

final MaterializedFrame callerFrame = getContext().getCallStack().getCallerFrameIgnoringSend()
.getFrame(FrameInstance.FrameAccess.MATERIALIZE, true).materialize();
Encoding encoding = Layouts.STRING.getRope(code).getEncoding();
Encoding encoding = Layouts.STRING.getRope(rubySource).getEncoding();

CompilerDirectives.transferToInterpreter();
// TODO (pitr 15-Oct-2015): fix this ugly hack, required for AS
// TODO (pitr 15-Oct-2015): fix this ugly hack, required for AS, copy-paste
final String space = new String(new char[Math.max(line - 1, 0)]).replace("\0", "\n");
Source source = Source.fromText(space + code.toString(), file);
Source source = Source.fromText(space + code, file);

final RubyRootNode rootNode = getContext().getCodeLoader().parse(source, encoding, ParserContext.MODULE, callerFrame, true, this);
return getContext().getCodeLoader().prepareExecute(ParserContext.MODULE, DeclarationContext.CLASS_EVAL, rootNode, callerFrame, module);
59 changes: 59 additions & 0 deletions truffle/src/main/java/org/jruby/truffle/core/rope/MutableRope.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* Copyright (c) 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:
*
* Eclipse Public License version 1.0
* GNU General Public License version 2
* GNU Lesser General Public License version 2.1
*/

package org.jruby.truffle.core.rope;

import org.jcodings.Encoding;
import org.jruby.util.ByteList;

public class MutableRope extends LeafRope {

private final ByteList byteList;

protected MutableRope(byte[] bytes, Encoding encoding, CodeRange codeRange, boolean singleByteOptimizable, int characterLength) {
super(bytes, encoding, codeRange, singleByteOptimizable, characterLength);
this.byteList = new ByteList(bytes, encoding, true);
}

public MutableRope(Rope original) {
this(original.getBytes(),
original.getEncoding(),
original.getCodeRange(),
original.isSingleByteOptimizable(),
original.characterLength());
}

@Override
public Rope withEncoding(Encoding newEncoding, CodeRange newCodeRange) {
byteList.setEncoding(newEncoding);
return this;
}

@Override
public byte getByteSlow(int index) {
return (byte) byteList.get(index);
}

@Override
public byte[] extractRange(int offset, int length) {
return new ByteList(byteList, offset, length).bytes();
}

public ByteList getByteList() {
return byteList;
}

@Override
public String toString() {
// This should be used for debugging only.
return byteList.toString();
}

}
35 changes: 28 additions & 7 deletions truffle/src/main/java/org/jruby/truffle/core/rope/RopeNodes.java
Original file line number Diff line number Diff line change
@@ -29,6 +29,8 @@
import org.jcodings.specific.UTF8Encoding;
import org.jruby.truffle.RubyContext;
import org.jruby.truffle.language.RubyNode;
import org.jruby.util.ByteList;
import org.jruby.util.ByteListHolder;
import org.jruby.util.StringSupport;

import static org.jruby.truffle.core.rope.CodeRange.CR_7BIT;
@@ -202,12 +204,12 @@ public MakeConcatNode(RubyContext context, SourceSection sourceSection) {

public abstract Rope executeMake(Rope left, Rope right, Encoding encoding);

@Specialization(guards = { "left.isEmpty()", "right.getEncoding() == encoding" })
@Specialization(guards = { "left.isEmpty()", "!isMutableRope(left)", "right.getEncoding() == encoding" })
public Rope concatEmptyLeftSameEncoding(Rope left, Rope right, Encoding encoding) {
return right;
}

@Specialization(guards = { "left.isEmpty()", "right.getEncoding() != encoding" })
@Specialization(guards = { "left.isEmpty()", "!isMutableRope(left)", "right.getEncoding() != encoding" })
public Rope concatEmptyLeftDifferentEncoding(Rope left, Rope right, Encoding encoding) {
return RopeOperations.withEncoding(right, encoding);
}
@@ -222,7 +224,21 @@ public Rope concatEmptyRightDifferentEncoding(Rope left, Rope right, Encoding en
return RopeOperations.withEncoding(left, encoding);
}

@Specialization(guards = { "!left.isEmpty()", "!right.isEmpty()", "left.byteLength() < SHORT_LEAF_BYTESIZE_THRESHOLD", "right.byteLength() < SHORT_LEAF_BYTESIZE_THRESHOLD" })
@Specialization(guards = "isMutableRope(left)")
public Rope concatMutableRope(MutableRope left, Rope right, Encoding encoding,
@Cached("createBinaryProfile()") ConditionProfile differentEncodingProfile) {
final ByteList byteList = left.getByteList();

byteList.append(right.getBytes());

if (differentEncodingProfile.profile(byteList.getEncoding() != encoding)) {
byteList.setEncoding(encoding);
}

return left;
}

@Specialization(guards = { "!left.isEmpty()", "!right.isEmpty()", "!isMutableRope(left)", "left.byteLength() < SHORT_LEAF_BYTESIZE_THRESHOLD", "right.byteLength() < SHORT_LEAF_BYTESIZE_THRESHOLD" })
public Rope concatLeaves(LeafRope left, LeafRope right, Encoding encoding,
@Cached("createBinaryProfile()") ConditionProfile sameCodeRangeProfile,
@Cached("createBinaryProfile()") ConditionProfile brokenCodeRangeProfile) {
@@ -240,7 +256,7 @@ public Rope concatLeaves(LeafRope left, LeafRope right, Encoding encoding,
return makeLeafRopeNode.executeMake(bytes, encoding, codeRange);
}

@Specialization(guards = { "!right.isEmpty()", "left.byteLength() >= SHORT_LEAF_BYTESIZE_THRESHOLD", "right.byteLength() < SHORT_LEAF_BYTESIZE_THRESHOLD" })
@Specialization(guards = { "!right.isEmpty()", "!isMutableRope(left)", "left.byteLength() >= SHORT_LEAF_BYTESIZE_THRESHOLD", "right.byteLength() < SHORT_LEAF_BYTESIZE_THRESHOLD" })
public Rope concatLeavesGeneral(LeafRope left, LeafRope right, Encoding encoding,
@Cached("createBinaryProfile()") ConditionProfile sameCodeRangeProfile,
@Cached("createBinaryProfile()") ConditionProfile brokenCodeRangeProfile,
@@ -252,9 +268,10 @@ public Rope concatLeavesGeneral(LeafRope left, LeafRope right, Encoding encoding
public Rope concatWithReduce(ConcatRope left, LeafRope right, Encoding encoding,
@Cached("createBinaryProfile()") ConditionProfile sameCodeRangeProfile,
@Cached("createBinaryProfile()") ConditionProfile brokenCodeRangeProfile,
@Cached("createBinaryProfile()") ConditionProfile isLeftSingleByteOptimizableProfile) {
@Cached("createBinaryProfile()") ConditionProfile isLeftSingleByteOptimizableProfile,
@Cached("createBinaryProfile()") ConditionProfile shouldCompactProfile) {

if ((left.getRight().byteLength() < SHORT_LEAF_BYTESIZE_THRESHOLD) && (left.getRight() instanceof LeafRope)) {
if (shouldCompactProfile.profile((left.getRight().byteLength() < SHORT_LEAF_BYTESIZE_THRESHOLD) && (left.getRight() instanceof LeafRope))) {
final Rope compacted = concatLeaves((LeafRope) left.getRight(), right, encoding, sameCodeRangeProfile, brokenCodeRangeProfile);
return concat(left.getLeft(), compacted, encoding, sameCodeRangeProfile, brokenCodeRangeProfile, isLeftSingleByteOptimizableProfile);
}
@@ -270,7 +287,7 @@ public Rope concatSubstringLeaf(SubstringRope left, LeafRope right, Encoding enc
return concat(left, right, encoding, sameCodeRangeProfile, brokenCodeRangeProfile, isLeftSingleByteOptimizableProfile);
}

@Specialization(guards = { "!left.isEmpty()", "!right.isEmpty()" })
@Specialization(guards = { "!isMutableRope(left)" })
public Rope concat(Rope left, Rope right, Encoding encoding,
@Cached("createBinaryProfile()") ConditionProfile sameCodeRangeProfile,
@Cached("createBinaryProfile()") ConditionProfile brokenCodeRangeProfile,
@@ -325,6 +342,10 @@ protected static boolean isShortLeafRope(Rope rope) {
protected static boolean isLeafRope(Rope rope) {
return rope instanceof LeafRope;
}

protected static boolean isMutableRope(Rope rope) {
return rope instanceof MutableRope;
}
}


Original file line number Diff line number Diff line change
@@ -75,6 +75,7 @@
import org.jruby.truffle.core.kernel.KernelNodesFactory;
import org.jruby.truffle.core.numeric.FixnumLowerNodeGen;
import org.jruby.truffle.core.rope.CodeRange;
import org.jruby.truffle.core.rope.MutableRope;
import org.jruby.truffle.core.rope.Rope;
import org.jruby.truffle.core.rope.RopeConstants;
import org.jruby.truffle.core.rope.RopeNodes;
@@ -1210,32 +1211,40 @@ public ForceEncodingNode(RubyContext context, SourceSection sourceSection) {
@TruffleBoundary
@Specialization(guards = "isRubyString(encodingName)")
public DynamicObject forceEncodingString(DynamicObject string, DynamicObject encodingName,
@Cached("createBinaryProfile()") ConditionProfile differentEncodingProfile) {
@Cached("createBinaryProfile()") ConditionProfile differentEncodingProfile,
@Cached("createBinaryProfile()") ConditionProfile mutableRopeProfile) {
final DynamicObject encoding = EncodingNodes.getEncoding(encodingName.toString());
return forceEncodingEncoding(string, encoding, differentEncodingProfile);
return forceEncodingEncoding(string, encoding, differentEncodingProfile, mutableRopeProfile);
}

@Specialization(guards = "isRubyEncoding(rubyEncoding)")
public DynamicObject forceEncodingEncoding(DynamicObject string, DynamicObject rubyEncoding,
@Cached("createBinaryProfile()") ConditionProfile differentEncodingProfile) {
@Cached("createBinaryProfile()") ConditionProfile differentEncodingProfile,
@Cached("createBinaryProfile()") ConditionProfile mutableRopeProfile) {
final Encoding encoding = EncodingOperations.getEncoding(rubyEncoding);
final Rope rope = rope(string);

if (differentEncodingProfile.profile(encoding(string) != encoding)) {
StringOperations.forceEncoding(string, encoding);
if (differentEncodingProfile.profile(rope.getEncoding() != encoding)) {
if (mutableRopeProfile.profile(rope instanceof MutableRope)) {
((MutableRope) rope).getByteList().setEncoding(encoding);
} else {
StringOperations.forceEncoding(string, encoding);
}
}

return string;
}

@Specialization(guards = { "!isRubyString(encoding)", "!isRubyEncoding(encoding)" })
public DynamicObject forceEncoding(VirtualFrame frame, DynamicObject string, Object encoding,
@Cached("createBinaryProfile()") ConditionProfile differentEncodingProfile) {
@Cached("createBinaryProfile()") ConditionProfile differentEncodingProfile,
@Cached("createBinaryProfile()") ConditionProfile mutableRopeProfile) {
if (toStrNode == null) {
CompilerDirectives.transferToInterpreter();
toStrNode = insert(ToStrNodeGen.create(getContext(), getSourceSection(), null));
}

return forceEncodingString(string, toStrNode.executeToStr(frame, encoding), differentEncodingProfile);
return forceEncodingString(string, toStrNode.executeToStr(frame, encoding), differentEncodingProfile, mutableRopeProfile);
}

}
@@ -1877,8 +1886,16 @@ public SizeNode(RubyContext context, SourceSection sourceSection) {
}

@Specialization
public int size(DynamicObject string) {
return rope(string).characterLength();
public int size(DynamicObject string,
@Cached("createBinaryProfile()") ConditionProfile mutableRopeProfile) {
final Rope rope = rope(string);

if (mutableRopeProfile.profile(rope instanceof MutableRope)) {
// TODO (nirvdrum 11-Mar-16): This response is only correct for CR_7BIT. Mutable ropes have not been updated for multi-byte characters.
return ((MutableRope) rope).getByteList().realSize();
} else {
return rope.characterLength();
}
}

}
Original file line number Diff line number Diff line change
@@ -43,6 +43,7 @@
import org.jruby.truffle.core.binding.BindingNodes;
import org.jruby.truffle.core.hash.BucketsStrategy;
import org.jruby.truffle.core.rope.CodeRange;
import org.jruby.truffle.core.rope.MutableRope;
import org.jruby.truffle.core.rope.Rope;
import org.jruby.truffle.core.rope.RopeNodes;
import org.jruby.truffle.core.rope.RopeNodesFactory;
@@ -417,6 +418,22 @@ public DynamicObject debugPrint(DynamicObject string) {

}

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

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

@Specialization(guards = "isRubyString(string)")
public DynamicObject convertToMutableRope(DynamicObject string) {
final MutableRope mutableRope = new MutableRope(StringOperations.rope(string));
StringOperations.setRope(string, mutableRope);

return string;
}
}

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

Original file line number Diff line number Diff line change
@@ -282,11 +282,17 @@ private boolean isCore(SourceSection sourceSection) {

final String path = source.getPath();

if (path == null) {
return true;
if (path != null) {
return path.startsWith(SourceLoader.TRUFFLE_SCHEME);
}

final String name = source.getName();

if (name != null) {
return name.startsWith(SourceLoader.TRUFFLE_SCHEME);
}

return path.startsWith(SourceLoader.TRUFFLE_SCHEME);
return true;
}

private String formatForeign(Node callNode) {

0 comments on commit 9326df8

Please sign in to comment.