diff options
author | 2022-08-03 16:05:38 -0500 | |
---|---|---|
committer | 2022-08-03 14:05:38 -0700 | |
commit | 033eebce2970c9cdd9dfc0fa3248f283d949d54d (patch) | |
tree | e11ed27ff54676f303f396fe8f183bc755a4c012 | |
parent | 40a187f0de7a18330ac572a1d70e9402c8cbeeae (diff) | |
download | bun-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.zig | 20 | ||||
-rw-r--r-- | src/bun.js/api/transpiler.zig | 110 | ||||
-rw-r--r-- | src/bun.js/bindings/bindings.zig | 192 | ||||
-rw-r--r-- | src/bun.js/bindings/exports.zig | 61 | ||||
-rw-r--r-- | src/js_ast.zig | 59 |
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; }, |