Compare commits

...

2 Commits

Author SHA1 Message Date
DonIsaac
4b305265bb bun run zig-format 2025-03-03 02:04:33 +00:00
Don Isaac
1802c79142 refactor: move JSValue.get to JSObject 2025-03-02 18:02:20 -08:00
4 changed files with 113 additions and 32 deletions

View File

@@ -14,6 +14,18 @@ pub const JSObject = extern struct {
return JSValue.fromCell(obj);
}
/// Non-objects will be runtime-coerced to objects.
///
/// For cells this is `toObjectSlow`, for other types it's `toObjectSlowCase`.
pub fn fromJS(value: JSValue, globalThis: JSValue) *JSObject {
return JSValue.toObject(value, globalThis);
}
/// Returns `null` if the value is not an object.
pub fn tryFromJS(maybe_obj: JSValue, globalThis: *JSC.JSGlobalObject) ?*JSObject {
return JSValue.asObject(maybe_obj, globalThis);
}
/// Marshall a struct instance into a JSObject, copying its properties.
///
/// Each field will be encoded with `JSC.toJS`. Fields whose types have a
@@ -84,6 +96,49 @@ pub const JSObject = extern struct {
}
}
/// Equivalent to `target[property]`. Calls userland getters/proxies. Can
/// throw. Null indicates the property does not exist. JavaScript undefined
/// and JavaScript null can exist as a property and is different than zig
/// `null` (property does not exist).
///
/// `property` must be either `[]const u8`. A comptime slice may defer to
/// calling `fastGet`, which use a more optimal code path. This function is
/// marked `inline` to allow Zig to determine if `fastGet` should be used
/// per invocation.
pub inline fn get(target: *JSObject, global: *JSGlobalObject, property: anytype) bun.JSError!?JSValue {
const property_slice: []const u8 = property; // must be a slice!
// This call requires `get` to be `inline`
if (bun.isComptimeKnown(property_slice)) {
if (comptime JSValue.BuiltinName.get(property_slice)) |builtin_name| {
return target.fastGetWithError(global, builtin_name);
}
}
return switch (JSC__JSObject__getIfPropertyExistsImpl(target, global, property_slice.ptr, @intCast(property_slice.len))) {
.zero => error.JSError,
.property_does_not_exist_on_object => null,
// TODO: see bug described in ObjectBindings.cpp
// since there are false positives, the better path is to make them
// negatives, as the number of places that desire throwing on
// existing undefined is extremely small, but non-zero.
.undefined => null,
else => |val| val,
};
}
extern fn JSC__JSObject__getIfPropertyExistsImpl(object: *JSObject, global: *JSGlobalObject, ptr: [*]const u8, len: u32) JSValue;
pub fn fastGetWithError(this: *JSObject, global: *JSGlobalObject, builtin_name: JSValue.BuiltinName) bun.JSError!?JSValue {
return switch (JSC__JSObject__fastGet(this, global, @intFromEnum(builtin_name))) {
.zero => error.JSError,
.undefined => null,
.property_does_not_exist_on_object => null,
else => |val| val,
};
}
extern fn JSC__JSObject__fastGet(object: *JSObject, global: *JSGlobalObject, builtin_name: u32) JSValue;
extern fn JSC__createStructure(*JSC.JSGlobalObject, *JSC.JSCell, u32, names: [*]ExternColumnIdentifier) JSC.JSValue;
pub const ExternColumnIdentifier = extern struct {
@@ -106,6 +161,7 @@ pub const JSObject = extern struct {
}
}
};
pub fn createStructure(global: *JSGlobalObject, owner: JSC.JSValue, length: u32, names: [*]ExternColumnIdentifier) JSValue {
JSC.markBinding(@src());
return JSC__createStructure(global, owner.asCell(), length, names);

View File

@@ -1535,10 +1535,17 @@ pub const JSValue = enum(i64) {
};
}
/// Non-objects will be converted to an object via `toObjectSlowCase()`
pub fn toObject(this: JSValue, globalThis: *JSGlobalObject) *JSObject {
return cppFn("toObject", .{ this, globalThis });
}
/// Cast `this` to a JSObject, or return null if it is not an object.
/// Use `toObject` to convert non-objects.
pub fn asObject(this: JSValue, globalThis: *JSGlobalObject) ?*JSObject {
return if (this.isObject()) this.toObject(globalThis) else null;
}
pub fn getPrototype(this: JSValue, globalObject: *JSGlobalObject) JSValue {
return cppFn("getPrototype", .{ this, globalObject });
}
@@ -1701,28 +1708,17 @@ pub const JSValue = enum(i64) {
/// calling `fastGet`, which use a more optimal code path. This function is
/// marked `inline` to allow Zig to determine if `fastGet` should be used
/// per invocation.
///
/// ## Deprecated
/// Cast `target` to a JSObject then use `.get()` instead.
/// ```zig
/// if (value.asObject(global)) |obj| {
/// const prop = obj.get(global, "foo");
/// }
/// ```
pub inline fn get(target: JSValue, global: *JSGlobalObject, property: anytype) JSError!?JSValue {
if (bun.Environment.isDebug) bun.assert(target.isObject());
const property_slice: []const u8 = property; // must be a slice!
// This call requires `get` to be `inline`
if (bun.isComptimeKnown(property_slice)) {
if (comptime BuiltinName.get(property_slice)) |builtin_name| {
return target.fastGetWithError(global, builtin_name);
}
}
return switch (JSC__JSValue__getIfPropertyExistsImpl(target, global, property_slice.ptr, @intCast(property_slice.len))) {
.zero => error.JSError,
.property_does_not_exist_on_object => null,
// TODO: see bug described in ObjectBindings.cpp
// since there are false positives, the better path is to make them
// negatives, as the number of places that desire throwing on
// existing undefined is extremely small, but non-zero.
.undefined => null,
else => |val| val,
};
return target.toObject(global).get(global, property);
}
extern fn JSC__JSValue__getOwn(value: JSValue, globalObject: *JSGlobalObject, propertyName: *const bun.String) JSValue;

View File

@@ -140,6 +140,8 @@
#endif
#endif
static inline const JSC::Identifier builtinNameMap(JSC::VM& vm, unsigned char name);
static WTF::StringView StringView_slice(WTF::StringView sv, unsigned start, unsigned end)
{
return sv.substring(start, end - start);
@@ -2416,6 +2418,35 @@ void JSC__JSValue__putRecord(JSC__JSValue objectValue, JSC__JSGlobalObject* glob
object->putDirect(global->vm(), ident, descriptor.value());
scope.release();
}
// Returns empty for exception, returns deleted if not found.
// Be careful when handling the return value.
JSC__JSValue JSC__JSObject__getIfPropertyExistsImpl(JSC::JSObject* object,
JSC__JSGlobalObject* globalObject,
const unsigned char* arg1, uint32_t arg2)
{
ASSERT_NO_PENDING_EXCEPTION(globalObject);
auto& vm = JSC::getVM(globalObject);
ASSERT(object != nullptr);
// Since Identifier might not ref the string, we need to ensure it doesn't get deref'd until this function returns
const auto propertyString = String(StringImpl::createWithoutCopying({ arg1, arg2 }));
const auto identifier = JSC::Identifier::fromString(vm, propertyString);
const auto property = JSC::PropertyName(identifier);
return JSC::JSValue::encode(Bun::getIfPropertyExistsPrototypePollutionMitigationUnsafe(vm, globalObject, object, property));
}
// Returns empty for exception, returns deleted if not found.
// Be careful when handling the return value.
JSC__JSValue JSC__JSObject__fastGet(JSC::JSObject* object, JSC__JSGlobalObject* globalObject, unsigned char arg2)
{
ASSERT(object);
auto& vm = JSC::getVM(globalObject);
const auto property = JSC::PropertyName(builtinNameMap(vm, arg2));
return JSC::JSValue::encode(Bun::getIfPropertyExistsPrototypePollutionMitigationUnsafe(vm, globalObject, object, property));
}
JSC__JSInternalPromise* JSC__JSValue__asInternalPromise(JSC__JSValue JSValue0)
{
@@ -3977,18 +4008,13 @@ JSC__JSValue JSC__JSValue__getIfPropertyExistsImpl(JSC__JSValue JSValue0,
JSValue value = JSC::JSValue::decode(JSValue0);
ASSERT_WITH_MESSAGE(!value.isEmpty(), "get() must not be called on empty value");
auto& vm = JSC::getVM(globalObject);
// auto& vm = JSC::getVM(globalObject);
JSC::JSObject* object = value.getObject();
if (UNLIKELY(!object)) {
return JSValue::encode(JSValue::decode(JSC::JSValue::ValueDeleted));
}
// Since Identifier might not ref the string, we need to ensure it doesn't get deref'd until this function returns
const auto propertyString = String(StringImpl::createWithoutCopying({ arg1, arg2 }));
const auto identifier = JSC::Identifier::fromString(vm, propertyString);
const auto property = JSC::PropertyName(identifier);
return JSC::JSValue::encode(Bun::getIfPropertyExistsPrototypePollutionMitigationUnsafe(vm, globalObject, object, property));
return JSC__JSObject__getIfPropertyExistsImpl(object, globalObject, arg1, arg2);
}
extern "C" JSC__JSValue JSC__JSValue__getOwn(JSC__JSValue JSValue0, JSC__JSGlobalObject* globalObject, BunString* propertyName)
@@ -4238,7 +4264,11 @@ JSC__JSValue JSC__JSValue__jsUndefined() { return JSC::JSValue::encode(JSC::jsUn
JSC__JSObject* JSC__JSValue__toObject(JSC__JSValue JSValue0, JSC__JSGlobalObject* arg1)
{
JSC::JSValue value = JSC::JSValue::decode(JSValue0);
return value.toObject(arg1);
auto* obj = value.toObject(arg1);
#if BUN_DEBUG
ASSERT(obj);
#endif
return obj;
}
JSC__JSString* JSC__JSValue__toString(JSC__JSValue JSValue0, JSC__JSGlobalObject* arg1)
@@ -5477,10 +5507,7 @@ JSC__JSValue JSC__JSValue__fastGet(JSC__JSValue JSValue0, JSC__JSGlobalObject* g
JSC::JSObject* object = value.getObject();
ASSERT_WITH_MESSAGE(object, "fastGet() called on non-object. Check that the JSValue is an object before calling fastGet().");
auto& vm = JSC::getVM(globalObject);
const auto property = JSC::PropertyName(builtinNameMap(vm, arg2));
return JSC::JSValue::encode(Bun::getIfPropertyExistsPrototypePollutionMitigationUnsafe(vm, globalObject, object, property));
return JSC__JSObject__fastGet(object, globalObject, arg2);
}
extern "C" JSC__JSValue JSC__JSValue__fastGetOwn(JSC__JSValue JSValue0, JSC__JSGlobalObject* globalObject, unsigned char arg2)

View File

@@ -145,6 +145,8 @@ typedef void* JSClassRef;
CPP_DECL JSC__JSValue JSC__JSObject__create(JSC__JSGlobalObject* arg0, size_t arg1, void* arg2, void(* ArgFn3)(void* arg0, JSC__JSObject* arg1, JSC__JSGlobalObject* arg2));
CPP_DECL size_t JSC__JSObject__getArrayLength(JSC__JSObject* arg0);
CPP_DECL JSC__JSValue JSC__JSObject__fastGet(JSC__JSObject* object, JSC__JSGlobalObject* arg1, unsigned char arg2);
CPP_DECL JSC__JSValue JSC__JSObject__getIfPropertyExistsImpl(JSC__JSObject* object, JSC__JSGlobalObject* arg1, const unsigned char* arg2, uint32_t arg3);
CPP_DECL JSC__JSValue JSC__JSObject__getDirect(JSC__JSObject* arg0, JSC__JSGlobalObject* arg1, const ZigString* arg2);
CPP_DECL JSC__JSValue JSC__JSObject__getIndex(JSC__JSValue JSValue0, JSC__JSGlobalObject* arg1, uint32_t arg2);
CPP_DECL void JSC__JSObject__putRecord(JSC__JSObject* arg0, JSC__JSGlobalObject* arg1, ZigString* arg2, ZigString* arg3, size_t arg4);