mirror of
https://github.com/oven-sh/bun
synced 2026-02-09 10:28:47 +00:00
refactor: move JSValue.get to JSObject
This commit is contained in:
@@ -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,50 @@ 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 +162,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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
2
src/bun.js/bindings/headers.h
generated
2
src/bun.js/bindings/headers.h
generated
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user