-
Notifications
You must be signed in to change notification settings - Fork 186
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Array methods #2137
Array methods #2137
Conversation
Hello Rodrigo Botafogo, thanks for contributing a PR to our project! We use the Oracle Contributor Agreement to make the copyright of contributions clear. We don't have a record of you having signed this yet, based on your email address rodrigo -(dot)- a -(dot)- botafogo -(at)- gmail -(dot)- com. You can sign it at that link. If you think you've already signed it, please comment below and we'll check. |
…, but this is printed on the terminal and not caught by minitest. Needs fixing.
Note that you can use |
Could you add tests in |
jt format fails with the following results. Is there a way to get more information on why it's failling?
|
@rbotafogo When it does, it will have modified the files as the diff it shows, so just committing these changes should be enough. |
@eregon although foreign_array_spec specifies:
and the IndexError is beign raised and printed when the test is executed, it is not actually caught, so the test actually fails. Thanks |
You need to wrap in a lambda for the |
#eregon How do I return 'nil' from an interop call? |
You can use |
@eregon, ruby arrays can be indexed by a range or a starting index and size. In this case, the return value is another array. When indexing a foreign array with a range, should we return a RubyArray or another foreign array? My first instinct is to return a RubyArray, but I guess it might make more sense to stay in the foreign land. What do you think? |
@rbotafogo I wouldn't handle indexing with a Range in this PR, that's a lot of extra complicated logic. |
@eregon OK, will move on to other methods. Still, other methods such as 'take' also return an array, so should this be a foreign array or a ruby array? |
@rbotafogo I would keep it to just the methods in this PR, to not make it too big. Many Array methods are also available through Enumerable, including Since we don't really know how to build a foreign Array, I think it would be a Ruby Array. It seems in Ruby 3 Array and String methods might stop returning subclasses and just always return a plain Array/String. |
@eregon OCA signed and mailed. OK, understand you idea. But where should ForeignArray be created? Can you elaborate a bit? There is a ForeignObject class in TruffleDebugNodes.java, but this does not seem like what we want. |
I wrote more about it in #2149 |
Rodrigo Botafogo has signed the Oracle Contributor Agreement (based on email address rodrigo -(dot)- a -(dot)- botafogo -(at)- gmail -(dot)- com) so can contribute to this repository. |
.gitignore
Outdated
@@ -3,6 +3,9 @@ | |||
|
|||
# Editors | |||
*~ | |||
*.# | |||
*/**/#* | |||
*/**/.#* |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you undo changes here? They seem too broad.
You can use local excludes for this kind of editor-specific ignores: https://stackoverflow.com/questions/1753070/how-do-i-configure-git-to-ignore-some-files-locally
it "can be printed with #print" do | ||
foreign = Truffle::Interop.to_java_array([1, 2, 3]) | ||
@foreign |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Dead code, and more cases below
@foreign.length.should == 3 | ||
end | ||
|
||
it "can access elements by indexing #[]" do |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
by indexing +with+ (same below)
# foreign = Truffle::Interop.to_java_array([]) | ||
@foreign |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can be removed
@@ -70,6 +70,34 @@ | |||
pa.log.should include([:polyglot_read_array_element, 0]) | |||
end | |||
|
|||
it description['.at(index)', :at, (:index)] do |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The second argument should be the primary interop message that is used, in this case that's readArrayElement
.
The third argument should be an Array like above.
Same below.
l.log.should include(['getArraySize']) | ||
pa.log.should include([:polyglot_array_size]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These should check for readArrayElement
since that's the primary interop message used.
Same below of course.
@Cached InteropNodes.ReadArrayElementNode readNode) { | ||
try { | ||
long size = interop.getArraySize(receiver); | ||
long bound = (size != 0) ? size : 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems to be the same as bound = size
, and so the bound
variable seems redundant.
try { | ||
long size = interop.getArraySize(receiver); | ||
long bound = (size != 0) ? size : 0; | ||
if (((int) args[0]) < -bound || (int) args[0] >= bound) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This cast will crash if args[0]
is a long
, a short
or a byte
.
Use LongCastNode
to properly cast it to a long
no matter what type it is.
throw new RaiseException( | ||
context, | ||
context.getCoreExceptions().indexError("Index " + (int) args[0] + | ||
" outside of array bounds: -" + bound + "..." + bound, this)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, InteropNodes.ReadArrayElementNode
already raises an IndexError if the index is out of bounds, so I think we don't need different logic for fetch
here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But the exception message send by InteropNodes.ReadArrayElementNode is not the same as send by MRI in this case. Maybe we need to change the ReadArraElementNode exception message.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the exception message doesn't matter too much, I think having the same exception class is enough for most cases.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just realized that the message has a bug, the same as in JRuby: if the array has 3 elements and we do
fetch(-5) the exception message is: "invalid array index -2".
@@ -296,6 +389,7 @@ protected static int expectedArity(String name) { | |||
case TO_A: | |||
case TO_ARY: | |||
case SIZE: | |||
case LENGTH: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This and below need to include the other new methods too, for a proper error when given an incorrect number of arguments.
@CachedContext(RubyLanguage.class) RubyContext context, | ||
@Cached InteropNodes.ReadArrayElementNode readNode) { | ||
try { | ||
long args0 = (LongCastNodeGen.create()).executeCastLong(args[0]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The node should be created with @Cached
, not on every call.
@CachedContext(RubyLanguage.class) RubyContext context, | ||
@Cached InteropNodes.ReadArrayElementNode readNode) { | ||
try { | ||
long args0 = (LongCastNodeGen.create()).executeCastLong(args[0]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you give it a better name, like index
?
} | ||
return (args0 < 0) | ||
? readNode.execute(receiver, size + args0) | ||
: readNode.execute(receiver, args0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Take a look at how index normalization is done in org.truffleruby.core.array.ArrayNodes.IndexNode.
Both conditions should use a ConditionProfile, and if < 0; += size
should be done first to simplify the logic.
Also, we should not have two calls to readNode.execute()
but a single one.
pa.log.should include([:polyglot_array_size]) | ||
pa.log.should include([:polyglot_has_array_elements?]) | ||
pa.log.should include([:polyglot_array_element_readable?, 0]) | ||
pa.log.should include([:polyglot_read_array_element, 0]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hasArrayElements/:polyglot_has_array_elements? is only used if assertions are enabled, we should not check for it here.
Same for polyglot_array_element_readable?
.
The main messages here are readArrayElement & getArraySize, we should not check for other messages.
Same for other new methods.
@@ -65,11 +65,57 @@ | |||
|
|||
it description['[index]', :readArrayElement, [:index]] do | |||
pfo, pa, l = proxy[TruffleInteropSpecs::PolyglotArray.new] | |||
-> { pfo[0] }.should raise_error(IndexError) | |||
pfo[0] = 1 | |||
pfo[0] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pfo[0].should == 1
to make sure it's correct.
I'll close this in favor of the more general #2153 |
Working on issue #1844 and trying to make foreign arrays look like Ruby arrays. The following methods were implemented: