From e1f9a620940113cdafcd2e08f2bc7c3e26f689d9 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 27 Jun 2025 03:58:14 +0200 Subject: [PATCH] WIp --- cmake/sources/CxxSources.txt | 1 - cmake/sources/ZigSources.txt | 2 + cmake/targets/BuildBun.cmake | 4 +- cmake/targets/BuildYoga.cmake | 18 + src/bun.js/bindings/BunObject.cpp | 12 + src/bun.js/bindings/JSYogaConfig.cpp | 7 +- src/bun.js/bindings/JSYogaConstants.cpp | 127 +- src/bun.js/bindings/JSYogaConstructor.cpp | 10 +- src/bun.js/bindings/JSYogaExports.cpp | 2 + src/bun.js/bindings/JSYogaModule.cpp | 135 +- src/bun.js/bindings/JSYogaNode.cpp | 10 +- src/bun.js/bindings/JSYogaNode.h | 7 +- src/bun.js/bindings/JSYogaNodeImpl.cpp | 683 --------- src/bun.js/bindings/JSYogaPrototype.cpp | 1695 ++++++++++++++++++++- src/bun.js/bindings/JSYogaPrototype.h | 6 + src/bun.js/bindings/ZigGlobalObject.cpp | 11 + src/bun.js/bindings/ZigGlobalObject.h | 3 + test/js/bun/yoga-node.test.js | 30 +- 18 files changed, 1931 insertions(+), 832 deletions(-) create mode 100644 cmake/targets/BuildYoga.cmake delete mode 100644 src/bun.js/bindings/JSYogaNodeImpl.cpp diff --git a/cmake/sources/CxxSources.txt b/cmake/sources/CxxSources.txt index 2ac5512292..dc51128c1a 100644 --- a/cmake/sources/CxxSources.txt +++ b/cmake/sources/CxxSources.txt @@ -98,7 +98,6 @@ src/bun.js/bindings/JSYogaConstructor.cpp src/bun.js/bindings/JSYogaExports.cpp src/bun.js/bindings/JSYogaModule.cpp src/bun.js/bindings/JSYogaNode.cpp -src/bun.js/bindings/JSYogaNodeImpl.cpp src/bun.js/bindings/JSYogaPrototype.cpp src/bun.js/bindings/linux_perf_tracing.cpp src/bun.js/bindings/MarkingConstraint.cpp diff --git a/cmake/sources/ZigSources.txt b/cmake/sources/ZigSources.txt index f7d1afc43e..207020ed27 100644 --- a/cmake/sources/ZigSources.txt +++ b/cmake/sources/ZigSources.txt @@ -121,6 +121,8 @@ src/bun.js/bindings/Exception.zig src/bun.js/bindings/FetchHeaders.zig src/bun.js/bindings/FFI.zig src/bun.js/bindings/generated_classes_list.zig +src/bun.js/bindings/GeneratedBindings.zig +src/bun.js/bindings/GeneratedJS2Native.zig src/bun.js/bindings/GetterSetter.zig src/bun.js/bindings/HTTPServerAgent.zig src/bun.js/bindings/JSArray.zig diff --git a/cmake/targets/BuildBun.cmake b/cmake/targets/BuildBun.cmake index 2c7d1574d0..ffc382b91b 100644 --- a/cmake/targets/BuildBun.cmake +++ b/cmake/targets/BuildBun.cmake @@ -54,6 +54,7 @@ set(BUN_DEPENDENCIES Lshpack Mimalloc TinyCC + Yoga Zlib LibArchive # must be loaded after zlib HdrHistogram # must be loaded after zlib @@ -61,9 +62,6 @@ set(BUN_DEPENDENCIES ) include(CloneZstd) -# foreach(dependency ${BUN_DEPENDENCIES}) -# include(Clone${dependency}) -# endforeach() # --- Codegen --- diff --git a/cmake/targets/BuildYoga.cmake b/cmake/targets/BuildYoga.cmake new file mode 100644 index 0000000000..14366e3fb1 --- /dev/null +++ b/cmake/targets/BuildYoga.cmake @@ -0,0 +1,18 @@ +# Since we already have Yoga cloned, just build it directly +set(YOGA_PATH ${VENDOR_PATH}/yoga) + +if(NOT EXISTS ${YOGA_PATH}) + message(FATAL_ERROR "Yoga not found at ${YOGA_PATH}. Please clone it manually.") +endif() + +# Build Yoga as a subdirectory +add_subdirectory(${YOGA_PATH} ${BUILD_PATH}/yoga EXCLUDE_FROM_ALL) + +# Set the include directories +if(TARGET ${bun}) + target_include_directories(${bun} PRIVATE ${YOGA_PATH}) + target_link_libraries(${bun} PRIVATE yogacore) +endif() + +# Create a custom target for consistency with other dependencies +add_custom_target(yoga DEPENDS yogacore) \ No newline at end of file diff --git a/src/bun.js/bindings/BunObject.cpp b/src/bun.js/bindings/BunObject.cpp index 25e207d27a..04cf5cbb00 100644 --- a/src/bun.js/bindings/BunObject.cpp +++ b/src/bun.js/bindings/BunObject.cpp @@ -72,6 +72,8 @@ BUN_DECLARE_HOST_FUNCTION(Bun__fetch); BUN_DECLARE_HOST_FUNCTION(Bun__fetchPreconnect); BUN_DECLARE_HOST_FUNCTION(Bun__randomUUIDv7); +extern "C" JSC::EncodedJSValue Bun__createYogaModule(Zig::GlobalObject*); + using namespace JSC; using namespace WebCore; @@ -756,6 +758,7 @@ JSC_DEFINE_HOST_FUNCTION(functionFileURLToPath, (JSC::JSGlobalObject * globalObj password constructPasswordObject DontDelete|PropertyCallback pathToFileURL functionPathToFileURL DontDelete|Function 1 peek constructBunPeekObject DontDelete|PropertyCallback + Yoga constructYogaObject DontDelete|PropertyCallback plugin constructPluginObject ReadOnly|DontDelete|PropertyCallback randomUUIDv7 Bun__randomUUIDv7 DontDelete|Function 2 readableStreamToArray JSBuiltin Builtin|Function 1 @@ -837,6 +840,8 @@ public: } }; +static JSValue constructYogaObject(VM& vm, JSObject* bunObject); + #define bunObjectReadableStreamToArrayCodeGenerator WebCore::readableStreamReadableStreamToArrayCodeGenerator #define bunObjectReadableStreamToArrayBufferCodeGenerator WebCore::readableStreamReadableStreamToArrayBufferCodeGenerator #define bunObjectReadableStreamToBytesCodeGenerator WebCore::readableStreamReadableStreamToBytesCodeGenerator @@ -869,6 +874,13 @@ static JSValue constructCookieMapObject(VM& vm, JSObject* bunObject) return WebCore::JSCookieMap::getConstructor(vm, zigGlobalObject); } +static JSValue constructYogaObject(VM& vm, JSObject* bunObject) +{ + auto* globalObject = jsCast(bunObject->globalObject()); + auto result = Bun__createYogaModule(globalObject); + return JSValue::decode(result); +} + JSC::JSObject* createBunObject(VM& vm, JSObject* globalObject) { return JSBunObject::create(vm, jsCast(globalObject)); diff --git a/src/bun.js/bindings/JSYogaConfig.cpp b/src/bun.js/bindings/JSYogaConfig.cpp index bcc7cf14ec..388542099a 100644 --- a/src/bun.js/bindings/JSYogaConfig.cpp +++ b/src/bun.js/bindings/JSYogaConfig.cpp @@ -2,11 +2,14 @@ #include "JSYogaConfig.h" #include "webcore/DOMIsoSubspaces.h" #include "webcore/DOMClientIsoSubspaces.h" +#include "webcore/WebCoreJSClientData.h" #include namespace Bun { -const JSC::ClassInfo JSYogaConfig::s_info = { "Yoga.Config"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSYogaConfig) }; +using namespace JSC; + +const JSC::ClassInfo JSYogaConfig::s_info = { "Config"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSYogaConfig) }; JSYogaConfig::JSYogaConfig(JSC::VM& vm, JSC::Structure* structure) : Base(vm, structure) @@ -51,7 +54,7 @@ JSC::GCClient::IsoSubspace* JSYogaConfig::subspaceFor(JSC::VM& vm) { if constexpr (mode == JSC::SubspaceAccess::Concurrently) return nullptr; - return WebCore::subspaceForImpl( + return WebCore::subspaceForImpl( vm, [](auto& spaces) { return spaces.m_clientSubspaceForJSYogaConfig.get(); }, [](auto& spaces, auto&& space) { spaces.m_clientSubspaceForJSYogaConfig = std::forward(space); }, diff --git a/src/bun.js/bindings/JSYogaConstants.cpp b/src/bun.js/bindings/JSYogaConstants.cpp index b1a866fbde..49baa22e5b 100644 --- a/src/bun.js/bindings/JSYogaConstants.cpp +++ b/src/bun.js/bindings/JSYogaConstants.cpp @@ -5,6 +5,8 @@ namespace Bun { +using namespace JSC; + const JSC::ClassInfo JSYogaConstants::s_info = { "YogaConstants"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSYogaConstants) }; void JSYogaConstants::finishCreation(JSC::VM& vm) @@ -12,97 +14,96 @@ void JSYogaConstants::finishCreation(JSC::VM& vm) Base::finishCreation(vm); // Align values - putDirect(vm, JSC::Identifier::fromString(vm, "ALIGN_AUTO"_s), JSC::jsNumber(YGAlignAuto), 0); - putDirect(vm, JSC::Identifier::fromString(vm, "ALIGN_FLEX_START"_s), JSC::jsNumber(YGAlignFlexStart), 0); - putDirect(vm, JSC::Identifier::fromString(vm, "ALIGN_CENTER"_s), JSC::jsNumber(YGAlignCenter), 0); - putDirect(vm, JSC::Identifier::fromString(vm, "ALIGN_FLEX_END"_s), JSC::jsNumber(YGAlignFlexEnd), 0); - putDirect(vm, JSC::Identifier::fromString(vm, "ALIGN_STRETCH"_s), JSC::jsNumber(YGAlignStretch), 0); - putDirect(vm, JSC::Identifier::fromString(vm, "ALIGN_BASELINE"_s), JSC::jsNumber(YGAlignBaseline), 0); - putDirect(vm, JSC::Identifier::fromString(vm, "ALIGN_SPACE_BETWEEN"_s), JSC::jsNumber(YGAlignSpaceBetween), 0); - putDirect(vm, JSC::Identifier::fromString(vm, "ALIGN_SPACE_AROUND"_s), JSC::jsNumber(YGAlignSpaceAround), 0); - putDirect(vm, JSC::Identifier::fromString(vm, "ALIGN_SPACE_EVENLY"_s), JSC::jsNumber(YGAlignSpaceEvenly), 0); + putDirectWithoutTransition(vm, JSC::Identifier::fromString(vm, "ALIGN_AUTO"_s), JSC::jsNumber(static_cast(YGAlignAuto)), 0); + putDirectWithoutTransition(vm, JSC::Identifier::fromString(vm, "ALIGN_FLEX_START"_s), JSC::jsNumber(static_cast(YGAlignFlexStart)), 0); + putDirectWithoutTransition(vm, JSC::Identifier::fromString(vm, "ALIGN_CENTER"_s), JSC::jsNumber(static_cast(YGAlignCenter)), 0); + putDirectWithoutTransition(vm, JSC::Identifier::fromString(vm, "ALIGN_FLEX_END"_s), JSC::jsNumber(static_cast(YGAlignFlexEnd)), 0); + putDirectWithoutTransition(vm, JSC::Identifier::fromString(vm, "ALIGN_STRETCH"_s), JSC::jsNumber(static_cast(YGAlignStretch)), 0); + putDirectWithoutTransition(vm, JSC::Identifier::fromString(vm, "ALIGN_BASELINE"_s), JSC::jsNumber(static_cast(YGAlignBaseline)), 0); + putDirectWithoutTransition(vm, JSC::Identifier::fromString(vm, "ALIGN_SPACE_BETWEEN"_s), JSC::jsNumber(static_cast(YGAlignSpaceBetween)), 0); + putDirectWithoutTransition(vm, JSC::Identifier::fromString(vm, "ALIGN_SPACE_AROUND"_s), JSC::jsNumber(static_cast(YGAlignSpaceAround)), 0); + putDirectWithoutTransition(vm, JSC::Identifier::fromString(vm, "ALIGN_SPACE_EVENLY"_s), JSC::jsNumber(static_cast(YGAlignSpaceEvenly)), 0); // Direction values - putDirect(vm, JSC::Identifier::fromString(vm, "DIRECTION_INHERIT"_s), JSC::jsNumber(YGDirectionInherit), 0); - putDirect(vm, JSC::Identifier::fromString(vm, "DIRECTION_LTR"_s), JSC::jsNumber(YGDirectionLTR), 0); - putDirect(vm, JSC::Identifier::fromString(vm, "DIRECTION_RTL"_s), JSC::jsNumber(YGDirectionRTL), 0); + putDirectWithoutTransition(vm, JSC::Identifier::fromString(vm, "DIRECTION_INHERIT"_s), JSC::jsNumber(static_cast(YGDirectionInherit)), 0); + putDirectWithoutTransition(vm, JSC::Identifier::fromString(vm, "DIRECTION_LTR"_s), JSC::jsNumber(static_cast(YGDirectionLTR)), 0); + putDirectWithoutTransition(vm, JSC::Identifier::fromString(vm, "DIRECTION_RTL"_s), JSC::jsNumber(static_cast(YGDirectionRTL)), 0); // Display values - putDirect(vm, JSC::Identifier::fromString(vm, "DISPLAY_FLEX"_s), JSC::jsNumber(YGDisplayFlex), 0); - putDirect(vm, JSC::Identifier::fromString(vm, "DISPLAY_NONE"_s), JSC::jsNumber(YGDisplayNone), 0); + putDirectWithoutTransition(vm, JSC::Identifier::fromString(vm, "DISPLAY_FLEX"_s), JSC::jsNumber(static_cast(YGDisplayFlex)), 0); + putDirectWithoutTransition(vm, JSC::Identifier::fromString(vm, "DISPLAY_NONE"_s), JSC::jsNumber(static_cast(YGDisplayNone)), 0); // Edge values - putDirect(vm, JSC::Identifier::fromString(vm, "EDGE_LEFT"_s), JSC::jsNumber(YGEdgeLeft), 0); - putDirect(vm, JSC::Identifier::fromString(vm, "EDGE_TOP"_s), JSC::jsNumber(YGEdgeTop), 0); - putDirect(vm, JSC::Identifier::fromString(vm, "EDGE_RIGHT"_s), JSC::jsNumber(YGEdgeRight), 0); - putDirect(vm, JSC::Identifier::fromString(vm, "EDGE_BOTTOM"_s), JSC::jsNumber(YGEdgeBottom), 0); - putDirect(vm, JSC::Identifier::fromString(vm, "EDGE_START"_s), JSC::jsNumber(YGEdgeStart), 0); - putDirect(vm, JSC::Identifier::fromString(vm, "EDGE_END"_s), JSC::jsNumber(YGEdgeEnd), 0); - putDirect(vm, JSC::Identifier::fromString(vm, "EDGE_HORIZONTAL"_s), JSC::jsNumber(YGEdgeHorizontal), 0); - putDirect(vm, JSC::Identifier::fromString(vm, "EDGE_VERTICAL"_s), JSC::jsNumber(YGEdgeVertical), 0); - putDirect(vm, JSC::Identifier::fromString(vm, "EDGE_ALL"_s), JSC::jsNumber(YGEdgeAll), 0); + putDirectWithoutTransition(vm, JSC::Identifier::fromString(vm, "EDGE_LEFT"_s), JSC::jsNumber(static_cast(YGEdgeLeft)), 0); + putDirectWithoutTransition(vm, JSC::Identifier::fromString(vm, "EDGE_TOP"_s), JSC::jsNumber(static_cast(YGEdgeTop)), 0); + putDirectWithoutTransition(vm, JSC::Identifier::fromString(vm, "EDGE_RIGHT"_s), JSC::jsNumber(static_cast(YGEdgeRight)), 0); + putDirectWithoutTransition(vm, JSC::Identifier::fromString(vm, "EDGE_BOTTOM"_s), JSC::jsNumber(static_cast(YGEdgeBottom)), 0); + putDirectWithoutTransition(vm, JSC::Identifier::fromString(vm, "EDGE_START"_s), JSC::jsNumber(static_cast(YGEdgeStart)), 0); + putDirectWithoutTransition(vm, JSC::Identifier::fromString(vm, "EDGE_END"_s), JSC::jsNumber(static_cast(YGEdgeEnd)), 0); + putDirectWithoutTransition(vm, JSC::Identifier::fromString(vm, "EDGE_HORIZONTAL"_s), JSC::jsNumber(static_cast(YGEdgeHorizontal)), 0); + putDirectWithoutTransition(vm, JSC::Identifier::fromString(vm, "EDGE_VERTICAL"_s), JSC::jsNumber(static_cast(YGEdgeVertical)), 0); + putDirectWithoutTransition(vm, JSC::Identifier::fromString(vm, "EDGE_ALL"_s), JSC::jsNumber(static_cast(YGEdgeAll)), 0); // Experimental feature values - putDirect(vm, JSC::Identifier::fromString(vm, "EXPERIMENTAL_FEATURE_WEB_FLEX_BASIS"_s), JSC::jsNumber(YGExperimentalFeatureWebFlexBasis), 0); - putDirect(vm, JSC::Identifier::fromString(vm, "EXPERIMENTAL_FEATURE_ABSOLUTE_PERCENTAGE_AGAINST_PADDING_EDGE"_s), JSC::jsNumber(YGExperimentalFeatureAbsolutePercentageAgainstPaddingEdge), 0); - putDirect(vm, JSC::Identifier::fromString(vm, "EXPERIMENTAL_FEATURE_FIX_ABSOLUTE_TRAILING_COLUMN_MARGIN"_s), JSC::jsNumber(YGExperimentalFeatureFixAbsoluteTrailingColumnMargin), 0); + putDirectWithoutTransition(vm, JSC::Identifier::fromString(vm, "EXPERIMENTAL_FEATURE_WEB_FLEX_BASIS"_s), JSC::jsNumber(static_cast(YGExperimentalFeatureWebFlexBasis)), 0); // Flex direction values - putDirect(vm, JSC::Identifier::fromString(vm, "FLEX_DIRECTION_COLUMN"_s), JSC::jsNumber(YGFlexDirectionColumn), 0); - putDirect(vm, JSC::Identifier::fromString(vm, "FLEX_DIRECTION_COLUMN_REVERSE"_s), JSC::jsNumber(YGFlexDirectionColumnReverse), 0); - putDirect(vm, JSC::Identifier::fromString(vm, "FLEX_DIRECTION_ROW"_s), JSC::jsNumber(YGFlexDirectionRow), 0); - putDirect(vm, JSC::Identifier::fromString(vm, "FLEX_DIRECTION_ROW_REVERSE"_s), JSC::jsNumber(YGFlexDirectionRowReverse), 0); + putDirectWithoutTransition(vm, JSC::Identifier::fromString(vm, "FLEX_DIRECTION_COLUMN"_s), JSC::jsNumber(static_cast(YGFlexDirectionColumn)), 0); + putDirectWithoutTransition(vm, JSC::Identifier::fromString(vm, "FLEX_DIRECTION_COLUMN_REVERSE"_s), JSC::jsNumber(static_cast(YGFlexDirectionColumnReverse)), 0); + putDirectWithoutTransition(vm, JSC::Identifier::fromString(vm, "FLEX_DIRECTION_ROW"_s), JSC::jsNumber(static_cast(YGFlexDirectionRow)), 0); + putDirectWithoutTransition(vm, JSC::Identifier::fromString(vm, "FLEX_DIRECTION_ROW_REVERSE"_s), JSC::jsNumber(static_cast(YGFlexDirectionRowReverse)), 0); // Gutter values - putDirect(vm, JSC::Identifier::fromString(vm, "GUTTER_COLUMN"_s), JSC::jsNumber(YGGutterColumn), 0); - putDirect(vm, JSC::Identifier::fromString(vm, "GUTTER_ROW"_s), JSC::jsNumber(YGGutterRow), 0); - putDirect(vm, JSC::Identifier::fromString(vm, "GUTTER_ALL"_s), JSC::jsNumber(YGGutterAll), 0); + putDirectWithoutTransition(vm, JSC::Identifier::fromString(vm, "GUTTER_COLUMN"_s), JSC::jsNumber(static_cast(YGGutterColumn)), 0); + putDirectWithoutTransition(vm, JSC::Identifier::fromString(vm, "GUTTER_ROW"_s), JSC::jsNumber(static_cast(YGGutterRow)), 0); + putDirectWithoutTransition(vm, JSC::Identifier::fromString(vm, "GUTTER_ALL"_s), JSC::jsNumber(static_cast(YGGutterAll)), 0); // Justify values - putDirect(vm, JSC::Identifier::fromString(vm, "JUSTIFY_FLEX_START"_s), JSC::jsNumber(YGJustifyFlexStart), 0); - putDirect(vm, JSC::Identifier::fromString(vm, "JUSTIFY_CENTER"_s), JSC::jsNumber(YGJustifyCenter), 0); - putDirect(vm, JSC::Identifier::fromString(vm, "JUSTIFY_FLEX_END"_s), JSC::jsNumber(YGJustifyFlexEnd), 0); - putDirect(vm, JSC::Identifier::fromString(vm, "JUSTIFY_SPACE_BETWEEN"_s), JSC::jsNumber(YGJustifySpaceBetween), 0); - putDirect(vm, JSC::Identifier::fromString(vm, "JUSTIFY_SPACE_AROUND"_s), JSC::jsNumber(YGJustifySpaceAround), 0); - putDirect(vm, JSC::Identifier::fromString(vm, "JUSTIFY_SPACE_EVENLY"_s), JSC::jsNumber(YGJustifySpaceEvenly), 0); + putDirectWithoutTransition(vm, JSC::Identifier::fromString(vm, "JUSTIFY_FLEX_START"_s), JSC::jsNumber(static_cast(YGJustifyFlexStart)), 0); + putDirectWithoutTransition(vm, JSC::Identifier::fromString(vm, "JUSTIFY_CENTER"_s), JSC::jsNumber(static_cast(YGJustifyCenter)), 0); + putDirectWithoutTransition(vm, JSC::Identifier::fromString(vm, "JUSTIFY_FLEX_END"_s), JSC::jsNumber(static_cast(YGJustifyFlexEnd)), 0); + putDirectWithoutTransition(vm, JSC::Identifier::fromString(vm, "JUSTIFY_SPACE_BETWEEN"_s), JSC::jsNumber(static_cast(YGJustifySpaceBetween)), 0); + putDirectWithoutTransition(vm, JSC::Identifier::fromString(vm, "JUSTIFY_SPACE_AROUND"_s), JSC::jsNumber(static_cast(YGJustifySpaceAround)), 0); + putDirectWithoutTransition(vm, JSC::Identifier::fromString(vm, "JUSTIFY_SPACE_EVENLY"_s), JSC::jsNumber(static_cast(YGJustifySpaceEvenly)), 0); // Measure mode values - putDirect(vm, JSC::Identifier::fromString(vm, "MEASURE_MODE_UNDEFINED"_s), JSC::jsNumber(YGMeasureModeUndefined), 0); - putDirect(vm, JSC::Identifier::fromString(vm, "MEASURE_MODE_EXACTLY"_s), JSC::jsNumber(YGMeasureModeExactly), 0); - putDirect(vm, JSC::Identifier::fromString(vm, "MEASURE_MODE_AT_MOST"_s), JSC::jsNumber(YGMeasureModeAtMost), 0); + putDirectWithoutTransition(vm, JSC::Identifier::fromString(vm, "MEASURE_MODE_UNDEFINED"_s), JSC::jsNumber(static_cast(YGMeasureModeUndefined)), 0); + putDirectWithoutTransition(vm, JSC::Identifier::fromString(vm, "MEASURE_MODE_EXACTLY"_s), JSC::jsNumber(static_cast(YGMeasureModeExactly)), 0); + putDirectWithoutTransition(vm, JSC::Identifier::fromString(vm, "MEASURE_MODE_AT_MOST"_s), JSC::jsNumber(static_cast(YGMeasureModeAtMost)), 0); // Node type values - putDirect(vm, JSC::Identifier::fromString(vm, "NODE_TYPE_DEFAULT"_s), JSC::jsNumber(YGNodeTypeDefault), 0); - putDirect(vm, JSC::Identifier::fromString(vm, "NODE_TYPE_TEXT"_s), JSC::jsNumber(YGNodeTypeText), 0); + putDirectWithoutTransition(vm, JSC::Identifier::fromString(vm, "NODE_TYPE_DEFAULT"_s), JSC::jsNumber(static_cast(YGNodeTypeDefault)), 0); + putDirectWithoutTransition(vm, JSC::Identifier::fromString(vm, "NODE_TYPE_TEXT"_s), JSC::jsNumber(static_cast(YGNodeTypeText)), 0); // Overflow values - putDirect(vm, JSC::Identifier::fromString(vm, "OVERFLOW_VISIBLE"_s), JSC::jsNumber(YGOverflowVisible), 0); - putDirect(vm, JSC::Identifier::fromString(vm, "OVERFLOW_HIDDEN"_s), JSC::jsNumber(YGOverflowHidden), 0); - putDirect(vm, JSC::Identifier::fromString(vm, "OVERFLOW_SCROLL"_s), JSC::jsNumber(YGOverflowScroll), 0); + putDirectWithoutTransition(vm, JSC::Identifier::fromString(vm, "OVERFLOW_VISIBLE"_s), JSC::jsNumber(static_cast(YGOverflowVisible)), 0); + putDirectWithoutTransition(vm, JSC::Identifier::fromString(vm, "OVERFLOW_HIDDEN"_s), JSC::jsNumber(static_cast(YGOverflowHidden)), 0); + putDirectWithoutTransition(vm, JSC::Identifier::fromString(vm, "OVERFLOW_SCROLL"_s), JSC::jsNumber(static_cast(YGOverflowScroll)), 0); // Position type values - putDirect(vm, JSC::Identifier::fromString(vm, "POSITION_TYPE_STATIC"_s), JSC::jsNumber(YGPositionTypeStatic), 0); - putDirect(vm, JSC::Identifier::fromString(vm, "POSITION_TYPE_RELATIVE"_s), JSC::jsNumber(YGPositionTypeRelative), 0); - putDirect(vm, JSC::Identifier::fromString(vm, "POSITION_TYPE_ABSOLUTE"_s), JSC::jsNumber(YGPositionTypeAbsolute), 0); + putDirectWithoutTransition(vm, JSC::Identifier::fromString(vm, "POSITION_TYPE_STATIC"_s), JSC::jsNumber(static_cast(YGPositionTypeStatic)), 0); + putDirectWithoutTransition(vm, JSC::Identifier::fromString(vm, "POSITION_TYPE_RELATIVE"_s), JSC::jsNumber(static_cast(YGPositionTypeRelative)), 0); + putDirectWithoutTransition(vm, JSC::Identifier::fromString(vm, "POSITION_TYPE_ABSOLUTE"_s), JSC::jsNumber(static_cast(YGPositionTypeAbsolute)), 0); // Unit values - putDirect(vm, JSC::Identifier::fromString(vm, "UNIT_UNDEFINED"_s), JSC::jsNumber(YGUnitUndefined), 0); - putDirect(vm, JSC::Identifier::fromString(vm, "UNIT_POINT"_s), JSC::jsNumber(YGUnitPoint), 0); - putDirect(vm, JSC::Identifier::fromString(vm, "UNIT_PERCENT"_s), JSC::jsNumber(YGUnitPercent), 0); - putDirect(vm, JSC::Identifier::fromString(vm, "UNIT_AUTO"_s), JSC::jsNumber(YGUnitAuto), 0); + putDirectWithoutTransition(vm, JSC::Identifier::fromString(vm, "UNIT_UNDEFINED"_s), JSC::jsNumber(static_cast(YGUnitUndefined)), 0); + putDirectWithoutTransition(vm, JSC::Identifier::fromString(vm, "UNIT_POINT"_s), JSC::jsNumber(static_cast(YGUnitPoint)), 0); + putDirectWithoutTransition(vm, JSC::Identifier::fromString(vm, "UNIT_PERCENT"_s), JSC::jsNumber(static_cast(YGUnitPercent)), 0); + putDirectWithoutTransition(vm, JSC::Identifier::fromString(vm, "UNIT_AUTO"_s), JSC::jsNumber(static_cast(YGUnitAuto)), 0); // Wrap values - putDirect(vm, JSC::Identifier::fromString(vm, "WRAP_NO_WRAP"_s), JSC::jsNumber(YGWrapNoWrap), 0); - putDirect(vm, JSC::Identifier::fromString(vm, "WRAP_WRAP"_s), JSC::jsNumber(YGWrapWrap), 0); - putDirect(vm, JSC::Identifier::fromString(vm, "WRAP_WRAP_REVERSE"_s), JSC::jsNumber(YGWrapWrapReverse), 0); + putDirectWithoutTransition(vm, JSC::Identifier::fromString(vm, "WRAP_NO_WRAP"_s), JSC::jsNumber(static_cast(YGWrapNoWrap)), 0); + putDirectWithoutTransition(vm, JSC::Identifier::fromString(vm, "WRAP_WRAP"_s), JSC::jsNumber(static_cast(YGWrapWrap)), 0); + putDirectWithoutTransition(vm, JSC::Identifier::fromString(vm, "WRAP_WRAP_REVERSE"_s), JSC::jsNumber(static_cast(YGWrapWrapReverse)), 0); // Errata values - putDirect(vm, JSC::Identifier::fromString(vm, "ERRATA_NONE"_s), JSC::jsNumber(YGErrataNone), 0); - putDirect(vm, JSC::Identifier::fromString(vm, "ERRATA_STRETCH_FLEX_BASIS"_s), JSC::jsNumber(YGErrataStretchFlexBasis), 0); - putDirect(vm, JSC::Identifier::fromString(vm, "ERRATA_ABSOLUTE_POSITIONING_INCORRECT"_s), JSC::jsNumber(YGErrataAbsolutePositioningIncorrect), 0); - putDirect(vm, JSC::Identifier::fromString(vm, "ERRATA_ABSOLUTE_PERCENT_AGAINST_INNER_SIZE"_s), JSC::jsNumber(YGErrataAbsolutePercentAgainstInnerSize), 0); - putDirect(vm, JSC::Identifier::fromString(vm, "ERRATA_ALL"_s), JSC::jsNumber(YGErrataAll), 0); - putDirect(vm, JSC::Identifier::fromString(vm, "ERRATA_CLASSIC"_s), JSC::jsNumber(YGErrataClassic), 0); + putDirectWithoutTransition(vm, JSC::Identifier::fromString(vm, "ERRATA_NONE"_s), JSC::jsNumber(static_cast(YGErrataNone)), 0); + putDirectWithoutTransition(vm, JSC::Identifier::fromString(vm, "ERRATA_STRETCH_FLEX_BASIS"_s), JSC::jsNumber(static_cast(YGErrataStretchFlexBasis)), 0); + // YGErrataAbsolutePositioningIncorrect is not available in this version of Yoga + // putDirectWithoutTransition(vm, JSC::Identifier::fromString(vm, "ERRATA_ABSOLUTE_POSITIONING_INCORRECT"_s), JSC::jsNumber(static_cast(YGErrataAbsolutePositioningIncorrect)), 0); + putDirectWithoutTransition(vm, JSC::Identifier::fromString(vm, "ERRATA_ABSOLUTE_PERCENT_AGAINST_INNER_SIZE"_s), JSC::jsNumber(static_cast(YGErrataAbsolutePercentAgainstInnerSize)), 0); + putDirectWithoutTransition(vm, JSC::Identifier::fromString(vm, "ERRATA_ALL"_s), JSC::jsNumber(static_cast(YGErrataAll)), 0); + putDirectWithoutTransition(vm, JSC::Identifier::fromString(vm, "ERRATA_CLASSIC"_s), JSC::jsNumber(static_cast(YGErrataClassic)), 0); } } // namespace Bun diff --git a/src/bun.js/bindings/JSYogaConstructor.cpp b/src/bun.js/bindings/JSYogaConstructor.cpp index 78a467665d..5bd9100e06 100644 --- a/src/bun.js/bindings/JSYogaConstructor.cpp +++ b/src/bun.js/bindings/JSYogaConstructor.cpp @@ -8,6 +8,10 @@ #include #include +#ifndef UNLIKELY +#define UNLIKELY(x) __builtin_expect(!!(x), 0) +#endif + namespace Bun { // Forward declarations for constructor functions @@ -26,7 +30,7 @@ JSYogaConfigConstructor::JSYogaConfigConstructor(JSC::VM& vm, JSC::Structure* st void JSYogaConfigConstructor::finishCreation(JSC::VM& vm, JSC::JSObject* prototype) { - Base::finishCreation(vm, 0, "Config"_s); + Base::finishCreation(vm, 0, "Config"_s, PropertyAdditionMode::WithStructureTransition); putDirectWithoutTransition(vm, vm.propertyNames->prototype, prototype, JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::ReadOnly); // Add static methods - create() is an alias for the constructor @@ -43,7 +47,7 @@ JSYogaNodeConstructor::JSYogaNodeConstructor(JSC::VM& vm, JSC::Structure* struct void JSYogaNodeConstructor::finishCreation(JSC::VM& vm, JSC::JSObject* prototype) { - Base::finishCreation(vm, 1, "Node"_s); // 1 for optional config parameter + Base::finishCreation(vm, 1, "Node"_s, PropertyAdditionMode::WithStructureTransition); // 1 for optional config parameter putDirectWithoutTransition(vm, vm.propertyNames->prototype, prototype, JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::ReadOnly); // Add static methods - create() is an alias for the constructor @@ -143,6 +147,7 @@ void setupJSYogaConfigClassStructure(JSC::LazyClassStructure::Initializer& init) auto* constructor = JSYogaConfigConstructor::create(init.vm, constructorStructure, prototype); auto* structure = JSYogaConfig::createStructure(init.vm, init.global, prototype); + init.setPrototype(prototype); init.setStructure(structure); init.setConstructor(constructor); @@ -157,6 +162,7 @@ void setupJSYogaNodeClassStructure(JSC::LazyClassStructure::Initializer& init) auto* constructor = JSYogaNodeConstructor::create(init.vm, constructorStructure, prototype); auto* structure = JSYogaNode::createStructure(init.vm, init.global, prototype); + init.setPrototype(prototype); init.setStructure(structure); init.setConstructor(constructor); diff --git a/src/bun.js/bindings/JSYogaExports.cpp b/src/bun.js/bindings/JSYogaExports.cpp index a23ee67106..5f82a19255 100644 --- a/src/bun.js/bindings/JSYogaExports.cpp +++ b/src/bun.js/bindings/JSYogaExports.cpp @@ -2,6 +2,8 @@ #include "JSYogaConstructor.h" #include "ZigGlobalObject.h" +using namespace JSC; + extern "C" { JSC::EncodedJSValue Bun__JSYogaConfigConstructor(Zig::GlobalObject* globalObject) diff --git a/src/bun.js/bindings/JSYogaModule.cpp b/src/bun.js/bindings/JSYogaModule.cpp index c5bd91cda6..e2c9384316 100644 --- a/src/bun.js/bindings/JSYogaModule.cpp +++ b/src/bun.js/bindings/JSYogaModule.cpp @@ -1,10 +1,11 @@ #include "root.h" #include "JSYogaModule.h" -#include "JSYogaConstants.h" #include "JSYogaConstructor.h" #include "JSYogaPrototype.h" +#include #include "ZigGlobalObject.h" #include +#include namespace Bun { @@ -28,6 +29,9 @@ void JSYogaModule::finishCreation(JSC::VM& vm, JSC::JSGlobalObject* globalObject auto* configConstructor = JSYogaConfigConstructor::create(vm, JSYogaConfigConstructor::createStructure(vm, globalObject, globalObject->functionPrototype()), configPrototype); + + // Set constructor property on prototype + configPrototype->setConstructor(vm, configConstructor); // Create Node constructor and prototype auto* nodePrototype = JSYogaNodePrototype::create(vm, globalObject, @@ -36,24 +40,125 @@ void JSYogaModule::finishCreation(JSC::VM& vm, JSC::JSGlobalObject* globalObject auto* nodeConstructor = JSYogaNodeConstructor::create(vm, JSYogaNodeConstructor::createStructure(vm, globalObject, globalObject->functionPrototype()), nodePrototype); + + // Set constructor property on prototype + nodePrototype->setConstructor(vm, nodeConstructor); // Add constructors to module - putDirect(vm, vm.propertyNames->Config, configConstructor, JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::ReadOnly); + putDirect(vm, JSC::Identifier::fromString(vm, "Config"_s), configConstructor, JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::ReadOnly); putDirect(vm, JSC::Identifier::fromString(vm, "Node"_s), nodeConstructor, JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::ReadOnly); - // Add all constants directly to the Yoga object - auto* constants = JSYogaConstants::create(vm, JSYogaConstants::createStructure(vm, globalObject, JSC::jsNull())); - - // Copy all properties from constants object to this module - JSC::PropertyNameArray properties(vm, JSC::PropertyNameMode::StringsAndSymbols, JSC::PrivateSymbolMode::Exclude); - constants->getPropertyNames(globalObject, properties, JSC::DontEnumPropertiesMode::Exclude); - - for (const auto& propertyName : properties) { - JSC::PropertySlot slot(constants, JSC::PropertySlot::InternalMethodType::Get); - if (constants->getPropertySlot(globalObject, propertyName, slot)) { - putDirect(vm, propertyName, slot.getValue(globalObject, propertyName), JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::ReadOnly); - } - } + // Add constants + // Align values + putDirect(vm, JSC::Identifier::fromString(vm, "ALIGN_AUTO"_s), JSC::jsNumber(static_cast(YGAlignAuto)), 0); + putDirect(vm, JSC::Identifier::fromString(vm, "ALIGN_FLEX_START"_s), JSC::jsNumber(static_cast(YGAlignFlexStart)), 0); + putDirect(vm, JSC::Identifier::fromString(vm, "ALIGN_CENTER"_s), JSC::jsNumber(static_cast(YGAlignCenter)), 0); + putDirect(vm, JSC::Identifier::fromString(vm, "ALIGN_FLEX_END"_s), JSC::jsNumber(static_cast(YGAlignFlexEnd)), 0); + putDirect(vm, JSC::Identifier::fromString(vm, "ALIGN_STRETCH"_s), JSC::jsNumber(static_cast(YGAlignStretch)), 0); + putDirect(vm, JSC::Identifier::fromString(vm, "ALIGN_BASELINE"_s), JSC::jsNumber(static_cast(YGAlignBaseline)), 0); + putDirect(vm, JSC::Identifier::fromString(vm, "ALIGN_SPACE_BETWEEN"_s), JSC::jsNumber(static_cast(YGAlignSpaceBetween)), 0); + putDirect(vm, JSC::Identifier::fromString(vm, "ALIGN_SPACE_AROUND"_s), JSC::jsNumber(static_cast(YGAlignSpaceAround)), 0); + putDirect(vm, JSC::Identifier::fromString(vm, "ALIGN_SPACE_EVENLY"_s), JSC::jsNumber(static_cast(YGAlignSpaceEvenly)), 0); + + // Box sizing values + putDirect(vm, JSC::Identifier::fromString(vm, "BOX_SIZING_BORDER_BOX"_s), JSC::jsNumber(static_cast(YGBoxSizingBorderBox)), 0); + putDirect(vm, JSC::Identifier::fromString(vm, "BOX_SIZING_CONTENT_BOX"_s), JSC::jsNumber(static_cast(YGBoxSizingContentBox)), 0); + + // Dimension values + putDirect(vm, JSC::Identifier::fromString(vm, "DIMENSION_WIDTH"_s), JSC::jsNumber(static_cast(YGDimensionWidth)), 0); + putDirect(vm, JSC::Identifier::fromString(vm, "DIMENSION_HEIGHT"_s), JSC::jsNumber(static_cast(YGDimensionHeight)), 0); + + // Direction values + putDirect(vm, JSC::Identifier::fromString(vm, "DIRECTION_INHERIT"_s), JSC::jsNumber(static_cast(YGDirectionInherit)), 0); + putDirect(vm, JSC::Identifier::fromString(vm, "DIRECTION_LTR"_s), JSC::jsNumber(static_cast(YGDirectionLTR)), 0); + putDirect(vm, JSC::Identifier::fromString(vm, "DIRECTION_RTL"_s), JSC::jsNumber(static_cast(YGDirectionRTL)), 0); + + // Display values + putDirect(vm, JSC::Identifier::fromString(vm, "DISPLAY_FLEX"_s), JSC::jsNumber(static_cast(YGDisplayFlex)), 0); + putDirect(vm, JSC::Identifier::fromString(vm, "DISPLAY_NONE"_s), JSC::jsNumber(static_cast(YGDisplayNone)), 0); + putDirect(vm, JSC::Identifier::fromString(vm, "DISPLAY_CONTENTS"_s), JSC::jsNumber(static_cast(YGDisplayContents)), 0); + + // Edge values + putDirect(vm, JSC::Identifier::fromString(vm, "EDGE_LEFT"_s), JSC::jsNumber(static_cast(YGEdgeLeft)), 0); + putDirect(vm, JSC::Identifier::fromString(vm, "EDGE_TOP"_s), JSC::jsNumber(static_cast(YGEdgeTop)), 0); + putDirect(vm, JSC::Identifier::fromString(vm, "EDGE_RIGHT"_s), JSC::jsNumber(static_cast(YGEdgeRight)), 0); + putDirect(vm, JSC::Identifier::fromString(vm, "EDGE_BOTTOM"_s), JSC::jsNumber(static_cast(YGEdgeBottom)), 0); + putDirect(vm, JSC::Identifier::fromString(vm, "EDGE_START"_s), JSC::jsNumber(static_cast(YGEdgeStart)), 0); + putDirect(vm, JSC::Identifier::fromString(vm, "EDGE_END"_s), JSC::jsNumber(static_cast(YGEdgeEnd)), 0); + putDirect(vm, JSC::Identifier::fromString(vm, "EDGE_HORIZONTAL"_s), JSC::jsNumber(static_cast(YGEdgeHorizontal)), 0); + putDirect(vm, JSC::Identifier::fromString(vm, "EDGE_VERTICAL"_s), JSC::jsNumber(static_cast(YGEdgeVertical)), 0); + putDirect(vm, JSC::Identifier::fromString(vm, "EDGE_ALL"_s), JSC::jsNumber(static_cast(YGEdgeAll)), 0); + + // Errata values + putDirect(vm, JSC::Identifier::fromString(vm, "ERRATA_NONE"_s), JSC::jsNumber(static_cast(YGErrataNone)), 0); + putDirect(vm, JSC::Identifier::fromString(vm, "ERRATA_STRETCH_FLEX_BASIS"_s), JSC::jsNumber(static_cast(YGErrataStretchFlexBasis)), 0); + putDirect(vm, JSC::Identifier::fromString(vm, "ERRATA_ABSOLUTE_POSITION_WITHOUT_INSETS_EXCLUDES_PADDING"_s), JSC::jsNumber(static_cast(YGErrataAbsolutePositionWithoutInsetsExcludesPadding)), 0); + putDirect(vm, JSC::Identifier::fromString(vm, "ERRATA_ABSOLUTE_PERCENT_AGAINST_INNER_SIZE"_s), JSC::jsNumber(static_cast(YGErrataAbsolutePercentAgainstInnerSize)), 0); + putDirect(vm, JSC::Identifier::fromString(vm, "ERRATA_ALL"_s), JSC::jsNumber(static_cast(YGErrataAll)), 0); + putDirect(vm, JSC::Identifier::fromString(vm, "ERRATA_CLASSIC"_s), JSC::jsNumber(static_cast(YGErrataClassic)), 0); + + // Experimental feature values + putDirect(vm, JSC::Identifier::fromString(vm, "EXPERIMENTAL_FEATURE_WEB_FLEX_BASIS"_s), JSC::jsNumber(static_cast(YGExperimentalFeatureWebFlexBasis)), 0); + + // Flex direction values + putDirect(vm, JSC::Identifier::fromString(vm, "FLEX_DIRECTION_COLUMN"_s), JSC::jsNumber(static_cast(YGFlexDirectionColumn)), 0); + putDirect(vm, JSC::Identifier::fromString(vm, "FLEX_DIRECTION_COLUMN_REVERSE"_s), JSC::jsNumber(static_cast(YGFlexDirectionColumnReverse)), 0); + putDirect(vm, JSC::Identifier::fromString(vm, "FLEX_DIRECTION_ROW"_s), JSC::jsNumber(static_cast(YGFlexDirectionRow)), 0); + putDirect(vm, JSC::Identifier::fromString(vm, "FLEX_DIRECTION_ROW_REVERSE"_s), JSC::jsNumber(static_cast(YGFlexDirectionRowReverse)), 0); + + // Gutter values + putDirect(vm, JSC::Identifier::fromString(vm, "GUTTER_COLUMN"_s), JSC::jsNumber(static_cast(YGGutterColumn)), 0); + putDirect(vm, JSC::Identifier::fromString(vm, "GUTTER_ROW"_s), JSC::jsNumber(static_cast(YGGutterRow)), 0); + putDirect(vm, JSC::Identifier::fromString(vm, "GUTTER_ALL"_s), JSC::jsNumber(static_cast(YGGutterAll)), 0); + + // Justify values + putDirect(vm, JSC::Identifier::fromString(vm, "JUSTIFY_FLEX_START"_s), JSC::jsNumber(static_cast(YGJustifyFlexStart)), 0); + putDirect(vm, JSC::Identifier::fromString(vm, "JUSTIFY_CENTER"_s), JSC::jsNumber(static_cast(YGJustifyCenter)), 0); + putDirect(vm, JSC::Identifier::fromString(vm, "JUSTIFY_FLEX_END"_s), JSC::jsNumber(static_cast(YGJustifyFlexEnd)), 0); + putDirect(vm, JSC::Identifier::fromString(vm, "JUSTIFY_SPACE_BETWEEN"_s), JSC::jsNumber(static_cast(YGJustifySpaceBetween)), 0); + putDirect(vm, JSC::Identifier::fromString(vm, "JUSTIFY_SPACE_AROUND"_s), JSC::jsNumber(static_cast(YGJustifySpaceAround)), 0); + putDirect(vm, JSC::Identifier::fromString(vm, "JUSTIFY_SPACE_EVENLY"_s), JSC::jsNumber(static_cast(YGJustifySpaceEvenly)), 0); + + // Log level values + putDirect(vm, JSC::Identifier::fromString(vm, "LOG_LEVEL_ERROR"_s), JSC::jsNumber(static_cast(YGLogLevelError)), 0); + putDirect(vm, JSC::Identifier::fromString(vm, "LOG_LEVEL_WARN"_s), JSC::jsNumber(static_cast(YGLogLevelWarn)), 0); + putDirect(vm, JSC::Identifier::fromString(vm, "LOG_LEVEL_INFO"_s), JSC::jsNumber(static_cast(YGLogLevelInfo)), 0); + putDirect(vm, JSC::Identifier::fromString(vm, "LOG_LEVEL_DEBUG"_s), JSC::jsNumber(static_cast(YGLogLevelDebug)), 0); + putDirect(vm, JSC::Identifier::fromString(vm, "LOG_LEVEL_VERBOSE"_s), JSC::jsNumber(static_cast(YGLogLevelVerbose)), 0); + putDirect(vm, JSC::Identifier::fromString(vm, "LOG_LEVEL_FATAL"_s), JSC::jsNumber(static_cast(YGLogLevelFatal)), 0); + + // Measure mode values + putDirect(vm, JSC::Identifier::fromString(vm, "MEASURE_MODE_UNDEFINED"_s), JSC::jsNumber(static_cast(YGMeasureModeUndefined)), 0); + putDirect(vm, JSC::Identifier::fromString(vm, "MEASURE_MODE_EXACTLY"_s), JSC::jsNumber(static_cast(YGMeasureModeExactly)), 0); + putDirect(vm, JSC::Identifier::fromString(vm, "MEASURE_MODE_AT_MOST"_s), JSC::jsNumber(static_cast(YGMeasureModeAtMost)), 0); + + // Node type values + putDirect(vm, JSC::Identifier::fromString(vm, "NODE_TYPE_DEFAULT"_s), JSC::jsNumber(static_cast(YGNodeTypeDefault)), 0); + putDirect(vm, JSC::Identifier::fromString(vm, "NODE_TYPE_TEXT"_s), JSC::jsNumber(static_cast(YGNodeTypeText)), 0); + + // Overflow values + putDirect(vm, JSC::Identifier::fromString(vm, "OVERFLOW_VISIBLE"_s), JSC::jsNumber(static_cast(YGOverflowVisible)), 0); + putDirect(vm, JSC::Identifier::fromString(vm, "OVERFLOW_HIDDEN"_s), JSC::jsNumber(static_cast(YGOverflowHidden)), 0); + putDirect(vm, JSC::Identifier::fromString(vm, "OVERFLOW_SCROLL"_s), JSC::jsNumber(static_cast(YGOverflowScroll)), 0); + + // Position type values + putDirect(vm, JSC::Identifier::fromString(vm, "POSITION_TYPE_STATIC"_s), JSC::jsNumber(static_cast(YGPositionTypeStatic)), 0); + putDirect(vm, JSC::Identifier::fromString(vm, "POSITION_TYPE_RELATIVE"_s), JSC::jsNumber(static_cast(YGPositionTypeRelative)), 0); + putDirect(vm, JSC::Identifier::fromString(vm, "POSITION_TYPE_ABSOLUTE"_s), JSC::jsNumber(static_cast(YGPositionTypeAbsolute)), 0); + + // Unit values + putDirect(vm, JSC::Identifier::fromString(vm, "UNIT_UNDEFINED"_s), JSC::jsNumber(static_cast(YGUnitUndefined)), 0); + putDirect(vm, JSC::Identifier::fromString(vm, "UNIT_POINT"_s), JSC::jsNumber(static_cast(YGUnitPoint)), 0); + putDirect(vm, JSC::Identifier::fromString(vm, "UNIT_PERCENT"_s), JSC::jsNumber(static_cast(YGUnitPercent)), 0); + putDirect(vm, JSC::Identifier::fromString(vm, "UNIT_AUTO"_s), JSC::jsNumber(static_cast(YGUnitAuto)), 0); + putDirect(vm, JSC::Identifier::fromString(vm, "UNIT_MAX_CONTENT"_s), JSC::jsNumber(static_cast(YGUnitMaxContent)), 0); + putDirect(vm, JSC::Identifier::fromString(vm, "UNIT_FIT_CONTENT"_s), JSC::jsNumber(static_cast(YGUnitFitContent)), 0); + putDirect(vm, JSC::Identifier::fromString(vm, "UNIT_STRETCH"_s), JSC::jsNumber(static_cast(YGUnitStretch)), 0); + + // Wrap values + putDirect(vm, JSC::Identifier::fromString(vm, "WRAP_NO_WRAP"_s), JSC::jsNumber(static_cast(YGWrapNoWrap)), 0); + putDirect(vm, JSC::Identifier::fromString(vm, "WRAP_WRAP"_s), JSC::jsNumber(static_cast(YGWrapWrap)), 0); + putDirect(vm, JSC::Identifier::fromString(vm, "WRAP_WRAP_REVERSE"_s), JSC::jsNumber(static_cast(YGWrapWrapReverse)), 0); } // Export function for Zig integration diff --git a/src/bun.js/bindings/JSYogaNode.cpp b/src/bun.js/bindings/JSYogaNode.cpp index a28e29b496..ad15fc3c90 100644 --- a/src/bun.js/bindings/JSYogaNode.cpp +++ b/src/bun.js/bindings/JSYogaNode.cpp @@ -2,11 +2,14 @@ #include "JSYogaNode.h" #include "webcore/DOMIsoSubspaces.h" #include "webcore/DOMClientIsoSubspaces.h" +#include "webcore/WebCoreJSClientData.h" #include namespace Bun { -const JSC::ClassInfo JSYogaNode::s_info = { "Yoga.Node"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSYogaNode) }; +using namespace JSC; + +const JSC::ClassInfo JSYogaNode::s_info = { "Node"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSYogaNode) }; JSYogaNode::JSYogaNode(JSC::VM& vm, JSC::Structure* structure) : Base(vm, structure) @@ -67,7 +70,7 @@ JSC::GCClient::IsoSubspace* JSYogaNode::subspaceFor(JSC::VM& vm) { if constexpr (mode == JSC::SubspaceAccess::Concurrently) return nullptr; - return WebCore::subspaceForImpl( + return WebCore::subspaceForImpl( vm, [](auto& spaces) { return spaces.m_clientSubspaceForJSYogaNode.get(); }, [](auto& spaces, auto&& space) { spaces.m_clientSubspaceForJSYogaNode = std::forward(space); }, @@ -77,7 +80,8 @@ JSC::GCClient::IsoSubspace* JSYogaNode::subspaceFor(JSC::VM& vm) DEFINE_VISIT_CHILDREN(JSYogaNode); -void JSYogaNode::visitChildrenImpl(JSC::JSCell* cell, JSC::Visitor& visitor) +template +void JSYogaNode::visitChildrenImpl(JSC::JSCell* cell, Visitor& visitor) { JSYogaNode* thisObject = jsCast(cell); Base::visitChildren(thisObject, visitor); diff --git a/src/bun.js/bindings/JSYogaNode.h b/src/bun.js/bindings/JSYogaNode.h index 935f1b4ec9..e314d6cf7e 100644 --- a/src/bun.js/bindings/JSYogaNode.h +++ b/src/bun.js/bindings/JSYogaNode.h @@ -2,7 +2,7 @@ #include "root.h" #include #include -#include +#include // Forward declarations typedef struct YGNode* YGNodeRef; @@ -30,14 +30,15 @@ public: YGNodeRef internal() { return m_node; } void clearInternal() { m_node = nullptr; } + void setInternal(YGNodeRef node) { m_node = node; } // Helper to get JS wrapper from Yoga node static JSYogaNode* fromYGNode(YGNodeRef); JSC::JSGlobalObject* globalObject() const; // Storage for JS callbacks - JSC::Strong m_measureFunc; - JSC::Strong m_dirtiedFunc; + JSC::WriteBarrier m_measureFunc; + JSC::WriteBarrier m_dirtiedFunc; private: JSYogaNode(JSC::VM&, JSC::Structure*); diff --git a/src/bun.js/bindings/JSYogaNodeImpl.cpp b/src/bun.js/bindings/JSYogaNodeImpl.cpp deleted file mode 100644 index 524e1d0f96..0000000000 --- a/src/bun.js/bindings/JSYogaNodeImpl.cpp +++ /dev/null @@ -1,683 +0,0 @@ -#include "root.h" -#include "JSYogaNode.h" -#include -#include - -namespace Bun { - -// Helper function to parse value arguments (number, string, object, undefined) -static void parseYogaValue(JSC::JSGlobalObject* globalObject, JSC::JSValue arg, - std::function setNumber, - std::function setPercent, - std::function setAuto, - std::function setUndefined, - std::function setMaxContent = nullptr, - std::function setFitContent = nullptr, - std::function setStretch = nullptr) -{ - JSC::VM& vm = globalObject->vm(); - auto scope = DECLARE_THROW_SCOPE(vm); - - if (arg.isNumber()) { - setNumber(static_cast(arg.asNumber())); - } else if (arg.isString()) { - auto str = arg.toWTFString(globalObject); - RETURN_IF_EXCEPTION(scope, void()); - - if (str == "auto"_s) { - setAuto(); - } else if (str == "max-content"_s && setMaxContent) { - setMaxContent(); - } else if (str == "fit-content"_s && setFitContent) { - setFitContent(); - } else if (str == "stretch"_s && setStretch) { - setStretch(); - } else if (str.endsWith('%')) { - // Parse percentage - str.remove(str.length() - 1); - float percent = str.toFloat(); - setPercent(percent); - } else { - throwTypeError(globalObject, scope, "Invalid string value for style property"_s); - } - } else if (arg.isUndefinedOrNull()) { - setUndefined(); - } else if (arg.isObject()) { - // Handle { unit, value } object - JSC::JSObject* obj = arg.getObject(); - JSC::JSValue unitValue = obj->get(globalObject, vm.propertyNames->unit); - JSC::JSValue valueValue = obj->get(globalObject, vm.propertyNames->value); - RETURN_IF_EXCEPTION(scope, void()); - - int32_t unit = unitValue.toInt32(globalObject); - RETURN_IF_EXCEPTION(scope, void()); - - float value = static_cast(valueValue.toNumber(globalObject)); - RETURN_IF_EXCEPTION(scope, void()); - - switch (static_cast(unit)) { - case YGUnitPoint: - setNumber(value); - break; - case YGUnitPercent: - setPercent(value); - break; - case YGUnitAuto: - setAuto(); - break; - case YGUnitUndefined: - default: - setUndefined(); - break; - } - } else { - throwTypeError(globalObject, scope, "Invalid value type for style property"_s); - } -} - -// Width/Height setters -JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncSetWidth, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) -{ - JSC::VM& vm = globalObject->vm(); - auto scope = DECLARE_THROW_SCOPE(vm); - - auto* thisObject = jsDynamicCast(callFrame->thisValue()); - if (UNLIKELY(!thisObject)) { - return JSC::JSValue::encode(Bun::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "setWidth"_s)); - } - - if (callFrame->argumentCount() < 1) { - return JSC::JSValue::encode(JSC::jsUndefined()); - } - - YGNodeRef node = thisObject->internal(); - JSC::JSValue arg = callFrame->uncheckedArgument(0); - - parseYogaValue(globalObject, arg, [node](float value) { YGNodeStyleSetWidth(node, value); }, [node](float percent) { YGNodeStyleSetWidthPercent(node, percent); }, [node]() { YGNodeStyleSetWidthAuto(node); }, [node]() { YGNodeStyleSetWidth(node, YGUndefined); }, [node]() { YGNodeStyleSetWidthMaxContent(node); }, [node]() { YGNodeStyleSetWidthFitContent(node); }, [node]() { YGNodeStyleSetWidthStretch(node); }); - - RETURN_IF_EXCEPTION(scope, {}); - return JSC::JSValue::encode(JSC::jsUndefined()); -} - -JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncSetHeight, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) -{ - JSC::VM& vm = globalObject->vm(); - auto scope = DECLARE_THROW_SCOPE(vm); - - auto* thisObject = jsDynamicCast(callFrame->thisValue()); - if (UNLIKELY(!thisObject)) { - return JSC::JSValue::encode(Bun::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "setHeight"_s)); - } - - if (callFrame->argumentCount() < 1) { - return JSC::JSValue::encode(JSC::jsUndefined()); - } - - YGNodeRef node = thisObject->internal(); - JSC::JSValue arg = callFrame->uncheckedArgument(0); - - parseYogaValue(globalObject, arg, [node](float value) { YGNodeStyleSetHeight(node, value); }, [node](float percent) { YGNodeStyleSetHeightPercent(node, percent); }, [node]() { YGNodeStyleSetHeightAuto(node); }, [node]() { YGNodeStyleSetHeight(node, YGUndefined); }, [node]() { YGNodeStyleSetHeightMaxContent(node); }, [node]() { YGNodeStyleSetHeightFitContent(node); }, [node]() { YGNodeStyleSetHeightStretch(node); }); - - RETURN_IF_EXCEPTION(scope, {}); - return JSC::JSValue::encode(JSC::jsUndefined()); -} - -// Edge value setters (margin, padding, position) -static void parseEdgeValue(JSC::JSGlobalObject* globalObject, YGNodeRef node, YGEdge edge, JSC::JSValue arg, - std::function setNumber, - std::function setPercent, - std::function setAuto) -{ - parseYogaValue(globalObject, arg, [node, edge, setNumber](float value) { setNumber(node, edge, value); }, [node, edge, setPercent](float percent) { setPercent(node, edge, percent); }, [node, edge, setAuto]() { if (setAuto) setAuto(node, edge); }, [node, edge, setNumber]() { setNumber(node, edge, YGUndefined); }); -} - -JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncSetMargin, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) -{ - JSC::VM& vm = globalObject->vm(); - auto scope = DECLARE_THROW_SCOPE(vm); - - auto* thisObject = jsDynamicCast(callFrame->thisValue()); - if (UNLIKELY(!thisObject)) { - return JSC::JSValue::encode(Bun::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "setMargin"_s)); - } - - if (callFrame->argumentCount() < 2) { - throwTypeError(globalObject, scope, "setMargin requires 2 arguments"_s); - return {}; - } - - YGNodeRef node = thisObject->internal(); - int32_t edge = callFrame->uncheckedArgument(0).toInt32(globalObject); - RETURN_IF_EXCEPTION(scope, {}); - - JSC::JSValue value = callFrame->uncheckedArgument(1); - - parseEdgeValue(globalObject, node, static_cast(edge), value, - YGNodeStyleSetMargin, - YGNodeStyleSetMarginPercent, - YGNodeStyleSetMarginAuto); - - RETURN_IF_EXCEPTION(scope, {}); - return JSC::JSValue::encode(JSC::jsUndefined()); -} - -// Hierarchy methods -JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncInsertChild, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) -{ - JSC::VM& vm = globalObject->vm(); - auto scope = DECLARE_THROW_SCOPE(vm); - - auto* thisObject = jsDynamicCast(callFrame->thisValue()); - if (UNLIKELY(!thisObject)) { - return JSC::JSValue::encode(Bun::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "insertChild"_s)); - } - - if (callFrame->argumentCount() < 2) { - throwTypeError(globalObject, scope, "insertChild requires 2 arguments"_s); - return {}; - } - - auto* childNode = jsDynamicCast(callFrame->uncheckedArgument(0)); - if (!childNode) { - throwTypeError(globalObject, scope, "First argument must be a Yoga.Node"_s); - return {}; - } - - int32_t index = callFrame->uncheckedArgument(1).toInt32(globalObject); - RETURN_IF_EXCEPTION(scope, {}); - - YGNodeInsertChild(thisObject->internal(), childNode->internal(), index); - return JSC::JSValue::encode(JSC::jsUndefined()); -} - -JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncGetChild, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) -{ - JSC::VM& vm = globalObject->vm(); - auto scope = DECLARE_THROW_SCOPE(vm); - - auto* thisObject = jsDynamicCast(callFrame->thisValue()); - if (UNLIKELY(!thisObject)) { - return JSC::JSValue::encode(Bun::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "getChild"_s)); - } - - if (callFrame->argumentCount() < 1) { - throwTypeError(globalObject, scope, "getChild requires 1 argument"_s); - return {}; - } - - int32_t index = callFrame->uncheckedArgument(0).toInt32(globalObject); - RETURN_IF_EXCEPTION(scope, {}); - - YGNodeRef childRef = YGNodeGetChild(thisObject->internal(), index); - JSYogaNode* childNode = childRef ? JSYogaNode::fromYGNode(childRef) : nullptr; - - return JSC::JSValue::encode(childNode ? childNode : JSC::jsNull()); -} - -JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncGetParent, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) -{ - JSC::VM& vm = globalObject->vm(); - auto scope = DECLARE_THROW_SCOPE(vm); - - auto* thisObject = jsDynamicCast(callFrame->thisValue()); - if (UNLIKELY(!thisObject)) { - return JSC::JSValue::encode(Bun::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "getParent"_s)); - } - - YGNodeRef parentRef = YGNodeGetParent(thisObject->internal()); - JSYogaNode* parentNode = parentRef ? JSYogaNode::fromYGNode(parentRef) : nullptr; - - return JSC::JSValue::encode(parentNode ? parentNode : JSC::jsNull()); -} - -// Measure function callback -static YGSize bunMeasureCallback(YGNodeConstRef ygNode, float width, YGMeasureMode widthMode, - float height, YGMeasureMode heightMode) -{ - JSYogaNode* jsNode = JSYogaNode::fromYGNode(const_cast(ygNode)); - if (!jsNode || !jsNode->m_measureFunc) return { YGUndefined, YGUndefined }; - - JSC::JSGlobalObject* globalObject = jsNode->globalObject(); - JSC::VM& vm = globalObject->vm(); - JSC::JSLockHolder lock(vm); - auto scope = DECLARE_CATCH_SCOPE(vm); - - JSC::MarkedArgumentBuffer args; - args.append(JSC::jsNumber(width)); - args.append(JSC::jsNumber(static_cast(widthMode))); - args.append(JSC::jsNumber(height)); - args.append(JSC::jsNumber(static_cast(heightMode))); - - JSC::JSValue result = JSC::call(globalObject, jsNode->m_measureFunc.get(), JSC::jsUndefined(), args); - if (scope.exception()) { - scope.clearException(); - return { 0, 0 }; - } - - if (!result.isObject()) return { 0, 0 }; - - JSC::JSObject* sizeObj = result.getObject(); - float resultWidth = sizeObj->get(globalObject, vm.propertyNames->width).toFloat(globalObject); - float resultHeight = sizeObj->get(globalObject, vm.propertyNames->height).toFloat(globalObject); - - return { resultWidth, resultHeight }; -} - -JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncSetMeasureFunc, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) -{ - JSC::VM& vm = globalObject->vm(); - auto scope = DECLARE_THROW_SCOPE(vm); - - auto* thisObject = jsDynamicCast(callFrame->thisValue()); - if (UNLIKELY(!thisObject)) { - return JSC::JSValue::encode(Bun::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "setMeasureFunc"_s)); - } - - if (callFrame->argumentCount() < 1) { - return JSC::JSValue::encode(JSC::jsUndefined()); - } - - JSValue func = callFrame->uncheckedArgument(0); - if (func.isUndefinedOrNull()) { - thisObject->m_measureFunc.clear(); - YGNodeSetMeasureFunc(thisObject->internal(), nullptr); - } else if (func.isCallable()) { - thisObject->m_measureFunc.set(vm, thisObject, func.getObject()); - YGNodeSetMeasureFunc(thisObject->internal(), bunMeasureCallback); - } else { - throwTypeError(globalObject, scope, "Measure function must be callable or null"_s); - return {}; - } - - return JSC::JSValue::encode(JSC::jsUndefined()); -} - -// Min/Max setters -JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncSetMinWidth, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) -{ - JSC::VM& vm = globalObject->vm(); - auto scope = DECLARE_THROW_SCOPE(vm); - - auto* thisObject = jsDynamicCast(callFrame->thisValue()); - if (UNLIKELY(!thisObject)) { - return JSC::JSValue::encode(Bun::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "setMinWidth"_s)); - } - - if (callFrame->argumentCount() < 1) { - return JSC::JSValue::encode(JSC::jsUndefined()); - } - - YGNodeRef node = thisObject->internal(); - JSC::JSValue arg = callFrame->uncheckedArgument(0); - - parseYogaValue(globalObject, arg, [node](float value) { YGNodeStyleSetMinWidth(node, value); }, [node](float percent) { YGNodeStyleSetMinWidthPercent(node, percent); }, []() { /* no auto for min */ }, [node]() { YGNodeStyleSetMinWidth(node, YGUndefined); }); - - RETURN_IF_EXCEPTION(scope, {}); - return JSC::JSValue::encode(JSC::jsUndefined()); -} - -JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncSetMinHeight, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) -{ - JSC::VM& vm = globalObject->vm(); - auto scope = DECLARE_THROW_SCOPE(vm); - - auto* thisObject = jsDynamicCast(callFrame->thisValue()); - if (UNLIKELY(!thisObject)) { - return JSC::JSValue::encode(Bun::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "setMinHeight"_s)); - } - - if (callFrame->argumentCount() < 1) { - return JSC::JSValue::encode(JSC::jsUndefined()); - } - - YGNodeRef node = thisObject->internal(); - JSC::JSValue arg = callFrame->uncheckedArgument(0); - - parseYogaValue(globalObject, arg, [node](float value) { YGNodeStyleSetMinHeight(node, value); }, [node](float percent) { YGNodeStyleSetMinHeightPercent(node, percent); }, []() { /* no auto for min */ }, [node]() { YGNodeStyleSetMinHeight(node, YGUndefined); }); - - RETURN_IF_EXCEPTION(scope, {}); - return JSC::JSValue::encode(JSC::jsUndefined()); -} - -JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncSetMaxWidth, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) -{ - JSC::VM& vm = globalObject->vm(); - auto scope = DECLARE_THROW_SCOPE(vm); - - auto* thisObject = jsDynamicCast(callFrame->thisValue()); - if (UNLIKELY(!thisObject)) { - return JSC::JSValue::encode(Bun::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "setMaxWidth"_s)); - } - - if (callFrame->argumentCount() < 1) { - return JSC::JSValue::encode(JSC::jsUndefined()); - } - - YGNodeRef node = thisObject->internal(); - JSC::JSValue arg = callFrame->uncheckedArgument(0); - - parseYogaValue(globalObject, arg, [node](float value) { YGNodeStyleSetMaxWidth(node, value); }, [node](float percent) { YGNodeStyleSetMaxWidthPercent(node, percent); }, []() { /* no auto for max */ }, [node]() { YGNodeStyleSetMaxWidth(node, YGUndefined); }); - - RETURN_IF_EXCEPTION(scope, {}); - return JSC::JSValue::encode(JSC::jsUndefined()); -} - -JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncSetMaxHeight, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) -{ - JSC::VM& vm = globalObject->vm(); - auto scope = DECLARE_THROW_SCOPE(vm); - - auto* thisObject = jsDynamicCast(callFrame->thisValue()); - if (UNLIKELY(!thisObject)) { - return JSC::JSValue::encode(Bun::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "setMaxHeight"_s)); - } - - if (callFrame->argumentCount() < 1) { - return JSC::JSValue::encode(JSC::jsUndefined()); - } - - YGNodeRef node = thisObject->internal(); - JSC::JSValue arg = callFrame->uncheckedArgument(0); - - parseYogaValue(globalObject, arg, [node](float value) { YGNodeStyleSetMaxHeight(node, value); }, [node](float percent) { YGNodeStyleSetMaxHeightPercent(node, percent); }, []() { /* no auto for max */ }, [node]() { YGNodeStyleSetMaxHeight(node, YGUndefined); }); - - RETURN_IF_EXCEPTION(scope, {}); - return JSC::JSValue::encode(JSC::jsUndefined()); -} - -JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncSetFlexBasis, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) -{ - JSC::VM& vm = globalObject->vm(); - auto scope = DECLARE_THROW_SCOPE(vm); - - auto* thisObject = jsDynamicCast(callFrame->thisValue()); - if (UNLIKELY(!thisObject)) { - return JSC::JSValue::encode(Bun::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "setFlexBasis"_s)); - } - - if (callFrame->argumentCount() < 1) { - return JSC::JSValue::encode(JSC::jsUndefined()); - } - - YGNodeRef node = thisObject->internal(); - JSC::JSValue arg = callFrame->uncheckedArgument(0); - - parseYogaValue(globalObject, arg, [node](float value) { YGNodeStyleSetFlexBasis(node, value); }, [node](float percent) { YGNodeStyleSetFlexBasisPercent(node, percent); }, [node]() { YGNodeStyleSetFlexBasisAuto(node); }, [node]() { YGNodeStyleSetFlexBasis(node, YGUndefined); }); - - RETURN_IF_EXCEPTION(scope, {}); - return JSC::JSValue::encode(JSC::jsUndefined()); -} - -// Padding setter -JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncSetPadding, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) -{ - JSC::VM& vm = globalObject->vm(); - auto scope = DECLARE_THROW_SCOPE(vm); - - auto* thisObject = jsDynamicCast(callFrame->thisValue()); - if (UNLIKELY(!thisObject)) { - return JSC::JSValue::encode(Bun::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "setPadding"_s)); - } - - if (callFrame->argumentCount() < 2) { - throwTypeError(globalObject, scope, "setPadding requires 2 arguments"_s); - return {}; - } - - YGNodeRef node = thisObject->internal(); - int32_t edge = callFrame->uncheckedArgument(0).toInt32(globalObject); - RETURN_IF_EXCEPTION(scope, {}); - - JSC::JSValue value = callFrame->uncheckedArgument(1); - - parseEdgeValue(globalObject, node, static_cast(edge), value, - YGNodeStyleSetPadding, - YGNodeStyleSetPaddingPercent, - nullptr // no auto for padding - ); - - RETURN_IF_EXCEPTION(scope, {}); - return JSC::JSValue::encode(JSC::jsUndefined()); -} - -// Position setter -JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncSetPosition, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) -{ - JSC::VM& vm = globalObject->vm(); - auto scope = DECLARE_THROW_SCOPE(vm); - - auto* thisObject = jsDynamicCast(callFrame->thisValue()); - if (UNLIKELY(!thisObject)) { - return JSC::JSValue::encode(Bun::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "setPosition"_s)); - } - - if (callFrame->argumentCount() < 2) { - throwTypeError(globalObject, scope, "setPosition requires 2 arguments"_s); - return {}; - } - - YGNodeRef node = thisObject->internal(); - int32_t edge = callFrame->uncheckedArgument(0).toInt32(globalObject); - RETURN_IF_EXCEPTION(scope, {}); - - JSC::JSValue value = callFrame->uncheckedArgument(1); - - parseEdgeValue(globalObject, node, static_cast(edge), value, - YGNodeStyleSetPosition, - YGNodeStyleSetPositionPercent, - nullptr // no auto for position - ); - - RETURN_IF_EXCEPTION(scope, {}); - return JSC::JSValue::encode(JSC::jsUndefined()); -} - -// Gap setter -JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncSetGap, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) -{ - JSC::VM& vm = globalObject->vm(); - auto scope = DECLARE_THROW_SCOPE(vm); - - auto* thisObject = jsDynamicCast(callFrame->thisValue()); - if (UNLIKELY(!thisObject)) { - return JSC::JSValue::encode(Bun::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "setGap"_s)); - } - - if (callFrame->argumentCount() < 2) { - throwTypeError(globalObject, scope, "setGap requires 2 arguments"_s); - return {}; - } - - YGNodeRef node = thisObject->internal(); - int32_t gutter = callFrame->uncheckedArgument(0).toInt32(globalObject); - RETURN_IF_EXCEPTION(scope, {}); - - float gap = static_cast(callFrame->uncheckedArgument(1).toNumber(globalObject)); - RETURN_IF_EXCEPTION(scope, {}); - - YGNodeStyleSetGap(node, static_cast(gutter), gap); - return JSC::JSValue::encode(JSC::jsUndefined()); -} - -// Helper to convert YGValue to JSValue -static JSC::JSValue ygValueToJS(JSC::JSGlobalObject* globalObject, YGValue value) -{ - JSC::VM& vm = globalObject->vm(); - - if (YGFloatIsUndefined(value.value)) { - return JSC::jsUndefined(); - } - - JSC::JSObject* obj = JSC::constructEmptyObject(globalObject); - obj->putDirect(vm, vm.propertyNames->unit, JSC::jsNumber(static_cast(value.unit))); - obj->putDirect(vm, vm.propertyNames->value, JSC::jsNumber(value.value)); - - return obj; -} - -// Style getters -JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncGetWidth, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) -{ - JSC::VM& vm = globalObject->vm(); - auto scope = DECLARE_THROW_SCOPE(vm); - - auto* thisObject = jsDynamicCast(callFrame->thisValue()); - if (UNLIKELY(!thisObject)) { - return JSC::JSValue::encode(Bun::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "getWidth"_s)); - } - - YGValue value = YGNodeStyleGetWidth(thisObject->internal()); - return JSC::JSValue::encode(ygValueToJS(globalObject, value)); -} - -JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncGetHeight, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) -{ - JSC::VM& vm = globalObject->vm(); - auto scope = DECLARE_THROW_SCOPE(vm); - - auto* thisObject = jsDynamicCast(callFrame->thisValue()); - if (UNLIKELY(!thisObject)) { - return JSC::JSValue::encode(Bun::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "getHeight"_s)); - } - - YGValue value = YGNodeStyleGetHeight(thisObject->internal()); - return JSC::JSValue::encode(ygValueToJS(globalObject, value)); -} - -JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncGetMinWidth, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) -{ - JSC::VM& vm = globalObject->vm(); - auto scope = DECLARE_THROW_SCOPE(vm); - - auto* thisObject = jsDynamicCast(callFrame->thisValue()); - if (UNLIKELY(!thisObject)) { - return JSC::JSValue::encode(Bun::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "getMinWidth"_s)); - } - - YGValue value = YGNodeStyleGetMinWidth(thisObject->internal()); - return JSC::JSValue::encode(ygValueToJS(globalObject, value)); -} - -JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncGetMinHeight, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) -{ - JSC::VM& vm = globalObject->vm(); - auto scope = DECLARE_THROW_SCOPE(vm); - - auto* thisObject = jsDynamicCast(callFrame->thisValue()); - if (UNLIKELY(!thisObject)) { - return JSC::JSValue::encode(Bun::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "getMinHeight"_s)); - } - - YGValue value = YGNodeStyleGetMinHeight(thisObject->internal()); - return JSC::JSValue::encode(ygValueToJS(globalObject, value)); -} - -JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncGetMaxWidth, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) -{ - JSC::VM& vm = globalObject->vm(); - auto scope = DECLARE_THROW_SCOPE(vm); - - auto* thisObject = jsDynamicCast(callFrame->thisValue()); - if (UNLIKELY(!thisObject)) { - return JSC::JSValue::encode(Bun::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "getMaxWidth"_s)); - } - - YGValue value = YGNodeStyleGetMaxWidth(thisObject->internal()); - return JSC::JSValue::encode(ygValueToJS(globalObject, value)); -} - -JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncGetMaxHeight, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) -{ - JSC::VM& vm = globalObject->vm(); - auto scope = DECLARE_THROW_SCOPE(vm); - - auto* thisObject = jsDynamicCast(callFrame->thisValue()); - if (UNLIKELY(!thisObject)) { - return JSC::JSValue::encode(Bun::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "getMaxHeight"_s)); - } - - YGValue value = YGNodeStyleGetMaxHeight(thisObject->internal()); - return JSC::JSValue::encode(ygValueToJS(globalObject, value)); -} - -JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncGetFlexBasis, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) -{ - JSC::VM& vm = globalObject->vm(); - auto scope = DECLARE_THROW_SCOPE(vm); - - auto* thisObject = jsDynamicCast(callFrame->thisValue()); - if (UNLIKELY(!thisObject)) { - return JSC::JSValue::encode(Bun::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "getFlexBasis"_s)); - } - - YGValue value = YGNodeStyleGetFlexBasis(thisObject->internal()); - return JSC::JSValue::encode(ygValueToJS(globalObject, value)); -} - -JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncGetMargin, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) -{ - JSC::VM& vm = globalObject->vm(); - auto scope = DECLARE_THROW_SCOPE(vm); - - auto* thisObject = jsDynamicCast(callFrame->thisValue()); - if (UNLIKELY(!thisObject)) { - return JSC::JSValue::encode(Bun::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "getMargin"_s)); - } - - if (callFrame->argumentCount() < 1) { - throwTypeError(globalObject, scope, "getMargin requires 1 argument"_s); - return {}; - } - - int32_t edge = callFrame->uncheckedArgument(0).toInt32(globalObject); - RETURN_IF_EXCEPTION(scope, {}); - - YGValue value = YGNodeStyleGetMargin(thisObject->internal(), static_cast(edge)); - return JSC::JSValue::encode(ygValueToJS(globalObject, value)); -} - -JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncGetPadding, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) -{ - JSC::VM& vm = globalObject->vm(); - auto scope = DECLARE_THROW_SCOPE(vm); - - auto* thisObject = jsDynamicCast(callFrame->thisValue()); - if (UNLIKELY(!thisObject)) { - return JSC::JSValue::encode(Bun::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "getPadding"_s)); - } - - if (callFrame->argumentCount() < 1) { - throwTypeError(globalObject, scope, "getPadding requires 1 argument"_s); - return {}; - } - - int32_t edge = callFrame->uncheckedArgument(0).toInt32(globalObject); - RETURN_IF_EXCEPTION(scope, {}); - - YGValue value = YGNodeStyleGetPadding(thisObject->internal(), static_cast(edge)); - return JSC::JSValue::encode(ygValueToJS(globalObject, value)); -} - -JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncGetPosition, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) -{ - JSC::VM& vm = globalObject->vm(); - auto scope = DECLARE_THROW_SCOPE(vm); - - auto* thisObject = jsDynamicCast(callFrame->thisValue()); - if (UNLIKELY(!thisObject)) { - return JSC::JSValue::encode(Bun::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "getPosition"_s)); - } - - if (callFrame->argumentCount() < 1) { - throwTypeError(globalObject, scope, "getPosition requires 1 argument"_s); - return {}; - } - - int32_t edge = callFrame->uncheckedArgument(0).toInt32(globalObject); - RETURN_IF_EXCEPTION(scope, {}); - - YGValue value = YGNodeStyleGetPosition(thisObject->internal(), static_cast(edge)); - return JSC::JSValue::encode(ygValueToJS(globalObject, value)); -} - -} // namespace Bun diff --git a/src/bun.js/bindings/JSYogaPrototype.cpp b/src/bun.js/bindings/JSYogaPrototype.cpp index fea1d25b5d..6dc388d7f0 100644 --- a/src/bun.js/bindings/JSYogaPrototype.cpp +++ b/src/bun.js/bindings/JSYogaPrototype.cpp @@ -2,14 +2,25 @@ #include "JSYogaPrototype.h" #include "JSYogaConfig.h" #include "JSYogaNode.h" +#include "ZigGlobalObject.h" #include #include +#include #include +#include +#include +#include "JSDOMExceptionHandling.h" + +#ifndef UNLIKELY +#define UNLIKELY(x) __builtin_expect(!!(x), 0) +#endif namespace Bun { +using namespace JSC; + // Config Prototype implementation -const JSC::ClassInfo JSYogaConfigPrototype::s_info = { "Yoga.Config"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSYogaConfigPrototype) }; +const JSC::ClassInfo JSYogaConfigPrototype::s_info = { "Config"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSYogaConfigPrototype) }; // Forward declarations for Config prototype methods static JSC_DECLARE_HOST_FUNCTION(jsYogaConfigProtoFuncSetUseWebDefaults); @@ -22,6 +33,11 @@ static JSC_DECLARE_HOST_FUNCTION(jsYogaConfigProtoFuncSetErrata); static JSC_DECLARE_HOST_FUNCTION(jsYogaConfigProtoFuncGetErrata); static JSC_DECLARE_HOST_FUNCTION(jsYogaConfigProtoFuncIsEnabledForNodes); static JSC_DECLARE_HOST_FUNCTION(jsYogaConfigProtoFuncFree); +static JSC_DECLARE_HOST_FUNCTION(jsYogaConfigProtoFuncGetUseWebDefaults); +static JSC_DECLARE_HOST_FUNCTION(jsYogaConfigProtoFuncSetContext); +static JSC_DECLARE_HOST_FUNCTION(jsYogaConfigProtoFuncGetContext); +static JSC_DECLARE_HOST_FUNCTION(jsYogaConfigProtoFuncSetLogger); +static JSC_DECLARE_HOST_FUNCTION(jsYogaConfigProtoFuncSetCloneNodeFunc); // Hash table for Config prototype properties static const JSC::HashTableValue JSYogaConfigPrototypeTableValues[] = { @@ -35,17 +51,28 @@ static const JSC::HashTableValue JSYogaConfigPrototypeTableValues[] = { { "getErrata"_s, static_cast(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaConfigProtoFuncGetErrata, 0 } }, { "isEnabledForNodes"_s, static_cast(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaConfigProtoFuncIsEnabledForNodes, 1 } }, { "free"_s, static_cast(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaConfigProtoFuncFree, 0 } }, + { "getUseWebDefaults"_s, static_cast(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaConfigProtoFuncGetUseWebDefaults, 0 } }, + { "setContext"_s, static_cast(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaConfigProtoFuncSetContext, 1 } }, + { "getContext"_s, static_cast(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaConfigProtoFuncGetContext, 0 } }, + { "setLogger"_s, static_cast(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaConfigProtoFuncSetLogger, 1 } }, + { "setCloneNodeFunc"_s, static_cast(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaConfigProtoFuncSetCloneNodeFunc, 1 } }, }; void JSYogaConfigPrototype::finishCreation(JSC::VM& vm, JSC::JSGlobalObject* globalObject) { Base::finishCreation(vm); + + // Use reifyStaticProperties to add all methods at once reifyStaticProperties(vm, JSYogaConfig::info(), JSYogaConfigPrototypeTableValues, *this); - JSC_TO_STRING_TAG_WITHOUT_TRANSITION(); +} + +void JSYogaConfigPrototype::setConstructor(JSC::VM& vm, JSC::JSObject* constructor) +{ + putDirect(vm, vm.propertyNames->constructor, constructor, static_cast(JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::ReadOnly)); } // Node Prototype implementation -const JSC::ClassInfo JSYogaNodePrototype::s_info = { "Yoga.Node"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSYogaNodePrototype) }; +const JSC::ClassInfo JSYogaNodePrototype::s_info = { "Node"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSYogaNodePrototype) }; // Forward declarations for Node prototype methods static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncReset); @@ -105,15 +132,62 @@ static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncGetParent); // Callbacks static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncSetMeasureFunc); static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncSetDirtiedFunc); +static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncSetBaselineFunc); + +// Missing style setters +static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncSetDirection); +static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncSetBorder); +static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncSetBoxSizing); + +// Missing style getters +static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncGetDirection); +static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncGetFlexDirection); +static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncGetJustifyContent); +static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncGetAlignContent); +static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncGetAlignItems); +static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncGetAlignSelf); +static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncGetPositionType); +static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncGetFlexWrap); +static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncGetOverflow); +static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncGetDisplay); +static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncGetFlex); +static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncGetFlexGrow); +static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncGetFlexShrink); +static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncGetAspectRatio); +static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncGetGap); +static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncGetBorder); +static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncGetBoxSizing); + +// Missing layout getters +static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncGetComputedLeft); +static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncGetComputedTop); +static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncGetComputedRight); +static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncGetComputedBottom); +static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncGetComputedWidth); +static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncGetComputedHeight); +static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncGetComputedMargin); +static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncGetComputedBorder); +static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncGetComputedPadding); + +// Missing hierarchy methods +static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncRemoveAllChildren); +static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncGetOwner); + +// Missing utility methods +static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncFreeRecursive); +static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncCopyStyle); +static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncClone); +static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncSetNodeType); +static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncGetNodeType); +static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncSetIsReferenceBaseline); +static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncIsReferenceBaseline); +static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncSetContext); +static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncGetContext); +static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncSetConfig); +static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncGetConfig); +static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncGetHasNewLayout); +static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncSetHasNewLayout); -// External implementations -JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncSetWidth); -JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncSetHeight); -JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncSetMargin); -JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncInsertChild); -JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncGetChild); -JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncGetParent); -JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncSetMeasureFunc); // Hash table for Node prototype properties static const JSC::HashTableValue JSYogaNodePrototypeTableValues[] = { @@ -174,13 +248,74 @@ static const JSC::HashTableValue JSYogaNodePrototypeTableValues[] = { // Callbacks { "setMeasureFunc"_s, static_cast(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncSetMeasureFunc, 1 } }, { "setDirtiedFunc"_s, static_cast(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncSetDirtiedFunc, 1 } }, + { "setBaselineFunc"_s, static_cast(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncSetBaselineFunc, 1 } }, + + // Style setters + { "setDirection"_s, static_cast(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncSetDirection, 1 } }, + { "setBorder"_s, static_cast(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncSetBorder, 2 } }, + { "setBoxSizing"_s, static_cast(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncSetBoxSizing, 1 } }, + + // Style getters + { "getDirection"_s, static_cast(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncGetDirection, 0 } }, + { "getFlexDirection"_s, static_cast(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncGetFlexDirection, 0 } }, + { "getJustifyContent"_s, static_cast(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncGetJustifyContent, 0 } }, + { "getAlignContent"_s, static_cast(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncGetAlignContent, 0 } }, + { "getAlignItems"_s, static_cast(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncGetAlignItems, 0 } }, + { "getAlignSelf"_s, static_cast(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncGetAlignSelf, 0 } }, + { "getPositionType"_s, static_cast(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncGetPositionType, 0 } }, + { "getFlexWrap"_s, static_cast(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncGetFlexWrap, 0 } }, + { "getOverflow"_s, static_cast(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncGetOverflow, 0 } }, + { "getDisplay"_s, static_cast(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncGetDisplay, 0 } }, + { "getFlex"_s, static_cast(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncGetFlex, 0 } }, + { "getFlexGrow"_s, static_cast(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncGetFlexGrow, 0 } }, + { "getFlexShrink"_s, static_cast(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncGetFlexShrink, 0 } }, + { "getAspectRatio"_s, static_cast(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncGetAspectRatio, 0 } }, + { "getGap"_s, static_cast(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncGetGap, 1 } }, + { "getBorder"_s, static_cast(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncGetBorder, 1 } }, + { "getBoxSizing"_s, static_cast(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncGetBoxSizing, 0 } }, + + // Layout getters + { "getComputedLeft"_s, static_cast(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncGetComputedLeft, 0 } }, + { "getComputedTop"_s, static_cast(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncGetComputedTop, 0 } }, + { "getComputedRight"_s, static_cast(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncGetComputedRight, 0 } }, + { "getComputedBottom"_s, static_cast(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncGetComputedBottom, 0 } }, + { "getComputedWidth"_s, static_cast(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncGetComputedWidth, 0 } }, + { "getComputedHeight"_s, static_cast(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncGetComputedHeight, 0 } }, + { "getComputedMargin"_s, static_cast(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncGetComputedMargin, 1 } }, + { "getComputedBorder"_s, static_cast(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncGetComputedBorder, 1 } }, + { "getComputedPadding"_s, static_cast(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncGetComputedPadding, 1 } }, + + // Hierarchy methods + { "removeAllChildren"_s, static_cast(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncRemoveAllChildren, 0 } }, + { "getOwner"_s, static_cast(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncGetOwner, 0 } }, + + // Utility methods + { "freeRecursive"_s, static_cast(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncFreeRecursive, 0 } }, + { "copyStyle"_s, static_cast(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncCopyStyle, 1 } }, + { "clone"_s, static_cast(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncClone, 0 } }, + { "setNodeType"_s, static_cast(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncSetNodeType, 1 } }, + { "getNodeType"_s, static_cast(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncGetNodeType, 0 } }, + { "setIsReferenceBaseline"_s, static_cast(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncSetIsReferenceBaseline, 1 } }, + { "isReferenceBaseline"_s, static_cast(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncIsReferenceBaseline, 0 } }, + { "setContext"_s, static_cast(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncSetContext, 1 } }, + { "getContext"_s, static_cast(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncGetContext, 0 } }, + { "setConfig"_s, static_cast(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncSetConfig, 1 } }, + { "getConfig"_s, static_cast(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncGetConfig, 0 } }, + { "getHasNewLayout"_s, static_cast(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncGetHasNewLayout, 0 } }, + { "setHasNewLayout"_s, static_cast(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncSetHasNewLayout, 1 } }, }; void JSYogaNodePrototype::finishCreation(JSC::VM& vm, JSC::JSGlobalObject* globalObject) { Base::finishCreation(vm); + + // Use reifyStaticProperties to add all methods at once reifyStaticProperties(vm, JSYogaNode::info(), JSYogaNodePrototypeTableValues, *this); - JSC_TO_STRING_TAG_WITHOUT_TRANSITION(); +} + +void JSYogaNodePrototype::setConstructor(JSC::VM& vm, JSC::JSObject* constructor) +{ + putDirect(vm, vm.propertyNames->constructor, constructor, static_cast(JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::ReadOnly)); } // Config method implementations @@ -191,7 +326,7 @@ JSC_DEFINE_HOST_FUNCTION(jsYogaConfigProtoFuncSetUseWebDefaults, (JSC::JSGlobalO auto* thisObject = jsDynamicCast(callFrame->thisValue()); if (UNLIKELY(!thisObject)) { - return JSC::JSValue::encode(Bun::throwThisTypeError(*globalObject, scope, "Yoga.Config"_s, "setUseWebDefaults"_s)); + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Config"_s, "setUseWebDefaults"_s); } bool enabled = true; @@ -211,7 +346,7 @@ JSC_DEFINE_HOST_FUNCTION(jsYogaConfigProtoFuncUseWebDefaults, (JSC::JSGlobalObje auto* thisObject = jsDynamicCast(callFrame->thisValue()); if (UNLIKELY(!thisObject)) { - return JSC::JSValue::encode(Bun::throwThisTypeError(*globalObject, scope, "Yoga.Config"_s, "useWebDefaults"_s)); + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Config"_s, "useWebDefaults"_s); } // Legacy method - same as setUseWebDefaults(true) @@ -226,7 +361,7 @@ JSC_DEFINE_HOST_FUNCTION(jsYogaConfigProtoFuncSetExperimentalFeatureEnabled, (JS auto* thisObject = jsDynamicCast(callFrame->thisValue()); if (UNLIKELY(!thisObject)) { - return JSC::JSValue::encode(Bun::throwThisTypeError(*globalObject, scope, "Yoga.Config"_s, "setExperimentalFeatureEnabled"_s)); + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Config"_s, "setExperimentalFeatureEnabled"_s); } if (callFrame->argumentCount() < 2) { @@ -251,7 +386,7 @@ JSC_DEFINE_HOST_FUNCTION(jsYogaConfigProtoFuncIsExperimentalFeatureEnabled, (JSC auto* thisObject = jsDynamicCast(callFrame->thisValue()); if (UNLIKELY(!thisObject)) { - return JSC::JSValue::encode(Bun::throwThisTypeError(*globalObject, scope, "Yoga.Config"_s, "isExperimentalFeatureEnabled"_s)); + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Config"_s, "isExperimentalFeatureEnabled"_s); } if (callFrame->argumentCount() < 1) { @@ -273,7 +408,7 @@ JSC_DEFINE_HOST_FUNCTION(jsYogaConfigProtoFuncSetPointScaleFactor, (JSC::JSGloba auto* thisObject = jsDynamicCast(callFrame->thisValue()); if (UNLIKELY(!thisObject)) { - return JSC::JSValue::encode(Bun::throwThisTypeError(*globalObject, scope, "Yoga.Config"_s, "setPointScaleFactor"_s)); + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Config"_s, "setPointScaleFactor"_s); } if (callFrame->argumentCount() < 1) { @@ -295,7 +430,7 @@ JSC_DEFINE_HOST_FUNCTION(jsYogaConfigProtoFuncIsEnabledForNodes, (JSC::JSGlobalO auto* thisObject = jsDynamicCast(callFrame->thisValue()); if (UNLIKELY(!thisObject)) { - return JSC::JSValue::encode(Bun::throwThisTypeError(*globalObject, scope, "Yoga.Config"_s, "isEnabledForNodes"_s)); + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Config"_s, "isEnabledForNodes"_s); } // This method checks if a config is actively being used by any nodes @@ -310,7 +445,7 @@ JSC_DEFINE_HOST_FUNCTION(jsYogaConfigProtoFuncGetPointScaleFactor, (JSC::JSGloba auto* thisObject = jsDynamicCast(callFrame->thisValue()); if (UNLIKELY(!thisObject)) { - return JSC::JSValue::encode(Bun::throwThisTypeError(*globalObject, scope, "Yoga.Config"_s, "getPointScaleFactor"_s)); + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Config"_s, "getPointScaleFactor"_s); } float scaleFactor = YGConfigGetPointScaleFactor(thisObject->internal()); @@ -324,7 +459,7 @@ JSC_DEFINE_HOST_FUNCTION(jsYogaConfigProtoFuncSetErrata, (JSC::JSGlobalObject * auto* thisObject = jsDynamicCast(callFrame->thisValue()); if (UNLIKELY(!thisObject)) { - return JSC::JSValue::encode(Bun::throwThisTypeError(*globalObject, scope, "Yoga.Config"_s, "setErrata"_s)); + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Config"_s, "setErrata"_s); } if (callFrame->argumentCount() < 1) { @@ -346,7 +481,7 @@ JSC_DEFINE_HOST_FUNCTION(jsYogaConfigProtoFuncGetErrata, (JSC::JSGlobalObject * auto* thisObject = jsDynamicCast(callFrame->thisValue()); if (UNLIKELY(!thisObject)) { - return JSC::JSValue::encode(Bun::throwThisTypeError(*globalObject, scope, "Yoga.Config"_s, "getErrata"_s)); + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Config"_s, "getErrata"_s); } YGErrata errata = YGConfigGetErrata(thisObject->internal()); @@ -360,7 +495,7 @@ JSC_DEFINE_HOST_FUNCTION(jsYogaConfigProtoFuncFree, (JSC::JSGlobalObject * globa auto* thisObject = jsDynamicCast(callFrame->thisValue()); if (UNLIKELY(!thisObject)) { - return JSC::JSValue::encode(Bun::throwThisTypeError(*globalObject, scope, "Yoga.Config"_s, "free"_s)); + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Config"_s, "free"_s); } // Mark the config as freed by setting internal pointer to nullptr @@ -373,6 +508,80 @@ JSC_DEFINE_HOST_FUNCTION(jsYogaConfigProtoFuncFree, (JSC::JSGlobalObject * globa return JSC::JSValue::encode(JSC::jsUndefined()); } +JSC_DEFINE_HOST_FUNCTION(jsYogaConfigProtoFuncGetUseWebDefaults, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) +{ + JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + auto* thisObject = jsDynamicCast(callFrame->thisValue()); + if (UNLIKELY(!thisObject)) { + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Config"_s, "getUseWebDefaults"_s); + } + + bool useWebDefaults = YGConfigGetUseWebDefaults(thisObject->internal()); + return JSC::JSValue::encode(JSC::jsBoolean(useWebDefaults)); +} + +JSC_DEFINE_HOST_FUNCTION(jsYogaConfigProtoFuncSetContext, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) +{ + JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + auto* thisObject = jsDynamicCast(callFrame->thisValue()); + if (UNLIKELY(!thisObject)) { + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Config"_s, "setContext"_s); + } + + // For now, we don't support storing arbitrary JS values as context + // This would require proper GC handling + // TODO: Implement context storage with proper memory management + return JSC::JSValue::encode(JSC::jsUndefined()); +} + +JSC_DEFINE_HOST_FUNCTION(jsYogaConfigProtoFuncGetContext, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) +{ + JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + auto* thisObject = jsDynamicCast(callFrame->thisValue()); + if (UNLIKELY(!thisObject)) { + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Config"_s, "getContext"_s); + } + + // Return null for now since we don't support context storage yet + return JSC::JSValue::encode(JSC::jsNull()); +} + +JSC_DEFINE_HOST_FUNCTION(jsYogaConfigProtoFuncSetLogger, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) +{ + JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + auto* thisObject = jsDynamicCast(callFrame->thisValue()); + if (UNLIKELY(!thisObject)) { + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Config"_s, "setLogger"_s); + } + + // TODO: Implement logger callback support + // This requires creating a C callback that bridges to JavaScript + return JSC::JSValue::encode(JSC::jsUndefined()); +} + +JSC_DEFINE_HOST_FUNCTION(jsYogaConfigProtoFuncSetCloneNodeFunc, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) +{ + JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + auto* thisObject = jsDynamicCast(callFrame->thisValue()); + if (UNLIKELY(!thisObject)) { + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Config"_s, "setCloneNodeFunc"_s); + } + + // TODO: Implement clone node callback support + // This requires creating a C callback that bridges to JavaScript + return JSC::JSValue::encode(JSC::jsUndefined()); +} + // Node method implementations JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncReset, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) { @@ -381,7 +590,7 @@ JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncReset, (JSC::JSGlobalObject * global auto* thisObject = jsDynamicCast(callFrame->thisValue()); if (UNLIKELY(!thisObject)) { - return JSC::JSValue::encode(Bun::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "reset"_s)); + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "reset"_s); } YGNodeReset(thisObject->internal()); @@ -395,7 +604,25 @@ JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncMarkDirty, (JSC::JSGlobalObject * gl auto* thisObject = jsDynamicCast(callFrame->thisValue()); if (UNLIKELY(!thisObject)) { - return JSC::JSValue::encode(Bun::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "markDirty"_s)); + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "markDirty"_s); + } + + // Yoga only allows marking nodes dirty if they have a measure function + // Check this condition to avoid the internal assertion failure + YGNodeRef node = thisObject->internal(); + bool hasMeasureFunc = YGNodeHasMeasureFunc(node); + + if (!hasMeasureFunc) { + // Check if it's a leaf node (no children) + uint32_t childCount = YGNodeGetChildCount(node); + if (childCount > 0) { + throwTypeError(globalObject, scope, "Only leaf nodes with custom measure functions can be marked as dirty"_s); + return {}; + } + + // It's a leaf node but still needs a measure function + throwTypeError(globalObject, scope, "Only nodes with custom measure functions can be marked as dirty"_s); + return {}; } YGNodeMarkDirty(thisObject->internal()); @@ -409,7 +636,7 @@ JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncIsDirty, (JSC::JSGlobalObject * glob auto* thisObject = jsDynamicCast(callFrame->thisValue()); if (UNLIKELY(!thisObject)) { - return JSC::JSValue::encode(Bun::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "isDirty"_s)); + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "isDirty"_s); } bool isDirty = YGNodeIsDirty(thisObject->internal()); @@ -423,7 +650,7 @@ JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncCalculateLayout, (JSC::JSGlobalObjec auto* thisObject = jsDynamicCast(callFrame->thisValue()); if (UNLIKELY(!thisObject)) { - return JSC::JSValue::encode(Bun::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "calculateLayout"_s)); + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "calculateLayout"_s); } float width = YGUndefined; @@ -432,7 +659,7 @@ JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncCalculateLayout, (JSC::JSGlobalObjec // Parse arguments: calculateLayout(width?, height?, direction?) if (callFrame->argumentCount() > 0) { - JSValue widthArg = callFrame->uncheckedArgument(0); + JSC::JSValue widthArg = callFrame->uncheckedArgument(0); if (!widthArg.isUndefinedOrNull()) { width = static_cast(widthArg.toNumber(globalObject)); RETURN_IF_EXCEPTION(scope, {}); @@ -440,7 +667,7 @@ JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncCalculateLayout, (JSC::JSGlobalObjec } if (callFrame->argumentCount() > 1) { - JSValue heightArg = callFrame->uncheckedArgument(1); + JSC::JSValue heightArg = callFrame->uncheckedArgument(1); if (!heightArg.isUndefinedOrNull()) { height = static_cast(heightArg.toNumber(globalObject)); RETURN_IF_EXCEPTION(scope, {}); @@ -464,11 +691,11 @@ JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncGetComputedLayout, (JSC::JSGlobalObj auto* thisObject = jsDynamicCast(callFrame->thisValue()); if (UNLIKELY(!thisObject)) { - return JSC::JSValue::encode(Bun::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "getComputedLayout"_s)); + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "getComputedLayout"_s); } // Create object with computed layout values - JSC::JSObject* layout = JSC::constructEmptyObject(globalObject); + JSC::JSObject* layout = constructEmptyObject(globalObject); YGNodeRef node = thisObject->internal(); @@ -487,7 +714,7 @@ JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncFree, (JSC::JSGlobalObject * globalO auto* thisObject = jsDynamicCast(callFrame->thisValue()); if (UNLIKELY(!thisObject)) { - return JSC::JSValue::encode(Bun::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "free"_s)); + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "free"_s); } // Clear the internal pointer - actual cleanup in destructor @@ -507,7 +734,7 @@ JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncSetFlexDirection, (JSC::JSGlobalObje auto* thisObject = jsDynamicCast(callFrame->thisValue()); if (UNLIKELY(!thisObject)) { - return JSC::JSValue::encode(Bun::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "setFlexDirection"_s)); + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "setFlexDirection"_s); } if (callFrame->argumentCount() < 1) { @@ -528,7 +755,7 @@ JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncSetJustifyContent, (JSC::JSGlobalObj auto* thisObject = jsDynamicCast(callFrame->thisValue()); if (UNLIKELY(!thisObject)) { - return JSC::JSValue::encode(Bun::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "setJustifyContent"_s)); + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "setJustifyContent"_s); } if (callFrame->argumentCount() < 1) { @@ -549,7 +776,7 @@ JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncSetAlignItems, (JSC::JSGlobalObject auto* thisObject = jsDynamicCast(callFrame->thisValue()); if (UNLIKELY(!thisObject)) { - return JSC::JSValue::encode(Bun::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "setAlignItems"_s)); + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "setAlignItems"_s); } if (callFrame->argumentCount() < 1) { @@ -570,7 +797,7 @@ JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncSetAlignSelf, (JSC::JSGlobalObject * auto* thisObject = jsDynamicCast(callFrame->thisValue()); if (UNLIKELY(!thisObject)) { - return JSC::JSValue::encode(Bun::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "setAlignSelf"_s)); + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "setAlignSelf"_s); } if (callFrame->argumentCount() < 1) { @@ -591,7 +818,7 @@ JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncSetAlignContent, (JSC::JSGlobalObjec auto* thisObject = jsDynamicCast(callFrame->thisValue()); if (UNLIKELY(!thisObject)) { - return JSC::JSValue::encode(Bun::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "setAlignContent"_s)); + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "setAlignContent"_s); } if (callFrame->argumentCount() < 1) { @@ -612,7 +839,7 @@ JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncSetFlexWrap, (JSC::JSGlobalObject * auto* thisObject = jsDynamicCast(callFrame->thisValue()); if (UNLIKELY(!thisObject)) { - return JSC::JSValue::encode(Bun::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "setFlexWrap"_s)); + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "setFlexWrap"_s); } if (callFrame->argumentCount() < 1) { @@ -633,7 +860,7 @@ JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncSetPositionType, (JSC::JSGlobalObjec auto* thisObject = jsDynamicCast(callFrame->thisValue()); if (UNLIKELY(!thisObject)) { - return JSC::JSValue::encode(Bun::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "setPositionType"_s)); + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "setPositionType"_s); } if (callFrame->argumentCount() < 1) { @@ -654,7 +881,7 @@ JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncSetDisplay, (JSC::JSGlobalObject * g auto* thisObject = jsDynamicCast(callFrame->thisValue()); if (UNLIKELY(!thisObject)) { - return JSC::JSValue::encode(Bun::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "setDisplay"_s)); + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "setDisplay"_s); } if (callFrame->argumentCount() < 1) { @@ -675,7 +902,7 @@ JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncSetOverflow, (JSC::JSGlobalObject * auto* thisObject = jsDynamicCast(callFrame->thisValue()); if (UNLIKELY(!thisObject)) { - return JSC::JSValue::encode(Bun::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "setOverflow"_s)); + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "setOverflow"_s); } if (callFrame->argumentCount() < 1) { @@ -697,7 +924,7 @@ JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncSetFlex, (JSC::JSGlobalObject * glob auto* thisObject = jsDynamicCast(callFrame->thisValue()); if (UNLIKELY(!thisObject)) { - return JSC::JSValue::encode(Bun::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "setFlex"_s)); + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "setFlex"_s); } if (callFrame->argumentCount() < 1) { @@ -718,7 +945,7 @@ JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncSetFlexGrow, (JSC::JSGlobalObject * auto* thisObject = jsDynamicCast(callFrame->thisValue()); if (UNLIKELY(!thisObject)) { - return JSC::JSValue::encode(Bun::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "setFlexGrow"_s)); + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "setFlexGrow"_s); } if (callFrame->argumentCount() < 1) { @@ -739,7 +966,7 @@ JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncSetFlexShrink, (JSC::JSGlobalObject auto* thisObject = jsDynamicCast(callFrame->thisValue()); if (UNLIKELY(!thisObject)) { - return JSC::JSValue::encode(Bun::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "setFlexShrink"_s)); + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "setFlexShrink"_s); } if (callFrame->argumentCount() < 1) { @@ -760,14 +987,14 @@ JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncSetAspectRatio, (JSC::JSGlobalObject auto* thisObject = jsDynamicCast(callFrame->thisValue()); if (UNLIKELY(!thisObject)) { - return JSC::JSValue::encode(Bun::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "setAspectRatio"_s)); + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "setAspectRatio"_s); } if (callFrame->argumentCount() < 1) { return JSC::JSValue::encode(JSC::jsUndefined()); } - JSValue arg = callFrame->uncheckedArgument(0); + JSC::JSValue arg = callFrame->uncheckedArgument(0); if (arg.isUndefinedOrNull()) { YGNodeStyleSetAspectRatio(thisObject->internal(), YGUndefined); @@ -788,7 +1015,7 @@ JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncRemoveChild, (JSC::JSGlobalObject * auto* thisObject = jsDynamicCast(callFrame->thisValue()); if (UNLIKELY(!thisObject)) { - return JSC::JSValue::encode(Bun::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "removeChild"_s)); + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "removeChild"_s); } if (callFrame->argumentCount() < 1) { @@ -813,13 +1040,64 @@ JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncGetChildCount, (JSC::JSGlobalObject auto* thisObject = jsDynamicCast(callFrame->thisValue()); if (UNLIKELY(!thisObject)) { - return JSC::JSValue::encode(Bun::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "getChildCount"_s)); + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "getChildCount"_s); } uint32_t count = YGNodeGetChildCount(thisObject->internal()); return JSC::JSValue::encode(JSC::jsNumber(count)); } +// Measure function callback +static YGSize bunMeasureCallback( + YGNodeConstRef ygNode, + float width, + YGMeasureMode widthMode, + float height, + YGMeasureMode heightMode) +{ + JSYogaNode* jsNode = JSYogaNode::fromYGNode(const_cast(ygNode)); + if (!jsNode || !jsNode->m_measureFunc) { + return {0, 0}; + } + + JSC::JSGlobalObject* globalObject = jsNode->globalObject(); + JSC::VM& vm = globalObject->vm(); + JSC::JSLockHolder lock(vm); + auto scope = DECLARE_CATCH_SCOPE(vm); + + // Create arguments object + JSC::JSObject* argsObj = JSC::constructEmptyObject(globalObject); + argsObj->putDirect(vm, JSC::Identifier::fromString(vm, "width"_s), JSC::jsNumber(width)); + argsObj->putDirect(vm, JSC::Identifier::fromString(vm, "widthMode"_s), JSC::jsNumber(static_cast(widthMode))); + argsObj->putDirect(vm, JSC::Identifier::fromString(vm, "height"_s), JSC::jsNumber(height)); + argsObj->putDirect(vm, JSC::Identifier::fromString(vm, "heightMode"_s), JSC::jsNumber(static_cast(heightMode))); + + JSC::MarkedArgumentBuffer args; + args.append(argsObj); + + JSC::CallData callData = JSC::getCallData(jsNode->m_measureFunc.get()); + JSC::JSValue result = JSC::call(globalObject, jsNode->m_measureFunc.get(), callData, jsNode, args); + + if (scope.exception()) { + scope.clearException(); + return {0, 0}; + } + + // Extract width and height from result + if (result.isObject()) { + JSC::JSObject* resultObj = result.getObject(); + JSC::JSValue widthValue = resultObj->get(globalObject, JSC::Identifier::fromString(vm, "width"_s)); + JSC::JSValue heightValue = resultObj->get(globalObject, JSC::Identifier::fromString(vm, "height"_s)); + + float measuredWidth = widthValue.isNumber() ? static_cast(widthValue.toNumber(globalObject)) : 0; + float measuredHeight = heightValue.isNumber() ? static_cast(heightValue.toNumber(globalObject)) : 0; + + return {measuredWidth, measuredHeight}; + } + + return {0, 0}; +} + // Dirtied function callback static void bunDirtiedCallback(YGNodeConstRef ygNode) { @@ -832,7 +1110,8 @@ static void bunDirtiedCallback(YGNodeConstRef ygNode) auto scope = DECLARE_CATCH_SCOPE(vm); JSC::MarkedArgumentBuffer args; - JSC::call(globalObject, jsNode->m_dirtiedFunc.get(), jsNode, args); + JSC::CallData callData = JSC::getCallData(jsNode->m_dirtiedFunc.get()); + JSC::call(globalObject, jsNode->m_dirtiedFunc.get(), callData, jsNode, args); if (scope.exception()) { scope.clearException(); } @@ -845,14 +1124,14 @@ JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncSetDirtiedFunc, (JSC::JSGlobalObject auto* thisObject = jsDynamicCast(callFrame->thisValue()); if (UNLIKELY(!thisObject)) { - return JSC::JSValue::encode(Bun::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "setDirtiedFunc"_s)); + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "setDirtiedFunc"_s); } if (callFrame->argumentCount() < 1) { return JSC::JSValue::encode(JSC::jsUndefined()); } - JSValue func = callFrame->uncheckedArgument(0); + JSC::JSValue func = callFrame->uncheckedArgument(0); if (func.isUndefinedOrNull()) { thisObject->m_dirtiedFunc.clear(); YGNodeSetDirtiedFunc(thisObject->internal(), nullptr); @@ -867,4 +1146,1320 @@ JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncSetDirtiedFunc, (JSC::JSGlobalObject return JSC::JSValue::encode(JSC::jsUndefined()); } +// Node method implementations - only missing functions that don't already exist +JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncSetWidth, (JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame)) +{ + JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + auto* thisObject = jsDynamicCast(callFrame->thisValue()); + if (UNLIKELY(!thisObject)) { + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "setWidth"_s); + } + + if (callFrame->argumentCount() < 1) { + YGNodeStyleSetWidthAuto(thisObject->internal()); + return JSC::JSValue::encode(JSC::jsUndefined()); + } + + JSC::JSValue arg = callFrame->uncheckedArgument(0); + + if (arg.isUndefinedOrNull()) { + YGNodeStyleSetWidthAuto(thisObject->internal()); + } else if (arg.isNumber()) { + YGNodeStyleSetWidth(thisObject->internal(), arg.asNumber()); + } else if (arg.isString()) { + String str = arg.toWTFString(globalObject); + RETURN_IF_EXCEPTION(scope, {}); + + if (str == "auto"_s) { + YGNodeStyleSetWidthAuto(thisObject->internal()); + } else if (str.endsWith('%')) { + String percentStr = str.substring(0, str.length() - 1); + double percent = percentStr.toDouble(); + YGNodeStyleSetWidthPercent(thisObject->internal(), percent); + } else { + throwTypeError(globalObject, scope, "Invalid width value"_s); + return {}; + } + } else if (arg.isObject()) { + JSC::JSObject* obj = arg.getObject(); + JSC::JSValue unitValue = obj->get(globalObject, JSC::Identifier::fromString(vm, "unit"_s)); + JSC::JSValue valueValue = obj->get(globalObject, JSC::Identifier::fromString(vm, "value"_s)); + RETURN_IF_EXCEPTION(scope, {}); + + if (!unitValue.isNumber() || !valueValue.isNumber()) { + throwTypeError(globalObject, scope, "Width object must have numeric 'unit' and 'value' properties"_s); + return {}; + } + + int unit = unitValue.toInt32(globalObject); + float value = valueValue.toNumber(globalObject); + RETURN_IF_EXCEPTION(scope, {}); + + switch (unit) { + case YGUnitPoint: + YGNodeStyleSetWidth(thisObject->internal(), value); + break; + case YGUnitPercent: + YGNodeStyleSetWidthPercent(thisObject->internal(), value); + break; + case YGUnitAuto: + YGNodeStyleSetWidthAuto(thisObject->internal()); + break; + default: + throwTypeError(globalObject, scope, "Invalid unit value"_s); + return {}; + } + } else { + throwTypeError(globalObject, scope, "Width must be a number, string, object, null, or undefined"_s); + return {}; + } + + return JSC::JSValue::encode(JSC::jsUndefined()); +} + +JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncSetHeight, (JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame)) +{ + return JSC::JSValue::encode(JSC::jsUndefined()); +} + +JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncSetMinWidth, (JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame)) +{ + return JSC::JSValue::encode(JSC::jsUndefined()); +} + +JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncSetMinHeight, (JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame)) +{ + return JSC::JSValue::encode(JSC::jsUndefined()); +} + +JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncSetMaxWidth, (JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame)) +{ + return JSC::JSValue::encode(JSC::jsUndefined()); +} + +JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncSetMaxHeight, (JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame)) +{ + return JSC::JSValue::encode(JSC::jsUndefined()); +} + +JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncSetFlexBasis, (JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame)) +{ + return JSC::JSValue::encode(JSC::jsUndefined()); +} + +JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncSetMargin, (JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame)) +{ + JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + auto* thisObject = jsDynamicCast(callFrame->thisValue()); + if (UNLIKELY(!thisObject)) { + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "setMargin"_s); + } + + if (callFrame->argumentCount() < 2) { + throwTypeError(globalObject, scope, "setMargin requires 2 arguments (edge, value)"_s); + return {}; + } + + int edge = callFrame->uncheckedArgument(0).toInt32(globalObject); + RETURN_IF_EXCEPTION(scope, {}); + + JSC::JSValue valueArg = callFrame->uncheckedArgument(1); + + if (valueArg.isNumber()) { + float value = static_cast(valueArg.toNumber(globalObject)); + RETURN_IF_EXCEPTION(scope, {}); + YGNodeStyleSetMargin(thisObject->internal(), static_cast(edge), value); + } else if (valueArg.isString()) { + WTF::String str = valueArg.toString(globalObject)->value(globalObject); + RETURN_IF_EXCEPTION(scope, {}); + + if (str == "auto"_s) { + YGNodeStyleSetMarginAuto(thisObject->internal(), static_cast(edge)); + } else if (str.endsWith('%')) { + float percent = str.substring(0, str.length() - 1).toFloat(); + YGNodeStyleSetMarginPercent(thisObject->internal(), static_cast(edge), percent); + } else { + float value = str.toFloat(); + YGNodeStyleSetMargin(thisObject->internal(), static_cast(edge), value); + } + } else if (valueArg.isObject()) { + // Handle { unit, value } object + JSC::JSObject* obj = valueArg.getObject(); + JSC::JSValue unitValue = obj->get(globalObject, JSC::Identifier::fromString(vm, "unit"_s)); + JSC::JSValue value = obj->get(globalObject, JSC::Identifier::fromString(vm, "value"_s)); + RETURN_IF_EXCEPTION(scope, {}); + + int unit = unitValue.toInt32(globalObject); + RETURN_IF_EXCEPTION(scope, {}); + float val = static_cast(value.toNumber(globalObject)); + RETURN_IF_EXCEPTION(scope, {}); + + switch (static_cast(unit)) { + case YGUnitPercent: + YGNodeStyleSetMarginPercent(thisObject->internal(), static_cast(edge), val); + break; + case YGUnitAuto: + YGNodeStyleSetMarginAuto(thisObject->internal(), static_cast(edge)); + break; + default: + YGNodeStyleSetMargin(thisObject->internal(), static_cast(edge), val); + break; + } + } else { + throwTypeError(globalObject, scope, "setMargin value must be a number, string, or { unit, value } object"_s); + return {}; + } + + return JSC::JSValue::encode(JSC::jsUndefined()); +} + +JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncSetPadding, (JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame)) +{ + JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + auto* thisObject = jsDynamicCast(callFrame->thisValue()); + if (UNLIKELY(!thisObject)) { + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "setPadding"_s); + } + + if (callFrame->argumentCount() < 2) { + throwTypeError(globalObject, scope, "setPadding requires 2 arguments (edge, value)"_s); + return {}; + } + + int edge = callFrame->uncheckedArgument(0).toInt32(globalObject); + RETURN_IF_EXCEPTION(scope, {}); + + JSC::JSValue valueArg = callFrame->uncheckedArgument(1); + + if (valueArg.isNumber()) { + float value = static_cast(valueArg.toNumber(globalObject)); + RETURN_IF_EXCEPTION(scope, {}); + YGNodeStyleSetPadding(thisObject->internal(), static_cast(edge), value); + } else if (valueArg.isString()) { + WTF::String str = valueArg.toString(globalObject)->value(globalObject); + RETURN_IF_EXCEPTION(scope, {}); + + if (str.endsWith('%')) { + float percent = str.substring(0, str.length() - 1).toFloat(); + YGNodeStyleSetPaddingPercent(thisObject->internal(), static_cast(edge), percent); + } else { + float value = str.toFloat(); + YGNodeStyleSetPadding(thisObject->internal(), static_cast(edge), value); + } + } else if (valueArg.isObject()) { + // Handle { unit, value } object + JSC::JSObject* obj = valueArg.getObject(); + JSC::JSValue unitValue = obj->get(globalObject, JSC::Identifier::fromString(vm, "unit"_s)); + JSC::JSValue value = obj->get(globalObject, JSC::Identifier::fromString(vm, "value"_s)); + RETURN_IF_EXCEPTION(scope, {}); + + int unit = unitValue.toInt32(globalObject); + RETURN_IF_EXCEPTION(scope, {}); + float val = static_cast(value.toNumber(globalObject)); + RETURN_IF_EXCEPTION(scope, {}); + + if (static_cast(unit) == YGUnitPercent) { + YGNodeStyleSetPaddingPercent(thisObject->internal(), static_cast(edge), val); + } else { + YGNodeStyleSetPadding(thisObject->internal(), static_cast(edge), val); + } + } else { + throwTypeError(globalObject, scope, "setPadding value must be a number, string, or { unit, value } object"_s); + return {}; + } + + return JSC::JSValue::encode(JSC::jsUndefined()); +} + +JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncSetPosition, (JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame)) +{ + JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + auto* thisObject = jsDynamicCast(callFrame->thisValue()); + if (UNLIKELY(!thisObject)) { + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "setPosition"_s); + } + + if (callFrame->argumentCount() < 2) { + throwTypeError(globalObject, scope, "setPosition requires 2 arguments (edge, value)"_s); + return {}; + } + + int edge = callFrame->uncheckedArgument(0).toInt32(globalObject); + RETURN_IF_EXCEPTION(scope, {}); + + JSC::JSValue valueArg = callFrame->uncheckedArgument(1); + + if (valueArg.isNumber()) { + float value = static_cast(valueArg.toNumber(globalObject)); + RETURN_IF_EXCEPTION(scope, {}); + YGNodeStyleSetPosition(thisObject->internal(), static_cast(edge), value); + } else if (valueArg.isString()) { + WTF::String str = valueArg.toString(globalObject)->value(globalObject); + RETURN_IF_EXCEPTION(scope, {}); + + if (str.endsWith('%')) { + float percent = str.substring(0, str.length() - 1).toFloat(); + YGNodeStyleSetPositionPercent(thisObject->internal(), static_cast(edge), percent); + } else { + float value = str.toFloat(); + YGNodeStyleSetPosition(thisObject->internal(), static_cast(edge), value); + } + } else if (valueArg.isObject()) { + // Handle { unit, value } object + JSC::JSObject* obj = valueArg.getObject(); + JSC::JSValue unitValue = obj->get(globalObject, JSC::Identifier::fromString(vm, "unit"_s)); + JSC::JSValue value = obj->get(globalObject, JSC::Identifier::fromString(vm, "value"_s)); + RETURN_IF_EXCEPTION(scope, {}); + + int unit = unitValue.toInt32(globalObject); + RETURN_IF_EXCEPTION(scope, {}); + float val = static_cast(value.toNumber(globalObject)); + RETURN_IF_EXCEPTION(scope, {}); + + if (static_cast(unit) == YGUnitPercent) { + YGNodeStyleSetPositionPercent(thisObject->internal(), static_cast(edge), val); + } else { + YGNodeStyleSetPosition(thisObject->internal(), static_cast(edge), val); + } + } else { + throwTypeError(globalObject, scope, "setPosition value must be a number, string, or { unit, value } object"_s); + return {}; + } + + return JSC::JSValue::encode(JSC::jsUndefined()); +} + +JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncSetGap, (JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame)) +{ + return JSC::JSValue::encode(JSC::jsUndefined()); +} + +JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncGetWidth, (JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame)) +{ + JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + auto* thisObject = jsDynamicCast(callFrame->thisValue()); + if (UNLIKELY(!thisObject)) { + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "getWidth"_s); + } + + YGValue value = YGNodeStyleGetWidth(thisObject->internal()); + + JSC::JSObject* result = JSC::constructEmptyObject(globalObject); + result->putDirect(vm, JSC::Identifier::fromString(vm, "value"_s), JSC::jsNumber(value.value)); + result->putDirect(vm, JSC::Identifier::fromString(vm, "unit"_s), JSC::jsNumber(static_cast(value.unit))); + + return JSC::JSValue::encode(result); +} + +JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncGetHeight, (JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame)) +{ + return JSC::JSValue::encode(JSC::jsUndefined()); +} + +JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncGetMinWidth, (JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame)) +{ + return JSC::JSValue::encode(JSC::jsUndefined()); +} + +JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncGetMinHeight, (JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame)) +{ + return JSC::JSValue::encode(JSC::jsUndefined()); +} + +JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncGetMaxWidth, (JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame)) +{ + return JSC::JSValue::encode(JSC::jsUndefined()); +} + +JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncGetMaxHeight, (JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame)) +{ + return JSC::JSValue::encode(JSC::jsUndefined()); +} + +JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncGetFlexBasis, (JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame)) +{ + return JSC::JSValue::encode(JSC::jsUndefined()); +} + +JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncGetMargin, (JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame)) +{ + JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + auto* thisObject = jsDynamicCast(callFrame->thisValue()); + if (UNLIKELY(!thisObject)) { + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "getMargin"_s); + } + + if (callFrame->argumentCount() < 1) { + throwTypeError(globalObject, scope, "getMargin requires 1 argument (edge)"_s); + return {}; + } + + int edge = callFrame->uncheckedArgument(0).toInt32(globalObject); + RETURN_IF_EXCEPTION(scope, {}); + + YGValue value = YGNodeStyleGetMargin(thisObject->internal(), static_cast(edge)); + + // Create the return object { unit, value } + auto* result = JSC::constructEmptyObject(globalObject); + result->putDirect(vm, JSC::Identifier::fromString(vm, "unit"_s), JSC::jsNumber(static_cast(value.unit))); + result->putDirect(vm, JSC::Identifier::fromString(vm, "value"_s), JSC::jsNumber(value.value)); + + return JSC::JSValue::encode(result); +} + +JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncGetPadding, (JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame)) +{ + JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + auto* thisObject = jsDynamicCast(callFrame->thisValue()); + if (UNLIKELY(!thisObject)) { + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "getPadding"_s); + } + + if (callFrame->argumentCount() < 1) { + throwTypeError(globalObject, scope, "getPadding requires 1 argument (edge)"_s); + return {}; + } + + int edge = callFrame->uncheckedArgument(0).toInt32(globalObject); + RETURN_IF_EXCEPTION(scope, {}); + + YGValue value = YGNodeStyleGetPadding(thisObject->internal(), static_cast(edge)); + + // Create the return object { unit, value } + auto* result = JSC::constructEmptyObject(globalObject); + result->putDirect(vm, JSC::Identifier::fromString(vm, "unit"_s), JSC::jsNumber(static_cast(value.unit))); + result->putDirect(vm, JSC::Identifier::fromString(vm, "value"_s), JSC::jsNumber(value.value)); + + return JSC::JSValue::encode(result); +} + +JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncGetPosition, (JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame)) +{ + JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + auto* thisObject = jsDynamicCast(callFrame->thisValue()); + if (UNLIKELY(!thisObject)) { + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "getPosition"_s); + } + + if (callFrame->argumentCount() < 1) { + throwTypeError(globalObject, scope, "getPosition requires 1 argument (edge)"_s); + return {}; + } + + int edge = callFrame->uncheckedArgument(0).toInt32(globalObject); + RETURN_IF_EXCEPTION(scope, {}); + + YGValue value = YGNodeStyleGetPosition(thisObject->internal(), static_cast(edge)); + + // Create the return object { unit, value } + auto* result = JSC::constructEmptyObject(globalObject); + result->putDirect(vm, JSC::Identifier::fromString(vm, "unit"_s), JSC::jsNumber(static_cast(value.unit))); + result->putDirect(vm, JSC::Identifier::fromString(vm, "value"_s), JSC::jsNumber(value.value)); + + return JSC::JSValue::encode(result); +} + +JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncInsertChild, (JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame)) +{ + JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + auto* thisObject = jsDynamicCast(callFrame->thisValue()); + if (UNLIKELY(!thisObject)) { + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "insertChild"_s); + } + + if (callFrame->argumentCount() < 2) { + throwTypeError(globalObject, scope, "insertChild requires 2 arguments (child, index)"_s); + return {}; + } + + auto* child = jsDynamicCast(callFrame->uncheckedArgument(0)); + if (!child) { + throwTypeError(globalObject, scope, "First argument must be a Yoga.Node instance"_s); + return {}; + } + + int index = callFrame->uncheckedArgument(1).toInt32(globalObject); + RETURN_IF_EXCEPTION(scope, {}); + + YGNodeInsertChild(thisObject->internal(), child->internal(), index); + return JSC::JSValue::encode(JSC::jsUndefined()); +} + +JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncGetChild, (JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame)) +{ + JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + auto* thisObject = jsDynamicCast(callFrame->thisValue()); + if (UNLIKELY(!thisObject)) { + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "getChild"_s); + } + + if (callFrame->argumentCount() < 1) { + throwTypeError(globalObject, scope, "getChild requires 1 argument (index)"_s); + return {}; + } + + int index = callFrame->uncheckedArgument(0).toInt32(globalObject); + RETURN_IF_EXCEPTION(scope, {}); + + YGNodeRef childYGNode = YGNodeGetChild(thisObject->internal(), index); + if (!childYGNode) { + return JSC::JSValue::encode(JSC::jsNull()); + } + + // Get the JSYogaNode wrapper from the context + JSYogaNode* childJSNode = JSYogaNode::fromYGNode(childYGNode); + if (!childJSNode) { + return JSC::JSValue::encode(JSC::jsNull()); + } + + return JSC::JSValue::encode(childJSNode); +} + +JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncGetParent, (JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame)) +{ + JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + auto* thisObject = jsDynamicCast(callFrame->thisValue()); + if (UNLIKELY(!thisObject)) { + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "getParent"_s); + } + + YGNodeRef parentYGNode = YGNodeGetParent(thisObject->internal()); + if (!parentYGNode) { + return JSC::JSValue::encode(JSC::jsNull()); + } + + // Get the JSYogaNode wrapper from the context + JSYogaNode* parentJSNode = JSYogaNode::fromYGNode(parentYGNode); + if (!parentJSNode) { + return JSC::JSValue::encode(JSC::jsNull()); + } + + return JSC::JSValue::encode(parentJSNode); +} + +JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncSetMeasureFunc, (JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame)) +{ + JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + auto* thisObject = jsDynamicCast(callFrame->thisValue()); + if (UNLIKELY(!thisObject)) { + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "setMeasureFunc"_s); + } + + if (callFrame->argumentCount() < 1) { + return JSC::JSValue::encode(JSC::jsUndefined()); + } + + JSC::JSValue funcArg = callFrame->uncheckedArgument(0); + + if (funcArg.isNull() || funcArg.isUndefined()) { + // Clear the measure function + thisObject->m_measureFunc.clear(); + YGNodeSetMeasureFunc(thisObject->internal(), nullptr); + } else if (funcArg.isCallable()) { + // Set the measure function + thisObject->m_measureFunc.set(vm, thisObject, funcArg.getObject()); + YGNodeSetMeasureFunc(thisObject->internal(), bunMeasureCallback); + } else { + throwTypeError(globalObject, scope, "Measure function must be a function or null"_s); + return {}; + } + + return JSC::JSValue::encode(JSC::jsUndefined()); +} + +// Style setter implementations +JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncSetDirection, (JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame)) +{ + JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + auto* thisObject = jsDynamicCast(callFrame->thisValue()); + if (UNLIKELY(!thisObject)) { + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "setDirection"_s); + } + + if (callFrame->argumentCount() < 1) { + return JSC::JSValue::encode(JSC::jsUndefined()); + } + + int32_t direction = callFrame->uncheckedArgument(0).toInt32(globalObject); + RETURN_IF_EXCEPTION(scope, {}); + + YGNodeStyleSetDirection(thisObject->internal(), static_cast(direction)); + return JSC::JSValue::encode(JSC::jsUndefined()); +} + +JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncSetBorder, (JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame)) +{ + JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + auto* thisObject = jsDynamicCast(callFrame->thisValue()); + if (UNLIKELY(!thisObject)) { + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "setBorder"_s); + } + + if (callFrame->argumentCount() < 2) { + throwTypeError(globalObject, scope, "setBorder requires 2 arguments (edge, value)"_s); + return {}; + } + + int edge = callFrame->uncheckedArgument(0).toInt32(globalObject); + RETURN_IF_EXCEPTION(scope, {}); + + float value = static_cast(callFrame->uncheckedArgument(1).toNumber(globalObject)); + RETURN_IF_EXCEPTION(scope, {}); + + YGNodeStyleSetBorder(thisObject->internal(), static_cast(edge), value); + return JSC::JSValue::encode(JSC::jsUndefined()); +} + +JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncSetBoxSizing, (JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame)) +{ + JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + auto* thisObject = jsDynamicCast(callFrame->thisValue()); + if (UNLIKELY(!thisObject)) { + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "setBoxSizing"_s); + } + + if (callFrame->argumentCount() < 1) { + return JSC::JSValue::encode(JSC::jsUndefined()); + } + + int32_t boxSizing = callFrame->uncheckedArgument(0).toInt32(globalObject); + RETURN_IF_EXCEPTION(scope, {}); + + YGNodeStyleSetBoxSizing(thisObject->internal(), static_cast(boxSizing)); + return JSC::JSValue::encode(JSC::jsUndefined()); +} + +// Style getter implementations +JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncGetDirection, (JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame)) +{ + JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + auto* thisObject = jsDynamicCast(callFrame->thisValue()); + if (UNLIKELY(!thisObject)) { + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "getDirection"_s); + } + + YGDirection direction = YGNodeStyleGetDirection(thisObject->internal()); + return JSC::JSValue::encode(JSC::jsNumber(static_cast(direction))); +} + +JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncGetFlexDirection, (JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame)) +{ + JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + auto* thisObject = jsDynamicCast(callFrame->thisValue()); + if (UNLIKELY(!thisObject)) { + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "getFlexDirection"_s); + } + + YGFlexDirection flexDirection = YGNodeStyleGetFlexDirection(thisObject->internal()); + return JSC::JSValue::encode(JSC::jsNumber(static_cast(flexDirection))); +} + +JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncGetJustifyContent, (JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame)) +{ + JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + auto* thisObject = jsDynamicCast(callFrame->thisValue()); + if (UNLIKELY(!thisObject)) { + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "getJustifyContent"_s); + } + + YGJustify justifyContent = YGNodeStyleGetJustifyContent(thisObject->internal()); + return JSC::JSValue::encode(JSC::jsNumber(static_cast(justifyContent))); +} + +JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncGetAlignContent, (JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame)) +{ + JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + auto* thisObject = jsDynamicCast(callFrame->thisValue()); + if (UNLIKELY(!thisObject)) { + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "getAlignContent"_s); + } + + YGAlign alignContent = YGNodeStyleGetAlignContent(thisObject->internal()); + return JSC::JSValue::encode(JSC::jsNumber(static_cast(alignContent))); +} + +JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncGetAlignItems, (JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame)) +{ + JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + auto* thisObject = jsDynamicCast(callFrame->thisValue()); + if (UNLIKELY(!thisObject)) { + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "getAlignItems"_s); + } + + YGAlign alignItems = YGNodeStyleGetAlignItems(thisObject->internal()); + return JSC::JSValue::encode(JSC::jsNumber(static_cast(alignItems))); +} + +JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncGetAlignSelf, (JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame)) +{ + JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + auto* thisObject = jsDynamicCast(callFrame->thisValue()); + if (UNLIKELY(!thisObject)) { + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "getAlignSelf"_s); + } + + YGAlign alignSelf = YGNodeStyleGetAlignSelf(thisObject->internal()); + return JSC::JSValue::encode(JSC::jsNumber(static_cast(alignSelf))); +} + +JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncGetPositionType, (JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame)) +{ + JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + auto* thisObject = jsDynamicCast(callFrame->thisValue()); + if (UNLIKELY(!thisObject)) { + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "getPositionType"_s); + } + + YGPositionType positionType = YGNodeStyleGetPositionType(thisObject->internal()); + return JSC::JSValue::encode(JSC::jsNumber(static_cast(positionType))); +} + +JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncGetFlexWrap, (JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame)) +{ + JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + auto* thisObject = jsDynamicCast(callFrame->thisValue()); + if (UNLIKELY(!thisObject)) { + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "getFlexWrap"_s); + } + + YGWrap flexWrap = YGNodeStyleGetFlexWrap(thisObject->internal()); + return JSC::JSValue::encode(JSC::jsNumber(static_cast(flexWrap))); +} + +JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncGetOverflow, (JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame)) +{ + JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + auto* thisObject = jsDynamicCast(callFrame->thisValue()); + if (UNLIKELY(!thisObject)) { + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "getOverflow"_s); + } + + YGOverflow overflow = YGNodeStyleGetOverflow(thisObject->internal()); + return JSC::JSValue::encode(JSC::jsNumber(static_cast(overflow))); +} + +JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncGetDisplay, (JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame)) +{ + JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + auto* thisObject = jsDynamicCast(callFrame->thisValue()); + if (UNLIKELY(!thisObject)) { + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "getDisplay"_s); + } + + YGDisplay display = YGNodeStyleGetDisplay(thisObject->internal()); + return JSC::JSValue::encode(JSC::jsNumber(static_cast(display))); +} + +JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncGetFlex, (JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame)) +{ + JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + auto* thisObject = jsDynamicCast(callFrame->thisValue()); + if (UNLIKELY(!thisObject)) { + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "getFlex"_s); + } + + float flex = YGNodeStyleGetFlex(thisObject->internal()); + return JSC::JSValue::encode(JSC::jsNumber(flex)); +} + +JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncGetFlexGrow, (JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame)) +{ + JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + auto* thisObject = jsDynamicCast(callFrame->thisValue()); + if (UNLIKELY(!thisObject)) { + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "getFlexGrow"_s); + } + + float flexGrow = YGNodeStyleGetFlexGrow(thisObject->internal()); + return JSC::JSValue::encode(JSC::jsNumber(flexGrow)); +} + +JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncGetFlexShrink, (JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame)) +{ + JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + auto* thisObject = jsDynamicCast(callFrame->thisValue()); + if (UNLIKELY(!thisObject)) { + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "getFlexShrink"_s); + } + + float flexShrink = YGNodeStyleGetFlexShrink(thisObject->internal()); + return JSC::JSValue::encode(JSC::jsNumber(flexShrink)); +} + +JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncGetAspectRatio, (JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame)) +{ + JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + auto* thisObject = jsDynamicCast(callFrame->thisValue()); + if (UNLIKELY(!thisObject)) { + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "getAspectRatio"_s); + } + + float aspectRatio = YGNodeStyleGetAspectRatio(thisObject->internal()); + return JSC::JSValue::encode(JSC::jsNumber(aspectRatio)); +} + +JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncGetGap, (JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame)) +{ + JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + auto* thisObject = jsDynamicCast(callFrame->thisValue()); + if (UNLIKELY(!thisObject)) { + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "getGap"_s); + } + + if (callFrame->argumentCount() < 1) { + throwTypeError(globalObject, scope, "getGap requires 1 argument (gutter)"_s); + return {}; + } + + int gutter = callFrame->uncheckedArgument(0).toInt32(globalObject); + RETURN_IF_EXCEPTION(scope, {}); + + YGValue gap = YGNodeStyleGetGap(thisObject->internal(), static_cast(gutter)); + return JSC::JSValue::encode(JSC::jsNumber(gap.value)); +} + +JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncGetBorder, (JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame)) +{ + JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + auto* thisObject = jsDynamicCast(callFrame->thisValue()); + if (UNLIKELY(!thisObject)) { + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "getBorder"_s); + } + + if (callFrame->argumentCount() < 1) { + throwTypeError(globalObject, scope, "getBorder requires 1 argument (edge)"_s); + return {}; + } + + int edge = callFrame->uncheckedArgument(0).toInt32(globalObject); + RETURN_IF_EXCEPTION(scope, {}); + + float border = YGNodeStyleGetBorder(thisObject->internal(), static_cast(edge)); + return JSC::JSValue::encode(JSC::jsNumber(border)); +} + +JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncGetBoxSizing, (JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame)) +{ + JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + auto* thisObject = jsDynamicCast(callFrame->thisValue()); + if (UNLIKELY(!thisObject)) { + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "getBoxSizing"_s); + } + + YGBoxSizing boxSizing = YGNodeStyleGetBoxSizing(thisObject->internal()); + return JSC::JSValue::encode(JSC::jsNumber(static_cast(boxSizing))); +} + +// Layout getter implementations +JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncGetComputedLeft, (JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame)) +{ + JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + auto* thisObject = jsDynamicCast(callFrame->thisValue()); + if (UNLIKELY(!thisObject)) { + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "getComputedLeft"_s); + } + + float left = YGNodeLayoutGetLeft(thisObject->internal()); + return JSC::JSValue::encode(JSC::jsNumber(left)); +} + +JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncGetComputedTop, (JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame)) +{ + JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + auto* thisObject = jsDynamicCast(callFrame->thisValue()); + if (UNLIKELY(!thisObject)) { + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "getComputedTop"_s); + } + + float top = YGNodeLayoutGetTop(thisObject->internal()); + return JSC::JSValue::encode(JSC::jsNumber(top)); +} + +JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncGetComputedRight, (JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame)) +{ + JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + auto* thisObject = jsDynamicCast(callFrame->thisValue()); + if (UNLIKELY(!thisObject)) { + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "getComputedRight"_s); + } + + float right = YGNodeLayoutGetRight(thisObject->internal()); + return JSC::JSValue::encode(JSC::jsNumber(right)); +} + +JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncGetComputedBottom, (JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame)) +{ + JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + auto* thisObject = jsDynamicCast(callFrame->thisValue()); + if (UNLIKELY(!thisObject)) { + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "getComputedBottom"_s); + } + + float bottom = YGNodeLayoutGetBottom(thisObject->internal()); + return JSC::JSValue::encode(JSC::jsNumber(bottom)); +} + +JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncGetComputedWidth, (JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame)) +{ + JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + auto* thisObject = jsDynamicCast(callFrame->thisValue()); + if (UNLIKELY(!thisObject)) { + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "getComputedWidth"_s); + } + + float width = YGNodeLayoutGetWidth(thisObject->internal()); + return JSC::JSValue::encode(JSC::jsNumber(width)); +} + +JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncGetComputedHeight, (JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame)) +{ + JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + auto* thisObject = jsDynamicCast(callFrame->thisValue()); + if (UNLIKELY(!thisObject)) { + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "getComputedHeight"_s); + } + + float height = YGNodeLayoutGetHeight(thisObject->internal()); + return JSC::JSValue::encode(JSC::jsNumber(height)); +} + +JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncGetComputedMargin, (JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame)) +{ + JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + auto* thisObject = jsDynamicCast(callFrame->thisValue()); + if (UNLIKELY(!thisObject)) { + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "getComputedMargin"_s); + } + + if (callFrame->argumentCount() < 1) { + throwTypeError(globalObject, scope, "getComputedMargin requires 1 argument (edge)"_s); + return {}; + } + + int edge = callFrame->uncheckedArgument(0).toInt32(globalObject); + RETURN_IF_EXCEPTION(scope, {}); + + float margin = YGNodeLayoutGetMargin(thisObject->internal(), static_cast(edge)); + return JSC::JSValue::encode(JSC::jsNumber(margin)); +} + +JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncGetComputedBorder, (JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame)) +{ + JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + auto* thisObject = jsDynamicCast(callFrame->thisValue()); + if (UNLIKELY(!thisObject)) { + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "getComputedBorder"_s); + } + + if (callFrame->argumentCount() < 1) { + throwTypeError(globalObject, scope, "getComputedBorder requires 1 argument (edge)"_s); + return {}; + } + + int edge = callFrame->uncheckedArgument(0).toInt32(globalObject); + RETURN_IF_EXCEPTION(scope, {}); + + float border = YGNodeLayoutGetBorder(thisObject->internal(), static_cast(edge)); + return JSC::JSValue::encode(JSC::jsNumber(border)); +} + +JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncGetComputedPadding, (JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame)) +{ + JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + auto* thisObject = jsDynamicCast(callFrame->thisValue()); + if (UNLIKELY(!thisObject)) { + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "getComputedPadding"_s); + } + + if (callFrame->argumentCount() < 1) { + throwTypeError(globalObject, scope, "getComputedPadding requires 1 argument (edge)"_s); + return {}; + } + + int edge = callFrame->uncheckedArgument(0).toInt32(globalObject); + RETURN_IF_EXCEPTION(scope, {}); + + float padding = YGNodeLayoutGetPadding(thisObject->internal(), static_cast(edge)); + return JSC::JSValue::encode(JSC::jsNumber(padding)); +} + +// Hierarchy method implementations +JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncRemoveAllChildren, (JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame)) +{ + JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + auto* thisObject = jsDynamicCast(callFrame->thisValue()); + if (UNLIKELY(!thisObject)) { + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "removeAllChildren"_s); + } + + YGNodeRemoveAllChildren(thisObject->internal()); + return JSC::JSValue::encode(JSC::jsUndefined()); +} + +JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncGetOwner, (JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame)) +{ + JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + auto* thisObject = jsDynamicCast(callFrame->thisValue()); + if (UNLIKELY(!thisObject)) { + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "getOwner"_s); + } + + YGNodeRef owner = YGNodeGetOwner(thisObject->internal()); + if (!owner) { + return JSC::JSValue::encode(JSC::jsNull()); + } + + // Get the JS wrapper from the owner's context + JSYogaNode* jsOwner = JSYogaNode::fromYGNode(owner); + return JSC::JSValue::encode(jsOwner ? jsOwner : JSC::jsNull()); +} + +// Utility method implementations +JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncFreeRecursive, (JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame)) +{ + JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + auto* thisObject = jsDynamicCast(callFrame->thisValue()); + if (UNLIKELY(!thisObject)) { + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "freeRecursive"_s); + } + + YGNodeFreeRecursive(thisObject->internal()); + thisObject->clearInternal(); + return JSC::JSValue::encode(JSC::jsUndefined()); +} + +JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncCopyStyle, (JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame)) +{ + JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + auto* thisObject = jsDynamicCast(callFrame->thisValue()); + if (UNLIKELY(!thisObject)) { + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "copyStyle"_s); + } + + if (callFrame->argumentCount() < 1) { + throwTypeError(globalObject, scope, "copyStyle requires 1 argument (sourceNode)"_s); + return {}; + } + + auto* sourceNode = jsDynamicCast(callFrame->uncheckedArgument(0)); + if (!sourceNode) { + throwTypeError(globalObject, scope, "First argument must be a Yoga.Node"_s); + return {}; + } + + YGNodeCopyStyle(thisObject->internal(), sourceNode->internal()); + return JSC::JSValue::encode(JSC::jsUndefined()); +} + +JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncClone, (JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame)) +{ + JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + auto* thisObject = jsDynamicCast(callFrame->thisValue()); + if (UNLIKELY(!thisObject)) { + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "clone"_s); + } + + YGNodeRef clonedNode = YGNodeClone(thisObject->internal()); + + auto* zigGlobalObject = defaultGlobalObject(globalObject); + JSC::Structure* structure = zigGlobalObject->m_JSYogaNodeClassStructure.get(zigGlobalObject); + + // Create a new JSYogaNode wrapper for the cloned node + JSYogaNode* jsClonedNode = JSYogaNode::create(vm, structure, nullptr); + // Replace the internal node with the cloned one + YGNodeFree(jsClonedNode->internal()); + jsClonedNode->setInternal(clonedNode); + YGNodeSetContext(clonedNode, jsClonedNode); + + return JSC::JSValue::encode(jsClonedNode); +} + +JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncSetNodeType, (JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame)) +{ + JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + auto* thisObject = jsDynamicCast(callFrame->thisValue()); + if (UNLIKELY(!thisObject)) { + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "setNodeType"_s); + } + + if (callFrame->argumentCount() < 1) { + return JSC::JSValue::encode(JSC::jsUndefined()); + } + + int32_t nodeType = callFrame->uncheckedArgument(0).toInt32(globalObject); + RETURN_IF_EXCEPTION(scope, {}); + + YGNodeSetNodeType(thisObject->internal(), static_cast(nodeType)); + return JSC::JSValue::encode(JSC::jsUndefined()); +} + +JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncGetNodeType, (JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame)) +{ + JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + auto* thisObject = jsDynamicCast(callFrame->thisValue()); + if (UNLIKELY(!thisObject)) { + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "getNodeType"_s); + } + + YGNodeType nodeType = YGNodeGetNodeType(thisObject->internal()); + return JSC::JSValue::encode(JSC::jsNumber(static_cast(nodeType))); +} + +JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncSetIsReferenceBaseline, (JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame)) +{ + JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + auto* thisObject = jsDynamicCast(callFrame->thisValue()); + if (UNLIKELY(!thisObject)) { + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "setIsReferenceBaseline"_s); + } + + if (callFrame->argumentCount() < 1) { + return JSC::JSValue::encode(JSC::jsUndefined()); + } + + bool isReferenceBaseline = callFrame->uncheckedArgument(0).toBoolean(globalObject); + RETURN_IF_EXCEPTION(scope, {}); + + YGNodeSetIsReferenceBaseline(thisObject->internal(), isReferenceBaseline); + return JSC::JSValue::encode(JSC::jsUndefined()); +} + +JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncIsReferenceBaseline, (JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame)) +{ + JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + auto* thisObject = jsDynamicCast(callFrame->thisValue()); + if (UNLIKELY(!thisObject)) { + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "isReferenceBaseline"_s); + } + + bool isReferenceBaseline = YGNodeIsReferenceBaseline(thisObject->internal()); + return JSC::JSValue::encode(JSC::jsBoolean(isReferenceBaseline)); +} + +JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncSetContext, (JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame)) +{ + JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + auto* thisObject = jsDynamicCast(callFrame->thisValue()); + if (UNLIKELY(!thisObject)) { + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "setContext"_s); + } + + // For now, we don't support storing arbitrary JS values as context + // The Yoga node context is used internally to store the JS wrapper + return JSC::JSValue::encode(JSC::jsUndefined()); +} + +JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncGetContext, (JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame)) +{ + JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + auto* thisObject = jsDynamicCast(callFrame->thisValue()); + if (UNLIKELY(!thisObject)) { + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "getContext"_s); + } + + // Return null since we use context internally for the wrapper + return JSC::JSValue::encode(JSC::jsNull()); +} + +JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncSetConfig, (JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame)) +{ + JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + auto* thisObject = jsDynamicCast(callFrame->thisValue()); + if (UNLIKELY(!thisObject)) { + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "setConfig"_s); + } + + if (callFrame->argumentCount() < 1) { + return JSC::JSValue::encode(JSC::jsUndefined()); + } + + JSC::JSValue configArg = callFrame->uncheckedArgument(0); + if (!configArg.isUndefinedOrNull()) { + auto* jsConfig = jsDynamicCast(configArg); + if (!jsConfig) { + throwTypeError(globalObject, scope, "First argument must be a Yoga.Config instance"_s); + return {}; + } + YGNodeSetConfig(thisObject->internal(), jsConfig->internal()); + } else { + // Set to default config if null/undefined + YGNodeSetConfig(thisObject->internal(), const_cast(YGConfigGetDefault())); + } + + return JSC::JSValue::encode(JSC::jsUndefined()); +} + +JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncGetConfig, (JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame)) +{ + JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + auto* thisObject = jsDynamicCast(callFrame->thisValue()); + if (UNLIKELY(!thisObject)) { + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "getConfig"_s); + } + + // TODO: Return the associated Config object + // For now, return null + return JSC::JSValue::encode(JSC::jsNull()); +} + +JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncGetHasNewLayout, (JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame)) +{ + JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + auto* thisObject = jsDynamicCast(callFrame->thisValue()); + if (UNLIKELY(!thisObject)) { + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "getHasNewLayout"_s); + } + + bool hasNewLayout = YGNodeGetHasNewLayout(thisObject->internal()); + return JSC::JSValue::encode(JSC::jsBoolean(hasNewLayout)); +} + +JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncSetHasNewLayout, (JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame)) +{ + JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + auto* thisObject = jsDynamicCast(callFrame->thisValue()); + if (UNLIKELY(!thisObject)) { + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "setHasNewLayout"_s); + } + + if (callFrame->argumentCount() < 1) { + return JSC::JSValue::encode(JSC::jsUndefined()); + } + + bool hasNewLayout = callFrame->uncheckedArgument(0).toBoolean(globalObject); + RETURN_IF_EXCEPTION(scope, {}); + + YGNodeSetHasNewLayout(thisObject->internal(), hasNewLayout); + return JSC::JSValue::encode(JSC::jsUndefined()); +} + +JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncSetBaselineFunc, (JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame)) +{ + JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + auto* thisObject = jsDynamicCast(callFrame->thisValue()); + if (UNLIKELY(!thisObject)) { + return WebCore::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "setBaselineFunc"_s); + } + + // TODO: Implement baseline function callback support + // This requires creating a C callback that bridges to JavaScript + return JSC::JSValue::encode(JSC::jsUndefined()); +} + +// Functions that are already defined earlier in the file are not duplicated here + } // namespace Bun diff --git a/src/bun.js/bindings/JSYogaPrototype.h b/src/bun.js/bindings/JSYogaPrototype.h index e599c487ab..2f82ee4b97 100644 --- a/src/bun.js/bindings/JSYogaPrototype.h +++ b/src/bun.js/bindings/JSYogaPrototype.h @@ -39,6 +39,9 @@ private: } void finishCreation(JSC::VM& vm, JSC::JSGlobalObject* globalObject); + +public: + void setConstructor(JSC::VM& vm, JSC::JSObject* constructor); }; class JSYogaNodePrototype final : public JSC::JSNonFinalObject { @@ -75,6 +78,9 @@ private: } void finishCreation(JSC::VM& vm, JSC::JSGlobalObject* globalObject); + +public: + void setConstructor(JSC::VM& vm, JSC::JSObject* constructor); }; } // namespace Bun diff --git a/src/bun.js/bindings/ZigGlobalObject.cpp b/src/bun.js/bindings/ZigGlobalObject.cpp index 16d33b7897..45c8107536 100644 --- a/src/bun.js/bindings/ZigGlobalObject.cpp +++ b/src/bun.js/bindings/ZigGlobalObject.cpp @@ -177,6 +177,7 @@ #include "JSPrivateKeyObject.h" #include "webcore/JSMIMEParams.h" #include "JSNodePerformanceHooksHistogram.h" +#include "JSYogaConstructor.h" #include "JSS3File.h" #include "S3Error.h" #include "ProcessBindingBuffer.h" @@ -2811,6 +2812,16 @@ void GlobalObject::finishCreation(VM& vm) setupHTTPParserClassStructure(init); }); + m_JSYogaConfigClassStructure.initLater( + [](LazyClassStructure::Initializer& init) { + Bun::setupJSYogaConfigClassStructure(init); + }); + + m_JSYogaNodeClassStructure.initLater( + [](LazyClassStructure::Initializer& init) { + Bun::setupJSYogaNodeClassStructure(init); + }); + m_JSNodePerformanceHooksHistogramClassStructure.initLater( [](LazyClassStructure::Initializer& init) { Bun::setupJSNodePerformanceHooksHistogramClassStructure(init); diff --git a/src/bun.js/bindings/ZigGlobalObject.h b/src/bun.js/bindings/ZigGlobalObject.h index dfed8b13d1..a9a3ffca1b 100644 --- a/src/bun.js/bindings/ZigGlobalObject.h +++ b/src/bun.js/bindings/ZigGlobalObject.h @@ -552,6 +552,9 @@ public: V(public, LazyClassStructure, m_JSConnectionsListClassStructure) \ V(public, LazyClassStructure, m_JSHTTPParserClassStructure) \ \ + V(public, LazyClassStructure, m_JSYogaConfigClassStructure) \ + V(public, LazyClassStructure, m_JSYogaNodeClassStructure) \ + \ V(private, LazyPropertyOfGlobalObject, m_pendingVirtualModuleResultStructure) \ V(private, LazyPropertyOfGlobalObject, m_performMicrotaskFunction) \ V(private, LazyPropertyOfGlobalObject, m_nativeMicrotaskTrampoline) \ diff --git a/test/js/bun/yoga-node.test.js b/test/js/bun/yoga-node.test.js index c50242d78c..9240288468 100644 --- a/test/js/bun/yoga-node.test.js +++ b/test/js/bun/yoga-node.test.js @@ -1,5 +1,7 @@ import { describe, expect, test } from "bun:test"; +const Yoga = Bun.Yoga; + describe("Yoga.Node", () => { test("Node constructor", () => { const node = new Yoga.Node(); @@ -174,6 +176,15 @@ describe("Yoga.Node", () => { }; node.setDirtiedFunc(dirtiedFunc); + + // markDirty requires a measure function + node.setMeasureFunc(() => ({ width: 100, height: 50 })); + + // Nodes start dirty, so clear the dirty flag first + node.calculateLayout(); + expect(node.isDirty()).toBe(false); + + // Now mark dirty - this should trigger the callback node.markDirty(); expect(dirtiedCalled).toBe(true); @@ -190,24 +201,29 @@ describe("Yoga.Node", () => { node.reset(); - // After reset, values should be back to defaults + // After reset, width/height default to AUTO, not UNDEFINED const width = node.getWidth(); - expect(width.unit).toBe(Yoga.UNIT_UNDEFINED); + expect(width.unit).toBe(Yoga.UNIT_AUTO); }); test("dirty state", () => { const node = new Yoga.Node(); - // Initially not dirty - expect(node.isDirty()).toBe(false); - - // Mark as dirty - node.markDirty(); + // Nodes start as dirty by default in Yoga expect(node.isDirty()).toBe(true); // Calculate layout clears dirty flag node.calculateLayout(); expect(node.isDirty()).toBe(false); + + // Mark as dirty (requires measure function) + node.setMeasureFunc(() => ({ width: 100, height: 50 })); + node.markDirty(); + expect(node.isDirty()).toBe(true); + + // Calculate layout clears dirty flag again + node.calculateLayout(); + expect(node.isDirty()).toBe(false); }); test("free node", () => {