aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Carter Snook <cartersnook04@gmail.com> 2022-08-03 16:05:38 -0500
committerGravatar GitHub <noreply@github.com> 2022-08-03 14:05:38 -0700
commit033eebce2970c9cdd9dfc0fa3248f283d949d54d (patch)
treee11ed27ff54676f303f396fe8f183bc755a4c012
parent40a187f0de7a18330ac572a1d70e9402c8cbeeae (diff)
downloadbun-033eebce2970c9cdd9dfc0fa3248f283d949d54d.tar.gz
bun-033eebce2970c9cdd9dfc0fa3248f283d949d54d.tar.zst
bun-033eebce2970c9cdd9dfc0fa3248f283d949d54d.zip
refactor: create a high-level property iterator (#972)
This also fixes multiple memory leaks double frees.
-rw-r--r--src/bun.js/api/ffi.zig20
-rw-r--r--src/bun.js/api/transpiler.zig110
-rw-r--r--src/bun.js/bindings/bindings.zig192
-rw-r--r--src/bun.js/bindings/exports.zig61
-rw-r--r--src/js_ast.zig59
5 files changed, 289 insertions, 153 deletions
diff --git a/src/bun.js/api/ffi.zig b/src/bun.js/api/ffi.zig
index 1e21330e5..afe442096 100644
--- a/src/bun.js/api/ffi.zig
+++ b/src/bun.js/api/ffi.zig
@@ -559,20 +559,18 @@ pub const FFI = struct {
pub fn generateSymbols(global: *JSGlobalObject, symbols: *std.StringArrayHashMapUnmanaged(Function), object: JSC.JSValue) !?JSValue {
const allocator = VirtualMachine.vm.allocator;
- var keys = JSC.C.JSObjectCopyPropertyNames(global.ref(), object.asObjectRef());
- defer JSC.C.JSPropertyNameArrayRelease(keys);
- const count = JSC.C.JSPropertyNameArrayGetCount(keys);
+ var symbols_iter = JSC.JSPropertyIterator(.{
+ .skip_empty_name = true,
+ .name_encoding = .utf8,
+ .include_value = true,
+ }).init(global.ref(), object.asObjectRef());
+ defer symbols_iter.deinit();
- try symbols.ensureTotalCapacity(allocator, count);
+ try symbols.ensureTotalCapacity(allocator, symbols_iter.len);
- var i: usize = 0;
- while (i < count) : (i += 1) {
- var property_name_ref = JSC.C.JSPropertyNameArrayGetNameAtIndex(keys, i);
- const len = JSC.C.JSStringGetLength(property_name_ref);
- if (len == 0) continue;
- var prop = JSC.C.JSStringGetCharacters8Ptr(property_name_ref)[0..len];
+ while (symbols_iter.next()) |prop| {
+ const value = symbols_iter.value;
- var value = JSC.JSValue.c(JSC.C.JSObjectGetProperty(global.ref(), object.asObjectRef(), property_name_ref, null));
if (value.isEmptyOrUndefinedOrNull()) {
return JSC.toTypeError(JSC.Node.ErrorCode.ERR_INVALID_ARG_VALUE, "Expected an object for key \"{s}\"", .{prop}, global.ref());
}
diff --git a/src/bun.js/api/transpiler.zig b/src/bun.js/api/transpiler.zig
index d71264a18..a21f40e2e 100644
--- a/src/bun.js/api/transpiler.zig
+++ b/src/bun.js/api/transpiler.zig
@@ -363,45 +363,37 @@ fn transformOptionsFromJSC(ctx: JSC.C.JSContextRef, temp_allocator: std.mem.Allo
return transpiler;
}
- var array = JSC.C.JSObjectCopyPropertyNames(globalThis.ref(), define.asObjectRef());
- defer JSC.C.JSPropertyNameArrayRelease(array);
- const count = JSC.C.JSPropertyNameArrayGetCount(array);
+ var define_iter = JSC.JSPropertyIterator(.{
+ .skip_empty_name = false,
+ .name_encoding = .utf8,
+ .include_value = true,
+ }).init(globalThis.ref(), define.asObjectRef());
+ defer define_iter.deinit();
+
// cannot be a temporary because it may be loaded on different threads.
- var map_entries = allocator.alloc([]u8, count * 2) catch unreachable;
- var names = map_entries[0..count];
-
- var values = map_entries[count..];
-
- var i: usize = 0;
- while (i < count) : (i += 1) {
- // no need to free because we free the name array at once
- var property_name_ref = JSC.C.JSPropertyNameArrayGetNameAtIndex(
- array,
- i,
- );
- const prop: []const u8 = JSC.C.JSStringGetCharacters8Ptr(property_name_ref)[0..JSC.C.JSStringGetLength(property_name_ref)];
- const property_value: JSC.JSValue = JSC.JSValue.fromRef(
- JSC.C.JSObjectGetProperty(
- globalThis.ref(),
- define.asObjectRef(),
- property_name_ref,
- null,
- ),
- );
+ var map_entries = allocator.alloc([]u8, define_iter.len * 2) catch unreachable;
+ var names = map_entries[0..define_iter.len];
+
+ var values = map_entries[define_iter.len..];
+
+ while (define_iter.next()) |prop| {
+ const property_value = define_iter.value;
const value_type = property_value.jsType();
if (!value_type.isStringLike()) {
JSC.throwInvalidArguments("define \"{s}\" must be a JSON string", .{prop}, ctx, exception);
return transpiler;
}
- names[i] = allocator.dupe(u8, prop) catch unreachable;
+
+ names[define_iter.i] = allocator.dupe(u8, prop) catch unreachable;
var val = JSC.ZigString.init("");
property_value.toZigString(&val, globalThis);
if (val.len == 0) {
val = JSC.ZigString.init("\"\"");
}
- values[i] = std.fmt.allocPrint(allocator, "{}", .{val}) catch unreachable;
+ values[define_iter.i] = std.fmt.allocPrint(allocator, "{}", .{val}) catch unreachable;
}
+
transpiler.transform.define = Api.StringMap{
.keys = names,
.values = values,
@@ -645,55 +637,30 @@ fn transformOptionsFromJSC(ctx: JSC.C.JSContextRef, temp_allocator: std.mem.Allo
return transpiler;
}
- var total_name_buf_len: usize = 0;
-
- var array = js.JSObjectCopyPropertyNames(ctx, replace.asObjectRef());
- defer js.JSPropertyNameArrayRelease(array);
- const property_names_count = @intCast(u32, js.JSPropertyNameArrayGetCount(array));
- var iter = JSC.JSPropertyNameIterator{
- .array = array,
- .count = @intCast(u32, property_names_count),
- };
-
- {
- var key_iter = iter;
- while (key_iter.next()) |item| {
- total_name_buf_len += JSC.C.JSStringGetLength(item);
- }
- }
-
- if (total_name_buf_len > 0) {
- var total_name_buf = try std.ArrayList(u8).initCapacity(bun.default_allocator, total_name_buf_len);
- errdefer total_name_buf.clearAndFree();
-
- try replacements.ensureUnusedCapacity(bun.default_allocator, property_names_count);
- defer {
- if (exception.* != null) {
- total_name_buf.clearAndFree();
- replacements.clearAndFree(bun.default_allocator);
- }
- }
-
- while (iter.next()) |item| {
- // no need to free key because we free the name array at once
-
- const start = total_name_buf.items.len;
- total_name_buf.items.len += @maximum(
- // this returns a null terminated string
- JSC.C.JSStringGetUTF8CString(item, total_name_buf.items.ptr + start, total_name_buf.capacity - start),
- 1,
- ) - 1;
- const key = total_name_buf.items[start..total_name_buf.items.len];
- // if somehow the string is empty, skip it
- if (key.len == 0)
- continue;
-
- const value = replace.get(globalThis, key).?;
+ var iter = JSC.JSPropertyIterator(.{
+ .skip_empty_name = true,
+ .name_encoding = .utf8,
+ .include_value = true,
+ .override_writing_cstring = true,
+ }).initCStringBuffer(globalThis.ref(), replace.asObjectRef(), bun.default_allocator);
+
+ if (iter.len > 0) {
+ errdefer iter.deinit(bun.default_allocator);
+ try replacements.ensureUnusedCapacity(bun.default_allocator, iter.len);
+
+ // We cannot set the exception before `try` because it could be
+ // a double free with the `errdefer`.
+ defer if (exception.* != null) {
+ iter.deinit(bun.default_allocator);
+ replacements.clearAndFree(bun.default_allocator);
+ };
+
+ while (iter.next()) |key| {
+ const value = iter.value;
if (value.isEmpty()) continue;
if (!JSLexer.isIdentifier(key)) {
JSC.throwInvalidArguments("\"{s}\" is not a valid ECMAScript identifier", .{key}, ctx, exception);
- total_name_buf.deinit();
return transpiler;
}
@@ -713,7 +680,6 @@ fn transformOptionsFromJSC(ctx: JSC.C.JSContextRef, temp_allocator: std.mem.Allo
if (!JSLexer.isIdentifier(replacement_name)) {
JSC.throwInvalidArguments("\"{s}\" is not a valid ECMAScript identifier", .{replacement_name}, ctx, exception);
- total_name_buf.deinit();
slice.deinit();
return transpiler;
}
diff --git a/src/bun.js/bindings/bindings.zig b/src/bun.js/bindings/bindings.zig
index cf3d19ff5..7873165a9 100644
--- a/src/bun.js/bindings/bindings.zig
+++ b/src/bun.js/bindings/bindings.zig
@@ -3664,3 +3664,195 @@ pub fn Thenable(comptime Then: type, comptime onResolve: fn (*Then, globalThis:
}
};
}
+
+pub const JSPropertyIteratorOptions = struct {
+ skip_empty_name: bool,
+ name_encoding: NameEncoding,
+ include_value: bool,
+ /// Overrides the setup for the iterator's item to instead be a `usize`
+ /// indicating the length of the latest property value and writes the
+ override_writing_cstring: bool = false,
+
+ pub const NameEncoding = enum {
+ utf8,
+ utf16,
+ /// Constructs the strings with a variable encoding using JSC's string
+ /// encoding API.
+ infer,
+ };
+};
+
+pub fn JSPropertyIterator(comptime options: JSPropertyIteratorOptions) type {
+ comptime if (options.override_writing_cstring) {
+ if (options.name_encoding != .utf8) {
+ @compileError(std.fmt.comptimePrint("{} is not a supported encoding when 'options.override_writing_cstring' is enabled. Try using utf8 instead.", .{options.name_encoding}));
+ }
+ };
+
+ return struct {
+ i: u32 = 0,
+ len: u32,
+ array_ref: JSC.C.JSPropertyNameArrayRef,
+
+ /// The `JSValue` of the current property.
+ ///
+ /// Invokes undefined behavior if an iteration has not yet occurred and
+ /// zero-sized when `options.include_value` is not enabled.
+ value: if (options.include_value) JSC.JSValue else void,
+ /// Zero-sized when `options.include_value` is not enabled.
+ object: if (options.include_value) JSC.C.JSObjectRef else void,
+ /// Zero-sized when `options.include_value` is not enabled.
+ global: if (options.include_value) JSC.C.JSContextRef else void,
+
+ /// The buffer for writing to when `options.override_writing_cstring`
+ /// is enabled. Zero-sized when `options.override_writing_cstring` is
+ /// not enabled.
+ cstring_buffer: if (options.override_writing_cstring) []u8 else void,
+
+ const Self = @This();
+
+ pub const Item = switch (options.name_encoding) {
+ .utf8 => []const u8,
+ .utf16 => []const u16,
+ .infer => ZigString,
+ };
+
+ inline fn initInternal(global: JSC.C.JSContextRef, object: JSC.C.JSObjectRef) Self {
+ return .{
+ .array_ref = JSC.C.JSObjectCopyPropertyNames(global, object),
+ .len = @intCast(u32, JSC.C.JSPropertyNameArrayGetCount(object)),
+ .object = if (comptime options.include_value) object else .{},
+ .global = if (comptime options.include_value) global else .{},
+ .value = undefined,
+ .cstring_buffer = undefined,
+ };
+ }
+
+ /// Initializes the iterator. Make sure you `deinit()` it!
+ ///
+ /// Not recommended for use when using the CString buffer mode as the
+ /// buffer must be manually initialized. Instead, see
+ /// `JSPropertyIterator.initCStringBuffer()`.
+ pub inline fn init(global: JSC.C.JSContextRef, object: JSC.C.JSObjectRef) Self {
+ return Self.initInternal(global, object);
+ }
+
+ usingnamespace if (options.override_writing_cstring) struct {
+ /// Initializes the iterator using the CString buffer created using your
+ /// provided allocator. Note that this is requires two iterations for
+ /// each item at the slowest speed because we need to do a look-ahead
+ /// to preallocate the string buffer so it should only be used in
+ /// secular cases.
+ pub fn initCStringBuffer(global: JSC.C.JSContextRef, object: JSC.C.JSObjectRef, allocator: std.mem.Allocator) Self {
+ var self = Self.initInternal(global, object);
+ const total_len = self.peekTotalPropertyLengths();
+ self.cstring_buffer = allocator.alloc(u8, total_len) catch unreachable;
+ return self;
+ }
+
+ /// Deinitializes the property name array and all of the string
+ /// references constructed by the copy along with the CString
+ /// buffer. The allocator the CString buffer was initialized with
+ /// **must** be the same as the one passed in here.
+ pub inline fn deinit(self: *Self, allocator: std.mem.Allocator) void {
+ JSC.C.JSPropertyNameArrayRelease(self.array_ref);
+ allocator.free(self.cstring_buffer);
+ }
+
+ /// Deinitializes the property name array from JSC and returns the
+ /// CString buffer. The caller owns the returned buffer and free the
+ /// buffer with the same allocator as it was initialized with.
+ pub inline fn toOwnedCStringBuffer(self: Self) []u8 {
+ JSC.C.JSPropertyNameArrayRelease(self.array_ref);
+ return self.cstring_buffer;
+ }
+ } else struct {
+ /// Deinitializes the property name array and all of the string
+ /// references constructed by the copy.
+ pub inline fn deinit(self: *Self) void {
+ JSC.C.JSPropertyNameArrayRelease(self.array_ref);
+ }
+ };
+
+ /// Finds the next property string and, if `options.include_value` is
+ /// enabled, updates the `iter.value` to respect the latest property's
+ /// value. Also note the behavior of the other options.
+ pub fn next(self: *Self) ?Item {
+ if (self.i == self.len) return null;
+
+ var property_name_ref = JSC.C.JSPropertyNameArrayGetNameAtIndex(self.array_ref, self.i);
+ self.i += 1;
+
+ if (comptime options.override_writing_cstring) {
+ const start = self.cstring_buffer.len;
+
+ // The JSStringGetUTF8CString returns a null terminated string
+ // and writes to the buffer. We just need to adjust the length
+ // to update the slice to hold the items wrote to the pointer
+ // because we already those bytes are allocated from the initial
+ // iteration.
+ _ = JSC.C.JSStringGetUTF8CString(property_name_ref, self.cstring_buffer.ptr + start, self.cstring_buffer.len - start);
+
+ const prop = self.cstring_buffer[start..self.cstring_buffer.len];
+
+ if (comptime options.skip_empty_name) {
+ if (prop.len == 0) return self.next();
+ }
+
+ if (comptime options.include_value) {
+ self.value = JSC.JSValue.fromRef(JSC.C.JSObjectGetProperty(self.global, self.object, property_name_ref, null));
+ }
+
+ return prop;
+ }
+
+ const len = JSC.C.JSStringGetLength(property_name_ref);
+
+ if (comptime options.skip_empty_name) {
+ if (len == 0) return self.next();
+ }
+
+ const prop = switch (comptime options.name_encoding) {
+ .utf8 => JSC.C.JSStringGetCharacters8Ptr(property_name_ref)[0..len],
+ .utf16 => JSC.C.JSStringGetCharactersPtr(property_name_ref)[0..len],
+ .infer => blk: {
+ const enc = JSC.C.JSStringEncoding(property_name_ref);
+ break :blk switch (enc) {
+ .empty => ZigString.Empty,
+ .char8 => blk2: {
+ var s = ZigString.init((JSC.C.JSStringGetCharacters8Ptr(property_name_ref))[0..len]);
+ s.markUTF8();
+ break :blk2 s;
+ },
+ .char16 => blk2: {
+ var s = ZigString.init16(JSC.C.JSStringGetCharactersPtr(property_name_ref)[0..len]);
+ s.markUTF16();
+ break :blk2 s;
+ },
+ };
+ },
+ };
+
+ if (comptime options.include_value) {
+ self.value = JSC.JSValue.fromRef(JSC.C.JSObjectGetProperty(self.global, self.object, property_name_ref, null));
+ }
+
+ return prop;
+ }
+
+ /// Finds the combined lengths of all of the unconsumed string property
+ /// names without advancing the iterator. Starts from the current index
+ /// in the iterator.
+ pub fn peekTotalPropertyLengths(self: *const Self) usize {
+ var i = self.i;
+ var total: usize = 0;
+
+ while (i < self.len) : (i += 1) {
+ const ref = JSC.C.JSPropertyNameArrayGetNameAtIndex(self.array_ref, i);
+ total += JSC.C.JSStringGetLength(ref);
+ }
+
+ return total;
+ }
+ };
+}
diff --git a/src/bun.js/bindings/exports.zig b/src/bun.js/bindings/exports.zig
index 69d3ae80e..14c6412f3 100644
--- a/src/bun.js/bindings/exports.zig
+++ b/src/bun.js/bindings/exports.zig
@@ -1927,29 +1927,28 @@ pub const ZigConsoleClient = struct {
const prev_quote_strings = this.quote_strings;
this.quote_strings = true;
defer this.quote_strings = prev_quote_strings;
- var array = CAPI.JSObjectCopyPropertyNames(this.globalThis.ref(), props.asObjectRef());
- defer CAPI.JSPropertyNameArrayRelease(array);
- const count_ = CAPI.JSPropertyNameArrayGetCount(array);
+
+ var props_iter = JSC.JSPropertyIterator(.{
+ .skip_empty_name = true,
+ .name_encoding = .utf8,
+ .include_value = true,
+ }).init(this.globalThis.ref(), props.asObjectRef());
+ defer props_iter.deinit();
+
var children_prop = props.get(this.globalThis, "children");
- if (count_ > 0) {
+ if (props_iter.len > 0) {
{
var i: usize = 0;
this.indent += 1;
defer this.indent -|= 1;
- const count_without_children = count_ - @as(usize, @boolToInt(children_prop != null));
-
- while (i < count_) : (i += 1) {
- // no need to free because we free the name array at once
- var property_name_ref = CAPI.JSPropertyNameArrayGetNameAtIndex(array, i);
+ const count_without_children = props_iter.len - @as(usize, @boolToInt(children_prop != null));
- const prop_len = CAPI.JSStringGetLength(property_name_ref);
- if (prop_len == 0) continue;
- var prop = CAPI.JSStringGetCharacters8Ptr(property_name_ref)[0..prop_len];
+ while (props_iter.next()) |prop| {
if (strings.eqlComptime(prop, "children"))
continue;
- var property_value = CAPI.JSObjectGetProperty(this.globalThis.ref(), props.asObjectRef(), property_name_ref, null);
- const tag = Tag.get(JSValue.fromRef(property_value), this.globalThis);
+ var property_value = props_iter.value;
+ const tag = Tag.get(property_value, this.globalThis);
if (tag.cell.isHidden()) continue;
@@ -1967,7 +1966,7 @@ pub const ZigConsoleClient = struct {
}
}
- this.format(tag, Writer, writer_, JSValue.fromRef(property_value), this.globalThis, enable_ansi_colors);
+ this.format(tag, Writer, writer_, property_value, this.globalThis, enable_ansi_colors);
if (tag.cell.isStringLike()) {
if (comptime enable_ansi_colors) {
@@ -2089,10 +2088,12 @@ pub const ZigConsoleClient = struct {
var object = value.asObjectRef();
{
- var array = CAPI.JSObjectCopyPropertyNames(this.globalThis.ref(), object);
- defer CAPI.JSPropertyNameArrayRelease(array);
- const count_ = CAPI.JSPropertyNameArrayGetCount(array);
- var i: usize = 0;
+ var props_iter = JSC.JSPropertyIterator(.{
+ .skip_empty_name = true,
+ .name_encoding = .infer,
+ .include_value = true,
+ }).init(this.globalThis.ref(), object);
+ defer props_iter.deinit();
const prev_quote_strings = this.quote_strings;
this.quote_strings = true;
@@ -2110,29 +2111,19 @@ pub const ZigConsoleClient = struct {
}
}
- if (count_ == 0) {
+ if (props_iter.len == 0) {
writer.writeAll("{ }");
return;
}
writer.writeAll("{ ");
- while (i < count_) : (i += 1) {
- var property_name_ref = CAPI.JSPropertyNameArrayGetNameAtIndex(array, i);
- const len = CAPI.JSStringGetLength(property_name_ref);
- if (len == 0) continue;
- const encoding = CAPI.JSStringEncoding(property_name_ref);
-
- var property_value = CAPI.JSObjectGetProperty(this.globalThis.ref(), object, property_name_ref, null);
- const tag = Tag.get(JSValue.fromRef(property_value), this.globalThis);
+ while (props_iter.next()) |key| {
+ var property_value = props_iter.value;
+ const tag = Tag.get(JSValue.fromRef(property_value.asObjectRef()), this.globalThis);
if (tag.cell.isHidden()) continue;
- const key: ZigString = if (encoding == .char8)
- ZigString.init((JSC.C.JSStringGetCharacters8Ptr(property_name_ref))[0..@minimum(len, 128)])
- else
- ZigString.init16(JSC.C.JSStringGetCharactersPtr(property_name_ref)[0..@minimum(len, 128)]);
-
// TODO: make this one pass?
if (!key.is16Bit() and JSLexer.isLatin1Identifier(@TypeOf(key.slice()), key.slice())) {
writer.print(
@@ -2167,7 +2158,7 @@ pub const ZigConsoleClient = struct {
}
}
- this.format(tag, Writer, writer_, JSValue.fromRef(property_value), this.globalThis, enable_ansi_colors);
+ this.format(tag, Writer, writer_, property_value, this.globalThis, enable_ansi_colors);
if (tag.cell.isStringLike()) {
if (comptime enable_ansi_colors) {
@@ -2175,7 +2166,7 @@ pub const ZigConsoleClient = struct {
}
}
- if (i + 1 < count_) {
+ if (props_iter.i + 1 < props_iter.len) {
this.printComma(Writer, writer_, enable_ansi_colors) catch unreachable;
writer.writeAll(" ");
}
diff --git a/src/js_ast.zig b/src/js_ast.zig
index a34cac452..3c6b6de44 100644
--- a/src/js_ast.zig
+++ b/src/js_ast.zig
@@ -1179,7 +1179,7 @@ pub const E = struct {
/// - static the function is React.jsxsDEV, "jsxs" instead of "jsx"
/// - one child? the function is React.jsxDEV,
/// - no children? the function is React.jsxDEV and children is an empty array.
- /// `isStaticChildren`: https://github.com/facebook/react/blob/4ca62cac45c288878d2532e5056981d177f9fdac/packages/react/src/jsx/ReactJSXElementValidator.js#L369-L384
+ /// `isStaticChildren`: https://github.com/facebook/react/blob/4ca62cac45c288878d2532e5056981d177f9fdac/packages/react/src/jsx/ReactJSXElementValidator.js#L369-L384
/// This flag means children is an array of JSX Elements literals.
/// The documentation on this is sparse, but it appears that
/// React just calls Object.freeze on the children array.
@@ -4147,7 +4147,7 @@ pub const Op = struct {
bin_bitwise_xor_assign,
/// Right-associative
bin_nullish_coalescing_assign,
- /// Right-associative
+ /// Right-associative
bin_logical_or_assign,
/// Right-associative
bin_logical_and_assign,
@@ -6990,27 +6990,21 @@ pub const Macro = struct {
}
const JSLexer = @import("./js_lexer.zig");
- var array = js.JSObjectCopyPropertyNames(writer.ctx, import_namespace_arg.asObjectRef());
- defer js.JSPropertyNameArrayRelease(array);
- const property_names_count = @intCast(u32, js.JSPropertyNameArrayGetCount(array));
- var iter = JSCBase.JSPropertyNameIterator{
- .array = array,
- .count = @intCast(u32, property_names_count),
- };
+
+ var array_iter = JSC.JSPropertyIterator(.{
+ .skip_empty_name = true,
+ .name_encoding = .utf8,
+ .include_value = true,
+ }).init(writer.ctx, import_namespace_arg.asObjectRef());
+ defer array_iter.deinit();
import.import.items = writer.allocator.alloc(
ClauseItem,
- @intCast(u32, @boolToInt(has_default)) + property_names_count,
+ @intCast(u32, @boolToInt(has_default)) + array_iter.len,
) catch return false;
- var object_ref = import_namespace_arg.asObjectRef();
-
- while (iter.next()) |prop| {
- const ptr = js.JSStringGetCharacters8Ptr(prop);
- const len = js.JSStringGetLength(prop);
- const name = ptr[0..len];
-
- const property_value = JSC.JSValue.fromRef(js.JSObjectGetProperty(writer.ctx, object_ref, prop, writer.exception));
+ while (array_iter.next()) |name| {
+ const property_value = array_iter.value;
if (!property_value.isString()) {
return false;
@@ -7938,10 +7932,13 @@ pub const Macro = struct {
}
var object = value.asObjectRef();
- var array = JSC.C.JSObjectCopyPropertyNames(this.global.ref(), object);
- defer JSC.C.JSPropertyNameArrayRelease(array);
- const count_ = JSC.C.JSPropertyNameArrayGetCount(array);
- var properties = this.allocator.alloc(G.Property, count_) catch unreachable;
+ var object_iter = JSC.JSPropertyIterator(.{
+ .skip_empty_name = false,
+ .name_encoding = .utf8,
+ .include_value = true,
+ }).init(this.global.ref(), object);
+ defer object_iter.deinit();
+ var properties = this.allocator.alloc(G.Property, object_iter.len) catch unreachable;
errdefer this.allocator.free(properties);
var out = Expr.init(
E.Object,
@@ -7953,21 +7950,13 @@ pub const Macro = struct {
);
_entry.value_ptr.* = out;
- var i: usize = 0;
- while (i < count_) : (i += 1) {
- var property_name_ref = JSC.C.JSPropertyNameArrayGetNameAtIndex(array, i);
- defer JSC.C.JSStringRelease(property_name_ref);
- properties[i] = G.Property{
- .key = Expr.init(E.String, E.String.init(this.allocator.dupe(
- u8,
- JSC.C.JSStringGetCharacters8Ptr(property_name_ref)[0..JSC.C.JSStringGetLength(property_name_ref)],
- ) catch unreachable), this.caller.loc),
- .value = try this.run(
- JSC.JSValue.fromRef(JSC.C.JSObjectGetProperty(this.global.ref(), object, property_name_ref, null)),
- ),
+ while (object_iter.next()) |prop| {
+ properties[object_iter.i] = G.Property{
+ .key = Expr.init(E.String, E.String.init(this.allocator.dupe(u8, prop) catch unreachable), this.caller.loc),
+ .value = try this.run(object_iter.value),
};
}
- out.data.e_object.properties = BabyList(G.Property).init(properties[0..i]);
+ out.data.e_object.properties = BabyList(G.Property).init(properties[0..object_iter.i]);
_entry.value_ptr.* = out;
return out;
},