Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: opal/opal
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: b9d776eb0ccd
Choose a base ref
...
head repository: opal/opal
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 3039f576f30c
Choose a head ref
  • 2 commits
  • 3 files changed
  • 1 contributor

Commits on Nov 12, 2013

  1. Copy the full SHA
    39ef14d View commit details
  2. Implement Array subclassing

    meh committed Nov 12, 2013

    Verified

    This commit was signed with the committer’s verified signature.
    Tienisto Tien Do Nam
    Copy the full SHA
    3039f57 View commit details
Showing with 195 additions and 80 deletions.
  1. +193 −31 opal/core/array.rb
  2. +2 −12 spec/filters/bugs/array.rb
  3. +0 −37 spec/filters/unsupported/array_subclasses.rb
224 changes: 193 additions & 31 deletions opal/core/array.rb
Original file line number Diff line number Diff line change
@@ -4,6 +4,21 @@ class Array
# Mark all javascript arrays as being valid ruby arrays
`def._isArray = true`

def self.inherited(klass)
replace = Class.new(Array::Wrapper)

%x{
klass._proto = replace._proto;
klass._proto._klass = klass;
klass._alloc = replace._alloc;
klass.__parent = #{Array::Wrapper};
klass.$allocate = replace.$allocate;
klass.$new = replace.$new;
klass["$[]"] = replace["$[]"];
}
end

def self.[](*objects)
objects
end
@@ -303,57 +318,111 @@ def [](index, length = undefined)
end

def []=(index, value, extra = undefined)
%x{
var size = self.length;
if Range === index
if Array === value
data = value.to_a
elsif value.respond_to? :to_ary
data = value.to_ary.to_a
else
data = [value]
end

if (typeof index !== 'number' && !index._isNumber) {
if (index._isRange) {
var exclude = index.exclude;
extra = value;
value = index.end;
index = index.begin;
%x{
var size = self.length,
exclude = index.exclude,
from = #{Opal.coerce_to `index.begin`, Integer, :to_int},
to = #{Opal.coerce_to `index.end`, Integer, :to_int};
if (value < 0) {
value += size;
if (from < 0) {
from += size;
if (from < 0) {
#{raise RangeError, "#{index.inspect} out of range"};
}
}
if (!exclude) value += 1;
#{Opal.fits_fixnum!(`from`)};
value = value - index;
if (to < 0) {
to += size;
}
#{Opal.fits_fixnum!(`to`)};
if (!exclude) {
to += 1;
}
if (from > size) {
for (var i = size; i < index; i++) {
self[i] = nil;
}
}
if (to < 0) {
self.splice.apply(self, [from, 0].concat(data));
}
else {
#{raise ArgumentError};
self.splice.apply(self, [from, to - from].concat(data));
}
}
if (index < 0) {
index += size;
return value;
}
else
if `extra === undefined`
length = 1
else
length = value
value = extra

if Array === value
data = value.to_a
elsif value.respond_to? :to_ary
data = value.to_ary.to_a
else
data = [value]
end
end

%x{
var size = self.length,
index = #{Opal.coerce_to index, Integer, :to_int},
length = #{Opal.coerce_to length, Integer, :to_int},
old;
if (index < 0) {
old = index;
index += size;
if (index < 0) {
#{raise IndexError, "index #{`old`} too small for array; minimum #{`-self.length`}"};
}
}
#{Opal.fits_fixnum!(`index`)};
if (extra != null) {
if (value < 0) {
#{raise IndexError};
if (length < 0) {
#{raise IndexError, "negative length (#{length})"}
}
#{Opal.fits_fixnum!(`length`)};
if (index > size) {
for (var i = size; index > i; i++) {
for (var i = size; i < index; i++) {
self[i] = nil;
}
}
self.splice.apply(self, [index, value].concat(extra));
return extra;
}
if (index > size) {
for (var i = size; i < index; i++) {
self[i] = nil;
if (extra === undefined) {
self[index] = value;
}
else {
self.splice.apply(self, [index, length].concat(data));
}
}
return self[index] = value;
}
return value;
}
end
end

def assoc(object)
@@ -1437,3 +1506,96 @@ def zip(*others, &block)
}
end
end

class Array::Wrapper
def self.allocate(array = [])
obj = super()
`obj.literal = array`
obj
end

def self.new(*args, &block)
obj = allocate
obj.initialize(*args, &block)
obj
end

def self.[](*objects)
allocate(objects)
end

def initialize(*args, &block)
@literal = Array.new(*args, &block)
end

def method_missing(*args, &block)
result = @literal.__send__(*args, &block)

if `result === #@literal`
self
else
result
end
end

def initialize_copy(other)
@literal = `other.literal`.clone
end

def respond_to?(name, *)
super || @literal.respond_to?(name)
end

def ==(other)
@literal == other
end

def to_a
@literal
end

def to_ary
self
end

def inspect
@literal.inspect
end

# wrapped results
def *(other)
%x{
var result = #{@literal * other};
if (result._isArray) {
return #{self.class.allocate(`result`)}
}
else {
return result;
}
}
end

def [](index, length = undefined)
%x{
var result = #{@literal.slice(index, length)};
if (result._isArray && (index._isRange || length !== undefined)) {
return #{self.class.allocate(`result`)}
}
else {
return result;
}
}
end

alias slice []

def uniq
self.class.allocate(@literal.uniq)
end

def flatten(level = undefined)
self.class.allocate(@literal.flatten(level))
end
end
14 changes: 2 additions & 12 deletions spec/filters/bugs/array.rb
Original file line number Diff line number Diff line change
@@ -39,19 +39,7 @@

fails "Array.[] can unpack 2 or more nested referenced array"

fails "Array#[]= calls to_ary on its rhs argument for multi-element sets"
fails "Array#[]= raises an IndexError when passed indexes out of bounds"
fails "Array#[]= tries to convert Range elements to Integers using #to_int with [m..n] and [m...n]"

fails "Array#[]= with [m..n] accepts Range subclasses"
fails "Array#[]= with [m..n] inserts the other section at m if m > n"
fails "Array#[]= with [m..n] replaces the section if m < 0 and n > 0"
fails "Array#[]= with [m..n] replaces the section if m and n < 0"
fails "Array#[]= with [m..n] just sets the section defined by range to nil if m and n < 0 and the rhs is nil"

fails "Array#[]= sets elements in the range arguments when passed ranges"
fails "Array#[]= calls to_int on its start and length arguments"
fails "Array#[]= does nothing if the section defined by range has negative width and the rhs is an empty array"

fails "Array#eql? ignores array class differences"
fails "Array#eql? handles well recursive arrays"
@@ -205,6 +193,7 @@
fails "Array#values_at returns an array of elements in the ranges when passes ranges"
fails "Array#values_at properly handles recursive arrays"
fails "Array#values_at calls to_int on arguments of ranges when passes ranges"
fails "Array#values_at does not return subclass instance on Array subclasses"

fails "Array#zip calls #to_ary to convert the argument to an Array"
fails "Array#zip uses #each to extract arguments' elements when #to_ary fails"
@@ -226,4 +215,5 @@
fails "Array#partition properly handles recursive arrays"
fails "Array#partition returns in the left array values for which the block evaluates to true"
fails "Array#partition returns two arrays"
fails "Array#partition does not return subclass instances on Array subclasses"
end
37 changes: 0 additions & 37 deletions spec/filters/unsupported/array_subclasses.rb

This file was deleted.