I'd still refrain from automatically wrapping into an Array anything that 
implements a List interface because it might be implementing other interfaces 
too, and it might be passed back to Java and have semantics dependent on 
referential identity. 

There's also the issue that currently, we don't do automatic wrapping of 
anything; Nashorn uses POJOs without wrappers and doesn't try to pretend that 
POJOs are native JS objects. Speaking of, some people would expect to be able 
to invoke Java collection API methods on the List object (stream, spliterator, 
removeIf, etc.) so automatic wrapping would foil that too (except of course if 
we had special JS Arrays that also expose these operations…)

There's also the issue of passing such a List-wrapping-Array back to a Java 
API: do we unwrap it? Of course, we can have a filter that unwraps the Array 
back to a List when it goes back, although the current policy is that whenever 
a native JS object passes an API boundary back to Java, it becomes a 
ScriptObjectMirror, so we'd then violate that policy for some Arrays (but not 
for others).

So, that's a number of issues :-)

Of course, none of these issues are arguments against having an Array backed by 
a List, only arguments against automatic conversion.

Now, speaking about a potential List-backed Array implementation: 

Nashorn arrays abstract their backing storage into ArrayData objects, so every 
JS Array normally looks like this: NativeArray->ArrayData->[actual storage]. 
This additional indirection allows us to switch between dense and sparse 
representations, and between different types of representations; e.g. if you 
only have ints in your array, you'll have NativeArray->IntArrayData->int[], 
with getters and setters linked as fast pass-through array element getters and 
setters (MethodHandles.arrayElementGetter). Only when you set an element to a 
double will it evolve into NativeArray->NumberArrayData->double[].

I can imagine having a ListArrayData. It'd have few surprising properties, 
though. E.g. writes to the array would of course write through to the List, and 
writes to the List would mostly be reflected in the Array. I say "mostly" 
because JS arrays aren't necessarily continuos after a delete operation. In 
Nashorn, we implement deletion by adding a DeletedArrayFilter (a special 
ArrayData that filters underlying ArrayData) into the chain, so if you did 
"delete arrayFromList[5]" then your NativeArray->ListArray->List becomes 
NativeArray->DeletedArrayFilter->ListArray->List, and even if you later set 
list.set(5, ...) from Java, on JS level the filter will intercept any [] 
operations and still claim the element doesn't exist.

Also, such an Array would still need to behave as a JS Array for indices in 
range of 2^31…2^32-1, which can't be represented in a java.util.List, so a 
backup storage might be required for those indices, further creating a 
discrepancy between Array.length and list.size().

So anyway, none of these are arguments against having a List-backed Array, just 
saying that it's not possible to entirely reconcile the collection models, so 
there'll always be corner cases where abstraction leaks through. JS arrays are 
heterogenous and non-continuos, with max capacity of 2^32-1. Java Lists are 
heterogeneous and continuous, with max capacity of 2^31-1. They're still a 
better match than Java arrays, which are homogenous and continuous with max 
capacity of 2^31-1 :-)

Attila.

On Nov 27, 2014, at 12:18 PM, Tim Fox <timvo...@gmail.com> wrote:

> On 27/11/14 10:33, Attila Szegedi wrote:
>> Nashorn explicitly allows [] operator on java lists and it also supports 
>> for…in on them. ".length" is also supported since 8u20, see 
>> <https://bugs.openjdk.java.net/browse/JDK-8039387>.
>> 
>> We indeed don't implicitly convert Lists into JS Array objects in Nashorn. 
>> The reason is that JS Array has a Java array as backing storage; wrapping a 
>> large list would incur a lot of copying.
> 
> To avoid the copying, could the Nashorn JS Array implementation be changed so 
> it backs either:
> 
> a) A Java Array (as it does currently) - this would the case if you created 
> the JS array directly in JS code.
> b) A Java List - this would be the case if a List was passed from Java to JS, 
> providing the user with a JS array
> 
> Then we can have our cake and eat it? I.e. we have a real JS Array object 
> (not a half way house with some of the functions and properties but not all), 
> and no copying overhead.
> 
>>  You can use "Java.from(someObject.provideList())" to explicitly convert a 
>> Java List to a JS array. We think it's better to provide an explicit 
>> conversion API than incur a linear conversion cost whenever a List object 
>> passes into the JS context.
>> The Java.from created copy is shallow; if you have a List of Lists, the 
>> nested List objects are not converted.
>> 
>> Typically you'll want to create a real Array if you want to use Array 
>> functionality that points beyond [], .length, and for…in, e.g. JS Array 
>> comprehension operations (arguably, we *could* implement even those so that 
>> Array.prototype.forEach.call(javaList, ...) works as expected, but we aren't 
>> there yet.)
>> 
>> Attila.
>> 
>> On Nov 27, 2014, at 10:41 AM, Tim Fox <timvo...@gmail.com> wrote:
>> 
>>> Hello again,
>>> 
>>> I am a bit confused about how Lists passed from Java into JS are converted.
>>> 
>>> I have a Java class as follows:
>>> 
>>> public class SomeClass {
>>> 
>>>   public List<String> provideList() {
>>>     List<String> list = new ArrayList<>();
>>>     list.add("foo");
>>>     list.add("bar");
>>>     return list;
>>>   }
>>> }
>>> 
>>> I call this from JavaScript:
>>> 
>>> var io = Packages.io;
>>> var someObject = new io.vertx.scratchpad.SomeClass();
>>> 
>>> var arr = someObject.provideList();
>>> 
>>> console.log(arr[0]); // prints: foo
>>> console.log(typeof arr.length); // undefined
>>> console.log(arr instanceof Array); // false
>>> 
>>> I was under the impression that Nashorn automatically converted Java lists 
>>> passed into JS into JS Arrays.
>>> 
>>> The object arr returned in some ways resembles a JavaScript array - the 
>>> operators [] and []= work on it, however it doesn't have the array property 
>>> "length" and it's not an instanceof Array.
>>> 
>>> Can anyone clarify to me what this object is? Any reason why Nashorn 
>>> doesn't just wrap it as a real JS Array?
>>> 
>>> Cheers
> 

Reply via email to