refactor: improve functions

Closes: #49
This commit is contained in:
2026-07-08 00:19:37 +02:00
parent 7de645d323
commit d49b4f5bc7
10 changed files with 188 additions and 122 deletions
+8 -5
View File
@@ -2,6 +2,7 @@
#include "furlang/ir/function.hpp" #include "furlang/ir/function.hpp"
#include "furlang/ir/instruction.hpp" #include "furlang/ir/instruction.hpp"
#include "furvm/function.hpp"
#include "furvm/fwd.hpp" #include "furvm/fwd.hpp"
#include <furvm/instruction.hpp> #include <furvm/instruction.hpp>
@@ -20,12 +21,14 @@ furvm::mod furvm_generator::generate(furlang::ir::mod& mod) {
} }
void furvm_generator::generate_function(furvm::mod& mod, const furlang::ir::function& function) { void furvm_generator::generate_function(furvm::mod& mod, const furlang::ir::function& function) {
furvm::function_sig signature; // TODO: Complete
switch (function.type()) { switch (function.type()) {
case furlang::ir::function_t::Normal: { case furlang::ir::function_t::Normal: {
if (function.access() == furlang::ir::function_access_t::Public) if (function.access() == furlang::ir::function_access_t::Public)
mod.emplace_function_named(function.name(), function.param_count(), mod.bytecode().size()).dispatch(); mod.emplace_function(function.name(), std::move(signature), mod.bytecode().size()).dispatch();
else else
mod.emplace_function(function.param_count(), mod.bytecode().size()).dispatch(); mod.emplace_function(std::move(signature), mod.bytecode().size()).dispatch();
function_context ctx; function_context ctx;
for (furlang::ir::block_index idx = 0; idx < function.blocks().size(); ++idx) { for (furlang::ir::block_index idx = 0; idx < function.blocks().size(); ++idx) {
@@ -48,9 +51,9 @@ void furvm_generator::generate_function(furvm::mod& mod, const furlang::ir::func
} break; } break;
case furlang::ir::function_t::Native: { case furlang::ir::function_t::Native: {
if (function.access() == furlang::ir::function_access_t::Public) if (function.access() == furlang::ir::function_access_t::Public)
mod.emplace_function_named(function.name(), function.param_count(), function.name()).dispatch(); mod.emplace_function(function.name(), std::move(signature), function.name()).dispatch();
else else
mod.emplace_function(function.param_count(), function.name()).dispatch(); mod.emplace_function(std::move(signature), function.name()).dispatch();
} break; } break;
} }
} }
@@ -150,7 +153,7 @@ void furvm_generator::generate_instruction(furvm::mod& mod,
const auto& call = dynamic_cast<const furlang::ir::call_instruction&>(instr); const auto& call = dynamic_cast<const furlang::ir::call_instruction&>(instr);
// TODO: Implement a queue for unknown functions // TODO: Implement a queue for unknown functions
furvm::function_id func = mod.function_at(call.name()).id(); furvm::function_id func = mod.function_at(call.name(), furvm::function_sig{}).id(); // TODO: Complete
mod.bytecode().push_back(static_cast<furvm::byte>(furvm::instruction_t::Call)); mod.bytecode().push_back(static_cast<furvm::byte>(furvm::instruction_t::Call));
mod.bytecode().push_back((func >> 0) & 0xFF); mod.bytecode().push_back((func >> 0) & 0xFF);
mod.bytecode().push_back((func >> 8) & 0xFF); mod.bytecode().push_back((func >> 8) & 0xFF);
+1 -1
View File
@@ -71,7 +71,7 @@ int main(void) {
[](furvm::executor& executor) { std::cout << executor.load_thing(0)->integer() << '\n'; }); [](furvm::executor& executor) { std::cout << executor.load_thing(0)->integer() << '\n'; });
furvm::executor_h executor = context->emplace_executor(context); furvm::executor_h executor = context->emplace_executor(context);
executor->push_frame(furvmMod, *furvmMod->function_at("main")); executor->push_frame(furvmMod, *furvmMod->function_at("main", furvm::function_sig{}));
std::cout << "--- Interpreting:\n"; std::cout << "--- Interpreting:\n";
+6
View File
@@ -1,6 +1,7 @@
#ifndef FURVM_DETAIL_HANDLE_HPP #ifndef FURVM_DETAIL_HANDLE_HPP
#define FURVM_DETAIL_HANDLE_HPP #define FURVM_DETAIL_HANDLE_HPP
#include <functional>
#include <type_traits> #include <type_traits>
namespace furvm { namespace furvm {
@@ -27,6 +28,11 @@ struct header_has_refcount<Header,
template <typename Header> template <typename Header>
static constexpr auto header_has_refcount_v = header_has_refcount<Header>::value; static constexpr auto header_has_refcount_v = header_has_refcount<Header>::value;
template <typename Handle, typename IdHash = std::hash<typename Handle::id_type>>
struct handle_hash {
std::size_t operator()(const Handle& handle) const { return IdHash{}(handle.id()); }
};
} // namespace detail } // namespace detail
} // namespace furvm } // namespace furvm
+41 -12
View File
@@ -1,6 +1,7 @@
#ifndef FURVM_FUNCTION_HPP #ifndef FURVM_FUNCTION_HPP
#define FURVM_FUNCTION_HPP #define FURVM_FUNCTION_HPP
#include "furlang/utility/hash.hpp"
#include "furvm/fwd.hpp" #include "furvm/fwd.hpp"
#include "furvm/handle.hpp" // IWYU pragma: keep #include "furvm/handle.hpp" // IWYU pragma: keep
@@ -9,6 +10,7 @@
#include <string> #include <string>
#include <type_traits> #include <type_traits>
#include <utility> #include <utility>
#include <vector>
namespace furvm { namespace furvm {
@@ -31,26 +33,43 @@ struct import_function {
function_id function; function_id function;
}; };
/**
* @brief Function signature.
*/
struct function_sig {
std::vector<type_h> params;
bool operator==(const function_sig& rhs) const { return params == rhs.params; }
bool operator!=(const function_sig& rhs) const { return !this->operator==(rhs); }
};
class function { class function {
public: public:
/** /**
* @brief Constructs a normal function. * @brief Constructs a normal function.
* *
* @param paramCount Paremeter count. * @param signature Function's signature.
* @param position Offset in bytecode of the function. * @param position Offset in bytecode of the function.
*/ */
function(std::uint32_t paramCount, bytecode_pos position) template <typename SigFwd, typename = std::enable_if_t<std::is_constructible_v<function_sig, SigFwd>>>
: m_type(function_t::Normal), m_paramCount(paramCount), m_value(position) {} function(SigFwd&& signature, bytecode_pos position)
: m_type(function_t::Normal), m_signature(std::forward<SigFwd>(signature)), m_value(position) {}
/** /**
* @brief Constructs a native function. * @brief Constructs a native function.
* *
* @param paramCount Paremeter count. * @param signature Function's signature.
* @param native Native function tag. * @param native Native function tag.
*/ */
template <typename Native, typename = std::enable_if_t<std::is_constructible_v<native_function, Native>>> template <typename SigFwd,
function(std::uint32_t paramCount, Native&& native) typename Native,
: m_type(function_t::Native), m_paramCount(paramCount), m_value(std::forward<Native>(native)) {} typename = std::enable_if_t<std::is_constructible_v<native_function, Native> &&
std::is_constructible_v<function_sig, SigFwd>>>
function(SigFwd&& signature, Native&& native)
: m_type(function_t::Native),
m_signature(std::forward<SigFwd>(signature)),
m_value(std::forward<Native>(native)) {}
/** /**
* @brief Constructs an import function. * @brief Constructs an import function.
@@ -60,7 +79,7 @@ public:
*/ */
template <typename ModFwd, typename = std::enable_if_t<std::is_constructible_v<mod_id, ModFwd>>> template <typename ModFwd, typename = std::enable_if_t<std::is_constructible_v<mod_id, ModFwd>>>
function(ModFwd&& mod, function_id function) function(ModFwd&& mod, function_id function)
: m_type(function_t::Import), m_paramCount(0), m_value(import_function{ std::forward<ModFwd>(mod), function }) {} : m_type(function_t::Import), m_signature(), m_value(import_function{ std::forward<ModFwd>(mod), function }) {}
/** /**
* @brief Constructs an import function. * @brief Constructs an import function.
@@ -103,11 +122,11 @@ public:
constexpr function_t type() const { return m_type; } constexpr function_t type() const { return m_type; }
/** /**
* @brief Returns this function's parameter count. * @brief Returns this function's signature.
* *
* @return The parameter count. * @return The signature.
*/ */
constexpr std::uint32_t param_count() const { return m_paramCount; } function_sig signature() const { return m_signature; }
public: public:
/** /**
* @brief Returns normal function's value. * @brief Returns normal function's value.
@@ -140,7 +159,7 @@ public:
} }
private: private:
function_t m_type; function_t m_type;
std::uint32_t m_paramCount; function_sig m_signature;
union value { union value {
std::size_t position = 0; std::size_t position = 0;
@@ -168,6 +187,16 @@ private:
} m_value; } m_value;
}; };
namespace detail {
struct function_sig_hash {
std::size_t operator()(const function_sig& signature) const {
return furlang::utility::vector_hash<type_h, detail::handle_hash<type_h>>{}(signature.params);
}
};
} // namespace detail
} // namespace furvm } // namespace furvm
#endif // FURVM_FUNCTION_HPP #endif // FURVM_FUNCTION_HPP
+4
View File
@@ -237,6 +237,10 @@ public:
* @brief Invalidates the handle without releasing. * @brief Invalidates the handle without releasing.
*/ */
void dispatch() { m_value = nullptr; } void dispatch() { m_value = nullptr; }
public:
bool operator==(const handle& rhs) const { return m_value == rhs.m_value; }
bool operator!=(const handle& rhs) const { return !this->operator==(rhs); }
private: private:
pair_type* m_value = nullptr; pair_type* m_value = nullptr;
}; };
+25 -22
View File
@@ -1,6 +1,7 @@
#ifndef FURVM_MODULE_HPP #ifndef FURVM_MODULE_HPP
#define FURVM_MODULE_HPP #define FURVM_MODULE_HPP
#include "furlang/utility/hash.hpp"
#include "furvm/function.hpp" #include "furvm/function.hpp"
#include "furvm/fwd.hpp" #include "furvm/fwd.hpp"
#include "furvm/handle.hpp" #include "furvm/handle.hpp"
@@ -90,7 +91,6 @@ public:
} else { } else {
function = std::move(m_functions.emplace(std::forward<Args>(args)...)); function = std::move(m_functions.emplace(std::forward<Args>(args)...));
} }
m_functionMap[function.id()] = "";
return std::move(function); return std::move(function);
} }
@@ -99,22 +99,23 @@ public:
* *
* Emplaces the function in module's function container and name to function map. * Emplaces the function in module's function container and name to function map.
* *
* @param name Name of the function.
* @param args Arguments forwarded into the container's emplace_back function. * @param args Arguments forwarded into the container's emplace_back function.
* @return A handle to the emplaced function. * @return A handle to the emplaced function.
*/ */
template <typename NameFwd, template <typename NameFwd,
typename... Args, typename... Args,
typename = std::enable_if_t<std::is_constructible_v<std::string, NameFwd>>> typename = std::enable_if_t<std::is_constructible_v<std::string, NameFwd>>>
function_h emplace_function_named(NameFwd&& name, Args&&... args) { function_h emplace_function(NameFwd&& name, Args&&... args) {
function_h function; function_h function;
if constexpr (std::is_constructible_v<class function, Args...>) { if constexpr (std::is_constructible_v<class function, Args...>) {
function = std::move(m_functions.emplace_back(std::forward<Args>(args)...)); function = std::move(m_functions.emplace_back(std::forward<Args>(args)...));
} else { } else {
function = std::move(m_functions.emplace(std::forward<Args>(args)...)); function = std::move(m_functions.emplace(std::forward<Args>(args)...));
} }
std::string nameInst = std::forward<NameFwd>(name); auto pair = std::make_pair(std::forward<NameFwd>(name), function->signature());
m_functionNames[nameInst] = function.id(); m_functionMap[function.id()] = pair;
m_functionMap[function.id()] = nameInst; m_functionSigs[std::move(pair)] = function.id();
return std::move(function); return std::move(function);
} }
@@ -140,20 +141,13 @@ public:
* @param name Name of the function. * @param name Name of the function.
* @return A handle to the function. * @return A handle to the function.
*/ */
template <typename NameFwd, typename = std::enable_if_t<std::is_constructible_v<std::string, NameFwd>>> template <typename NameFwd,
auto function_at(NameFwd&& name) { typename SigFwd,
return function_at(m_functionNames.at(std::forward<NameFwd>(name))); typename = std::enable_if_t<std::is_constructible_v<std::string, NameFwd> &&
} std::is_constructible_v<function_sig, SigFwd>>>
auto function_at(NameFwd&& name, SigFwd&& signature) {
/** return function_at(
* @brief Returns a function from the module. m_functionSigs.at(std::make_pair<>(std::forward<NameFwd>(name), std::forward<SigFwd>(signature))));
*
* @param name Name of the function.
* @return A handle to the function.
*/
template <typename NameFwd, typename = std::enable_if_t<std::is_constructible_v<std::string, NameFwd>>>
auto function_at(NameFwd&& name) const {
return function_at(m_functionNames.at(std::forward<NameFwd>(name)));
} }
/** /**
@@ -161,7 +155,13 @@ public:
* *
* @param id Identifier of the function. * @param id Identifier of the function.
*/ */
void erase_function(function_id id) { m_functions.erase(id); } void erase_function(function_id id) {
m_functions.erase(id);
if (auto it = m_functionMap.find(id); it != m_functionMap.end()) {
m_functionSigs.erase(it->second);
m_functionMap.erase(it);
}
}
public: public:
template <typename NameFwd, typename Func> template <typename NameFwd, typename Func>
void set_native_function(NameFwd&& name, Func&& func) { void set_native_function(NameFwd&& name, Func&& func) {
@@ -238,8 +238,11 @@ public:
private: private:
bytecode_t m_bytecode; bytecode_t m_bytecode;
std::unordered_map<std::string, function_id> m_functionNames; using pair_type = std::pair<std::string, function_sig>;
std::unordered_map<function_id, std::string> m_functionMap; using pair_hash =
furlang::utility::pair_hash<std::string, function_sig, std::hash<std::string>, detail::function_sig_hash>;
std::unordered_map<pair_type, function_id, pair_hash> m_functionSigs;
std::unordered_map<function_id, pair_type> m_functionMap;
handle_container<function_h> m_functions; handle_container<function_h> m_functions;
handle_container<type_h> m_types; handle_container<type_h> m_types;
+2 -2
View File
@@ -22,8 +22,8 @@ void executor::push_frame(const mod_h& mod, function function) {
} }
std::vector<thing_h> args; std::vector<thing_h> args;
args.reserve(function.param_count()); args.reserve(function.signature().params.size());
for (size_t i = 0; i < function.param_count(); ++i) while (args.size() < args.capacity())
args.push_back(pop_thing()); args.push_back(pop_thing());
switch (function.type()) { switch (function.type()) {
+5 -5
View File
@@ -7,7 +7,7 @@
namespace furvm { namespace furvm {
function::function(const mod_h& mod, const function_h& function) function::function(const mod_h& mod, const function_h& function)
: m_type(function_t::Import), m_paramCount(0), m_value(import_function{ mod.id(), function.id() }) {} : m_type(function_t::Import), m_value(import_function{ mod.id(), function.id() }) {}
function::~function() { function::~function() {
switch (m_type) { switch (m_type) {
@@ -23,7 +23,7 @@ function::~function() {
} }
function::function(function&& other) noexcept function::function(function&& other) noexcept
: m_type(other.m_type), m_paramCount(other.m_paramCount) { : m_type(other.m_type), m_signature(std::move(other.m_signature)) {
switch (m_type) { switch (m_type) {
case function_t::Normal: { case function_t::Normal: {
m_value.position = other.m_value.position; m_value.position = other.m_value.position;
@@ -43,7 +43,7 @@ function& function::operator=(function&& other) noexcept {
if (this == &other) return *this; if (this == &other) return *this;
m_type = other.m_type; m_type = other.m_type;
m_paramCount = other.m_paramCount; m_signature = other.m_signature;
switch (m_type) { switch (m_type) {
case function_t::Normal: { case function_t::Normal: {
m_value.position = other.m_value.position; m_value.position = other.m_value.position;
@@ -62,7 +62,7 @@ function& function::operator=(function&& other) noexcept {
} }
function::function(const function& other) function::function(const function& other)
: m_type(other.m_type), m_paramCount(other.m_paramCount) { : m_type(other.m_type), m_signature(other.m_signature) {
switch (m_type) { switch (m_type) {
case function_t::Normal: { case function_t::Normal: {
m_value.position = other.m_value.position; m_value.position = other.m_value.position;
@@ -81,7 +81,7 @@ function& function::operator=(const function& other) {
if (this == &other) return *this; if (this == &other) return *this;
m_type = other.m_type; m_type = other.m_type;
m_paramCount = other.m_paramCount; m_signature = other.m_signature;
switch (m_type) { switch (m_type) {
case function_t::Normal: { case function_t::Normal: {
m_value.position = other.m_value.position; m_value.position = other.m_value.position;
+3 -3
View File
@@ -59,9 +59,9 @@ int main(int argc, char** argv) {
}; };
furvm::mod_h mod = context->emplace("main", s_bytecode, s_bytecode + sizeof(s_bytecode)); furvm::mod_h mod = context->emplace("main", s_bytecode, s_bytecode + sizeof(s_bytecode));
mod->emplace_type(std::make_shared<furvm::type>(context->at("core")->type_at(2), 10)).dispatch(); furvm::type_h intType = mod->emplace_type(std::make_shared<furvm::type>(context->at("core")->type_at(2), 10));
auto mainFunc = mod->emplace_function_named("main", 0, 0); auto mainFunc = mod->emplace_function("main", furvm::function_sig{}, 0);
mod->emplace_function(1, "print").dispatch(); mod->emplace_function(furvm::function_sig{ { intType } }, "print").dispatch();
#endif #endif
mod->set_native_function("print", [](furvm::executor& executor) { print_thing(*executor.load_thing(0)); }); mod->set_native_function("print", [](furvm::executor& executor) { print_thing(*executor.load_thing(0)); });
+88 -67
View File
@@ -1,6 +1,7 @@
#include "furvm/module.hpp" #include "furvm/module.hpp"
#include "furvm/detail/serialization.hpp" #include "furvm/detail/serialization.hpp"
#include "furvm/function.hpp"
#include "furvm/fwd.hpp" #include "furvm/fwd.hpp"
#include "furvm/type.hpp" #include "furvm/type.hpp"
@@ -17,34 +18,6 @@ std::ostream& mod::serialize(std::ostream& os) const {
os.write(MAGIC, sizeof(MAGIC)); os.write(MAGIC, sizeof(MAGIC));
detail::serialize(os, std::uint32_t(0)); // version detail::serialize(os, std::uint32_t(0)); // version
function_id funcCount = m_functions.cend() - m_functions.cbegin();
detail::serialize(os, funcCount);
for (function_id id = 0; id < funcCount; ++id) {
if (!m_functions.contains(id)) {
detail::serialize(os, "");
detail::serialize(os, 0);
detail::serialize(os, std::uint8_t(0xFF)); // null function
continue;
}
function_h func = m_functions.at(id);
detail::serialize(os, m_functionMap.at(func.id()));
detail::serialize(os, func->param_count());
detail::serialize(os, std::uint8_t(func->type()));
switch (func->type()) {
case function_t::Normal: {
detail::serialize(os, func->position());
} break;
case function_t::Native: {
detail::serialize(os, func->native());
} break;
case function_t::Import: {
detail::serialize(os, func->imp().mod);
detail::serialize(os, func->imp().function);
} break;
}
}
type_id typeCount = type_id(m_types.cend() - m_types.cbegin()); type_id typeCount = type_id(m_types.cend() - m_types.cbegin());
detail::serialize(os, typeCount); detail::serialize(os, typeCount);
for (type_id id = 0; id < typeCount; ++id) { for (type_id id = 0; id < typeCount; ++id) {
@@ -67,6 +40,45 @@ std::ostream& mod::serialize(std::ostream& os) const {
} }
} }
function_id funcCount = m_functions.cend() - m_functions.cbegin();
detail::serialize(os, funcCount);
for (function_id id = 0; id < funcCount; ++id) {
if (!m_functions.contains(id)) {
detail::serialize(os, "");
detail::serialize(os, 0);
detail::serialize(os, std::uint8_t(0xFF)); // null function
continue;
}
// Function name
function_h func = m_functions.at(id);
if (auto it = m_functionMap.find(func.id()); it != m_functionMap.end()) {
detail::serialize(os, it->second.first);
} else {
detail::serialize(os, "");
}
// Function signature
detail::serialize(os, func->signature().params.size());
for (std::uint32_t i = 0; i < func->signature().params.size(); ++i)
detail::serialize(os, func->signature().params[i].id());
// Function body/value
detail::serialize(os, std::uint8_t(func->type()));
switch (func->type()) {
case function_t::Normal: {
detail::serialize(os, func->position());
} break;
case function_t::Native: {
detail::serialize(os, func->native());
} break;
case function_t::Import: {
detail::serialize(os, func->imp().mod);
detail::serialize(os, func->imp().function);
} break;
}
}
detail::serialize(os, std::uint64_t(m_bytecode.size())); detail::serialize(os, std::uint64_t(m_bytecode.size()));
return os.write(reinterpret_cast<const char*>(m_bytecode.data()), static_cast<std::streamsize>(m_bytecode.size())); return os.write(reinterpret_cast<const char*>(m_bytecode.data()), static_cast<std::streamsize>(m_bytecode.size()));
} }
@@ -81,45 +93,6 @@ mod mod::load(std::istream& is) {
mod mod; mod mod;
function_id functionCount = 0;
detail::load(is, functionCount);
for (function_id id = 0; id < functionCount; ++id) {
std::string name;
detail::load(is, name);
std::uint32_t paramCount = 0;
detail::load(is, paramCount);
std::uint8_t type = 0;
detail::load(is, type);
if (type == 0xFF) continue;
switch ((function_t)type) {
case function_t::Normal: {
decltype(std::declval<function>().position()) offset = 0;
detail::load(is, offset);
if (name.empty())
mod.emplace_function_named(std::move(name), id, paramCount, offset).dispatch();
else
mod.emplace_function(id, paramCount, offset).dispatch();
} break;
case function_t::Native: {
std::string native;
detail::load(is, native);
if (name.empty())
mod.emplace_function_named(std::move(name), id, paramCount, std::move(native)).dispatch();
else
mod.emplace_function(id, paramCount, std::move(native)).dispatch();
} break;
case function_t::Import: {
std::string modName;
detail::load(is, modName);
function_id function = 0;
detail::load(is, function);
mod.emplace_function(id, std::move(modName), function).dispatch();
} break;
default: throw std::runtime_error("unknown function type");
}
}
type_id typeCount = 0; type_id typeCount = 0;
detail::load(is, typeCount); detail::load(is, typeCount);
for (type_id id = 0; id < typeCount; ++id) { for (type_id id = 0; id < typeCount; ++id) {
@@ -151,6 +124,54 @@ mod mod::load(std::istream& is) {
} }
} }
function_id functionCount = 0;
detail::load(is, functionCount);
for (function_id id = 0; id < functionCount; ++id) {
std::string name;
detail::load(is, name);
function_sig signature;
std::uint32_t paramCount = 0;
detail::load(is, paramCount);
signature.params.reserve(paramCount);
while (signature.params.size() < paramCount) {
thing_id param{ 0 };
detail::load(is, param);
signature.params.emplace_back(mod.type_at(param));
}
std::uint8_t type = 0;
detail::load(is, type);
if (type == 0xFF) continue;
switch ((function_t)type) {
case function_t::Normal: {
decltype(std::declval<function>().position()) offset = 0;
detail::load(is, offset);
if (name.empty())
mod.emplace_function(std::move(name), id, std::move(signature), offset).dispatch();
else
mod.emplace_function(id, std::move(signature), offset).dispatch();
} break;
case function_t::Native: {
std::string native;
detail::load(is, native);
if (name.empty())
mod.emplace_function(std::move(name), id, std::move(signature), std::move(native)).dispatch();
else
mod.emplace_function(id, std::move(signature), std::move(native)).dispatch();
} break;
case function_t::Import: {
std::string modName;
detail::load(is, modName);
function_id function = 0;
detail::load(is, function);
mod.emplace_function(id, std::move(modName), function).dispatch();
} break;
default: throw std::runtime_error("unknown function type");
}
}
std::uint64_t bytecodeLength = 0; std::uint64_t bytecodeLength = 0;
detail::load(is, bytecodeLength); detail::load(is, bytecodeLength);
mod.bytecode().resize(bytecodeLength); mod.bytecode().resize(bytecodeLength);