Skip to content

Commit

Permalink
Showing 32 changed files with 788 additions and 417 deletions.
14 changes: 12 additions & 2 deletions core/src/main/java/org/jruby/RubyModule.java
Original file line number Diff line number Diff line change
@@ -1868,10 +1868,19 @@ public IRubyObject newMethod(IRubyObject receiver, final String methodName, bool

@JRubyMethod(name = "define_method", visibility = PRIVATE, reads = VISIBILITY)
public IRubyObject define_method(ThreadContext context, IRubyObject arg0, Block block) {
Visibility visibility = context.getCurrentVisibility();
Visibility visibility = getVisibilityForDefineMethod(context);

return defineMethodFromBlock(context, arg0, block, visibility);
}

private Visibility getVisibilityForDefineMethod(ThreadContext context) {
Visibility visibility = PUBLIC;

// These checks are similar to rb_vm_cref_in_context from MRI.
if (context.getCurrentFrame().getSelf() == this) visibility = context.getCurrentVisibility();
return visibility;
}

public IRubyObject defineMethodFromBlock(ThreadContext context, IRubyObject arg0, Block block, Visibility visibility) {
final Ruby runtime = context.runtime;
RubySymbol nameSym = TypeConverter.checkID(arg0);
@@ -1913,7 +1922,8 @@ public IRubyObject defineMethodFromBlock(ThreadContext context, IRubyObject arg0

@JRubyMethod(name = "define_method", visibility = PRIVATE, reads = VISIBILITY)
public IRubyObject define_method(ThreadContext context, IRubyObject arg0, IRubyObject arg1, Block block) {
Visibility visibility = context.getCurrentVisibility();
Visibility visibility = getVisibilityForDefineMethod(context);

return defineMethodFromCallable(context, arg0, arg1, visibility);
}

Original file line number Diff line number Diff line change
@@ -261,7 +261,7 @@ public static MethodHandle finishAdapting(final SmartBinder binder, final RubyMo
SmartHandle smartTarget = targetBinder.invoke(method);
if (frame) {
smartTarget = SmartHandle
.from(smartTarget.signature(), InvocationLinker.wrapWithFrameOnly(binder.baseSignature(), implementationClass, rubyName, smartTarget.handle(), null));
.from(smartTarget.signature(), InvocationLinker.wrapWithFrameOnly(binder.baseSignature(), implementationClass, rubyName, smartTarget.handle()));
}

MethodHandle target = smartTarget.handle();
8 changes: 7 additions & 1 deletion core/src/main/java/org/jruby/ir/targets/Bootstrap.java
Original file line number Diff line number Diff line change
@@ -7,6 +7,7 @@
import org.jcodings.Encoding;
import org.jcodings.EncodingDB;
import org.jruby.*;
import org.jruby.anno.JRubyMethod;
import org.jruby.common.IRubyWarnings;
import org.jruby.internal.runtime.GlobalVariable;
import org.jruby.internal.runtime.methods.*;
@@ -19,10 +20,10 @@
import org.jruby.runtime.CompiledIRBlockBody;
import org.jruby.runtime.DynamicScope;
import org.jruby.runtime.Frame;
import org.jruby.runtime.Helpers;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.runtime.invokedynamic.GlobalSite;
import org.jruby.runtime.invokedynamic.InvocationLinker;
import org.jruby.runtime.invokedynamic.MathLinker;
import org.jruby.runtime.invokedynamic.VariableSite;
import org.jruby.runtime.ivars.FieldVariableAccessor;
@@ -589,6 +590,11 @@ static MethodHandle buildNativeHandle(InvokeSite site, DynamicMethod method, boo
.handle();
}
}

JRubyMethod anno = nativeCall.getMethod().getAnnotation(JRubyMethod.class);
if (anno != null && anno.frame()) {
mh = InvocationLinker.wrapWithFrameOnly(site.signature, method.getImplementationClass(), site.name(), mh);
}
}
}

4 changes: 4 additions & 0 deletions core/src/main/java/org/jruby/runtime/ThreadContext.java
Original file line number Diff line number Diff line change
@@ -835,6 +835,10 @@ public void preMethodFrameOnly(RubyModule clazz, String name, IRubyObject self,
pushCallFrame(clazz, name, self, block);
}

public void preMethodFrameOnly(RubyModule clazz, String name, IRubyObject self) {
pushCallFrame(clazz, name, self, Block.NULL_BLOCK);
}

public void postMethodFrameOnly() {
popFrame();
}
Original file line number Diff line number Diff line change
@@ -82,8 +82,8 @@ public static MethodHandle wrapWithFraming(Signature signature, CallConfiguratio
return nativeTarget;
}

public static MethodHandle wrapWithFrameOnly(Signature signature, RubyModule implClass, String name, MethodHandle nativeTarget, StaticScope scope) {
MethodHandle framePre = getFramePre(signature, CallConfiguration.FrameFullScopeNone, implClass, name, scope);
public static MethodHandle wrapWithFrameOnly(Signature signature, RubyModule implClass, String name, MethodHandle nativeTarget) {
MethodHandle framePre = getFrameOnlyPre(signature, CallConfiguration.FrameFullScopeNone, implClass, name);

MethodHandle framePost = getFramePost(signature, CallConfiguration.FrameFullScopeNone);

@@ -141,14 +141,6 @@ public static MethodHandle getFramePre(Signature signature, CallConfiguration ca
.invokeVirtualQuiet(lookup(), "preMethodFrameOnly")
.handle();

case FrameNoneScopeFull:
// before logic
return binder
.permute("context")
.insert(1, arrayOf("selfClass", "scope"), arrayOf(RubyModule.class, StaticScope.class), implClass, scope)
.invokeVirtualQuiet(lookup(), "preMethodScopeOnly")
.handle();

case FrameNoneScopeDummy:
// before logic
return binder
@@ -157,11 +149,34 @@ public static MethodHandle getFramePre(Signature signature, CallConfiguration ca
.invokeVirtualQuiet(lookup(), "preMethodNoFrameAndDummyScope")
.handle();

case FrameNoneScopeFull:
return getFrameOnlyPre(signature, callConfig, implClass, name);

}

return null;
}

public static MethodHandle getFrameOnlyPre(Signature signature, CallConfiguration callConfig, RubyModule implClass, String name) {
Signature inbound = signature.asFold(void.class);
SmartBinder binder = SmartBinder
.from(inbound);

switch (callConfig) {
case FrameFullScopeNone:
// before logic
return binder
.permute("context", "self", "block")
.insert(1, arrayOf("selfClass", "name"), arrayOf(RubyModule.class, String.class), implClass, name)
.invokeVirtualQuiet(lookup(), "preMethodFrameOnly")
.handle();

default:
throw new RuntimeException("invalid input: " + callConfig);

}
}

public static MethodHandle getFramePost(Signature signature, CallConfiguration callConfig) {
Signature inbound = signature.asFold(void.class);
SmartBinder binder = SmartBinder
8 changes: 7 additions & 1 deletion core/src/main/java/org/jruby/util/RubyDateFormatter.java
Original file line number Diff line number Diff line change
@@ -43,6 +43,7 @@
import java.util.List;
import java.util.Locale;

import jnr.constants.platform.Errno;
import org.jcodings.Encoding;
import org.jcodings.specific.ASCIIEncoding;
import org.joda.time.Chronology;
@@ -548,7 +549,12 @@ public ByteList formatToByteList(List<Token> compiledPattern, DateTime dt, long
throw new Error("FORMAT_SPECIAL is a special token only for the lexer.");
}

output = formatter.format(output, value, type);
try {
output = formatter.format(output, value, type);
} catch (IndexOutOfBoundsException ioobe) {
throw context.runtime.newErrnoFromErrno(Errno.ERANGE, "strftime");
}

// reset formatter
formatter = RubyTimeOutputFormatter.DEFAULT_FORMATTER;

Original file line number Diff line number Diff line change
@@ -119,11 +119,15 @@ static String formatSignedNumber(long value, int width, char padder) {
}
}

private static final int SMALLBUF = 100;

static String padding(String sequence, int width, char padder) {
if (sequence.length() >= width) {
return sequence;
}

if (width > SMALLBUF) throw new IndexOutOfBoundsException("padding width " + width + " too large");

StringBuilder buf = new StringBuilder(width + sequence.length());
for (int i = sequence.length(); i < width; i++) {
buf.append(padder);
3 changes: 1 addition & 2 deletions core/src/main/java/org/jruby/util/io/OpenFile.java
Original file line number Diff line number Diff line change
@@ -2077,8 +2077,7 @@ else if (encs.enc != EncodingUtils.ascii8bitEncoding(context.runtime))
}

if (writeconv != null) {
((RubyString) str).setValue(
EncodingUtils.econvStrConvert(context, writeconv, ((RubyString) str).getByteList(), EConvFlags.PARTIAL_INPUT));
str = context.runtime.newString(EncodingUtils.econvStrConvert(context, writeconv, ((RubyString) str).getByteList(), EConvFlags.PARTIAL_INPUT));
}
}
// #if defined(RUBY_TEST_CRLF_ENVIRONMENT) || defined(_WIN32)
61 changes: 51 additions & 10 deletions spec/ruby/core/module/define_method_spec.rb
Original file line number Diff line number Diff line change
@@ -73,19 +73,60 @@ def test_method

describe "Module#define_method when name is not a special private name" do
describe "given an UnboundMethod" do
it "sets the visibility of the method to the current visibility" do
m = Module.new do
def foo
describe "and called from the target module" do
it "sets the visibility of the method to the current visibility" do
klass = Class.new do
define_method(:bar, ModuleSpecs::EmptyFooMethod)
private
define_method(:baz, ModuleSpecs::EmptyFooMethod)
end
private :foo

klass.should have_public_instance_method(:bar)
klass.should have_private_instance_method(:baz)
end
klass = Class.new do
define_method(:bar, m.instance_method(:foo))
private
define_method(:baz, m.instance_method(:foo))
end

describe "and called from another module" do
it "sets the visibility of the method to public" do
klass = Class.new
Class.new do
klass.send(:define_method, :bar, ModuleSpecs::EmptyFooMethod)
private
klass.send(:define_method, :baz, ModuleSpecs::EmptyFooMethod)
end

klass.should have_public_instance_method(:bar)
klass.should have_public_instance_method(:baz)
end
end
end

describe "passed a block" do
describe "and called from the target module" do
it "sets the visibility of the method to the current visibility" do
klass = Class.new do
define_method(:bar) {}
private
define_method(:baz) {}
end

klass.should have_public_instance_method(:bar)
klass.should have_private_instance_method(:baz)
end
end

describe "and called from another module" do
it "sets the visibility of the method to public" do
klass = Class.new
Class.new do
klass.send(:define_method, :bar) {}
private
klass.send(:define_method, :baz) {}
end

klass.should have_public_instance_method(:bar)
klass.should have_public_instance_method(:baz)
end
klass.should have_public_instance_method(:bar)
klass.should have_private_instance_method(:baz)
end
end
end
7 changes: 7 additions & 0 deletions spec/ruby/core/module/fixtures/classes.rb
Original file line number Diff line number Diff line change
@@ -576,6 +576,13 @@ def self.===(*)
raise 'method contents are irrelevant to test'
end
end

m = Module.new do
def foo
end
private :foo
end
EmptyFooMethod = m.instance_method(:foo)
end

class Object
25 changes: 25 additions & 0 deletions spec/truffle/specs/truffle/interop/boxed_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# 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

require_relative '../../../../ruby/spec_helper'

describe "Truffle::Interop.boxed?" do

it "returns false for empty strings" do
Truffle::Interop.boxed?('').should be_false
end

it "returns true for strings with one byte" do
Truffle::Interop.boxed?('1').should be_true
end

it "returns false for strings with two bytes" do
Truffle::Interop.boxed?('12').should be_false
end

end
36 changes: 36 additions & 0 deletions spec/truffle/specs/truffle/interop/executable_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# 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

require_relative '../../../../ruby/spec_helper'

describe "Truffle::Interop.executable?" do

def test_method
end

it "returns true for methods" do
Truffle::Interop.executable?(method(:test_method)).should be_true
end

it "returns true for procs" do
Truffle::Interop.executable?(proc { }).should be_true
end

it "returns true for lambdas" do
Truffle::Interop.executable?(lambda { }).should be_true
end

it "returns false for nil" do
Truffle::Interop.executable?(nil).should be_false
end

it "returns false for strings" do
Truffle::Interop.executable?('hello').should be_false
end

end
37 changes: 37 additions & 0 deletions spec/truffle/specs/truffle/interop/execute_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# 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

require_relative '../../../../ruby/spec_helper'

describe "Truffle::Interop.execute" do

def add(a, b)
a + b
end

it "calls methods" do
Truffle::Interop.execute(method(:add), 14, 2).should == 16
end

it "calls procs" do
Truffle::Interop.execute(proc { |a, b| a + b }, 14, 2).should == 16
end

it "calls lambdas" do
Truffle::Interop.execute(lambda { |a, b| a + b }, 14, 2).should == 16
end

it "doesn't call nil" do
lambda { Truffle::Interop.execute(nil) }.should raise_error(RubyTruffleError)
end

it "doesn't call strings" do
lambda { Truffle::Interop.execute('hello') }.should raise_error(RubyTruffleError)
end

end
29 changes: 29 additions & 0 deletions spec/truffle/specs/truffle/interop/invoke_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# 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

require_relative '../../../../ruby/spec_helper'

describe "Truffle::Interop.invoke" do

class InvokeTestClass

def add(a, b)
a + b
end

end

it "invokes methods using symbols" do
Truffle::Interop.invoke(InvokeTestClass.new, :add, 14, 2).should == 16
end

it "invokes methods using strings" do
Truffle::Interop.invoke(InvokeTestClass.new, 'add', 14, 2).should == 16
end

end
29 changes: 29 additions & 0 deletions spec/truffle/specs/truffle/interop/null_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# 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

require_relative '../../../../ruby/spec_helper'

describe "Truffle::Interop.null?" do

it "returns true for nil" do
Truffle::Interop.null?(nil).should be_true
end

it "returns false for strings" do
Truffle::Interop.null?('').should be_false
end

it "returns false for zero" do
Truffle::Interop.null?(0).should be_false
end

it "returns false for false" do
Truffle::Interop.null?(false).should be_false
end

end
79 changes: 79 additions & 0 deletions spec/truffle/specs/truffle/interop/read_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# 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

require_relative '../../../../ruby/spec_helper'

describe "Truffle::Interop.read" do

class ReadInstanceVariable

def initialize
@foo = 14
end

end

class HasMethod

def foo
14
end

end

class HasIndex

def [](n)
14
end

end

it "reads a byte from a string" do
Truffle::Interop.read('123', 1).should == '2'.ord
end

describe "reads an instance variable if given an @name" do
it "as a symbol" do
Truffle::Interop.read(ReadInstanceVariable.new, :@foo).should == 14
end

it "as a string" do
Truffle::Interop.read(ReadInstanceVariable.new, '@foo').should == 14
end
end

describe "calls #[] if there isn't a method with the same name" do
it "as a symbol" do
Truffle::Interop.read(HasIndex.new, :foo).should == 14
end

it "as a string" do
Truffle::Interop.read(HasIndex.new, 'foo').should == 14
end
end

describe "calls a method if there is a method with the same name" do
it "as a symbol" do
Truffle::Interop.read(HasMethod.new, :foo).should == 14
end

it "as a string" do
Truffle::Interop.read(HasMethod.new, 'foo').should == 14
end
end

it "can be used to index an array" do
Truffle::Interop.read([1, 2, 3], 1).should == 2
end

it "can be used to index a hash" do
Truffle::Interop.read({1 => 2, 3 => 4, 5 => 6}, 3).should == 4
end

end
37 changes: 37 additions & 0 deletions spec/truffle/specs/truffle/interop/size_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# 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

require_relative '../../../../ruby/spec_helper'

describe "Truffle::Interop.size" do

class InteropSizeClass

def size
14
end

end

it "returns the size of an array" do
Truffle::Interop.size([1, 2, 3]).should == 3
end

it "returns the size of an array" do
Truffle::Interop.size({a: 1, b: 2, c: 3}).should == 3
end

it "returns the size of an string" do
Truffle::Interop.size('123').should == 3
end

it "returns the size of any object with a size method" do
Truffle::Interop.size(InteropSizeClass.new).should == 14
end

end
29 changes: 29 additions & 0 deletions spec/truffle/specs/truffle/interop/sizep_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# 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

require_relative '../../../../ruby/spec_helper'

describe "Truffle::Interop.size?" do

it "returns true for arrays" do
Truffle::Interop.size?([]).should be_true
end

it "returns true for hashes" do
Truffle::Interop.size?({}).should be_true
end

it "returns true for strings" do
Truffle::Interop.size?('').should be_true
end

it "returns false for nil" do
Truffle::Interop.size?(nil).should be_false
end

end
45 changes: 45 additions & 0 deletions spec/truffle/specs/truffle/interop/unbox_spec.rb
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

require_relative '../../../../ruby/spec_helper'

describe "Truffle::Interop.unbox" do

it "passes through fixnums" do
Truffle::Interop.unbox(14).should == 14
end

it "passes through floats" do
Truffle::Interop.unbox(14.2).should == 14.2
end

it "passes through true" do
Truffle::Interop.unbox(true).should == true
end

it "passes through false" do
Truffle::Interop.unbox(false).should == false
end

it "doesn't work on empty strings" do
lambda { Truffle::Interop.unbox('') }.should raise_error(RubyTruffleError)
end

it "returns the first byte on strings with one byte" do
Truffle::Interop.unbox('1').should == '1'.ord
end

it "returns the first byte on strings with two bytes" do
Truffle::Interop.unbox('1').should == '1'.ord
end

it "returns nil for nil" do
Truffle::Interop.unbox(nil).should be_nil
end

end
91 changes: 91 additions & 0 deletions spec/truffle/specs/truffle/interop/write_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# 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

require_relative '../../../../ruby/spec_helper'

describe "Truffle::Interop.write" do

class HasMethod

def foo=(value)
@called = true
end

def called?
@called
end

end

class HasIndexSet

def []=(n, value)
@called = true
end

def called?
@called
end

end

describe "writes an instance variable if given an @name" do
it "as a symbol" do
object = Object.new
Truffle::Interop.write object, :@foo, 14
object.instance_variable_get(:@foo).should == 14
end

it "as a string" do
object = Object.new
Truffle::Interop.write object, '@foo', 14
object.instance_variable_get(:@foo).should == 14
end
end

describe "calls #[]= if there isn't a method with the same name" do
it "as a symbol" do
object = HasIndexSet.new
Truffle::Interop.write object, :foo, 14
object.called?.should be_true
end

it "as a string" do
object = HasIndexSet.new
Truffle::Interop.write object, 'foo', 14
object.called?.should be_true
end
end

describe "calls a method if there is a method with the same name plus =" do
it "as a symbol" do
object = HasMethod.new
Truffle::Interop.write object, :foo, 14
object.called?.should be_true
end

it "as a string" do
object = HasMethod.new
Truffle::Interop.write object, 'foo', 14
object.called?.should be_true
end
end

it "can be used to assign an array" do
array = [1, 2, 3]
Truffle::Interop.write array, 1, 14
array[1].should == 14
end

it "can be used to assign a hash" do
hash = {1 => 2, 3 => 4, 5 => 6}
Truffle::Interop.write hash, 3, 14
hash[3].should == 14
end

end
2 changes: 2 additions & 0 deletions spec/truffle/tags/core/module/define_method_tags.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
fails:Module#define_method when name is not a special private name given an UnboundMethod and called from another module sets the visibility of the method to public
fails:Module#define_method when name is not a special private name passed a block and called from another module sets the visibility of the method to public
20 changes: 20 additions & 0 deletions test/truffle/compiler/pe/core/interop_pe.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# 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

module InteropPE
class A
attr_accessor :foo
def initialize(value)
@foo = value
end
end
end


example "Truffle::Interop.read(InteropPE::A.new(42), :@foo)", 42
example "Truffle::Interop.read(InteropPE::A.new(42), '@foo')", 42
1 change: 1 addition & 0 deletions test/truffle/compiler/pe/pe.rb
Original file line number Diff line number Diff line change
@@ -86,6 +86,7 @@ def counter(example)
require_relative 'core/block_given_pe.rb'
require_relative 'core/string_pe.rb'
require_relative 'core/class_pe'
require_relative 'core/interop_pe'
require_relative 'macro/pushing_pixels_pe.rb'

if Truffle::Interop.mime_type_supported?('application/javascript')
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
package org.jruby.truffle.interop;

/*
* Copyright (c) 2013, 2016 Oracle and/or its affiliates. All rights reserved. This
* code is released under a tri EPL/GPL/LGPL license. You can use it,
@@ -9,6 +7,8 @@
* GNU General Public License version 2
* GNU Lesser General Public License version 2.1
*/
package org.jruby.truffle.interop;

import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.NodeChild;
@@ -27,75 +27,59 @@
@NodeChild("receiver"),
@NodeChild("name"),
@NodeChild("stringName"),
@NodeChild("startsAt")
@NodeChild("isIVar")
})
abstract class ForeignReadStringCachedHelperNode extends RubyNode {

@Child
private DoesRespondDispatchHeadNode definedNode;
@Child
private DoesRespondDispatchHeadNode indexDefinedNode;
@Child
private CallDispatchHeadNode callNode;
@Child private DoesRespondDispatchHeadNode definedNode;
@Child private DoesRespondDispatchHeadNode indexDefinedNode;
@Child private CallDispatchHeadNode callNode;

protected final static String INDEX_METHOD_NAME = "[]";

public abstract Object executeStringCachedHelper(VirtualFrame frame, DynamicObject receiver, Object name,
String stringName, boolean startsAt);
public abstract Object executeStringCachedHelper(VirtualFrame frame, DynamicObject receiver, Object name, Object stringName, boolean isIVar);

@Specialization(guards = "startsAt(startsAt)")
@Specialization(guards = "isIVar")
public Object readInstanceVariable(
DynamicObject receiver,
Object name,
String stringName,
boolean startsAt,
Object stringName,
boolean isIVar,
@Cached("createReadObjectFieldNode(stringName)") ReadObjectFieldNode readObjectFieldNode) {
return readObjectFieldNode.execute(receiver);
}

protected boolean startsAt(boolean startsAt) {
return startsAt;
}

protected ReadObjectFieldNode createReadObjectFieldNode(String name) {
protected ReadObjectFieldNode createReadObjectFieldNode(Object name) {
return ReadObjectFieldNodeGen.create(name, nil());
}

@Specialization(
guards = {
"notStartsAt(startsAt)",
"methodDefined(frame, receiver, stringName, getDefinedNode())"
}
)
@Specialization(guards = {
"!isIVar",
"methodDefined(frame, receiver, stringName, getDefinedNode())"
})
public Object callMethod(
VirtualFrame frame,
DynamicObject receiver,
Object name,
String stringName,
boolean startsAt) {
Object stringName,
boolean isIVar) {
return getCallNode().call(frame, receiver, stringName, null);
}

@Specialization(
guards = {
"notStartsAt(startsAt)",
"!methodDefined(frame, receiver, stringName, getDefinedNode())",
"methodDefined(frame, receiver, INDEX_METHOD_NAME, getIndexDefinedNode())"
}
)
@Specialization(guards = {
"!isIVar",
"!methodDefined(frame, receiver, stringName, getDefinedNode())",
"methodDefined(frame, receiver, INDEX_METHOD_NAME, getIndexDefinedNode())"
})
public Object index(
VirtualFrame frame,
DynamicObject receiver,
Object name,
String stringName,
boolean startsAt) {
Object stringName,
boolean isIVar) {
return getCallNode().call(frame, receiver, "[]", null, name);
}

protected boolean notStartsAt(boolean startsAt) {
return !startsAt;
}

protected DoesRespondDispatchHeadNode getDefinedNode() {
if (definedNode == null) {
CompilerDirectives.transferToInterpreter();
@@ -114,9 +98,13 @@ protected DoesRespondDispatchHeadNode getIndexDefinedNode() {
return indexDefinedNode;
}

protected boolean methodDefined(VirtualFrame frame, DynamicObject receiver, String stringName,
protected boolean methodDefined(VirtualFrame frame, DynamicObject receiver, Object stringName,
DoesRespondDispatchHeadNode definedNode) {
return definedNode.doesRespondTo(frame, stringName, receiver);
if (stringName == null) {
return false;
} else {
return definedNode.doesRespondTo(frame, stringName, receiver);
}
}

protected CallDispatchHeadNode getCallNode() {
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
package org.jruby.truffle.interop;

/*
* Copyright (c) 2013, 2016 Oracle and/or its affiliates. All rights reserved. This
* code is released under a tri EPL/GPL/LGPL license. You can use it,
@@ -9,6 +7,7 @@
* GNU General Public License version 2
* GNU Lesser General Public License version 2.1
*/
package org.jruby.truffle.interop;

import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.Cached;
@@ -20,8 +19,6 @@
import com.oracle.truffle.api.object.DynamicObject;
import org.jruby.truffle.Layouts;
import org.jruby.truffle.RubyContext;
import org.jruby.truffle.core.rope.Rope;
import org.jruby.truffle.core.rope.RopeOperations;
import org.jruby.truffle.core.string.StringCachingGuards;
import org.jruby.truffle.language.RubyNode;

@@ -32,147 +29,58 @@
})
abstract class ForeignReadStringCachingHelperNode extends RubyNode {

private @Child IsStringLikeNode isStringLikeNode;

public ForeignReadStringCachingHelperNode(RubyContext context) {
super(context, null);
}

public abstract Object executeStringCachingHelper(VirtualFrame frame, DynamicObject receiver, Object name);

@Specialization(
guards = {
"isRubyString(name)",
"ropesEqual(name, cachedRope)"
},
limit = "getCacheLimit()"
)
public Object cacheStringAndForward(
VirtualFrame frame,
DynamicObject receiver,
DynamicObject name,
@Cached("privatizeRope(name)") Rope cachedRope,
@Cached("ropeToString(cachedRope)") String cachedString,
@Cached("startsWithAt(cachedString)") boolean cachedStartsWithAt,
@Cached("createNextHelper()") ForeignReadStringCachedHelperNode nextHelper) {
return nextHelper.executeStringCachedHelper(frame, receiver, name, cachedString, cachedStartsWithAt);
}

@Specialization(
guards = "isRubyString(name)",
contains = "cacheStringAndForward"
)
public Object uncachedStringAndForward(
VirtualFrame frame,
DynamicObject receiver,
DynamicObject name,
@Cached("createNextHelper()") ForeignReadStringCachedHelperNode nextHelper) {
final String nameString = objectToString(name);
return nextHelper.executeStringCachedHelper(frame, receiver, name, nameString, startsWithAt(nameString));
}

@Specialization(
guards = {
"isRubySymbol(name)",
"name == cachedName"
},
limit = "getCacheLimit()"
)
public Object cacheSymbolAndForward(
VirtualFrame frame,
DynamicObject receiver,
DynamicObject name,
@Cached("name") DynamicObject cachedName,
@Cached("objectToString(cachedName)") String cachedString,
@Cached("startsWithAt(cachedString)") boolean cachedStartsWithAt,
@Specialization(guards = "isStringLike(name)")
public Object cacheStringLikeAndForward(VirtualFrame frame, DynamicObject receiver, Object name,
@Cached("create()") ToJavaStringNode toJavaStringNode,
@Cached("createNextHelper()") ForeignReadStringCachedHelperNode nextHelper) {
return nextHelper.executeStringCachedHelper(frame, receiver, cachedName, cachedString, cachedStartsWithAt);
String nameAsJavaString = toJavaStringNode.executeToJavaString(name);
boolean isIVar = isIVar(nameAsJavaString);
return nextHelper.executeStringCachedHelper(frame, receiver, name, nameAsJavaString, isIVar);
}

@Specialization(
guards = "isRubySymbol(name)",
contains = "cacheSymbolAndForward"
)
public Object uncachedSymbolAndForward(
VirtualFrame frame,
DynamicObject receiver,
DynamicObject name,
@Cached("createNextHelper()") ForeignReadStringCachedHelperNode nextHelper) {
final String nameString = objectToString(name);
return nextHelper.executeStringCachedHelper(frame, receiver, name, nameString, startsWithAt(nameString));
@Specialization(guards = { "isRubyString(receiver)", "inRange(receiver, index)" })
public int indexString(DynamicObject receiver, int index) {
return Layouts.STRING.getRope(receiver).get(index);
}

@Specialization(
guards = "name == cachedName",
limit = "getCacheLimit()"
)
public Object cacheJavaStringAndForward(
VirtualFrame frame,
DynamicObject receiver,
String name,
@Cached("name") String cachedName,
@Cached("startsWithAt(cachedName)") boolean cachedStartsWithAt,
@Cached("createNextHelper()") ForeignReadStringCachedHelperNode nextHelper) {
return nextHelper.executeStringCachedHelper(frame, receiver, cachedName, cachedName, cachedStartsWithAt);
@Specialization(guards = { "isRubyString(receiver)", "!inRange(receiver, index)" })
public int indexStringOutOfRange(DynamicObject receiver, int index) {
return 0;
}

@Specialization(contains = "cacheJavaStringAndForward")
public Object uncachedJavaStringAndForward(
VirtualFrame frame,
DynamicObject receiver,
String name,
@Specialization(guards = { "!isRubyString(receiver)", "!isStringLike(name)" })
public Object indexObject(VirtualFrame frame, DynamicObject receiver, Object name,
@Cached("createNextHelper()") ForeignReadStringCachedHelperNode nextHelper) {
return nextHelper.executeStringCachedHelper(frame, receiver, name, name, startsWithAt(name));
return nextHelper.executeStringCachedHelper(frame, receiver, name, null, false);
}

protected ForeignReadStringCachedHelperNode createNextHelper() {
return ForeignReadStringCachedHelperNodeGen.create(null, null, null, null);
protected boolean inRange(DynamicObject string, int index) {
return index >= 0 && index < Layouts.STRING.getRope(string).byteLength();
}

@CompilerDirectives.TruffleBoundary
protected String objectToString(DynamicObject string) {
return string.toString();
}
protected boolean isStringLike(Object value) {
if (isStringLikeNode == null) {
CompilerDirectives.transferToInterpreter();
isStringLikeNode = insert(IsStringLikeNode.create());
}

protected String ropeToString(Rope rope) {
return RopeOperations.decodeRope(getContext().getJRubyRuntime(), rope);
return isStringLikeNode.executeIsStringLike(value);
}

@CompilerDirectives.TruffleBoundary
protected boolean startsWithAt(String name) {
protected boolean isIVar(String name) {
return !name.isEmpty() && name.charAt(0) == '@';
}

@Specialization(guards = {
"isRubyString(receiver)",
"index < 0"
})
public int indexStringNegative(DynamicObject receiver, int index) {
return 0;
}

@Specialization(guards = {
"isRubyString(receiver)",
"index >= 0",
"!inRange(receiver, index)"
})
public int indexStringOutOfRange(DynamicObject receiver, int index) {
return 0;
}

@Specialization(guards = {
"isRubyString(receiver)",
"index >= 0",
"inRange(receiver, index)"
})
public int indexString(DynamicObject receiver, int index) {
return Layouts.STRING.getRope(receiver).get(index);
}

protected boolean inRange(DynamicObject string, int index) {
return index < Layouts.STRING.getRope(string).byteLength();
}

protected int getCacheLimit() {
return getContext().getOptions().INTEROP_READ_CACHE;
protected ForeignReadStringCachedHelperNode createNextHelper() {
return ForeignReadStringCachedHelperNodeGen.create(null, null, null, null);
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
package org.jruby.truffle.interop;

/*
* Copyright (c) 2013, 2016 Oracle and/or its affiliates. All rights reserved. This
* code is released under a tri EPL/GPL/LGPL license. You can use it,
@@ -9,6 +7,8 @@
* GNU General Public License version 2
* GNU Lesser General Public License version 2.1
*/
package org.jruby.truffle.interop;

import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.NodeChild;
@@ -27,84 +27,71 @@
@NodeChild("receiver"),
@NodeChild("name"),
@NodeChild("stringName"),
@NodeChild("startsAt"),
@NodeChild("isIVar"),
@NodeChild("value")
})
abstract class ForeignWriteStringCachedHelperNode extends RubyNode {

@Child
private DoesRespondDispatchHeadNode definedNode;
@Child
private DoesRespondDispatchHeadNode indexDefinedNode;
@Child
private CallDispatchHeadNode callNode;
private @Child DoesRespondDispatchHeadNode definedNode;
private @Child DoesRespondDispatchHeadNode indexDefinedNode;
private @Child CallDispatchHeadNode callNode;

protected final static String INDEX_METHOD_NAME = "[]=";
protected final static String INDEX_SET_METHOD_NAME = "[]=";

public abstract Object executeStringCachedHelper(VirtualFrame frame, DynamicObject receiver, Object name,
String stringName, boolean startsAt, Object value);
Object stringName, boolean isIVar, Object value);

@Specialization(guards = "startsAt(startsAt)")
@Specialization(guards = "isIVar")
public Object readInstanceVariable(
DynamicObject receiver,
Object name,
String stringName,
boolean startsAt,
Object stringName,
boolean isIVar,
Object value,
@Cached("createWriteObjectFieldNode(stringName)") WriteObjectFieldNode writeObjectFieldNode) {
writeObjectFieldNode.execute(receiver, value);
return value;
}

protected boolean startsAt(boolean startsAt) {
return startsAt;
}

protected WriteObjectFieldNode createWriteObjectFieldNode(String name) {
protected WriteObjectFieldNode createWriteObjectFieldNode(Object name) {
return WriteObjectFieldNodeGen.create(name);
}

@Specialization(
guards = {
"notStartsAt(startsAt)",
"methodDefined(frame, receiver, writeMethodName, getDefinedNode())"
}
)
@Specialization(guards = { "not(isIVar)", "methodDefined(frame, receiver, writeMethodName, getDefinedNode())" })
public Object callMethod(
VirtualFrame frame,
DynamicObject receiver,
Object name,
String stringName,
boolean startsAt,
Object stringName,
boolean isIVar,
Object value,
@Cached("createWriteMethodName(stringName)") String writeMethodName) {
return getCallNode().call(frame, receiver, writeMethodName, null, value);
}

protected String createWriteMethodName(String name) {
// Workaround for DSL bug
protected boolean not(boolean value) {
return !value;
}

protected String createWriteMethodName(Object name) {
return name + "=";
}

@Specialization(
guards = {
"notStartsAt(startsAt)",
"!methodDefined(frame, receiver, writeMethodName, getDefinedNode())",
"methodDefined(frame, receiver, INDEX_METHOD_NAME, getIndexDefinedNode())"
}
)
@Specialization(guards = {
"!isIVar",
"!methodDefined(frame, receiver, writeMethodName, getDefinedNode())",
"methodDefined(frame, receiver, INDEX_SET_METHOD_NAME, getIndexDefinedNode())"
})
public Object index(
VirtualFrame frame,
DynamicObject receiver,
Object name,
String stringName,
boolean startsAt,
Object stringName,
boolean isIVar,
Object value,
@Cached("createWriteMethodName(stringName)") String writeMethodName) {
return getCallNode().call(frame, receiver, "[]", null, name, value);
}

protected boolean notStartsAt(boolean startsAt) {
return !startsAt;
return getCallNode().call(frame, receiver, "[]=", null, name, value);
}

protected DoesRespondDispatchHeadNode getDefinedNode() {
@@ -125,9 +112,13 @@ protected DoesRespondDispatchHeadNode getIndexDefinedNode() {
return indexDefinedNode;
}

protected boolean methodDefined(VirtualFrame frame, DynamicObject receiver, String stringName,
protected boolean methodDefined(VirtualFrame frame, DynamicObject receiver, Object stringName,
DoesRespondDispatchHeadNode definedNode) {
return definedNode.doesRespondTo(frame, stringName, receiver);
if (stringName == null) {
return false;
} else {
return definedNode.doesRespondTo(frame, stringName, receiver);
}
}

protected CallDispatchHeadNode getCallNode() {
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
package org.jruby.truffle.interop;

/*
* Copyright (c) 2013, 2016 Oracle and/or its affiliates. All rights reserved. This
* code is released under a tri EPL/GPL/LGPL license. You can use it,
@@ -9,6 +7,8 @@
* GNU General Public License version 2
* GNU Lesser General Public License version 2.1
*/
package org.jruby.truffle.interop;

import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.ImportStatic;
@@ -18,8 +18,6 @@
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.object.DynamicObject;
import org.jruby.truffle.RubyContext;
import org.jruby.truffle.core.rope.Rope;
import org.jruby.truffle.core.rope.RopeOperations;
import org.jruby.truffle.core.string.StringCachingGuards;
import org.jruby.truffle.language.RubyNode;

@@ -31,128 +29,45 @@
})
abstract class ForeignWriteStringCachingHelperNode extends RubyNode {

private @Child IsStringLikeNode isStringLikeNode;

public ForeignWriteStringCachingHelperNode(RubyContext context) {
super(context, null);
}

public abstract Object executeStringCachingHelper(VirtualFrame frame, DynamicObject receiver,
Object name, Object value);
public abstract Object executeStringCachingHelper(VirtualFrame frame, DynamicObject receiver, Object name, Object value);

@Specialization(
guards = {
"isRubyString(name)",
"ropesEqual(name, cachedRope)"
},
limit = "getCacheLimit()"
)
public Object cacheStringAndForward(
VirtualFrame frame,
DynamicObject receiver,
DynamicObject name,
Object value,
@Cached("privatizeRope(name)") Rope cachedRope,
@Cached("ropeToString(cachedRope)") String cachedString,
@Cached("startsWithAt(cachedString)") boolean cachedStartsWithAt,
@Specialization(guards = "isStringLike(name)")
public Object cacheStringLikeAndForward(VirtualFrame frame, DynamicObject receiver, Object name, Object value,
@Cached("create()") ToJavaStringNode toJavaStringNode,
@Cached("createNextHelper()") ForeignWriteStringCachedHelperNode nextHelper) {
return nextHelper.executeStringCachedHelper(frame, receiver, name, cachedString, cachedStartsWithAt, value);
String nameAsJavaString = toJavaStringNode.executeToJavaString(name);
boolean isIVar = isIVar(nameAsJavaString);
return nextHelper.executeStringCachedHelper(frame, receiver, name, nameAsJavaString, isIVar, value);
}

@Specialization(
guards = "isRubyString(name)",
contains = "cacheStringAndForward"
)
public Object uncachedStringAndForward(
VirtualFrame frame,
DynamicObject receiver,
DynamicObject name,
Object value,
@Specialization(guards = "!isStringLike(name)")
public Object passThroughNonString(VirtualFrame frame, DynamicObject receiver, Object name, Object value,
@Cached("create()") ToJavaStringNode toJavaStringNode,
@Cached("createNextHelper()") ForeignWriteStringCachedHelperNode nextHelper) {
final String nameString = objectToString(name);
return nextHelper.executeStringCachedHelper(frame, receiver, name, nameString,
startsWithAt(nameString), value);
return nextHelper.executeStringCachedHelper(frame, receiver, name, null, false, value);
}

@Specialization(
guards = {
"isRubySymbol(name)",
"name == cachedName"
},
limit = "getCacheLimit()"
)
public Object cacheSymbolAndForward(
VirtualFrame frame,
DynamicObject receiver,
DynamicObject name,
Object value,
@Cached("name") DynamicObject cachedName,
@Cached("objectToString(cachedName)") String cachedString,
@Cached("startsWithAt(cachedString)") boolean cachedStartsWithAt,
@Cached("createNextHelper()") ForeignWriteStringCachedHelperNode nextHelper) {
return nextHelper.executeStringCachedHelper(frame, receiver, cachedName, cachedString,
cachedStartsWithAt, value);
}
protected boolean isStringLike(Object value) {
if (isStringLikeNode == null) {
CompilerDirectives.transferToInterpreter();
isStringLikeNode = insert(IsStringLikeNode.create());
}

@Specialization(
guards = "isRubySymbol(name)",
contains = "cacheSymbolAndForward"
)
public Object uncachedSymbolAndForward(
VirtualFrame frame,
DynamicObject receiver,
DynamicObject name,
Object value,
@Cached("createNextHelper()") ForeignWriteStringCachedHelperNode nextHelper) {
final String nameString = objectToString(name);
return nextHelper.executeStringCachedHelper(frame, receiver, name, nameString,
startsWithAt(nameString), value);
}

@Specialization(
guards = "name == cachedName",
limit = "getCacheLimit()"
)
public Object cacheJavaStringAndForward(
VirtualFrame frame,
DynamicObject receiver,
String name,
Object value,
@Cached("name") String cachedName,
@Cached("startsWithAt(cachedName)") boolean cachedStartsWithAt,
@Cached("createNextHelper()") ForeignWriteStringCachedHelperNode nextHelper) {
return nextHelper.executeStringCachedHelper(frame, receiver, cachedName, cachedName,
cachedStartsWithAt, value);
return isStringLikeNode.executeIsStringLike(value);
}

@Specialization(contains = "cacheJavaStringAndForward")
public Object uncachedJavaStringAndForward(
VirtualFrame frame,
DynamicObject receiver,
String name,
Object value,
@Cached("createNextHelper()") ForeignWriteStringCachedHelperNode nextHelper) {
return nextHelper.executeStringCachedHelper(frame, receiver, name, name, startsWithAt(name), value);
protected boolean isIVar(String name) {
return !name.isEmpty() && name.charAt(0) == '@';
}

protected ForeignWriteStringCachedHelperNode createNextHelper() {
return ForeignWriteStringCachedHelperNodeGen.create(null, null, null, null, null);
}

@CompilerDirectives.TruffleBoundary
protected String objectToString(DynamicObject string) {
return string.toString();
}

protected String ropeToString(Rope rope) {
return RopeOperations.decodeRope(getContext().getJRubyRuntime(), rope);
}

@CompilerDirectives.TruffleBoundary
protected boolean startsWithAt(String name) {
return !name.isEmpty() && name.charAt(0) == '@';
}

protected int getCacheLimit() {
return getContext().getOptions().INTEROP_WRITE_CACHE;
}

}
55 changes: 13 additions & 42 deletions truffle/src/main/java/org/jruby/truffle/interop/InteropNodes.java
Original file line number Diff line number Diff line change
@@ -126,14 +126,14 @@ public Object invokeCached(
Object[] args,
@Cached("args.length") int cachedArgsLength,
@Cached("createInvokeNode(cachedArgsLength)") Node invokeNode,
@Cached("createToJavaStringNode()") ToJavaStringNode toJavaStringNode,
@Cached("create()") ToJavaStringNode toJavaStringNode,
@Cached("create()") BranchProfile exceptionProfile) {
try {
return ForeignAccess.sendInvoke(
invokeNode,
frame,
receiver,
toJavaStringNode.executeToJavaString(frame, identifier),
toJavaStringNode.executeToJavaString(identifier),
args);
} catch (UnsupportedTypeException
| ArityException
@@ -144,10 +144,6 @@ public Object invokeCached(
}
}

protected ToJavaStringNode createToJavaStringNode() {
return ToJavaStringNodeGen.create(getContext(), null, null);
}

@Specialization(
guards = "isRubyString(identifier) || isRubySymbol(identifier)",
contains = "invokeCached"
@@ -193,7 +189,7 @@ public boolean hasSize(
}

protected Node createHasSizeNode() {
return Message.IS_EXECUTABLE.createNode();
return Message.HAS_SIZE.createNode();
}

}
@@ -292,41 +288,6 @@ public boolean isBoxed(Object receiver) {
@CoreMethod(names = "unbox", isModuleFunction = true, needsSelf = false, required = 1)
public abstract static class UnboxNode extends CoreMethodArrayArgumentsNode {

@Specialization
public boolean unbox(boolean receiver) {
return receiver;
}

@Specialization
public byte unbox(byte receiver) {
return receiver;
}

@Specialization
public short unbox(short receiver) {
return receiver;
}

@Specialization
public int unbox(int receiver) {
return receiver;
}

@Specialization
public long unbox(long receiver) {
return receiver;
}

@Specialization
public float unbox(float receiver) {
return receiver;
}

@Specialization
public double unbox(double receiver) {
return receiver;
}

@Specialization
public DynamicObject unbox(CharSequence receiver) {
// TODO CS-21-Dec-15 this shouldn't be needed - we need to convert j.l.String to Ruby's String automatically
@@ -353,6 +314,11 @@ protected Node createUnboxNode() {
return Message.UNBOX.createNode();
}

@Fallback
public Object unbox(Object receiver) {
return receiver;
}

}

@CoreMethod(names = "null?", isModuleFunction = true, needsSelf = false, required = 1)
@@ -370,6 +336,11 @@ protected Node createIsNullNode() {
return Message.IS_NULL.createNode();
}

@Fallback
public boolean isNull(Object receiver) {
return false;
}

}

@CoreMethod(names = "read", isModuleFunction = true, needsSelf = false, required = 2)
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* 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.interop;

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.language.RubyNode;

@NodeChild(value = "value", type = RubyNode.class)
public abstract class IsStringLikeNode extends RubyNode {

public static IsStringLikeNode create() {
return IsStringLikeNodeGen.create(null);
}

public abstract boolean executeIsStringLike(Object value);

@Specialization(guards = "isRubyString(value)")
boolean isRubyString(DynamicObject value) {
return true;
}

@Specialization(guards = "isRubySymbol(value)")
public boolean isRubySymbol(DynamicObject value) {
return true;
}

@Specialization
public boolean isJavaString(String value) {
return true;
}

@Specialization(guards = { "!isRubyString(value)", "!isRubySymbol(value)", "!isString(value)" })
public boolean notStringLike(Object value) {
return false;
}

}
Original file line number Diff line number Diff line change
@@ -34,12 +34,8 @@ public RubyToForeignNode(RubyContext context, SourceSection sourceSection) {
public String convert(
VirtualFrame frame,
DynamicObject value,
@Cached("createToJavaStringNode()") ToJavaStringNode toJavaStringNode) {
return toJavaStringNode.executeToJavaString(frame, value);
}

protected ToJavaStringNode createToJavaStringNode() {
return ToJavaStringNodeGen.create(getContext(), null, null);
@Cached("create()") ToJavaStringNode toJavaStringNode) {
return toJavaStringNode.executeToJavaString(value);
}

@Specialization(guards = {"!isRubyString(value)", "!isRubySymbol(value)"})
Original file line number Diff line number Diff line change
@@ -14,11 +14,8 @@
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.frame.VirtualFrame;
import com.oracle.truffle.api.object.DynamicObject;
import com.oracle.truffle.api.source.SourceSection;
import org.jruby.truffle.Layouts;
import org.jruby.truffle.RubyContext;
import org.jruby.truffle.core.rope.Rope;
import org.jruby.truffle.core.string.StringCachingGuards;
import org.jruby.truffle.language.RubyNode;
@@ -27,45 +24,52 @@
@NodeChild(value = "value", type = RubyNode.class)
public abstract class ToJavaStringNode extends RubyNode {

public ToJavaStringNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
public static ToJavaStringNode create() {
return ToJavaStringNodeGen.create(null);
}

public abstract String executeToJavaString(VirtualFrame frame, Object value);
public abstract String executeToJavaString(Object value);

@Specialization(
guards = {
"isRubyString(value)",
"ropesEqual(value, cachedRope)"
},
limit = "getLimit()")
public String stringUncached(
DynamicObject value,
@Specialization(guards = { "isRubyString(value)", "ropesEqual(value, cachedRope)" }, limit = "getLimit()")
String stringCached(DynamicObject value,
@Cached("privatizeRope(value)") Rope cachedRope,
@Cached("value.toString()") String convertedString) {
return convertedString;
}

protected String objectToString(DynamicObject object) {
return object.toString();
}

@TruffleBoundary
@Specialization(guards = "isRubyString(value)", contains = "stringUncached")
public String stringCached(DynamicObject value) {
@Specialization(guards = "isRubyString(value)", contains = "stringCached")
public String stringUncached(DynamicObject value) {
return value.toString();
}

@Specialization(guards = "isRubySymbol(value)")
public String symbol(DynamicObject value) {
return Layouts.SYMBOL.getString(value);
@Specialization(guards = { "isRubySymbol(symbol)", "symbol == cachedSymbol" }, limit = "getLimit()")
public String symbolCached(DynamicObject symbol,
@Cached("symbol") DynamicObject cachedSymbol,
@Cached("symbolToString(symbol)") String convertedString) {
return convertedString;
}

@Specialization(guards = "isRubySymbol(symbol)", contains = "symbolCached")
public String symbolUncached(DynamicObject symbol) {
return symbolToString(symbol);
}

@Specialization
public String javaString(String value) {
@Specialization(guards = "string == cachedString", limit = "getLimit()")
public String javaStringCached(String string,
@Cached("string") String cachedString) {
return cachedString;
}

@Specialization(contains = "javaStringCached")
public String javaStringUncached(String value) {
return value;
}

protected String symbolToString(DynamicObject symbol) {
return Layouts.SYMBOL.getString(symbol);
}

protected int getLimit() {
return getContext().getOptions().INTEROP_CONVERT_CACHE;
}
Original file line number Diff line number Diff line change
@@ -47,7 +47,7 @@ public UncachedDispatchNode(RubyContext context, boolean ignoreVisibility, Dispa
this.missingBehavior = missingBehavior;
this.indirectCallNode = Truffle.getRuntime().createIndirectCallNode();
this.toSymbolNode = ToSymbolNodeGen.create(context, null, null);
this.toJavaStringNode = ToJavaStringNodeGen.create(context, null, null);
this.toJavaStringNode = ToJavaStringNodeGen.create();
this.metaClassNode = MetaClassNodeGen.create(context, null, null);
}

@@ -67,7 +67,7 @@ public Object executeDispatch(

final DynamicObject callerClass = ignoreVisibility ? null : metaClassNode.executeMetaClass(RubyArguments.getSelf(frame));

final InternalMethod method = lookup(callerClass, receiverObject, toJavaStringNode.executeToJavaString(frame, name), ignoreVisibility);
final InternalMethod method = lookup(callerClass, receiverObject, toJavaStringNode.executeToJavaString(name), ignoreVisibility);

if (method != null) {
if (dispatchAction == DispatchAction.CALL_METHOD) {

0 comments on commit dc775e6

Please sign in to comment.