2014-11-15 08:20:22 +01:00
/******************************************************************************
2015-03-21 11:02:01 +01:00
* Icinga 2 *
2016-01-12 08:29:59 +01:00
* Copyright ( C ) 2012 - 2016 Icinga Development Team ( https : //www.icinga.org/) *
2015-03-21 11:02:01 +01:00
* *
* This program 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 2 *
* of the License , or ( at your option ) any later version . *
* *
* This program 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 this program ; if not , write to the Free Software Foundation *
* Inc . , 51 Franklin St , Fifth Floor , Boston , MA 02110 - 1301 , USA . *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2014-11-15 08:20:22 +01:00
# ifndef VMOPS_H
# define VMOPS_H
# include "config/i2-config.hpp"
# include "config/expression.hpp"
# include "config/configitembuilder.hpp"
# include "config/applyrule.hpp"
# include "config/objectrule.hpp"
# include "base/debuginfo.hpp"
# include "base/array.hpp"
# include "base/dictionary.hpp"
2015-01-21 08:47:45 +01:00
# include "base/function.hpp"
2014-12-14 11:33:45 +01:00
# include "base/scriptglobal.hpp"
2014-12-18 15:11:57 +01:00
# include "base/exception.hpp"
2014-11-15 08:20:22 +01:00
# include "base/convert.hpp"
2014-11-20 22:53:59 +01:00
# include "base/objectlock.hpp"
2014-11-15 08:20:22 +01:00
# include <boost/foreach.hpp>
2014-12-11 21:12:34 +01:00
# include <boost/smart_ptr/make_shared.hpp>
2014-11-15 08:20:22 +01:00
# include <map>
# include <vector>
namespace icinga
{
class VMOps
{
public :
2014-12-12 15:33:02 +01:00
static inline Value Variable ( ScriptFrame & frame , const String & name , const DebugInfo & debugInfo = DebugInfo ( ) )
2014-11-15 08:20:22 +01:00
{
2015-03-31 11:45:38 +02:00
Value value ;
if ( frame . Locals & & frame . Locals - > Get ( name , & value ) )
return value ;
2014-12-12 15:19:23 +01:00
else if ( frame . Self . IsObject ( ) & & frame . Locals ! = static_cast < Object : : Ptr > ( frame . Self ) & & HasField ( frame . Self , name ) )
2015-10-26 10:41:00 +01:00
return GetField ( frame . Self , name , frame . Sandboxed , debugInfo ) ;
2014-11-22 12:21:28 +01:00
else
2014-12-14 11:33:45 +01:00
return ScriptGlobal : : Get ( name ) ;
2014-11-15 08:20:22 +01:00
}
2015-03-21 22:48:23 +01:00
static inline Value CopyConstructorCall ( const Type : : Ptr & type , const Value & value , const DebugInfo & debugInfo = DebugInfo ( ) )
{
if ( type - > GetName ( ) = = " String " )
return Convert : : ToString ( value ) ;
else if ( type - > GetName ( ) = = " Number " )
return Convert : : ToDouble ( value ) ;
else if ( type - > GetName ( ) = = " Boolean " )
return Convert : : ToBool ( value ) ;
2015-11-14 05:36:48 +01:00
else if ( ! value . IsEmpty ( ) & & ! type - > IsAssignableFrom ( value . GetReflectionType ( ) ) )
2015-03-21 22:48:23 +01:00
BOOST_THROW_EXCEPTION ( ScriptError ( " Invalid cast: Tried to cast object of type ' " + value . GetReflectionType ( ) - > GetName ( ) + " ' to type ' " + type - > GetName ( ) + " ' " , debugInfo ) ) ;
else
return value ;
}
static inline Value ConstructorCall ( const Type : : Ptr & type , const DebugInfo & debugInfo = DebugInfo ( ) )
{
if ( type - > GetName ( ) = = " String " )
return " " ;
else if ( type - > GetName ( ) = = " Number " )
return 0 ;
else if ( type - > GetName ( ) = = " Boolean " )
return false ;
else {
Object : : Ptr object = type - > Instantiate ( ) ;
if ( ! object )
BOOST_THROW_EXCEPTION ( ScriptError ( " Failed to instantiate object of type ' " + type - > GetName ( ) + " ' " , debugInfo ) ) ;
return object ;
}
}
2015-01-21 08:47:45 +01:00
static inline Value FunctionCall ( ScriptFrame & frame , const Value & self , const Function : : Ptr & func , const std : : vector < Value > & arguments )
2014-11-15 08:20:22 +01:00
{
2015-04-01 22:48:51 +02:00
ScriptFrame vframe ;
2015-09-22 11:24:00 +02:00
if ( ! self . IsEmpty ( ) | | self . IsString ( ) )
2015-04-01 22:48:51 +02:00
vframe . Self = self ;
2014-12-11 21:12:34 +01:00
2014-11-15 08:20:22 +01:00
return func - > Invoke ( arguments ) ;
}
2014-12-13 17:48:58 +01:00
static inline Value NewFunction ( ScriptFrame & frame , const std : : vector < String > & args ,
2014-11-22 12:21:28 +01:00
std : : map < String , Expression * > * closedVars , const boost : : shared_ptr < Expression > & expression )
2014-11-15 08:20:22 +01:00
{
2015-01-21 08:47:45 +01:00
return new Function ( boost : : bind ( & FunctionWrapper , _1 , args ,
2014-11-22 12:21:28 +01:00
EvaluateClosedVars ( frame , closedVars ) , expression ) ) ;
2014-11-15 08:20:22 +01:00
}
2014-12-12 15:33:02 +01:00
static inline Value NewApply ( ScriptFrame & frame , const String & type , const String & target , const String & name , const boost : : shared_ptr < Expression > & filter ,
2015-08-28 17:58:29 +02:00
const String & package , const String & fkvar , const String & fvvar , const boost : : shared_ptr < Expression > & fterm , std : : map < String , Expression * > * closedVars ,
2015-10-05 12:44:11 +02:00
bool ignoreOnError , const boost : : shared_ptr < Expression > & expression , const DebugInfo & debugInfo = DebugInfo ( ) )
2014-11-15 08:20:22 +01:00
{
2015-08-28 17:58:29 +02:00
ApplyRule : : AddRule ( type , target , name , expression , filter , package , fkvar ,
2015-10-05 12:44:11 +02:00
fvvar , fterm , ignoreOnError , debugInfo , EvaluateClosedVars ( frame , closedVars ) ) ;
2014-11-15 08:20:22 +01:00
return Empty ;
}
2014-12-12 15:33:02 +01:00
static inline Value NewObject ( ScriptFrame & frame , bool abstract , const String & type , const String & name , const boost : : shared_ptr < Expression > & filter ,
2015-10-05 12:44:11 +02:00
const String & zone , const String & package , bool ignoreOnError , std : : map < String , Expression * > * closedVars , const boost : : shared_ptr < Expression > & expression , const DebugInfo & debugInfo = DebugInfo ( ) )
2014-11-15 08:20:22 +01:00
{
ConfigItemBuilder : : Ptr item = new ConfigItemBuilder ( debugInfo ) ;
String checkName = name ;
if ( ! abstract ) {
Type : : Ptr ptype = Type : : GetByName ( type ) ;
NameComposer * nc = dynamic_cast < NameComposer * > ( ptype . get ( ) ) ;
if ( nc )
checkName = nc - > MakeName ( name , Dictionary : : Ptr ( ) ) ;
}
if ( ! checkName . IsEmpty ( ) ) {
2015-08-20 16:43:03 +02:00
ConfigItem : : Ptr oldItem = ConfigItem : : GetByTypeAndName ( type , checkName ) ;
2014-11-15 08:20:22 +01:00
if ( oldItem ) {
std : : ostringstream msgbuf ;
msgbuf < < " Object ' " < < name < < " ' of type ' " < < type < < " ' re-defined: " < < debugInfo < < " ; previous definition: " < < oldItem - > GetDebugInfo ( ) ;
2014-12-10 15:06:09 +01:00
BOOST_THROW_EXCEPTION ( ScriptError ( msgbuf . str ( ) , debugInfo ) ) ;
2014-11-15 08:20:22 +01:00
}
}
item - > SetType ( type ) ;
item - > SetName ( name ) ;
item - > AddExpression ( new OwnedExpression ( expression ) ) ;
item - > SetAbstract ( abstract ) ;
2014-11-22 12:21:28 +01:00
item - > SetScope ( EvaluateClosedVars ( frame , closedVars ) ) ;
2014-11-15 08:20:22 +01:00
item - > SetZone ( zone ) ;
2015-08-28 17:58:29 +02:00
item - > SetPackage ( package ) ;
2014-11-20 22:53:59 +01:00
item - > SetFilter ( filter ) ;
2015-10-05 12:44:11 +02:00
item - > SetIgnoreOnError ( ignoreOnError ) ;
2014-11-15 08:20:22 +01:00
item - > Compile ( ) - > Register ( ) ;
return Empty ;
}
2015-03-22 00:04:57 +01:00
static inline ExpressionResult For ( ScriptFrame & frame , const String & fkvar , const String & fvvar , const Value & value , Expression * expression , const DebugInfo & debugInfo = DebugInfo ( ) )
2014-11-15 08:20:22 +01:00
{
if ( value . IsObjectType < Array > ( ) ) {
if ( ! fvvar . IsEmpty ( ) )
2014-12-10 15:06:09 +01:00
BOOST_THROW_EXCEPTION ( ScriptError ( " Cannot use dictionary iterator for array. " , debugInfo ) ) ;
2014-11-15 08:20:22 +01:00
Array : : Ptr arr = value ;
2015-01-16 08:40:19 +01:00
for ( Array : : SizeType i = 0 ; i < arr - > GetLength ( ) ; i + + ) {
frame . Locals - > Set ( fkvar , arr - > Get ( i ) ) ;
2015-02-19 12:57:52 +01:00
ExpressionResult res = expression - > Evaluate ( frame ) ;
CHECK_RESULT_LOOP ( res ) ;
2014-12-12 06:54:38 +01:00
}
2014-11-22 12:21:28 +01:00
} else if ( value . IsObjectType < Dictionary > ( ) ) {
2014-11-15 08:20:22 +01:00
if ( fvvar . IsEmpty ( ) )
2014-12-10 15:06:09 +01:00
BOOST_THROW_EXCEPTION ( ScriptError ( " Cannot use array iterator for dictionary. " , debugInfo ) ) ;
2014-11-15 08:20:22 +01:00
Dictionary : : Ptr dict = value ;
2015-01-16 08:40:19 +01:00
std : : vector < String > keys ;
{
ObjectLock olock ( dict ) ;
BOOST_FOREACH ( const Dictionary : : Pair & kv , dict ) {
keys . push_back ( kv . first ) ;
}
}
2014-11-15 08:20:22 +01:00
2015-01-16 08:40:19 +01:00
BOOST_FOREACH ( const String & key , keys ) {
frame . Locals - > Set ( fkvar , key ) ;
frame . Locals - > Set ( fvvar , dict - > Get ( key ) ) ;
2015-02-19 12:57:52 +01:00
ExpressionResult res = expression - > Evaluate ( frame ) ;
CHECK_RESULT_LOOP ( res ) ;
2014-11-15 08:20:22 +01:00
}
2014-12-12 06:54:38 +01:00
} else
2014-12-11 15:07:21 +01:00
BOOST_THROW_EXCEPTION ( ScriptError ( " Invalid type in for expression: " + value . GetTypeName ( ) , debugInfo ) ) ;
2014-11-15 08:20:22 +01:00
return Empty ;
}
static inline bool HasField ( const Object : : Ptr & context , const String & field )
{
Dictionary : : Ptr dict = dynamic_pointer_cast < Dictionary > ( context ) ;
if ( dict )
return dict - > Contains ( field ) ;
else {
Type : : Ptr type = context - > GetReflectionType ( ) ;
if ( ! type )
return false ;
return type - > GetFieldId ( field ) ! = - 1 ;
}
}
2014-12-15 17:23:18 +01:00
static inline Value GetPrototypeField ( const Value & context , const String & field , bool not_found_error = true , const DebugInfo & debugInfo = DebugInfo ( ) )
2014-11-15 08:20:22 +01:00
{
2015-03-19 13:47:40 +01:00
Type : : Ptr ctype = context . GetReflectionType ( ) ;
Type : : Ptr type = ctype ;
2014-12-12 15:19:23 +01:00
do {
Object : : Ptr object = type - > GetPrototype ( ) ;
2014-12-16 18:15:47 +01:00
if ( object & & HasField ( object , field ) )
2015-10-26 10:41:00 +01:00
return GetField ( object , field , false , debugInfo ) ;
2014-12-12 06:54:38 +01:00
2014-12-12 15:19:23 +01:00
type = type - > GetBaseType ( ) ;
} while ( type ) ;
2014-12-15 17:23:18 +01:00
if ( not_found_error )
2015-03-19 13:47:40 +01:00
BOOST_THROW_EXCEPTION ( ScriptError ( " Invalid field access (for value of type ' " + ctype - > GetName ( ) + " '): ' " + field + " ' " , debugInfo ) ) ;
2014-12-15 17:23:18 +01:00
else
return Empty ;
2014-12-12 15:19:23 +01:00
}
2015-10-26 10:41:00 +01:00
static inline Value GetField ( const Value & context , const String & field , bool sandboxed = false , const DebugInfo & debugInfo = DebugInfo ( ) )
2014-12-12 15:19:23 +01:00
{
2015-09-22 11:24:00 +02:00
if ( context . IsEmpty ( ) & & ! context . IsString ( ) )
2014-12-15 17:23:18 +01:00
return Empty ;
2014-12-12 06:54:38 +01:00
if ( ! context . IsObject ( ) )
2014-12-15 17:23:18 +01:00
return GetPrototypeField ( context , field , true , debugInfo ) ;
2014-12-12 06:54:38 +01:00
Object : : Ptr object = context ;
Dictionary : : Ptr dict = dynamic_pointer_cast < Dictionary > ( object ) ;
2014-11-15 08:20:22 +01:00
2014-12-12 15:19:23 +01:00
if ( dict ) {
2015-03-31 11:45:38 +02:00
Value value ;
if ( dict - > Get ( field , & value ) )
return value ;
2014-12-12 15:19:23 +01:00
else
2014-12-15 17:23:18 +01:00
return GetPrototypeField ( context , field , false , debugInfo ) ;
2014-12-12 15:19:23 +01:00
}
2014-11-15 08:20:22 +01:00
2014-12-12 06:54:38 +01:00
Array : : Ptr arr = dynamic_pointer_cast < Array > ( object ) ;
2014-11-15 08:20:22 +01:00
2014-11-23 15:20:51 +01:00
if ( arr ) {
2014-12-12 15:19:23 +01:00
int index ;
try {
index = Convert : : ToLong ( field ) ;
} catch ( . . . ) {
2014-12-15 17:23:18 +01:00
return GetPrototypeField ( context , field , true , debugInfo ) ;
2014-12-12 15:19:23 +01:00
}
2015-01-12 14:19:35 +01:00
if ( index < 0 | | index > = arr - > GetLength ( ) )
BOOST_THROW_EXCEPTION ( ScriptError ( " Array index ' " + Convert : : ToString ( index ) + " ' is out of bounds. " , debugInfo ) ) ;
2014-11-23 15:20:51 +01:00
return arr - > Get ( index ) ;
}
2014-11-15 08:20:22 +01:00
2014-12-12 06:54:38 +01:00
Type : : Ptr type = object - > GetReflectionType ( ) ;
2014-11-15 08:20:22 +01:00
2014-11-23 15:20:51 +01:00
if ( ! type )
return Empty ;
int fid = type - > GetFieldId ( field ) ;
if ( fid = = - 1 )
2014-12-15 17:23:18 +01:00
return GetPrototypeField ( context , field , true , debugInfo ) ;
2014-11-23 15:20:51 +01:00
2015-10-26 10:41:00 +01:00
if ( sandboxed ) {
Field fieldInfo = type - > GetFieldInfo ( fid ) ;
if ( fieldInfo . Attributes & FANoUserView )
BOOST_THROW_EXCEPTION ( ScriptError ( " Accessing the field ' " + field + " ' for type ' " + type - > GetName ( ) + " ' is not allowed in sandbox mode. " ) ) ;
}
2014-12-12 06:54:38 +01:00
return object - > GetField ( fid ) ;
2014-11-15 08:20:22 +01:00
}
2014-12-10 15:06:09 +01:00
static inline void SetField ( const Object : : Ptr & context , const String & field , const Value & value , const DebugInfo & debugInfo = DebugInfo ( ) )
2014-11-15 08:20:22 +01:00
{
2015-07-30 08:28:07 +02:00
if ( ! context )
BOOST_THROW_EXCEPTION ( ScriptError ( " Cannot set field ' " + field + " ' on a value that is not an object. " , debugInfo ) ) ;
2014-11-15 08:20:22 +01:00
Dictionary : : Ptr dict = dynamic_pointer_cast < Dictionary > ( context ) ;
2014-11-23 15:20:51 +01:00
if ( dict ) {
2014-11-15 08:20:22 +01:00
dict - > Set ( field , value ) ;
2014-11-23 15:20:51 +01:00
return ;
}
2014-11-15 08:20:22 +01:00
2014-11-23 15:20:51 +01:00
Array : : Ptr arr = dynamic_pointer_cast < Array > ( context ) ;
2014-11-15 08:20:22 +01:00
2014-11-23 15:20:51 +01:00
if ( arr ) {
int index = Convert : : ToLong ( field ) ;
if ( index > = arr - > GetLength ( ) )
arr - > Resize ( index + 1 ) ;
arr - > Set ( index , value ) ;
return ;
}
2014-11-15 08:20:22 +01:00
2014-11-23 15:20:51 +01:00
Type : : Ptr type = context - > GetReflectionType ( ) ;
2014-11-15 08:20:22 +01:00
2014-11-23 15:20:51 +01:00
if ( ! type )
2014-12-10 15:06:09 +01:00
BOOST_THROW_EXCEPTION ( ScriptError ( " Cannot set field on object. " , debugInfo ) ) ;
2014-11-23 15:20:51 +01:00
int fid = type - > GetFieldId ( field ) ;
if ( fid = = - 1 )
2014-12-10 15:06:09 +01:00
BOOST_THROW_EXCEPTION ( ScriptError ( " Attribute ' " + field + " ' does not exist. " , debugInfo ) ) ;
2014-11-23 15:20:51 +01:00
try {
context - > SetField ( fid , value ) ;
} catch ( const boost : : bad_lexical_cast & ) {
2014-11-30 23:32:13 +01:00
Field fieldInfo = type - > GetFieldInfo ( fid ) ;
Type : : Ptr ftype = Type : : GetByName ( fieldInfo . TypeName ) ;
BOOST_THROW_EXCEPTION ( ScriptError ( " Attribute ' " + field + " ' cannot be set to value of type ' " + value . GetTypeName ( ) + " ', expected ' " + ftype - > GetName ( ) + " ' " , debugInfo ) ) ;
2014-11-23 15:20:51 +01:00
} catch ( const std : : bad_cast & ) {
2014-11-30 23:32:13 +01:00
Field fieldInfo = type - > GetFieldInfo ( fid ) ;
Type : : Ptr ftype = Type : : GetByName ( fieldInfo . TypeName ) ;
BOOST_THROW_EXCEPTION ( ScriptError ( " Attribute ' " + field + " ' cannot be set to value of type ' " + value . GetTypeName ( ) + " ', expected ' " + ftype - > GetName ( ) + " ' " , debugInfo ) ) ;
2014-11-15 08:20:22 +01:00
}
}
private :
static inline Value FunctionWrapper ( const std : : vector < Value > & arguments ,
2014-11-22 12:21:28 +01:00
const std : : vector < String > & funcargs , const Dictionary : : Ptr & closedVars , const boost : : shared_ptr < Expression > & expr )
2014-11-15 08:20:22 +01:00
{
if ( arguments . size ( ) < funcargs . size ( ) )
2014-12-10 15:06:09 +01:00
BOOST_THROW_EXCEPTION ( std : : invalid_argument ( " Too few arguments for function " ) ) ;
2014-11-15 08:20:22 +01:00
2015-03-31 07:09:20 +02:00
ScriptFrame * frame = ScriptFrame : : GetCurrentFrame ( ) ;
2014-11-22 12:21:28 +01:00
if ( closedVars )
2015-03-31 07:09:20 +02:00
closedVars - > CopyTo ( frame - > Locals ) ;
2014-11-15 08:20:22 +01:00
for ( std : : vector < Value > : : size_type i = 0 ; i < std : : min ( arguments . size ( ) , funcargs . size ( ) ) ; i + + )
2015-03-31 07:09:20 +02:00
frame - > Locals - > Set ( funcargs [ i ] , arguments [ i ] ) ;
2014-11-15 08:20:22 +01:00
2015-03-31 07:09:20 +02:00
return expr - > Evaluate ( * frame ) ;
2014-11-15 08:20:22 +01:00
}
2014-12-12 15:33:02 +01:00
static inline Dictionary : : Ptr EvaluateClosedVars ( ScriptFrame & frame , std : : map < String , Expression * > * closedVars )
2014-11-22 12:21:28 +01:00
{
Dictionary : : Ptr locals ;
if ( closedVars ) {
locals = new Dictionary ( ) ;
typedef std : : pair < String , Expression * > ClosedVar ;
BOOST_FOREACH ( const ClosedVar & cvar , * closedVars ) {
locals - > Set ( cvar . first , cvar . second - > Evaluate ( frame ) ) ;
}
}
return locals ;
}
2014-11-15 08:20:22 +01:00
} ;
}
# endif /* VMOPS_H */