From 43540eda3c11dcf8a0b8b6d7d2d2234e92a1f408 Mon Sep 17 00:00:00 2001 From: alandefreitas Date: Sat, 21 Oct 2023 02:39:50 -0300 Subject: [PATCH] [FOLD] --- include/mrdox/Support/JavaScript.hpp | 125 +++++++++++++++++++++++++ src/lib/Support/JavaScript.cpp | 27 +++--- src/test/Support/Javascript.cpp | 135 ++++++++++++++++++++++++++- 3 files changed, 274 insertions(+), 13 deletions(-) diff --git a/include/mrdox/Support/JavaScript.hpp b/include/mrdox/Support/JavaScript.hpp index ff9b26ee9..39d450ec5 100644 --- a/include/mrdox/Support/JavaScript.hpp +++ b/include/mrdox/Support/JavaScript.hpp @@ -197,6 +197,23 @@ class Scope Expected script(std::string_view jsCode); + /** Compile and run a expression. + + This function compiles and executes + the specified JavaScript code. The script + can be used to execute commands or define + global variables in the parent context. + + It evaluates the ECMAScript source code and + converts any internal errors to @ref Error. + + @param jsCode The JavaScript code to execute. + + */ + MRDOX_DECL + Expected + eval(std::string_view jsCode); + /** Compile a script and push results to stack. Compile ECMAScript source code and return it @@ -273,6 +290,28 @@ class Scope //------------------------------------------------ /** An ECMAScript value. + + This class represents a value in the + JavaScript interpreter. + + A value is a variable that is defined + in a @ref Scope. It can be a primitive + type or an object. + + A @ref Value not associated with a + @ref Scope is undefined. + + The user is responsible for ensuring that + the lifetime of a @ref Value does not + exceed the lifetime of the @ref Scope + that created it. + + A value can be converted to a DOM value + using the @ref getDom function. + + @see Scope + @see Type + */ class Value { @@ -285,22 +324,103 @@ class Value Value(int, Scope&) noexcept; public: + /** Destructor + + If the value is associated with a + @ref Scope and it is on top of the + stack, it is popped. Also, if + there are no other Value references + to the @ref Scope, all variables + defined in that scope are popped + via @ref Scope::reset. + + */ MRDOX_DECL ~Value(); + + /** Constructor + + Construct a value that is not associated + with a @ref Scope. + + The value is undefined. + + */ MRDOX_DECL Value() noexcept; + + /** Constructor + + The function pushes a duplicate of + value to the stack and associates + the new value the top of the stack. + */ MRDOX_DECL Value(Value const&); + + /** Constructor + + The function associates the + existing value with this object. + */ MRDOX_DECL Value(Value&&) noexcept; + + /** Copy assignment. + + @copydetails Value(Value const&) + + */ MRDOX_DECL Value& operator=(Value const&); + + /** Move assignment. + + @copydetails Value(Value&&) + + */ MRDOX_DECL Value& operator=(Value&&) noexcept; + /** Return the type of the value. + + This function returns the JavaScript + type of the value. + + The type can represent a primitive + type (such as boolean, number, + and string) or an object. + + When the type is an object, the + return type also classifies the + object as an array or function. + + An array is an object with the + internal ECMAScript class `Array` + or a Proxy wrapping an `Array`. + + A function is an object with the + internal ECMAScript class `Function`. + + */ MRDOX_DECL Type type() const noexcept; + /// Check if the value is undefined. bool isUndefined() const noexcept; + + /// Check if the value is null. bool isNull() const noexcept; + + /// Check if the value is a boolean. bool isBoolean() const noexcept; + + /// Check if the value is a number. bool isNumber() const noexcept; + + /// Check if the value is a string. bool isString() const noexcept; + + /// Check if the value is an array. bool isArray() const noexcept; + + /// Check if the value is an object. bool isObject() const noexcept; + + /// Check if the value is a function. bool isFunction() const noexcept; std::string getString() const; @@ -408,6 +528,11 @@ inline bool Value::isObject() const noexcept return type() == Type::object; } +inline bool Value::isArray() const noexcept +{ + return type() == Type::array; +} + inline bool Value::isFunction() const noexcept { return type() == Type::function; diff --git a/src/lib/Support/JavaScript.cpp b/src/lib/Support/JavaScript.cpp index 14d53069e..894adc8d9 100644 --- a/src/lib/Support/JavaScript.cpp +++ b/src/lib/Support/JavaScript.cpp @@ -253,6 +253,21 @@ script( return {}; } +Expected +Scope:: +eval( + std::string_view jsCode) +{ + Access A(*this); + duk_int_t failed = duk_peval_lstring( + A, jsCode.data(), jsCode.size()); + if (failed) + { + return Unexpected(dukM_popError(*this)); + } + return Access::construct(duk_get_top_index(A), *this); +} + Expected Scope:: compile_script( @@ -894,7 +909,7 @@ Value:: Access A(*scope_); if(idx_ == duk_get_top(A) - 1) duk_pop(A); - A.release(*scope_); + Access::release(*scope_); } // construct an empty value @@ -979,16 +994,6 @@ type() const noexcept } } -bool -Value:: -isArray() const noexcept -{ - Access A(*scope_); - return - isObject() && - duk_is_array(A, idx_); -} - std::string Value:: getString() const diff --git a/src/test/Support/Javascript.cpp b/src/test/Support/Javascript.cpp index 135e10fbc..97c6d75ee 100644 --- a/src/test/Support/Javascript.cpp +++ b/src/test/Support/Javascript.cpp @@ -39,8 +39,10 @@ struct Path_test // script { Scope scope(ctx); - scope.script("var x = 1;"); - scope.script("print(x);"); + Expected r = scope.script("var x = 1;"); + BOOST_TEST(r); + r = scope.script("print(x);"); + BOOST_TEST(!r); auto exp = scope.getGlobal("x"); BOOST_TEST(exp); js::Value x = *exp; @@ -48,6 +50,16 @@ struct Path_test BOOST_TEST(x.getDom() == 1); } + // eval + { + Scope scope(ctx); + Expected r = scope.eval("1 + 2 + 3"); + BOOST_TEST(r); + Value v = r.value(); + BOOST_TEST(v.isNumber()); + BOOST_TEST(v.getDom() == 6); + } + // compile_script { // last expression as implicit return value @@ -166,7 +178,126 @@ struct Path_test void test_value() { + // Value() + { + Context ctx; + Scope scope(ctx); + Value v; + BOOST_TEST(v.isUndefined()); + } + + // Value(Value const&) + { + Context ctx; + Scope scope(ctx); + Value v1; + Value v2(v1); + BOOST_TEST(v2.isUndefined()); + } + + // Value(Value&&) + { + Context ctx; + Scope scope(ctx); + Value v1; + Value v2(std::move(v1)); + BOOST_TEST(v2.isUndefined()); + } + + // operator=(Value const&) + { + Context ctx; + Scope scope(ctx); + Value v1; + Value v2; + v2 = v1; + BOOST_TEST(v2.isUndefined()); + } + + // operator=(Value&&) + { + Context ctx; + Scope scope(ctx); + Value v1; + Value v2; + v2 = std::move(v1); + BOOST_TEST(v2.isUndefined()); + } + + // type() + { + // undefined + { + Context context; + Scope scope(context); + Value x = scope.eval("undefined").value(); + BOOST_TEST(x.isUndefined()); + BOOST_TEST(x.type() == Type::undefined); + } + + // null + { + Context context; + Scope scope(context); + Value x = scope.eval("null").value(); + BOOST_TEST(x.isNull()); + BOOST_TEST(x.type() == Type::null); + } + + // boolean + { + Context context; + Scope scope(context); + Value x = scope.eval("true").value(); + BOOST_TEST(x.isBoolean()); + BOOST_TEST(x.type() == Type::boolean); + } + + // number + { + Context context; + Scope scope(context); + Value x = scope.eval("1 + 2 + 3").value(); + BOOST_TEST(x.isNumber()); + BOOST_TEST(x.type() == Type::number); + } + // string + { + Context context; + Scope scope(context); + Value x = scope.eval("'hello world'").value(); + BOOST_TEST(x.isString()); + BOOST_TEST(x.type() == Type::string); + } + + // object + { + Context context; + Scope scope(context); + Value x = scope.eval("({ x: 1 })").value(); + BOOST_TEST(x.isObject()); + BOOST_TEST(x.type() == Type::object); + } + + // function + { + Context context; + Scope scope(context); + Value x = scope.eval("(function() { return 1; })").value(); + BOOST_TEST(x.isFunction()); + BOOST_TEST(x.type() == Type::function); + } + + // array + { + Context context; + Scope scope(context); + Value x = scope.eval("([1, 2, 3])").value(); + BOOST_TEST(x.isArray()); + BOOST_TEST(x.type() == Type::array); + } + } } void run()