From aaaee23bb2c399a06c55e967c4a2a3b98f93e4ce Mon Sep 17 00:00:00 2001 From: Nick Gerleman Date: Thu, 5 Mar 2026 03:05:44 -0800 Subject: [PATCH] Remove embind from Yoga JavaScript bindings (#1906) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary: X-link: https://github.com/facebook/react-native/pull/55864 Yoga's JavaScript bindings previously used Emscripten embind to bridge C++ wrapper classes to JavaScript. This added unnecessary overhead and complexity: JS calls went through embind → C++ wrapper classes (Node.cpp, Config.cpp) → Yoga C API, requiring the embind runtime to be bundled into the WASM output. This diff removes embind entirely. JavaScript now calls Yoga's C API directly, with a thin C bridge (wasm_bridge.c, ~140 lines) handling only what raw C exports cannot: - Struct returns (YGValue) via a static buffer read through HEAPF32/HEAP32 - Measure/dirtied callbacks via EM_JS dispatching to JS-side Maps The JS side (wrapAssembly.ts) is rewritten to use classes (NodeImpl, ConfigImpl) that store a WASM pointer and call C functions directly via `lib._YGFunctionName()`. Tree hierarchy (children/parent) is tracked entirely in JS, matching the pattern used by the Java JNI bindings. The public API shape (Yoga, Node, Config types) remains identical. **Bundle size (SINGLE_FILE base64-embedded WASM):** | Metric | Before (embind) | After (direct C) | Change | |---|---|---|---| | Raw | 126,288 B | 115,370 B | -8.6% | | Gzip | 51,042 B | 42,764 B | -16.2% | **Benchmark results (median of 3 runs):** | Benchmark | Before | After | Change | |---|---|---|---| | Stack with flex | 20ms | 8ms | 2.5x faster | | Align stretch in undefined axis | 19ms | 5ms | 3.8x faster | | Nested flex | 17ms | 4ms | 4.3x faster | | Huge nested layout | 18ms | 5ms | 3.6x faster | Fixes https://github.com/facebook/yoga/issues/1545 Changelog: [Internal] Reviewed By: elicwhite Differential Revision: D95011356 --- javascript/CMakeLists.txt | 18 +- javascript/src/Config.cpp | 54 -- javascript/src/Config.h | 44 -- javascript/src/Layout.h | 19 - javascript/src/Node.cpp | 592 ---------------------- javascript/src/Node.h | 260 ---------- javascript/src/Size.h | 17 - javascript/src/Value.h | 23 - javascript/src/embind.cpp | 228 --------- javascript/src/wasm_bridge.c | 155 ++++++ javascript/src/wrapAssembly.ts | 871 ++++++++++++++++++++++++++++----- yoga/YGMacros.h | 2 + 12 files changed, 907 insertions(+), 1376 deletions(-) delete mode 100644 javascript/src/Config.cpp delete mode 100644 javascript/src/Config.h delete mode 100644 javascript/src/Layout.h delete mode 100644 javascript/src/Node.cpp delete mode 100644 javascript/src/Node.h delete mode 100644 javascript/src/Size.h delete mode 100644 javascript/src/Value.h delete mode 100644 javascript/src/embind.cpp create mode 100644 javascript/src/wasm_bridge.c diff --git a/javascript/CMakeLists.txt b/javascript/CMakeLists.txt index d3ddf3a6f2..4215a2294a 100644 --- a/javascript/CMakeLists.txt +++ b/javascript/CMakeLists.txt @@ -5,24 +5,24 @@ cmake_minimum_required(VERSION 3.13...3.26) set(CMAKE_VERBOSE_MAKEFILE on) -project(yoga) +project(yoga C CXX) -file(GLOB SOURCES CONFIGURE_DEPENDS +file(GLOB YOGA_SOURCES CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/../yoga/*.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/../yoga/**/*.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp) + ${CMAKE_CURRENT_SOURCE_DIR}/../yoga/**/*.cpp) + +set(BRIDGE_SOURCES + ${CMAKE_CURRENT_SOURCE_DIR}/src/wasm_bridge.c) include_directories(..) set(CMAKE_CXX_STANDARD 20) -add_compile_definitions( - EMSCRIPTEN_HAS_UNBOUND_TYPE_NAMES=0) - set(COMPILE_OPTIONS -flto -fno-exceptions -fno-rtti + -Wno-ignored-attributes -g0 -Os "SHELL:-s STRICT=1") @@ -51,9 +51,7 @@ add_link_options( "SHELL:-s SINGLE_FILE=1" "SHELL:-s ENVIRONMENT='web'") -link_libraries(embind) - -add_library(yogaObjLib OBJECT ${SOURCES}) +add_library(yogaObjLib OBJECT ${YOGA_SOURCES} ${BRIDGE_SOURCES}) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/binaries) diff --git a/javascript/src/Config.cpp b/javascript/src/Config.cpp deleted file mode 100644 index d9313d8e38..0000000000 --- a/javascript/src/Config.cpp +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include - -#include "./Config.h" - -/* static */ Config* Config::create(void) { - return new Config(); -} - -/* static */ void Config::destroy(Config* node) { - delete node; -} - -Config::Config(void) : m_config(YGConfigNew()) {} - -Config::~Config(void) { - YGConfigFree(m_config); -} - -void Config::setExperimentalFeatureEnabled(int feature, bool enabled) { - YGConfigSetExperimentalFeatureEnabled( - m_config, static_cast(feature), enabled); -} - -void Config::setPointScaleFactor(float pixelsInPoint) { - YGConfigSetPointScaleFactor(m_config, pixelsInPoint); -} - -void Config::setErrata(int errata) { - YGConfigSetErrata(m_config, static_cast(errata)); -} - -void Config::setUseWebDefaults(bool useWebDefaults) { - YGConfigSetUseWebDefaults(m_config, useWebDefaults); -} - -bool Config::isExperimentalFeatureEnabled(int feature) const { - return YGConfigIsExperimentalFeatureEnabled( - m_config, static_cast(feature)); -} - -int Config::getErrata() const { - return static_cast(YGConfigGetErrata(m_config)); -} - -bool Config::useWebDefaults() const { - return YGConfigGetUseWebDefaults(m_config); -} diff --git a/javascript/src/Config.h b/javascript/src/Config.h deleted file mode 100644 index cc1441fee1..0000000000 --- a/javascript/src/Config.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#pragma once - -#include - -class Config { - friend class Node; - - public: - static Config* create(void); - - static void destroy(Config* config); - - private: - Config(void); - - public: - ~Config(void); - - public: // Prevent accidental copy - Config(Config const&) = delete; - - Config const& operator=(Config const&) = delete; - - public: // Setters - void setExperimentalFeatureEnabled(int feature, bool enabled); - void setPointScaleFactor(float pixelsInPoint); - void setErrata(int errata); - void setUseWebDefaults(bool useWebDefaults); - - public: // Getters - bool isExperimentalFeatureEnabled(int feature) const; - int getErrata() const; - bool useWebDefaults() const; - - private: - YGConfigRef m_config; -}; diff --git a/javascript/src/Layout.h b/javascript/src/Layout.h deleted file mode 100644 index 72380afe83..0000000000 --- a/javascript/src/Layout.h +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#pragma once - -struct Layout { - double left; - double right; - - double top; - double bottom; - - double width; - double height; -}; diff --git a/javascript/src/Node.cpp b/javascript/src/Node.cpp deleted file mode 100644 index 42f6d82f63..0000000000 --- a/javascript/src/Node.cpp +++ /dev/null @@ -1,592 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include - -#include - -#include "./Config.h" -#include "./Layout.h" -#include "./Node.h" -#include "./Size.h" - -static YGSize globalMeasureFunc( - YGNodeConstRef nodeRef, - float width, - YGMeasureMode widthMode, - float height, - YGMeasureMode heightMode) { - Node const& node = *reinterpret_cast(YGNodeGetContext(nodeRef)); - - Size size = node.callMeasureFunc(width, widthMode, height, heightMode); - YGSize ygSize = { - static_cast(size.width), static_cast(size.height)}; - - return ygSize; -} - -static void globalDirtiedFunc(YGNodeConstRef nodeRef) { - Node const& node = *reinterpret_cast(YGNodeGetContext(nodeRef)); - - node.callDirtiedFunc(); -} - -/* static */ Node* Node::createDefault(void) { - return new Node(nullptr); -} - -/* static */ Node* Node::createWithConfig(Config* config) { - return new Node(config); -} - -/* static */ void Node::destroy(Node* node) { - delete node; -} - -/* static */ Node* Node::fromYGNode(YGNodeRef nodeRef) { - return reinterpret_cast(YGNodeGetContext(nodeRef)); -} - -Node::Node(Config* config) - : m_node( - config != nullptr ? YGNodeNewWithConfig(config->m_config) - : YGNodeNew()), - m_measureFunc(nullptr), - m_dirtiedFunc(nullptr) { - YGNodeSetContext(m_node, reinterpret_cast(this)); -} - -Node::~Node(void) { - YGNodeFree(m_node); -} - -void Node::reset(void) { - m_measureFunc.reset(nullptr); - m_dirtiedFunc.reset(nullptr); - - YGNodeReset(m_node); -} - -void Node::copyStyle(Node const& other) { - YGNodeCopyStyle(m_node, other.m_node); -} - -void Node::setBoxSizing(int boxSizing) { - YGNodeStyleSetBoxSizing(m_node, static_cast(boxSizing)); -} - -void Node::setPositionType(int positionType) { - YGNodeStyleSetPositionType(m_node, static_cast(positionType)); -} - -void Node::setPosition(int edge, double position) { - YGNodeStyleSetPosition(m_node, static_cast(edge), position); -} - -void Node::setPositionPercent(int edge, double position) { - YGNodeStyleSetPositionPercent(m_node, static_cast(edge), position); -} - -void Node::setPositionAuto(int edge) { - YGNodeStyleSetPositionAuto(m_node, static_cast(edge)); -} - -void Node::setAlignContent(int alignContent) { - YGNodeStyleSetAlignContent(m_node, static_cast(alignContent)); -} - -void Node::setAlignItems(int alignItems) { - YGNodeStyleSetAlignItems(m_node, static_cast(alignItems)); -} - -void Node::setAlignSelf(int alignSelf) { - YGNodeStyleSetAlignSelf(m_node, static_cast(alignSelf)); -} - -void Node::setFlexDirection(int flexDirection) { - YGNodeStyleSetFlexDirection( - m_node, static_cast(flexDirection)); -} - -void Node::setDirection(int direction) { - YGNodeStyleSetDirection(m_node, static_cast(direction)); -} - -void Node::setFlexWrap(int flexWrap) { - YGNodeStyleSetFlexWrap(m_node, static_cast(flexWrap)); -} - -void Node::setJustifyContent(int justifyContent) { - YGNodeStyleSetJustifyContent(m_node, static_cast(justifyContent)); -} - -void Node::setMargin(int edge, double margin) { - YGNodeStyleSetMargin(m_node, static_cast(edge), margin); -} - -void Node::setMarginPercent(int edge, double margin) { - YGNodeStyleSetMarginPercent(m_node, static_cast(edge), margin); -} - -void Node::setMarginAuto(int edge) { - YGNodeStyleSetMarginAuto(m_node, static_cast(edge)); -} - -void Node::setOverflow(int overflow) { - YGNodeStyleSetOverflow(m_node, static_cast(overflow)); -} - -void Node::setDisplay(int display) { - YGNodeStyleSetDisplay(m_node, static_cast(display)); -} - -void Node::setFlex(double flex) { - YGNodeStyleSetFlex(m_node, flex); -} - -void Node::setFlexBasis(double flexBasis) { - YGNodeStyleSetFlexBasis(m_node, flexBasis); -} - -void Node::setFlexBasisPercent(double flexBasis) { - YGNodeStyleSetFlexBasisPercent(m_node, flexBasis); -} - -void Node::setFlexBasisAuto() { - YGNodeStyleSetFlexBasisAuto(m_node); -} - -void Node::setFlexBasisMaxContent() { - YGNodeStyleSetFlexBasisMaxContent(m_node); -} - -void Node::setFlexBasisFitContent() { - YGNodeStyleSetFlexBasisFitContent(m_node); -} - -void Node::setFlexBasisStretch() { - YGNodeStyleSetFlexBasisStretch(m_node); -} - -void Node::setFlexGrow(double flexGrow) { - YGNodeStyleSetFlexGrow(m_node, flexGrow); -} - -void Node::setFlexShrink(double flexShrink) { - YGNodeStyleSetFlexShrink(m_node, flexShrink); -} - -void Node::setWidth(double width) { - YGNodeStyleSetWidth(m_node, width); -} - -void Node::setWidthPercent(double width) { - YGNodeStyleSetWidthPercent(m_node, width); -} - -void Node::setWidthAuto() { - YGNodeStyleSetWidthAuto(m_node); -} - -void Node::setWidthMaxContent() { - YGNodeStyleSetWidthMaxContent(m_node); -} - -void Node::setWidthFitContent() { - YGNodeStyleSetWidthFitContent(m_node); -} - -void Node::setWidthStretch() { - YGNodeStyleSetWidthStretch(m_node); -} - -void Node::setHeight(double height) { - YGNodeStyleSetHeight(m_node, height); -} - -void Node::setHeightPercent(double height) { - YGNodeStyleSetHeightPercent(m_node, height); -} - -void Node::setHeightAuto() { - YGNodeStyleSetHeightAuto(m_node); -} - -void Node::setHeightMaxContent() { - YGNodeStyleSetHeightMaxContent(m_node); -} - -void Node::setHeightFitContent() { - YGNodeStyleSetHeightFitContent(m_node); -} - -void Node::setHeightStretch() { - YGNodeStyleSetHeightStretch(m_node); -} - -void Node::setMinWidth(double minWidth) { - YGNodeStyleSetMinWidth(m_node, minWidth); -} - -void Node::setMinWidthPercent(double minWidth) { - YGNodeStyleSetMinWidthPercent(m_node, minWidth); -} - -void Node::setMinWidthMaxContent() { - YGNodeStyleSetMinWidthMaxContent(m_node); -} - -void Node::setMinWidthFitContent() { - YGNodeStyleSetMinWidthFitContent(m_node); -} - -void Node::setMinWidthStretch() { - YGNodeStyleSetMinWidthStretch(m_node); -} - -void Node::setMinHeight(double minHeight) { - YGNodeStyleSetMinHeight(m_node, minHeight); -} - -void Node::setMinHeightPercent(double minHeight) { - YGNodeStyleSetMinHeightPercent(m_node, minHeight); -} - -void Node::setMinHeightMaxContent() { - YGNodeStyleSetMinHeightMaxContent(m_node); -} - -void Node::setMinHeightFitContent() { - YGNodeStyleSetMinHeightFitContent(m_node); -} - -void Node::setMinHeightStretch() { - YGNodeStyleSetMinHeightStretch(m_node); -} - -void Node::setMaxWidth(double maxWidth) { - YGNodeStyleSetMaxWidth(m_node, maxWidth); -} - -void Node::setMaxWidthPercent(double maxWidth) { - YGNodeStyleSetMaxWidthPercent(m_node, maxWidth); -} - -void Node::setMaxWidthMaxContent() { - YGNodeStyleSetMaxWidthMaxContent(m_node); -} - -void Node::setMaxWidthFitContent() { - YGNodeStyleSetMaxWidthFitContent(m_node); -} - -void Node::setMaxWidthStretch() { - YGNodeStyleSetMaxWidthStretch(m_node); -} - -void Node::setMaxHeight(double maxHeight) { - YGNodeStyleSetMaxHeight(m_node, maxHeight); -} - -void Node::setMaxHeightPercent(double maxHeight) { - YGNodeStyleSetMaxHeightPercent(m_node, maxHeight); -} - -void Node::setMaxHeightMaxContent() { - YGNodeStyleSetMaxHeightMaxContent(m_node); -} - -void Node::setMaxHeightFitContent() { - YGNodeStyleSetMaxHeightFitContent(m_node); -} - -void Node::setMaxHeightStretch() { - YGNodeStyleSetMaxHeightStretch(m_node); -} - -void Node::setAspectRatio(double aspectRatio) { - YGNodeStyleSetAspectRatio(m_node, aspectRatio); -} - -void Node::setBorder(int edge, double border) { - YGNodeStyleSetBorder(m_node, static_cast(edge), border); -} - -void Node::setPadding(int edge, double padding) { - YGNodeStyleSetPadding(m_node, static_cast(edge), padding); -} - -void Node::setPaddingPercent(int edge, double padding) { - YGNodeStyleSetPaddingPercent(m_node, static_cast(edge), padding); -} - -void Node::setIsReferenceBaseline(bool isReferenceBaseline) { - YGNodeSetIsReferenceBaseline(m_node, isReferenceBaseline); -} - -void Node::setGap(int gutter, double gapLength) { - YGNodeStyleSetGap(m_node, static_cast(gutter), gapLength); -} - -void Node::setGapPercent(int gutter, double gapLength) { - YGNodeStyleSetGapPercent(m_node, static_cast(gutter), gapLength); -} - -int Node::getBoxSizing(void) const { - return YGNodeStyleGetBoxSizing(m_node); -} - -int Node::getPositionType(void) const { - return YGNodeStyleGetPositionType(m_node); -} - -Value Node::getPosition(int edge) const { - return Value::fromYGValue( - YGNodeStyleGetPosition(m_node, static_cast(edge))); -} - -int Node::getAlignContent(void) const { - return YGNodeStyleGetAlignContent(m_node); -} - -int Node::getAlignItems(void) const { - return YGNodeStyleGetAlignItems(m_node); -} - -int Node::getAlignSelf(void) const { - return YGNodeStyleGetAlignSelf(m_node); -} - -int Node::getFlexDirection(void) const { - return YGNodeStyleGetFlexDirection(m_node); -} - -int Node::getDirection(void) const { - return YGNodeStyleGetDirection(m_node); -} - -int Node::getFlexWrap(void) const { - return YGNodeStyleGetFlexWrap(m_node); -} - -int Node::getJustifyContent(void) const { - return YGNodeStyleGetJustifyContent(m_node); -} - -Value Node::getMargin(int edge) const { - return Value::fromYGValue( - YGNodeStyleGetMargin(m_node, static_cast(edge))); -} - -int Node::getOverflow(void) const { - return YGNodeStyleGetOverflow(m_node); -} - -int Node::getDisplay(void) const { - return YGNodeStyleGetDisplay(m_node); -} - -Value Node::getFlexBasis(void) const { - return Value::fromYGValue(YGNodeStyleGetFlexBasis(m_node)); -} - -double Node::getFlexGrow(void) const { - return YGNodeStyleGetFlexGrow(m_node); -} - -double Node::getFlexShrink(void) const { - return YGNodeStyleGetFlexShrink(m_node); -} - -Value Node::getWidth(void) const { - return Value::fromYGValue(YGNodeStyleGetWidth(m_node)); -} - -Value Node::getHeight(void) const { - return Value::fromYGValue(YGNodeStyleGetHeight(m_node)); -} - -Value Node::getMinWidth(void) const { - return Value::fromYGValue(YGNodeStyleGetMinWidth(m_node)); -} - -Value Node::getMinHeight(void) const { - return Value::fromYGValue(YGNodeStyleGetMinHeight(m_node)); -} - -Value Node::getMaxWidth(void) const { - return Value::fromYGValue(YGNodeStyleGetMaxWidth(m_node)); -} - -Value Node::getMaxHeight(void) const { - return Value::fromYGValue(YGNodeStyleGetMaxHeight(m_node)); -} - -double Node::getAspectRatio(void) const { - return YGNodeStyleGetAspectRatio(m_node); -} - -double Node::getBorder(int edge) const { - return YGNodeStyleGetBorder(m_node, static_cast(edge)); -} - -Value Node::getPadding(int edge) const { - return Value::fromYGValue( - YGNodeStyleGetPadding(m_node, static_cast(edge))); -} - -Value Node::getGap(int gutter) const { - return Value::fromYGValue( - YGNodeStyleGetGap(m_node, static_cast(gutter))); -} - -bool Node::isReferenceBaseline() { - return YGNodeIsReferenceBaseline(m_node); -} - -void Node::insertChild(Node* child, unsigned index) { - YGNodeInsertChild(m_node, child->m_node, index); -} - -void Node::removeChild(Node* child) { - YGNodeRemoveChild(m_node, child->m_node); -} - -unsigned Node::getChildCount(void) const { - return YGNodeGetChildCount(m_node); -} - -Node* Node::getParent(void) { - auto nodePtr = YGNodeGetParent(m_node); - - if (nodePtr == nullptr) - return nullptr; - - return Node::fromYGNode(nodePtr); -} - -Node* Node::getChild(unsigned index) { - auto nodePtr = YGNodeGetChild(m_node, index); - - if (nodePtr == nullptr) - return nullptr; - - return Node::fromYGNode(nodePtr); -} - -void Node::setMeasureFunc(MeasureCallback* measureFunc) { - m_measureFunc.reset(measureFunc); - - YGNodeSetMeasureFunc(m_node, &globalMeasureFunc); -} - -void Node::unsetMeasureFunc(void) { - m_measureFunc.reset(nullptr); - - YGNodeSetMeasureFunc(m_node, nullptr); -} - -Size Node::callMeasureFunc( - double width, - int widthMode, - double height, - int heightMode) const { - return m_measureFunc->measure(width, widthMode, height, heightMode); -} - -void Node::setDirtiedFunc(DirtiedCallback* dirtiedFunc) { - m_dirtiedFunc.reset(dirtiedFunc); - - YGNodeSetDirtiedFunc(m_node, &globalDirtiedFunc); -} - -void Node::unsetDirtiedFunc(void) { - m_dirtiedFunc.reset(nullptr); - - YGNodeSetDirtiedFunc(m_node, nullptr); -} - -void Node::callDirtiedFunc(void) const { - m_dirtiedFunc->dirtied(); -} - -void Node::markDirty(void) { - YGNodeMarkDirty(m_node); -} - -bool Node::isDirty(void) const { - return YGNodeIsDirty(m_node); -} - -void Node::markLayoutSeen() { - YGNodeSetHasNewLayout(m_node, false); -} - -bool Node::hasNewLayout(void) const { - return YGNodeGetHasNewLayout(m_node); -} - -void Node::calculateLayout(double width, double height, int direction) { - YGNodeCalculateLayout( - m_node, width, height, static_cast(direction)); -} - -double Node::getComputedLeft(void) const { - return YGNodeLayoutGetLeft(m_node); -} - -double Node::getComputedRight(void) const { - return YGNodeLayoutGetRight(m_node); -} - -double Node::getComputedTop(void) const { - return YGNodeLayoutGetTop(m_node); -} - -double Node::getComputedBottom(void) const { - return YGNodeLayoutGetBottom(m_node); -} - -double Node::getComputedWidth(void) const { - return YGNodeLayoutGetWidth(m_node); -} - -double Node::getComputedHeight(void) const { - return YGNodeLayoutGetHeight(m_node); -} - -Layout Node::getComputedLayout(void) const { - Layout layout; - - layout.left = YGNodeLayoutGetLeft(m_node); - layout.right = YGNodeLayoutGetRight(m_node); - - layout.top = YGNodeLayoutGetTop(m_node); - layout.bottom = YGNodeLayoutGetBottom(m_node); - - layout.width = YGNodeLayoutGetWidth(m_node); - layout.height = YGNodeLayoutGetHeight(m_node); - - return layout; -} - -double Node::getComputedMargin(int edge) const { - return YGNodeLayoutGetMargin(m_node, static_cast(edge)); -} - -double Node::getComputedBorder(int edge) const { - return YGNodeLayoutGetBorder(m_node, static_cast(edge)); -} - -double Node::getComputedPadding(int edge) const { - return YGNodeLayoutGetPadding(m_node, static_cast(edge)); -} - -void Node::setAlwaysFormsContainingBlock(bool alwaysFormsContainingBlock) { - return YGNodeSetAlwaysFormsContainingBlock( - m_node, alwaysFormsContainingBlock); -} diff --git a/javascript/src/Node.h b/javascript/src/Node.h deleted file mode 100644 index e101b436db..0000000000 --- a/javascript/src/Node.h +++ /dev/null @@ -1,260 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#pragma once - -#include - -#include -#include - -#include "./Config.h" -#include "./Layout.h" -#include "./Size.h" -#include "./Value.h" - -class MeasureCallback { - public: - virtual ~MeasureCallback() {} - virtual Size - measure(float width, int widthMode, float height, int heightMode) = 0; -}; - -class MeasureCallbackWrapper : public emscripten::wrapper { - public: - EMSCRIPTEN_WRAPPER(MeasureCallbackWrapper); - Size measure(float width, int widthMode, float height, int heightMode) { - return call("measure", width, widthMode, height, heightMode); - } -}; - -class DirtiedCallback { - public: - virtual ~DirtiedCallback() {} - virtual void dirtied() = 0; -}; - -class DirtiedCallbackWrapper : public emscripten::wrapper { - public: - EMSCRIPTEN_WRAPPER(DirtiedCallbackWrapper); - void dirtied() { - return call("dirtied"); - } -}; - -class Node { - public: - static Node* createDefault(void); - static Node* createWithConfig(Config* config); - - static void destroy(Node* node); - - public: - static Node* fromYGNode(YGNodeRef nodeRef); - - private: - Node(Config* config); - - public: - ~Node(void); - - public: // Prevent accidental copy - Node(Node const&) = delete; - - Node const& operator=(Node const&) = delete; - - public: - void reset(void); - - public: // Style setters - void copyStyle(Node const& other); - - void setPositionType(int positionType); - void setPosition(int edge, double position); - void setPositionPercent(int edge, double position); - void setPositionAuto(int edge); - - void setAlignContent(int alignContent); - void setAlignItems(int alignItems); - void setAlignSelf(int alignSelf); - void setFlexDirection(int flexDirection); - void setFlexWrap(int flexWrap); - void setJustifyContent(int justifyContent); - void setDirection(int direction); - - void setMargin(int edge, double margin); - void setMarginPercent(int edge, double margin); - void setMarginAuto(int edge); - - void setOverflow(int overflow); - void setDisplay(int display); - - void setFlex(double flex); - void setFlexBasis(double flexBasis); - void setFlexBasisPercent(double flexBasis); - void setFlexBasisAuto(); - void setFlexBasisMaxContent(); - void setFlexBasisFitContent(); - void setFlexBasisStretch(); - void setFlexGrow(double flexGrow); - void setFlexShrink(double flexShrink); - - void setWidth(double width); - void setWidthPercent(double width); - void setWidthAuto(); - void setWidthMaxContent(); - void setWidthFitContent(); - void setWidthStretch(); - void setHeight(double height); - void setHeightPercent(double height); - void setHeightAuto(); - void setHeightMaxContent(); - void setHeightFitContent(); - void setHeightStretch(); - - void setMinWidth(double minWidth); - void setMinWidthPercent(double minWidth); - void setMinWidthMaxContent(); - void setMinWidthFitContent(); - void setMinWidthStretch(); - void setMinHeight(double minHeight); - void setMinHeightPercent(double minHeight); - void setMinHeightMaxContent(); - void setMinHeightFitContent(); - void setMinHeightStretch(); - - void setMaxWidth(double maxWidth); - void setMaxWidthPercent(double maxWidth); - void setMaxWidthMaxContent(); - void setMaxWidthFitContent(); - void setMaxWidthStretch(); - void setMaxHeight(double maxHeight); - void setMaxHeightPercent(double maxHeight); - void setMaxHeightMaxContent(); - void setMaxHeightFitContent(); - void setMaxHeightStretch(); - - void setAspectRatio(double aspectRatio); - - void setBorder(int edge, double border); - - void setPadding(int edge, double padding); - void setPaddingPercent(int edge, double padding); - - void setGap(int gutter, double gapLength); - void setGapPercent(int gutter, double gapLength); - - void setBoxSizing(int boxSizing); - - public: // Style getters - int getPositionType(void) const; - Value getPosition(int edge) const; - - int getAlignContent(void) const; - int getAlignItems(void) const; - int getAlignSelf(void) const; - int getFlexDirection(void) const; - int getFlexWrap(void) const; - int getJustifyContent(void) const; - int getDirection(void) const; - - Value getMargin(int edge) const; - - int getOverflow(void) const; - int getDisplay(void) const; - - Value getFlexBasis(void) const; - double getFlexGrow(void) const; - double getFlexShrink(void) const; - - Value getWidth(void) const; - Value getHeight(void) const; - - Value getMinWidth(void) const; - Value getMinHeight(void) const; - - Value getMaxWidth(void) const; - Value getMaxHeight(void) const; - - double getAspectRatio(void) const; - - double getBorder(int edge) const; - - Value getPadding(int edge) const; - - Value getGap(int gutter) const; - - int getBoxSizing(void) const; - - public: // Tree hierarchy mutators - void insertChild(Node* child, unsigned index); - void removeChild(Node* child); - - public: // Tree hierarchy inspectors - unsigned getChildCount(void) const; - - // The following functions cannot be const because they could discard const - // qualifiers (ex: constNode->getChild(0)->getParent() wouldn't be const) - - Node* getParent(void); - Node* getChild(unsigned index); - - public: // Measure func mutators - void setMeasureFunc(MeasureCallback* measureFunc); - void unsetMeasureFunc(void); - - public: // Measure func inspectors - Size callMeasureFunc( - double width, - int widthMode, - double height, - int heightMode) const; - - public: // Dirtied func mutators - void setDirtiedFunc(DirtiedCallback* dirtiedFunc); - void unsetDirtiedFunc(void); - - public: // Dirtied func inspectors - void callDirtiedFunc(void) const; - - public: // Dirtiness accessors - void markDirty(void); - bool isDirty(void) const; - void markLayoutSeen(); - bool hasNewLayout(void) const; - - public: // Layout mutators - void calculateLayout(double width, double height, int direction); - - public: // Layout inspectors - double getComputedLeft(void) const; - double getComputedRight(void) const; - - double getComputedTop(void) const; - double getComputedBottom(void) const; - - double getComputedWidth(void) const; - double getComputedHeight(void) const; - - Layout getComputedLayout(void) const; - - double getComputedMargin(int edge) const; - double getComputedBorder(int edge) const; - double getComputedPadding(int edge) const; - - public: - void setIsReferenceBaseline(bool isReferenceBaseline); - bool isReferenceBaseline(); - - YGNodeRef m_node; - - std::unique_ptr m_measureFunc; - std::unique_ptr m_dirtiedFunc; - - public: - void setAlwaysFormsContainingBlock(bool alwaysFormContainingBlock); -}; diff --git a/javascript/src/Size.h b/javascript/src/Size.h deleted file mode 100644 index c42bb90ac7..0000000000 --- a/javascript/src/Size.h +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#pragma once - -struct Size { - double width; - double height; - - Size(void) : width(0.0), height(0.0) {} - - Size(double width, double height) : width(width), height(height) {} -}; diff --git a/javascript/src/Value.h b/javascript/src/Value.h deleted file mode 100644 index 2a37d0e90a..0000000000 --- a/javascript/src/Value.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#pragma once - -#include - -struct Value { - static Value fromYGValue(YGValue const& ygValue) { - return Value(static_cast(ygValue.unit), ygValue.value); - } - - int unit; - double value; - - Value(void) : unit(YGUnitUndefined), value(0.0) {} - - Value(int unit, double value) : unit(unit), value(value) {} -}; diff --git a/javascript/src/embind.cpp b/javascript/src/embind.cpp deleted file mode 100644 index a2f7202961..0000000000 --- a/javascript/src/embind.cpp +++ /dev/null @@ -1,228 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include "./Config.h" -#include "./Layout.h" -#include "./Node.h" -#include "./Size.h" -#include "./Value.h" - -#include -#include - -using namespace emscripten; - -EMSCRIPTEN_BINDINGS(YOGA_LAYOUT) { - class_("MeasureCallback") - .function("measure", &MeasureCallback::measure, pure_virtual()) - .allow_subclass("MeasureCallbackWrapper"); - class_("DirtiedCallback") - .function("dirtied", &DirtiedCallback::dirtied, pure_virtual()) - .allow_subclass("DirtiedCallbackWrapper"); - - class_("Config") - .constructor<>(&Config::create, allow_raw_pointers()) - .class_function<>("create", &Config::create, allow_raw_pointers()) - .class_function<>("destroy", &Config::destroy, allow_raw_pointers()) - .function( - "setExperimentalFeatureEnabled", - &Config::setExperimentalFeatureEnabled) - .function("setPointScaleFactor", &Config::setPointScaleFactor) - .function("setErrata", &Config::setErrata) - .function("setUseWebDefaults", &Config::setUseWebDefaults) - .function( - "isExperimentalFeatureEnabled", &Config::isExperimentalFeatureEnabled) - .function("getErrata", &Config::getErrata) - .function("useWebDefaults", &Config::useWebDefaults); - - value_object("Layout") - .field("left", &Layout::left) - .field("right", &Layout::right) - .field("top", &Layout::top) - .field("bottom", &Layout::bottom) - .field("width", &Layout::width) - .field("height", &Layout::height); - - value_object("Size") - .field("width", &Size::width) - .field("height", &Size::height); - - value_object("Value") - .field("value", &Value::value) - .field("unit", &Value::unit); - - class_("Node") - .constructor<>(&Node::createDefault, allow_raw_pointers()) - - .class_function<>( - "createDefault", &Node::createDefault, allow_raw_pointers()) - .class_function<>( - "createWithConfig", &Node::createWithConfig, allow_raw_pointers()) - .class_function<>("destroy", &Node::destroy, allow_raw_pointers()) - .function("reset", &Node::reset) - - .function("copyStyle", &Node::copyStyle) - - .function("setPositionType", &Node::setPositionType) - .function("setPosition", &Node::setPosition) - .function("setPositionPercent", &Node::setPositionPercent) - .function("setPositionAuto", &Node::setPositionAuto) - - .function("setAlignContent", &Node::setAlignContent) - .function("setAlignItems", &Node::setAlignItems) - .function("setAlignSelf", &Node::setAlignSelf) - .function("setFlexDirection", &Node::setFlexDirection) - .function("setFlexWrap", &Node::setFlexWrap) - .function("setJustifyContent", &Node::setJustifyContent) - - .function("setMargin", &Node::setMargin) - .function("setMarginPercent", &Node::setMarginPercent) - .function("setMarginAuto", &Node::setMarginAuto) - - .function("setOverflow", &Node::setOverflow) - .function("setDisplay", &Node::setDisplay) - - .function("setFlex", &Node::setFlex) - .function("setFlexBasis", &Node::setFlexBasis) - .function("setFlexBasisPercent", &Node::setFlexBasisPercent) - .function("setFlexBasisAuto", &Node::setFlexBasisAuto) - .function("setFlexBasisMaxContent", &Node::setFlexBasisMaxContent) - .function("setFlexBasisFitContent", &Node::setFlexBasisFitContent) - .function("setFlexBasisStretch", &Node::setFlexBasisStretch) - .function("setFlexGrow", &Node::setFlexGrow) - .function("setFlexShrink", &Node::setFlexShrink) - - .function("setWidth", &Node::setWidth) - .function("setWidthPercent", &Node::setWidthPercent) - .function("setWidthAuto", &Node::setWidthAuto) - .function("setWidthMaxContent", &Node::setWidthMaxContent) - .function("setWidthFitContent", &Node::setWidthFitContent) - .function("setWidthStretch", &Node::setWidthStretch) - .function("setHeight", &Node::setHeight) - .function("setHeightPercent", &Node::setHeightPercent) - .function("setHeightAuto", &Node::setHeightAuto) - .function("setHeightMaxContent", &Node::setHeightMaxContent) - .function("setHeightFitContent", &Node::setHeightFitContent) - .function("setHeightStretch", &Node::setHeightStretch) - - .function("setMinWidth", &Node::setMinWidth) - .function("setMinWidthPercent", &Node::setMinWidthPercent) - .function("setMinWidthMaxContent", &Node::setMinWidthMaxContent) - .function("setMinWidthFitContent", &Node::setMinWidthFitContent) - .function("setMinWidthStretch", &Node::setMinWidthStretch) - .function("setMinHeight", &Node::setMinHeight) - .function("setMinHeightPercent", &Node::setMinHeightPercent) - .function("setMinHeightMaxContent", &Node::setMinHeightMaxContent) - .function("setMinHeightFitContent", &Node::setMinHeightFitContent) - .function("setMinHeightStretch", &Node::setMinHeightStretch) - - .function("setMaxWidth", &Node::setMaxWidth) - .function("setMaxWidthPercent", &Node::setMaxWidthPercent) - .function("setMaxWidthMaxContent", &Node::setMaxWidthMaxContent) - .function("setMaxWidthFitContent", &Node::setMaxWidthFitContent) - .function("setMaxWidthStretch", &Node::setMaxWidthStretch) - .function("setMaxHeight", &Node::setMaxHeight) - .function("setMaxHeightPercent", &Node::setMaxHeightPercent) - .function("setMaxHeightMaxContent", &Node::setMaxHeightMaxContent) - .function("setMaxHeightFitContent", &Node::setMaxHeightFitContent) - .function("setMaxHeightStretch", &Node::setMaxHeightStretch) - - .function("setBoxSizing", &Node::setBoxSizing) - - .function("setAspectRatio", &Node::setAspectRatio) - - .function("setBorder", &Node::setBorder) - - .function("setPadding", &Node::setPadding) - .function("setPaddingPercent", &Node::setPaddingPercent) - .function("setGap", &Node::setGap) - .function("setGapPercent", &Node::setGapPercent) - - .function("setDirection", &Node::setDirection) - - .function("getPositionType", &Node::getPositionType) - .function("getPosition", &Node::getPosition) - - .function("getAlignContent", &Node::getAlignContent) - .function("getAlignItems", &Node::getAlignItems) - .function("getAlignSelf", &Node::getAlignSelf) - .function("getFlexDirection", &Node::getFlexDirection) - .function("getFlexWrap", &Node::getFlexWrap) - .function("getJustifyContent", &Node::getJustifyContent) - - .function("getMargin", &Node::getMargin) - - .function("getFlexBasis", &Node::getFlexBasis) - .function("getFlexGrow", &Node::getFlexGrow) - .function("getFlexShrink", &Node::getFlexShrink) - - .function("getWidth", &Node::getWidth) - .function("getHeight", &Node::getHeight) - - .function("getMinWidth", &Node::getMinWidth) - .function("getMinHeight", &Node::getMinHeight) - - .function("getMaxWidth", &Node::getMaxWidth) - .function("getMaxHeight", &Node::getMaxHeight) - - .function("getBoxSizing", &Node::getBoxSizing) - - .function("getAspectRatio", &Node::getAspectRatio) - - .function("getBorder", &Node::getBorder) - - .function("getOverflow", &Node::getOverflow) - .function("getDisplay", &Node::getDisplay) - - .function("getPadding", &Node::getPadding) - .function("getGap", &Node::getGap) - - .function("insertChild", &Node::insertChild, allow_raw_pointers()) - .function("removeChild", &Node::removeChild, allow_raw_pointers()) - - .function("getChildCount", &Node::getChildCount) - - .function("getParent", &Node::getParent, allow_raw_pointers()) - .function("getChild", &Node::getChild, allow_raw_pointers()) - - .function( - "setAlwaysFormsContainingBlock", &Node::setAlwaysFormsContainingBlock) - - .function("isReferenceBaseline", &Node::isReferenceBaseline) - .function("setIsReferenceBaseline", &Node::setIsReferenceBaseline) - - .function("setMeasureFunc", &Node::setMeasureFunc, allow_raw_pointers()) - .function("unsetMeasureFunc", &Node::unsetMeasureFunc) - - .function("setDirtiedFunc", &Node::setDirtiedFunc, allow_raw_pointers()) - .function("unsetDirtiedFunc", &Node::unsetDirtiedFunc) - - .function("markDirty", &Node::markDirty) - .function("isDirty", &Node::isDirty) - - .function("markLayoutSeen", &Node::markLayoutSeen) - .function("hasNewLayout", &Node::hasNewLayout) - - .function("calculateLayout", &Node::calculateLayout) - - .function("getComputedLeft", &Node::getComputedLeft) - .function("getComputedRight", &Node::getComputedRight) - - .function("getComputedTop", &Node::getComputedTop) - .function("getComputedBottom", &Node::getComputedBottom) - - .function("getComputedWidth", &Node::getComputedWidth) - .function("getComputedHeight", &Node::getComputedHeight) - - .function("getComputedLayout", &Node::getComputedLayout) - - .function("getComputedMargin", &Node::getComputedMargin) - .function("getComputedBorder", &Node::getComputedBorder) - .function("getComputedPadding", &Node::getComputedPadding) - - .function("getDirection", &Node::getDirection); -} diff --git a/javascript/src/wasm_bridge.c b/javascript/src/wasm_bridge.c new file mode 100644 index 0000000000..0ad3db0efe --- /dev/null +++ b/javascript/src/wasm_bridge.c @@ -0,0 +1,155 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include +#include + +// Static buffer for returning YGValue structs to JS. +// JS reads buf[0] as float (value) and buf[1] as int (unit) via HEAPF32/HEAP32. +// Safe because WASM is single-threaded. +static float ygvalue_buf[2]; + +static void writeYGValue(YGValue v) { + ygvalue_buf[0] = v.value; + // Store unit as integer bits in a float slot; JS reads via HEAP32. + ((int*)ygvalue_buf)[1] = (int)v.unit; +} + +EMSCRIPTEN_KEEPALIVE float* jswrap_YGValueBuffer(void) { + return ygvalue_buf; +} + +// --- YGValue getter wrappers --- + +EMSCRIPTEN_KEEPALIVE void jswrap_YGNodeStyleGetWidth(YGNodeConstRef node) { + writeYGValue(YGNodeStyleGetWidth(node)); +} + +EMSCRIPTEN_KEEPALIVE void jswrap_YGNodeStyleGetHeight(YGNodeConstRef node) { + writeYGValue(YGNodeStyleGetHeight(node)); +} + +EMSCRIPTEN_KEEPALIVE void jswrap_YGNodeStyleGetPosition( + YGNodeConstRef node, + YGEdge edge) { + writeYGValue(YGNodeStyleGetPosition(node, edge)); +} + +EMSCRIPTEN_KEEPALIVE void jswrap_YGNodeStyleGetMargin( + YGNodeConstRef node, + YGEdge edge) { + writeYGValue(YGNodeStyleGetMargin(node, edge)); +} + +EMSCRIPTEN_KEEPALIVE void jswrap_YGNodeStyleGetPadding( + YGNodeConstRef node, + YGEdge edge) { + writeYGValue(YGNodeStyleGetPadding(node, edge)); +} + +EMSCRIPTEN_KEEPALIVE void jswrap_YGNodeStyleGetFlexBasis(YGNodeConstRef node) { + writeYGValue(YGNodeStyleGetFlexBasis(node)); +} + +EMSCRIPTEN_KEEPALIVE void jswrap_YGNodeStyleGetMinWidth(YGNodeConstRef node) { + writeYGValue(YGNodeStyleGetMinWidth(node)); +} + +EMSCRIPTEN_KEEPALIVE void jswrap_YGNodeStyleGetMinHeight(YGNodeConstRef node) { + writeYGValue(YGNodeStyleGetMinHeight(node)); +} + +EMSCRIPTEN_KEEPALIVE void jswrap_YGNodeStyleGetMaxWidth(YGNodeConstRef node) { + writeYGValue(YGNodeStyleGetMaxWidth(node)); +} + +EMSCRIPTEN_KEEPALIVE void jswrap_YGNodeStyleGetMaxHeight(YGNodeConstRef node) { + writeYGValue(YGNodeStyleGetMaxHeight(node)); +} + +EMSCRIPTEN_KEEPALIVE void jswrap_YGNodeStyleGetGap( + YGNodeConstRef node, + YGGutter gutter) { + writeYGValue(YGNodeStyleGetGap(node, gutter)); +} + +// --- Measure callback bridge --- +// Uses EM_JS to call into a JS-side Map stored on Module. + +EM_JS( + float, + callMeasureFunc, + (YGNodeConstRef nodePtr, + float width, + int widthMode, + float height, + int heightMode, + int returnWidthOrHeight), + { + // clang-format off + var fn = Module["_yogaMeasureFuncs"].get(nodePtr); + if (!fn) + return 0; + if (!fn._cachedResult || fn._cachedWidth !== width || fn._cachedWidthMode !== widthMode || fn._cachedHeight !== height || fn._cachedHeightMode !== heightMode) { + fn._cachedResult = fn(width, widthMode, height, heightMode); + fn._cachedWidth = width; + fn._cachedWidthMode = widthMode; + fn._cachedHeight = height; + fn._cachedHeightMode = heightMode; + } + var result = fn._cachedResult; + if (returnWidthOrHeight === 0) { + var w = result.width; + return (w === undefined || w === null) ? NaN : +w; + } else { + var h = result.height; + return (h === undefined || h === null) ? NaN : +h; + } + // clang-format on + }); + +static YGSize globalMeasureFunc( + YGNodeConstRef nodeRef, + float width, + YGMeasureMode widthMode, + float height, + YGMeasureMode heightMode) { + YGSize size; + size.width = callMeasureFunc( + nodeRef, width, (int)widthMode, height, (int)heightMode, 0); + size.height = callMeasureFunc( + nodeRef, width, (int)widthMode, height, (int)heightMode, 1); + return size; +} + +EMSCRIPTEN_KEEPALIVE void jswrap_YGNodeSetMeasureFunc(YGNodeRef node) { + YGNodeSetMeasureFunc(node, &globalMeasureFunc); +} + +EMSCRIPTEN_KEEPALIVE void jswrap_YGNodeUnsetMeasureFunc(YGNodeRef node) { + YGNodeSetMeasureFunc(node, NULL); +} + +// --- Dirtied callback bridge --- + +EM_JS(void, callDirtiedFunc, (YGNodeConstRef nodePtr), { + var fn = Module["_yogaDirtiedFuncs"].get(nodePtr); + if (fn) + fn(); +}); + +static void globalDirtiedFunc(YGNodeConstRef nodeRef) { + callDirtiedFunc(nodeRef); +} + +EMSCRIPTEN_KEEPALIVE void jswrap_YGNodeSetDirtiedFunc(YGNodeRef node) { + YGNodeSetDirtiedFunc(node, &globalDirtiedFunc); +} + +EMSCRIPTEN_KEEPALIVE void jswrap_YGNodeUnsetDirtiedFunc(YGNodeRef node) { + YGNodeSetDirtiedFunc(node, NULL); +} diff --git a/javascript/src/wrapAssembly.ts b/javascript/src/wrapAssembly.ts index 9b2dc1e6ec..f7a5653a15 100644 --- a/javascript/src/wrapAssembly.ts +++ b/javascript/src/wrapAssembly.ts @@ -275,154 +275,767 @@ export type Yoga = { // eslint-disable-next-line @typescript-eslint/no-explicit-any export default function wrapAssembly(lib: any): Yoga { - function patch(prototype, name, fn) { - const original = prototype[name]; + // Pointer to the static YGValue return buffer (2 floats = 8 bytes). + const valueBufPtr = lib._jswrap_YGValueBuffer(); + // Byte offset into HEAPF32 (4 bytes per float). + const valueBufIdx = valueBufPtr >> 2; - prototype[name] = function (...args) { - return fn.call(this, original, ...args); + // Callback maps stored on Module for EM_JS access. + lib._yogaMeasureFuncs = new Map(); + lib._yogaDirtiedFuncs = new Map(); + + function readYGValue(): Value { + return { + value: lib.HEAPF32[valueBufIdx], + unit: lib.HEAP32[valueBufIdx + 1], }; } - for (const fnName of [ - 'setPosition', - 'setMargin', - 'setFlexBasis', - 'setWidth', - 'setHeight', - 'setMinWidth', - 'setMinHeight', - 'setMaxWidth', - 'setMaxHeight', - 'setPadding', - 'setGap', - ]) { - const methods = { - [Unit.Point]: lib.Node.prototype[fnName], - [Unit.Percent]: lib.Node.prototype[`${fnName}Percent`], - [Unit.Auto]: lib.Node.prototype[`${fnName}Auto`], - [Unit.MaxContent]: lib.Node.prototype[`${fnName}MaxContent`], - [Unit.FitContent]: lib.Node.prototype[`${fnName}FitContent`], - [Unit.Stretch]: lib.Node.prototype[`${fnName}Stretch`], - }; + // --- Polymorphic setter dispatch --- + // wasmPointFn is the direct WASM function for Point values, to avoid + // infinite recursion (since e.g. setWidth is both the polymorphic entry + // point AND what would resolve for Point suffix ''). + function dispatchSetter(fnName, wasmPointFn, args) { + const value = args.pop(); + let unit, asNumber; - patch(lib.Node.prototype, fnName, function (original, ...args) { - // We patch all these functions to add support for the following calls: - // .setWidth(100) / .setWidth("100%") / .setWidth(.getWidth()) / .setWidth("auto") - - const value = args.pop(); - let unit, asNumber; - - if (value === 'auto') { - unit = Unit.Auto; - asNumber = undefined; - } else if (value == 'max-content') { - unit = Unit.MaxContent; - asNumber = undefined; - } else if (value == 'fit-content') { - unit = Unit.FitContent; - asNumber = undefined; - } else if (value == 'stretch') { - unit = Unit.Stretch; - asNumber = undefined; - } else if (typeof value === 'object') { - unit = value.unit; - asNumber = value.valueOf(); - } else { - unit = - typeof value === 'string' && value.endsWith('%') - ? Unit.Percent - : Unit.Point; - asNumber = parseFloat(value); - if ( - value !== undefined && - !Number.isNaN(value) && - Number.isNaN(asNumber) - ) { - throw new Error(`Invalid value ${value} for ${fnName}`); - } + if (value === 'auto') { + unit = Unit.Auto; + asNumber = undefined; + } else if (value === 'max-content') { + unit = Unit.MaxContent; + asNumber = undefined; + } else if (value === 'fit-content') { + unit = Unit.FitContent; + asNumber = undefined; + } else if (value === 'stretch') { + unit = Unit.Stretch; + asNumber = undefined; + } else if (typeof value === 'object') { + unit = value.unit; + asNumber = value.valueOf(); + } else { + unit = + typeof value === 'string' && value.endsWith('%') + ? Unit.Percent + : Unit.Point; + asNumber = parseFloat(value); + if ( + value !== undefined && + !Number.isNaN(value) && + Number.isNaN(asNumber) + ) { + throw new Error(`Invalid value ${value} for ${fnName}`); } + } - if (!methods[unit]) - throw new Error( - `Failed to execute "${fnName}": Unsupported unit '${value}'`, - ); - + if (unit === Unit.Point) { if (asNumber !== undefined) { - return methods[unit].call(this, ...args, asNumber); + return wasmPointFn(this._ptr, ...args, asNumber); } else { - return methods[unit].call(this, ...args); + return wasmPointFn(this._ptr, ...args); } - }); - } + } - function wrapMeasureFunction(measureFunction) { - return lib.MeasureCallback.implement({ - measure: (...args) => { - const {width, height} = measureFunction(...args); - return { - width: width ?? NaN, - height: height ?? NaN, - }; - }, - }); - } + const suffix = { + [Unit.Percent]: 'Percent', + [Unit.Auto]: 'Auto', + [Unit.MaxContent]: 'MaxContent', + [Unit.FitContent]: 'FitContent', + [Unit.Stretch]: 'Stretch', + }[unit]; - patch(lib.Node.prototype, 'setMeasureFunc', function (original, measureFunc) { - // This patch is just a convenience patch, since it helps write more - // idiomatic source code (such as .setMeasureFunc(null)) - if (measureFunc) { - return original.call(this, wrapMeasureFunction(measureFunc)); + if (suffix === undefined) { + throw new Error( + `Failed to execute "${fnName}": Unsupported unit '${value}'`, + ); + } + + const method = this[`${fnName}${suffix}`]; + if (!method) { + throw new Error( + `Failed to execute "${fnName}": Unsupported unit '${value}'`, + ); + } + + if (asNumber !== undefined) { + return method.call(this, ...args, asNumber); } else { - return this.unsetMeasureFunc(); + return method.call(this, ...args); + } + } + + // --- Config class --- + class ConfigImpl { + _ptr: number; + + constructor(ptr: number) { + this._ptr = ptr; + } + + free(): void { + lib._YGConfigFree(this._ptr); + } + + setExperimentalFeatureEnabled( + feature: ExperimentalFeature, + enabled: boolean, + ): void { + lib._YGConfigSetExperimentalFeatureEnabled( + this._ptr, + feature, + enabled ? 1 : 0, + ); } - }); - function wrapDirtiedFunc(dirtiedFunction) { - return lib.DirtiedCallback.implement({dirtied: dirtiedFunction}); + isExperimentalFeatureEnabled(feature: ExperimentalFeature): boolean { + return !!lib._YGConfigIsExperimentalFeatureEnabled(this._ptr, feature); + } + + setPointScaleFactor(factor: number): void { + lib._YGConfigSetPointScaleFactor(this._ptr, factor); + } + + getErrata(): Errata { + return lib._YGConfigGetErrata(this._ptr); + } + + setErrata(errata: Errata): void { + lib._YGConfigSetErrata(this._ptr, errata); + } + + useWebDefaults(): boolean { + return !!lib._YGConfigGetUseWebDefaults(this._ptr); + } + + setUseWebDefaults(useWebDefaults: boolean): void { + lib._YGConfigSetUseWebDefaults(this._ptr, useWebDefaults ? 1 : 0); + } } - patch(lib.Node.prototype, 'setDirtiedFunc', function (original, dirtiedFunc) { - original.call(this, wrapDirtiedFunc(dirtiedFunc)); - }); - - patch(lib.Config.prototype, 'free', function () { - // Since we handle the memory allocation ourselves (via lib.Config.create), - // we also need to handle the deallocation - lib.Config.destroy(this); - }); - - patch(lib.Node, 'create', (_, config) => { - // We decide the constructor we want to call depending on the parameters - return config - ? lib.Node.createWithConfig(config) - : lib.Node.createDefault(); - }); - - patch(lib.Node.prototype, 'free', function () { - // Since we handle the memory allocation ourselves (via lib.Node.create), - // we also need to handle the deallocation - lib.Node.destroy(this); - }); - - patch(lib.Node.prototype, 'freeRecursive', function () { - for (let t = 0, T = this.getChildCount(); t < T; ++t) { - this.getChild(0).freeRecursive(); - } - this.free(); - }); - - patch( - lib.Node.prototype, - 'calculateLayout', - function (original, width = NaN, height = NaN, direction = Direction.LTR) { - // Just a small patch to add support for the function default parameters - return original.call(this, width, height, direction); - }, - ); + // --- Node class --- + class NodeImpl { + _ptr: number; + _children: NodeImpl[]; + _parent: NodeImpl | null; + + constructor(ptr: number) { + this._ptr = ptr; + this._children = []; + this._parent = null; + } + + // --- Tree hierarchy --- + insertChild(child: NodeImpl, index: number): void { + lib._YGNodeInsertChild(this._ptr, child._ptr, index); + this._children.splice(index, 0, child); + child._parent = this; + } + + removeChild(child: NodeImpl): void { + lib._YGNodeRemoveChild(this._ptr, child._ptr); + const idx = this._children.indexOf(child); + if (idx !== -1) { + this._children.splice(idx, 1); + } + child._parent = null; + } + + getChildCount(): number { + return this._children.length; + } + + getChild(index: number): NodeImpl { + return this._children[index]; + } + + getParent(): NodeImpl | null { + return this._parent; + } + + // --- Lifecycle --- + free(): void { + lib._yogaMeasureFuncs.delete(this._ptr); + lib._yogaDirtiedFuncs.delete(this._ptr); + + // Clear JS tree references + if (this._parent) { + const idx = this._parent._children.indexOf(this); + if (idx !== -1) { + this._parent._children.splice(idx, 1); + } + this._parent = null; + } + this._children = []; + + lib._YGNodeFree(this._ptr); + } + + freeRecursive(): void { + // Walk children in JS, calling freeRecursive on each + while (this._children.length > 0) { + this._children[0].freeRecursive(); + } + this.free(); + } + + reset(): void { + lib._yogaMeasureFuncs.delete(this._ptr); + lib._yogaDirtiedFuncs.delete(this._ptr); + this._children = []; + this._parent = null; + lib._YGNodeReset(this._ptr); + } + + // --- Style setters --- + copyStyle(other: NodeImpl): void { + lib._YGNodeCopyStyle(this._ptr, other._ptr); + } + + setPositionType(positionType: PositionType): void { + lib._YGNodeStyleSetPositionType(this._ptr, positionType); + } + + setPosition(edge: Edge, position): void { + dispatchSetter.call(this, 'setPosition', lib._YGNodeStyleSetPosition, [ + edge, + position, + ]); + } + + setPositionPercent(edge: Edge, position: number): void { + lib._YGNodeStyleSetPositionPercent(this._ptr, edge, position); + } + + setPositionAuto(edge: Edge): void { + lib._YGNodeStyleSetPositionAuto(this._ptr, edge); + } + + setAlignContent(alignContent: Align): void { + lib._YGNodeStyleSetAlignContent(this._ptr, alignContent); + } + + setAlignItems(alignItems: Align): void { + lib._YGNodeStyleSetAlignItems(this._ptr, alignItems); + } + + setAlignSelf(alignSelf: Align): void { + lib._YGNodeStyleSetAlignSelf(this._ptr, alignSelf); + } + + setFlexDirection(flexDirection: FlexDirection): void { + lib._YGNodeStyleSetFlexDirection(this._ptr, flexDirection); + } + + setFlexWrap(flexWrap: Wrap): void { + lib._YGNodeStyleSetFlexWrap(this._ptr, flexWrap); + } + + setJustifyContent(justifyContent: Justify): void { + lib._YGNodeStyleSetJustifyContent(this._ptr, justifyContent); + } + + setDirection(direction: Direction): void { + lib._YGNodeStyleSetDirection(this._ptr, direction); + } + + setMargin(edge: Edge, margin): void { + dispatchSetter.call(this, 'setMargin', lib._YGNodeStyleSetMargin, [ + edge, + margin, + ]); + } + + setMarginPercent(edge: Edge, margin: number): void { + lib._YGNodeStyleSetMarginPercent(this._ptr, edge, margin); + } + + setMarginAuto(edge: Edge): void { + lib._YGNodeStyleSetMarginAuto(this._ptr, edge); + } + + setOverflow(overflow: Overflow): void { + lib._YGNodeStyleSetOverflow(this._ptr, overflow); + } + + setDisplay(display: Display): void { + lib._YGNodeStyleSetDisplay(this._ptr, display); + } + + setFlex(flex: number): void { + lib._YGNodeStyleSetFlex(this._ptr, flex); + } + + setFlexBasis(flexBasis): void { + dispatchSetter.call(this, 'setFlexBasis', lib._YGNodeStyleSetFlexBasis, [ + flexBasis, + ]); + } + + setFlexBasisPercent(flexBasis: number): void { + lib._YGNodeStyleSetFlexBasisPercent(this._ptr, flexBasis); + } + + setFlexBasisAuto(): void { + lib._YGNodeStyleSetFlexBasisAuto(this._ptr); + } + + setFlexBasisMaxContent(): void { + lib._YGNodeStyleSetFlexBasisMaxContent(this._ptr); + } + + setFlexBasisFitContent(): void { + lib._YGNodeStyleSetFlexBasisFitContent(this._ptr); + } + + setFlexBasisStretch(): void { + lib._YGNodeStyleSetFlexBasisStretch(this._ptr); + } + + setFlexGrow(flexGrow: number): void { + lib._YGNodeStyleSetFlexGrow(this._ptr, flexGrow); + } + + setFlexShrink(flexShrink: number): void { + lib._YGNodeStyleSetFlexShrink(this._ptr, flexShrink); + } + + setWidth(width): void { + dispatchSetter.call(this, 'setWidth', lib._YGNodeStyleSetWidth, [width]); + } + + setWidthPercent(width: number): void { + lib._YGNodeStyleSetWidthPercent(this._ptr, width); + } + + setWidthAuto(): void { + lib._YGNodeStyleSetWidthAuto(this._ptr); + } + + setWidthMaxContent(): void { + lib._YGNodeStyleSetWidthMaxContent(this._ptr); + } + + setWidthFitContent(): void { + lib._YGNodeStyleSetWidthFitContent(this._ptr); + } + + setWidthStretch(): void { + lib._YGNodeStyleSetWidthStretch(this._ptr); + } + + setHeight(height): void { + dispatchSetter.call(this, 'setHeight', lib._YGNodeStyleSetHeight, [ + height, + ]); + } + + setHeightPercent(height: number): void { + lib._YGNodeStyleSetHeightPercent(this._ptr, height); + } + + setHeightAuto(): void { + lib._YGNodeStyleSetHeightAuto(this._ptr); + } + + setHeightMaxContent(): void { + lib._YGNodeStyleSetHeightMaxContent(this._ptr); + } + + setHeightFitContent(): void { + lib._YGNodeStyleSetHeightFitContent(this._ptr); + } + + setHeightStretch(): void { + lib._YGNodeStyleSetHeightStretch(this._ptr); + } + + setMinWidth(minWidth): void { + dispatchSetter.call(this, 'setMinWidth', lib._YGNodeStyleSetMinWidth, [ + minWidth, + ]); + } + + setMinWidthPercent(minWidth: number): void { + lib._YGNodeStyleSetMinWidthPercent(this._ptr, minWidth); + } + + setMinWidthMaxContent(): void { + lib._YGNodeStyleSetMinWidthMaxContent(this._ptr); + } + + setMinWidthFitContent(): void { + lib._YGNodeStyleSetMinWidthFitContent(this._ptr); + } + + setMinWidthStretch(): void { + lib._YGNodeStyleSetMinWidthStretch(this._ptr); + } + + setMinHeight(minHeight): void { + dispatchSetter.call(this, 'setMinHeight', lib._YGNodeStyleSetMinHeight, [ + minHeight, + ]); + } + + setMinHeightPercent(minHeight: number): void { + lib._YGNodeStyleSetMinHeightPercent(this._ptr, minHeight); + } + + setMinHeightMaxContent(): void { + lib._YGNodeStyleSetMinHeightMaxContent(this._ptr); + } + + setMinHeightFitContent(): void { + lib._YGNodeStyleSetMinHeightFitContent(this._ptr); + } + + setMinHeightStretch(): void { + lib._YGNodeStyleSetMinHeightStretch(this._ptr); + } + + setMaxWidth(maxWidth): void { + dispatchSetter.call(this, 'setMaxWidth', lib._YGNodeStyleSetMaxWidth, [ + maxWidth, + ]); + } + + setMaxWidthPercent(maxWidth: number): void { + lib._YGNodeStyleSetMaxWidthPercent(this._ptr, maxWidth); + } + + setMaxWidthMaxContent(): void { + lib._YGNodeStyleSetMaxWidthMaxContent(this._ptr); + } + + setMaxWidthFitContent(): void { + lib._YGNodeStyleSetMaxWidthFitContent(this._ptr); + } + + setMaxWidthStretch(): void { + lib._YGNodeStyleSetMaxWidthStretch(this._ptr); + } + + setMaxHeight(maxHeight): void { + dispatchSetter.call(this, 'setMaxHeight', lib._YGNodeStyleSetMaxHeight, [ + maxHeight, + ]); + } + + setMaxHeightPercent(maxHeight: number): void { + lib._YGNodeStyleSetMaxHeightPercent(this._ptr, maxHeight); + } + + setMaxHeightMaxContent(): void { + lib._YGNodeStyleSetMaxHeightMaxContent(this._ptr); + } + + setMaxHeightFitContent(): void { + lib._YGNodeStyleSetMaxHeightFitContent(this._ptr); + } + + setMaxHeightStretch(): void { + lib._YGNodeStyleSetMaxHeightStretch(this._ptr); + } + + setAspectRatio(aspectRatio: number): void { + lib._YGNodeStyleSetAspectRatio(this._ptr, aspectRatio); + } + + setBorder(edge: Edge, border: number): void { + lib._YGNodeStyleSetBorder(this._ptr, edge, border); + } + + setPadding(edge: Edge, padding): void { + dispatchSetter.call(this, 'setPadding', lib._YGNodeStyleSetPadding, [ + edge, + padding, + ]); + } + + setPaddingPercent(edge: Edge, padding: number): void { + lib._YGNodeStyleSetPaddingPercent(this._ptr, edge, padding); + } + + setGap(gutter: Gutter, gapLength): void { + dispatchSetter.call(this, 'setGap', lib._YGNodeStyleSetGap, [ + gutter, + gapLength, + ]); + } + + setGapPercent(gutter: Gutter, gapLength: number): void { + lib._YGNodeStyleSetGapPercent(this._ptr, gutter, gapLength); + } + + setBoxSizing(boxSizing: BoxSizing): void { + lib._YGNodeStyleSetBoxSizing(this._ptr, boxSizing); + } + + setIsReferenceBaseline(isReferenceBaseline: boolean): void { + lib._YGNodeSetIsReferenceBaseline(this._ptr, isReferenceBaseline ? 1 : 0); + } + + setAlwaysFormsContainingBlock(alwaysFormsContainingBlock: boolean): void { + lib._YGNodeSetAlwaysFormsContainingBlock( + this._ptr, + alwaysFormsContainingBlock ? 1 : 0, + ); + } + + // --- Style getters --- + getPositionType(): PositionType { + return lib._YGNodeStyleGetPositionType(this._ptr); + } + + getPosition(edge: Edge): Value { + lib._jswrap_YGNodeStyleGetPosition(this._ptr, edge); + return readYGValue(); + } + + getAlignContent(): Align { + return lib._YGNodeStyleGetAlignContent(this._ptr); + } + + getAlignItems(): Align { + return lib._YGNodeStyleGetAlignItems(this._ptr); + } + + getAlignSelf(): Align { + return lib._YGNodeStyleGetAlignSelf(this._ptr); + } + + getFlexDirection(): FlexDirection { + return lib._YGNodeStyleGetFlexDirection(this._ptr); + } + + getFlexWrap(): Wrap { + return lib._YGNodeStyleGetFlexWrap(this._ptr); + } + + getJustifyContent(): Justify { + return lib._YGNodeStyleGetJustifyContent(this._ptr); + } + + getDirection(): Direction { + return lib._YGNodeStyleGetDirection(this._ptr); + } + + getMargin(edge: Edge): Value { + lib._jswrap_YGNodeStyleGetMargin(this._ptr, edge); + return readYGValue(); + } + + getOverflow(): Overflow { + return lib._YGNodeStyleGetOverflow(this._ptr); + } + + getDisplay(): Display { + return lib._YGNodeStyleGetDisplay(this._ptr); + } + + getFlexBasis(): Value { + lib._jswrap_YGNodeStyleGetFlexBasis(this._ptr); + return readYGValue(); + } + + getFlexGrow(): number { + return lib._YGNodeStyleGetFlexGrow(this._ptr); + } + + getFlexShrink(): number { + return lib._YGNodeStyleGetFlexShrink(this._ptr); + } + + getWidth(): Value { + lib._jswrap_YGNodeStyleGetWidth(this._ptr); + return readYGValue(); + } + + getHeight(): Value { + lib._jswrap_YGNodeStyleGetHeight(this._ptr); + return readYGValue(); + } + + getMinWidth(): Value { + lib._jswrap_YGNodeStyleGetMinWidth(this._ptr); + return readYGValue(); + } + + getMinHeight(): Value { + lib._jswrap_YGNodeStyleGetMinHeight(this._ptr); + return readYGValue(); + } + + getMaxWidth(): Value { + lib._jswrap_YGNodeStyleGetMaxWidth(this._ptr); + return readYGValue(); + } + + getMaxHeight(): Value { + lib._jswrap_YGNodeStyleGetMaxHeight(this._ptr); + return readYGValue(); + } + + getAspectRatio(): number { + return lib._YGNodeStyleGetAspectRatio(this._ptr); + } + + getBorder(edge: Edge): number { + return lib._YGNodeStyleGetBorder(this._ptr, edge); + } + + getPadding(edge: Edge): Value { + lib._jswrap_YGNodeStyleGetPadding(this._ptr, edge); + return readYGValue(); + } + + getGap(gutter: Gutter): Value { + lib._jswrap_YGNodeStyleGetGap(this._ptr, gutter); + return readYGValue(); + } + + getBoxSizing(): BoxSizing { + return lib._YGNodeStyleGetBoxSizing(this._ptr); + } + + isReferenceBaseline(): boolean { + return !!lib._YGNodeIsReferenceBaseline(this._ptr); + } + + // --- Measure / Dirtied --- + setMeasureFunc(measureFunc: MeasureFunction | null): void { + if (measureFunc) { + lib._yogaMeasureFuncs.set(this._ptr, measureFunc); + lib._jswrap_YGNodeSetMeasureFunc(this._ptr); + } else { + this.unsetMeasureFunc(); + } + } + + unsetMeasureFunc(): void { + lib._yogaMeasureFuncs.delete(this._ptr); + lib._jswrap_YGNodeUnsetMeasureFunc(this._ptr); + } + + setDirtiedFunc(dirtiedFunc: DirtiedFunction | null): void { + if (dirtiedFunc) { + lib._yogaDirtiedFuncs.set(this._ptr, () => dirtiedFunc(this)); + lib._jswrap_YGNodeSetDirtiedFunc(this._ptr); + } else { + this.unsetDirtiedFunc(); + } + } + + unsetDirtiedFunc(): void { + lib._yogaDirtiedFuncs.delete(this._ptr); + lib._jswrap_YGNodeUnsetDirtiedFunc(this._ptr); + } + + // --- Dirty / Layout --- + markDirty(): void { + lib._YGNodeMarkDirty(this._ptr); + } + + isDirty(): boolean { + return !!lib._YGNodeIsDirty(this._ptr); + } + + markLayoutSeen(): void { + lib._YGNodeSetHasNewLayout(this._ptr, 0); + } + + hasNewLayout(): boolean { + return !!lib._YGNodeGetHasNewLayout(this._ptr); + } + + calculateLayout( + width: number | 'auto' | undefined = NaN, + height: number | 'auto' | undefined = NaN, + direction: Direction = Direction.LTR, + ): void { + lib._YGNodeCalculateLayout(this._ptr, width, height, direction); + } + + // --- Layout getters --- + getComputedLeft(): number { + return lib._YGNodeLayoutGetLeft(this._ptr); + } + + getComputedRight(): number { + return lib._YGNodeLayoutGetRight(this._ptr); + } + + getComputedTop(): number { + return lib._YGNodeLayoutGetTop(this._ptr); + } + + getComputedBottom(): number { + return lib._YGNodeLayoutGetBottom(this._ptr); + } + + getComputedWidth(): number { + return lib._YGNodeLayoutGetWidth(this._ptr); + } + + getComputedHeight(): number { + return lib._YGNodeLayoutGetHeight(this._ptr); + } + + getComputedLayout(): Layout { + return { + left: lib._YGNodeLayoutGetLeft(this._ptr), + right: lib._YGNodeLayoutGetRight(this._ptr), + top: lib._YGNodeLayoutGetTop(this._ptr), + bottom: lib._YGNodeLayoutGetBottom(this._ptr), + width: lib._YGNodeLayoutGetWidth(this._ptr), + height: lib._YGNodeLayoutGetHeight(this._ptr), + }; + } + + getComputedMargin(edge: Edge): number { + return lib._YGNodeLayoutGetMargin(this._ptr, edge); + } + + getComputedBorder(edge: Edge): number { + return lib._YGNodeLayoutGetBorder(this._ptr, edge); + } + + getComputedPadding(edge: Edge): number { + return lib._YGNodeLayoutGetPadding(this._ptr, edge); + } + } return { - Config: lib.Config, - Node: lib.Node, + Config: { + create(): Config { + return new ConfigImpl(lib._YGConfigNew()); + }, + destroy(config: Config): void { + (config as ConfigImpl).free(); + }, + }, + Node: { + create(config?: Config): Node { + if (config) { + return new NodeImpl( + lib._YGNodeNewWithConfig((config as ConfigImpl)._ptr), + ); + } + return new NodeImpl(lib._YGNodeNew()); + }, + createDefault(): Node { + return new NodeImpl(lib._YGNodeNew()); + }, + createWithConfig(config: Config): Node { + return new NodeImpl( + lib._YGNodeNewWithConfig((config as ConfigImpl)._ptr), + ); + }, + destroy(node: Node): void { + (node as NodeImpl).free(); + }, + }, ...YGEnums, }; } diff --git a/yoga/YGMacros.h b/yoga/YGMacros.h index a2b6d60efc..ab9b2627a4 100644 --- a/yoga/YGMacros.h +++ b/yoga/YGMacros.h @@ -29,6 +29,8 @@ #ifdef _WINDLL #define YG_EXPORT __declspec(dllexport) +#elif defined(__EMSCRIPTEN__) +#define YG_EXPORT __attribute__((visibility("default"), used)) #elif !defined(_MSC_VER) #define YG_EXPORT __attribute__((visibility("default"))) #else