Skip to content

Commit

Permalink
Showing 21 changed files with 383 additions and 86 deletions.
2 changes: 1 addition & 1 deletion core/src/main/java/org/jruby/RubyFile.java
Original file line number Diff line number Diff line change
@@ -1640,7 +1640,7 @@ else if ( ( preFix.equals("uri:classloader:") || preFix.equals("classpath:") )
if (path.startsWith("uri:")) {
cwd = path;
} else {
cwd = StringSupport.checkEmbeddedNulls(runtime, get_path(context, args[1])).getUnicodeValue();
cwd = StringSupport.checkEmbeddedNulls(runtime, get_path(context, args[1])).toString();

// Handle ~user paths.
if (expandUser) {
31 changes: 22 additions & 9 deletions core/src/main/java/org/jruby/RubyGlobal.java
Original file line number Diff line number Diff line change
@@ -58,6 +58,7 @@
import org.jruby.util.OSEnvironment;
import org.jruby.util.RegexpOptions;
import org.jruby.util.cli.OutputStrings;
import org.jruby.util.io.EncodingUtils;
import org.jruby.util.io.OpenFile;
import org.jruby.util.io.STDIO;

@@ -420,9 +421,9 @@ protected IRubyObject case_aware_op_aset(ThreadContext context,
return super.delete(context, key, org.jruby.runtime.Block.NULL_BLOCK);
}

IRubyObject keyAsStr = normalizeEnvString(Helpers.invoke(context, key, "to_str"));
IRubyObject keyAsStr = normalizeEnvString(context, key, Helpers.invoke(context, key, "to_str"));
IRubyObject valueAsStr = value.isNil() ? context.nil :
normalizeEnvString(Helpers.invoke(context, value, "to_str"));
normalizeEnvString(context, key, Helpers.invoke(context, value, "to_str"));

if (updateRealENV) {
POSIX posix = context.runtime.getPosix();
@@ -458,14 +459,26 @@ private RubyString getCorrectKey(IRubyObject key, ThreadContext context) {
return actualKey;
}

private IRubyObject normalizeEnvString(IRubyObject str) {
if (str instanceof RubyString) {
Encoding enc = getRuntime().getEncodingService().getLocaleEncoding();
RubyString newStr = getRuntime().newString(new ByteList(str.toString().getBytes(), enc));
newStr.setFrozen(true);
return newStr;
private IRubyObject normalizeEnvString(ThreadContext context, IRubyObject key, IRubyObject value) {
if (value instanceof RubyString) {
Ruby runtime = context.runtime;
RubyString valueStr = (RubyString) value;

// Ensure PATH is encoded like filesystem
if (Platform.IS_WINDOWS ?
key.toString().equalsIgnoreCase("PATH") :
key.toString().equals("PATH")) {
Encoding enc = runtime.getEncodingService().getFileSystemEncoding();
valueStr = EncodingUtils.strConvEnc(context, valueStr, valueStr.getEncoding(), enc);
} else {
valueStr = RubyString.newString(runtime, valueStr.toString(), runtime.getEncodingService().getLocaleEncoding());
}

valueStr.setFrozen(true);

return valueStr;
}
return str;
return value;
}
}

12 changes: 6 additions & 6 deletions core/src/main/java/org/jruby/runtime/Helpers.java
Original file line number Diff line number Diff line change
@@ -57,6 +57,7 @@
import static org.jruby.util.StringSupport.EMPTY_STRING_ARRAY;

import org.jruby.util.JavaNameMangler;
import org.jruby.util.io.EncodingUtils;

/**
* Helper methods which are called by the compiler. Note: These will show no consumers, but
@@ -2645,15 +2646,14 @@ public static String decodeByteList(Ruby runtime, ByteList value) {
Charset charset = runtime.getEncodingService().charsetForEncoding(encoding);

if (charset == null) {
try {
return new String(unsafeBytes, begin, length, encoding.toString());
} catch (UnsupportedEncodingException uee) {
return value.toString();
}
// No JDK Charset available for this encoding; convert to UTF-16 ourselves.
Encoding utf16 = EncodingUtils.getUTF16ForPlatform();

return EncodingUtils.strConvEnc(runtime.getCurrentContext(), value, value.getEncoding(), utf16).toString();
}

return RubyEncoding.decode(unsafeBytes, begin, length, charset);
}
}

/**
* Convert a ByteList into a Java String by using its Encoding's Charset. If
22 changes: 15 additions & 7 deletions core/src/main/java/org/jruby/util/OSEnvironment.java
Original file line number Diff line number Diff line change
@@ -89,15 +89,15 @@ public Map<RubyString, RubyString> getSystemPropertiesMap(Ruby runtime) {
private static Map<RubyString, RubyString> asMapOfRubyStrings(final Ruby runtime, final Map<?, ?> map) {
@SuppressWarnings("unchecked")
final Map<RubyString, RubyString> rubyMap = new HashMap(map.size() + 2);
Encoding encoding = runtime.getEncodingService().getLocaleEncoding();
Encoding keyEncoding = runtime.getEncodingService().getLocaleEncoding();

// On Windows, map doesn't have corresponding keys for these
if (Platform.IS_WINDOWS) {
// these may be null when in a restricted environment (JRUBY-6514)
String home = SafePropertyAccessor.getProperty("user.home");
String user = SafePropertyAccessor.getProperty("user.name");
putRubyKeyValuePair(runtime, rubyMap, "HOME", home == null ? "/" : home, encoding);
putRubyKeyValuePair(runtime, rubyMap, "USER", user == null ? "" : user, encoding);
putRubyKeyValuePair(runtime, rubyMap, "HOME", keyEncoding, home == null ? "/" : home, keyEncoding);
putRubyKeyValuePair(runtime, rubyMap, "USER", keyEncoding, user == null ? "" : user, keyEncoding);
}

for (Map.Entry<?, ?> entry : map.entrySet()) {
@@ -111,17 +111,25 @@ private static Map<RubyString, RubyString> asMapOfRubyStrings(final Ruby runtime
val = entry.getValue();
if ( ! (val instanceof String) ) continue; // Java devs can stuff non-string objects into env

putRubyKeyValuePair(runtime, rubyMap, key, (String) val, encoding);
// Ensure PATH is encoded like filesystem
Encoding valueEncoding = keyEncoding;
if ( org.jruby.platform.Platform.IS_WINDOWS ?
key.toString().equalsIgnoreCase("PATH") :
key.toString().equals("PATH") ) {
valueEncoding = runtime.getEncodingService().getFileSystemEncoding();
}

putRubyKeyValuePair(runtime, rubyMap, key, keyEncoding, (String) val, valueEncoding);
}

return rubyMap;
}

private static void putRubyKeyValuePair(Ruby runtime,
final Map<RubyString, RubyString> map,
String key, String value, Encoding encoding) {
ByteList keyBytes = new ByteList(key.getBytes(), encoding);
ByteList valueBytes = new ByteList(value.getBytes(), encoding);
String key, Encoding keyEncoding, String value, Encoding valueEncoding) {
ByteList keyBytes = RubyString.encodeBytelist(key, keyEncoding);
ByteList valueBytes = RubyString.encodeBytelist(value, valueEncoding);

RubyString keyString = runtime.newString(keyBytes);
RubyString valueString = runtime.newString(valueBytes);
17 changes: 11 additions & 6 deletions core/src/main/java/org/jruby/util/io/EncodingUtils.java
Original file line number Diff line number Diff line change
@@ -1268,12 +1268,7 @@ public static void transcodeLoop(ThreadContext context, byte[] inBytes, Ptr inPo
public static ByteList transcodeString(String string, Encoding toEncoding, int ecflags) {
Encoding encoding;

// This may be inefficient if we aren't matching endianness right
if (Platform.BYTE_ORDER == Platform.LITTLE_ENDIAN) {
encoding = UTF16LEEncoding.INSTANCE;
} else {
encoding = UTF16BEEncoding.INSTANCE;
}
encoding = getUTF16ForPlatform();

EConv ec = TranscoderDB.open(encoding.getName(), toEncoding.getName(), ecflags);

@@ -1298,6 +1293,16 @@ public static ByteList transcodeString(String string, Encoding toEncoding, int e
return destination;
}

public static Encoding getUTF16ForPlatform() {
Encoding encoding;// This may be inefficient if we aren't matching endianness right
if (Platform.BYTE_ORDER == Platform.LITTLE_ENDIAN) {
encoding = UTF16LEEncoding.INSTANCE;
} else {
encoding = UTF16BEEncoding.INSTANCE;
}
return encoding;
}

/**
* Perform the inner transcoding loop.
*
3 changes: 2 additions & 1 deletion core/src/main/ruby/jruby/kernel.rb
Original file line number Diff line number Diff line change
@@ -32,4 +32,5 @@
load 'jruby/kernel/time.rb'
load 'jruby/kernel/gc.rb'
load 'jruby/kernel/range.rb'
load 'jruby/kernel/load_error.rb'
load 'jruby/kernel/load_error.rb'
load 'jruby/kernel/file.rb'
159 changes: 159 additions & 0 deletions core/src/main/ruby/jruby/kernel/file.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
# Windows symlink support borrowed from djberg96/win32-file and ffi-win32-extensions

if org.jruby.platform.Platform::IS_WINDOWS

module JRuby
module Windows
module File
module Constants
FILE_ATTRIBUTE_REPARSE_POINT = 0x00000400
INVALID_HANDLE_VALUE = (1 << FFI::Platform::ADDRESS_SIZE) - 1
INVALID_FILE_ATTRIBUTES = (1 << FFI::Platform::ADDRESS_SIZE) - 1
IO_REPARSE_TAG_SYMLINK = 0xA000000C
end

module Functions
extend FFI::Library
ffi_lib :kernel32

typedef :ulong, :dword
typedef :uintptr_t, :handle
typedef :pointer, :ptr

def self.attach_pfunc(*args)
attach_function(*args)
private args[0]
end

attach_pfunc :CloseHandle, [:handle], :bool
attach_pfunc :FindFirstFileW, [:buffer_in, :pointer], :handle
attach_pfunc :CreateSymbolicLinkW, [:buffer_in, :buffer_in, :dword], :bool
attach_pfunc :GetFileAttributesW, [:buffer_in], :dword

attach_pfunc :CreateFileW, [:buffer_in, :dword, :dword, :pointer, :dword, :dword, :handle], :handle
attach_pfunc :GetDiskFreeSpaceW, [:buffer_in, :pointer, :pointer, :pointer, :pointer], :bool
attach_pfunc :GetDriveTypeW, [:buffer_in], :uint
attach_pfunc :GetFileType, [:handle], :dword
attach_pfunc :GetFinalPathNameByHandleW, [:handle, :buffer_out, :dword, :dword], :dword
attach_pfunc :GetShortPathNameW, [:buffer_in, :buffer_out, :dword], :dword
attach_pfunc :GetLongPathNameW, [:buffer_in, :buffer_out, :dword], :dword
attach_pfunc :QueryDosDeviceA, [:string, :buffer_out, :dword], :dword
attach_pfunc :SetFileTime, [:handle, :ptr, :ptr, :ptr], :bool
attach_pfunc :SystemTimeToFileTime, [:ptr, :ptr], :bool

ffi_lib :shlwapi

attach_pfunc :PathFindExtensionW, [:buffer_in], :pointer
attach_pfunc :PathIsRootW, [:buffer_in], :bool
attach_pfunc :PathStripPathW, [:pointer], :void
attach_pfunc :PathRemoveBackslashW, [:buffer_in], :string
attach_pfunc :PathRemoveFileSpecW, [:pointer], :bool
attach_pfunc :PathRemoveExtensionW, [:buffer_in], :void
attach_pfunc :PathStripToRootW, [:buffer_in], :bool
end

module Structs
class FILETIME < FFI::Struct
layout(:dwLowDateTime, :ulong, :dwHighDateTime, :ulong)
end

class WIN32_FIND_DATA < FFI::Struct
layout(
:dwFileAttributes, :ulong,
:ftCreationTime, FILETIME,
:ftLastAccessTime, FILETIME,
:ftLastWriteTime, FILETIME,
:nFileSizeHigh, :ulong,
:nFileSizeLow, :ulong,
:dwReserved0, :ulong,
:dwReserved1, :ulong,
:cFileName, [:uint8, 260*2],
:cAlternateFileName, [:uint8, 14*2]
)
end
end
end
end
end

class File
include Windows::File::Constants
include Windows::File::Structs
extend Windows::File::Functions

# Creates a symbolic link called +new_name+ for the file or directory
# +old_name+.
#
# This method requires Windows Vista or later to work. Otherwise, it
# returns nil as per MRI.
#
def self.symlink(target, link)
target = string_check(target)
link = string_check(link)

flags = File.directory?(target) ? 1 : 0

wlink = link.wincode
wtarget = target.wincode

unless CreateSymbolicLinkW(wlink, wtarget, flags)
raise SystemCallError.new('CreateSymbolicLink', FFI.errno)
end

0 # Comply with spec
end

# Returns whether or not +file+ is a symlink.
#
def self.symlink?(file)
return false unless File.exist?(file)

bool = false
wfile = string_check(file).wincode

attrib = GetFileAttributesW(wfile)

if attrib == INVALID_FILE_ATTRIBUTES
raise SystemCallError.new('GetFileAttributes', FFI.errno)
end

if attrib & FILE_ATTRIBUTE_REPARSE_POINT > 0
begin
find_data = WIN32_FIND_DATA.new
handle = FindFirstFileW(wfile, find_data)

if handle == INVALID_HANDLE_VALUE
raise SystemCallError.new('FindFirstFile', FFI.errno)
end

if find_data[:dwReserved0] == IO_REPARSE_TAG_SYMLINK
bool = true
end
ensure
CloseHandle(handle)
end
end

bool
end

private

# Simulate Ruby's string checking
def self.string_check(arg)
return arg if arg.is_a?(String)
return arg.send(:to_str) if arg.respond_to?(:to_str, true) # MRI allows private to_str
return arg.to_path if arg.respond_to?(:to_path)
raise TypeError
end
end

class String
# Convenience method for converting strings to UTF-16LE for wide character
# functions that require it.
#
def wincode
(self.tr(File::SEPARATOR, File::ALT_SEPARATOR) + 0.chr).encode('UTF-16LE')
end
end
end
Original file line number Diff line number Diff line change
@@ -44,17 +44,10 @@
exclude_tests.rb: |
failures = { KernelTest: [:test_silence_stream,
:test_quietly],
InflectorTest: [:test_titleize_mixture_to_title_case_14],
LoadPathsTest: [:test_uniq_load_paths],
LoggerTest: [:test_buffer_multibyte],
MultibyteCharsExtrasTest: [:test_titleize_should_be_unicode_aware,
:test_titleize_should_not_affect_characters_that_do_not_case_fold],
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],
StringInflectionsTest: [:test_string_parameterized_no_separator,
:test_string_parameterized_normal,
:test_string_parameterized_underscore],
TimeZoneTest: :test_map_srednekolymsk_to_tzinfo }
11 changes: 3 additions & 8 deletions mx.jruby/mx_jruby.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import mx
import os
from os.path import join, exists
import subprocess
import shutil

@@ -84,9 +83,9 @@ def build(self):

mx.run_mx(['maven-install'], suite=truffle)

open(join(rubyDir, 'VERSION'), 'w').write('graal-vm\n')
open(os.path.join(rubyDir, 'VERSION'), 'w').write('graal-vm\n')

# Build jruby-truffle and
# Build jruby-truffle

mx.run(['find', '.'], nonZeroIsFatal=False, cwd=rubyDir)
mx.run_maven(['--version'], nonZeroIsFatal=False, cwd=rubyDir)
@@ -100,14 +99,10 @@ def build(self):
mx.run_maven(['-Pcomplete', '-DskipTests', '-Dtruffle.version=' + truffle_commit], cwd=rubyDir)
mx.run(['zip', '-d', 'maven/jruby-complete/target/jruby-complete-graal-vm.jar', 'META-INF/jruby.home/lib/*'], cwd=rubyDir)
mx.run(['bin/jruby', 'bin/gem', 'install', 'bundler', '-v', '1.10.6'], cwd=rubyDir)
# shutil.rmtree(os.path.join(_suite.dir, "lib", "target"), True)
# shutil.rmtree(os.path.join(_suite.dir, 'lib', 'lib', 'jni'), True)
# shutil.copytree(os.path.join(_suite.dir, 'lib', 'jni'), os.path.join(_suite.dir, 'lib', 'lib', 'jni'))
# shutil.rmtree(os.path.join(_suite.dir, 'lib', 'jni'), True)
mx.log('...finished build of {}'.format(self.subject))

def clean(self, forBuild=False):
if forBuild:
return
rubyDir = _suite.dir
mx.run_maven(['clean'], nonZeroIsFatal=False, cwd=rubyDir)

6 changes: 6 additions & 0 deletions spec/ruby/core/array/pack/shared/unicode.rb
Original file line number Diff line number Diff line change
@@ -23,6 +23,12 @@
].should be_computed_by(:pack, "U")
end

it "constructs strings with valid encodings" do
str = [0x85].pack("U*")
str.should == "\xc2\x85"
str.valid_encoding?.should be_true
end

it "encodes UTF-8 max codepoints" do
[ [[0x10000], "\xf0\x90\x80\x80"],
[[0xfffff], "\xf3\xbf\xbf\xbf"],
2 changes: 2 additions & 0 deletions tool/jruby_eclipse
Original file line number Diff line number Diff line change
@@ -13,6 +13,7 @@ TRUFFLEJARS = %W[

SNAKEYAMLJAR = "#{M2REPO}/org/yaml/snakeyaml/1.14/snakeyaml-1.14.jar"
ANTLR4JAR = "#{M2REPO}/org/antlr/antlr4-runtime/4.5.1-1/antlr4-runtime-4.5.1-1.jar"
JLINE = "#{M2REPO}/jline/jline/2.11/jline-2.11.jar"

GRAAL_OPTIONS_PREFIX = "graal."

@@ -62,6 +63,7 @@ if rest.include?('-X+T')
bootclasspath += TRUFFLEJARS
classpath << SNAKEYAMLJAR
classpath << ANTLR4JAR
classpath << JLINE
classpath << "#{JRUBY}/truffle/build.eclipse"
java_flags << "-Djruby.truffle.core.load_path=#{JRUBY}/truffle/src/main/ruby"
end
20 changes: 14 additions & 6 deletions tool/jt.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
#!/usr/bin/env ruby
# encoding: utf-8

# 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:
@@ -12,7 +14,7 @@
# Recommended: function jt { ruby tool/jt.rb "$@"; }

require 'fileutils'
require 'digest/sha1'
require 'json'

GRAALVM_VERSION = "0.11"

@@ -322,7 +324,7 @@ def help
puts 'jt tag all spec/ruby/language tag all specs in this file, without running them'
puts 'jt untag spec/ruby/language untag passing specs in this directory'
puts 'jt untag spec/ruby/language/while_spec.rb untag passing specs in this file'
puts 'jt metrics alloc ... how much memory is allocated running a program (use -X-T to test normal JRuby on this metric and others)'
puts 'jt metrics alloc [--json] ... how much memory is allocated running a program (use -X-T to test normal JRuby on this metric and others)'
puts 'jt metrics minheap ... what is the smallest heap you can use to run an application'
puts 'jt metrics time ... how long does it take to run a command, broken down into different phases'
puts 'jt tarball build the and test the distribution tarball'
@@ -685,6 +687,7 @@ def metrics(command, *args)
end

def metrics_alloc(*args)
use_json = args.delete '--json'
samples = []
METRICS_REPS.times do
log '.', 'sampling'
@@ -696,7 +699,12 @@ def metrics_alloc(*args)
end
log "\n", nil
mean = samples.inject(:+) / samples.size
puts "#{human_size(mean)}, max #{human_size(samples.max)}"
error = samples.max - mean
if use_json
puts JSON.generate({mean: mean, error: error})
else
puts "#{human_size(mean)} ± #{human_size(error)}"
end
end

def memory_allocated(trace)
@@ -819,10 +827,10 @@ def tarball
end

def log(tty_message, full_message)
if STDOUT.tty?
print(tty_message) unless tty_message.nil?
if STDERR.tty?
STDERR.print tty_message unless tty_message.nil?
else
puts full_message unless full_message.nil?
STDERR.puts full_message unless full_message.nil?
end
end

25 changes: 21 additions & 4 deletions truffle/src/main/java/org/jruby/truffle/core/array/ArrayNodes.java
Original file line number Diff line number Diff line change
@@ -87,9 +87,8 @@
import org.jruby.truffle.language.objects.TaintNodeGen;
import org.jruby.truffle.language.yield.YieldNode;
import org.jruby.util.Memo;

import java.util.Arrays;

import java.util.Comparator;
import static org.jruby.truffle.core.array.ArrayHelpers.createArray;
import static org.jruby.truffle.core.array.ArrayHelpers.getSize;
import static org.jruby.truffle.core.array.ArrayHelpers.getStore;
@@ -975,7 +974,7 @@ public DynamicObject initializeWithSizeAndValue(DynamicObject array, int size, O
@Cached("forValue(value)") ArrayStrategy strategy,
@Cached("createBinaryProfile()") ConditionProfile needsFill) {
final ArrayMirror store = strategy.newArray(size);
if (needsFill.profile(size > 0 && store.get(0) != value)) {
if (needsFill.profile(size > 0 && strategy.isDefaultValue(value))) {
for (int i = 0; i < size; i++) {
store.set(i, value);
}
@@ -2072,7 +2071,25 @@ public Object sortLargeArray(VirtualFrame frame, DynamicObject array, NotProvide
"right", getSize(array));
}

@Specialization(guards = { "!isNullArray(array)" })
@Specialization(guards = { "isObjectArray(array)" })
public Object sortObjectWithBlock(DynamicObject array, DynamicObject block) {
final int size = getSize(array);
Object[] copy = ((Object[]) getStore(array)).clone();
doSort(copy, size, block);
return createArray(getContext(), copy, size);
}

@TruffleBoundary
private void doSort(Object[] copy, int size, DynamicObject block) {
Arrays.sort(copy, 0, size, new Comparator<Object>() {
@Override
public int compare(Object a, Object b) {
return castSortValue(ProcOperations.rootCall(block, a, b));
}
});
}

@Specialization(guards = { "!isNullArray(array)", "!isObjectArray(array)" })
public Object sortWithBlock(VirtualFrame frame, DynamicObject array, DynamicObject block,
@Cached("new()") SnippetNode snippet) {
return snippet.execute(frame,
Original file line number Diff line number Diff line change
@@ -23,6 +23,10 @@ public boolean specializesFor(Object value) {
throw unsupported();
}

public boolean isDefaultValue(Object value) {
throw unsupported();
}

public abstract boolean matches(DynamicObject array);

public abstract ArrayMirror newArray(int size);
@@ -126,6 +130,10 @@ public boolean specializesFor(Object value) {
return value instanceof Integer;
}

public boolean isDefaultValue(Object value) {
return (int) value == 0;
}

public boolean matches(DynamicObject array) {
return ArrayGuards.isIntArray(array);
}
@@ -176,6 +184,10 @@ public boolean specializesFor(Object value) {
return value instanceof Long;
}

public boolean isDefaultValue(Object value) {
return (long) value == 0L;
}

public boolean matches(DynamicObject array) {
return ArrayGuards.isLongArray(array);
}
@@ -214,6 +226,10 @@ public boolean specializesFor(Object value) {
return value instanceof Double;
}

public boolean isDefaultValue(Object value) {
return (double) value == 0.0;
}

public boolean matches(DynamicObject array) {
return ArrayGuards.isDoubleArray(array);
}
@@ -252,6 +268,10 @@ public boolean specializesFor(Object value) {
return !(value instanceof Integer) && !(value instanceof Long) && !(value instanceof Double);
}

public boolean isDefaultValue(Object value) {
return value == null;
}

public boolean matches(DynamicObject array) {
return ArrayGuards.isObjectArray(array);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* 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.array;

import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.ImportStatic;
import com.oracle.truffle.api.dsl.NodeChild;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.object.DynamicObject;
import org.jruby.truffle.Layouts;
import org.jruby.truffle.language.RubyGuards;
import org.jruby.truffle.language.RubyNode;

@NodeChild(value = "array", type = RubyNode.class)
@ImportStatic(ArrayGuards.class)
public abstract class ArrayToObjectArrayNode extends RubyNode {

public Object[] unsplat(Object[] arguments) {
assert arguments.length == 1;
assert RubyGuards.isRubyArray(arguments[0]);
return executeToObjectArray((DynamicObject) arguments[0]);
}

public abstract Object[] executeToObjectArray(DynamicObject array);

@Specialization(guards = "isNullArray(array)")
public Object[] toObjectArrayNull(DynamicObject array) {
return new Object[0];
}

@Specialization(guards = "strategy.matches(array)", limit = "ARRAY_STRATEGIES")
public Object[] toObjectArrayOther(DynamicObject array,
@Cached("of(array)") ArrayStrategy strategy) {
final int size = Layouts.ARRAY.getSize(array);
return strategy.newMirror(array).getBoxedCopy(size);
}

}
Original file line number Diff line number Diff line change
@@ -3128,6 +3128,7 @@ public Object stringFindCharacterSingleByte(DynamicObject string, int offset) {
return propagate(string, ret);
}

@TruffleBoundary
@Specialization(guards = { "offset >= 0", "!offsetTooLarge(string, offset)", "!isSingleByteOptimizable(string)" })
public Object stringFindCharacter(DynamicObject string, int offset) {
// Taken from Rubinius's String::find_character.
Original file line number Diff line number Diff line change
@@ -285,8 +285,8 @@ public static boolean isUTF8ValidOneByte(byte b) {
public static boolean isUTF8ValidTwoBytes(byte... bytes) {
assert bytes.length == 2;

if (bytes[0] >= 0xc2 && bytes[0] <= 0xdf) {
return bytes[1] >= 0x80 && bytes[1] <= 0xbf;
if ((bytes[0] & 0xff) >= 0xc2 && (bytes[0] & 0xff) <= 0xdf) {
return (bytes[1] & 0xff) >= 0x80 && (bytes[1] & 0xff) <= 0xbf;
}

return false;
@@ -295,21 +295,21 @@ public static boolean isUTF8ValidTwoBytes(byte... bytes) {
public static boolean isUTF8ValidThreeBytes(byte... bytes) {
assert bytes.length == 3;

if (bytes[0] < 0xe0 || bytes[0] > 0xef) {
if ((bytes[0] & 0xff) < 0xe0 || (bytes[0] & 0xff) > 0xef) {
return false;
}

if (bytes[2] < 0x80 || bytes[2] > 0xbf) {
if ((bytes[2] & 0xff) < 0x80 || (bytes[2] & 0xff) > 0xbf) {
return false;
}

if (bytes[1] >= 0x80 || bytes[2] <= 0xbf) {
if (bytes[0] == 0xe0) {
return bytes[1] >= 0xa0;
if ((bytes[1] & 0xff) >= 0x80 || (bytes[2] & 0xff) <= 0xbf) {
if ((bytes[0] & 0xff) == 0xe0) {
return (bytes[1] & 0xff) >= 0xa0;
}

if (bytes[0] == 0xed) {
return bytes[1] <= 0x9f;
if ((bytes[0] & 0xff) == 0xed) {
return (bytes[1] & 0xff) <= 0x9f;
}

return true;
@@ -321,25 +321,25 @@ public static boolean isUTF8ValidThreeBytes(byte... bytes) {
public static boolean isUTF8ValidFourBytes(byte... bytes) {
assert bytes.length == 4;

if (bytes[3] < 0x80 || bytes[3] > 0xbf) {
if ((bytes[3] & 0xff) < 0x80 || (bytes[3] & 0xff) > 0xbf) {
return false;
}

if (bytes[2] < 0x80 || bytes[2] > 0xbf) {
if ((bytes[2] & 0xff) < 0x80 || (bytes[2] & 0xff) > 0xbf) {
return false;
}

if (bytes[0] < 0xf0 || bytes[0] > 0xf4) {
if ((bytes[0] & 0xff) < 0xf0 || (bytes[0] & 0xff) > 0xf4) {
return false;
}

if (bytes[1] >= 0x80 || bytes[2] <= 0xbf) {
if (bytes[0] == 0xf0) {
return bytes[1] >= 0x90;
if ((bytes[1] & 0xff) >= 0x80 || (bytes[2] & 0xff) <= 0xbf) {
if ((bytes[0] & 0xff) == 0xf0) {
return (bytes[1] & 0xff) >= 0x90;
}

if (bytes[0] == 0xf4) {
return bytes[1] <= 0x8f;
if ((bytes[0] & 0xff) == 0xf4) {
return (bytes[1] & 0xff) <= 0x8f;
}

return true;
Original file line number Diff line number Diff line change
@@ -204,10 +204,10 @@ public interface BlockingTimeoutAction<T> {
*/
@TruffleBoundary
public <T> T runUntilResult(Node currentNode, BlockingAction<T> action) {
final DynamicObject runningThread = getCurrentThread();
T result = null;

do {
final DynamicObject runningThread = getCurrentThread();
Layouts.THREAD.setStatus(runningThread, Status.SLEEP);

try {
Original file line number Diff line number Diff line change
@@ -10,12 +10,13 @@
package org.jruby.truffle.language.supercall;

import com.oracle.truffle.api.CompilerAsserts;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.ExplodeLoop;
import com.oracle.truffle.api.object.DynamicObject;
import com.oracle.truffle.api.source.SourceSection;
import org.jruby.truffle.RubyContext;
import org.jruby.truffle.core.array.ArrayOperations;
import org.jruby.truffle.core.array.ArrayToObjectArrayNode;
import org.jruby.truffle.core.array.ArrayToObjectArrayNodeGen;
import org.jruby.truffle.language.RubyNode;

/**
@@ -24,6 +25,8 @@
public class ReadSuperArgumentsNode extends RubyNode {

@Children private final RubyNode[] arguments;
@Child private ArrayToObjectArrayNode unsplatNode;

private final boolean isSplatted;

public ReadSuperArgumentsNode(RubyContext context, SourceSection sourceSection, RubyNode[] arguments, boolean isSplatted) {
@@ -45,11 +48,18 @@ public final Object execute(VirtualFrame frame) {
}

if (isSplatted) {
// TODO(CS): need something better to splat the arguments array
return ArrayOperations.toObjectArray((DynamicObject) argumentsObjects[0]);
return unsplat(argumentsObjects);
} else {
return argumentsObjects;
}
}

private Object[] unsplat(Object[] argumentsObjects) {
if (unsplatNode == null) {
CompilerDirectives.transferToInterpreter();
unsplatNode = insert(ArrayToObjectArrayNodeGen.create(null));
}
return unsplatNode.unsplat(argumentsObjects);
}

}
Original file line number Diff line number Diff line change
@@ -10,12 +10,14 @@
package org.jruby.truffle.language.supercall;

import com.oracle.truffle.api.CompilerAsserts;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.ExplodeLoop;
import com.oracle.truffle.api.object.DynamicObject;
import com.oracle.truffle.api.source.SourceSection;
import org.jruby.truffle.RubyContext;
import org.jruby.truffle.core.array.ArrayOperations;
import org.jruby.truffle.core.array.ArrayToObjectArrayNode;
import org.jruby.truffle.core.array.ArrayToObjectArrayNodeGen;
import org.jruby.truffle.core.array.ArrayUtils;
import org.jruby.truffle.language.RubyGuards;
import org.jruby.truffle.language.RubyNode;
@@ -25,8 +27,10 @@
*/
public class ReadZSuperArgumentsNode extends RubyNode {

private final boolean hasRestParameter;
@Children private final RubyNode[] reloadNodes;
@Child private ArrayToObjectArrayNode unsplatNode;

private final boolean hasRestParameter;

public ReadZSuperArgumentsNode(RubyContext context, SourceSection sourceSection, boolean hasRestParameter, RubyNode[] reloadNodes) {
super(context, sourceSection);
@@ -51,12 +55,20 @@ public final Object execute(VirtualFrame frame) {
final int restArgIndex = reloadNodes.length - 1;
final Object restArg = superArguments[restArgIndex];
assert RubyGuards.isRubyArray(restArg);
final Object[] restArgs = ArrayOperations.toObjectArray((DynamicObject) restArg);
final Object[] restArgs = unsplat((DynamicObject) restArg);
superArguments = ArrayUtils.copyOf(superArguments, restArgIndex + restArgs.length);
ArrayUtils.arraycopy(restArgs, 0, superArguments, restArgIndex, restArgs.length);
}

return superArguments;
}

private Object[] unsplat(DynamicObject array) {
if (unsplatNode == null) {
CompilerDirectives.transferToInterpreter();
unsplatNode = insert(ArrayToObjectArrayNodeGen.create(null));
}
return unsplatNode.executeToObjectArray(array);
}

}
Original file line number Diff line number Diff line change
@@ -10,14 +10,14 @@
package org.jruby.truffle.language.yield;

import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.ExplodeLoop;
import com.oracle.truffle.api.object.DynamicObject;
import com.oracle.truffle.api.profiles.BranchProfile;
import com.oracle.truffle.api.source.SourceSection;
import org.jruby.truffle.RubyContext;
import org.jruby.truffle.core.array.ArrayOperations;
import org.jruby.truffle.core.array.ArrayToObjectArrayNode;
import org.jruby.truffle.core.array.ArrayToObjectArrayNodeGen;
import org.jruby.truffle.language.RubyGuards;
import org.jruby.truffle.language.RubyNode;
import org.jruby.truffle.language.arguments.RubyArguments;
@@ -29,6 +29,7 @@ public class YieldExpressionNode extends RubyNode {

@Children private final RubyNode[] arguments;
@Child private YieldNode yieldNode;
@Child private ArrayToObjectArrayNode unsplatNode;

private final BranchProfile useCapturedBlock = BranchProfile.create();
private final BranchProfile noCapturedBlock = BranchProfile.create();
@@ -69,11 +70,12 @@ public final Object execute(VirtualFrame frame) {
return getYieldNode().dispatch(frame, block, argumentsObjects);
}

@TruffleBoundary
private Object[] unsplat(Object[] argumentsObjects) {
assert argumentsObjects.length == 1;
assert RubyGuards.isRubyArray(argumentsObjects[0]);
return ArrayOperations.toObjectArray(((DynamicObject) argumentsObjects[0]));
if (unsplatNode == null) {
CompilerDirectives.transferToInterpreter();
unsplatNode = insert(ArrayToObjectArrayNodeGen.create(null));
}
return unsplatNode.unsplat(argumentsObjects);
}

@Override

0 comments on commit 8432aab

Please sign in to comment.