GetCallResult browser bridge function implemented
JSBridgeArgumentWrap added to support method-defined arguments JSBaseTransporter updated
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
|
||||
#include "Active/Setting/ValueSetting.h"
|
||||
#include "Active/Setting/Values/StringValue.h"
|
||||
#include "Speckle/Interface/Browser/JSPortal.h"
|
||||
#include "Speckle/Interface/Browser/Bridge/Functions/GetBindingsMethodNames.h"
|
||||
#include "Speckle/Interface/Browser/Bridge/Functions/RunMethod.h"
|
||||
|
||||
@@ -17,7 +18,7 @@ using namespace speckle::utility;
|
||||
|
||||
namespace speckle::interface::browser::bridge {
|
||||
|
||||
class BrowserBridge::ResultCache : public std::map<String, std::unique_ptr<active::serialise::Cargo>> {
|
||||
class BrowserBridge::ResultCache : public std::map<String, std::unique_ptr<Cargo>> {
|
||||
public:
|
||||
//Mutex to control access to the cache
|
||||
std::mutex mutex;
|
||||
@@ -31,7 +32,7 @@ namespace speckle::interface::browser::bridge {
|
||||
name: The JS object name
|
||||
toReserve: The number of supported methods to reserve space for
|
||||
--------------------------------------------------------------------*/
|
||||
BrowserBridge::BrowserBridge(const speckle::utility::String& name) : JSObject{name} {
|
||||
BrowserBridge::BrowserBridge(const String& name) : JSObject{name} {
|
||||
//Populate the required browser bridge functions callable from JS
|
||||
emplace_back(std::make_unique<GetBindingsMethodNames>(*this));
|
||||
emplace_back(std::make_unique<RunMethod>(*this));
|
||||
@@ -56,7 +57,7 @@ ValueSetting BrowserBridge::getMethodNames() const {
|
||||
|
||||
return: A pointer to the requested method (owner does not take ownership, nullptr = failure)
|
||||
--------------------------------------------------------------------*/
|
||||
Functional<>* BrowserBridge::getMethod(const speckle::utility::String& name) const {
|
||||
Functional<>* BrowserBridge::getMethod(const String& name) const {
|
||||
Functional<>* result = nullptr;
|
||||
if (auto method = std::find_if(m_methods->begin(), m_methods->end(), [&](const auto& i) { return i->getName() == name; }); method != m_methods->end())
|
||||
result = method->get();
|
||||
@@ -70,7 +71,27 @@ Functional<>* BrowserBridge::getMethod(const speckle::utility::String& name) con
|
||||
result: The result cargo to send back to the JS
|
||||
requestID: The resquest ID from the JS caller (to correctly pair up the caller and result)
|
||||
--------------------------------------------------------------------*/
|
||||
void BrowserBridge::cacheResult(std::unique_ptr<active::serialise::Cargo> result, speckle::utility::String requestID) {
|
||||
void BrowserBridge::cacheResult(std::unique_ptr<Cargo> result, String requestID) {
|
||||
if (m_portal == nullptr)
|
||||
throw; //TODO: Add exception detail
|
||||
const std::lock_guard<std::mutex> lock(m_result->mutex);
|
||||
(*m_result)[requestID] = std::move(result);
|
||||
m_portal->execute(getName() + ".responseReady('" + requestID + "')"); //TODO: Need to confirm target object name
|
||||
} //BrowserBridge::cacheResult
|
||||
|
||||
|
||||
/*--------------------------------------------------------------------
|
||||
Release the results linked to a specified request ID
|
||||
|
||||
requestID: The required result ID
|
||||
|
||||
return: The results linked to the specified ID (nullptr on failure)
|
||||
--------------------------------------------------------------------*/
|
||||
std::unique_ptr<Cargo> BrowserBridge::releaseResult(String requestID) {
|
||||
std::unique_ptr<Cargo> result;
|
||||
if (auto iter = m_result->find(requestID); iter != m_result->end()) {
|
||||
result = std::move(iter->second);
|
||||
m_result->erase(iter);
|
||||
}
|
||||
return result;
|
||||
} //BrowserBridge::releaseResult
|
||||
|
||||
@@ -53,6 +53,12 @@ namespace speckle::interface::browser::bridge {
|
||||
@param requestID The resquest ID from the JS caller (to correctly pair up the caller and result)
|
||||
*/
|
||||
void cacheResult(std::unique_ptr<active::serialise::Cargo> result, speckle::utility::String requestID);
|
||||
/*!
|
||||
Release the results linked to a specified request ID
|
||||
@param requestID The required result ID
|
||||
@return The results linked to the specified ID (nullptr on failure)
|
||||
*/
|
||||
std::unique_ptr<active::serialise::Cargo> releaseResult(speckle::utility::String requestID);
|
||||
|
||||
protected:
|
||||
/*!
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
#include "Speckle/Interface/Browser/Bridge/Functions/GetCallResult.h"
|
||||
|
||||
#include "Speckle/Interface/Browser/Bridge/BrowserBridge.h"
|
||||
|
||||
#ifdef ARCHICAD
|
||||
#include <ACAPinc.h>
|
||||
#include <MessageLoopExecutor.hpp>
|
||||
#endif
|
||||
|
||||
using namespace active::serialise;
|
||||
using namespace speckle::serialise::jsbase;
|
||||
using namespace speckle::interface::browser;
|
||||
using namespace speckle::interface::browser::bridge;
|
||||
|
||||
/*--------------------------------------------------------------------
|
||||
Constructor
|
||||
|
||||
bridge: The parent bridge object (provides access to bridge methods)
|
||||
--------------------------------------------------------------------*/
|
||||
GetCallResult::GetCallResult(BrowserBridge& bridge) : m_bridge{bridge},
|
||||
JSFunction{"GetCallResult", [&](auto args) { return runMethod(args); }} {
|
||||
} //GetCallResult::GetCallResult
|
||||
|
||||
|
||||
/*--------------------------------------------------------------------
|
||||
Run a specified bridge method
|
||||
|
||||
arguments: The method arguments
|
||||
--------------------------------------------------------------------*/
|
||||
std::unique_ptr<Cargo> GetCallResult::runMethod(JSBridgeArgumentWrap& argument) const {
|
||||
//Confirm argument and function validity
|
||||
if (!argument || (argument.getObjectName() != m_bridge.getName()))
|
||||
return nullptr;
|
||||
return m_bridge.releaseResult(argument.getRequestID());
|
||||
} //GetCallResult::runMethod
|
||||
@@ -0,0 +1,55 @@
|
||||
#ifndef SPECKLE_INTERFACE_BRIDGE_GET_CALL_RESULT
|
||||
#define SPECKLE_INTERFACE_BRIDGE_GET_CALL_RESULT
|
||||
|
||||
#include "Speckle/Interface/Browser/PlatformBinding.h"
|
||||
#include "Speckle/Interface/Browser/JSFunction.h"
|
||||
#include "Speckle/Interface/Browser/Bridge/JSBridgeArgumentWrap.h"
|
||||
|
||||
namespace speckle::interface::browser::bridge {
|
||||
|
||||
class BrowserBridge;
|
||||
|
||||
/*!
|
||||
Function to retrieve the names of the methods supported by the bridge
|
||||
*/
|
||||
class GetCallResult : public JSFunction<JSBridgeArgumentWrap, active::serialise::Cargo, PlatformBinding> {
|
||||
public:
|
||||
|
||||
// MARK: - Constructors
|
||||
|
||||
/*!
|
||||
Constructor
|
||||
@param bridge The parent bridge object (provides access to bridge methods)
|
||||
*/
|
||||
GetCallResult(BrowserBridge& bridge);
|
||||
/*!
|
||||
Copy constructor
|
||||
@param source The object to copy
|
||||
*/
|
||||
GetCallResult(const GetCallResult& source) = default;
|
||||
|
||||
/*!
|
||||
Object cloning
|
||||
@return A clone of this object
|
||||
*/
|
||||
GetCallResult* clonePtr() const override { return new GetCallResult{*this}; }
|
||||
/*!
|
||||
Get an argument instance for the function (used to deserialise/unpack incoming arguments)
|
||||
@return An argument instance
|
||||
*/
|
||||
std::unique_ptr<active::serialise::Cargo> getArgument() const override;
|
||||
|
||||
private:
|
||||
/*!
|
||||
Run a specified bridge method
|
||||
@param argument The method arguments
|
||||
*/
|
||||
std::unique_ptr<active::serialise::Cargo> runMethod(JSBridgeArgumentWrap& argument) const;
|
||||
|
||||
///The parent browser bridge
|
||||
BrowserBridge& m_bridge;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //SPECKLE_INTERFACE_BRIDGE_GET_CALL_RESULT
|
||||
@@ -1,85 +0,0 @@
|
||||
#include "Speckle/Interface/Browser/Bridge/JSBridgeArgument.h"
|
||||
|
||||
#include "Active/Serialise/Inventory/Inventory.h"
|
||||
#include "Active/Serialise/Item/Wrapper/ValueWrap.h"
|
||||
|
||||
using namespace active::serialise;
|
||||
using namespace speckle::interface::browser::bridge;
|
||||
using namespace speckle::utility;
|
||||
|
||||
namespace {
|
||||
|
||||
using enum active::serialise::Entry::Type;
|
||||
|
||||
///The indices of the package items
|
||||
enum FieldIndex {
|
||||
objectName,
|
||||
methodName,
|
||||
requestID,
|
||||
};
|
||||
|
||||
///The package inventory
|
||||
auto myInventory = Inventory {
|
||||
{
|
||||
{ {"binding_name"}, objectName, attribute },
|
||||
{ {"name"}, methodName, attribute },
|
||||
{ {"request_id"}, requestID, attribute },
|
||||
},
|
||||
}.withType(&typeid(JSBridgeArgument));;
|
||||
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------------------
|
||||
Fill an inventory with the cargo items
|
||||
|
||||
inventory: The inventory to receive the cargo items
|
||||
|
||||
return: True if items have been added to the inventory
|
||||
--------------------------------------------------------------------*/
|
||||
bool JSBridgeArgument::fillInventory(active::serialise::Inventory& inventory) const {
|
||||
inventory.merge(myInventory);
|
||||
return true;
|
||||
} //JSBridgeArgument::fillInventory
|
||||
|
||||
|
||||
/*--------------------------------------------------------------------
|
||||
Get the specified cargo
|
||||
|
||||
item: The inventory item to retrieve
|
||||
|
||||
return: The requested cargo (nullptr on failure)
|
||||
--------------------------------------------------------------------*/
|
||||
Cargo::Unique JSBridgeArgument::getCargo(const active::serialise::Inventory::Item& item) const {
|
||||
if (item.ownerType != &typeid(JSBridgeArgument))
|
||||
return nullptr;
|
||||
switch (item.index) {
|
||||
case FieldIndex::objectName:
|
||||
return std::make_unique<ValueWrap<String>>(m_objectName);
|
||||
case FieldIndex::methodName:
|
||||
return std::make_unique<ValueWrap<String>>(m_methodName);
|
||||
case FieldIndex::requestID:
|
||||
return std::make_unique<ValueWrap<String>>(m_requestID);
|
||||
default:
|
||||
return nullptr; //Requested an unknown index
|
||||
}
|
||||
} //JSBridgeArgument::getCargo
|
||||
|
||||
|
||||
/*--------------------------------------------------------------------
|
||||
Set to the default package content
|
||||
--------------------------------------------------------------------*/
|
||||
void JSBridgeArgument::setDefault() {
|
||||
m_objectName.clear();
|
||||
m_methodName.clear();
|
||||
m_requestID.clear();
|
||||
} //JSBridgeArgument::setDefault
|
||||
|
||||
|
||||
/*--------------------------------------------------------------------
|
||||
Validate the cargo data
|
||||
|
||||
return: True if the data has been validated
|
||||
--------------------------------------------------------------------*/
|
||||
bool JSBridgeArgument::validate() {
|
||||
return !m_objectName.empty() && !m_methodName.empty() && !m_requestID.empty();
|
||||
} //JSBridgeArgument::validate
|
||||
@@ -8,6 +8,11 @@ namespace speckle::interface::browser::bridge {
|
||||
|
||||
/*!
|
||||
Base class for the argments passed from JavaScript to a named C++ method in a Speckle bridging object
|
||||
|
||||
NB: The JSBridgeArgumentWrap class will:
|
||||
- Deserialise the essential attributes for determining the target method and arguments;
|
||||
- Create the correct JSBridgeArgument subclass for the emthod/argument and populate it with the collected attributes
|
||||
Therefore, there is no need for this class to handle any deserialisation, and subclasses should only handle the core arguments data
|
||||
*/
|
||||
class JSBridgeArgument : public active::serialise::Package {
|
||||
public:
|
||||
@@ -44,30 +49,25 @@ namespace speckle::interface::browser::bridge {
|
||||
@return The request ID
|
||||
*/
|
||||
const speckle::utility::String& getRequestID() const { return m_requestID; }
|
||||
/*!
|
||||
Fill an inventory with the cargo items
|
||||
@param inventory The inventory to receive the cargo items
|
||||
@return True if items have been added to the inventory
|
||||
*/
|
||||
bool fillInventory(active::serialise::Inventory& inventory) const override { return false; } //Nothing to serialise at this level
|
||||
/*!
|
||||
Get the specified cargo
|
||||
@param item The inventory item to retrieve
|
||||
@return The requested cargo (nullptr on failure
|
||||
*/
|
||||
Cargo::Unique getCargo(const active::serialise::Inventory::Item& item) const override { return nullptr; } //Nothing to serialise at this level
|
||||
|
||||
// MARK: - Functions (serialisation)
|
||||
// MARK: - Functions (mutating)
|
||||
|
||||
/*!
|
||||
Fill an inventory with the cargo items
|
||||
@param inventory The inventory to receive the cargo items
|
||||
@return True if items have been added to the inventory
|
||||
*/
|
||||
bool fillInventory(active::serialise::Inventory& inventory) const override;
|
||||
/*!
|
||||
Get the specified cargo
|
||||
@param item The inventory item to retrieve
|
||||
@return The requested cargo (nullptr on failure)
|
||||
*/
|
||||
Cargo::Unique getCargo(const active::serialise::Inventory::Item& item) const override;
|
||||
/*!
|
||||
Set to the default package content
|
||||
*/
|
||||
Set to the default package content
|
||||
*/
|
||||
void setDefault() override;
|
||||
/*!
|
||||
Validate the cargo data
|
||||
@return True if the data has been validated
|
||||
*/
|
||||
bool validate() override;
|
||||
|
||||
private:
|
||||
///The name of the JS object the argument is targeting
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
#include "Speckle/Interface/Browser/Bridge/JSBridgeArgumentWrap.h"
|
||||
|
||||
#include "Active/Serialise/Inventory/Inventory.h"
|
||||
#include "Active/Serialise/Item/Wrapper/ValueWrap.h"
|
||||
|
||||
using namespace active::serialise;
|
||||
using namespace speckle::interface::browser::bridge;
|
||||
using namespace speckle::utility;
|
||||
|
||||
namespace {
|
||||
|
||||
using enum active::serialise::Entry::Type;
|
||||
|
||||
///The indices of the package items
|
||||
enum FieldIndex {
|
||||
objectName,
|
||||
methodName,
|
||||
requestID,
|
||||
};
|
||||
|
||||
///The package inventory
|
||||
auto myInventory = Inventory {
|
||||
{
|
||||
{ {"binding_name"}, objectName, attribute },
|
||||
{ {"name"}, methodName, attribute },
|
||||
{ {"request_id"}, requestID, attribute },
|
||||
},
|
||||
}.withType(&typeid(JSBridgeArgumentWrap));;
|
||||
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------------------
|
||||
Fill an inventory with the cargo items
|
||||
|
||||
inventory: The inventory to receive the cargo items
|
||||
|
||||
return: True if items have been added to the inventory
|
||||
--------------------------------------------------------------------*/
|
||||
bool JSBridgeArgumentWrap::fillInventory(active::serialise::Inventory& inventory) const {
|
||||
if (!m_isReadingAttributes.has_value() || *m_isReadingAttributes)
|
||||
inventory.merge(myInventory);
|
||||
if (m_argument)
|
||||
m_argument->fillInventory(inventory);
|
||||
return true;
|
||||
} //JSBridgeArgumentWrap::fillInventory
|
||||
|
||||
|
||||
/*--------------------------------------------------------------------
|
||||
Get the specified cargo
|
||||
|
||||
item: The inventory item to retrieve
|
||||
|
||||
return: The requested cargo (nullptr on failure)
|
||||
--------------------------------------------------------------------*/
|
||||
Cargo::Unique JSBridgeArgumentWrap::getCargo(const active::serialise::Inventory::Item& item) const {
|
||||
if (item.ownerType != &typeid(JSBridgeArgumentWrap))
|
||||
return nullptr;
|
||||
switch (item.index) {
|
||||
case FieldIndex::objectName:
|
||||
return std::make_unique<ValueWrap<String>>(m_objectName);
|
||||
case FieldIndex::methodName:
|
||||
return std::make_unique<ValueWrap<String>>(m_methodName);
|
||||
case FieldIndex::requestID:
|
||||
return std::make_unique<ValueWrap<String>>(m_requestID);
|
||||
default:
|
||||
return nullptr; //Requested an unknown index
|
||||
}
|
||||
} //JSBridgeArgumentWrap::getCargo
|
||||
|
||||
|
||||
/*--------------------------------------------------------------------
|
||||
Set to the default package content
|
||||
--------------------------------------------------------------------*/
|
||||
void JSBridgeArgumentWrap::setDefault() {
|
||||
m_objectName.clear();
|
||||
m_methodName.clear();
|
||||
m_requestID.clear();
|
||||
m_argument.reset(); //This will be populated once the target bridge and method are known (and hence the required argument type)
|
||||
} //JSBridgeArgumentWrap::setDefault
|
||||
|
||||
|
||||
/*--------------------------------------------------------------------
|
||||
Validate the cargo data
|
||||
|
||||
return: True if the data has been validated
|
||||
--------------------------------------------------------------------*/
|
||||
bool JSBridgeArgumentWrap::validate() {
|
||||
return !m_objectName.empty() && !m_methodName.empty() && !m_requestID.empty() && (!m_argument | m_argument->validate());
|
||||
} //JSBridgeArgumentWrap::validate
|
||||
|
||||
|
||||
/*--------------------------------------------------------------------
|
||||
Finalise the package attributes (called when isAttributeFirst = true and attributes have been imported)
|
||||
|
||||
return: True if the attributes have been successfully finalised (returning false will cause an exception to be thrown)
|
||||
--------------------------------------------------------------------*/
|
||||
bool JSBridgeArgumentWrap::finaliseAttributes() {
|
||||
if (!m_isReadingAttributes.has_value() || !*m_isReadingAttributes ||m_objectName.empty() || m_methodName.empty())
|
||||
return false;
|
||||
m_isReadingAttributes = false;
|
||||
//Use the deserialised target bridge and method to establish the required arguments (if any)
|
||||
m_argument.reset(JSBridgeArgumentWrap::makeArgument(m_objectName, m_methodName));
|
||||
//If the function doesn't take an argument, we still need to pass along the base class with object name, method etc
|
||||
if (!m_argument)
|
||||
m_argument = std::make_unique<JSBridgeArgument>(m_objectName, m_methodName, m_requestID);
|
||||
return true;
|
||||
} //JSBridgeArgumentWrap::finaliseAttributes
|
||||
@@ -4,9 +4,24 @@
|
||||
#include "Active/Serialise/Package/Package.h"
|
||||
#include "Speckle/Interface/Browser/Bridge/JSBridgeArgument.h"
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
namespace speckle::interface::browser::bridge {
|
||||
|
||||
class JSBridgeArgument;
|
||||
|
||||
/*!
|
||||
Factory function to make an argument object
|
||||
@return A new argument object
|
||||
*/
|
||||
template<typename T>
|
||||
void* constructArgument() {
|
||||
try {
|
||||
return new T();
|
||||
} catch(...) {
|
||||
return nullptr; //Object constructors should throw an exception if incoming data isn't viable (NB: only use for unrecoverable problems)
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
Wrapper for bridge function arguments, determing the target requirement on demand
|
||||
@@ -19,7 +34,7 @@ namespace speckle::interface::browser::bridge {
|
||||
/*!
|
||||
Default constructor
|
||||
*/
|
||||
JSBridgeArgumentWrap();
|
||||
JSBridgeArgumentWrap() {}
|
||||
/*!
|
||||
Copy constructor
|
||||
*/
|
||||
@@ -63,30 +78,85 @@ namespace speckle::interface::browser::bridge {
|
||||
// MARK: - Functions (serialisation)
|
||||
|
||||
/*!
|
||||
Fill an inventory with the cargo items
|
||||
@param inventory The inventory to receive the cargo items
|
||||
@return True if items have been added to the inventory
|
||||
Determine if the package requires attributes to be imported first (primarily for unordered serialisation, e.g. JSON)
|
||||
@return True if the package requires attributes first
|
||||
*/
|
||||
bool isAttributeFirst() const override { return m_isReadingAttributes.value_or(false); }
|
||||
/*!
|
||||
Fill an inventory with the cargo items
|
||||
@param inventory The inventory to receive the cargo items
|
||||
@return True if items have been added to the inventory
|
||||
*/
|
||||
bool fillInventory(active::serialise::Inventory& inventory) const override;
|
||||
/*!
|
||||
Get the specified cargo
|
||||
@param item The inventory item to retrieve
|
||||
@return The requested cargo (nullptr on failure)
|
||||
*/
|
||||
Get the specified cargo
|
||||
@param item The inventory item to retrieve
|
||||
@return The requested cargo (nullptr on failure)
|
||||
*/
|
||||
Cargo::Unique getCargo(const active::serialise::Inventory::Item& item) const override;
|
||||
/*!
|
||||
Set to the default package content
|
||||
*/
|
||||
Set to the default package content
|
||||
*/
|
||||
void setDefault() override;
|
||||
/*!
|
||||
Validate the cargo data
|
||||
@return True if the data has been validated
|
||||
*/
|
||||
Validate the cargo data
|
||||
@return True if the data has been validated
|
||||
*/
|
||||
bool validate() override;
|
||||
/*!
|
||||
Finalise the package attributes (called when isAttributeFirst = true and attributes have been imported)
|
||||
@return True if the attributes have been successfully finalised (returning false will cause an exception to be thrown)
|
||||
*/
|
||||
bool finaliseAttributes() override;
|
||||
|
||||
/*!
|
||||
Make an argument object for a specified bridge method
|
||||
@param bridge The name of the target bridge
|
||||
@param method The name of the target method
|
||||
@return An argument object (nullptr on failure)
|
||||
*/
|
||||
static JSBridgeArgument* makeArgument(const speckle::utility::String& bridge, const speckle::utility::String& method) {
|
||||
if (auto maker = m_argumentFactory.find(JSBridgeArgumentWrap::encode(bridge, method)); (maker != m_argumentFactory.end()))
|
||||
return reinterpret_cast<JSBridgeArgument*>(maker->second());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/*!
|
||||
Add a factory method for constructing the arguments of a specified bridge method
|
||||
@param bridge The name of the target bridge
|
||||
@param method The name of the target method
|
||||
*/
|
||||
template<typename T> requires std::is_base_of_v<JSBridgeArgument, T>
|
||||
static void defineArgument(const speckle::utility::String& bridge, const speckle::utility::String& method) {
|
||||
m_argumentFactory[JSBridgeArgumentWrap::encode(bridge, method)] = std::make_pair( &typeid(T), constructArgument<T>);
|
||||
}
|
||||
|
||||
private:
|
||||
/*!
|
||||
Encode bridge and method names into a single string
|
||||
@param bridge The name of the target bridge
|
||||
@param method The name of the target method
|
||||
@return The encoded string
|
||||
*/
|
||||
static speckle::utility::String encode(const speckle::utility::String& bridge, const speckle::utility::String& method) {
|
||||
return bridge + ":" + method;
|
||||
}
|
||||
|
||||
//Factory function for producing instances from a serialised document object
|
||||
using Production = std::function<void*()>;
|
||||
///Factory functions to construct arguments from linked bridge/method names
|
||||
static std::unordered_map<speckle::utility::String, Production> m_argumentFactory;
|
||||
|
||||
///The name of the JS object the argument is targeting
|
||||
speckle::utility::String m_objectName;
|
||||
///The name of the method to receive the argument
|
||||
speckle::utility::String m_methodName;
|
||||
///An ID to be paired with the method return value
|
||||
speckle::utility::String m_requestID;
|
||||
///The function arguments
|
||||
std::shared_ptr<JSBridgeArgument> m_argument;
|
||||
///True while the attribute are being deserialised
|
||||
std::optional<bool> m_isReadingAttributes = true;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ namespace speckle::interface::browser {
|
||||
@param code The JS code
|
||||
@return True if the code was successfully executed
|
||||
*/
|
||||
bool execute(const speckle::utility::String& code);
|
||||
bool execute(const speckle::utility::String& code) const;
|
||||
/*!
|
||||
Install a JS function object
|
||||
@param object The object to install
|
||||
@@ -70,7 +70,7 @@ namespace speckle::interface::browser {
|
||||
return: True if the code was successfully executed
|
||||
--------------------------------------------------------------------*/
|
||||
template<typename FunctionBinding>
|
||||
bool JSPortal<FunctionBinding>::execute(const speckle::utility::String& code) {
|
||||
bool JSPortal<FunctionBinding>::execute(const speckle::utility::String& code) const {
|
||||
#ifdef ARCHICAD
|
||||
std::shared_ptr<JavascriptEngine> engine{m_engine};
|
||||
return engine ? engine->ExecuteJS(code) : false;
|
||||
|
||||
@@ -116,8 +116,10 @@ namespace speckle::interface::browser {
|
||||
throw; //NB: Throw a system exception here in future with a defined error
|
||||
if constexpr(std::is_same<Return, void>::value)
|
||||
m_function(*parameters); //Parameters and no return value
|
||||
else
|
||||
result.reset(cloneMove(m_function(*parameters))); //Parameters with return value
|
||||
else {
|
||||
auto outgoing = m_function(*parameters); //Parameters with return value
|
||||
result = std::move(outgoing);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
} //NamedFunction<Param, Return, Packaging>::execute
|
||||
|
||||
@@ -142,8 +142,29 @@ namespace {
|
||||
}
|
||||
};
|
||||
|
||||
using JSElements = std::vector<std::pair<JS::Base*, String::Option>>;
|
||||
|
||||
using enum JSBaseIdentity::Type;
|
||||
using enum JSBaseIdentity::Stage;
|
||||
|
||||
|
||||
/*--------------------------------------------------------------------
|
||||
Add an item to a JSBase object
|
||||
|
||||
item: The item to write
|
||||
destination: The JSBase destination
|
||||
--------------------------------------------------------------------*/
|
||||
void addJSBase(GS::Ref<JS::Base> item, const String& tag, GS::Ref<JS::Base>& destination) {
|
||||
//Attempt to add to object
|
||||
if (auto object = dynamic_cast<JS::Object*>(destination.operator JS::Base*()); object != nullptr)
|
||||
object->AddItem(tag, item);
|
||||
//Attempt to add to array
|
||||
else if (auto array = dynamic_cast<JS::Array*>(destination.operator JS::Base*()); object != nullptr)
|
||||
array->AddItem(item);
|
||||
else
|
||||
throw std::system_error(makeJSBaseError(badDestination)); //The destination isn't a container
|
||||
return;
|
||||
} //addJSBase
|
||||
|
||||
|
||||
/*--------------------------------------------------------------------
|
||||
@@ -177,126 +198,104 @@ namespace {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (destination) {
|
||||
auto object = JSON::ObjectValue::Cast(destination);
|
||||
if (object == nullptr)
|
||||
throw std::system_error(makeJSBaseError(badDestination));
|
||||
object->AddValue(tag, newValue);
|
||||
return;
|
||||
}
|
||||
destination = newValue;
|
||||
if (destination)
|
||||
addJSBase(newValue, tag, destination);
|
||||
else
|
||||
destination = newValue;
|
||||
} //writeValue
|
||||
|
||||
|
||||
/*--------------------------------------------------------------------
|
||||
Decompose a JSBase into constitient items, paired with a name where possible
|
||||
|
||||
source: The source JSBase
|
||||
|
||||
return: The items in the JSBase
|
||||
--------------------------------------------------------------------*/
|
||||
JSElements decomposeJSBase(JS::Base& source) {
|
||||
JSElements result;
|
||||
if (auto object = dynamic_cast<JS::Object*>(&source); object != nullptr) {
|
||||
//Decomose an object
|
||||
for (auto& item : object->GetItemTable())
|
||||
result.push_back({item.value->operator JS::Base*(), String{*item.key}});
|
||||
} else if (auto array = dynamic_cast<JS::Array*>(&source); object != nullptr) {
|
||||
//Decomose an array
|
||||
for (auto& item : array->GetItemArray())
|
||||
result.push_back({item, std::nullopt});
|
||||
} else
|
||||
throw std::system_error(makeJSBaseError(badSource)); //The source isn't a container
|
||||
return result;
|
||||
} //decomposeJSBase
|
||||
|
||||
|
||||
/*--------------------------------------------------------------------
|
||||
Import a cargo item from a JSBase element
|
||||
|
||||
cargo: A cargo item to import the phrase
|
||||
source: The JSBase element to be imported
|
||||
--------------------------------------------------------------------*/
|
||||
void readValue(Cargo& cargo, JS::Base& source) {
|
||||
auto* item = dynamic_cast<Item*>(&cargo);
|
||||
if (item == nullptr)
|
||||
throw std::system_error(makeJSBaseError(badValue));
|
||||
|
||||
} //doJSBaseItemImport
|
||||
|
||||
|
||||
/*--------------------------------------------------------------------
|
||||
Import the contents of the specified cargo from JSBase
|
||||
|
||||
container: The cargo container to receive the imported data
|
||||
containerIdentity: The container identity
|
||||
importer: The JSBase data importer
|
||||
depth: The recursion depth into the JSBase hierarchy
|
||||
source: The JSBase source
|
||||
--------------------------------------------------------------------*/
|
||||
/* void doJSBaseImport(Cargo& container, const JSBaseIdentity& containerIdentity, JSBaseImporter& importer, int32_t depth) {
|
||||
if (containerIdentity.type == valueStart) {
|
||||
if (auto* item = dynamic_cast<active::serialise::Item*>(&container); item != nullptr) {
|
||||
importer.getContent(*item);
|
||||
return;
|
||||
}
|
||||
void doJSBaseImport(Cargo& container, const JSBaseIdentity& containerIdentity, JS::Base& source) {
|
||||
if (dynamic_cast<Item*>(&container) != nullptr) {
|
||||
//If we've got a single-value item at the root, import the source value and end
|
||||
readValue(container, source);
|
||||
return;
|
||||
}
|
||||
Inventory inventory = getImportInventoryFor(container);
|
||||
auto attributesRemaining = inventory.attributeSize(true); //This is tracked where the container requires attributes first
|
||||
auto parsingStage = containerIdentity.stage;
|
||||
auto* package = dynamic_cast<Package*>(&container);
|
||||
auto isReadingAttribute = (package != nullptr) && package->isAttributeFirst();
|
||||
std::optional<Memory::size_type> restorePoint;
|
||||
for (;;) { //We break out of this loop when an error occurs or we run out of data
|
||||
Memory::size_type readPoint = importer.getPosition();
|
||||
auto identity = importer.getIdentity(parsingStage); //Get the identity of the next item in the JSBase source
|
||||
switch (identity.type) {
|
||||
case undefined: //End of file
|
||||
if (depth != 0) //Failure if tags haven't been balanced correctly
|
||||
throw std::system_error(makeJSBaseError(unbalancedScope));
|
||||
return;
|
||||
case delimiter:
|
||||
if (parsingStage != complete) //A delimiter has been found before anything was read
|
||||
throw std::system_error(makeJSBaseError(unbalancedScope));
|
||||
parsingStage = containerIdentity.stage;
|
||||
continue; //Move onto the next item
|
||||
case objectStart: case valueStart: case arrayStart: {
|
||||
if (parsingStage == complete) //An element has been read, but no delimiter reached - expected a closing symbol
|
||||
throw std::system_error(makeJSBaseError(unbalancedScope));
|
||||
Cargo::Unique cargo;
|
||||
Inventory::iterator incomingItem = inventory.end();
|
||||
bool isArrayStart = ((identity.type == arrayStart) && !identity.name.empty()), isKnown = true;
|
||||
if (identity.name.empty() || isArrayStart) {
|
||||
if (identity.name.empty() && parsingStage == object) //An element within an object must be identified with a name
|
||||
throw std::system_error(makeJSBaseError(nameMissing));
|
||||
cargo = wrapped(container); //The next element is a child (for array) or instance (for root) of the parent container
|
||||
if (identity.name.empty()) {
|
||||
auto incomingType = identity.type;
|
||||
identity = containerIdentity; //If no name is specified, we adopt the identity specified by the container
|
||||
identity.type = incomingType;
|
||||
if (parsingStage == root)
|
||||
identity.stage = isArrayStart ? array : object;
|
||||
}
|
||||
if (parsingStage == root)
|
||||
cargo->setDefault(); //The root object is sourced externally, so has to be reset to the default separately
|
||||
}
|
||||
if (!identity.name.empty() && (parsingStage != root) && !isArrayStart) { //Allocate new cargo when a new element is reached
|
||||
if (incomingItem = inventory.registerIncoming(identity); incomingItem != inventory.end()) { //Seek the incoming element in the inventory
|
||||
if (isReadingAttribute && !incomingItem->isAttribute())
|
||||
incomingItem = inventory.end();
|
||||
else {
|
||||
if (!incomingItem->bumpAvailable())
|
||||
throw std::system_error(makeJSBaseError(inventoryBoundsExceeded));
|
||||
if ((attributesRemaining > 0) && incomingItem->isAttribute() && incomingItem->required)
|
||||
--attributesRemaining;
|
||||
cargo = (incomingItem == inventory.end()) ? nullptr : container.getCargo(*incomingItem);
|
||||
}
|
||||
}
|
||||
//Allow the parser to move beyond unknown/unwanted elements
|
||||
if (!cargo) {
|
||||
if (importer.isUnknownSkipped() || isReadingAttribute) {
|
||||
isKnown = false;
|
||||
cargo = makeUnknown(identity);
|
||||
if (isReadingAttribute && !restorePoint) //If not all attributes read, parse data twice (first for attributes only)
|
||||
restorePoint = readPoint; //If this is the first instance, set a restore point so reading can resume here
|
||||
} else
|
||||
throw std::system_error(makeJSBaseError(unknownName));
|
||||
}
|
||||
cargo->setDefault();
|
||||
}
|
||||
doJSBaseImport(*cargo, JSBaseIdentity{identity}.atStage((identity.type == arrayStart) ? array : object), importer, depth + 1);
|
||||
if (incomingItem != inventory.end()) {
|
||||
if (incomingItem->isRepeating()) {
|
||||
if ((package != nullptr) && !package->insert(std::move(cargo), *incomingItem))
|
||||
throw std::system_error(makeJSBaseError(invalidObject));
|
||||
}
|
||||
} else if (isKnown && !isArrayStart)
|
||||
return; //If there is no defined item, we're in an array or the root - we need to return the imported element now
|
||||
parsingStage = complete; //An element has been parsed - we either expect a delimiter or a terminator
|
||||
break;
|
||||
}
|
||||
case objectEnd: case arrayEnd:
|
||||
if (containerIdentity.stage != (identity.type == objectEnd ? object : array))
|
||||
throw std::system_error(makeJSBaseError(unbalancedScope)); //The scope end couldn't be paired with the atart
|
||||
if (restorePoint) {
|
||||
isReadingAttribute = false;
|
||||
importer.setPosition(*restorePoint); //Move the read position back to the first non-attribute
|
||||
restorePoint.reset();
|
||||
attributesRemaining = 0; //It may not be an error is this is not already zero - the container will validate the result
|
||||
if (!package->finaliseAttributes())
|
||||
throw std::system_error(makeJSBaseError(invalidObject));
|
||||
inventory = getImportInventoryFor(container); //Having finalised attributes, the container inventory will probably change
|
||||
parsingStage = object; //Resuming reading at non-attributes is always in the context of an object
|
||||
break;
|
||||
}
|
||||
if (!container.validate())
|
||||
throw std::system_error(makeJSBaseError(invalidObject)); //The incoming data was rejected as invalid
|
||||
return;
|
||||
}
|
||||
//Find out what the container can hold
|
||||
Inventory inventory;
|
||||
if (!container.fillInventory(inventory))
|
||||
throw std::system_error(makeJSBaseError(missingInventory));
|
||||
inventory.resetAvailable(); //Reset the availability of each entry to zero so we can count incoming items
|
||||
auto elements = decomposeJSBase(source);
|
||||
if (elements.empty())
|
||||
return;
|
||||
bool isArray = !elements[0].second;
|
||||
Identity parentIdentity{containerIdentity};
|
||||
//Anonymous arrays need an identity
|
||||
if (isArray && parentIdentity.name.empty()) {
|
||||
for (auto& entry : inventory)
|
||||
if (entry.isRepeating())
|
||||
parentIdentity = entry.identity();
|
||||
if (parentIdentity.name.empty())
|
||||
throw std::system_error(makeJSBaseError(invalidObject));
|
||||
}
|
||||
}*/ //doJSBaseImport
|
||||
for (auto& element : elements) {
|
||||
Cargo::Unique cargo;
|
||||
Inventory::iterator incomingItem = inventory.end();
|
||||
Identity identity{element.second.value_or(parentIdentity.name)};
|
||||
if (incomingItem = inventory.registerIncoming(identity); incomingItem != inventory.end()) { //Seek the incoming element in the inventory
|
||||
if (!incomingItem->bumpAvailable())
|
||||
throw std::system_error(makeJSBaseError(inventoryBoundsExceeded));
|
||||
cargo = container.getCargo(*incomingItem);
|
||||
}
|
||||
if (!cargo)
|
||||
continue; //TODO: Add option to throw exception for unknown elements
|
||||
cargo->setDefault();
|
||||
doJSBaseImport(*cargo, identity, *element.first);
|
||||
if (incomingItem->isRepeating()) {
|
||||
if (auto package = dynamic_cast<Package*>(&container);
|
||||
(package != nullptr) && !package->insert(std::move(cargo), *incomingItem))
|
||||
throw std::system_error(makeJSBaseError(invalidObject));
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (!container.validate())
|
||||
throw std::system_error(makeJSBaseError(invalidObject)); //The incoming data was rejected as invalid
|
||||
} //doJSBaseImport
|
||||
|
||||
|
||||
/*--------------------------------------------------------------------
|
||||
@@ -307,75 +306,56 @@ namespace {
|
||||
destination: The JSBase destination
|
||||
--------------------------------------------------------------------*/
|
||||
void doJSBaseExport(const Cargo& cargo, const JSBaseIdentity& identity, GS::Ref<JS::Base>& destination) {
|
||||
String tag;
|
||||
if (identity.stage != root) {
|
||||
if (identity.name.empty()) //Non-root values, i.e. values embedded in an object, must have an identifying name
|
||||
throw std::system_error(makeJSBaseError(nameMissing));
|
||||
//Formulate and write the identifying name
|
||||
tag = identity.name;
|
||||
}
|
||||
const auto* item = dynamic_cast<const Item*>(&cargo);
|
||||
Inventory inventory;
|
||||
//Single-value items won't specify an inventory (no point)
|
||||
if (!cargo.fillInventory(inventory) || (inventory.empty())) {
|
||||
if (item == nullptr)
|
||||
throw std::system_error(makeJSBaseError(missingInventory)); //If anything other than a single-value item lands here, it's an error
|
||||
writeValue(*item, tag, destination);
|
||||
throw std::system_error(makeJSBaseError(badValue)); //Non-items must be named
|
||||
writeValue(*item, identity.name, destination);
|
||||
return;
|
||||
}
|
||||
if ((item != nullptr) && (inventory.size() != 1)) //An item can have multiple values but they must all be a homogenous type, e.g. an array
|
||||
throw std::system_error(makeJSBaseError(badValue));
|
||||
//Determine if this element acts as an object/array wrapper for values
|
||||
//The package will have an outer object wrapper (even if an array) if the outer element has a name that differs from the inner item
|
||||
bool isWrapper = (inventory.size() > 1) || (identity.stage == root) ||
|
||||
(!identity.name.empty() && !inventory.begin()->identity().name.empty() && (inventory.begin()->identity() != identity));
|
||||
//An array package will have a single item within more than one possible value
|
||||
bool isArray = !isWrapper && (inventory.size() == 1) && !(inventory.begin()->maximum() == 1),
|
||||
isFirstItem = true;
|
||||
if (isWrapper)
|
||||
exporter.writeTag(tag, nameSpace, JSBaseIdentity::Type::objectStart, depth++);
|
||||
else if (isArray)
|
||||
exporter.writeTag(tag, nameSpace, JSBaseIdentity::Type::arrayStart, depth);
|
||||
//Determine if this cargo is a wrapper for other cargo, i.e. an object/array
|
||||
bool isWrapperTag = true;
|
||||
if (item != nullptr) {
|
||||
if (inventory.size() != 1)
|
||||
throw std::system_error(makeJSBaseError(badValue));
|
||||
//Items only act as a wrapper when different (non-empty) names are defined by the inventory and the item identity
|
||||
isWrapperTag = !identity.name.empty() && !inventory.begin()->identity().name.empty() && (inventory.begin()->identity() != identity);
|
||||
}
|
||||
auto sequence = inventory.sequence();
|
||||
auto container = destination;
|
||||
if (isWrapperTag) {
|
||||
auto containerType = cargo.entryType().value_or((inventory.size() == 1) && !(inventory.begin()->maximum() == 1) ?
|
||||
Entry::Type::array : Entry::Type::element);
|
||||
if (containerType == Entry::Type::array)
|
||||
container = new JS::Array();
|
||||
else
|
||||
container = new JS::Object();
|
||||
if (destination)
|
||||
addJSBase(container, identity.name, destination);
|
||||
else
|
||||
destination = container;
|
||||
}
|
||||
for (auto& entry : sequence) {
|
||||
auto item = *entry.second;
|
||||
if (!item.required || (item.available == 0))
|
||||
if (!item.required)
|
||||
continue;
|
||||
if (isFirstItem)
|
||||
isFirstItem = false;
|
||||
else
|
||||
exporter.write(",");
|
||||
auto entryNameSpace{item.identity().group.value_or(String())};
|
||||
//Each package item may have multiple available cargo items to export
|
||||
//Each cargo container may contain multiple export items
|
||||
auto limit = item.available;
|
||||
bool isItemArray = item.isRepeating(),
|
||||
isFirstValue = true;
|
||||
if (isItemArray)
|
||||
exporter.writeTag(item.identity().name, entryNameSpace, JSBaseIdentity::Type::arrayStart, depth);
|
||||
for (item.available = 0; item.available < limit; ++item.available) {
|
||||
auto content = cargo.getCargo(item);
|
||||
if (!content)
|
||||
if (auto content = cargo.getCargo(item); content) {
|
||||
doJSBaseExport(*content, item.identity(), container);
|
||||
} else
|
||||
break; //Discontinue an inventory item when the supply runs out
|
||||
if (isFirstValue)
|
||||
isFirstValue = false;
|
||||
else
|
||||
exporter.write(",");
|
||||
doJSBaseExport(*content, isItemArray ? item.identity() : JSBaseIdentity{item.identity()}.atStage(object),
|
||||
exporter, (dynamic_cast<Package*>(content.get()) == nullptr) ? depth : depth + ((identity.stage == root) ? 0 : 1));
|
||||
}
|
||||
if (isItemArray)
|
||||
exporter.writeTag(String{}, String{}, JSBaseIdentity::Type::arrayEnd, depth);
|
||||
}
|
||||
if (isWrapper)
|
||||
exporter.writeTag(String{}, String{}, JSBaseIdentity::Type::objectEnd, --depth);
|
||||
else if (isArray)
|
||||
exporter.writeTag(String{}, String{}, JSBaseIdentity::Type::arrayEnd, depth);
|
||||
} //doJSBaseExport
|
||||
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------------------
|
||||
Send cargo as XML to a specified destination
|
||||
Send cargo as JSBase to a specified destination
|
||||
|
||||
cargo: The cargo to be sent as JS::Base
|
||||
identity: The cargo identity (name, optional namespace)
|
||||
@@ -387,12 +367,14 @@ void JSBaseTransport::send(Cargo&& cargo, const Identity& identity, GS::Ref<JS::
|
||||
|
||||
|
||||
/*--------------------------------------------------------------------
|
||||
Receive cargo from a specified XML source
|
||||
Receive cargo from a specified JSBase source
|
||||
|
||||
cargo: The cargo to receive the JS::Base data
|
||||
identity: The cargo identity (name, optional namespace)
|
||||
source: A reference to a JS::Base object
|
||||
--------------------------------------------------------------------*/
|
||||
void JSBaseTransport::receive(Cargo&& cargo, const Identity& identity, GS::Ref<JS::Base> source) const {
|
||||
doJSBaseImport(cargo, JSBaseIdentity(identity).atStage(root), source);
|
||||
if (!source)
|
||||
throw std::system_error(makeJSBaseError(badSource));
|
||||
doJSBaseImport(cargo, JSBaseIdentity(identity).atStage(root), *source);
|
||||
} //JSBaseTransport::receive
|
||||
|
||||
@@ -123,4 +123,13 @@ namespace speckle::utility {
|
||||
|
||||
}
|
||||
|
||||
///Hashing for String class, e.g. to use as a key in unordered_map
|
||||
template <>
|
||||
struct std::hash<speckle::utility::String> {
|
||||
std::size_t operator()(const speckle::utility::String& k) const {
|
||||
return hash<std::string>()(k); //Just use the hashing provided by std::string
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#endif //SPECKLE_UTILITY_STRING
|
||||
|
||||
@@ -17,10 +17,11 @@
|
||||
21F69EBE2C63C954008B6A06 /* Link.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 21F69EBD2C63C954008B6A06 /* Link.cpp */; };
|
||||
21F69F3B2C6B880C008B6A06 /* JSBaseTransport.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 21F69F382C6B880B008B6A06 /* JSBaseTransport.cpp */; };
|
||||
21F69F512C6CCC25008B6A06 /* BrowserBridge.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 21F69F4A2C6CCC25008B6A06 /* BrowserBridge.cpp */; };
|
||||
21F69F532C6CCC25008B6A06 /* JSBridgeArgument.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 21F69F4C2C6CCC25008B6A06 /* JSBridgeArgument.cpp */; };
|
||||
21F69F5A2C6CDB67008B6A06 /* FunctionBinding.h in Headers */ = {isa = PBXBuildFile; fileRef = 21F69F592C6CDB67008B6A06 /* FunctionBinding.h */; };
|
||||
21F69F612C6D0286008B6A06 /* GetBindingsMethodNames.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 21F69F602C6D0286008B6A06 /* GetBindingsMethodNames.cpp */; };
|
||||
21F69F682C6DFB01008B6A06 /* RunMethod.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 21F69F662C6DFB01008B6A06 /* RunMethod.cpp */; };
|
||||
21F69F7E2C6FD9FC008B6A06 /* GetCallResult.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 21F69F7A2C6FD9FC008B6A06 /* GetCallResult.cpp */; };
|
||||
21F69F812C6FF3B0008B6A06 /* JSBridgeArgumentWrap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 21F69F802C6FF3B0008B6A06 /* JSBridgeArgumentWrap.cpp */; };
|
||||
21F93AEC2B2F406E009A2C5B /* Addon.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 21F93AEA2B2F406D009A2C5B /* Addon.cpp */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
@@ -98,7 +99,6 @@
|
||||
21F69F492C6CC2B8008B6A06 /* Functional.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Functional.h; sourceTree = "<group>"; };
|
||||
21F69F4A2C6CCC25008B6A06 /* BrowserBridge.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BrowserBridge.cpp; sourceTree = "<group>"; };
|
||||
21F69F4B2C6CCC25008B6A06 /* BrowserBridge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BrowserBridge.h; sourceTree = "<group>"; };
|
||||
21F69F4C2C6CCC25008B6A06 /* JSBridgeArgument.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSBridgeArgument.cpp; sourceTree = "<group>"; };
|
||||
21F69F4D2C6CCC25008B6A06 /* JSBridgeArgument.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSBridgeArgument.h; sourceTree = "<group>"; };
|
||||
21F69F4F2C6CCC25008B6A06 /* JSBridgeMethod.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSBridgeMethod.h; sourceTree = "<group>"; };
|
||||
21F69F572C6CDAEE008B6A06 /* GetBindingsMethodNames.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GetBindingsMethodNames.h; sourceTree = "<group>"; };
|
||||
@@ -109,6 +109,9 @@
|
||||
21F69F692C6E0D59008B6A06 /* JSBridgeArgumentWrap.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JSBridgeArgumentWrap.h; sourceTree = "<group>"; };
|
||||
21F69F6A2C6E61E1008B6A06 /* JSPortal.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JSPortal.h; sourceTree = "<group>"; };
|
||||
21F69F6D2C6E7D9F008B6A06 /* PlatformBinding.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PlatformBinding.h; sourceTree = "<group>"; };
|
||||
21F69F7A2C6FD9FC008B6A06 /* GetCallResult.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GetCallResult.cpp; sourceTree = "<group>"; };
|
||||
21F69F7D2C6FD9FC008B6A06 /* GetCallResult.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GetCallResult.h; sourceTree = "<group>"; };
|
||||
21F69F802C6FF3B0008B6A06 /* JSBridgeArgumentWrap.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSBridgeArgumentWrap.cpp; sourceTree = "<group>"; };
|
||||
21F93AE92B2F406D009A2C5B /* Addon.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Addon.h; sourceTree = "<group>"; };
|
||||
21F93AEA2B2F406D009A2C5B /* Addon.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Addon.cpp; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
@@ -290,8 +293,8 @@
|
||||
21F69F4A2C6CCC25008B6A06 /* BrowserBridge.cpp */,
|
||||
21F69F4B2C6CCC25008B6A06 /* BrowserBridge.h */,
|
||||
21F69F582C6CDAEE008B6A06 /* Functions */,
|
||||
21F69F4C2C6CCC25008B6A06 /* JSBridgeArgument.cpp */,
|
||||
21F69F4D2C6CCC25008B6A06 /* JSBridgeArgument.h */,
|
||||
21F69F802C6FF3B0008B6A06 /* JSBridgeArgumentWrap.cpp */,
|
||||
21F69F692C6E0D59008B6A06 /* JSBridgeArgumentWrap.h */,
|
||||
21F69F4F2C6CCC25008B6A06 /* JSBridgeMethod.h */,
|
||||
);
|
||||
@@ -304,6 +307,8 @@
|
||||
21F69F592C6CDB67008B6A06 /* FunctionBinding.h */,
|
||||
21F69F602C6D0286008B6A06 /* GetBindingsMethodNames.cpp */,
|
||||
21F69F572C6CDAEE008B6A06 /* GetBindingsMethodNames.h */,
|
||||
21F69F7A2C6FD9FC008B6A06 /* GetCallResult.cpp */,
|
||||
21F69F7D2C6FD9FC008B6A06 /* GetCallResult.h */,
|
||||
21F69F662C6DFB01008B6A06 /* RunMethod.cpp */,
|
||||
21F69F652C6DFB01008B6A06 /* RunMethod.h */,
|
||||
);
|
||||
@@ -464,11 +469,12 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
21F69F532C6CCC25008B6A06 /* JSBridgeArgument.cpp in Sources */,
|
||||
21F69F682C6DFB01008B6A06 /* RunMethod.cpp in Sources */,
|
||||
21F69F812C6FF3B0008B6A06 /* JSBridgeArgumentWrap.cpp in Sources */,
|
||||
2193517B2C624FC100E5A69C /* MenuSubscriber.cpp in Sources */,
|
||||
21F69F612C6D0286008B6A06 /* GetBindingsMethodNames.cpp in Sources */,
|
||||
21F93AEC2B2F406E009A2C5B /* Addon.cpp in Sources */,
|
||||
21F69F7E2C6FD9FC008B6A06 /* GetCallResult.cpp in Sources */,
|
||||
2193519B2C6278D900E5A69C /* SelectionSubscriber.cpp in Sources */,
|
||||
21F69EBE2C63C954008B6A06 /* Link.cpp in Sources */,
|
||||
219351B32C62CC1A00E5A69C /* String.cpp in Sources */,
|
||||
|
||||
Reference in New Issue
Block a user