Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions reference/REFERENCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,30 +168,30 @@ Status of built-in methods and properties on GiavaScript runtime types.
| `concat()` | Instance method | Available |
| `copyWithin()` | Instance method | Not available |
| `entries()` | Instance method | Not available |
| `every()` | Instance method | Not available |
| `every()` | Instance method | Available |
| `fill()` | Instance method | Not available |
| `filter()` | Instance method | Not available |
| `find()` | Instance method | Not available |
| `findIndex()` | Instance method | Not available |
| `filter()` | Instance method | Available |
| `find()` | Instance method | Available |
| `findIndex()` | Instance method | Available |
| `findLast()` | Instance method | Not available |
| `findLastIndex()` | Instance method | Not available |
| `flat()` | Instance method | Not available |
| `flatMap()` | Instance method | Not available |
| `forEach()` | Instance method | Not available |
| `forEach()` | Instance method | Available |
| `includes()` | Instance method | Available |
| `indexOf()` | Instance method | Available |
| `join()` | Instance method | Available |
| `keys()` | Instance method | Not available |
| `lastIndexOf()` | Instance method | Available |
| `map()` | Instance method | Not available |
| `map()` | Instance method | Available |
| `pop()` | Instance method | Available |
| `push()` | Instance method | Available |
| `reduce()` | Instance method | Not available |
| `reduce()` | Instance method | Available |
| `reduceRight()` | Instance method | Not available |
| `reverse()` | Instance method | Available |
| `shift()` | Instance method | Available |
| `slice()` | Instance method | Available |
| `some()` | Instance method | Not available |
| `some()` | Instance method | Available |
| `sort()` | Instance method | Available |
| `splice()` | Instance method | Not available |
| `toLocaleString()` | Instance method | Not available |
Expand Down
16 changes: 8 additions & 8 deletions reference/Types.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,30 +62,30 @@ Status of built-in methods and properties on GiavaScript runtime types.
| `concat()` | Instance method | Available |
| `copyWithin()` | Instance method | Not available |
| `entries()` | Instance method | Not available |
| `every()` | Instance method | Not available |
| `every()` | Instance method | Available |
| `fill()` | Instance method | Not available |
| `filter()` | Instance method | Not available |
| `find()` | Instance method | Not available |
| `findIndex()` | Instance method | Not available |
| `filter()` | Instance method | Available |
| `find()` | Instance method | Available |
| `findIndex()` | Instance method | Available |
| `findLast()` | Instance method | Not available |
| `findLastIndex()` | Instance method | Not available |
| `flat()` | Instance method | Not available |
| `flatMap()` | Instance method | Not available |
| `forEach()` | Instance method | Not available |
| `forEach()` | Instance method | Available |
| `includes()` | Instance method | Available |
| `indexOf()` | Instance method | Available |
| `join()` | Instance method | Available |
| `keys()` | Instance method | Not available |
| `lastIndexOf()` | Instance method | Available |
| `map()` | Instance method | Not available |
| `map()` | Instance method | Available |
| `pop()` | Instance method | Available |
| `push()` | Instance method | Available |
| `reduce()` | Instance method | Not available |
| `reduce()` | Instance method | Available |
| `reduceRight()` | Instance method | Not available |
| `reverse()` | Instance method | Available |
| `shift()` | Instance method | Available |
| `slice()` | Instance method | Available |
| `some()` | Instance method | Not available |
| `some()` | Instance method | Available |
| `sort()` | Instance method | Available |
| `splice()` | Instance method | Not available |
| `toLocaleString()` | Instance method | Not available |
Expand Down
32 changes: 32 additions & 0 deletions spec/giavascript_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -861,6 +861,34 @@ describe GiavaScript do
interpreter.eval("items;").should eq(["[1, 2, 3, 4]"])
end

it "supports callback-based Array methods" do
interpreter = GiavaScript::Interpreter.new
interpreter.eval("var numbers = [1, 2, 3, 4];").should eq([] of String)
interpreter.eval("var seen = []; numbers.forEach(function(value, index, array) { seen.push(value + index + array.length); });").should eq(["undefined"])
interpreter.eval("seen;").should eq(["[5, 7, 9, 11]"])
interpreter.eval("numbers.map(function(value, index, array) { return value * index + array.length; });").should eq(["[4, 6, 10, 16]"])
interpreter.eval("numbers.filter(function(value, index, array) { return value + index > array.length; });").should eq(["[3, 4]"])
interpreter.eval("numbers.some(function(value, index, array) { return value + index == array.length; });").should eq(["false"])
interpreter.eval("numbers.every(function(value, index, array) { return value + index >= 1; });").should eq(["true"])
interpreter.eval("numbers.find(function(value, index, array) { return value * 2 == array.length + index + 1; });").should eq(["4"])
interpreter.eval("numbers.findIndex(function(value, index, array) { return value * 2 == array.length + index + 1; });").should eq(["3"])
end

it "supports Array.reduce with and without an initial value" do
interpreter = GiavaScript::Interpreter.new
interpreter.eval("[1, 2, 3].reduce(function(acc, value, index, array) { return acc + value + index + array.length; }, 0);").should eq(["18"])
interpreter.eval("[1, 2, 3].reduce(function(acc, value, index, array) { return acc + value + index + array.length; });").should eq(["15"])
interpreter.eval("[].reduce(function(acc, value, index, array) { return acc + value + index + array.length; }, 10);").should eq(["10"])
end

it "handles empty and miss cases for callback-based Array methods" do
interpreter = GiavaScript::Interpreter.new
interpreter.eval("[].some(function(value, index, array) { return true; });").should eq(["false"])
interpreter.eval("[].every(function(value, index, array) { return false; });").should eq(["true"])
interpreter.eval("[1, 2].find(function(value, index, array) { return value == 9; });").should eq(["undefined"])
interpreter.eval("[1, 2].findIndex(function(value, index, array) { return value == 9; });").should eq(["-1"])
end

it "validates Array method argument counts and index types" do
interpreter = GiavaScript::Interpreter.new
interpreter.eval("[1].at();").should eq(["Error: Array.at expects 1 arguments but got 0"])
Expand All @@ -873,6 +901,10 @@ describe GiavaScript do
interpreter.eval("[1].shift(1);").should eq(["Error: Array.shift expects 0 arguments but got 1"])
interpreter.eval("[1].reverse(1);").should eq(["Error: Array.reverse expects 0 arguments but got 1"])
interpreter.eval("[1].sort(1);").should eq(["Error: Array.sort expects 0 arguments but got 1"])
interpreter.eval("[1].forEach();").should eq(["Error: Array.forEach expects 1 arguments but got 0"])
interpreter.eval("[1].map(1);").should eq(["Error: Array.map expects a function argument"])
interpreter.eval("[1].reduce();").should eq(["Error: Array.reduce expects between 1 and 2 arguments but got 0"])
interpreter.eval("[].reduce(function(acc, value, index, array) { return acc + value + index + array.length; });").should eq(["Error: Array.reduce cannot reduce an empty array without an initial value"])
end

it "handles String.at and String.charAt out-of-range indexes" do
Expand Down
8 changes: 7 additions & 1 deletion src/giavascript/expression_evaluator.cr
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,13 @@ module GiavaScript

private def invoke_callable(value : Value, receiver : Value, args : Array(Value)) : Value
if value.is_a?(BuiltinFunction)
return value.call(receiver, args)
callback_invoker = ->(callable : Value, callback_args : Array(Value)) do
invoke_callable(callable, nil, callback_args).as(Value)
end

return RuntimeTypes.with_callback_invoker(callback_invoker) do
value.call(receiver, args)
end
end

if value.is_a?(UserFunction)
Expand Down
Loading
Loading