2017-05-03 08:30:01 +00:00
/*
This file is part of solidity .
solidity is free software : you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation , either version 3 of the License , or
( at your option ) any later version .
solidity is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with solidity . If not , see < http : //www.gnu.org/licenses/>.
*/
/**
* @ date 2017
2018-06-12 18:55:14 +00:00
* Unit tests for parsing Yul .
2017-05-03 08:30:01 +00:00
*/
2018-03-14 11:04:04 +00:00
# include <test/Options.h>
2017-05-03 08:30:01 +00:00
2017-05-24 16:34:19 +00:00
# include <test/libsolidity/ErrorCheck.h>
2018-12-04 10:23:28 +00:00
# include <test/libyul/Common.h>
2017-05-24 16:34:19 +00:00
2018-11-23 10:18:57 +00:00
# include <libyul/AsmParser.h>
# include <libyul/AsmAnalysis.h>
# include <libyul/AsmAnalysisInfo.h>
2018-12-04 10:23:28 +00:00
# include <libyul/Dialect.h>
2018-11-14 13:59:30 +00:00
# include <liblangutil/Scanner.h>
# include <liblangutil/ErrorReporter.h>
2017-05-03 08:30:01 +00:00
# include <boost/optional.hpp>
# include <boost/algorithm/string/replace.hpp>
# include <string>
# include <memory>
using namespace std ;
2018-11-21 11:42:34 +00:00
using namespace dev ;
2018-11-14 16:11:55 +00:00
using namespace langutil ;
2017-05-03 08:30:01 +00:00
2018-11-21 11:42:34 +00:00
namespace yul
2017-05-03 08:30:01 +00:00
{
namespace test
{
namespace
{
2018-12-06 23:56:16 +00:00
bool parse ( string const & _source , std : : shared_ptr < Dialect > _dialect , ErrorReporter & errorReporter )
2017-05-03 08:30:01 +00:00
{
try
{
2018-11-28 15:13:36 +00:00
auto scanner = make_shared < Scanner > ( CharStream ( _source , " " ) ) ;
2018-12-03 17:06:07 +00:00
auto parserResult = yul : : Parser ( errorReporter , _dialect ) . parse ( scanner , false ) ;
2017-05-03 08:30:01 +00:00
if ( parserResult )
2017-05-25 00:28:47 +00:00
{
2018-11-21 11:42:34 +00:00
yul : : AsmAnalysisInfo analysisInfo ;
return ( yul : : AsmAnalyzer (
2018-02-23 10:42:53 +00:00
analysisInfo ,
errorReporter ,
2018-02-15 14:18:09 +00:00
boost : : none ,
2018-12-03 17:06:07 +00:00
_dialect
2018-02-23 10:42:53 +00:00
) ) . analyze ( * parserResult ) ;
2017-05-25 00:28:47 +00:00
}
2017-05-03 08:30:01 +00:00
}
catch ( FatalError const & )
{
BOOST_FAIL ( " Fatal error leaked. " ) ;
}
return false ;
}
2018-12-06 23:56:16 +00:00
boost : : optional < Error > parseAndReturnFirstError ( string const & _source , shared_ptr < Dialect > _dialect , bool _allowWarnings = true )
2017-05-03 08:30:01 +00:00
{
ErrorList errors ;
2017-05-11 13:26:35 +00:00
ErrorReporter errorReporter ( errors ) ;
2018-12-03 17:06:07 +00:00
if ( ! parse ( _source , _dialect , errorReporter ) )
2017-05-03 08:30:01 +00:00
{
2018-12-04 10:23:58 +00:00
BOOST_REQUIRE ( ! errors . empty ( ) ) ;
BOOST_CHECK_EQUAL ( errors . size ( ) , 1 ) ;
2017-05-03 08:30:01 +00:00
return * errors . front ( ) ;
}
else
{
// If success is true, there might still be an error in the assembly stage.
if ( _allowWarnings & & Error : : containsOnlyWarnings ( errors ) )
return { } ;
else if ( ! errors . empty ( ) )
{
if ( ! _allowWarnings )
BOOST_CHECK_EQUAL ( errors . size ( ) , 1 ) ;
return * errors . front ( ) ;
}
}
return { } ;
}
2018-12-06 23:56:16 +00:00
bool successParse ( std : : string const & _source , shared_ptr < Dialect > _dialect = Dialect : : yul ( ) , bool _allowWarnings = true )
2017-05-03 08:30:01 +00:00
{
2018-12-03 17:06:07 +00:00
return ! parseAndReturnFirstError ( _source , _dialect , _allowWarnings ) ;
2017-05-03 08:30:01 +00:00
}
2018-12-06 23:56:16 +00:00
Error expectError ( std : : string const & _source , shared_ptr < Dialect > _dialect = Dialect : : yul ( ) , bool _allowWarnings = false )
2017-05-03 08:30:01 +00:00
{
2018-12-03 17:06:07 +00:00
auto error = parseAndReturnFirstError ( _source , _dialect , _allowWarnings ) ;
2017-05-03 08:30:01 +00:00
BOOST_REQUIRE ( error ) ;
return * error ;
}
}
2018-12-03 17:06:07 +00:00
# define CHECK_ERROR_DIALECT(text, typ, substring, dialect) \
2017-05-03 08:30:01 +00:00
do \
{ \
2018-12-03 17:06:07 +00:00
Error err = expectError ( ( text ) , dialect , false ) ; \
2017-05-03 08:30:01 +00:00
BOOST_CHECK ( err . type ( ) = = ( Error : : Type : : typ ) ) ; \
2018-11-21 11:42:34 +00:00
BOOST_CHECK ( dev : : solidity : : searchErrorMessage ( err , ( substring ) ) ) ; \
2017-05-03 08:30:01 +00:00
} while ( 0 )
2018-12-03 17:06:07 +00:00
# define CHECK_ERROR(text, typ, substring) CHECK_ERROR_DIALECT(text, typ, substring, Dialect::yul())
2018-06-14 22:29:45 +00:00
BOOST_AUTO_TEST_SUITE ( YulParser )
2017-05-03 08:30:01 +00:00
BOOST_AUTO_TEST_CASE ( smoke_test )
{
BOOST_CHECK ( successParse ( " { } " ) ) ;
}
BOOST_AUTO_TEST_CASE ( vardecl )
{
2017-05-05 13:51:36 +00:00
BOOST_CHECK ( successParse ( " { let x:u256 := 7:u256 } " ) ) ;
2017-05-03 08:30:01 +00:00
}
2017-05-17 12:20:24 +00:00
BOOST_AUTO_TEST_CASE ( vardecl_bool )
{
BOOST_CHECK ( successParse ( " { let x:bool := true:bool } " ) ) ;
BOOST_CHECK ( successParse ( " { let x:bool := false:bool } " ) ) ;
}
2017-05-05 15:46:26 +00:00
BOOST_AUTO_TEST_CASE ( vardecl_empty )
{
2017-07-03 10:01:07 +00:00
BOOST_CHECK ( successParse ( " { let x:u256 } " ) ) ;
2017-05-05 15:46:26 +00:00
}
2017-05-03 08:30:01 +00:00
BOOST_AUTO_TEST_CASE ( assignment )
{
2017-05-05 13:51:36 +00:00
BOOST_CHECK ( successParse ( " { let x:u256 := 2:u256 let y:u256 := x } " ) ) ;
2017-05-03 08:30:01 +00:00
}
BOOST_AUTO_TEST_CASE ( vardecl_complex )
{
2017-05-25 00:28:47 +00:00
BOOST_CHECK ( successParse ( " { function add(a:u256, b:u256) -> c:u256 {} let y:u256 := 2:u256 let x:u256 := add(7:u256, add(6:u256, y)) } " ) ) ;
2017-05-03 08:30:01 +00:00
}
BOOST_AUTO_TEST_CASE ( blocks )
{
2017-05-05 13:51:36 +00:00
BOOST_CHECK ( successParse ( " { let x:u256 := 7:u256 { let y:u256 := 3:u256 } { let z:u256 := 2:u256 } } " ) ) ;
2017-05-03 08:30:01 +00:00
}
BOOST_AUTO_TEST_CASE ( function_definitions )
{
2017-05-05 13:51:36 +00:00
BOOST_CHECK ( successParse ( " { function f() { } function g(a:u256) -> x:u256 { } } " ) ) ;
2017-05-03 08:30:01 +00:00
}
BOOST_AUTO_TEST_CASE ( function_definitions_multiple_args )
{
2017-05-05 13:51:36 +00:00
BOOST_CHECK ( successParse ( " { function f(a:u256, d:u256) { } function g(a:u256, d:u256) -> x:u256, y:u256 { } } " ) ) ;
2017-05-03 08:30:01 +00:00
}
BOOST_AUTO_TEST_CASE ( function_calls )
{
2017-05-25 00:28:47 +00:00
BOOST_CHECK ( successParse ( " { function f(a:u256) -> b:u256 {} function g(a:u256, b:u256, c:u256) {} function x() { g(1:u256, 2:u256, f(3:u256)) x() } } " ) ) ;
2017-05-06 14:49:22 +00:00
}
BOOST_AUTO_TEST_CASE ( tuple_assignment )
{
BOOST_CHECK ( successParse ( " { function f() -> a:u256, b:u256, c:u256 {} let x:u256, y:u256, z:u256 := f() } " ) ) ;
2017-05-03 08:30:01 +00:00
}
BOOST_AUTO_TEST_CASE ( label )
{
CHECK_ERROR ( " { label: } " , ParserError , " Labels are not supported. " ) ;
}
BOOST_AUTO_TEST_CASE ( instructions )
{
CHECK_ERROR ( " { pop } " , ParserError , " Call or assignment expected. " ) ;
}
BOOST_AUTO_TEST_CASE ( push )
{
2017-05-05 13:51:36 +00:00
CHECK_ERROR ( " { 0x42:u256 } " , ParserError , " Call or assignment expected. " ) ;
2017-05-03 08:30:01 +00:00
}
BOOST_AUTO_TEST_CASE ( assign_from_stack )
{
2017-05-05 13:51:36 +00:00
CHECK_ERROR ( " { =: x:u256 } " , ParserError , " Literal or identifier expected. " ) ;
2017-05-03 08:30:01 +00:00
}
BOOST_AUTO_TEST_CASE ( empty_call )
{
CHECK_ERROR ( " { () } " , ParserError , " Literal or identifier expected. " ) ;
}
2018-01-04 23:25:31 +00:00
BOOST_AUTO_TEST_CASE ( tokens_as_identifers )
{
BOOST_CHECK ( successParse ( " { let return:u256 := 1:u256 } " ) ) ;
BOOST_CHECK ( successParse ( " { let byte:u256 := 1:u256 } " ) ) ;
BOOST_CHECK ( successParse ( " { let address:u256 := 1:u256 } " ) ) ;
BOOST_CHECK ( successParse ( " { let bool:u256 := 1:u256 } " ) ) ;
}
2017-05-05 13:51:36 +00:00
BOOST_AUTO_TEST_CASE ( lacking_types )
{
2019-02-18 14:07:15 +00:00
CHECK_ERROR ( " { let x := 1:u256 } " , ParserError , " Expected ':' but got ':=' " ) ;
2018-05-02 18:49:36 +00:00
CHECK_ERROR ( " { let x:u256 := 1 } " , ParserError , " Expected ':' but got '}' " ) ;
CHECK_ERROR ( " { function f(a) {} } " , ParserError , " Expected ':' but got ')' " ) ;
CHECK_ERROR ( " { function f(a:u256) -> b {} } " , ParserError , " Expected ':' but got '{' " ) ;
2017-05-05 13:51:36 +00:00
}
2017-05-24 23:27:48 +00:00
BOOST_AUTO_TEST_CASE ( invalid_types )
{
/// testing invalid literal
/// NOTE: these will need to change when types are compared
2017-05-26 19:42:17 +00:00
CHECK_ERROR ( " { let x:bool := 1:invalid } " , TypeError , " \" invalid \" is not a valid type (user defined types are not yet supported). " ) ;
2017-05-24 23:27:48 +00:00
/// testing invalid variable declaration
2017-05-26 19:42:17 +00:00
CHECK_ERROR ( " { let x:invalid := 1:bool } " , TypeError , " \" invalid \" is not a valid type (user defined types are not yet supported). " ) ;
CHECK_ERROR ( " { function f(a:invalid) {} } " , TypeError , " \" invalid \" is not a valid type (user defined types are not yet supported). " ) ;
2017-05-24 23:27:48 +00:00
}
2017-08-21 10:08:29 +00:00
BOOST_AUTO_TEST_CASE ( number_literals )
{
BOOST_CHECK ( successParse ( " { let x:u256 := 1:u256 } " ) ) ;
CHECK_ERROR ( " { let x:u256 := .1:u256 } " , ParserError , " Invalid number literal. " ) ;
CHECK_ERROR ( " { let x:u256 := 1e5:u256 } " , ParserError , " Invalid number literal. " ) ;
CHECK_ERROR ( " { let x:u256 := 67.235:u256 } " , ParserError , " Invalid number literal. " ) ;
2018-01-04 23:25:41 +00:00
CHECK_ERROR ( " { let x:u256 := 0x1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff:u256 } " , TypeError , " Number literal too large (> 256 bits) " ) ;
2017-08-21 10:08:29 +00:00
}
2017-05-24 23:27:48 +00:00
BOOST_AUTO_TEST_CASE ( builtin_types )
{
BOOST_CHECK ( successParse ( " { let x:bool := true:bool } " ) ) ;
BOOST_CHECK ( successParse ( " { let x:u8 := 1:u8 } " ) ) ;
BOOST_CHECK ( successParse ( " { let x:s8 := 1:u8 } " ) ) ;
BOOST_CHECK ( successParse ( " { let x:u32 := 1:u32 } " ) ) ;
BOOST_CHECK ( successParse ( " { let x:s32 := 1:s32 } " ) ) ;
BOOST_CHECK ( successParse ( " { let x:u64 := 1:u64 } " ) ) ;
BOOST_CHECK ( successParse ( " { let x:s64 := 1:s64 } " ) ) ;
BOOST_CHECK ( successParse ( " { let x:u128 := 1:u128 } " ) ) ;
BOOST_CHECK ( successParse ( " { let x:s128 := 1:s128 } " ) ) ;
BOOST_CHECK ( successParse ( " { let x:u256 := 1:u256 } " ) ) ;
BOOST_CHECK ( successParse ( " { let x:s256 := 1:s256 } " ) ) ;
}
2017-08-21 10:29:04 +00:00
BOOST_AUTO_TEST_CASE ( recursion_depth )
{
string input ;
for ( size_t i = 0 ; i < 20000 ; i + + )
input + = " { " ;
input + = " let x:u256 := 0:u256 " ;
for ( size_t i = 0 ; i < 20000 ; i + + )
input + = " } " ;
2017-08-21 10:33:29 +00:00
CHECK_ERROR ( input , ParserError , " recursion " ) ;
2017-08-21 10:29:04 +00:00
}
2017-05-19 16:06:26 +00:00
BOOST_AUTO_TEST_CASE ( multiple_assignment )
{
2019-02-18 14:07:15 +00:00
CHECK_ERROR ( " { let x:u256 function f() -> a:u256, b:u256 {} 123:u256, x := f() } " , ParserError , " Variable name must precede \" , \" in multiple assignment. " ) ;
CHECK_ERROR ( " { let x:u256 function f() -> a:u256, b:u256 {} x, 123:u256 := f() } " , ParserError , " Variable name must precede \" := \" in assignment. " ) ;
2017-05-19 16:06:26 +00:00
/// NOTE: Travis hiccups if not having a variable
char const * text = R " (
{
function f ( a : u256 ) - > r1 : u256 , r2 : u256 {
r1 : = a
r2 : = 7 : u256
}
let x : u256 : = 9 : u256
let y : u256 : = 2 : u256
x , y : = f ( x )
}
) " ;
BOOST_CHECK ( successParse ( text ) ) ;
}
2017-11-22 15:24:59 +00:00
BOOST_AUTO_TEST_CASE ( if_statement )
{
2017-11-23 17:52:04 +00:00
BOOST_CHECK ( successParse ( " { if true:bool {} } " ) ) ;
BOOST_CHECK ( successParse ( " { if false:bool { let x:u256 := 3:u256 } } " ) ) ;
BOOST_CHECK ( successParse ( " { function f() -> x:bool {} if f() { let b:bool := f() } } " ) ) ;
2017-11-22 15:24:59 +00:00
}
2019-03-04 14:38:05 +00:00
BOOST_AUTO_TEST_CASE ( for_statement )
{
auto dialect = EVMDialect : : strictAssemblyForEVMObjects ( EVMVersion : : constantinople ( ) ) ;
BOOST_CHECK ( successParse ( " { for {let i := 0} iszero(eq(i, 10)) {i := add(i, 1)} {} } " , dialect ) ) ;
}
BOOST_AUTO_TEST_CASE ( for_statement_break )
{
auto dialect = EVMDialect : : strictAssemblyForEVMObjects ( EVMVersion : : constantinople ( ) ) ;
BOOST_CHECK ( successParse ( " { for {let i := 0} iszero(eq(i, 10)) {i := add(i, 1)} {break} } " , dialect ) ) ;
}
BOOST_AUTO_TEST_CASE ( for_statement_break_init )
{
auto dialect = EVMDialect : : strictAssemblyForEVMObjects ( EVMVersion : : constantinople ( ) ) ;
CHECK_ERROR_DIALECT (
" { for {let i := 0 break} iszero(eq(i, 10)) {i := add(i, 1)} {} } " ,
SyntaxError ,
" Keyword break outside for-loop body is not allowed. " ,
dialect ) ;
}
BOOST_AUTO_TEST_CASE ( for_statement_break_post )
{
auto dialect = EVMDialect : : strictAssemblyForEVMObjects ( EVMVersion : : constantinople ( ) ) ;
CHECK_ERROR_DIALECT (
" { for {let i := 0} iszero(eq(i, 10)) {i := add(i, 1) break} {} } " ,
SyntaxError ,
" Keyword break outside for-loop body is not allowed. " ,
dialect ) ;
}
BOOST_AUTO_TEST_CASE ( for_statement_nested_break )
{
auto dialect = EVMDialect : : strictAssemblyForEVMObjects ( EVMVersion : : constantinople ( ) ) ;
CHECK_ERROR_DIALECT (
" { for {let i := 0} iszero(eq(i, 10)) {} { function f() { break } } } " ,
SyntaxError ,
" Keyword break outside for-loop body is not allowed. " ,
dialect ) ;
}
BOOST_AUTO_TEST_CASE ( for_statement_continue )
{
auto dialect = EVMDialect : : strictAssemblyForEVMObjects ( EVMVersion : : constantinople ( ) ) ;
BOOST_CHECK ( successParse ( " { for {let i := 0} iszero(eq(i, 10)) {i := add(i, 1)} {continue} } " , dialect ) ) ;
}
BOOST_AUTO_TEST_CASE ( for_statement_continue_fail_init )
{
auto dialect = EVMDialect : : strictAssemblyForEVMObjects ( EVMVersion : : constantinople ( ) ) ;
CHECK_ERROR_DIALECT (
" { for {let i := 0 continue} iszero(eq(i, 10)) {i := add(i, 1)} {} } " ,
SyntaxError ,
" Keyword continue outside for-loop body is not allowed. " ,
dialect ) ;
}
BOOST_AUTO_TEST_CASE ( for_statement_continue_fail_post )
{
auto dialect = EVMDialect : : strictAssemblyForEVMObjects ( EVMVersion : : constantinople ( ) ) ;
CHECK_ERROR_DIALECT (
" { for {let i := 0} iszero(eq(i, 10)) {i := add(i, 1) continue} {} } " ,
SyntaxError ,
" Keyword continue outside for-loop body is not allowed. " ,
dialect ) ;
}
2017-11-22 15:24:59 +00:00
BOOST_AUTO_TEST_CASE ( if_statement_invalid )
{
CHECK_ERROR ( " { if let x:u256 {} } " , ParserError , " Literal or identifier expected. " ) ;
2018-05-02 18:49:36 +00:00
CHECK_ERROR ( " { if true:bool let x:u256 := 3:u256 } " , ParserError , " Expected '{' but got reserved keyword 'let' " ) ;
2017-11-23 17:52:04 +00:00
// TODO change this to an error once we check types.
BOOST_CHECK ( successParse ( " { if 42:u256 { } } " ) ) ;
2017-11-22 15:24:59 +00:00
}
2019-01-15 12:40:10 +00:00
BOOST_AUTO_TEST_CASE ( switch_case_types )
{
CHECK_ERROR ( " { switch 0:u256 case 0:u256 {} case 1:u32 {} } " , TypeError , " Switch cases have non-matching types. " ) ;
// The following should be an error in the future, but this is not yet detected.
BOOST_CHECK ( successParse ( " { switch 0:u256 case 0:u32 {} case 1:u32 {} } " ) ) ;
}
BOOST_AUTO_TEST_CASE ( switch_duplicate_case )
{
CHECK_ERROR ( " { switch 0:u256 case 0:u256 {} case 0x0:u256 {} } " , DeclarationError , " Duplicate case defined. " ) ;
BOOST_CHECK ( successParse ( " { switch 0:u256 case 42:u256 {} case 0x42:u256 {} } " ) ) ;
}
2018-12-03 17:15:32 +00:00
BOOST_AUTO_TEST_CASE ( builtins_parser )
{
2018-12-06 23:56:16 +00:00
struct SimpleDialect : public Dialect
2018-12-03 17:15:32 +00:00
{
2018-12-06 23:56:16 +00:00
SimpleDialect ( ) : Dialect ( AsmFlavour : : Strict ) { }
BuiltinFunction const * builtin ( YulString _name ) const override
2018-12-03 17:15:32 +00:00
{
2018-12-10 03:25:51 +00:00
return _name = = " builtin " _yulstring ? & f : nullptr ;
2018-12-03 17:15:32 +00:00
}
BuiltinFunction f ;
} ;
2018-12-06 23:56:16 +00:00
shared_ptr < Dialect > dialect = make_shared < SimpleDialect > ( ) ;
2018-12-03 17:15:32 +00:00
CHECK_ERROR_DIALECT ( " { let builtin := 6 } " , ParserError , " Cannot use builtin function name \" builtin \" as identifier name. " , dialect ) ;
CHECK_ERROR_DIALECT ( " { function builtin() {} } " , ParserError , " Cannot use builtin function name \" builtin \" as identifier name. " , dialect ) ;
CHECK_ERROR_DIALECT ( " { builtin := 6 } " , ParserError , " Cannot assign to builtin function \" builtin \" . " , dialect ) ;
2019-02-18 14:07:15 +00:00
CHECK_ERROR_DIALECT ( " { function g() -> a,b {} builtin, builtin2 := g() } " , ParserError , " Cannot assign to builtin function \" builtin \" . " , dialect ) ;
2018-12-03 17:15:32 +00:00
}
2018-12-04 10:23:28 +00:00
BOOST_AUTO_TEST_CASE ( builtins_analysis )
{
2018-12-06 23:56:16 +00:00
struct SimpleDialect : public Dialect
2018-12-04 10:23:28 +00:00
{
2018-12-06 23:56:16 +00:00
SimpleDialect ( ) : Dialect ( AsmFlavour : : Strict ) { }
BuiltinFunction const * builtin ( YulString _name ) const override
2018-12-04 10:23:28 +00:00
{
2018-12-10 03:25:51 +00:00
return _name = = " builtin " _yulstring ? & f : nullptr ;
2018-12-04 10:23:28 +00:00
}
2018-12-20 16:22:17 +00:00
BuiltinFunction f { " builtin " _yulstring , vector < Type > ( 2 ) , vector < Type > ( 3 ) , false , false } ;
2018-12-04 10:23:28 +00:00
} ;
2018-12-06 23:56:16 +00:00
shared_ptr < Dialect > dialect = make_shared < SimpleDialect > ( ) ;
2018-12-04 10:23:28 +00:00
BOOST_CHECK ( successParse ( " { let a, b, c := builtin(1, 2) } " , dialect ) ) ;
CHECK_ERROR_DIALECT ( " { let a, b, c := builtin(1) } " , TypeError , " Function expects 2 arguments but got 1 " , dialect ) ;
CHECK_ERROR_DIALECT ( " { let a, b := builtin(1, 2) } " , DeclarationError , " Variable count mismatch: 2 variables and 3 values. " , dialect ) ;
}
2017-05-03 08:30:01 +00:00
BOOST_AUTO_TEST_SUITE_END ( )
}
} // end namespaces