mirror of
https://github.com/status-im/react-native.git
synced 2025-02-25 15:45:32 +00:00
Fabric: Exposing EventEmitter's ownership model as a shared_ptr
Summary: As we did in the previous diff, here we implemented `EventEmitter`'s ownership model as a `shared_ptr`. This change fixes problem with leaking `WeakObject`s which happens on hot-reload. So, in short: * `EventTargetWrapper` object owns `jsi::WeakObject` that can be converted to actual `jsi::Object` that represent event target in JavaScript realm; * `EventTargetWrapper` and `jsi::WeakObject` objects must be deallocated as soon as native part does not need them anymore; * `EventEmitter` objects retain `EventTarget` objects; * `EventEmitter` can loose event target object in case if assosiated `ShadowNode` got unmounted (not deallocated); in this case `EventEmitter` is loosing possibility to dispatch event even if some mounting-layer code is still retaining it. Reviewed By: mdvacca Differential Revision: D9762755 fbshipit-source-id: 96e989767a32914db9f4627fce51b044c71f257a
This commit is contained in:
parent
a089df3f8b
commit
c25d5948a5
@ -80,7 +80,7 @@ public:
|
|||||||
* shadow nodes.
|
* shadow nodes.
|
||||||
*/
|
*/
|
||||||
virtual SharedEventEmitter createEventEmitter(
|
virtual SharedEventEmitter createEventEmitter(
|
||||||
const EventTarget &eventTarget,
|
SharedEventTarget eventTarget,
|
||||||
const Tag &tag
|
const Tag &tag
|
||||||
) const = 0;
|
) const = 0;
|
||||||
};
|
};
|
||||||
|
@ -92,10 +92,10 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
virtual SharedEventEmitter createEventEmitter(
|
virtual SharedEventEmitter createEventEmitter(
|
||||||
const EventTarget &eventTarget,
|
SharedEventTarget eventTarget,
|
||||||
const Tag &tag
|
const Tag &tag
|
||||||
) const override {
|
) const override {
|
||||||
return std::make_shared<ConcreteEventEmitter>(eventTarget, tag, eventDispatcher_);
|
return std::make_shared<ConcreteEventEmitter>(std::move(eventTarget), tag, eventDispatcher_);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -18,6 +18,7 @@ namespace react {
|
|||||||
|
|
||||||
class EventDispatcher;
|
class EventDispatcher;
|
||||||
using SharedEventDispatcher = std::shared_ptr<const EventDispatcher>;
|
using SharedEventDispatcher = std::shared_ptr<const EventDispatcher>;
|
||||||
|
using WeakEventDispatcher = std::weak_ptr<const EventDispatcher>;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Represents event-delivery infrastructure.
|
* Represents event-delivery infrastructure.
|
||||||
|
@ -31,10 +31,14 @@ std::recursive_mutex &EventEmitter::DispatchMutex() {
|
|||||||
return mutex;
|
return mutex;
|
||||||
}
|
}
|
||||||
|
|
||||||
EventEmitter::EventEmitter(const EventTarget &eventTarget, const Tag &tag, const std::shared_ptr<const EventDispatcher> &eventDispatcher):
|
EventEmitter::EventEmitter(
|
||||||
eventTarget_(eventTarget),
|
SharedEventTarget eventTarget,
|
||||||
|
Tag tag,
|
||||||
|
WeakEventDispatcher eventDispatcher
|
||||||
|
):
|
||||||
|
eventTarget_(std::move(eventTarget)),
|
||||||
tag_(tag),
|
tag_(tag),
|
||||||
eventDispatcher_(eventDispatcher) {}
|
eventDispatcher_(std::move(eventDispatcher)) {}
|
||||||
|
|
||||||
void EventEmitter::dispatchEvent(
|
void EventEmitter::dispatchEvent(
|
||||||
const std::string &type,
|
const std::string &type,
|
||||||
@ -73,6 +77,9 @@ void EventEmitter::dispatchEvent(
|
|||||||
|
|
||||||
void EventEmitter::setEnabled(bool enabled) const {
|
void EventEmitter::setEnabled(bool enabled) const {
|
||||||
enabled_ = enabled;
|
enabled_ = enabled;
|
||||||
|
if (!enabled) {
|
||||||
|
eventTarget_ = nullptr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EventEmitter::getEnabled() const {
|
bool EventEmitter::getEnabled() const {
|
||||||
|
@ -22,8 +22,16 @@ using SharedEventEmitter = std::shared_ptr<const EventEmitter>;
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Base class for all particular typed event handlers.
|
* Base class for all particular typed event handlers.
|
||||||
* Stores `InstanceHandle` identifying a particular component and the pointer
|
* Stores a pointer to `EventTarget` identifying a particular component and
|
||||||
* to `EventDispatcher` which is responsible for delivering the event.
|
* a weak pointer to `EventDispatcher` which is responsible for delivering the event.
|
||||||
|
*
|
||||||
|
* Note: Retaining an `EventTarget` does *not* guarantee that actual event target
|
||||||
|
* exists and/or valid in JavaScript realm. The `EventTarget` retains an `EventTargetWrapper`
|
||||||
|
* which wraps JavaScript object in `unsafe-unretained` manner. Retaining
|
||||||
|
* the `EventTarget` *does* indicate that we can use that to get an actual
|
||||||
|
* JavaScript object from that in the future *ensuring safety beforehand somehow*;
|
||||||
|
* JSI maintains `WeakObject` object as long as we retain the `EventTarget`.
|
||||||
|
* All `EventTarget` instances must be deallocated before stopping JavaScript machine.
|
||||||
*/
|
*/
|
||||||
class EventEmitter:
|
class EventEmitter:
|
||||||
public std::enable_shared_from_this<EventEmitter> {
|
public std::enable_shared_from_this<EventEmitter> {
|
||||||
@ -37,7 +45,12 @@ class EventEmitter:
|
|||||||
public:
|
public:
|
||||||
static std::recursive_mutex &DispatchMutex();
|
static std::recursive_mutex &DispatchMutex();
|
||||||
|
|
||||||
EventEmitter(const EventTarget &eventTarget, const Tag &tag, const std::shared_ptr<const EventDispatcher> &eventDispatcher);
|
EventEmitter(
|
||||||
|
SharedEventTarget eventTarget,
|
||||||
|
Tag tag,
|
||||||
|
WeakEventDispatcher eventDispatcher
|
||||||
|
);
|
||||||
|
|
||||||
virtual ~EventEmitter() = default;
|
virtual ~EventEmitter() = default;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -59,9 +72,9 @@ protected:
|
|||||||
) const;
|
) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
EventTarget eventTarget_;
|
mutable SharedEventTarget eventTarget_;
|
||||||
Tag tag_;
|
Tag tag_;
|
||||||
std::weak_ptr<const EventDispatcher> eventDispatcher_;
|
WeakEventDispatcher eventDispatcher_;
|
||||||
mutable bool enabled_; // Protected by `DispatchMutex`.
|
mutable bool enabled_; // Protected by `DispatchMutex`.
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -12,8 +12,8 @@
|
|||||||
namespace facebook {
|
namespace facebook {
|
||||||
namespace react {
|
namespace react {
|
||||||
|
|
||||||
EventQueue::EventQueue(const EventPipe &eventPipe, std::unique_ptr<EventBeat> eventBeat):
|
EventQueue::EventQueue(EventPipe eventPipe, std::unique_ptr<EventBeat> eventBeat):
|
||||||
eventPipe_(eventPipe),
|
eventPipe_(std::move(eventPipe)),
|
||||||
eventBeat_(std::move(eventBeat)) {
|
eventBeat_(std::move(eventBeat)) {
|
||||||
eventBeat_->setBeatCallback(std::bind(&EventQueue::onBeat, this));
|
eventBeat_->setBeatCallback(std::bind(&EventQueue::onBeat, this));
|
||||||
}
|
}
|
||||||
@ -40,8 +40,9 @@ void EventQueue::onBeat() const {
|
|||||||
{
|
{
|
||||||
std::lock_guard<std::recursive_mutex> lock(EventEmitter::DispatchMutex());
|
std::lock_guard<std::recursive_mutex> lock(EventEmitter::DispatchMutex());
|
||||||
for (const auto &event : queue) {
|
for (const auto &event : queue) {
|
||||||
|
auto eventTarget = event.eventTarget.lock();
|
||||||
eventPipe_(
|
eventPipe_(
|
||||||
event.isDispachable() ? event.eventTarget : EmptyEventTarget,
|
eventTarget && event.isDispatchable() ? eventTarget.get() : nullptr,
|
||||||
event.type,
|
event.type,
|
||||||
event.payload
|
event.payload
|
||||||
);
|
);
|
||||||
|
@ -25,7 +25,7 @@ namespace react {
|
|||||||
class EventQueue {
|
class EventQueue {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
EventQueue(const EventPipe &eventPipe, std::unique_ptr<EventBeat> eventBeat);
|
EventQueue(EventPipe eventPipe, std::unique_ptr<EventBeat> eventBeat);
|
||||||
virtual ~EventQueue() = default;
|
virtual ~EventQueue() = default;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -11,18 +11,18 @@ namespace facebook {
|
|||||||
namespace react {
|
namespace react {
|
||||||
|
|
||||||
RawEvent::RawEvent(
|
RawEvent::RawEvent(
|
||||||
const std::string &type,
|
std::string type,
|
||||||
const folly::dynamic &payload,
|
folly::dynamic payload,
|
||||||
const EventTarget &eventTarget,
|
WeakEventTarget eventTarget,
|
||||||
const std::function<bool()> &isDispatchable
|
RawEventDispatchable isDispatchable
|
||||||
):
|
):
|
||||||
type(type),
|
type(std::move(type)),
|
||||||
payload(payload),
|
payload(std::move(payload)),
|
||||||
eventTarget(eventTarget),
|
eventTarget(std::move(eventTarget)),
|
||||||
isDispachable_(isDispatchable) {}
|
isDispatchable_(std::move(isDispatchable)) {}
|
||||||
|
|
||||||
bool RawEvent::isDispachable() const {
|
bool RawEvent::isDispatchable() const {
|
||||||
return isDispachable_();
|
return isDispatchable_();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace react
|
} // namespace react
|
||||||
|
@ -23,25 +23,25 @@ public:
|
|||||||
using RawEventDispatchable = std::function<bool()>;
|
using RawEventDispatchable = std::function<bool()>;
|
||||||
|
|
||||||
RawEvent(
|
RawEvent(
|
||||||
const std::string &type,
|
std::string type,
|
||||||
const folly::dynamic &payload,
|
folly::dynamic payload,
|
||||||
const EventTarget &eventTarget,
|
WeakEventTarget eventTarget,
|
||||||
const RawEventDispatchable &isDispachable
|
RawEventDispatchable isDispatchable
|
||||||
);
|
);
|
||||||
|
|
||||||
const std::string type;
|
const std::string type;
|
||||||
const folly::dynamic payload;
|
const folly::dynamic payload;
|
||||||
const EventTarget eventTarget;
|
const WeakEventTarget eventTarget;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Returns `true` if event can be dispatched to `eventTarget`.
|
* Returns `true` if event can be dispatched to `eventTarget`.
|
||||||
* Events that associated with unmounted or deallocated `ShadowNode`s
|
* Events that associated with unmounted or deallocated `ShadowNode`s
|
||||||
* must not be dispatched.
|
* must not be dispatched.
|
||||||
*/
|
*/
|
||||||
bool isDispachable() const;
|
bool isDispatchable() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const RawEventDispatchable isDispachable_;
|
const RawEventDispatchable isDispatchable_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace react
|
} // namespace react
|
||||||
|
@ -24,28 +24,17 @@ enum class EventPriority: int {
|
|||||||
Deferred = AsynchronousBatched
|
Deferred = AsynchronousBatched
|
||||||
};
|
};
|
||||||
|
|
||||||
/* `InstanceHandler`, `EventTarget`, and `EventHandler` are all opaque
|
|
||||||
* raw pointers. We use `struct {} *` trick to differentiate them in compiler's
|
|
||||||
* eyes to ensure type safety.
|
|
||||||
* These structs must have names (and the names must be exported)
|
|
||||||
* to allow consistent template (e.g. `std::function`) instantiating
|
|
||||||
* across different modules.
|
|
||||||
*/
|
|
||||||
using EventTarget = struct EventTargetDummyStruct {} *;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We need this types only to ensure type-safety when we deal with them. Conceptually,
|
* We need this types only to ensure type-safety when we deal with them. Conceptually,
|
||||||
* they are opaque pointers to some types that derived from those classes.
|
* they are opaque pointers to some types that derived from those classes.
|
||||||
*/
|
*/
|
||||||
class EventHandler {};
|
class EventHandler {};
|
||||||
|
class EventTarget {};
|
||||||
|
using SharedEventHandler = std::shared_ptr<const EventHandler>;
|
||||||
|
using SharedEventTarget = std::shared_ptr<const EventTarget>;
|
||||||
|
using WeakEventTarget = std::weak_ptr<const EventTarget>;
|
||||||
|
|
||||||
/*
|
using EventPipe = std::function<void(const EventTarget *eventTarget, const std::string &type, const folly::dynamic &payload)>;
|
||||||
* EmptyEventTarget is used when some event cannot be dispatched to an original
|
|
||||||
* event target but still has to be dispatched to preserve consistency of event flow.
|
|
||||||
*/
|
|
||||||
static const EventTarget EmptyEventTarget = nullptr;
|
|
||||||
|
|
||||||
using EventPipe = std::function<void(const EventTarget &eventTarget, const std::string &type, const folly::dynamic &payload)>;
|
|
||||||
|
|
||||||
} // namespace react
|
} // namespace react
|
||||||
} // namespace facebook
|
} // namespace facebook
|
||||||
|
@ -105,20 +105,15 @@ void FabricUIManager::setDispatchEventToTargetFunction(std::function<DispatchEve
|
|||||||
dispatchEventToTargetFunction_ = dispatchEventFunction;
|
dispatchEventToTargetFunction_ = dispatchEventFunction;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FabricUIManager::setReleaseEventTargetFunction(std::function<ReleaseEventTargetFunction> releaseEventTargetFunction) {
|
void FabricUIManager::dispatchEventToTarget(const EventTarget *eventTarget, const std::string &type, const folly::dynamic &payload) const {
|
||||||
releaseEventTargetFunction_ = releaseEventTargetFunction;
|
if (eventTarget) {
|
||||||
}
|
|
||||||
|
|
||||||
void FabricUIManager::dispatchEventToTarget(const EventTarget &eventTarget, const std::string &type, const folly::dynamic &payload) const {
|
|
||||||
if (eventTarget != EmptyEventTarget) {
|
|
||||||
dispatchEventToTargetFunction_(
|
dispatchEventToTargetFunction_(
|
||||||
*eventHandler_,
|
*eventHandler_,
|
||||||
eventTarget,
|
*eventTarget,
|
||||||
const_cast<std::string &>(type),
|
const_cast<std::string &>(type),
|
||||||
const_cast<folly::dynamic &>(payload)
|
const_cast<folly::dynamic &>(payload)
|
||||||
);
|
);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
dispatchEventToEmptyTargetFunction_(
|
dispatchEventToEmptyTargetFunction_(
|
||||||
*eventHandler_,
|
*eventHandler_,
|
||||||
const_cast<std::string &>(type),
|
const_cast<std::string &>(type),
|
||||||
@ -127,11 +122,7 @@ void FabricUIManager::dispatchEventToTarget(const EventTarget &eventTarget, cons
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FabricUIManager::releaseEventTarget(const EventTarget &eventTarget) const {
|
SharedShadowNode FabricUIManager::createNode(int tag, std::string viewName, int rootTag, folly::dynamic props, SharedEventTarget eventTarget) {
|
||||||
releaseEventTargetFunction_(eventTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
SharedShadowNode FabricUIManager::createNode(int tag, std::string viewName, int rootTag, folly::dynamic props, EventTarget eventTarget) {
|
|
||||||
isLoggingEnabled && LOG(INFO) << "FabricUIManager::createNode(tag: " << tag << ", name: " << viewName << ", rootTag: " << rootTag << ", props: " << props << ")";
|
isLoggingEnabled && LOG(INFO) << "FabricUIManager::createNode(tag: " << tag << ", name: " << viewName << ", rootTag: " << rootTag << ", props: " << props << ")";
|
||||||
|
|
||||||
ComponentName componentName = componentNameByReactViewName(viewName);
|
ComponentName componentName = componentNameByReactViewName(viewName);
|
||||||
@ -142,7 +133,7 @@ SharedShadowNode FabricUIManager::createNode(int tag, std::string viewName, int
|
|||||||
componentDescriptor->createShadowNode({
|
componentDescriptor->createShadowNode({
|
||||||
.tag = tag,
|
.tag = tag,
|
||||||
.rootTag = rootTag,
|
.rootTag = rootTag,
|
||||||
.eventEmitter = componentDescriptor->createEventEmitter(eventTarget, tag),
|
.eventEmitter = componentDescriptor->createEventEmitter(std::move(eventTarget), tag),
|
||||||
.props = componentDescriptor->cloneProps(nullptr, rawProps)
|
.props = componentDescriptor->cloneProps(nullptr, rawProps)
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -246,7 +237,7 @@ void FabricUIManager::completeRoot(int rootTag, const SharedShadowNodeUnsharedLi
|
|||||||
|
|
||||||
void FabricUIManager::registerEventHandler(std::shared_ptr<EventHandler> eventHandler) {
|
void FabricUIManager::registerEventHandler(std::shared_ptr<EventHandler> eventHandler) {
|
||||||
isLoggingEnabled && LOG(INFO) << "FabricUIManager::registerEventHandler(eventHandler: " << eventHandler.get() << ")";
|
isLoggingEnabled && LOG(INFO) << "FabricUIManager::registerEventHandler(eventHandler: " << eventHandler.get() << ")";
|
||||||
eventHandler_ = eventHandler;
|
eventHandler_ = std::move(eventHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace react
|
} // namespace react
|
||||||
|
@ -19,7 +19,7 @@ namespace facebook {
|
|||||||
namespace react {
|
namespace react {
|
||||||
|
|
||||||
using DispatchEventToEmptyTargetFunction = void (const EventHandler &eventHandler, std::string type, folly::dynamic payload);
|
using DispatchEventToEmptyTargetFunction = void (const EventHandler &eventHandler, std::string type, folly::dynamic payload);
|
||||||
using DispatchEventToTargetFunction = void (const EventHandler &eventHandler, EventTarget eventTarget, std::string type, folly::dynamic payload);
|
using DispatchEventToTargetFunction = void (const EventHandler &eventHandler, const EventTarget &eventTarget, std::string type, folly::dynamic payload);
|
||||||
using ReleaseEventTargetFunction = void (EventTarget eventTarget);
|
using ReleaseEventTargetFunction = void (EventTarget eventTarget);
|
||||||
|
|
||||||
class FabricUIManager {
|
class FabricUIManager {
|
||||||
@ -44,16 +44,14 @@ public:
|
|||||||
*/
|
*/
|
||||||
void setDispatchEventToEmptyTargetFunction(std::function<DispatchEventToEmptyTargetFunction> dispatchEventFunction);
|
void setDispatchEventToEmptyTargetFunction(std::function<DispatchEventToEmptyTargetFunction> dispatchEventFunction);
|
||||||
void setDispatchEventToTargetFunction(std::function<DispatchEventToTargetFunction> dispatchEventFunction);
|
void setDispatchEventToTargetFunction(std::function<DispatchEventToTargetFunction> dispatchEventFunction);
|
||||||
void setReleaseEventTargetFunction(std::function<ReleaseEventTargetFunction> releaseEventTargetFunction);
|
|
||||||
|
|
||||||
#pragma mark - Native-facing Interface
|
#pragma mark - Native-facing Interface
|
||||||
|
|
||||||
void dispatchEventToTarget(const EventTarget &eventTarget, const std::string &type, const folly::dynamic &payload) const;
|
void dispatchEventToTarget(const EventTarget *eventTarget, const std::string &type, const folly::dynamic &payload) const;
|
||||||
void releaseEventTarget(const EventTarget &eventTarget) const;
|
|
||||||
|
|
||||||
#pragma mark - JavaScript/React-facing Interface
|
#pragma mark - JavaScript/React-facing Interface
|
||||||
|
|
||||||
SharedShadowNode createNode(Tag reactTag, std::string viewName, Tag rootTag, folly::dynamic props, EventTarget eventTarget);
|
SharedShadowNode createNode(Tag reactTag, std::string viewName, Tag rootTag, folly::dynamic props, SharedEventTarget eventTarget);
|
||||||
SharedShadowNode cloneNode(const SharedShadowNode &node);
|
SharedShadowNode cloneNode(const SharedShadowNode &node);
|
||||||
SharedShadowNode cloneNodeWithNewChildren(const SharedShadowNode &node);
|
SharedShadowNode cloneNodeWithNewChildren(const SharedShadowNode &node);
|
||||||
SharedShadowNode cloneNodeWithNewProps(const SharedShadowNode &node, folly::dynamic props);
|
SharedShadowNode cloneNodeWithNewProps(const SharedShadowNode &node, folly::dynamic props);
|
||||||
@ -71,7 +69,6 @@ private:
|
|||||||
std::shared_ptr<EventHandler> eventHandler_;
|
std::shared_ptr<EventHandler> eventHandler_;
|
||||||
std::function<DispatchEventToEmptyTargetFunction> dispatchEventToEmptyTargetFunction_;
|
std::function<DispatchEventToEmptyTargetFunction> dispatchEventToEmptyTargetFunction_;
|
||||||
std::function<DispatchEventToTargetFunction> dispatchEventToTargetFunction_;
|
std::function<DispatchEventToTargetFunction> dispatchEventToTargetFunction_;
|
||||||
std::function<ReleaseEventTargetFunction> releaseEventTargetFunction_;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace react
|
} // namespace react
|
||||||
|
@ -18,7 +18,7 @@ namespace react {
|
|||||||
ShadowTree::ShadowTree(Tag rootTag):
|
ShadowTree::ShadowTree(Tag rootTag):
|
||||||
rootTag_(rootTag) {
|
rootTag_(rootTag) {
|
||||||
|
|
||||||
const auto noopEventEmitter = std::make_shared<const ViewEventEmitter>(nullptr, rootTag, nullptr);
|
const auto noopEventEmitter = std::make_shared<const ViewEventEmitter>(nullptr, rootTag, std::shared_ptr<const EventDispatcher>());
|
||||||
rootShadowNode_ = std::make_shared<RootShadowNode>(
|
rootShadowNode_ = std::make_shared<RootShadowNode>(
|
||||||
ShadowNodeFragment {
|
ShadowNodeFragment {
|
||||||
.tag = rootTag,
|
.tag = rootTag,
|
||||||
@ -30,6 +30,10 @@ ShadowTree::ShadowTree(Tag rootTag):
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ShadowTree::~ShadowTree() {
|
||||||
|
complete(std::make_shared<SharedShadowNodeList>(SharedShadowNodeList {}));
|
||||||
|
}
|
||||||
|
|
||||||
Tag ShadowTree::getRootTag() const {
|
Tag ShadowTree::getRootTag() const {
|
||||||
return rootTag_;
|
return rootTag_;
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,8 @@ public:
|
|||||||
*/
|
*/
|
||||||
ShadowTree(Tag rootTag);
|
ShadowTree(Tag rootTag);
|
||||||
|
|
||||||
|
~ShadowTree();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Returns the rootTag associated with the shadow tree (the tag of the
|
* Returns the rootTag associated with the shadow tree (the tag of the
|
||||||
* root shadow node).
|
* root shadow node).
|
||||||
|
Loading…
x
Reference in New Issue
Block a user