mirror of
https://github.com/oven-sh/bun
synced 2026-02-16 22:01:47 +00:00
Compare commits
8 Commits
riskymh/re
...
cursor/cre
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
87410eba91 | ||
|
|
0f4115f823 | ||
|
|
2082fdf3a6 | ||
|
|
5b0dd953c0 | ||
|
|
4896b9ba93 | ||
|
|
7d2b071631 | ||
|
|
d50c41d063 | ||
|
|
b42bb48611 |
@@ -92,6 +92,14 @@ src/bun.js/bindings/JSWrappingFunction.cpp
|
||||
src/bun.js/bindings/JSX509Certificate.cpp
|
||||
src/bun.js/bindings/JSX509CertificateConstructor.cpp
|
||||
src/bun.js/bindings/JSX509CertificatePrototype.cpp
|
||||
src/bun.js/bindings/JSYogaConfig.cpp
|
||||
src/bun.js/bindings/JSYogaConstants.cpp
|
||||
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
|
||||
src/bun.js/bindings/ModuleLoader.cpp
|
||||
|
||||
62
src/bun.js/bindings/JSYogaConfig.cpp
Normal file
62
src/bun.js/bindings/JSYogaConfig.cpp
Normal file
@@ -0,0 +1,62 @@
|
||||
#include "root.h"
|
||||
#include "JSYogaConfig.h"
|
||||
#include "webcore/DOMIsoSubspaces.h"
|
||||
#include "webcore/DOMClientIsoSubspaces.h"
|
||||
#include <yoga/Yoga.h>
|
||||
|
||||
namespace Bun {
|
||||
|
||||
const JSC::ClassInfo JSYogaConfig::s_info = { "Yoga.Config"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSYogaConfig) };
|
||||
|
||||
JSYogaConfig::JSYogaConfig(JSC::VM& vm, JSC::Structure* structure)
|
||||
: Base(vm, structure)
|
||||
, m_config(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
JSYogaConfig::~JSYogaConfig()
|
||||
{
|
||||
// Only free if not already freed via free() method
|
||||
if (m_config) {
|
||||
YGConfigFree(m_config);
|
||||
m_config = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
JSYogaConfig* JSYogaConfig::create(JSC::VM& vm, JSC::Structure* structure)
|
||||
{
|
||||
JSYogaConfig* config = new (NotNull, JSC::allocateCell<JSYogaConfig>(vm)) JSYogaConfig(vm, structure);
|
||||
config->finishCreation(vm);
|
||||
return config;
|
||||
}
|
||||
|
||||
void JSYogaConfig::finishCreation(JSC::VM& vm)
|
||||
{
|
||||
Base::finishCreation(vm);
|
||||
m_config = YGConfigNew();
|
||||
}
|
||||
|
||||
JSC::Structure* JSYogaConfig::createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype)
|
||||
{
|
||||
return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info());
|
||||
}
|
||||
|
||||
void JSYogaConfig::destroy(JSC::JSCell* cell)
|
||||
{
|
||||
static_cast<JSYogaConfig*>(cell)->~JSYogaConfig();
|
||||
}
|
||||
|
||||
template<typename MyClassT, JSC::SubspaceAccess mode>
|
||||
JSC::GCClient::IsoSubspace* JSYogaConfig::subspaceFor(JSC::VM& vm)
|
||||
{
|
||||
if constexpr (mode == JSC::SubspaceAccess::Concurrently)
|
||||
return nullptr;
|
||||
return WebCore::subspaceForImpl<JSYogaConfig, WebCore::UseCustomHeapCellType::No>(
|
||||
vm,
|
||||
[](auto& spaces) { return spaces.m_clientSubspaceForJSYogaConfig.get(); },
|
||||
[](auto& spaces, auto&& space) { spaces.m_clientSubspaceForJSYogaConfig = std::forward<decltype(space)>(space); },
|
||||
[](auto& spaces) { return spaces.m_subspaceForJSYogaConfig.get(); },
|
||||
[](auto& spaces, auto&& space) { spaces.m_subspaceForJSYogaConfig = std::forward<decltype(space)>(space); });
|
||||
}
|
||||
|
||||
} // namespace Bun
|
||||
37
src/bun.js/bindings/JSYogaConfig.h
Normal file
37
src/bun.js/bindings/JSYogaConfig.h
Normal file
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
#include "root.h"
|
||||
#include <memory>
|
||||
#include <JavaScriptCore/JSDestructibleObject.h>
|
||||
|
||||
// Forward declarations
|
||||
typedef struct YGConfig* YGConfigRef;
|
||||
|
||||
namespace Bun {
|
||||
|
||||
class JSYogaConfig final : public JSC::JSDestructibleObject {
|
||||
public:
|
||||
using Base = JSC::JSDestructibleObject;
|
||||
static constexpr unsigned StructureFlags = Base::StructureFlags;
|
||||
static constexpr JSC::DestructionMode needsDestruction = JSC::NeedsDestruction;
|
||||
|
||||
static JSYogaConfig* create(JSC::VM&, JSC::Structure*);
|
||||
static void destroy(JSC::JSCell*);
|
||||
static JSC::Structure* createStructure(JSC::VM&, JSC::JSGlobalObject*, JSC::JSValue);
|
||||
~JSYogaConfig();
|
||||
|
||||
template<typename, JSC::SubspaceAccess>
|
||||
static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM&);
|
||||
|
||||
DECLARE_INFO;
|
||||
|
||||
YGConfigRef internal() { return m_config; }
|
||||
void clearInternal() { m_config = nullptr; }
|
||||
|
||||
private:
|
||||
JSYogaConfig(JSC::VM&, JSC::Structure*);
|
||||
void finishCreation(JSC::VM&);
|
||||
|
||||
YGConfigRef m_config;
|
||||
};
|
||||
|
||||
} // namespace Bun
|
||||
108
src/bun.js/bindings/JSYogaConstants.cpp
Normal file
108
src/bun.js/bindings/JSYogaConstants.cpp
Normal file
@@ -0,0 +1,108 @@
|
||||
#include "root.h"
|
||||
#include "JSYogaConstants.h"
|
||||
#include <JavaScriptCore/JSCInlines.h>
|
||||
#include <yoga/Yoga.h>
|
||||
|
||||
namespace Bun {
|
||||
|
||||
const JSC::ClassInfo JSYogaConstants::s_info = { "YogaConstants"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSYogaConstants) };
|
||||
|
||||
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);
|
||||
|
||||
// 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);
|
||||
|
||||
// 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);
|
||||
|
||||
// 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);
|
||||
|
||||
// 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);
|
||||
|
||||
// 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);
|
||||
|
||||
// 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);
|
||||
|
||||
// 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);
|
||||
|
||||
// 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);
|
||||
|
||||
// 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);
|
||||
|
||||
// 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);
|
||||
|
||||
// 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);
|
||||
|
||||
// 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);
|
||||
|
||||
// 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);
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
} // namespace Bun
|
||||
41
src/bun.js/bindings/JSYogaConstants.h
Normal file
41
src/bun.js/bindings/JSYogaConstants.h
Normal file
@@ -0,0 +1,41 @@
|
||||
#pragma once
|
||||
#include "root.h"
|
||||
#include <JavaScriptCore/JSObject.h>
|
||||
|
||||
namespace Bun {
|
||||
|
||||
class JSYogaConstants final : public JSC::JSNonFinalObject {
|
||||
public:
|
||||
using Base = JSC::JSNonFinalObject;
|
||||
static constexpr unsigned StructureFlags = Base::StructureFlags;
|
||||
|
||||
static JSYogaConstants* create(JSC::VM& vm, JSC::Structure* structure)
|
||||
{
|
||||
JSYogaConstants* constants = new (NotNull, allocateCell<JSYogaConstants>(vm)) JSYogaConstants(vm, structure);
|
||||
constants->finishCreation(vm);
|
||||
return constants;
|
||||
}
|
||||
|
||||
DECLARE_INFO;
|
||||
|
||||
static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype)
|
||||
{
|
||||
return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info());
|
||||
}
|
||||
|
||||
template<typename, JSC::SubspaceAccess>
|
||||
static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm)
|
||||
{
|
||||
return &vm.plainObjectSpace();
|
||||
}
|
||||
|
||||
private:
|
||||
JSYogaConstants(JSC::VM& vm, JSC::Structure* structure)
|
||||
: Base(vm, structure)
|
||||
{
|
||||
}
|
||||
|
||||
void finishCreation(JSC::VM&);
|
||||
};
|
||||
|
||||
} // namespace Bun
|
||||
165
src/bun.js/bindings/JSYogaConstructor.cpp
Normal file
165
src/bun.js/bindings/JSYogaConstructor.cpp
Normal file
@@ -0,0 +1,165 @@
|
||||
#include "root.h"
|
||||
#include "JSYogaConstructor.h"
|
||||
#include "JSYogaConfig.h"
|
||||
#include "JSYogaNode.h"
|
||||
#include "JSYogaPrototype.h"
|
||||
#include "ZigGlobalObject.h"
|
||||
#include <JavaScriptCore/FunctionPrototype.h>
|
||||
#include <JavaScriptCore/JSCInlines.h>
|
||||
#include <yoga/Yoga.h>
|
||||
|
||||
namespace Bun {
|
||||
|
||||
// Forward declarations for constructor functions
|
||||
static JSC_DECLARE_HOST_FUNCTION(constructJSYogaConfig);
|
||||
static JSC_DECLARE_HOST_FUNCTION(callJSYogaConfig);
|
||||
static JSC_DECLARE_HOST_FUNCTION(constructJSYogaNode);
|
||||
static JSC_DECLARE_HOST_FUNCTION(callJSYogaNode);
|
||||
|
||||
// Config Constructor implementation
|
||||
const JSC::ClassInfo JSYogaConfigConstructor::s_info = { "Config"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSYogaConfigConstructor) };
|
||||
|
||||
JSYogaConfigConstructor::JSYogaConfigConstructor(JSC::VM& vm, JSC::Structure* structure)
|
||||
: Base(vm, structure, callJSYogaConfig, constructJSYogaConfig)
|
||||
{
|
||||
}
|
||||
|
||||
void JSYogaConfigConstructor::finishCreation(JSC::VM& vm, JSC::JSObject* prototype)
|
||||
{
|
||||
Base::finishCreation(vm, 0, "Config"_s);
|
||||
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
|
||||
putDirectNativeFunction(vm, this->globalObject(), JSC::Identifier::fromString(vm, "create"_s), 0, constructJSYogaConfig, ImplementationVisibility::Public, NoIntrinsic, JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::ReadOnly);
|
||||
}
|
||||
|
||||
// Node Constructor implementation
|
||||
const JSC::ClassInfo JSYogaNodeConstructor::s_info = { "Node"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSYogaNodeConstructor) };
|
||||
|
||||
JSYogaNodeConstructor::JSYogaNodeConstructor(JSC::VM& vm, JSC::Structure* structure)
|
||||
: Base(vm, structure, callJSYogaNode, constructJSYogaNode)
|
||||
{
|
||||
}
|
||||
|
||||
void JSYogaNodeConstructor::finishCreation(JSC::VM& vm, JSC::JSObject* prototype)
|
||||
{
|
||||
Base::finishCreation(vm, 1, "Node"_s); // 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
|
||||
putDirectNativeFunction(vm, this->globalObject(), JSC::Identifier::fromString(vm, "create"_s), 1, constructJSYogaNode, ImplementationVisibility::Public, NoIntrinsic, JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::ReadOnly);
|
||||
}
|
||||
|
||||
// Constructor functions
|
||||
JSC_DEFINE_HOST_FUNCTION(constructJSYogaConfig, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
|
||||
{
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* zigGlobalObject = defaultGlobalObject(globalObject);
|
||||
JSC::Structure* structure = zigGlobalObject->m_JSYogaConfigClassStructure.get(zigGlobalObject);
|
||||
|
||||
// Handle subclassing
|
||||
JSC::JSValue newTarget = callFrame->newTarget();
|
||||
if (UNLIKELY(zigGlobalObject->m_JSYogaConfigClassStructure.constructor(zigGlobalObject) != newTarget)) {
|
||||
if (!newTarget) {
|
||||
throwTypeError(globalObject, scope, "Class constructor Config cannot be invoked without 'new'"_s);
|
||||
return {};
|
||||
}
|
||||
|
||||
auto* functionGlobalObject = defaultGlobalObject(getFunctionRealm(globalObject, newTarget.getObject()));
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
structure = JSC::InternalFunction::createSubclassStructure(
|
||||
globalObject, newTarget.getObject(), functionGlobalObject->m_JSYogaConfigClassStructure.get(functionGlobalObject));
|
||||
scope.release();
|
||||
}
|
||||
|
||||
return JSC::JSValue::encode(JSYogaConfig::create(vm, structure));
|
||||
}
|
||||
|
||||
JSC_DEFINE_HOST_FUNCTION(callJSYogaConfig, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
|
||||
{
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
throwTypeError(globalObject, scope, "Class constructor Config cannot be invoked without 'new'"_s);
|
||||
return {};
|
||||
}
|
||||
|
||||
JSC_DEFINE_HOST_FUNCTION(constructJSYogaNode, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
|
||||
{
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* zigGlobalObject = defaultGlobalObject(globalObject);
|
||||
JSC::Structure* structure = zigGlobalObject->m_JSYogaNodeClassStructure.get(zigGlobalObject);
|
||||
|
||||
// Handle subclassing
|
||||
JSC::JSValue newTarget = callFrame->newTarget();
|
||||
if (UNLIKELY(zigGlobalObject->m_JSYogaNodeClassStructure.constructor(zigGlobalObject) != newTarget)) {
|
||||
if (!newTarget) {
|
||||
throwTypeError(globalObject, scope, "Class constructor Node cannot be invoked without 'new'"_s);
|
||||
return {};
|
||||
}
|
||||
|
||||
auto* functionGlobalObject = defaultGlobalObject(getFunctionRealm(globalObject, newTarget.getObject()));
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
structure = JSC::InternalFunction::createSubclassStructure(
|
||||
globalObject, newTarget.getObject(), functionGlobalObject->m_JSYogaNodeClassStructure.get(functionGlobalObject));
|
||||
scope.release();
|
||||
}
|
||||
|
||||
// Optional config parameter
|
||||
YGConfigRef config = nullptr;
|
||||
if (callFrame->argumentCount() > 0) {
|
||||
JSC::JSValue configArg = callFrame->uncheckedArgument(0);
|
||||
if (!configArg.isUndefinedOrNull()) {
|
||||
auto* jsConfig = JSC::jsDynamicCast<JSYogaConfig*>(configArg);
|
||||
if (!jsConfig) {
|
||||
throwTypeError(globalObject, scope, "First argument must be a Yoga.Config instance"_s);
|
||||
return {};
|
||||
}
|
||||
config = jsConfig->internal();
|
||||
}
|
||||
}
|
||||
|
||||
return JSC::JSValue::encode(JSYogaNode::create(vm, structure, config));
|
||||
}
|
||||
|
||||
JSC_DEFINE_HOST_FUNCTION(callJSYogaNode, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
|
||||
{
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
throwTypeError(globalObject, scope, "Class constructor Node cannot be invoked without 'new'"_s);
|
||||
return {};
|
||||
}
|
||||
|
||||
// Setup functions for lazy initialization
|
||||
void setupJSYogaConfigClassStructure(JSC::LazyClassStructure::Initializer& init)
|
||||
{
|
||||
auto* prototypeStructure = JSYogaConfigPrototype::createStructure(init.vm, init.global, init.global->objectPrototype());
|
||||
auto* prototype = JSYogaConfigPrototype::create(init.vm, init.global, prototypeStructure);
|
||||
|
||||
auto* constructorStructure = JSYogaConfigConstructor::createStructure(init.vm, init.global, init.global->functionPrototype());
|
||||
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);
|
||||
}
|
||||
|
||||
void setupJSYogaNodeClassStructure(JSC::LazyClassStructure::Initializer& init)
|
||||
{
|
||||
auto* prototypeStructure = JSYogaNodePrototype::createStructure(init.vm, init.global, init.global->objectPrototype());
|
||||
auto* prototype = JSYogaNodePrototype::create(init.vm, init.global, prototypeStructure);
|
||||
|
||||
auto* constructorStructure = JSYogaNodeConstructor::createStructure(init.vm, init.global, init.global->functionPrototype());
|
||||
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);
|
||||
}
|
||||
|
||||
} // namespace Bun
|
||||
71
src/bun.js/bindings/JSYogaConstructor.h
Normal file
71
src/bun.js/bindings/JSYogaConstructor.h
Normal file
@@ -0,0 +1,71 @@
|
||||
#pragma once
|
||||
#include "root.h"
|
||||
#include <JavaScriptCore/InternalFunction.h>
|
||||
|
||||
namespace Bun {
|
||||
|
||||
class JSYogaConfigConstructor final : public JSC::InternalFunction {
|
||||
public:
|
||||
using Base = JSC::InternalFunction;
|
||||
static constexpr unsigned StructureFlags = Base::StructureFlags;
|
||||
|
||||
static JSYogaConfigConstructor* create(JSC::VM& vm, JSC::Structure* structure, JSC::JSObject* prototype)
|
||||
{
|
||||
JSYogaConfigConstructor* constructor = new (NotNull, JSC::allocateCell<JSYogaConfigConstructor>(vm)) JSYogaConfigConstructor(vm, structure);
|
||||
constructor->finishCreation(vm, prototype);
|
||||
return constructor;
|
||||
}
|
||||
|
||||
DECLARE_INFO;
|
||||
|
||||
template<typename CellType, JSC::SubspaceAccess>
|
||||
static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm)
|
||||
{
|
||||
return &vm.internalFunctionSpace();
|
||||
}
|
||||
|
||||
static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype)
|
||||
{
|
||||
return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::InternalFunctionType, StructureFlags), info());
|
||||
}
|
||||
|
||||
private:
|
||||
JSYogaConfigConstructor(JSC::VM& vm, JSC::Structure* structure);
|
||||
void finishCreation(JSC::VM& vm, JSC::JSObject* prototype);
|
||||
};
|
||||
|
||||
class JSYogaNodeConstructor final : public JSC::InternalFunction {
|
||||
public:
|
||||
using Base = JSC::InternalFunction;
|
||||
static constexpr unsigned StructureFlags = Base::StructureFlags;
|
||||
|
||||
static JSYogaNodeConstructor* create(JSC::VM& vm, JSC::Structure* structure, JSC::JSObject* prototype)
|
||||
{
|
||||
JSYogaNodeConstructor* constructor = new (NotNull, JSC::allocateCell<JSYogaNodeConstructor>(vm)) JSYogaNodeConstructor(vm, structure);
|
||||
constructor->finishCreation(vm, prototype);
|
||||
return constructor;
|
||||
}
|
||||
|
||||
DECLARE_INFO;
|
||||
|
||||
template<typename CellType, JSC::SubspaceAccess>
|
||||
static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm)
|
||||
{
|
||||
return &vm.internalFunctionSpace();
|
||||
}
|
||||
|
||||
static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype)
|
||||
{
|
||||
return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::InternalFunctionType, StructureFlags), info());
|
||||
}
|
||||
|
||||
private:
|
||||
JSYogaNodeConstructor(JSC::VM& vm, JSC::Structure* structure);
|
||||
void finishCreation(JSC::VM& vm, JSC::JSObject* prototype);
|
||||
};
|
||||
|
||||
// Helper functions to set up class structures
|
||||
void setupJSYogaConfigClassStructure(JSC::LazyClassStructure::Initializer&);
|
||||
void setupJSYogaNodeClassStructure(JSC::LazyClassStructure::Initializer&);
|
||||
|
||||
} // namespace Bun
|
||||
17
src/bun.js/bindings/JSYogaExports.cpp
Normal file
17
src/bun.js/bindings/JSYogaExports.cpp
Normal file
@@ -0,0 +1,17 @@
|
||||
#include "root.h"
|
||||
#include "JSYogaConstructor.h"
|
||||
#include "ZigGlobalObject.h"
|
||||
|
||||
extern "C" {
|
||||
|
||||
JSC::EncodedJSValue Bun__JSYogaConfigConstructor(Zig::GlobalObject* globalObject)
|
||||
{
|
||||
return JSValue::encode(globalObject->m_JSYogaConfigClassStructure.constructor(globalObject));
|
||||
}
|
||||
|
||||
JSC::EncodedJSValue Bun__JSYogaNodeConstructor(Zig::GlobalObject* globalObject)
|
||||
{
|
||||
return JSValue::encode(globalObject->m_JSYogaNodeClassStructure.constructor(globalObject));
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
68
src/bun.js/bindings/JSYogaModule.cpp
Normal file
68
src/bun.js/bindings/JSYogaModule.cpp
Normal file
@@ -0,0 +1,68 @@
|
||||
#include "root.h"
|
||||
#include "JSYogaModule.h"
|
||||
#include "JSYogaConstants.h"
|
||||
#include "JSYogaConstructor.h"
|
||||
#include "JSYogaPrototype.h"
|
||||
#include "ZigGlobalObject.h"
|
||||
#include <JavaScriptCore/JSCInlines.h>
|
||||
|
||||
namespace Bun {
|
||||
|
||||
const JSC::ClassInfo JSYogaModule::s_info = { "Yoga"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSYogaModule) };
|
||||
|
||||
JSYogaModule* JSYogaModule::create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure)
|
||||
{
|
||||
JSYogaModule* module = new (NotNull, allocateCell<JSYogaModule>(vm)) JSYogaModule(vm, structure);
|
||||
module->finishCreation(vm, globalObject);
|
||||
return module;
|
||||
}
|
||||
|
||||
void JSYogaModule::finishCreation(JSC::VM& vm, JSC::JSGlobalObject* globalObject)
|
||||
{
|
||||
Base::finishCreation(vm);
|
||||
|
||||
// Create Config constructor and prototype
|
||||
auto* configPrototype = JSYogaConfigPrototype::create(vm, globalObject,
|
||||
JSYogaConfigPrototype::createStructure(vm, globalObject, globalObject->objectPrototype()));
|
||||
|
||||
auto* configConstructor = JSYogaConfigConstructor::create(vm,
|
||||
JSYogaConfigConstructor::createStructure(vm, globalObject, globalObject->functionPrototype()),
|
||||
configPrototype);
|
||||
|
||||
// Create Node constructor and prototype
|
||||
auto* nodePrototype = JSYogaNodePrototype::create(vm, globalObject,
|
||||
JSYogaNodePrototype::createStructure(vm, globalObject, globalObject->objectPrototype()));
|
||||
|
||||
auto* nodeConstructor = JSYogaNodeConstructor::create(vm,
|
||||
JSYogaNodeConstructor::createStructure(vm, globalObject, globalObject->functionPrototype()),
|
||||
nodePrototype);
|
||||
|
||||
// Add constructors to module
|
||||
putDirect(vm, vm.propertyNames->Config, 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Export function for Zig integration
|
||||
extern "C" JSC::EncodedJSValue Bun__createYogaModule(Zig::GlobalObject* globalObject)
|
||||
{
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
auto* structure = JSYogaModule::createStructure(vm, globalObject, globalObject->objectPrototype());
|
||||
auto* module = JSYogaModule::create(vm, globalObject, structure);
|
||||
return JSC::JSValue::encode(module);
|
||||
}
|
||||
|
||||
} // namespace Bun
|
||||
36
src/bun.js/bindings/JSYogaModule.h
Normal file
36
src/bun.js/bindings/JSYogaModule.h
Normal file
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
#include "root.h"
|
||||
#include <JavaScriptCore/JSObject.h>
|
||||
|
||||
namespace Bun {
|
||||
|
||||
class JSYogaModule final : public JSC::JSNonFinalObject {
|
||||
public:
|
||||
using Base = JSC::JSNonFinalObject;
|
||||
static constexpr unsigned StructureFlags = Base::StructureFlags;
|
||||
|
||||
static JSYogaModule* create(JSC::VM&, JSC::JSGlobalObject*, JSC::Structure*);
|
||||
|
||||
DECLARE_INFO;
|
||||
|
||||
static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype)
|
||||
{
|
||||
return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info());
|
||||
}
|
||||
|
||||
template<typename, JSC::SubspaceAccess>
|
||||
static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm)
|
||||
{
|
||||
return &vm.plainObjectSpace();
|
||||
}
|
||||
|
||||
private:
|
||||
JSYogaModule(JSC::VM& vm, JSC::Structure* structure)
|
||||
: Base(vm, structure)
|
||||
{
|
||||
}
|
||||
|
||||
void finishCreation(JSC::VM&, JSC::JSGlobalObject*);
|
||||
};
|
||||
|
||||
} // namespace Bun
|
||||
88
src/bun.js/bindings/JSYogaNode.cpp
Normal file
88
src/bun.js/bindings/JSYogaNode.cpp
Normal file
@@ -0,0 +1,88 @@
|
||||
#include "root.h"
|
||||
#include "JSYogaNode.h"
|
||||
#include "webcore/DOMIsoSubspaces.h"
|
||||
#include "webcore/DOMClientIsoSubspaces.h"
|
||||
#include <yoga/Yoga.h>
|
||||
|
||||
namespace Bun {
|
||||
|
||||
const JSC::ClassInfo JSYogaNode::s_info = { "Yoga.Node"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSYogaNode) };
|
||||
|
||||
JSYogaNode::JSYogaNode(JSC::VM& vm, JSC::Structure* structure)
|
||||
: Base(vm, structure)
|
||||
, m_node(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
JSYogaNode::~JSYogaNode()
|
||||
{
|
||||
if (m_node) {
|
||||
YGNodeFree(m_node);
|
||||
}
|
||||
}
|
||||
|
||||
JSYogaNode* JSYogaNode::create(JSC::VM& vm, JSC::Structure* structure, YGConfigRef config)
|
||||
{
|
||||
JSYogaNode* node = new (NotNull, JSC::allocateCell<JSYogaNode>(vm)) JSYogaNode(vm, structure);
|
||||
node->finishCreation(vm, config);
|
||||
return node;
|
||||
}
|
||||
|
||||
void JSYogaNode::finishCreation(JSC::VM& vm, YGConfigRef config)
|
||||
{
|
||||
Base::finishCreation(vm);
|
||||
if (config) {
|
||||
m_node = YGNodeNewWithConfig(config);
|
||||
} else {
|
||||
m_node = YGNodeNew();
|
||||
}
|
||||
|
||||
// Essential: store JS wrapper in Yoga node's context for callbacks and hierarchy traversal
|
||||
YGNodeSetContext(m_node, this);
|
||||
}
|
||||
|
||||
JSC::Structure* JSYogaNode::createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype)
|
||||
{
|
||||
return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info());
|
||||
}
|
||||
|
||||
void JSYogaNode::destroy(JSC::JSCell* cell)
|
||||
{
|
||||
static_cast<JSYogaNode*>(cell)->~JSYogaNode();
|
||||
}
|
||||
|
||||
JSYogaNode* JSYogaNode::fromYGNode(YGNodeRef nodeRef)
|
||||
{
|
||||
if (!nodeRef) return nullptr;
|
||||
return static_cast<JSYogaNode*>(YGNodeGetContext(nodeRef));
|
||||
}
|
||||
|
||||
JSC::JSGlobalObject* JSYogaNode::globalObject() const
|
||||
{
|
||||
return this->structure()->globalObject();
|
||||
}
|
||||
|
||||
template<typename MyClassT, JSC::SubspaceAccess mode>
|
||||
JSC::GCClient::IsoSubspace* JSYogaNode::subspaceFor(JSC::VM& vm)
|
||||
{
|
||||
if constexpr (mode == JSC::SubspaceAccess::Concurrently)
|
||||
return nullptr;
|
||||
return WebCore::subspaceForImpl<JSYogaNode, WebCore::UseCustomHeapCellType::No>(
|
||||
vm,
|
||||
[](auto& spaces) { return spaces.m_clientSubspaceForJSYogaNode.get(); },
|
||||
[](auto& spaces, auto&& space) { spaces.m_clientSubspaceForJSYogaNode = std::forward<decltype(space)>(space); },
|
||||
[](auto& spaces) { return spaces.m_subspaceForJSYogaNode.get(); },
|
||||
[](auto& spaces, auto&& space) { spaces.m_subspaceForJSYogaNode = std::forward<decltype(space)>(space); });
|
||||
}
|
||||
|
||||
DEFINE_VISIT_CHILDREN(JSYogaNode);
|
||||
|
||||
void JSYogaNode::visitChildrenImpl(JSC::JSCell* cell, JSC::Visitor& visitor)
|
||||
{
|
||||
JSYogaNode* thisObject = jsCast<JSYogaNode*>(cell);
|
||||
Base::visitChildren(thisObject, visitor);
|
||||
visitor.append(thisObject->m_measureFunc);
|
||||
visitor.append(thisObject->m_dirtiedFunc);
|
||||
}
|
||||
|
||||
} // namespace Bun
|
||||
49
src/bun.js/bindings/JSYogaNode.h
Normal file
49
src/bun.js/bindings/JSYogaNode.h
Normal file
@@ -0,0 +1,49 @@
|
||||
#pragma once
|
||||
#include "root.h"
|
||||
#include <memory>
|
||||
#include <JavaScriptCore/JSDestructibleObject.h>
|
||||
#include <JavaScriptCore/Strong.h>
|
||||
|
||||
// Forward declarations
|
||||
typedef struct YGNode* YGNodeRef;
|
||||
typedef struct YGConfig* YGConfigRef;
|
||||
typedef const struct YGNode* YGNodeConstRef;
|
||||
|
||||
namespace Bun {
|
||||
|
||||
class JSYogaNode final : public JSC::JSDestructibleObject {
|
||||
public:
|
||||
using Base = JSC::JSDestructibleObject;
|
||||
static constexpr unsigned StructureFlags = Base::StructureFlags;
|
||||
static constexpr JSC::DestructionMode needsDestruction = JSC::NeedsDestruction;
|
||||
|
||||
static JSYogaNode* create(JSC::VM&, JSC::Structure*, YGConfigRef config = nullptr);
|
||||
static void destroy(JSC::JSCell*);
|
||||
static JSC::Structure* createStructure(JSC::VM&, JSC::JSGlobalObject*, JSC::JSValue);
|
||||
~JSYogaNode();
|
||||
|
||||
template<typename, JSC::SubspaceAccess>
|
||||
static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM&);
|
||||
|
||||
DECLARE_INFO;
|
||||
DECLARE_VISIT_CHILDREN;
|
||||
|
||||
YGNodeRef internal() { return m_node; }
|
||||
void clearInternal() { m_node = nullptr; }
|
||||
|
||||
// Helper to get JS wrapper from Yoga node
|
||||
static JSYogaNode* fromYGNode(YGNodeRef);
|
||||
JSC::JSGlobalObject* globalObject() const;
|
||||
|
||||
// Storage for JS callbacks
|
||||
JSC::Strong<JSC::JSObject> m_measureFunc;
|
||||
JSC::Strong<JSC::JSObject> m_dirtiedFunc;
|
||||
|
||||
private:
|
||||
JSYogaNode(JSC::VM&, JSC::Structure*);
|
||||
void finishCreation(JSC::VM&, YGConfigRef config);
|
||||
|
||||
YGNodeRef m_node;
|
||||
};
|
||||
|
||||
} // namespace Bun
|
||||
683
src/bun.js/bindings/JSYogaNodeImpl.cpp
Normal file
683
src/bun.js/bindings/JSYogaNodeImpl.cpp
Normal file
@@ -0,0 +1,683 @@
|
||||
#include "root.h"
|
||||
#include "JSYogaNode.h"
|
||||
#include <JavaScriptCore/JSCInlines.h>
|
||||
#include <yoga/Yoga.h>
|
||||
|
||||
namespace Bun {
|
||||
|
||||
// Helper function to parse value arguments (number, string, object, undefined)
|
||||
static void parseYogaValue(JSC::JSGlobalObject* globalObject, JSC::JSValue arg,
|
||||
std::function<void(float)> setNumber,
|
||||
std::function<void(float)> setPercent,
|
||||
std::function<void()> setAuto,
|
||||
std::function<void()> setUndefined,
|
||||
std::function<void()> setMaxContent = nullptr,
|
||||
std::function<void()> setFitContent = nullptr,
|
||||
std::function<void()> setStretch = nullptr)
|
||||
{
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
if (arg.isNumber()) {
|
||||
setNumber(static_cast<float>(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<float>(valueValue.toNumber(globalObject));
|
||||
RETURN_IF_EXCEPTION(scope, void());
|
||||
|
||||
switch (static_cast<YGUnit>(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<JSYogaNode*>(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<JSYogaNode*>(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<void(YGNodeRef, YGEdge, float)> setNumber,
|
||||
std::function<void(YGNodeRef, YGEdge, float)> setPercent,
|
||||
std::function<void(YGNodeRef, YGEdge)> 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<JSYogaNode*>(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<YGEdge>(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<JSYogaNode*>(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<JSYogaNode*>(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<JSYogaNode*>(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<JSYogaNode*>(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<YGNodeRef>(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<int>(widthMode)));
|
||||
args.append(JSC::jsNumber(height));
|
||||
args.append(JSC::jsNumber(static_cast<int>(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<JSYogaNode*>(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<JSYogaNode*>(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<JSYogaNode*>(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<JSYogaNode*>(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<JSYogaNode*>(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<JSYogaNode*>(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<JSYogaNode*>(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<YGEdge>(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<JSYogaNode*>(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<YGEdge>(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<JSYogaNode*>(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<float>(callFrame->uncheckedArgument(1).toNumber(globalObject));
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
|
||||
YGNodeStyleSetGap(node, static_cast<YGGutter>(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<int>(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<JSYogaNode*>(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<JSYogaNode*>(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<JSYogaNode*>(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<JSYogaNode*>(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<JSYogaNode*>(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<JSYogaNode*>(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<JSYogaNode*>(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<JSYogaNode*>(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<YGEdge>(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<JSYogaNode*>(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<YGEdge>(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<JSYogaNode*>(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<YGEdge>(edge));
|
||||
return JSC::JSValue::encode(ygValueToJS(globalObject, value));
|
||||
}
|
||||
|
||||
} // namespace Bun
|
||||
870
src/bun.js/bindings/JSYogaPrototype.cpp
Normal file
870
src/bun.js/bindings/JSYogaPrototype.cpp
Normal file
@@ -0,0 +1,870 @@
|
||||
#include "root.h"
|
||||
#include "JSYogaPrototype.h"
|
||||
#include "JSYogaConfig.h"
|
||||
#include "JSYogaNode.h"
|
||||
#include <JavaScriptCore/FunctionPrototype.h>
|
||||
#include <JavaScriptCore/JSCInlines.h>
|
||||
#include <yoga/Yoga.h>
|
||||
|
||||
namespace Bun {
|
||||
|
||||
// Config Prototype implementation
|
||||
const JSC::ClassInfo JSYogaConfigPrototype::s_info = { "Yoga.Config"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSYogaConfigPrototype) };
|
||||
|
||||
// Forward declarations for Config prototype methods
|
||||
static JSC_DECLARE_HOST_FUNCTION(jsYogaConfigProtoFuncSetUseWebDefaults);
|
||||
static JSC_DECLARE_HOST_FUNCTION(jsYogaConfigProtoFuncUseWebDefaults);
|
||||
static JSC_DECLARE_HOST_FUNCTION(jsYogaConfigProtoFuncSetExperimentalFeatureEnabled);
|
||||
static JSC_DECLARE_HOST_FUNCTION(jsYogaConfigProtoFuncIsExperimentalFeatureEnabled);
|
||||
static JSC_DECLARE_HOST_FUNCTION(jsYogaConfigProtoFuncSetPointScaleFactor);
|
||||
static JSC_DECLARE_HOST_FUNCTION(jsYogaConfigProtoFuncGetPointScaleFactor);
|
||||
static JSC_DECLARE_HOST_FUNCTION(jsYogaConfigProtoFuncSetErrata);
|
||||
static JSC_DECLARE_HOST_FUNCTION(jsYogaConfigProtoFuncGetErrata);
|
||||
static JSC_DECLARE_HOST_FUNCTION(jsYogaConfigProtoFuncIsEnabledForNodes);
|
||||
static JSC_DECLARE_HOST_FUNCTION(jsYogaConfigProtoFuncFree);
|
||||
|
||||
// Hash table for Config prototype properties
|
||||
static const JSC::HashTableValue JSYogaConfigPrototypeTableValues[] = {
|
||||
{ "setUseWebDefaults"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaConfigProtoFuncSetUseWebDefaults, 1 } },
|
||||
{ "useWebDefaults"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaConfigProtoFuncUseWebDefaults, 0 } },
|
||||
{ "setExperimentalFeatureEnabled"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaConfigProtoFuncSetExperimentalFeatureEnabled, 2 } },
|
||||
{ "isExperimentalFeatureEnabled"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaConfigProtoFuncIsExperimentalFeatureEnabled, 1 } },
|
||||
{ "setPointScaleFactor"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaConfigProtoFuncSetPointScaleFactor, 1 } },
|
||||
{ "getPointScaleFactor"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaConfigProtoFuncGetPointScaleFactor, 0 } },
|
||||
{ "setErrata"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaConfigProtoFuncSetErrata, 1 } },
|
||||
{ "getErrata"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaConfigProtoFuncGetErrata, 0 } },
|
||||
{ "isEnabledForNodes"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaConfigProtoFuncIsEnabledForNodes, 1 } },
|
||||
{ "free"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaConfigProtoFuncFree, 0 } },
|
||||
};
|
||||
|
||||
void JSYogaConfigPrototype::finishCreation(JSC::VM& vm, JSC::JSGlobalObject* globalObject)
|
||||
{
|
||||
Base::finishCreation(vm);
|
||||
reifyStaticProperties(vm, JSYogaConfig::info(), JSYogaConfigPrototypeTableValues, *this);
|
||||
JSC_TO_STRING_TAG_WITHOUT_TRANSITION();
|
||||
}
|
||||
|
||||
// Node Prototype implementation
|
||||
const JSC::ClassInfo JSYogaNodePrototype::s_info = { "Yoga.Node"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSYogaNodePrototype) };
|
||||
|
||||
// Forward declarations for Node prototype methods
|
||||
static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncReset);
|
||||
static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncMarkDirty);
|
||||
static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncIsDirty);
|
||||
static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncCalculateLayout);
|
||||
static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncGetComputedLayout);
|
||||
static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncFree);
|
||||
|
||||
// Style setters
|
||||
static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncSetWidth);
|
||||
static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncSetHeight);
|
||||
static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncSetMinWidth);
|
||||
static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncSetMinHeight);
|
||||
static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncSetMaxWidth);
|
||||
static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncSetMaxHeight);
|
||||
static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncSetFlexBasis);
|
||||
static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncSetMargin);
|
||||
static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncSetPadding);
|
||||
static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncSetPosition);
|
||||
static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncSetGap);
|
||||
|
||||
// Style getters
|
||||
static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncGetWidth);
|
||||
static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncGetHeight);
|
||||
static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncGetMinWidth);
|
||||
static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncGetMinHeight);
|
||||
static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncGetMaxWidth);
|
||||
static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncGetMaxHeight);
|
||||
static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncGetFlexBasis);
|
||||
static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncGetMargin);
|
||||
static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncGetPadding);
|
||||
static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncGetPosition);
|
||||
|
||||
// Layout properties
|
||||
static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncSetFlexDirection);
|
||||
static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncSetJustifyContent);
|
||||
static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncSetAlignItems);
|
||||
static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncSetAlignSelf);
|
||||
static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncSetAlignContent);
|
||||
static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncSetFlexWrap);
|
||||
static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncSetPositionType);
|
||||
static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncSetDisplay);
|
||||
static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncSetOverflow);
|
||||
static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncSetFlex);
|
||||
static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncSetFlexGrow);
|
||||
static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncSetFlexShrink);
|
||||
static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncSetAspectRatio);
|
||||
|
||||
// Hierarchy
|
||||
static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncInsertChild);
|
||||
static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncRemoveChild);
|
||||
static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncGetChildCount);
|
||||
static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncGetChild);
|
||||
static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncGetParent);
|
||||
|
||||
// Callbacks
|
||||
static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncSetMeasureFunc);
|
||||
static JSC_DECLARE_HOST_FUNCTION(jsYogaNodeProtoFuncSetDirtiedFunc);
|
||||
|
||||
// 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[] = {
|
||||
{ "reset"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncReset, 0 } },
|
||||
{ "markDirty"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncMarkDirty, 0 } },
|
||||
{ "isDirty"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncIsDirty, 0 } },
|
||||
{ "calculateLayout"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncCalculateLayout, 3 } },
|
||||
{ "getComputedLayout"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncGetComputedLayout, 0 } },
|
||||
{ "free"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncFree, 0 } },
|
||||
|
||||
// Style setters
|
||||
{ "setWidth"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncSetWidth, 1 } },
|
||||
{ "setHeight"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncSetHeight, 1 } },
|
||||
{ "setMinWidth"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncSetMinWidth, 1 } },
|
||||
{ "setMinHeight"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncSetMinHeight, 1 } },
|
||||
{ "setMaxWidth"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncSetMaxWidth, 1 } },
|
||||
{ "setMaxHeight"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncSetMaxHeight, 1 } },
|
||||
{ "setFlexBasis"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncSetFlexBasis, 1 } },
|
||||
{ "setMargin"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncSetMargin, 2 } },
|
||||
{ "setPadding"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncSetPadding, 2 } },
|
||||
{ "setPosition"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncSetPosition, 2 } },
|
||||
{ "setGap"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncSetGap, 2 } },
|
||||
|
||||
// Style getters
|
||||
{ "getWidth"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncGetWidth, 0 } },
|
||||
{ "getHeight"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncGetHeight, 0 } },
|
||||
{ "getMinWidth"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncGetMinWidth, 0 } },
|
||||
{ "getMinHeight"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncGetMinHeight, 0 } },
|
||||
{ "getMaxWidth"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncGetMaxWidth, 0 } },
|
||||
{ "getMaxHeight"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncGetMaxHeight, 0 } },
|
||||
{ "getFlexBasis"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncGetFlexBasis, 0 } },
|
||||
{ "getMargin"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncGetMargin, 1 } },
|
||||
{ "getPadding"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncGetPadding, 1 } },
|
||||
{ "getPosition"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncGetPosition, 1 } },
|
||||
|
||||
// Layout properties
|
||||
{ "setFlexDirection"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncSetFlexDirection, 1 } },
|
||||
{ "setJustifyContent"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncSetJustifyContent, 1 } },
|
||||
{ "setAlignItems"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncSetAlignItems, 1 } },
|
||||
{ "setAlignSelf"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncSetAlignSelf, 1 } },
|
||||
{ "setAlignContent"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncSetAlignContent, 1 } },
|
||||
{ "setFlexWrap"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncSetFlexWrap, 1 } },
|
||||
{ "setPositionType"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncSetPositionType, 1 } },
|
||||
{ "setDisplay"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncSetDisplay, 1 } },
|
||||
{ "setOverflow"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncSetOverflow, 1 } },
|
||||
{ "setFlex"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncSetFlex, 1 } },
|
||||
{ "setFlexGrow"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncSetFlexGrow, 1 } },
|
||||
{ "setFlexShrink"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncSetFlexShrink, 1 } },
|
||||
{ "setAspectRatio"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncSetAspectRatio, 1 } },
|
||||
|
||||
// Hierarchy
|
||||
{ "insertChild"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncInsertChild, 2 } },
|
||||
{ "removeChild"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncRemoveChild, 1 } },
|
||||
{ "getChildCount"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncGetChildCount, 0 } },
|
||||
{ "getChild"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncGetChild, 1 } },
|
||||
{ "getParent"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncGetParent, 0 } },
|
||||
|
||||
// Callbacks
|
||||
{ "setMeasureFunc"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncSetMeasureFunc, 1 } },
|
||||
{ "setDirtiedFunc"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsYogaNodeProtoFuncSetDirtiedFunc, 1 } },
|
||||
};
|
||||
|
||||
void JSYogaNodePrototype::finishCreation(JSC::VM& vm, JSC::JSGlobalObject* globalObject)
|
||||
{
|
||||
Base::finishCreation(vm);
|
||||
reifyStaticProperties(vm, JSYogaNode::info(), JSYogaNodePrototypeTableValues, *this);
|
||||
JSC_TO_STRING_TAG_WITHOUT_TRANSITION();
|
||||
}
|
||||
|
||||
// Config method implementations
|
||||
JSC_DEFINE_HOST_FUNCTION(jsYogaConfigProtoFuncSetUseWebDefaults, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
|
||||
{
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* thisObject = jsDynamicCast<JSYogaConfig*>(callFrame->thisValue());
|
||||
if (UNLIKELY(!thisObject)) {
|
||||
return JSC::JSValue::encode(Bun::throwThisTypeError(*globalObject, scope, "Yoga.Config"_s, "setUseWebDefaults"_s));
|
||||
}
|
||||
|
||||
bool enabled = true;
|
||||
if (callFrame->argumentCount() > 0) {
|
||||
enabled = callFrame->uncheckedArgument(0).toBoolean(globalObject);
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
}
|
||||
|
||||
YGConfigSetUseWebDefaults(thisObject->internal(), enabled);
|
||||
return JSC::JSValue::encode(JSC::jsUndefined());
|
||||
}
|
||||
|
||||
JSC_DEFINE_HOST_FUNCTION(jsYogaConfigProtoFuncUseWebDefaults, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
|
||||
{
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* thisObject = jsDynamicCast<JSYogaConfig*>(callFrame->thisValue());
|
||||
if (UNLIKELY(!thisObject)) {
|
||||
return JSC::JSValue::encode(Bun::throwThisTypeError(*globalObject, scope, "Yoga.Config"_s, "useWebDefaults"_s));
|
||||
}
|
||||
|
||||
// Legacy method - same as setUseWebDefaults(true)
|
||||
YGConfigSetUseWebDefaults(thisObject->internal(), true);
|
||||
return JSC::JSValue::encode(JSC::jsUndefined());
|
||||
}
|
||||
|
||||
JSC_DEFINE_HOST_FUNCTION(jsYogaConfigProtoFuncSetExperimentalFeatureEnabled, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
|
||||
{
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* thisObject = jsDynamicCast<JSYogaConfig*>(callFrame->thisValue());
|
||||
if (UNLIKELY(!thisObject)) {
|
||||
return JSC::JSValue::encode(Bun::throwThisTypeError(*globalObject, scope, "Yoga.Config"_s, "setExperimentalFeatureEnabled"_s));
|
||||
}
|
||||
|
||||
if (callFrame->argumentCount() < 2) {
|
||||
throwTypeError(globalObject, scope, "setExperimentalFeatureEnabled requires 2 arguments"_s);
|
||||
return {};
|
||||
}
|
||||
|
||||
int32_t feature = callFrame->uncheckedArgument(0).toInt32(globalObject);
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
|
||||
bool enabled = callFrame->uncheckedArgument(1).toBoolean(globalObject);
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
|
||||
YGConfigSetExperimentalFeatureEnabled(thisObject->internal(), static_cast<YGExperimentalFeature>(feature), enabled);
|
||||
return JSC::JSValue::encode(JSC::jsUndefined());
|
||||
}
|
||||
|
||||
JSC_DEFINE_HOST_FUNCTION(jsYogaConfigProtoFuncIsExperimentalFeatureEnabled, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
|
||||
{
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* thisObject = jsDynamicCast<JSYogaConfig*>(callFrame->thisValue());
|
||||
if (UNLIKELY(!thisObject)) {
|
||||
return JSC::JSValue::encode(Bun::throwThisTypeError(*globalObject, scope, "Yoga.Config"_s, "isExperimentalFeatureEnabled"_s));
|
||||
}
|
||||
|
||||
if (callFrame->argumentCount() < 1) {
|
||||
throwTypeError(globalObject, scope, "isExperimentalFeatureEnabled requires 1 argument"_s);
|
||||
return {};
|
||||
}
|
||||
|
||||
int32_t feature = callFrame->uncheckedArgument(0).toInt32(globalObject);
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
|
||||
bool enabled = YGConfigIsExperimentalFeatureEnabled(thisObject->internal(), static_cast<YGExperimentalFeature>(feature));
|
||||
return JSC::JSValue::encode(JSC::jsBoolean(enabled));
|
||||
}
|
||||
|
||||
JSC_DEFINE_HOST_FUNCTION(jsYogaConfigProtoFuncSetPointScaleFactor, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
|
||||
{
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* thisObject = jsDynamicCast<JSYogaConfig*>(callFrame->thisValue());
|
||||
if (UNLIKELY(!thisObject)) {
|
||||
return JSC::JSValue::encode(Bun::throwThisTypeError(*globalObject, scope, "Yoga.Config"_s, "setPointScaleFactor"_s));
|
||||
}
|
||||
|
||||
if (callFrame->argumentCount() < 1) {
|
||||
throwTypeError(globalObject, scope, "setPointScaleFactor requires 1 argument"_s);
|
||||
return {};
|
||||
}
|
||||
|
||||
double scaleFactor = callFrame->uncheckedArgument(0).toNumber(globalObject);
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
|
||||
YGConfigSetPointScaleFactor(thisObject->internal(), static_cast<float>(scaleFactor));
|
||||
return JSC::JSValue::encode(JSC::jsUndefined());
|
||||
}
|
||||
|
||||
JSC_DEFINE_HOST_FUNCTION(jsYogaConfigProtoFuncIsEnabledForNodes, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
|
||||
{
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* thisObject = jsDynamicCast<JSYogaConfig*>(callFrame->thisValue());
|
||||
if (UNLIKELY(!thisObject)) {
|
||||
return JSC::JSValue::encode(Bun::throwThisTypeError(*globalObject, scope, "Yoga.Config"_s, "isEnabledForNodes"_s));
|
||||
}
|
||||
|
||||
// This method checks if a config is actively being used by any nodes
|
||||
// In the future, we might track this, but for now always return true if valid config
|
||||
return JSC::JSValue::encode(JSC::jsBoolean(thisObject->internal() != nullptr));
|
||||
}
|
||||
|
||||
JSC_DEFINE_HOST_FUNCTION(jsYogaConfigProtoFuncGetPointScaleFactor, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
|
||||
{
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* thisObject = jsDynamicCast<JSYogaConfig*>(callFrame->thisValue());
|
||||
if (UNLIKELY(!thisObject)) {
|
||||
return JSC::JSValue::encode(Bun::throwThisTypeError(*globalObject, scope, "Yoga.Config"_s, "getPointScaleFactor"_s));
|
||||
}
|
||||
|
||||
float scaleFactor = YGConfigGetPointScaleFactor(thisObject->internal());
|
||||
return JSC::JSValue::encode(JSC::jsNumber(scaleFactor));
|
||||
}
|
||||
|
||||
JSC_DEFINE_HOST_FUNCTION(jsYogaConfigProtoFuncSetErrata, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
|
||||
{
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* thisObject = jsDynamicCast<JSYogaConfig*>(callFrame->thisValue());
|
||||
if (UNLIKELY(!thisObject)) {
|
||||
return JSC::JSValue::encode(Bun::throwThisTypeError(*globalObject, scope, "Yoga.Config"_s, "setErrata"_s));
|
||||
}
|
||||
|
||||
if (callFrame->argumentCount() < 1) {
|
||||
throwTypeError(globalObject, scope, "setErrata requires 1 argument"_s);
|
||||
return {};
|
||||
}
|
||||
|
||||
int32_t errata = callFrame->uncheckedArgument(0).toInt32(globalObject);
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
|
||||
YGConfigSetErrata(thisObject->internal(), static_cast<YGErrata>(errata));
|
||||
return JSC::JSValue::encode(JSC::jsUndefined());
|
||||
}
|
||||
|
||||
JSC_DEFINE_HOST_FUNCTION(jsYogaConfigProtoFuncGetErrata, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
|
||||
{
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* thisObject = jsDynamicCast<JSYogaConfig*>(callFrame->thisValue());
|
||||
if (UNLIKELY(!thisObject)) {
|
||||
return JSC::JSValue::encode(Bun::throwThisTypeError(*globalObject, scope, "Yoga.Config"_s, "getErrata"_s));
|
||||
}
|
||||
|
||||
YGErrata errata = YGConfigGetErrata(thisObject->internal());
|
||||
return JSC::JSValue::encode(JSC::jsNumber(static_cast<int32_t>(errata)));
|
||||
}
|
||||
|
||||
JSC_DEFINE_HOST_FUNCTION(jsYogaConfigProtoFuncFree, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
|
||||
{
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* thisObject = jsDynamicCast<JSYogaConfig*>(callFrame->thisValue());
|
||||
if (UNLIKELY(!thisObject)) {
|
||||
return JSC::JSValue::encode(Bun::throwThisTypeError(*globalObject, scope, "Yoga.Config"_s, "free"_s));
|
||||
}
|
||||
|
||||
// Mark the config as freed by setting internal pointer to nullptr
|
||||
// The actual cleanup will happen in the destructor
|
||||
if (thisObject->internal()) {
|
||||
YGConfigFree(thisObject->internal());
|
||||
thisObject->clearInternal();
|
||||
}
|
||||
|
||||
return JSC::JSValue::encode(JSC::jsUndefined());
|
||||
}
|
||||
|
||||
// Node method implementations
|
||||
JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncReset, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
|
||||
{
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* thisObject = jsDynamicCast<JSYogaNode*>(callFrame->thisValue());
|
||||
if (UNLIKELY(!thisObject)) {
|
||||
return JSC::JSValue::encode(Bun::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "reset"_s));
|
||||
}
|
||||
|
||||
YGNodeReset(thisObject->internal());
|
||||
return JSC::JSValue::encode(JSC::jsUndefined());
|
||||
}
|
||||
|
||||
JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncMarkDirty, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
|
||||
{
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* thisObject = jsDynamicCast<JSYogaNode*>(callFrame->thisValue());
|
||||
if (UNLIKELY(!thisObject)) {
|
||||
return JSC::JSValue::encode(Bun::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "markDirty"_s));
|
||||
}
|
||||
|
||||
YGNodeMarkDirty(thisObject->internal());
|
||||
return JSC::JSValue::encode(JSC::jsUndefined());
|
||||
}
|
||||
|
||||
JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncIsDirty, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
|
||||
{
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* thisObject = jsDynamicCast<JSYogaNode*>(callFrame->thisValue());
|
||||
if (UNLIKELY(!thisObject)) {
|
||||
return JSC::JSValue::encode(Bun::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "isDirty"_s));
|
||||
}
|
||||
|
||||
bool isDirty = YGNodeIsDirty(thisObject->internal());
|
||||
return JSC::JSValue::encode(JSC::jsBoolean(isDirty));
|
||||
}
|
||||
|
||||
JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncCalculateLayout, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
|
||||
{
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* thisObject = jsDynamicCast<JSYogaNode*>(callFrame->thisValue());
|
||||
if (UNLIKELY(!thisObject)) {
|
||||
return JSC::JSValue::encode(Bun::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "calculateLayout"_s));
|
||||
}
|
||||
|
||||
float width = YGUndefined;
|
||||
float height = YGUndefined;
|
||||
YGDirection direction = YGDirectionLTR;
|
||||
|
||||
// Parse arguments: calculateLayout(width?, height?, direction?)
|
||||
if (callFrame->argumentCount() > 0) {
|
||||
JSValue widthArg = callFrame->uncheckedArgument(0);
|
||||
if (!widthArg.isUndefinedOrNull()) {
|
||||
width = static_cast<float>(widthArg.toNumber(globalObject));
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
}
|
||||
}
|
||||
|
||||
if (callFrame->argumentCount() > 1) {
|
||||
JSValue heightArg = callFrame->uncheckedArgument(1);
|
||||
if (!heightArg.isUndefinedOrNull()) {
|
||||
height = static_cast<float>(heightArg.toNumber(globalObject));
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
}
|
||||
}
|
||||
|
||||
if (callFrame->argumentCount() > 2) {
|
||||
int32_t dir = callFrame->uncheckedArgument(2).toInt32(globalObject);
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
direction = static_cast<YGDirection>(dir);
|
||||
}
|
||||
|
||||
YGNodeCalculateLayout(thisObject->internal(), width, height, direction);
|
||||
return JSC::JSValue::encode(JSC::jsUndefined());
|
||||
}
|
||||
|
||||
JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncGetComputedLayout, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
|
||||
{
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* thisObject = jsDynamicCast<JSYogaNode*>(callFrame->thisValue());
|
||||
if (UNLIKELY(!thisObject)) {
|
||||
return JSC::JSValue::encode(Bun::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "getComputedLayout"_s));
|
||||
}
|
||||
|
||||
// Create object with computed layout values
|
||||
JSC::JSObject* layout = JSC::constructEmptyObject(globalObject);
|
||||
|
||||
YGNodeRef node = thisObject->internal();
|
||||
|
||||
layout->putDirect(vm, JSC::Identifier::fromString(vm, "left"_s), JSC::jsNumber(YGNodeLayoutGetLeft(node)));
|
||||
layout->putDirect(vm, JSC::Identifier::fromString(vm, "top"_s), JSC::jsNumber(YGNodeLayoutGetTop(node)));
|
||||
layout->putDirect(vm, JSC::Identifier::fromString(vm, "width"_s), JSC::jsNumber(YGNodeLayoutGetWidth(node)));
|
||||
layout->putDirect(vm, JSC::Identifier::fromString(vm, "height"_s), JSC::jsNumber(YGNodeLayoutGetHeight(node)));
|
||||
|
||||
return JSC::JSValue::encode(layout);
|
||||
}
|
||||
|
||||
JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncFree, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
|
||||
{
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* thisObject = jsDynamicCast<JSYogaNode*>(callFrame->thisValue());
|
||||
if (UNLIKELY(!thisObject)) {
|
||||
return JSC::JSValue::encode(Bun::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "free"_s));
|
||||
}
|
||||
|
||||
// Clear the internal pointer - actual cleanup in destructor
|
||||
if (thisObject->internal()) {
|
||||
YGNodeFree(thisObject->internal());
|
||||
thisObject->clearInternal();
|
||||
}
|
||||
|
||||
return JSC::JSValue::encode(JSC::jsUndefined());
|
||||
}
|
||||
|
||||
// Layout property setters (simple enum setters)
|
||||
JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncSetFlexDirection, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
|
||||
{
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* thisObject = jsDynamicCast<JSYogaNode*>(callFrame->thisValue());
|
||||
if (UNLIKELY(!thisObject)) {
|
||||
return JSC::JSValue::encode(Bun::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "setFlexDirection"_s));
|
||||
}
|
||||
|
||||
if (callFrame->argumentCount() < 1) {
|
||||
return JSC::JSValue::encode(JSC::jsUndefined());
|
||||
}
|
||||
|
||||
int32_t direction = callFrame->uncheckedArgument(0).toInt32(globalObject);
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
|
||||
YGNodeStyleSetFlexDirection(thisObject->internal(), static_cast<YGFlexDirection>(direction));
|
||||
return JSC::JSValue::encode(JSC::jsUndefined());
|
||||
}
|
||||
|
||||
JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncSetJustifyContent, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
|
||||
{
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* thisObject = jsDynamicCast<JSYogaNode*>(callFrame->thisValue());
|
||||
if (UNLIKELY(!thisObject)) {
|
||||
return JSC::JSValue::encode(Bun::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "setJustifyContent"_s));
|
||||
}
|
||||
|
||||
if (callFrame->argumentCount() < 1) {
|
||||
return JSC::JSValue::encode(JSC::jsUndefined());
|
||||
}
|
||||
|
||||
int32_t justify = callFrame->uncheckedArgument(0).toInt32(globalObject);
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
|
||||
YGNodeStyleSetJustifyContent(thisObject->internal(), static_cast<YGJustify>(justify));
|
||||
return JSC::JSValue::encode(JSC::jsUndefined());
|
||||
}
|
||||
|
||||
JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncSetAlignItems, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
|
||||
{
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* thisObject = jsDynamicCast<JSYogaNode*>(callFrame->thisValue());
|
||||
if (UNLIKELY(!thisObject)) {
|
||||
return JSC::JSValue::encode(Bun::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "setAlignItems"_s));
|
||||
}
|
||||
|
||||
if (callFrame->argumentCount() < 1) {
|
||||
return JSC::JSValue::encode(JSC::jsUndefined());
|
||||
}
|
||||
|
||||
int32_t align = callFrame->uncheckedArgument(0).toInt32(globalObject);
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
|
||||
YGNodeStyleSetAlignItems(thisObject->internal(), static_cast<YGAlign>(align));
|
||||
return JSC::JSValue::encode(JSC::jsUndefined());
|
||||
}
|
||||
|
||||
JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncSetAlignSelf, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
|
||||
{
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* thisObject = jsDynamicCast<JSYogaNode*>(callFrame->thisValue());
|
||||
if (UNLIKELY(!thisObject)) {
|
||||
return JSC::JSValue::encode(Bun::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "setAlignSelf"_s));
|
||||
}
|
||||
|
||||
if (callFrame->argumentCount() < 1) {
|
||||
return JSC::JSValue::encode(JSC::jsUndefined());
|
||||
}
|
||||
|
||||
int32_t align = callFrame->uncheckedArgument(0).toInt32(globalObject);
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
|
||||
YGNodeStyleSetAlignSelf(thisObject->internal(), static_cast<YGAlign>(align));
|
||||
return JSC::JSValue::encode(JSC::jsUndefined());
|
||||
}
|
||||
|
||||
JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncSetAlignContent, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
|
||||
{
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* thisObject = jsDynamicCast<JSYogaNode*>(callFrame->thisValue());
|
||||
if (UNLIKELY(!thisObject)) {
|
||||
return JSC::JSValue::encode(Bun::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "setAlignContent"_s));
|
||||
}
|
||||
|
||||
if (callFrame->argumentCount() < 1) {
|
||||
return JSC::JSValue::encode(JSC::jsUndefined());
|
||||
}
|
||||
|
||||
int32_t align = callFrame->uncheckedArgument(0).toInt32(globalObject);
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
|
||||
YGNodeStyleSetAlignContent(thisObject->internal(), static_cast<YGAlign>(align));
|
||||
return JSC::JSValue::encode(JSC::jsUndefined());
|
||||
}
|
||||
|
||||
JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncSetFlexWrap, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
|
||||
{
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* thisObject = jsDynamicCast<JSYogaNode*>(callFrame->thisValue());
|
||||
if (UNLIKELY(!thisObject)) {
|
||||
return JSC::JSValue::encode(Bun::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "setFlexWrap"_s));
|
||||
}
|
||||
|
||||
if (callFrame->argumentCount() < 1) {
|
||||
return JSC::JSValue::encode(JSC::jsUndefined());
|
||||
}
|
||||
|
||||
int32_t wrap = callFrame->uncheckedArgument(0).toInt32(globalObject);
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
|
||||
YGNodeStyleSetFlexWrap(thisObject->internal(), static_cast<YGWrap>(wrap));
|
||||
return JSC::JSValue::encode(JSC::jsUndefined());
|
||||
}
|
||||
|
||||
JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncSetPositionType, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
|
||||
{
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* thisObject = jsDynamicCast<JSYogaNode*>(callFrame->thisValue());
|
||||
if (UNLIKELY(!thisObject)) {
|
||||
return JSC::JSValue::encode(Bun::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "setPositionType"_s));
|
||||
}
|
||||
|
||||
if (callFrame->argumentCount() < 1) {
|
||||
return JSC::JSValue::encode(JSC::jsUndefined());
|
||||
}
|
||||
|
||||
int32_t posType = callFrame->uncheckedArgument(0).toInt32(globalObject);
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
|
||||
YGNodeStyleSetPositionType(thisObject->internal(), static_cast<YGPositionType>(posType));
|
||||
return JSC::JSValue::encode(JSC::jsUndefined());
|
||||
}
|
||||
|
||||
JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncSetDisplay, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
|
||||
{
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* thisObject = jsDynamicCast<JSYogaNode*>(callFrame->thisValue());
|
||||
if (UNLIKELY(!thisObject)) {
|
||||
return JSC::JSValue::encode(Bun::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "setDisplay"_s));
|
||||
}
|
||||
|
||||
if (callFrame->argumentCount() < 1) {
|
||||
return JSC::JSValue::encode(JSC::jsUndefined());
|
||||
}
|
||||
|
||||
int32_t display = callFrame->uncheckedArgument(0).toInt32(globalObject);
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
|
||||
YGNodeStyleSetDisplay(thisObject->internal(), static_cast<YGDisplay>(display));
|
||||
return JSC::JSValue::encode(JSC::jsUndefined());
|
||||
}
|
||||
|
||||
JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncSetOverflow, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
|
||||
{
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* thisObject = jsDynamicCast<JSYogaNode*>(callFrame->thisValue());
|
||||
if (UNLIKELY(!thisObject)) {
|
||||
return JSC::JSValue::encode(Bun::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "setOverflow"_s));
|
||||
}
|
||||
|
||||
if (callFrame->argumentCount() < 1) {
|
||||
return JSC::JSValue::encode(JSC::jsUndefined());
|
||||
}
|
||||
|
||||
int32_t overflow = callFrame->uncheckedArgument(0).toInt32(globalObject);
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
|
||||
YGNodeStyleSetOverflow(thisObject->internal(), static_cast<YGOverflow>(overflow));
|
||||
return JSC::JSValue::encode(JSC::jsUndefined());
|
||||
}
|
||||
|
||||
// Flex properties
|
||||
JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncSetFlex, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
|
||||
{
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* thisObject = jsDynamicCast<JSYogaNode*>(callFrame->thisValue());
|
||||
if (UNLIKELY(!thisObject)) {
|
||||
return JSC::JSValue::encode(Bun::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "setFlex"_s));
|
||||
}
|
||||
|
||||
if (callFrame->argumentCount() < 1) {
|
||||
return JSC::JSValue::encode(JSC::jsUndefined());
|
||||
}
|
||||
|
||||
float flex = static_cast<float>(callFrame->uncheckedArgument(0).toNumber(globalObject));
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
|
||||
YGNodeStyleSetFlex(thisObject->internal(), flex);
|
||||
return JSC::JSValue::encode(JSC::jsUndefined());
|
||||
}
|
||||
|
||||
JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncSetFlexGrow, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
|
||||
{
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* thisObject = jsDynamicCast<JSYogaNode*>(callFrame->thisValue());
|
||||
if (UNLIKELY(!thisObject)) {
|
||||
return JSC::JSValue::encode(Bun::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "setFlexGrow"_s));
|
||||
}
|
||||
|
||||
if (callFrame->argumentCount() < 1) {
|
||||
return JSC::JSValue::encode(JSC::jsUndefined());
|
||||
}
|
||||
|
||||
float flexGrow = static_cast<float>(callFrame->uncheckedArgument(0).toNumber(globalObject));
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
|
||||
YGNodeStyleSetFlexGrow(thisObject->internal(), flexGrow);
|
||||
return JSC::JSValue::encode(JSC::jsUndefined());
|
||||
}
|
||||
|
||||
JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncSetFlexShrink, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
|
||||
{
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* thisObject = jsDynamicCast<JSYogaNode*>(callFrame->thisValue());
|
||||
if (UNLIKELY(!thisObject)) {
|
||||
return JSC::JSValue::encode(Bun::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "setFlexShrink"_s));
|
||||
}
|
||||
|
||||
if (callFrame->argumentCount() < 1) {
|
||||
return JSC::JSValue::encode(JSC::jsUndefined());
|
||||
}
|
||||
|
||||
float flexShrink = static_cast<float>(callFrame->uncheckedArgument(0).toNumber(globalObject));
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
|
||||
YGNodeStyleSetFlexShrink(thisObject->internal(), flexShrink);
|
||||
return JSC::JSValue::encode(JSC::jsUndefined());
|
||||
}
|
||||
|
||||
JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncSetAspectRatio, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
|
||||
{
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* thisObject = jsDynamicCast<JSYogaNode*>(callFrame->thisValue());
|
||||
if (UNLIKELY(!thisObject)) {
|
||||
return JSC::JSValue::encode(Bun::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "setAspectRatio"_s));
|
||||
}
|
||||
|
||||
if (callFrame->argumentCount() < 1) {
|
||||
return JSC::JSValue::encode(JSC::jsUndefined());
|
||||
}
|
||||
|
||||
JSValue arg = callFrame->uncheckedArgument(0);
|
||||
|
||||
if (arg.isUndefinedOrNull()) {
|
||||
YGNodeStyleSetAspectRatio(thisObject->internal(), YGUndefined);
|
||||
} else {
|
||||
float aspectRatio = static_cast<float>(arg.toNumber(globalObject));
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
YGNodeStyleSetAspectRatio(thisObject->internal(), aspectRatio);
|
||||
}
|
||||
|
||||
return JSC::JSValue::encode(JSC::jsUndefined());
|
||||
}
|
||||
|
||||
// Hierarchy methods
|
||||
JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncRemoveChild, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
|
||||
{
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* thisObject = jsDynamicCast<JSYogaNode*>(callFrame->thisValue());
|
||||
if (UNLIKELY(!thisObject)) {
|
||||
return JSC::JSValue::encode(Bun::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "removeChild"_s));
|
||||
}
|
||||
|
||||
if (callFrame->argumentCount() < 1) {
|
||||
throwTypeError(globalObject, scope, "removeChild requires 1 argument"_s);
|
||||
return {};
|
||||
}
|
||||
|
||||
auto* childNode = jsDynamicCast<JSYogaNode*>(callFrame->uncheckedArgument(0));
|
||||
if (!childNode) {
|
||||
throwTypeError(globalObject, scope, "Argument must be a Yoga.Node"_s);
|
||||
return {};
|
||||
}
|
||||
|
||||
YGNodeRemoveChild(thisObject->internal(), childNode->internal());
|
||||
return JSC::JSValue::encode(JSC::jsUndefined());
|
||||
}
|
||||
|
||||
JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncGetChildCount, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
|
||||
{
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* thisObject = jsDynamicCast<JSYogaNode*>(callFrame->thisValue());
|
||||
if (UNLIKELY(!thisObject)) {
|
||||
return JSC::JSValue::encode(Bun::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "getChildCount"_s));
|
||||
}
|
||||
|
||||
uint32_t count = YGNodeGetChildCount(thisObject->internal());
|
||||
return JSC::JSValue::encode(JSC::jsNumber(count));
|
||||
}
|
||||
|
||||
// Dirtied function callback
|
||||
static void bunDirtiedCallback(YGNodeConstRef ygNode)
|
||||
{
|
||||
JSYogaNode* jsNode = JSYogaNode::fromYGNode(const_cast<YGNodeRef>(ygNode));
|
||||
if (!jsNode || !jsNode->m_dirtiedFunc) return;
|
||||
|
||||
JSC::JSGlobalObject* globalObject = jsNode->globalObject();
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
JSC::JSLockHolder lock(vm);
|
||||
auto scope = DECLARE_CATCH_SCOPE(vm);
|
||||
|
||||
JSC::MarkedArgumentBuffer args;
|
||||
JSC::call(globalObject, jsNode->m_dirtiedFunc.get(), jsNode, args);
|
||||
if (scope.exception()) {
|
||||
scope.clearException();
|
||||
}
|
||||
}
|
||||
|
||||
JSC_DEFINE_HOST_FUNCTION(jsYogaNodeProtoFuncSetDirtiedFunc, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
|
||||
{
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* thisObject = jsDynamicCast<JSYogaNode*>(callFrame->thisValue());
|
||||
if (UNLIKELY(!thisObject)) {
|
||||
return JSC::JSValue::encode(Bun::throwThisTypeError(*globalObject, scope, "Yoga.Node"_s, "setDirtiedFunc"_s));
|
||||
}
|
||||
|
||||
if (callFrame->argumentCount() < 1) {
|
||||
return JSC::JSValue::encode(JSC::jsUndefined());
|
||||
}
|
||||
|
||||
JSValue func = callFrame->uncheckedArgument(0);
|
||||
if (func.isUndefinedOrNull()) {
|
||||
thisObject->m_dirtiedFunc.clear();
|
||||
YGNodeSetDirtiedFunc(thisObject->internal(), nullptr);
|
||||
} else if (func.isCallable()) {
|
||||
thisObject->m_dirtiedFunc.set(vm, thisObject, func.getObject());
|
||||
YGNodeSetDirtiedFunc(thisObject->internal(), bunDirtiedCallback);
|
||||
} else {
|
||||
throwTypeError(globalObject, scope, "Dirtied function must be callable or null"_s);
|
||||
return {};
|
||||
}
|
||||
|
||||
return JSC::JSValue::encode(JSC::jsUndefined());
|
||||
}
|
||||
|
||||
} // namespace Bun
|
||||
80
src/bun.js/bindings/JSYogaPrototype.h
Normal file
80
src/bun.js/bindings/JSYogaPrototype.h
Normal file
@@ -0,0 +1,80 @@
|
||||
#pragma once
|
||||
#include "root.h"
|
||||
#include <JavaScriptCore/JSObject.h>
|
||||
|
||||
namespace Bun {
|
||||
|
||||
// Base class for Yoga prototypes
|
||||
class JSYogaConfigPrototype final : public JSC::JSNonFinalObject {
|
||||
public:
|
||||
using Base = JSC::JSNonFinalObject;
|
||||
static constexpr unsigned StructureFlags = Base::StructureFlags;
|
||||
|
||||
static JSYogaConfigPrototype* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure)
|
||||
{
|
||||
JSYogaConfigPrototype* prototype = new (NotNull, allocateCell<JSYogaConfigPrototype>(vm)) JSYogaConfigPrototype(vm, structure);
|
||||
prototype->finishCreation(vm, globalObject);
|
||||
return prototype;
|
||||
}
|
||||
|
||||
template<typename, JSC::SubspaceAccess>
|
||||
static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm)
|
||||
{
|
||||
return &vm.plainObjectSpace();
|
||||
}
|
||||
|
||||
DECLARE_INFO;
|
||||
|
||||
static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype)
|
||||
{
|
||||
auto* structure = JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info());
|
||||
structure->setMayBePrototype(true);
|
||||
return structure;
|
||||
}
|
||||
|
||||
private:
|
||||
JSYogaConfigPrototype(JSC::VM& vm, JSC::Structure* structure)
|
||||
: Base(vm, structure)
|
||||
{
|
||||
}
|
||||
|
||||
void finishCreation(JSC::VM& vm, JSC::JSGlobalObject* globalObject);
|
||||
};
|
||||
|
||||
class JSYogaNodePrototype final : public JSC::JSNonFinalObject {
|
||||
public:
|
||||
using Base = JSC::JSNonFinalObject;
|
||||
static constexpr unsigned StructureFlags = Base::StructureFlags;
|
||||
|
||||
static JSYogaNodePrototype* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure)
|
||||
{
|
||||
JSYogaNodePrototype* prototype = new (NotNull, allocateCell<JSYogaNodePrototype>(vm)) JSYogaNodePrototype(vm, structure);
|
||||
prototype->finishCreation(vm, globalObject);
|
||||
return prototype;
|
||||
}
|
||||
|
||||
template<typename, JSC::SubspaceAccess>
|
||||
static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm)
|
||||
{
|
||||
return &vm.plainObjectSpace();
|
||||
}
|
||||
|
||||
DECLARE_INFO;
|
||||
|
||||
static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype)
|
||||
{
|
||||
auto* structure = JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info());
|
||||
structure->setMayBePrototype(true);
|
||||
return structure;
|
||||
}
|
||||
|
||||
private:
|
||||
JSYogaNodePrototype(JSC::VM& vm, JSC::Structure* structure)
|
||||
: Base(vm, structure)
|
||||
{
|
||||
}
|
||||
|
||||
void finishCreation(JSC::VM& vm, JSC::JSGlobalObject* globalObject);
|
||||
};
|
||||
|
||||
} // namespace Bun
|
||||
@@ -71,6 +71,8 @@ public:
|
||||
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForJSS3File;
|
||||
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForJSX509Certificate;
|
||||
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForJSNodePerformanceHooksHistogram;
|
||||
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForJSYogaConfig;
|
||||
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForJSYogaNode;
|
||||
#include "ZigGeneratedClasses+DOMClientIsoSubspaces.h"
|
||||
/* --- bun --- */
|
||||
|
||||
|
||||
@@ -68,6 +68,8 @@ public:
|
||||
std::unique_ptr<IsoSubspace> m_subspaceForJSS3File;
|
||||
std::unique_ptr<IsoSubspace> m_subspaceForJSX509Certificate;
|
||||
std::unique_ptr<IsoSubspace> m_subspaceForJSNodePerformanceHooksHistogram;
|
||||
std::unique_ptr<IsoSubspace> m_subspaceForJSYogaConfig;
|
||||
std::unique_ptr<IsoSubspace> m_subspaceForJSYogaNode;
|
||||
#include "ZigGeneratedClasses+DOMIsoSubspaces.h"
|
||||
/*-- BUN --*/
|
||||
|
||||
|
||||
100
test/js/bun/yoga-config.test.js
Normal file
100
test/js/bun/yoga-config.test.js
Normal file
@@ -0,0 +1,100 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
|
||||
describe("Yoga.Config", () => {
|
||||
test("Config constructor", () => {
|
||||
const config = new Yoga.Config();
|
||||
expect(config).toBeDefined();
|
||||
expect(config.constructor.name).toBe("Config");
|
||||
});
|
||||
|
||||
test("Config.create() static method", () => {
|
||||
const config = Yoga.Config.create();
|
||||
expect(config).toBeDefined();
|
||||
expect(config.constructor.name).toBe("Config");
|
||||
});
|
||||
|
||||
test("setUseWebDefaults", () => {
|
||||
const config = new Yoga.Config();
|
||||
|
||||
// Should not throw
|
||||
expect(() => config.setUseWebDefaults(true)).not.toThrow();
|
||||
expect(() => config.setUseWebDefaults(false)).not.toThrow();
|
||||
expect(() => config.setUseWebDefaults()).not.toThrow(); // defaults to true
|
||||
});
|
||||
|
||||
test("useWebDefaults (legacy)", () => {
|
||||
const config = new Yoga.Config();
|
||||
|
||||
// Should not throw
|
||||
expect(() => config.useWebDefaults()).not.toThrow();
|
||||
});
|
||||
|
||||
test("setPointScaleFactor and getPointScaleFactor", () => {
|
||||
const config = new Yoga.Config();
|
||||
|
||||
config.setPointScaleFactor(2.0);
|
||||
expect(config.getPointScaleFactor()).toBe(2.0);
|
||||
|
||||
config.setPointScaleFactor(0); // disable pixel rounding
|
||||
expect(config.getPointScaleFactor()).toBe(0);
|
||||
|
||||
config.setPointScaleFactor(3.5);
|
||||
expect(config.getPointScaleFactor()).toBe(3.5);
|
||||
});
|
||||
|
||||
test("setErrata and getErrata", () => {
|
||||
const config = new Yoga.Config();
|
||||
|
||||
// Test with different errata values
|
||||
config.setErrata(Yoga.Errata.None);
|
||||
expect(config.getErrata()).toBe(Yoga.Errata.None);
|
||||
|
||||
config.setErrata(Yoga.Errata.Classic);
|
||||
expect(config.getErrata()).toBe(Yoga.Errata.Classic);
|
||||
|
||||
config.setErrata(Yoga.Errata.All);
|
||||
expect(config.getErrata()).toBe(Yoga.Errata.All);
|
||||
});
|
||||
|
||||
test("setExperimentalFeatureEnabled and isExperimentalFeatureEnabled", () => {
|
||||
const config = new Yoga.Config();
|
||||
|
||||
// Test with a hypothetical experimental feature
|
||||
const feature = 0; // Assuming 0 is a valid experimental feature
|
||||
|
||||
config.setExperimentalFeatureEnabled(feature, true);
|
||||
expect(config.isExperimentalFeatureEnabled(feature)).toBe(true);
|
||||
|
||||
config.setExperimentalFeatureEnabled(feature, false);
|
||||
expect(config.isExperimentalFeatureEnabled(feature)).toBe(false);
|
||||
});
|
||||
|
||||
test("isEnabledForNodes", () => {
|
||||
const config = new Yoga.Config();
|
||||
|
||||
// Should return true for a valid config
|
||||
expect(config.isEnabledForNodes()).toBe(true);
|
||||
});
|
||||
|
||||
test("free", () => {
|
||||
const config = new Yoga.Config();
|
||||
|
||||
// Should not throw
|
||||
expect(() => config.free()).not.toThrow();
|
||||
|
||||
// After free, methods should throw or handle gracefully
|
||||
// This depends on implementation - for now just test it doesn't crash
|
||||
expect(() => config.free()).not.toThrow(); // double free should be safe
|
||||
});
|
||||
|
||||
test("error handling", () => {
|
||||
const config = new Yoga.Config();
|
||||
|
||||
// Test invalid arguments
|
||||
expect(() => config.setErrata()).toThrow();
|
||||
expect(() => config.setExperimentalFeatureEnabled()).toThrow();
|
||||
expect(() => config.setExperimentalFeatureEnabled(0)).toThrow(); // missing second arg
|
||||
expect(() => config.isExperimentalFeatureEnabled()).toThrow();
|
||||
expect(() => config.setPointScaleFactor()).toThrow();
|
||||
});
|
||||
});
|
||||
121
test/js/bun/yoga-constants.test.js
Normal file
121
test/js/bun/yoga-constants.test.js
Normal file
@@ -0,0 +1,121 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
|
||||
// Test if we can access Yoga via globalThis (once it's exposed)
|
||||
const Yoga = globalThis.Yoga;
|
||||
|
||||
describe("Yoga Constants", () => {
|
||||
test("should export all alignment constants", () => {
|
||||
expect(Yoga.ALIGN_AUTO).toBeDefined();
|
||||
expect(Yoga.ALIGN_FLEX_START).toBeDefined();
|
||||
expect(Yoga.ALIGN_CENTER).toBeDefined();
|
||||
expect(Yoga.ALIGN_FLEX_END).toBeDefined();
|
||||
expect(Yoga.ALIGN_STRETCH).toBeDefined();
|
||||
expect(Yoga.ALIGN_BASELINE).toBeDefined();
|
||||
expect(Yoga.ALIGN_SPACE_BETWEEN).toBeDefined();
|
||||
expect(Yoga.ALIGN_SPACE_AROUND).toBeDefined();
|
||||
expect(Yoga.ALIGN_SPACE_EVENLY).toBeDefined();
|
||||
});
|
||||
|
||||
test("should export all direction constants", () => {
|
||||
expect(Yoga.DIRECTION_INHERIT).toBeDefined();
|
||||
expect(Yoga.DIRECTION_LTR).toBeDefined();
|
||||
expect(Yoga.DIRECTION_RTL).toBeDefined();
|
||||
});
|
||||
|
||||
test("should export all display constants", () => {
|
||||
expect(Yoga.DISPLAY_FLEX).toBeDefined();
|
||||
expect(Yoga.DISPLAY_NONE).toBeDefined();
|
||||
});
|
||||
|
||||
test("should export all edge constants", () => {
|
||||
expect(Yoga.EDGE_LEFT).toBeDefined();
|
||||
expect(Yoga.EDGE_TOP).toBeDefined();
|
||||
expect(Yoga.EDGE_RIGHT).toBeDefined();
|
||||
expect(Yoga.EDGE_BOTTOM).toBeDefined();
|
||||
expect(Yoga.EDGE_START).toBeDefined();
|
||||
expect(Yoga.EDGE_END).toBeDefined();
|
||||
expect(Yoga.EDGE_HORIZONTAL).toBeDefined();
|
||||
expect(Yoga.EDGE_VERTICAL).toBeDefined();
|
||||
expect(Yoga.EDGE_ALL).toBeDefined();
|
||||
});
|
||||
|
||||
test("should export all experimental feature constants", () => {
|
||||
expect(Yoga.EXPERIMENTAL_FEATURE_WEB_FLEX_BASIS).toBeDefined();
|
||||
expect(Yoga.EXPERIMENTAL_FEATURE_ABSOLUTE_PERCENTAGE_AGAINST_PADDING_EDGE).toBeDefined();
|
||||
expect(Yoga.EXPERIMENTAL_FEATURE_FIX_ABSOLUTE_TRAILING_COLUMN_MARGIN).toBeDefined();
|
||||
});
|
||||
|
||||
test("should export all flex direction constants", () => {
|
||||
expect(Yoga.FLEX_DIRECTION_COLUMN).toBeDefined();
|
||||
expect(Yoga.FLEX_DIRECTION_COLUMN_REVERSE).toBeDefined();
|
||||
expect(Yoga.FLEX_DIRECTION_ROW).toBeDefined();
|
||||
expect(Yoga.FLEX_DIRECTION_ROW_REVERSE).toBeDefined();
|
||||
});
|
||||
|
||||
test("should export all gutter constants", () => {
|
||||
expect(Yoga.GUTTER_COLUMN).toBeDefined();
|
||||
expect(Yoga.GUTTER_ROW).toBeDefined();
|
||||
expect(Yoga.GUTTER_ALL).toBeDefined();
|
||||
});
|
||||
|
||||
test("should export all justify constants", () => {
|
||||
expect(Yoga.JUSTIFY_FLEX_START).toBeDefined();
|
||||
expect(Yoga.JUSTIFY_CENTER).toBeDefined();
|
||||
expect(Yoga.JUSTIFY_FLEX_END).toBeDefined();
|
||||
expect(Yoga.JUSTIFY_SPACE_BETWEEN).toBeDefined();
|
||||
expect(Yoga.JUSTIFY_SPACE_AROUND).toBeDefined();
|
||||
expect(Yoga.JUSTIFY_SPACE_EVENLY).toBeDefined();
|
||||
});
|
||||
|
||||
test("should export all measure mode constants", () => {
|
||||
expect(Yoga.MEASURE_MODE_UNDEFINED).toBeDefined();
|
||||
expect(Yoga.MEASURE_MODE_EXACTLY).toBeDefined();
|
||||
expect(Yoga.MEASURE_MODE_AT_MOST).toBeDefined();
|
||||
});
|
||||
|
||||
test("should export all node type constants", () => {
|
||||
expect(Yoga.NODE_TYPE_DEFAULT).toBeDefined();
|
||||
expect(Yoga.NODE_TYPE_TEXT).toBeDefined();
|
||||
});
|
||||
|
||||
test("should export all overflow constants", () => {
|
||||
expect(Yoga.OVERFLOW_VISIBLE).toBeDefined();
|
||||
expect(Yoga.OVERFLOW_HIDDEN).toBeDefined();
|
||||
expect(Yoga.OVERFLOW_SCROLL).toBeDefined();
|
||||
});
|
||||
|
||||
test("should export all position type constants", () => {
|
||||
expect(Yoga.POSITION_TYPE_STATIC).toBeDefined();
|
||||
expect(Yoga.POSITION_TYPE_RELATIVE).toBeDefined();
|
||||
expect(Yoga.POSITION_TYPE_ABSOLUTE).toBeDefined();
|
||||
});
|
||||
|
||||
test("should export all unit constants", () => {
|
||||
expect(Yoga.UNIT_UNDEFINED).toBeDefined();
|
||||
expect(Yoga.UNIT_POINT).toBeDefined();
|
||||
expect(Yoga.UNIT_PERCENT).toBeDefined();
|
||||
expect(Yoga.UNIT_AUTO).toBeDefined();
|
||||
});
|
||||
|
||||
test("should export all wrap constants", () => {
|
||||
expect(Yoga.WRAP_NO_WRAP).toBeDefined();
|
||||
expect(Yoga.WRAP_WRAP).toBeDefined();
|
||||
expect(Yoga.WRAP_WRAP_REVERSE).toBeDefined();
|
||||
});
|
||||
|
||||
test("should export all errata constants", () => {
|
||||
expect(Yoga.ERRATA_NONE).toBeDefined();
|
||||
expect(Yoga.ERRATA_STRETCH_FLEX_BASIS).toBeDefined();
|
||||
expect(Yoga.ERRATA_ABSOLUTE_POSITIONING_INCORRECT).toBeDefined();
|
||||
expect(Yoga.ERRATA_ABSOLUTE_PERCENT_AGAINST_INNER_SIZE).toBeDefined();
|
||||
expect(Yoga.ERRATA_ALL).toBeDefined();
|
||||
expect(Yoga.ERRATA_CLASSIC).toBeDefined();
|
||||
});
|
||||
|
||||
test("constants should have correct numeric values", () => {
|
||||
// Check a few key constants have reasonable values
|
||||
expect(typeof Yoga.EDGE_TOP).toBe("number");
|
||||
expect(typeof Yoga.UNIT_PERCENT).toBe("number");
|
||||
expect(typeof Yoga.FLEX_DIRECTION_ROW).toBe("number");
|
||||
});
|
||||
});
|
||||
256
test/js/bun/yoga-node.test.js
Normal file
256
test/js/bun/yoga-node.test.js
Normal file
@@ -0,0 +1,256 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
|
||||
describe("Yoga.Node", () => {
|
||||
test("Node constructor", () => {
|
||||
const node = new Yoga.Node();
|
||||
expect(node).toBeDefined();
|
||||
expect(node.constructor.name).toBe("Node");
|
||||
});
|
||||
|
||||
test("Node.create() static method", () => {
|
||||
const node = Yoga.Node.create();
|
||||
expect(node).toBeDefined();
|
||||
expect(node.constructor.name).toBe("Node");
|
||||
});
|
||||
|
||||
test("Node with config", () => {
|
||||
const config = new Yoga.Config();
|
||||
const node = new Yoga.Node(config);
|
||||
expect(node).toBeDefined();
|
||||
});
|
||||
|
||||
test("setWidth with various values", () => {
|
||||
const node = new Yoga.Node();
|
||||
|
||||
// Number
|
||||
expect(() => node.setWidth(100)).not.toThrow();
|
||||
|
||||
// Percentage string
|
||||
expect(() => node.setWidth("50%")).not.toThrow();
|
||||
|
||||
// Auto
|
||||
expect(() => node.setWidth("auto")).not.toThrow();
|
||||
|
||||
// Object format
|
||||
expect(() => node.setWidth({ unit: Yoga.UNIT_POINT, value: 200 })).not.toThrow();
|
||||
expect(() => node.setWidth({ unit: Yoga.UNIT_PERCENT, value: 75 })).not.toThrow();
|
||||
|
||||
// Undefined/null
|
||||
expect(() => node.setWidth(undefined)).not.toThrow();
|
||||
expect(() => node.setWidth(null)).not.toThrow();
|
||||
});
|
||||
|
||||
test("getWidth returns correct format", () => {
|
||||
const node = new Yoga.Node();
|
||||
|
||||
node.setWidth(100);
|
||||
let width = node.getWidth();
|
||||
expect(width).toEqual({ unit: Yoga.UNIT_POINT, value: 100 });
|
||||
|
||||
node.setWidth("50%");
|
||||
width = node.getWidth();
|
||||
expect(width).toEqual({ unit: Yoga.UNIT_PERCENT, value: 50 });
|
||||
|
||||
node.setWidth("auto");
|
||||
width = node.getWidth();
|
||||
expect(width).toEqual({ unit: Yoga.UNIT_AUTO, value: expect.any(Number) });
|
||||
});
|
||||
|
||||
test("setMargin/getPadding edge values", () => {
|
||||
const node = new Yoga.Node();
|
||||
|
||||
// Set margins
|
||||
node.setMargin(Yoga.EDGE_TOP, 10);
|
||||
node.setMargin(Yoga.EDGE_RIGHT, "20%");
|
||||
node.setMargin(Yoga.EDGE_BOTTOM, "auto");
|
||||
node.setMargin(Yoga.EDGE_LEFT, { unit: Yoga.UNIT_POINT, value: 30 });
|
||||
|
||||
// Get margins
|
||||
expect(node.getMargin(Yoga.EDGE_TOP)).toEqual({ unit: Yoga.UNIT_POINT, value: 10 });
|
||||
expect(node.getMargin(Yoga.EDGE_RIGHT)).toEqual({ unit: Yoga.UNIT_PERCENT, value: 20 });
|
||||
expect(node.getMargin(Yoga.EDGE_BOTTOM)).toEqual({ unit: Yoga.UNIT_AUTO, value: expect.any(Number) });
|
||||
expect(node.getMargin(Yoga.EDGE_LEFT)).toEqual({ unit: Yoga.UNIT_POINT, value: 30 });
|
||||
});
|
||||
|
||||
test("flexbox properties", () => {
|
||||
const node = new Yoga.Node();
|
||||
|
||||
// Flex direction
|
||||
expect(() => node.setFlexDirection(Yoga.FLEX_DIRECTION_ROW)).not.toThrow();
|
||||
expect(() => node.setFlexDirection(Yoga.FLEX_DIRECTION_COLUMN)).not.toThrow();
|
||||
|
||||
// Justify content
|
||||
expect(() => node.setJustifyContent(Yoga.JUSTIFY_CENTER)).not.toThrow();
|
||||
expect(() => node.setJustifyContent(Yoga.JUSTIFY_SPACE_BETWEEN)).not.toThrow();
|
||||
|
||||
// Align items
|
||||
expect(() => node.setAlignItems(Yoga.ALIGN_CENTER)).not.toThrow();
|
||||
expect(() => node.setAlignItems(Yoga.ALIGN_FLEX_START)).not.toThrow();
|
||||
|
||||
// Flex properties
|
||||
expect(() => node.setFlex(1)).not.toThrow();
|
||||
expect(() => node.setFlexGrow(2)).not.toThrow();
|
||||
expect(() => node.setFlexShrink(0.5)).not.toThrow();
|
||||
expect(() => node.setFlexBasis(100)).not.toThrow();
|
||||
expect(() => node.setFlexBasis("auto")).not.toThrow();
|
||||
});
|
||||
|
||||
test("hierarchy operations", () => {
|
||||
const parent = new Yoga.Node();
|
||||
const child1 = new Yoga.Node();
|
||||
const child2 = new Yoga.Node();
|
||||
|
||||
// Insert children
|
||||
parent.insertChild(child1, 0);
|
||||
parent.insertChild(child2, 1);
|
||||
|
||||
expect(parent.getChildCount()).toBe(2);
|
||||
expect(parent.getChild(0)).toBe(child1);
|
||||
expect(parent.getChild(1)).toBe(child2);
|
||||
|
||||
expect(child1.getParent()).toBe(parent);
|
||||
expect(child2.getParent()).toBe(parent);
|
||||
|
||||
// Remove child
|
||||
parent.removeChild(child1);
|
||||
expect(parent.getChildCount()).toBe(1);
|
||||
expect(parent.getChild(0)).toBe(child2);
|
||||
expect(child1.getParent()).toBeNull();
|
||||
});
|
||||
|
||||
test("layout calculation", () => {
|
||||
const root = new Yoga.Node();
|
||||
root.setWidth(500);
|
||||
root.setHeight(300);
|
||||
root.setFlexDirection(Yoga.FLEX_DIRECTION_ROW);
|
||||
|
||||
const child = new Yoga.Node();
|
||||
child.setFlex(1);
|
||||
root.insertChild(child, 0);
|
||||
|
||||
// Calculate layout
|
||||
root.calculateLayout(500, 300, Yoga.DIRECTION_LTR);
|
||||
|
||||
// Get computed layout
|
||||
const layout = root.getComputedLayout();
|
||||
expect(layout).toHaveProperty("left");
|
||||
expect(layout).toHaveProperty("top");
|
||||
expect(layout).toHaveProperty("width");
|
||||
expect(layout).toHaveProperty("height");
|
||||
expect(layout.width).toBe(500);
|
||||
expect(layout.height).toBe(300);
|
||||
|
||||
const childLayout = child.getComputedLayout();
|
||||
expect(childLayout.width).toBe(500); // Should fill parent width
|
||||
expect(childLayout.height).toBe(300); // Should fill parent height
|
||||
});
|
||||
|
||||
test("measure function", () => {
|
||||
const node = new Yoga.Node();
|
||||
let measureCalled = false;
|
||||
|
||||
const measureFunc = (width, widthMode, height, heightMode) => {
|
||||
measureCalled = true;
|
||||
return { width: 100, height: 50 };
|
||||
};
|
||||
|
||||
node.setMeasureFunc(measureFunc);
|
||||
node.markDirty();
|
||||
|
||||
// Calculate layout - this should call measure function
|
||||
node.calculateLayout();
|
||||
expect(measureCalled).toBe(true);
|
||||
|
||||
// Clear measure function
|
||||
node.setMeasureFunc(null);
|
||||
});
|
||||
|
||||
test("dirtied callback", () => {
|
||||
const node = new Yoga.Node();
|
||||
let dirtiedCalled = false;
|
||||
|
||||
const dirtiedFunc = () => {
|
||||
dirtiedCalled = true;
|
||||
};
|
||||
|
||||
node.setDirtiedFunc(dirtiedFunc);
|
||||
node.markDirty();
|
||||
|
||||
expect(dirtiedCalled).toBe(true);
|
||||
|
||||
// Clear dirtied function
|
||||
node.setDirtiedFunc(null);
|
||||
});
|
||||
|
||||
test("reset node", () => {
|
||||
const node = new Yoga.Node();
|
||||
node.setWidth(100);
|
||||
node.setHeight(200);
|
||||
node.setFlexDirection(Yoga.FLEX_DIRECTION_ROW);
|
||||
|
||||
node.reset();
|
||||
|
||||
// After reset, values should be back to defaults
|
||||
const width = node.getWidth();
|
||||
expect(width.unit).toBe(Yoga.UNIT_UNDEFINED);
|
||||
});
|
||||
|
||||
test("dirty state", () => {
|
||||
const node = new Yoga.Node();
|
||||
|
||||
// Initially not dirty
|
||||
expect(node.isDirty()).toBe(false);
|
||||
|
||||
// Mark as dirty
|
||||
node.markDirty();
|
||||
expect(node.isDirty()).toBe(true);
|
||||
|
||||
// Calculate layout clears dirty flag
|
||||
node.calculateLayout();
|
||||
expect(node.isDirty()).toBe(false);
|
||||
});
|
||||
|
||||
test("free node", () => {
|
||||
const node = new Yoga.Node();
|
||||
expect(() => node.free()).not.toThrow();
|
||||
// After free, the node should not crash but operations may not work
|
||||
});
|
||||
|
||||
test("aspect ratio", () => {
|
||||
const node = new Yoga.Node();
|
||||
|
||||
// Set aspect ratio
|
||||
expect(() => node.setAspectRatio(16 / 9)).not.toThrow();
|
||||
expect(() => node.setAspectRatio(undefined)).not.toThrow();
|
||||
expect(() => node.setAspectRatio(null)).not.toThrow();
|
||||
});
|
||||
|
||||
test("display type", () => {
|
||||
const node = new Yoga.Node();
|
||||
|
||||
expect(() => node.setDisplay(Yoga.DISPLAY_FLEX)).not.toThrow();
|
||||
expect(() => node.setDisplay(Yoga.DISPLAY_NONE)).not.toThrow();
|
||||
});
|
||||
|
||||
test("overflow", () => {
|
||||
const node = new Yoga.Node();
|
||||
|
||||
expect(() => node.setOverflow(Yoga.OVERFLOW_VISIBLE)).not.toThrow();
|
||||
expect(() => node.setOverflow(Yoga.OVERFLOW_HIDDEN)).not.toThrow();
|
||||
expect(() => node.setOverflow(Yoga.OVERFLOW_SCROLL)).not.toThrow();
|
||||
});
|
||||
|
||||
test("position type", () => {
|
||||
const node = new Yoga.Node();
|
||||
|
||||
expect(() => node.setPositionType(Yoga.POSITION_TYPE_RELATIVE)).not.toThrow();
|
||||
expect(() => node.setPositionType(Yoga.POSITION_TYPE_ABSOLUTE)).not.toThrow();
|
||||
});
|
||||
|
||||
test("gap property", () => {
|
||||
const node = new Yoga.Node();
|
||||
|
||||
expect(() => node.setGap(Yoga.GUTTER_ROW, 10)).not.toThrow();
|
||||
expect(() => node.setGap(Yoga.GUTTER_COLUMN, 20)).not.toThrow();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user