use crate::host::HostFunction;
use crate::object::array_obj::JSArrayObject;
use crate::object::function::JSFunction;
use crate::object::object::JSObject;
use crate::runtime::context::JSContext;
#[inline(always)]
fn array_get(obj: &JSObject, index: usize, ctx: &mut JSContext) -> Option<crate::value::JSValue> {
if obj.is_array() {
let ptr = obj as *const JSObject as usize;
if obj.is_dense_array() {
let arr = unsafe { &*(ptr as *const JSArrayObject) };
if let Some(val) = arr.get(index) {
return Some(val);
}
}
}
if let Some(val) = obj.get_indexed(index) {
return Some(val);
}
let key = ctx.int_atom_mut(index);
obj.get(key)
}
use crate::value::JSValue;
fn init_array_length(obj: &mut JSObject, len: i64, ctx: &mut JSContext) {
use crate::object::object::PropertyDescriptor;
let len_atom = ctx.common_atoms.length;
if obj.has_own(len_atom) {
obj.set_length(len_atom, JSValue::new_int(len));
} else {
obj.define_property(
len_atom,
PropertyDescriptor {
value: Some(JSValue::new_int(len)),
writable: true,
enumerable: false,
configurable: false,
get: None,
set: None,
},
);
}
obj.assign_shape_from_existing_props(ctx.shape_cache_mut());
}
fn create_builtin_function(ctx: &mut JSContext, name: &str) -> JSValue {
let mut func = JSFunction::new_builtin(ctx.intern(name), 1);
func.set_builtin_marker(ctx, name);
let ptr = Box::into_raw(Box::new(func)) as usize;
ctx.runtime_mut().gc_heap_mut().track_function(ptr);
debug_assert_ne!(
unsafe { (*(ptr as *const JSObject)).gc_slot },
u32::MAX,
"track_function did not set gc_slot"
);
JSValue::new_function(ptr)
}
pub fn init_array(ctx: &mut JSContext) {
let array_atom = ctx.common_atoms.array;
let mut array_func = JSFunction::new_builtin(array_atom, 1);
array_func.set_builtin_marker(ctx, "array_constructor");
array_func.base.set(
ctx.intern("isArray"),
create_builtin_function(ctx, "array_isArray"),
);
array_func.base.set(
ctx.intern("fromAsync"),
create_builtin_function(ctx, "array_fromAsync"),
);
let array_ptr = Box::into_raw(Box::new(array_func)) as usize;
ctx.runtime_mut().gc_heap_mut().track_function(array_ptr);
let array_value = JSValue::new_function(array_ptr);
let global = ctx.global();
if global.is_object() {
let global_obj = global.as_object_mut();
global_obj.set(array_atom, array_value);
}
let proto_atom = ctx.intern("ArrayPrototype");
let mut proto_obj = JSObject::new();
proto_obj.set(
ctx.intern("push"),
create_builtin_function(ctx, "array_push"),
);
proto_obj.set(ctx.intern("pop"), create_builtin_function(ctx, "array_pop"));
proto_obj.set(
ctx.intern("shift"),
create_builtin_function(ctx, "array_shift"),
);
proto_obj.set(
ctx.intern("unshift"),
create_builtin_function(ctx, "array_unshift"),
);
proto_obj.set(
ctx.intern("concat"),
create_builtin_function(ctx, "array_concat"),
);
proto_obj.set(
ctx.intern("slice"),
create_builtin_function(ctx, "array_slice"),
);
proto_obj.set(
ctx.intern("indexOf"),
create_builtin_function(ctx, "array_indexOf"),
);
proto_obj.set(
ctx.intern("includes"),
create_builtin_function(ctx, "array_includes"),
);
proto_obj.set(
ctx.intern("join"),
create_builtin_function(ctx, "array_join"),
);
init_array_length(&mut proto_obj, 0, ctx);
proto_obj.set(
ctx.intern("forEach"),
create_builtin_function(ctx, "array_forEach"),
);
proto_obj.set(ctx.intern("map"), create_builtin_function(ctx, "array_map"));
proto_obj.set(
ctx.intern("filter"),
create_builtin_function(ctx, "array_filter"),
);
proto_obj.set(
ctx.intern("reduce"),
create_builtin_function(ctx, "array_reduce"),
);
proto_obj.set(
ctx.intern("every"),
create_builtin_function(ctx, "array_every"),
);
proto_obj.set(
ctx.intern("some"),
create_builtin_function(ctx, "array_some"),
);
proto_obj.set(
ctx.intern("find"),
create_builtin_function(ctx, "array_find"),
);
proto_obj.set(
ctx.intern("findIndex"),
create_builtin_function(ctx, "array_findIndex"),
);
proto_obj.set(
ctx.intern("reduceRight"),
create_builtin_function(ctx, "array_reduceRight"),
);
proto_obj.set(
ctx.intern("sort"),
create_builtin_function(ctx, "array_sort"),
);
proto_obj.set(
ctx.intern("reverse"),
create_builtin_function(ctx, "array_reverse"),
);
proto_obj.set(
ctx.intern("fill"),
create_builtin_function(ctx, "array_fill"),
);
proto_obj.set(
ctx.intern("splice"),
create_builtin_function(ctx, "array_splice"),
);
proto_obj.set(
ctx.intern("flat"),
create_builtin_function(ctx, "array_flat"),
);
proto_obj.set(
ctx.intern("flatMap"),
create_builtin_function(ctx, "array_flatMap"),
);
proto_obj.set(
ctx.intern("findLast"),
create_builtin_function(ctx, "array_findLast"),
);
proto_obj.set(
ctx.intern("findLastIndex"),
create_builtin_function(ctx, "array_findLastIndex"),
);
proto_obj.set(ctx.intern("at"), create_builtin_function(ctx, "array_at"));
proto_obj.set(
ctx.intern("toSorted"),
create_builtin_function(ctx, "array_toSorted"),
);
proto_obj.set(
ctx.intern("toReversed"),
create_builtin_function(ctx, "array_toReversed"),
);
proto_obj.set(
ctx.intern("with"),
create_builtin_function(ctx, "array_with"),
);
proto_obj.set(
ctx.intern("lastIndexOf"),
create_builtin_function(ctx, "array_lastIndexOf"),
);
proto_obj.set(
ctx.intern("toSpliced"),
create_builtin_function(ctx, "array_toSpliced"),
);
proto_obj.set(ctx.common_atoms.constructor, array_value);
if let Some(obj_proto_ptr) = ctx.get_object_prototype() {
proto_obj.prototype = Some(obj_proto_ptr);
}
let proto_ptr = Box::into_raw(Box::new(proto_obj)) as usize;
ctx.runtime_mut().gc_heap_mut().track(proto_ptr);
ctx.set_array_prototype(proto_ptr);
let proto_value = JSValue::new_object(proto_ptr);
let array_func_ref = array_value.as_function_mut();
array_func_ref
.base
.set(ctx.common_atoms.prototype, proto_value);
if global.is_object() {
let global_obj = global.as_object_mut();
global_obj.set(proto_atom, proto_value);
}
}
pub fn register_builtins(ctx: &mut JSContext) {
ctx.register_builtin("array_push", HostFunction::new("push", 1, array_push));
ctx.register_builtin("array_pop", HostFunction::new("pop", 0, array_pop));
ctx.register_builtin("array_shift", HostFunction::new("shift", 0, array_shift));
ctx.register_builtin(
"array_unshift",
HostFunction::new("unshift", 1, array_unshift),
);
ctx.register_builtin("array_concat", HostFunction::new("concat", 1, array_concat));
ctx.register_builtin("array_slice", HostFunction::new("slice", 2, array_slice));
ctx.register_builtin(
"array_indexOf",
HostFunction::new("indexOf", 1, array_index_of),
);
ctx.register_builtin(
"array_includes",
HostFunction::new("includes", 1, array_includes),
);
ctx.register_builtin("array_join", HostFunction::new("join", 1, array_join));
ctx.register_builtin(
"array_isArray",
HostFunction::new("isArray", 1, array_is_array),
);
ctx.register_builtin(
"array_fromAsync",
HostFunction::new("fromAsync", 1, array_from_async),
);
ctx.register_builtin(
"array_forEach",
HostFunction::new("forEach", 1, array_for_each),
);
ctx.register_builtin("array_map", HostFunction::new("map", 1, array_map));
ctx.register_builtin("array_filter", HostFunction::new("filter", 1, array_filter));
ctx.register_builtin("array_reduce", HostFunction::new("reduce", 2, array_reduce));
ctx.register_builtin("array_every", HostFunction::new("every", 1, array_every));
ctx.register_builtin("array_some", HostFunction::new("some", 1, array_some));
ctx.register_builtin("array_find", HostFunction::new("find", 1, array_find));
ctx.register_builtin(
"array_findIndex",
HostFunction::new("findIndex", 1, array_find_index),
);
ctx.register_builtin(
"array_reduceRight",
HostFunction::new("reduceRight", 2, array_reduce_right),
);
ctx.register_builtin("array_sort", HostFunction::new("sort", 1, array_sort));
ctx.register_builtin(
"array_reverse",
HostFunction::new("reverse", 0, array_reverse),
);
ctx.register_builtin("array_fill", HostFunction::new("fill", 1, array_fill));
ctx.register_builtin("array_splice", HostFunction::new("splice", 2, array_splice));
ctx.register_builtin("array_flat", HostFunction::new("flat", 0, array_flat));
ctx.register_builtin(
"array_flatMap",
HostFunction::new("flatMap", 1, array_flat_map),
);
ctx.register_builtin(
"array_findLast",
HostFunction::new("findLast", 1, array_find_last),
);
ctx.register_builtin(
"array_findLastIndex",
HostFunction::new("findLastIndex", 1, array_find_last_index),
);
ctx.register_builtin("array_at", HostFunction::new("at", 1, array_at));
ctx.register_builtin(
"array_toSorted",
HostFunction::new("toSorted", 1, array_to_sorted),
);
ctx.register_builtin(
"array_toReversed",
HostFunction::new("toReversed", 0, array_to_reversed),
);
ctx.register_builtin("array_with", HostFunction::new("with", 2, array_with));
ctx.register_builtin(
"array_lastIndexOf",
HostFunction::new("lastIndexOf", 1, array_last_index_of),
);
ctx.register_builtin(
"array_toSpliced",
HostFunction::new("toSpliced", 3, array_to_spliced),
);
ctx.register_builtin(
"array_constructor",
HostFunction::new("Array", 1, array_constructor),
);
}
fn new_jsarray_with_proto(ctx: &mut JSContext) -> JSArrayObject {
let mut arr = JSArrayObject::new();
if let Some(proto_ptr) = ctx.get_array_prototype() {
arr.header.set_prototype_raw(proto_ptr);
}
arr
}
fn alloc_jsarray(arr: JSArrayObject, ctx: &mut JSContext) -> JSValue {
let ptr = Box::into_raw(Box::new(arr)) as usize;
ctx.runtime_mut().gc_heap_mut().track_array(ptr);
JSValue::new_object(ptr)
}
pub fn array_constructor(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
if args.is_empty() {
let mut arr = new_jsarray_with_proto(ctx);
init_array_length(&mut arr.header, 0, ctx);
return alloc_jsarray(arr, ctx);
}
if args.len() == 1 && args[0].is_int() {
let len = args[0].get_int();
let mut arr = if len > 0 {
JSArrayObject::with_length(len as usize)
} else {
JSArrayObject::new()
};
if let Some(proto_ptr) = ctx.get_array_prototype() {
arr.header.set_prototype_raw(proto_ptr);
}
if len < 0 {
init_array_length(&mut arr.header, 0, ctx);
} else {
init_array_length(&mut arr.header, len, ctx);
}
return alloc_jsarray(arr, ctx);
}
let mut arr = new_jsarray_with_proto(ctx);
for arg in args.iter() {
arr.push(*arg);
}
init_array_length(&mut arr.header, args.len() as i64, ctx);
alloc_jsarray(arr, ctx)
}
fn array_push(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
if args.is_empty() {
return JSValue::new_int(0);
}
let this = &args[0];
if !this.is_object() {
return JSValue::new_int(0);
}
let length_atom = ctx.common_atoms.length;
let ptr = this.get_ptr();
if this.as_object().is_dense_array() {
let arr = unsafe { &mut *(ptr as *mut JSArrayObject) };
let current_len = arr.len() as u32;
for arg in args.iter().skip(1) {
arr.push(arg.clone());
}
let new_len = current_len + (args.len() - 1) as u32;
arr.header.set_length_ic(
length_atom,
JSValue::new_int(new_len as i64),
ctx.shape_cache_mut(),
);
return JSValue::new_int(new_len as i64);
}
let obj = unsafe { &mut *(ptr as *mut JSObject) };
let current_len = if let Some(l) = obj.get(length_atom) {
if l.is_int() { l.get_int() as u32 } else { 0 }
} else {
0
};
for (i, arg) in args.iter().skip(1).enumerate() {
let idx = current_len as usize + i;
let key = ctx.int_atom_mut(idx);
obj.set(key, arg.clone());
}
let new_len = current_len + (args.len() - 1) as u32;
obj.set_length(length_atom, JSValue::new_int(new_len as i64));
JSValue::new_int(new_len as i64)
}
fn array_pop(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
if args.is_empty() {
return JSValue::undefined();
}
let this = &args[0];
if !this.is_object() {
return JSValue::undefined();
}
let length_atom = ctx.common_atoms.length;
let ptr = this.get_ptr();
if this.as_object().is_dense_array() {
let arr = unsafe { &mut *(ptr as *mut JSArrayObject) };
let current_len = arr.len();
if current_len == 0 {
return JSValue::undefined();
}
let result = arr
.elements()
.last()
.copied()
.unwrap_or_else(JSValue::undefined);
arr.elements_mut().pop();
arr.header
.set_length(length_atom, JSValue::new_int((current_len - 1) as i64));
return result;
}
let obj = unsafe { &mut *(ptr as *mut JSObject) };
let current_len = if let Some(l) = obj.get(length_atom) {
if l.is_int() { l.get_int() as u32 } else { 0 }
} else {
0
};
if current_len == 0 {
return JSValue::undefined();
}
let idx = (current_len - 1) as usize;
let key = ctx.int_atom_mut(idx);
let result = obj.get(key).unwrap_or_else(JSValue::undefined);
obj.delete(key);
obj.set_length(length_atom, JSValue::new_int(idx as i64));
result
}
fn array_shift(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
if args.is_empty() {
return JSValue::undefined();
}
let this = &args[0];
if !this.is_object() {
return JSValue::undefined();
}
let length_atom = ctx.common_atoms.length;
let ptr = this.get_ptr();
if this.as_object().is_dense_array() {
let arr = unsafe { &mut *(ptr as *mut JSArrayObject) };
let current_len = arr.len();
if current_len == 0 {
return JSValue::undefined();
}
let result = arr.get(0).unwrap_or_else(JSValue::undefined);
arr.elements_mut().remove(0);
arr.header
.set_length(length_atom, JSValue::new_int((current_len - 1) as i64));
return result;
}
let obj = unsafe { &mut *(ptr as *mut JSObject) };
let current_len = if let Some(l) = obj.get(length_atom) {
if l.is_int() { l.get_int() as u32 } else { 0 }
} else {
0
};
if current_len == 0 {
return JSValue::undefined();
}
let first_key = ctx.common_atoms.n0;
let result = obj.get(first_key).unwrap_or_else(JSValue::undefined);
for i in 1..current_len {
let old_key = ctx.int_atom_mut(i as usize);
let new_key = ctx.int_atom_mut(i as usize - 1);
if let Some(val) = obj.get(old_key) {
obj.set(new_key, val);
}
obj.delete(old_key);
}
let last_key = ctx.int_atom_mut(current_len as usize - 1);
obj.delete(last_key);
obj.set_length(length_atom, JSValue::new_int((current_len - 1) as i64));
result
}
fn array_unshift(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
if args.is_empty() {
return JSValue::new_int(0);
}
let this = &args[0];
if !this.is_object() {
return JSValue::new_int(0);
}
let length_atom = ctx.common_atoms.length;
let add_count = (args.len() - 1) as usize;
let ptr = this.get_ptr();
if this.as_object().is_dense_array() {
let arr = unsafe { &mut *(ptr as *mut JSArrayObject) };
let items: Vec<JSValue> = args.iter().skip(1).cloned().collect();
let mut new_elements = items;
new_elements.extend(arr.elements().iter().copied());
arr.set_elements(new_elements);
let new_len = arr.len();
arr.header
.set_length(length_atom, JSValue::new_int(new_len as i64));
return JSValue::new_int(new_len as i64);
}
let obj = unsafe { &mut *(ptr as *mut JSObject) };
let current_len = if let Some(l) = obj.get(length_atom) {
if l.is_int() { l.get_int() as u32 } else { 0 }
} else {
0
};
for i in (0..current_len).rev() {
let old_key = ctx.int_atom_mut(i as usize);
let new_key = ctx.int_atom_mut(i as usize + add_count);
if let Some(val) = obj.get(old_key) {
obj.set(new_key, val);
}
obj.delete(old_key);
}
for (i, arg) in args.iter().skip(1).enumerate() {
let key = ctx.int_atom_mut(i);
obj.set(key, arg.clone());
}
let new_len = current_len + add_count as u32;
obj.set_length(length_atom, JSValue::new_int(new_len as i64));
JSValue::new_int(new_len as i64)
}
fn array_concat(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
if args.is_empty() {
let mut result = new_jsarray_with_proto(ctx);
result
.header
.set_length(ctx.common_atoms.length, JSValue::new_int(0));
return alloc_jsarray(result, ctx);
}
let this = &args[0];
let mut result = new_jsarray_with_proto(ctx);
let length_atom = ctx.common_atoms.length;
if this.is_object() {
let obj = this.as_object();
let this_len = if let Some(l) = obj.get(length_atom) {
if l.is_int() { l.get_int() as u32 } else { 0 }
} else {
0
};
for i in 0..this_len {
if let Some(val) = array_get(obj, i as usize, ctx) {
result.push(val);
}
}
}
for arg in args.iter().skip(1) {
if arg.is_object() {
let obj = arg.as_object();
let arg_len = if let Some(l) = obj.get(length_atom) {
if l.is_int() { l.get_int() as u32 } else { 0 }
} else {
0
};
for i in 0..arg_len {
if let Some(val) = array_get(obj, i as usize, ctx) {
result.push(val);
}
}
} else {
result.push(arg.clone());
}
}
let result_len = result.len();
result
.header
.set_length(length_atom, JSValue::new_int(result_len as i64));
alloc_jsarray(result, ctx)
}
fn array_slice(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
let mut result = new_jsarray_with_proto(ctx);
let length_atom = ctx.common_atoms.length;
if args.is_empty() {
result.header.set_length(length_atom, JSValue::new_int(0));
return alloc_jsarray(result, ctx);
}
let this = &args[0];
if !this.is_object() {
result.header.set_length(length_atom, JSValue::new_int(0));
return alloc_jsarray(result, ctx);
}
let obj = this.as_object();
let len = if let Some(l) = obj.get(length_atom) {
if l.is_int() { l.get_int() as i32 } else { 0 }
} else {
0
};
let start = if args.len() > 1 {
let s = args[1].get_int() as i32;
if s < 0 { (len + s).max(0) } else { s.min(len) }
} else {
0
};
let end = if args.len() > 2 {
let e = args[2].get_int() as i32;
if e < 0 { (len + e).max(0) } else { e.min(len) }
} else {
len
};
for i in start..end {
if let Some(val) = array_get(obj, i as usize, ctx) {
result.push(val);
}
}
result
.header
.set_length(length_atom, JSValue::new_int(result.len() as i64));
alloc_jsarray(result, ctx)
}
fn array_index_of(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
if args.len() < 2 {
return JSValue::new_int(-1);
}
let this = &args[0];
let search = &args[1];
if !this.is_object() {
return JSValue::new_int(-1);
}
let obj = this.as_object();
let length_atom = ctx.common_atoms.length;
let len = if let Some(l) = obj.get(length_atom) {
if l.is_int() { l.get_int() as u32 } else { 0 }
} else {
0
};
let from_index = if args.len() > 2 {
args[2].get_int() as i32
} else {
0
};
let start = if from_index < 0 {
((len as i32) + from_index).max(0) as u32
} else {
from_index.min(len as i32) as u32
};
for i in start..len {
if let Some(val) = array_get(obj, i as usize, ctx) {
if val.strict_eq(search) {
return JSValue::new_int(i as i64);
}
}
}
JSValue::new_int(-1)
}
fn array_includes(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
if args.len() < 2 {
return JSValue::bool(false);
}
let this = &args[0];
let search = &args[1];
if !this.is_object() {
return JSValue::bool(false);
}
let obj = this.as_object();
let length_atom = ctx.common_atoms.length;
let len = if let Some(l) = obj.get(length_atom) {
if l.is_int() { l.get_int() as u32 } else { 0 }
} else {
0
};
for i in 0..len {
if let Some(val) = array_get(obj, i as usize, ctx) {
if val.strict_eq(search) {
return JSValue::bool(true);
}
}
}
JSValue::bool(false)
}
fn array_join(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
if args.is_empty() {
return JSValue::new_string(ctx.intern(""));
}
let this = &args[0];
if !this.is_object() {
return JSValue::new_string(ctx.intern(""));
}
let obj = this.as_object();
let length_atom = ctx.common_atoms.length;
let len = if let Some(l) = obj.get(length_atom) {
if l.is_int() { l.get_int() as u32 } else { 0 }
} else {
0
};
let separator = if args.len() > 1 && args[1].is_string() {
ctx.get_atom_str(args[1].get_atom()).to_string()
} else {
",".to_string()
};
let mut parts = Vec::new();
for i in 0..len {
if let Some(val) = array_get(obj, i as usize, ctx) {
if val.is_undefined() || val.is_null() {
parts.push(String::new());
} else if val.is_string() {
parts.push(ctx.get_atom_str(val.get_atom()).to_string());
} else if val.is_int() {
parts.push(val.get_int().to_string());
} else if val.is_float() {
parts.push(val.get_float().to_string());
} else if val.is_bool() {
parts.push(val.get_bool().to_string());
} else {
parts.push(String::new());
}
} else {
parts.push(String::new());
}
}
JSValue::new_string(ctx.intern(&parts.join(&separator)))
}
fn array_is_array(_ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
if args.is_empty() {
return JSValue::bool(false);
}
let arg = &args[0];
if arg.is_object() {
let obj = arg.as_object();
JSValue::bool(obj.is_array())
} else {
JSValue::bool(false)
}
}
fn call_callback(
ctx: &mut JSContext,
callback: JSValue,
args: &[JSValue],
) -> Result<JSValue, String> {
if let Some(ptr) = ctx.get_register_vm_ptr() {
let vm = unsafe { &mut *(ptr as *mut crate::runtime::vm::VM) };
vm.call_function(ctx, callback, args)
} else {
Err("call_callback: VM not available".to_string())
}
}
fn array_for_each(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
if args.len() < 2 {
return JSValue::undefined();
}
let this = &args[0];
let callback = &args[1];
if !this.is_object() || !callback.is_function() {
return JSValue::undefined();
}
let obj = this.as_object();
let length_atom = ctx.common_atoms.length;
let len = if let Some(l) = obj.get(length_atom) {
if l.is_int() { l.get_int() as u32 } else { 0 }
} else {
0
};
for i in 0..len {
if let Some(value) = array_get(obj, i as usize, ctx) {
let callback_args = vec![value, JSValue::new_int(i as i64), *this];
let _ = call_callback(ctx, *callback, &callback_args);
}
}
JSValue::undefined()
}
fn array_map(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
if args.len() < 2 {
let mut result = new_jsarray_with_proto(ctx);
result
.header
.set_length(ctx.common_atoms.length, JSValue::new_int(0));
return alloc_jsarray(result, ctx);
}
let this = &args[0];
let callback = &args[1];
if !this.is_object() || !callback.is_function() {
let mut result = new_jsarray_with_proto(ctx);
result
.header
.set_length(ctx.common_atoms.length, JSValue::new_int(0));
return alloc_jsarray(result, ctx);
}
let obj = this.as_object();
let length_atom = ctx.common_atoms.length;
let len = if let Some(l) = obj.get(length_atom) {
if l.is_int() { l.get_int() as u32 } else { 0 }
} else {
0
};
let mut result = new_jsarray_with_proto(ctx);
for i in 0..len {
if let Some(value) = array_get(obj, i as usize, ctx) {
let callback_args = vec![value];
if let Ok(mapped_value) = call_callback(ctx, *callback, &callback_args) {
result.push(mapped_value);
}
}
}
result
.header
.set_length(length_atom, JSValue::new_int(result.len() as i64));
alloc_jsarray(result, ctx)
}
fn array_filter(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
if args.len() < 2 {
let mut result = new_jsarray_with_proto(ctx);
result
.header
.set_length(ctx.common_atoms.length, JSValue::new_int(0));
return alloc_jsarray(result, ctx);
}
let this = &args[0];
let callback = &args[1];
if !this.is_object() || !callback.is_function() {
let mut result = new_jsarray_with_proto(ctx);
result
.header
.set_length(ctx.common_atoms.length, JSValue::new_int(0));
return alloc_jsarray(result, ctx);
}
let obj = this.as_object();
let length_atom = ctx.common_atoms.length;
let len = if let Some(l) = obj.get(length_atom) {
if l.is_int() { l.get_int() as u32 } else { 0 }
} else {
0
};
let mut result = new_jsarray_with_proto(ctx);
for i in 0..len {
if let Some(value) = array_get(obj, i as usize, ctx) {
let callback_args = vec![value, JSValue::new_int(i as i64), *this];
if let Ok(test_result) = call_callback(ctx, *callback, &callback_args) {
if test_result.is_truthy() {
result.push(value);
}
}
}
}
result
.header
.set_length(length_atom, JSValue::new_int(result.len() as i64));
alloc_jsarray(result, ctx)
}
fn array_reduce(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
if args.len() < 2 {
return JSValue::undefined();
}
let this = &args[0];
let callback = &args[1];
if !this.is_object() || !callback.is_function() {
return JSValue::undefined();
}
let obj = this.as_object();
let length_atom = ctx.common_atoms.length;
let len = if let Some(l) = obj.get(length_atom) {
if l.is_int() { l.get_int() as u32 } else { 0 }
} else {
0
};
if len == 0 {
if args.len() > 2 {
return args[2];
}
return JSValue::undefined();
}
let mut accumulator: JSValue;
let mut start_index: u32 = 0;
if args.len() > 2 {
accumulator = args[2];
} else {
accumulator = array_get(obj, 0, ctx).unwrap_or_else(JSValue::undefined);
start_index = 1;
}
for i in start_index..len {
if let Some(value) = array_get(obj, i as usize, ctx) {
let callback_args = vec![accumulator, value, JSValue::new_int(i as i64), *this];
if let Ok(result) = call_callback(ctx, *callback, &callback_args) {
accumulator = result;
}
}
}
accumulator
}
fn array_every(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
if args.len() < 2 {
return JSValue::bool(true);
}
let this = &args[0];
let callback = &args[1];
if !this.is_object() || !callback.is_function() {
return JSValue::bool(true);
}
let obj = this.as_object();
let length_atom = ctx.common_atoms.length;
let len = if let Some(l) = obj.get(length_atom) {
if l.is_int() { l.get_int() as u32 } else { 0 }
} else {
0
};
for i in 0..len {
if let Some(value) = array_get(obj, i as usize, ctx) {
let callback_args = vec![value, JSValue::new_int(i as i64), *this];
if let Ok(result) = call_callback(ctx, *callback, &callback_args) {
if !result.is_truthy() {
return JSValue::bool(false);
}
}
}
}
JSValue::bool(true)
}
fn array_some(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
if args.len() < 2 {
return JSValue::bool(false);
}
let this = &args[0];
let callback = &args[1];
if !this.is_object() || !callback.is_function() {
return JSValue::bool(false);
}
let obj = this.as_object();
let length_atom = ctx.common_atoms.length;
let len = if let Some(l) = obj.get(length_atom) {
if l.is_int() { l.get_int() as u32 } else { 0 }
} else {
0
};
for i in 0..len {
if let Some(value) = array_get(obj, i as usize, ctx) {
let callback_args = vec![value, JSValue::new_int(i as i64), *this];
if let Ok(result) = call_callback(ctx, *callback, &callback_args) {
if result.is_truthy() {
return JSValue::bool(true);
}
}
}
}
JSValue::bool(false)
}
fn array_find(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
if args.len() < 2 {
return JSValue::undefined();
}
let this = &args[0];
let callback = &args[1];
if !this.is_object() || !callback.is_function() {
return JSValue::undefined();
}
let obj = this.as_object();
let length_atom = ctx.common_atoms.length;
let len = if let Some(l) = obj.get(length_atom) {
if l.is_int() { l.get_int() as u32 } else { 0 }
} else {
0
};
for i in 0..len {
if let Some(value) = array_get(obj, i as usize, ctx) {
let callback_args = vec![value, JSValue::new_int(i as i64), *this];
if let Ok(result) = call_callback(ctx, *callback, &callback_args) {
if result.is_truthy() {
return value;
}
}
}
}
JSValue::undefined()
}
fn array_find_index(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
if args.len() < 2 {
return JSValue::new_int(-1);
}
let this = &args[0];
let callback = &args[1];
if !this.is_object() || !callback.is_function() {
return JSValue::new_int(-1);
}
let obj = this.as_object();
let length_atom = ctx.common_atoms.length;
let len = if let Some(l) = obj.get(length_atom) {
if l.is_int() { l.get_int() as u32 } else { 0 }
} else {
0
};
for i in 0..len {
if let Some(value) = array_get(obj, i as usize, ctx) {
let callback_args = vec![value, JSValue::new_int(i as i64), *this];
if let Ok(result) = call_callback(ctx, *callback, &callback_args) {
if result.is_truthy() {
return JSValue::new_int(i as i64);
}
}
}
}
JSValue::new_int(-1)
}
fn array_reduce_right(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
if args.len() < 2 {
return JSValue::undefined();
}
let this = &args[0];
let callback = &args[1];
if !this.is_object() || !callback.is_function() {
return JSValue::undefined();
}
let obj = this.as_object();
let length_atom = ctx.common_atoms.length;
let len = if let Some(l) = obj.get(length_atom) {
if l.is_int() { l.get_int() as u32 } else { 0 }
} else {
0
};
if len == 0 {
if args.len() > 2 {
return args[2];
}
return JSValue::undefined();
}
let mut accumulator: JSValue;
let mut end_index: i64;
if args.len() > 2 {
accumulator = args[2];
end_index = len as i64 - 1;
} else {
accumulator = array_get(obj, (len - 1) as usize, ctx).unwrap_or_else(JSValue::undefined);
end_index = len as i64 - 2;
}
while end_index >= 0 {
if let Some(value) = array_get(obj, end_index as usize, ctx) {
let callback_args = vec![accumulator, value, JSValue::new_int(end_index), *this];
if let Ok(result) = call_callback(ctx, *callback, &callback_args) {
accumulator = result;
}
}
end_index -= 1;
}
accumulator
}
fn val_to_str(ctx: &mut JSContext, v: JSValue) -> String {
if v.is_int() {
format!("{}", v.get_int())
} else if v.is_float() {
format!("{}", v.get_float())
} else if v.is_string() {
ctx.get_atom_str(v.get_atom()).to_string()
} else if v.is_bool() {
v.get_bool().to_string()
} else if v.is_null() {
"null".to_string()
} else {
"undefined".to_string()
}
}
fn array_sort(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
if args.is_empty() {
return JSValue::undefined();
}
let this = args[0];
if !this.is_object() {
return this;
}
let len_atom = ctx.common_atoms.length;
let len = {
let arr = this.as_object();
arr.get(len_atom).map(|v| v.get_int() as usize).unwrap_or(0)
};
let mut elements: Vec<JSValue> = (0..len)
.map(|i| {
let arr = this.as_object();
array_get(arr, i, ctx).unwrap_or(JSValue::undefined())
})
.collect();
let cmp_fn = args.get(1).copied().filter(|v| v.is_function());
let n = elements.len();
for i in 1..n {
let mut j = i;
while j > 0 {
let should_swap = if let Some(func) = cmp_fn {
match call_callback(ctx, func, &[elements[j - 1], elements[j]]) {
Ok(r) => r.get_float() > 0.0 || (r.is_int() && r.get_int() > 0),
Err(_) => false,
}
} else {
let a_str = val_to_str(ctx, elements[j - 1]);
let b_str = val_to_str(ctx, elements[j]);
a_str > b_str
};
if should_swap {
elements.swap(j - 1, j);
j -= 1;
} else {
break;
}
}
}
let arr = this.as_object_mut();
if this.as_object().is_dense_array() {
let arr_obj = unsafe { &mut *(this.get_ptr() as *mut JSArrayObject) };
arr_obj.set_elements(elements);
} else {
if arr.has_dense_storage() {
arr.set_array_elements(elements.clone());
}
for (i, val) in elements.into_iter().enumerate() {
let key = ctx.int_atom_mut(i);
arr.set(key, val);
}
}
this
}
fn array_reverse(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
if args.is_empty() {
return JSValue::undefined();
}
let this = args[0];
if !this.is_object() {
return this;
}
let len_atom = ctx.common_atoms.length;
let len = {
let arr = this.as_object();
arr.get(len_atom).map(|v| v.get_int() as usize).unwrap_or(0)
};
let mut elements: Vec<JSValue> = (0..len)
.map(|i| {
let arr = this.as_object();
array_get(arr, i, ctx).unwrap_or(JSValue::undefined())
})
.collect();
elements.reverse();
let arr = this.as_object_mut();
if this.as_object().is_dense_array() {
let arr_obj = unsafe { &mut *(this.get_ptr() as *mut JSArrayObject) };
arr_obj.set_elements(elements);
} else {
if arr.has_dense_storage() {
arr.set_array_elements(elements.clone());
}
for (i, val) in elements.into_iter().enumerate() {
let key = ctx.int_atom_mut(i);
arr.set(key, val);
}
}
this
}
fn array_fill(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
if args.is_empty() {
return JSValue::undefined();
}
let this = args[0];
if !this.is_object() {
return this;
}
let value = args.get(1).copied().unwrap_or(JSValue::undefined());
let len_atom = ctx.common_atoms.length;
let len = {
let arr = this.as_object();
arr.get(len_atom).map(|v| v.get_int() as usize).unwrap_or(0)
};
let start = args
.get(2)
.map(|v| {
let i = v.get_int() as isize;
if i < 0 {
(len as isize + i).max(0) as usize
} else {
(i as usize).min(len)
}
})
.unwrap_or(0);
let end = args
.get(3)
.map(|v| {
let i = v.get_int() as isize;
if i < 0 {
(len as isize + i).max(0) as usize
} else {
(i as usize).min(len)
}
})
.unwrap_or(len);
let arr = this.as_object_mut();
for i in start..end {
let key = ctx.int_atom_mut(i);
arr.set(key, value);
}
this
}
fn array_splice(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
if args.is_empty() {
return JSValue::undefined();
}
let this = args[0];
if !this.is_object() {
return JSValue::undefined();
}
let len_atom = ctx.common_atoms.length;
let len = {
let arr = this.as_object();
arr.get(len_atom).map(|v| v.get_int() as usize).unwrap_or(0)
};
let start = args
.get(1)
.map(|v| {
let i = v.get_int() as isize;
if i < 0 {
(len as isize + i).max(0) as usize
} else {
(i as usize).min(len)
}
})
.unwrap_or(0);
let delete_count = args
.get(2)
.map(|v| (v.get_int() as usize).min(len - start))
.unwrap_or(len - start);
let insert_items: Vec<JSValue> = args.iter().skip(3).copied().collect();
let mut elements: Vec<JSValue> = (0..len)
.map(|i| {
let arr = this.as_object();
array_get(arr, i, ctx).unwrap_or(JSValue::undefined())
})
.collect();
let removed: Vec<JSValue> = elements
.splice(start..start + delete_count, insert_items)
.collect();
let mut removed_arr = new_jsarray_with_proto(ctx);
for v in removed.iter() {
removed_arr.push(*v);
}
removed_arr
.header
.set_length(len_atom, JSValue::new_int(removed_arr.len() as i64));
let arr = this.as_object_mut();
if this.as_object().is_dense_array() {
let arr_obj = unsafe { &mut *(this.get_ptr() as *mut JSArrayObject) };
arr_obj.set_elements(elements);
arr_obj
.header
.set_length(len_atom, JSValue::new_int(arr_obj.len() as i64));
} else {
for (i, val) in elements.iter().enumerate() {
let key = ctx.int_atom_mut(i);
arr.set(key, *val);
}
arr.set_length(len_atom, JSValue::new_int(elements.len() as i64));
}
alloc_jsarray(removed_arr, ctx)
}
fn array_to_spliced(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
if args.is_empty() {
return JSValue::undefined();
}
let this = args[0];
if !this.is_object() {
return JSValue::undefined();
}
let len_atom = ctx.common_atoms.length;
let len = {
let arr = this.as_object();
arr.get(len_atom).map(|v| v.get_int() as usize).unwrap_or(0)
};
let start = args
.get(1)
.map(|v| {
let i = v.get_int() as isize;
if i < 0 {
(len as isize + i).max(0) as usize
} else {
(i as usize).min(len)
}
})
.unwrap_or(0);
let delete_count = args
.get(2)
.map(|v| (v.get_int() as usize).min(len - start))
.unwrap_or(len - start);
let insert_items: Vec<JSValue> = args.iter().skip(3).copied().collect();
let mut elements: Vec<JSValue> = (0..len)
.map(|i| {
let arr = this.as_object();
array_get(arr, i, ctx).unwrap_or(JSValue::undefined())
})
.collect();
elements.splice(start..start + delete_count, insert_items);
let mut result_arr = new_jsarray_with_proto(ctx);
for v in elements.iter() {
result_arr.push(*v);
}
result_arr
.header
.set_length(len_atom, JSValue::new_int(result_arr.len() as i64));
alloc_jsarray(result_arr, ctx)
}
fn array_flat(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
if args.is_empty() {
return JSValue::undefined();
}
let this = args[0];
if !this.is_object() {
return JSValue::undefined();
}
let depth = args.get(1).map(|v| v.get_int() as usize).unwrap_or(1);
fn flatten(ctx: &mut JSContext, arr_val: JSValue, depth: usize, result: &mut Vec<JSValue>) {
if !arr_val.is_object() {
result.push(arr_val);
return;
}
let len_atom = ctx.common_atoms.length;
let arr = arr_val.as_object();
if !arr.is_array() {
result.push(arr_val);
return;
}
let len = arr.get(len_atom).map(|v| v.get_int() as usize).unwrap_or(0);
let elements: Vec<JSValue> = (0..len)
.map(|i| array_get(arr, i, ctx).unwrap_or(JSValue::undefined()))
.collect();
for el in elements {
if depth > 0 && el.is_object() {
let el_obj = el.as_object();
if el_obj.is_array() {
flatten(ctx, el, depth - 1, result);
continue;
}
}
result.push(el);
}
}
let mut flat_elements: Vec<JSValue> = Vec::new();
flatten(ctx, this, depth, &mut flat_elements);
let len_atom = ctx.common_atoms.length;
let mut result_arr = new_jsarray_with_proto(ctx);
for v in flat_elements.iter() {
result_arr.push(*v);
}
result_arr
.header
.set_length(len_atom, JSValue::new_int(result_arr.len() as i64));
alloc_jsarray(result_arr, ctx)
}
fn array_flat_map(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
if args.len() < 2 {
return JSValue::undefined();
}
let this = args[0];
let callback = args[1];
if !this.is_object() || !callback.is_function() {
return JSValue::undefined();
}
let len_atom = ctx.common_atoms.length;
let len = {
let arr = this.as_object();
arr.get(len_atom).map(|v| v.get_int() as usize).unwrap_or(0)
};
let mut result_elements: Vec<JSValue> = Vec::new();
for i in 0..len {
let el = {
let arr = this.as_object();
array_get(arr, i, ctx).unwrap_or(JSValue::undefined())
};
let idx_val = JSValue::new_int(i as i64);
match call_callback(ctx, callback, &[el, idx_val, this]) {
Ok(mapped) => {
if mapped.is_object() {
let mapped_obj = mapped.as_object();
if mapped_obj.is_array() {
let mlen = mapped_obj
.get(len_atom)
.map(|v| v.get_int() as usize)
.unwrap_or(0);
let mel_vec: Vec<JSValue> = (0..mlen)
.map(|j| array_get(mapped_obj, j, ctx).unwrap_or(JSValue::undefined()))
.collect();
result_elements.extend(mel_vec);
continue;
}
}
result_elements.push(mapped);
}
Err(_) => {}
}
}
let mut result_arr = new_jsarray_with_proto(ctx);
for v in result_elements.iter() {
result_arr.push(*v);
}
result_arr
.header
.set_length(len_atom, JSValue::new_int(result_arr.len() as i64));
alloc_jsarray(result_arr, ctx)
}
fn array_find_last(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
if args.len() < 2 {
return JSValue::undefined();
}
let this = args[0];
let callback = args[1];
if !this.is_object() {
return JSValue::undefined();
}
let len_atom = ctx.common_atoms.length;
let len = {
let arr = this.as_object();
arr.get(len_atom).map(|v| v.get_int() as usize).unwrap_or(0)
};
for i in (0..len).rev() {
let el = {
let arr = this.as_object();
array_get(arr, i, ctx).unwrap_or(JSValue::undefined())
};
let idx_val = JSValue::new_int(i as i64);
if let Ok(result) = call_callback(ctx, callback, &[el, idx_val, this]) {
if result.is_truthy() {
return el;
}
}
}
JSValue::undefined()
}
fn array_find_last_index(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
if args.len() < 2 {
return JSValue::new_int(-1);
}
let this = args[0];
let callback = args[1];
if !this.is_object() {
return JSValue::new_int(-1);
}
let len_atom = ctx.common_atoms.length;
let len = {
let arr = this.as_object();
arr.get(len_atom).map(|v| v.get_int() as usize).unwrap_or(0)
};
for i in (0..len).rev() {
let el = {
let arr = this.as_object();
array_get(arr, i, ctx).unwrap_or(JSValue::undefined())
};
let idx_val = JSValue::new_int(i as i64);
if let Ok(result) = call_callback(ctx, callback, &[el, idx_val, this]) {
if result.is_truthy() {
return JSValue::new_int(i as i64);
}
}
}
JSValue::new_int(-1)
}
fn array_at(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
if args.is_empty() {
return JSValue::undefined();
}
let this = args[0];
if !this.is_object() {
return JSValue::undefined();
}
let len_atom = ctx.common_atoms.length;
let arr = this.as_object();
let len = arr.get(len_atom).map(|v| v.get_int() as usize).unwrap_or(0);
let idx = args.get(1).map(|v| v.get_int()).unwrap_or(0);
let actual_idx = if idx < 0 { len as i64 + idx } else { idx };
if actual_idx < 0 || actual_idx as usize >= len {
return JSValue::undefined();
}
array_get(arr, actual_idx as usize, ctx).unwrap_or(JSValue::undefined())
}
fn array_to_sorted(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
if args.is_empty() {
return JSValue::undefined();
}
let this = args[0];
if !this.is_object() {
return JSValue::undefined();
}
let len_atom = ctx.common_atoms.length;
let len = {
let arr = this.as_object();
arr.get(len_atom).map(|v| v.get_int() as usize).unwrap_or(0)
};
let mut elements: Vec<JSValue> = (0..len)
.map(|i| {
let arr = this.as_object();
array_get(arr, i, ctx).unwrap_or(JSValue::undefined())
})
.collect();
let cmp_fn = args.get(1).copied().filter(|v| v.is_function());
let n = elements.len();
for i in 1..n {
let mut j = i;
while j > 0 {
let should_swap = if let Some(func) = cmp_fn {
match call_callback(ctx, func, &[elements[j - 1], elements[j]]) {
Ok(r) => r.get_float() > 0.0 || (r.is_int() && r.get_int() > 0),
Err(_) => false,
}
} else {
let a_str = val_to_str(ctx, elements[j - 1]);
let b_str = val_to_str(ctx, elements[j]);
a_str > b_str
};
if should_swap {
elements.swap(j - 1, j);
j -= 1;
} else {
break;
}
}
}
let mut result_arr = new_jsarray_with_proto(ctx);
for v in elements.iter() {
result_arr.push(*v);
}
result_arr
.header
.set_length(len_atom, JSValue::new_int(result_arr.len() as i64));
alloc_jsarray(result_arr, ctx)
}
fn array_to_reversed(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
if args.is_empty() {
return JSValue::undefined();
}
let this = args[0];
if !this.is_object() {
return JSValue::undefined();
}
let len_atom = ctx.common_atoms.length;
let len = {
let arr = this.as_object();
arr.get(len_atom).map(|v| v.get_int() as usize).unwrap_or(0)
};
let mut elements: Vec<JSValue> = (0..len)
.map(|i| {
let arr = this.as_object();
array_get(arr, i, ctx).unwrap_or(JSValue::undefined())
})
.collect();
elements.reverse();
let mut result_arr = new_jsarray_with_proto(ctx);
for v in elements.iter() {
result_arr.push(*v);
}
result_arr
.header
.set_length(len_atom, JSValue::new_int(result_arr.len() as i64));
alloc_jsarray(result_arr, ctx)
}
fn array_with(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
if args.len() < 3 {
return JSValue::undefined();
}
let this = args[0];
let idx = args[1].get_int();
let value = args[2];
if !this.is_object() {
return JSValue::undefined();
}
let len_atom = ctx.common_atoms.length;
let len = {
let arr = this.as_object();
arr.get(len_atom).map(|v| v.get_int() as usize).unwrap_or(0)
};
let mut elements: Vec<JSValue> = (0..len)
.map(|i| {
let arr = this.as_object();
array_get(arr, i, ctx).unwrap_or(JSValue::undefined())
})
.collect();
let actual_idx = if idx < 0 {
(len as i64 + idx) as usize
} else {
idx as usize
};
if actual_idx < len {
elements[actual_idx] = value;
}
let mut result_arr = new_jsarray_with_proto(ctx);
for v in elements.iter() {
result_arr.push(*v);
}
result_arr
.header
.set_length(len_atom, JSValue::new_int(result_arr.len() as i64));
alloc_jsarray(result_arr, ctx)
}
fn array_last_index_of(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
if args.len() < 2 {
return JSValue::new_int(-1);
}
let this = args[0];
let search = args[1];
if !this.is_object() {
return JSValue::new_int(-1);
}
let len_atom = ctx.common_atoms.length;
let arr = this.as_object();
let len = arr.get(len_atom).map(|v| v.get_int() as usize).unwrap_or(0);
for i in (0..len).rev() {
if let Some(el) = array_get(arr, i, ctx) {
if el.strict_eq(&search) {
return JSValue::new_int(i as i64);
}
}
}
JSValue::new_int(-1)
}
fn array_from_async(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
if args.is_empty() {
return create_thenable_result(ctx, Vec::new());
}
let items = &args[0];
if items.is_null() || items.is_undefined() {
return create_thenable_result(ctx, Vec::new());
}
if items.is_object() {
let obj = items.as_object();
let then_atom = ctx.common_atoms.then;
if obj.get(then_atom).is_some() {
let result = vec![*items];
return create_thenable_result(ctx, result);
}
let len_atom = ctx.common_atoms.length;
if let Some(len_val) = obj.get(len_atom) {
let len = len_val.get_int() as usize;
let mut elements: Vec<JSValue> = Vec::with_capacity(len);
for i in 0..len {
if let Some(val) = array_get(obj, i, ctx) {
elements.push(val);
} else {
elements.push(JSValue::undefined());
}
}
return create_thenable_result(ctx, elements);
}
}
create_thenable_result(ctx, Vec::new())
}
fn create_thenable_result(ctx: &mut JSContext, elements: Vec<JSValue>) -> JSValue {
use crate::object::object::JSObject;
let mut result_arr = new_jsarray_with_proto(ctx);
let len_atom = ctx.common_atoms.length;
for val in elements.iter() {
result_arr.push(*val);
}
result_arr
.header
.set_length(len_atom, JSValue::new_int(result_arr.len() as i64));
let arr_value = alloc_jsarray(result_arr, ctx);
let mut promise = JSObject::new_promise();
if let Some(proto_ptr) = ctx.get_promise_prototype() {
promise.prototype = Some(proto_ptr);
}
let state_atom = ctx.intern("__promise_state__");
promise.set(state_atom, JSValue::new_int(1));
let result_atom = ctx.intern("__promise_result__");
promise.set(result_atom, arr_value);
let reactions_atom = ctx.intern("__promise_reactions__");
promise.set(reactions_atom, JSValue::null());
let promise_ptr = Box::into_raw(Box::new(promise)) as usize;
JSValue::new_object(promise_ptr)
}