2016-03-01 15:47:10 +00:00
/*
* * thingdef_expression . cpp
* *
* * Expression evaluation
* *
* * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* * Copyright 2008 Christoph Oelckers
* * All rights reserved .
* *
* * Redistribution and use in source and binary forms , with or without
* * modification , are permitted provided that the following conditions
* * are met :
* *
* * 1. Redistributions of source code must retain the above copyright
* * notice , this list of conditions and the following disclaimer .
* * 2. Redistributions in binary form must reproduce the above copyright
* * notice , this list of conditions and the following disclaimer in the
* * documentation and / or other materials provided with the distribution .
* * 3. The name of the author may not be used to endorse or promote products
* * derived from this software without specific prior written permission .
* * 4. When not used as part of ZDoom or a ZDoom derivative , this code will be
* * covered by 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 SOFTWARE IS PROVIDED BY THE AUTHOR ` ` AS IS ' ' AND ANY EXPRESS OR
* * IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE IMPLIED WARRANTIES
* * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED .
* * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT , INDIRECT ,
* * INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL DAMAGES ( INCLUDING , BUT
* * NOT LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ; LOSS OF USE ,
* * DATA , OR PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY
* * THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY , OR TORT
* * ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF
* * THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
* * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* *
*/
# include <stdlib.h>
# include "actor.h"
# include "sc_man.h"
# include "tarray.h"
# include "templates.h"
# include "cmdlib.h"
# include "i_system.h"
# include "m_random.h"
# include "a_pickups.h"
# include "thingdef.h"
# include "p_lnspec.h"
# include "doomstat.h"
2016-10-15 15:40:27 +00:00
# include "codegen.h"
2016-03-01 15:47:10 +00:00
# include "m_fixed.h"
# include "vmbuilder.h"
# include "v_text.h"
2016-03-11 14:45:47 +00:00
# include "math/cmath.h"
2016-03-01 15:47:10 +00:00
struct FLOP
{
ENamedName Name ;
int Flop ;
double ( * Evaluate ) ( double ) ;
} ;
// Decorate operates on degrees, so the evaluate functions need to convert
// degrees to radians for those that work with angles.
static const FLOP FxFlops [ ] =
{
2016-03-11 14:45:47 +00:00
{ NAME_Exp , FLOP_EXP , [ ] ( double v ) { return g_exp ( v ) ; } } ,
{ NAME_Log , FLOP_LOG , [ ] ( double v ) { return g_log ( v ) ; } } ,
{ NAME_Log10 , FLOP_LOG10 , [ ] ( double v ) { return g_log10 ( v ) ; } } ,
{ NAME_Sqrt , FLOP_SQRT , [ ] ( double v ) { return g_sqrt ( v ) ; } } ,
2016-03-01 15:47:10 +00:00
{ NAME_Ceil , FLOP_CEIL , [ ] ( double v ) { return ceil ( v ) ; } } ,
{ NAME_Floor , FLOP_FLOOR , [ ] ( double v ) { return floor ( v ) ; } } ,
2016-03-11 14:45:47 +00:00
{ NAME_ACos , FLOP_ACOS_DEG , [ ] ( double v ) { return g_acos ( v ) * ( 180.0 / M_PI ) ; } } ,
{ NAME_ASin , FLOP_ASIN_DEG , [ ] ( double v ) { return g_asin ( v ) * ( 180.0 / M_PI ) ; } } ,
{ NAME_ATan , FLOP_ATAN_DEG , [ ] ( double v ) { return g_atan ( v ) * ( 180.0 / M_PI ) ; } } ,
{ NAME_Cos , FLOP_COS_DEG , [ ] ( double v ) { return g_cosdeg ( v ) ; } } ,
{ NAME_Sin , FLOP_SIN_DEG , [ ] ( double v ) { return g_sindeg ( v ) ; } } ,
{ NAME_Tan , FLOP_TAN_DEG , [ ] ( double v ) { return g_tan ( v * ( M_PI / 180.0 ) ) ; } } ,
2016-03-01 15:47:10 +00:00
2016-03-11 14:45:47 +00:00
{ NAME_CosH , FLOP_COSH , [ ] ( double v ) { return g_cosh ( v ) ; } } ,
{ NAME_SinH , FLOP_SINH , [ ] ( double v ) { return g_sinh ( v ) ; } } ,
{ NAME_TanH , FLOP_TANH , [ ] ( double v ) { return g_tanh ( v ) ; } } ,
2016-03-01 15:47:10 +00:00
} ;
2016-07-26 21:57:26 +00:00
//==========================================================================
//
// FCompileContext
//
//==========================================================================
2016-10-15 19:35:31 +00:00
FCompileContext : : FCompileContext ( PFunction * fnc , PPrototype * ret , bool fromdecorate ) : ReturnProto ( ret ) , Function ( fnc ) , Class ( nullptr ) , FromDecorate ( fromdecorate )
2016-07-26 21:57:26 +00:00
{
2016-10-15 15:40:27 +00:00
if ( fnc ! = nullptr ) Class = fnc - > OwningClass ;
2016-07-26 21:57:26 +00:00
}
2016-10-15 19:35:31 +00:00
FCompileContext : : FCompileContext ( PClass * cls ) : ReturnProto ( nullptr ) , Function ( nullptr ) , Class ( cls ) , FromDecorate ( true ) // only used by DECORATE constants.
2016-07-26 21:57:26 +00:00
{
2016-10-15 15:40:27 +00:00
}
PSymbol * FCompileContext : : FindInClass ( FName identifier , PSymbolTable * & symt )
{
return Class ! = nullptr ? Class - > Symbols . FindSymbolInTable ( identifier , symt ) : nullptr ;
}
PSymbol * FCompileContext : : FindInSelfClass ( FName identifier , PSymbolTable * & symt )
{
// If we have no self we cannot retrieve any values from it.
if ( Function = = nullptr | | Function - > Variants [ 0 ] . SelfClass = = nullptr ) return nullptr ;
return Function - > Variants [ 0 ] . SelfClass - > Symbols . FindSymbolInTable ( identifier , symt ) ;
2016-07-26 21:57:26 +00:00
}
PSymbol * FCompileContext : : FindGlobal ( FName identifier )
{
return GlobalSymbols . FindSymbol ( identifier , true ) ;
}
2016-08-02 16:50:34 +00:00
void FCompileContext : : CheckReturn ( PPrototype * proto , FScriptPosition & pos )
{
assert ( proto ! = nullptr ) ;
bool fail = false ;
if ( ReturnProto = = nullptr )
{
ReturnProto = proto ;
return ;
}
// A prototype that defines fewer return types can be compatible with
// one that defines more if the shorter one matches the initial types
// for the longer one.
if ( ReturnProto - > ReturnTypes . Size ( ) < proto - > ReturnTypes . Size ( ) )
{ // Make proto the shorter one to avoid code duplication below.
swapvalues ( proto , ReturnProto ) ;
}
// If one prototype returns nothing, they both must.
if ( proto - > ReturnTypes . Size ( ) = = 0 )
{
if ( ReturnProto - > ReturnTypes . Size ( ) ! = 0 )
{
fail = true ;
}
}
else
{
for ( unsigned i = 0 ; i < proto - > ReturnTypes . Size ( ) ; i + + )
{
if ( ReturnProto - > ReturnTypes [ i ] ! = proto - > ReturnTypes [ i ] )
{ // Incompatible
fail = true ;
break ;
}
}
}
if ( fail )
{
pos . Message ( MSG_ERROR , " All return expressions must deduce to the same type " ) ;
}
}
2016-10-20 23:12:54 +00:00
FxLocalVariableDeclaration * FCompileContext : : FindLocalVariable ( FName name )
{
if ( Block = = nullptr )
{
return nullptr ;
}
else
{
return Block - > FindLocalVariable ( name , * this ) ;
}
}
2016-07-26 21:57:26 +00:00
//==========================================================================
//
// ExpEmit
//
//==========================================================================
2016-03-01 15:47:10 +00:00
ExpEmit : : ExpEmit ( VMFunctionBuilder * build , int type )
2016-10-21 08:09:01 +00:00
: RegNum ( build - > Registers [ type ] . Get ( 1 ) ) , RegType ( type ) , Konst ( false ) , Fixed ( false ) , Final ( false ) , Target ( false )
2016-03-01 15:47:10 +00:00
{
}
void ExpEmit : : Free ( VMFunctionBuilder * build )
{
if ( ! Fixed & & ! Konst & & RegType < = REGT_TYPE )
{
build - > Registers [ RegType ] . Return ( RegNum , 1 ) ;
}
}
void ExpEmit : : Reuse ( VMFunctionBuilder * build )
{
if ( ! Fixed & & ! Konst )
{
bool success = build - > Registers [ RegType ] . Reuse ( RegNum ) ;
assert ( success & & " Attempt to reuse a register that is already in use " ) ;
}
}
//==========================================================================
//
// FindDecorateBuiltinFunction
//
// Returns the symbol for a decorate utility function. If not found, create
// it and install it in Actor.
//
//==========================================================================
static PSymbol * FindDecorateBuiltinFunction ( FName funcname , VMNativeFunction : : NativeCallType func )
{
PSymbol * sym = RUNTIME_CLASS ( AActor ) - > Symbols . FindSymbol ( funcname , false ) ;
if ( sym = = NULL )
{
PSymbolVMFunction * symfunc = new PSymbolVMFunction ( funcname ) ;
VMNativeFunction * calldec = new VMNativeFunction ( func , funcname ) ;
symfunc - > Function = calldec ;
sym = symfunc ;
RUNTIME_CLASS ( AActor ) - > Symbols . AddSymbol ( sym ) ;
}
return sym ;
}
//==========================================================================
//
//
//
//==========================================================================
2016-10-21 22:50:04 +00:00
static bool AreCompatiblePointerTypes ( PType * dest , PType * source )
{
if ( dest - > IsKindOf ( RUNTIME_CLASS ( PPointer ) ) & & source - > IsKindOf ( RUNTIME_CLASS ( PPointer ) ) )
{
// Pointers to different types are only compatible if both point to an object and the source type is a child of the destination type.
auto fromtype = static_cast < PPointer * > ( source ) ;
auto totype = static_cast < PPointer * > ( dest ) ;
if ( fromtype = = nullptr ) return true ;
if ( fromtype = = totype ) return true ;
if ( fromtype - > PointedType - > IsKindOf ( RUNTIME_CLASS ( PClass ) ) & & totype - > PointedType - > IsKindOf ( RUNTIME_CLASS ( PClass ) ) )
{
auto fromcls = static_cast < PClass * > ( fromtype - > PointedType ) ;
auto tocls = static_cast < PClass * > ( totype - > PointedType ) ;
return ( fromcls - > IsDescendantOf ( tocls ) ) ;
}
}
return false ;
}
//==========================================================================
//
//
//
//==========================================================================
2016-03-01 15:47:10 +00:00
ExpEmit FxExpression : : Emit ( VMFunctionBuilder * build )
{
ScriptPosition . Message ( MSG_ERROR , " Unemitted expression found " ) ;
return ExpEmit ( ) ;
}
//==========================================================================
//
//
//
//==========================================================================
bool FxExpression : : isConstant ( ) const
{
return false ;
}
//==========================================================================
//
//
//
//==========================================================================
VMFunction * FxExpression : : GetDirectFunction ( )
{
return NULL ;
}
//==========================================================================
//
//
//
//==========================================================================
FxExpression * FxExpression : : Resolve ( FCompileContext & ctx )
{
isresolved = true ;
return this ;
}
//==========================================================================
//
2016-08-05 15:33:29 +00:00
// Returns true if we can write to the address.
2016-03-01 15:47:10 +00:00
//
//==========================================================================
2016-10-17 18:33:35 +00:00
bool FxExpression : : RequestAddress ( bool * writable )
2016-03-01 15:47:10 +00:00
{
2016-10-17 18:33:35 +00:00
if ( writable ! = nullptr ) * writable = false ;
2016-08-05 15:33:29 +00:00
return false ;
2016-03-01 15:47:10 +00:00
}
2016-08-02 16:50:34 +00:00
//==========================================================================
//
// Called by return statements.
//
//==========================================================================
PPrototype * FxExpression : : ReturnProto ( )
{
assert ( ValueType ! = nullptr ) ;
TArray < PType * > ret ( 0 ) ;
TArray < PType * > none ( 0 ) ;
if ( ValueType ! = TypeVoid )
{
ret . Push ( ValueType ) ;
}
return NewPrototype ( ret , none ) ;
}
2016-03-01 15:47:10 +00:00
//==========================================================================
//
//
//
//==========================================================================
static void EmitParameter ( VMFunctionBuilder * build , FxExpression * operand , const FScriptPosition & pos )
{
ExpEmit where = operand - > Emit ( build ) ;
if ( where . RegType = = REGT_NIL )
{
pos . Message ( MSG_ERROR , " Attempted to pass a non-value " ) ;
build - > Emit ( OP_PARAM , 0 , where . RegType , where . RegNum ) ;
}
else
{
int regtype = where . RegType ;
if ( where . Konst )
{
regtype | = REGT_KONST ;
}
build - > Emit ( OP_PARAM , 0 , regtype , where . RegNum ) ;
where . Free ( build ) ;
}
}
//==========================================================================
//
//
//
//==========================================================================
FxExpression * FxConstant : : MakeConstant ( PSymbol * sym , const FScriptPosition & pos )
{
FxExpression * x ;
PSymbolConstNumeric * csym = dyn_cast < PSymbolConstNumeric > ( sym ) ;
if ( csym ! = NULL )
{
if ( csym - > ValueType - > IsA ( RUNTIME_CLASS ( PInt ) ) )
{
x = new FxConstant ( csym - > Value , pos ) ;
}
else if ( csym - > ValueType - > IsA ( RUNTIME_CLASS ( PFloat ) ) )
{
x = new FxConstant ( csym - > Float , pos ) ;
}
else
{
pos . Message ( MSG_ERROR , " Invalid constant '%s' \n " , csym - > SymbolName . GetChars ( ) ) ;
return NULL ;
}
}
else
{
pos . Message ( MSG_ERROR , " '%s' is not a constant \n " , sym - > SymbolName . GetChars ( ) ) ;
x = NULL ;
}
return x ;
}
ExpEmit FxConstant : : Emit ( VMFunctionBuilder * build )
{
ExpEmit out ;
out . Konst = true ;
int regtype = value . Type - > GetRegType ( ) ;
out . RegType = regtype ;
if ( regtype = = REGT_INT )
{
out . RegNum = build - > GetConstantInt ( value . Int ) ;
}
else if ( regtype = = REGT_FLOAT )
{
out . RegNum = build - > GetConstantFloat ( value . Float ) ;
}
else if ( regtype = = REGT_POINTER )
{
VM_ATAG tag = ATAG_GENERIC ;
if ( value . Type = = TypeState )
{
tag = ATAG_STATE ;
}
else if ( value . Type - > GetLoadOp ( ) = = OP_LO )
{
tag = ATAG_OBJECT ;
}
out . RegNum = build - > GetConstantAddress ( value . pointer , tag ) ;
}
else if ( regtype = = REGT_STRING )
{
out . RegNum = build - > GetConstantString ( value . GetString ( ) ) ;
}
else
{
ScriptPosition . Message ( MSG_ERROR , " Cannot emit needed constant " ) ;
out . RegNum = 0 ;
}
return out ;
}
//==========================================================================
//
//
//
//==========================================================================
2016-10-23 13:30:58 +00:00
FxBoolCast : : FxBoolCast ( FxExpression * x , bool needvalue )
2016-10-22 23:14:49 +00:00
: FxExpression ( EFX_BoolCast , x - > ScriptPosition )
2016-07-04 00:19:52 +00:00
{
basex = x ;
ValueType = TypeBool ;
2016-10-23 13:30:58 +00:00
NeedValue = needvalue ;
2016-07-04 00:19:52 +00:00
}
//==========================================================================
//
//
//
//==========================================================================
FxBoolCast : : ~ FxBoolCast ( )
{
SAFE_DELETE ( basex ) ;
}
//==========================================================================
//
//
//
//==========================================================================
FxExpression * FxBoolCast : : Resolve ( FCompileContext & ctx )
{
CHECKRESOLVED ( ) ;
SAFE_RESOLVE ( basex , ctx ) ;
if ( basex - > ValueType = = TypeBool )
{
FxExpression * x = basex ;
basex = nullptr ;
delete this ;
return x ;
}
else if ( basex - > ValueType - > GetRegType ( ) = = REGT_INT | | basex - > ValueType - > GetRegType ( ) = = REGT_FLOAT | | basex - > ValueType - > GetRegType ( ) = = REGT_POINTER )
{
if ( basex - > isConstant ( ) )
{
assert ( basex - > ValueType ! = TypeState & & " We shouldn't be able to generate a constant state ref " ) ;
ExpVal constval = static_cast < FxConstant * > ( basex ) - > GetValue ( ) ;
FxExpression * x = new FxConstant ( constval . GetBool ( ) , ScriptPosition ) ;
delete this ;
return x ;
}
return this ;
}
ScriptPosition . Message ( MSG_ERROR , " Numeric type expected " ) ;
delete this ;
return nullptr ;
}
//==========================================================================
//
//
//
//==========================================================================
ExpEmit FxBoolCast : : Emit ( VMFunctionBuilder * build )
{
ExpEmit from = basex - > Emit ( build ) ;
assert ( ! from . Konst ) ;
assert ( basex - > ValueType - > GetRegType ( ) = = REGT_INT | | basex - > ValueType - > GetRegType ( ) = = REGT_FLOAT | | basex - > ValueType - > GetRegType ( ) = = REGT_POINTER ) ;
2016-10-23 13:30:58 +00:00
if ( NeedValue )
2016-07-04 00:19:52 +00:00
{
2016-10-23 13:30:58 +00:00
ExpEmit to ( build , REGT_INT ) ;
from . Free ( build ) ;
// Preload result with 0.
build - > Emit ( OP_LI , to . RegNum , 0 ) ;
// Check source against 0.
if ( from . RegType = = REGT_INT )
{
build - > Emit ( OP_EQ_R , 1 , from . RegNum , to . RegNum ) ;
}
else if ( from . RegType = = REGT_FLOAT )
{
build - > Emit ( OP_EQF_K , 1 , from . RegNum , build - > GetConstantFloat ( 0. ) ) ;
}
else if ( from . RegType = = REGT_POINTER )
{
build - > Emit ( OP_EQA_K , 1 , from . RegNum , build - > GetConstantAddress ( nullptr , ATAG_GENERIC ) ) ;
}
build - > Emit ( OP_JMP , 1 ) ;
// Reload result with 1 if the comparison fell through.
build - > Emit ( OP_LI , to . RegNum , 1 ) ;
return to ;
2016-07-04 00:19:52 +00:00
}
2016-10-23 13:30:58 +00:00
else
2016-07-04 00:19:52 +00:00
{
2016-10-23 13:30:58 +00:00
return from ;
2016-07-04 00:19:52 +00:00
}
}
//==========================================================================
//
//
//
//==========================================================================
2016-10-15 19:35:31 +00:00
FxIntCast : : FxIntCast ( FxExpression * x , bool nowarn )
2016-10-22 23:14:49 +00:00
: FxExpression ( EFX_IntCast , x - > ScriptPosition )
2016-03-01 15:47:10 +00:00
{
basex = x ;
ValueType = TypeSInt32 ;
2016-10-15 19:35:31 +00:00
NoWarn = nowarn ;
2016-03-01 15:47:10 +00:00
}
//==========================================================================
//
//
//
//==========================================================================
FxIntCast : : ~ FxIntCast ( )
{
SAFE_DELETE ( basex ) ;
}
//==========================================================================
//
//
//
//==========================================================================
FxExpression * FxIntCast : : Resolve ( FCompileContext & ctx )
{
CHECKRESOLVED ( ) ;
SAFE_RESOLVE ( basex , ctx ) ;
if ( basex - > ValueType - > GetRegType ( ) = = REGT_INT )
{
2016-06-08 08:46:35 +00:00
if ( basex - > ValueType ! = TypeName )
{
FxExpression * x = basex ;
basex = NULL ;
delete this ;
return x ;
}
else
{
// Ugh. This should abort, but too many mods fell into this logic hole somewhere, so this seroious error needs to be reduced to a warning. :(
2016-10-15 19:35:31 +00:00
// At least in ZScript, MSG_OPTERROR always means to report an error, not a warning so the problem only exists in DECORATE.
2016-06-08 08:56:11 +00:00
if ( ! basex - > isConstant ( ) ) ScriptPosition . Message ( MSG_OPTERROR , " Numeric type expected, got a name " ) ;
else ScriptPosition . Message ( MSG_OPTERROR , " Numeric type expected, got \" %s \" " , static_cast < FxConstant * > ( basex ) - > GetValue ( ) . GetName ( ) . GetChars ( ) ) ;
2016-06-08 08:46:35 +00:00
FxExpression * x = new FxConstant ( 0 , ScriptPosition ) ;
delete this ;
return x ;
}
2016-03-01 15:47:10 +00:00
}
else if ( basex - > ValueType - > GetRegType ( ) = = REGT_FLOAT )
{
if ( basex - > isConstant ( ) )
{
ExpVal constval = static_cast < FxConstant * > ( basex ) - > GetValue ( ) ;
FxExpression * x = new FxConstant ( constval . GetInt ( ) , ScriptPosition ) ;
2016-10-15 19:35:31 +00:00
if ( ! NoWarn & & constval . GetInt ( ) ! = constval . GetFloat ( ) )
{
ScriptPosition . Message ( MSG_WARNING , " Truncation of floating point constant %f " , constval . GetFloat ( ) ) ;
}
2016-03-01 15:47:10 +00:00
delete this ;
return x ;
}
2016-10-15 19:35:31 +00:00
else if ( ! NoWarn )
{
ScriptPosition . Message ( MSG_WARNING , " Truncation of floating point value " ) ;
}
2016-03-01 15:47:10 +00:00
return this ;
}
2016-06-08 08:46:35 +00:00
ScriptPosition . Message ( MSG_ERROR , " Numeric type expected " ) ;
delete this ;
return NULL ;
2016-03-01 15:47:10 +00:00
}
//==========================================================================
//
//
//
//==========================================================================
ExpEmit FxIntCast : : Emit ( VMFunctionBuilder * build )
{
ExpEmit from = basex - > Emit ( build ) ;
assert ( ! from . Konst ) ;
assert ( basex - > ValueType - > GetRegType ( ) = = REGT_FLOAT ) ;
from . Free ( build ) ;
ExpEmit to ( build , REGT_INT ) ;
build - > Emit ( OP_CAST , to . RegNum , from . RegNum , CAST_F2I ) ;
return to ;
}
//==========================================================================
//
//
//
//==========================================================================
FxFloatCast : : FxFloatCast ( FxExpression * x )
2016-10-22 23:14:49 +00:00
: FxExpression ( EFX_FloatCast , x - > ScriptPosition )
2016-03-01 15:47:10 +00:00
{
2016-10-16 17:42:22 +00:00
basex = x ;
2016-03-01 15:47:10 +00:00
ValueType = TypeFloat64 ;
}
//==========================================================================
//
//
//
//==========================================================================
FxFloatCast : : ~ FxFloatCast ( )
{
SAFE_DELETE ( basex ) ;
}
//==========================================================================
//
//
//
//==========================================================================
FxExpression * FxFloatCast : : Resolve ( FCompileContext & ctx )
{
CHECKRESOLVED ( ) ;
SAFE_RESOLVE ( basex , ctx ) ;
if ( basex - > ValueType - > GetRegType ( ) = = REGT_FLOAT )
{
FxExpression * x = basex ;
basex = NULL ;
delete this ;
return x ;
}
else if ( basex - > ValueType - > GetRegType ( ) = = REGT_INT )
{
2016-06-08 08:46:35 +00:00
if ( basex - > ValueType ! = TypeName )
2016-03-01 15:47:10 +00:00
{
2016-06-08 08:46:35 +00:00
if ( basex - > isConstant ( ) )
{
ExpVal constval = static_cast < FxConstant * > ( basex ) - > GetValue ( ) ;
FxExpression * x = new FxConstant ( constval . GetFloat ( ) , ScriptPosition ) ;
delete this ;
return x ;
}
return this ;
}
else
{
// Ugh. This should abort, but too many mods fell into this logic hole somewhere, so this seroious error needs to be reduced to a warning. :(
2016-10-15 19:35:31 +00:00
// At least in ZScript, MSG_OPTERROR always means to report an error, not a warning so the problem only exists in DECORATE.
2016-06-08 08:56:11 +00:00
if ( ! basex - > isConstant ( ) ) ScriptPosition . Message ( MSG_OPTERROR , " Numeric type expected, got a name " ) ;
else ScriptPosition . Message ( MSG_OPTERROR , " Numeric type expected, got \" %s \" " , static_cast < FxConstant * > ( basex ) - > GetValue ( ) . GetName ( ) . GetChars ( ) ) ;
2016-06-08 08:46:35 +00:00
FxExpression * x = new FxConstant ( 0.0 , ScriptPosition ) ;
2016-03-01 15:47:10 +00:00
delete this ;
return x ;
}
}
else
{
ScriptPosition . Message ( MSG_ERROR , " Numeric type expected " ) ;
delete this ;
return NULL ;
}
}
//==========================================================================
//
//
//
//==========================================================================
ExpEmit FxFloatCast : : Emit ( VMFunctionBuilder * build )
{
ExpEmit from = basex - > Emit ( build ) ;
assert ( ! from . Konst ) ;
assert ( basex - > ValueType - > GetRegType ( ) = = REGT_INT ) ;
from . Free ( build ) ;
ExpEmit to ( build , REGT_FLOAT ) ;
build - > Emit ( OP_CAST , to . RegNum , from . RegNum , CAST_I2F ) ;
return to ;
}
//==========================================================================
//
//
//
//==========================================================================
2016-10-16 17:42:22 +00:00
FxNameCast : : FxNameCast ( FxExpression * x )
2016-10-22 23:14:49 +00:00
: FxExpression ( EFX_NameCast , x - > ScriptPosition )
2016-10-16 17:42:22 +00:00
{
basex = x ;
ValueType = TypeName ;
}
//==========================================================================
//
//
//
//==========================================================================
FxNameCast : : ~ FxNameCast ( )
{
SAFE_DELETE ( basex ) ;
}
//==========================================================================
//
//
//
//==========================================================================
FxExpression * FxNameCast : : Resolve ( FCompileContext & ctx )
{
CHECKRESOLVED ( ) ;
SAFE_RESOLVE ( basex , ctx ) ;
if ( basex - > ValueType = = TypeName )
{
FxExpression * x = basex ;
basex = NULL ;
delete this ;
return x ;
}
else if ( basex - > ValueType = = TypeString )
{
if ( basex - > isConstant ( ) )
{
ExpVal constval = static_cast < FxConstant * > ( basex ) - > GetValue ( ) ;
FxExpression * x = new FxConstant ( constval . GetName ( ) , ScriptPosition ) ;
delete this ;
return x ;
}
return this ;
}
else
{
ScriptPosition . Message ( MSG_ERROR , " Cannot convert to name " ) ;
delete this ;
return NULL ;
}
}
//==========================================================================
//
//
//
//==========================================================================
ExpEmit FxNameCast : : Emit ( VMFunctionBuilder * build )
{
ExpEmit from = basex - > Emit ( build ) ;
assert ( ! from . Konst ) ;
assert ( basex - > ValueType = = TypeString ) ;
from . Free ( build ) ;
ExpEmit to ( build , REGT_INT ) ;
build - > Emit ( OP_CAST , to . RegNum , from . RegNum , CAST_S2N ) ;
return to ;
}
//==========================================================================
//
//
//
//==========================================================================
FxStringCast : : FxStringCast ( FxExpression * x )
2016-10-22 23:14:49 +00:00
: FxExpression ( EFX_StringCast , x - > ScriptPosition )
2016-10-16 17:42:22 +00:00
{
basex = x ;
ValueType = TypeString ;
}
//==========================================================================
//
//
//
//==========================================================================
FxStringCast : : ~ FxStringCast ( )
{
SAFE_DELETE ( basex ) ;
}
//==========================================================================
//
//
//
//==========================================================================
FxExpression * FxStringCast : : Resolve ( FCompileContext & ctx )
{
CHECKRESOLVED ( ) ;
SAFE_RESOLVE ( basex , ctx ) ;
if ( basex - > ValueType = = TypeString )
{
FxExpression * x = basex ;
basex = NULL ;
delete this ;
return x ;
}
else if ( basex - > ValueType = = TypeName )
{
if ( basex - > isConstant ( ) )
{
ExpVal constval = static_cast < FxConstant * > ( basex ) - > GetValue ( ) ;
FxExpression * x = new FxConstant ( constval . GetString ( ) , ScriptPosition ) ;
delete this ;
return x ;
}
return this ;
}
else if ( basex - > ValueType = = TypeSound )
{
if ( basex - > isConstant ( ) )
{
ExpVal constval = static_cast < FxConstant * > ( basex ) - > GetValue ( ) ;
FxExpression * x = new FxConstant ( S_sfx [ constval . GetInt ( ) ] . name , ScriptPosition ) ;
delete this ;
return x ;
}
return this ;
}
// although it could be done, let's not convert colors back to strings.
else
{
ScriptPosition . Message ( MSG_ERROR , " Cannot convert to string " ) ;
delete this ;
return NULL ;
}
}
//==========================================================================
//
//
//
//==========================================================================
ExpEmit FxStringCast : : Emit ( VMFunctionBuilder * build )
{
ExpEmit from = basex - > Emit ( build ) ;
assert ( ! from . Konst ) ;
from . Free ( build ) ;
ExpEmit to ( build , REGT_STRING ) ;
if ( ValueType = = TypeName )
{
build - > Emit ( OP_CAST , to . RegNum , from . RegNum , CAST_N2S ) ;
}
else if ( ValueType = = TypeSound )
{
build - > Emit ( OP_CAST , to . RegNum , from . RegNum , CAST_So2S ) ;
}
return to ;
}
//==========================================================================
//
//
//
//==========================================================================
FxColorCast : : FxColorCast ( FxExpression * x )
2016-10-22 23:14:49 +00:00
: FxExpression ( EFX_ColorCast , x - > ScriptPosition )
2016-10-16 17:42:22 +00:00
{
basex = x ;
ValueType = TypeColor ;
}
//==========================================================================
//
//
//
//==========================================================================
FxColorCast : : ~ FxColorCast ( )
{
SAFE_DELETE ( basex ) ;
}
//==========================================================================
//
//
//
//==========================================================================
FxExpression * FxColorCast : : Resolve ( FCompileContext & ctx )
{
CHECKRESOLVED ( ) ;
SAFE_RESOLVE ( basex , ctx ) ;
if ( basex - > ValueType = = TypeColor | | basex - > ValueType - > GetClass ( ) = = RUNTIME_CLASS ( PInt ) )
{
FxExpression * x = basex ;
x - > ValueType = TypeColor ;
basex = NULL ;
delete this ;
return x ;
}
else if ( basex - > ValueType = = TypeString )
{
if ( basex - > isConstant ( ) )
{
ExpVal constval = static_cast < FxConstant * > ( basex ) - > GetValue ( ) ;
FxExpression * x = new FxConstant ( V_GetColor ( nullptr , constval . GetString ( ) ) , ScriptPosition ) ;
delete this ;
return x ;
}
return this ;
}
else
{
ScriptPosition . Message ( MSG_ERROR , " Cannot convert to color " ) ;
delete this ;
return NULL ;
}
}
//==========================================================================
//
//
//
//==========================================================================
ExpEmit FxColorCast : : Emit ( VMFunctionBuilder * build )
{
ExpEmit from = basex - > Emit ( build ) ;
assert ( ! from . Konst ) ;
assert ( basex - > ValueType = = TypeString ) ;
from . Free ( build ) ;
ExpEmit to ( build , REGT_INT ) ;
build - > Emit ( OP_CAST , to . RegNum , from . RegNum , CAST_S2Co ) ;
return to ;
}
//==========================================================================
//
//
//
//==========================================================================
FxSoundCast : : FxSoundCast ( FxExpression * x )
2016-10-22 23:14:49 +00:00
: FxExpression ( EFX_SoundCast , x - > ScriptPosition )
2016-10-16 17:42:22 +00:00
{
basex = x ;
ValueType = TypeSound ;
}
//==========================================================================
//
//
//
//==========================================================================
FxSoundCast : : ~ FxSoundCast ( )
{
SAFE_DELETE ( basex ) ;
}
//==========================================================================
//
//
//
//==========================================================================
FxExpression * FxSoundCast : : Resolve ( FCompileContext & ctx )
{
CHECKRESOLVED ( ) ;
SAFE_RESOLVE ( basex , ctx ) ;
if ( basex - > ValueType = = TypeSound | | basex - > ValueType - > GetClass ( ) = = RUNTIME_CLASS ( PInt ) )
{
FxExpression * x = basex ;
x - > ValueType = TypeSound ;
basex = NULL ;
delete this ;
return x ;
}
else if ( basex - > ValueType = = TypeString )
{
if ( basex - > isConstant ( ) )
{
ExpVal constval = static_cast < FxConstant * > ( basex ) - > GetValue ( ) ;
FxExpression * x = new FxConstant ( FSoundID ( constval . GetString ( ) ) , ScriptPosition ) ;
delete this ;
return x ;
}
return this ;
}
else
{
ScriptPosition . Message ( MSG_ERROR , " Cannot convert to sound " ) ;
delete this ;
return NULL ;
}
}
//==========================================================================
//
//
//
//==========================================================================
ExpEmit FxSoundCast : : Emit ( VMFunctionBuilder * build )
{
ExpEmit from = basex - > Emit ( build ) ;
assert ( ! from . Konst ) ;
assert ( basex - > ValueType = = TypeString ) ;
from . Free ( build ) ;
ExpEmit to ( build , REGT_INT ) ;
build - > Emit ( OP_CAST , to . RegNum , from . RegNum , CAST_S2So ) ;
return to ;
}
//==========================================================================
//
// generic type cast operator
//
//==========================================================================
FxTypeCast : : FxTypeCast ( FxExpression * x , PType * type , bool nowarn )
2016-10-22 23:14:49 +00:00
: FxExpression ( EFX_TypeCast , x - > ScriptPosition )
2016-10-16 17:42:22 +00:00
{
basex = x ;
ValueType = type ;
2016-10-26 12:15:11 +00:00
assert ( ValueType ! = nullptr ) ;
2016-10-16 17:42:22 +00:00
}
//==========================================================================
//
//
//
//==========================================================================
FxTypeCast : : ~ FxTypeCast ( )
{
SAFE_DELETE ( basex ) ;
}
//==========================================================================
//
//
//
//==========================================================================
FxExpression * FxTypeCast : : Resolve ( FCompileContext & ctx )
{
CHECKRESOLVED ( ) ;
SAFE_RESOLVE ( basex , ctx ) ;
// first deal with the simple types
if ( ValueType = = TypeError | | basex - > ValueType = = TypeError )
{
delete this ;
return nullptr ;
}
else if ( ValueType = = TypeVoid ) // this should never happen
{
goto errormsg ;
}
else if ( basex - > ValueType = = TypeVoid )
{
goto errormsg ;
}
else if ( basex - > ValueType = = ValueType )
{
// don't go through the entire list if the types are the same.
goto basereturn ;
}
2016-10-21 17:18:39 +00:00
else if ( basex - > ValueType = = TypeNullPtr & & ( ValueType = = TypeState | | ValueType - > IsKindOf ( RUNTIME_CLASS ( PPointer ) ) ) )
{
goto basereturn ;
}
2016-10-16 17:42:22 +00:00
else if ( ValueType - > GetRegType ( ) = = REGT_FLOAT )
{
FxExpression * x = new FxFloatCast ( basex ) ;
x = x - > Resolve ( ctx ) ;
basex = nullptr ;
delete this ;
return x ;
}
else if ( ValueType - > IsA ( RUNTIME_CLASS ( PInt ) ) )
{
// This is only for casting to actual ints. Subtypes representing an int will be handled elsewhere.
FxExpression * x = new FxIntCast ( basex , NoWarn ) ;
x = x - > Resolve ( ctx ) ;
basex = nullptr ;
delete this ;
return x ;
}
else if ( ValueType = = TypeBool )
{
FxExpression * x = new FxBoolCast ( basex ) ;
x = x - > Resolve ( ctx ) ;
basex = nullptr ;
delete this ;
return x ;
}
else if ( ValueType = = TypeString )
{
FxExpression * x = new FxStringCast ( basex ) ;
x = x - > Resolve ( ctx ) ;
basex = nullptr ;
delete this ;
return x ;
}
else if ( ValueType = = TypeName )
{
FxExpression * x = new FxNameCast ( basex ) ;
x = x - > Resolve ( ctx ) ;
basex = nullptr ;
delete this ;
return x ;
}
else if ( ValueType = = TypeSound )
{
FxExpression * x = new FxSoundCast ( basex ) ;
x = x - > Resolve ( ctx ) ;
basex = nullptr ;
delete this ;
return x ;
}
else if ( ValueType = = TypeColor )
{
FxExpression * x = new FxColorCast ( basex ) ;
x = x - > Resolve ( ctx ) ;
basex = nullptr ;
delete this ;
return x ;
}
2016-10-16 20:32:52 +00:00
else if ( ValueType = = TypeState )
{
// Right now this only supports string constants. There should be an option to pass a string variable, too.
if ( basex - > isConstant ( ) & & ( basex - > ValueType = = TypeString | | basex - > ValueType = = TypeName ) )
{
FxExpression * x = new FxMultiNameState ( static_cast < FxConstant * > ( basex ) - > GetValue ( ) . GetString ( ) , basex - > ScriptPosition ) ;
x = x - > Resolve ( ctx ) ;
basex = nullptr ;
delete this ;
return x ;
}
}
2016-10-16 17:42:22 +00:00
else if ( ValueType - > IsKindOf ( RUNTIME_CLASS ( PClassPointer ) ) )
{
FxExpression * x = new FxClassTypeCast ( static_cast < PClassPointer * > ( ValueType ) , basex ) ;
x = x - > Resolve ( ctx ) ;
basex = nullptr ;
delete this ;
return x ;
}
/* else if (ValueType->IsKindOf(RUNTIME_CLASS(PEnum)))
{
// this is not yet ready and does not get assigned to actual values.
}
*/
else if ( ValueType - > IsKindOf ( RUNTIME_CLASS ( PClass ) ) ) // this should never happen because the VM doesn't handle plain class types - just pointers
{
if ( basex - > ValueType - > IsKindOf ( RUNTIME_CLASS ( PClass ) ) )
{
// class types are only compatible if the base type is a descendant of the result type.
auto fromtype = static_cast < PClass * > ( basex - > ValueType ) ;
auto totype = static_cast < PClass * > ( ValueType ) ;
if ( fromtype - > IsDescendantOf ( totype ) ) goto basereturn ;
}
}
2016-10-21 22:50:04 +00:00
else if ( AreCompatiblePointerTypes ( ValueType , basex - > ValueType ) )
2016-10-16 17:42:22 +00:00
{
2016-10-21 22:50:04 +00:00
goto basereturn ;
2016-10-16 17:42:22 +00:00
}
// todo: pointers to class objects.
// All other types are only compatible to themselves and have already been handled above by the equality check.
// Anything that falls through here is not compatible and must print an error.
errormsg :
ScriptPosition . Message ( MSG_ERROR , " Cannot convert %s to %s " , basex - > ValueType - > DescriptiveName ( ) , ValueType - > DescriptiveName ( ) ) ;
delete this ;
return nullptr ;
basereturn :
auto x = basex ;
2016-10-21 22:50:04 +00:00
x - > ValueType = ValueType ;
2016-10-16 17:42:22 +00:00
basex = nullptr ;
delete this ;
return x ;
}
//==========================================================================
//
//
//
//==========================================================================
ExpEmit FxTypeCast : : Emit ( VMFunctionBuilder * build )
{
assert ( false ) ;
// This should never be reached
return ExpEmit ( ) ;
}
//==========================================================================
//
//
//
//==========================================================================
2016-03-01 15:47:10 +00:00
FxPlusSign : : FxPlusSign ( FxExpression * operand )
2016-10-22 23:14:49 +00:00
: FxExpression ( EFX_PlusSign , operand - > ScriptPosition )
2016-03-01 15:47:10 +00:00
{
Operand = operand ;
}
//==========================================================================
//
//
//
//==========================================================================
FxPlusSign : : ~ FxPlusSign ( )
{
SAFE_DELETE ( Operand ) ;
}
//==========================================================================
//
//
//
//==========================================================================
FxExpression * FxPlusSign : : Resolve ( FCompileContext & ctx )
{
CHECKRESOLVED ( ) ;
SAFE_RESOLVE ( Operand , ctx ) ;
if ( Operand - > IsNumeric ( ) )
{
FxExpression * e = Operand ;
Operand = NULL ;
delete this ;
return e ;
}
else
{
ScriptPosition . Message ( MSG_ERROR , " Numeric type expected " ) ;
delete this ;
return NULL ;
}
}
ExpEmit FxPlusSign : : Emit ( VMFunctionBuilder * build )
{
return Operand - > Emit ( build ) ;
}
//==========================================================================
//
//
//
//==========================================================================
FxMinusSign : : FxMinusSign ( FxExpression * operand )
2016-10-22 23:14:49 +00:00
: FxExpression ( EFX_MinusSign , operand - > ScriptPosition )
2016-03-01 15:47:10 +00:00
{
Operand = operand ;
}
//==========================================================================
//
//
//
//==========================================================================
FxMinusSign : : ~ FxMinusSign ( )
{
SAFE_DELETE ( Operand ) ;
}
//==========================================================================
//
//
//
//==========================================================================
FxExpression * FxMinusSign : : Resolve ( FCompileContext & ctx )
{
CHECKRESOLVED ( ) ;
SAFE_RESOLVE ( Operand , ctx ) ;
if ( Operand - > IsNumeric ( ) )
{
if ( Operand - > isConstant ( ) )
{
ExpVal val = static_cast < FxConstant * > ( Operand ) - > GetValue ( ) ;
FxExpression * e = val . Type - > GetRegType ( ) = = REGT_INT ?
new FxConstant ( - val . Int , ScriptPosition ) :
new FxConstant ( - val . Float , ScriptPosition ) ;
delete this ;
return e ;
}
ValueType = Operand - > ValueType ;
return this ;
}
else
{
ScriptPosition . Message ( MSG_ERROR , " Numeric type expected " ) ;
delete this ;
return NULL ;
}
}
//==========================================================================
//
//
//
//==========================================================================
ExpEmit FxMinusSign : : Emit ( VMFunctionBuilder * build )
{
assert ( ValueType = = Operand - > ValueType ) ;
ExpEmit from = Operand - > Emit ( build ) ;
assert ( from . Konst = = 0 ) ;
// Do it in-place.
if ( ValueType - > GetRegType ( ) = = REGT_INT )
{
build - > Emit ( OP_NEG , from . RegNum , from . RegNum , 0 ) ;
}
else
{
assert ( ValueType - > GetRegType ( ) = = REGT_FLOAT ) ;
build - > Emit ( OP_FLOP , from . RegNum , from . RegNum , FLOP_NEG ) ;
}
return from ;
}
//==========================================================================
//
//
//
//==========================================================================
FxUnaryNotBitwise : : FxUnaryNotBitwise ( FxExpression * operand )
2016-10-22 23:14:49 +00:00
: FxExpression ( EFX_UnaryNotBitwise , operand - > ScriptPosition )
2016-03-01 15:47:10 +00:00
{
Operand = operand ;
}
//==========================================================================
//
//
//
//==========================================================================
FxUnaryNotBitwise : : ~ FxUnaryNotBitwise ( )
{
SAFE_DELETE ( Operand ) ;
}
//==========================================================================
//
//
//
//==========================================================================
FxExpression * FxUnaryNotBitwise : : Resolve ( FCompileContext & ctx )
{
CHECKRESOLVED ( ) ;
SAFE_RESOLVE ( Operand , ctx ) ;
if ( Operand - > ValueType - > GetRegType ( ) = = REGT_FLOAT /* lax */ )
{
// DECORATE allows floats here so cast them to int.
2016-10-15 19:35:31 +00:00
Operand = new FxIntCast ( Operand , ctx . FromDecorate ) ;
2016-03-01 15:47:10 +00:00
Operand = Operand - > Resolve ( ctx ) ;
if ( Operand = = NULL )
{
delete this ;
return NULL ;
}
}
if ( Operand - > ValueType - > GetRegType ( ) ! = REGT_INT )
{
ScriptPosition . Message ( MSG_ERROR , " Integer type expected " ) ;
delete this ;
return NULL ;
}
if ( Operand - > isConstant ( ) )
{
int result = ~ static_cast < FxConstant * > ( Operand ) - > GetValue ( ) . GetInt ( ) ;
FxExpression * e = new FxConstant ( result , ScriptPosition ) ;
delete this ;
return e ;
}
ValueType = TypeSInt32 ;
return this ;
}
//==========================================================================
//
//
//
//==========================================================================
ExpEmit FxUnaryNotBitwise : : Emit ( VMFunctionBuilder * build )
{
2016-07-04 00:19:52 +00:00
assert ( Operand - > ValueType - > GetRegType ( ) = = REGT_INT ) ;
2016-03-01 15:47:10 +00:00
ExpEmit from = Operand - > Emit ( build ) ;
2016-07-04 00:19:52 +00:00
assert ( ! from . Konst ) ;
2016-03-01 15:47:10 +00:00
// Do it in-place.
build - > Emit ( OP_NOT , from . RegNum , from . RegNum , 0 ) ;
return from ;
}
//==========================================================================
//
//
//
//==========================================================================
FxUnaryNotBoolean : : FxUnaryNotBoolean ( FxExpression * operand )
2016-10-22 23:14:49 +00:00
: FxExpression ( EFX_UnaryNotBoolean , operand - > ScriptPosition )
2016-03-01 15:47:10 +00:00
{
Operand = operand ;
}
//==========================================================================
//
//
//
//==========================================================================
FxUnaryNotBoolean : : ~ FxUnaryNotBoolean ( )
{
SAFE_DELETE ( Operand ) ;
}
//==========================================================================
//
//
//
//==========================================================================
FxExpression * FxUnaryNotBoolean : : Resolve ( FCompileContext & ctx )
{
CHECKRESOLVED ( ) ;
2016-07-04 00:19:52 +00:00
SAFE_RESOLVE ( Operand , ctx ) ;
2016-03-01 15:47:10 +00:00
2016-07-04 00:19:52 +00:00
if ( Operand - > ValueType ! = TypeBool )
2016-03-01 15:47:10 +00:00
{
2016-07-04 00:19:52 +00:00
Operand = new FxBoolCast ( Operand ) ;
SAFE_RESOLVE ( Operand , ctx ) ;
2016-03-01 15:47:10 +00:00
}
2016-07-04 00:19:52 +00:00
if ( Operand - > isConstant ( ) )
2016-03-01 15:47:10 +00:00
{
2016-07-04 00:19:52 +00:00
bool result = ! static_cast < FxConstant * > ( Operand ) - > GetValue ( ) . GetBool ( ) ;
FxExpression * e = new FxConstant ( result , ScriptPosition ) ;
2016-03-01 15:47:10 +00:00
delete this ;
2016-07-04 00:19:52 +00:00
return e ;
2016-03-01 15:47:10 +00:00
}
2016-07-04 00:19:52 +00:00
ValueType = TypeBool ;
2016-03-01 15:47:10 +00:00
return this ;
}
//==========================================================================
//
//
//
//==========================================================================
ExpEmit FxUnaryNotBoolean : : Emit ( VMFunctionBuilder * build )
{
2016-07-04 00:19:52 +00:00
assert ( Operand - > ValueType = = ValueType ) ;
assert ( ValueType = = TypeBool ) ;
2016-03-01 15:47:10 +00:00
ExpEmit from = Operand - > Emit ( build ) ;
assert ( ! from . Konst ) ;
2016-10-26 09:30:30 +00:00
// boolean not is the same as XOR-ing the lowest bit
build - > Emit ( OP_XOR_RK , from . RegNum , from . RegNum , build - > GetConstantInt ( 1 ) ) ;
2016-07-04 00:19:52 +00:00
return from ;
2016-03-01 15:47:10 +00:00
}
2016-10-17 18:33:35 +00:00
//==========================================================================
//
//
//
//==========================================================================
FxSizeAlign : : FxSizeAlign ( FxExpression * operand , int which )
2016-10-22 23:14:49 +00:00
: FxExpression ( EFX_SizeAlign , operand - > ScriptPosition )
2016-10-17 18:33:35 +00:00
{
Operand = operand ;
Which = which ;
}
//==========================================================================
//
//
//
//==========================================================================
FxSizeAlign : : ~ FxSizeAlign ( )
{
SAFE_DELETE ( Operand ) ;
}
//==========================================================================
//
//
//
//==========================================================================
FxExpression * FxSizeAlign : : Resolve ( FCompileContext & ctx )
{
CHECKRESOLVED ( ) ;
SAFE_RESOLVE ( Operand , ctx ) ;
auto type = Operand - > ValueType ;
if ( Operand - > isConstant ( ) )
{
ScriptPosition . Message ( MSG_ERROR , " cannot determine %s of a constant " , Which = = ' a ' ? " alignment " : " size " ) ;
delete this ;
return NULL ;
}
else if ( ! Operand - > RequestAddress ( nullptr ) )
{
ScriptPosition . Message ( MSG_ERROR , " Operand must be addressable to determine %s " , Which = = ' a ' ? " alignment " : " size " ) ;
delete this ;
return NULL ;
}
else
{
2016-10-21 12:18:31 +00:00
FxExpression * x = new FxConstant ( Which = = TK_AlignOf ? int ( type - > Align ) : int ( type - > Size ) , Operand - > ScriptPosition ) ;
2016-10-17 18:33:35 +00:00
delete this ;
return x - > Resolve ( ctx ) ;
}
}
ExpEmit FxSizeAlign : : Emit ( VMFunctionBuilder * build )
{
return ExpEmit ( ) ;
}
2016-08-05 15:33:29 +00:00
//==========================================================================
//
// FxPreIncrDecr
//
//==========================================================================
FxPreIncrDecr : : FxPreIncrDecr ( FxExpression * base , int token )
2016-10-22 23:14:49 +00:00
: FxExpression ( EFX_PreIncrDecr , base - > ScriptPosition ) , Token ( token ) , Base ( base )
2016-08-05 15:33:29 +00:00
{
AddressRequested = false ;
AddressWritable = false ;
}
FxPreIncrDecr : : ~ FxPreIncrDecr ( )
{
SAFE_DELETE ( Base ) ;
}
2016-10-17 18:33:35 +00:00
bool FxPreIncrDecr : : RequestAddress ( bool * writable )
2016-08-05 15:33:29 +00:00
{
AddressRequested = true ;
2016-10-17 18:33:35 +00:00
if ( writable ! = nullptr ) * writable = AddressWritable ;
return true ;
2016-08-05 15:33:29 +00:00
}
FxExpression * FxPreIncrDecr : : Resolve ( FCompileContext & ctx )
{
CHECKRESOLVED ( ) ;
SAFE_RESOLVE ( Base , ctx ) ;
ValueType = Base - > ValueType ;
if ( ! Base - > IsNumeric ( ) )
{
ScriptPosition . Message ( MSG_ERROR , " Numeric type expected " ) ;
delete this ;
return nullptr ;
}
else if ( Base - > ValueType = = TypeBool )
{
ScriptPosition . Message ( MSG_ERROR , " %s is not allowed on type bool " , FScanner : : TokenName ( Token ) . GetChars ( ) ) ;
delete this ;
return nullptr ;
}
2016-10-17 18:33:35 +00:00
if ( ! Base - > RequestAddress ( & AddressWritable ) | | ! AddressWritable )
2016-08-05 15:33:29 +00:00
{
ScriptPosition . Message ( MSG_ERROR , " Expression must be a modifiable value " ) ;
delete this ;
return nullptr ;
}
return this ;
}
ExpEmit FxPreIncrDecr : : Emit ( VMFunctionBuilder * build )
{
assert ( Token = = TK_Incr | | Token = = TK_Decr ) ;
assert ( ValueType = = Base - > ValueType & & IsNumeric ( ) ) ;
int zero = build - > GetConstantInt ( 0 ) ;
int regtype = ValueType - > GetRegType ( ) ;
ExpEmit pointer = Base - > Emit ( build ) ;
2016-10-20 23:12:54 +00:00
ExpEmit value = pointer ;
2016-08-05 15:33:29 +00:00
2016-10-20 23:12:54 +00:00
if ( ! pointer . Target )
{
value = ExpEmit ( build , regtype ) ;
build - > Emit ( ValueType - > GetLoadOp ( ) , value . RegNum , pointer . RegNum , zero ) ;
}
2016-08-05 15:33:29 +00:00
if ( regtype = = REGT_INT )
{
build - > Emit ( ( Token = = TK_Incr ) ? OP_ADD_RK : OP_SUB_RK , value . RegNum , value . RegNum , build - > GetConstantInt ( 1 ) ) ;
}
else
{
build - > Emit ( ( Token = = TK_Incr ) ? OP_ADDF_RK : OP_SUBF_RK , value . RegNum , value . RegNum , build - > GetConstantFloat ( 1. ) ) ;
}
2016-10-20 23:12:54 +00:00
if ( ! pointer . Target )
{
build - > Emit ( ValueType - > GetStoreOp ( ) , pointer . RegNum , value . RegNum , zero ) ;
}
2016-08-05 15:33:29 +00:00
if ( AddressRequested )
{
value . Free ( build ) ;
return pointer ;
}
pointer . Free ( build ) ;
return value ;
}
//==========================================================================
//
// FxPostIncrDecr
//
//==========================================================================
FxPostIncrDecr : : FxPostIncrDecr ( FxExpression * base , int token )
2016-10-22 23:14:49 +00:00
: FxExpression ( EFX_PostIncrDecr , base - > ScriptPosition ) , Token ( token ) , Base ( base )
2016-08-05 15:33:29 +00:00
{
}
FxPostIncrDecr : : ~ FxPostIncrDecr ( )
{
SAFE_DELETE ( Base ) ;
}
FxExpression * FxPostIncrDecr : : Resolve ( FCompileContext & ctx )
{
CHECKRESOLVED ( ) ;
SAFE_RESOLVE ( Base , ctx ) ;
2016-10-17 18:33:35 +00:00
bool AddressWritable ;
2016-08-05 15:33:29 +00:00
ValueType = Base - > ValueType ;
if ( ! Base - > IsNumeric ( ) )
{
ScriptPosition . Message ( MSG_ERROR , " Numeric type expected " ) ;
delete this ;
return nullptr ;
}
else if ( Base - > ValueType = = TypeBool )
{
ScriptPosition . Message ( MSG_ERROR , " %s is not allowed on type bool " , FScanner : : TokenName ( Token ) . GetChars ( ) ) ;
delete this ;
return nullptr ;
}
2016-10-17 18:33:35 +00:00
if ( ! Base - > RequestAddress ( & AddressWritable ) | | ! AddressWritable )
2016-08-05 15:33:29 +00:00
{
ScriptPosition . Message ( MSG_ERROR , " Expression must be a modifiable value " ) ;
delete this ;
return nullptr ;
}
return this ;
}
ExpEmit FxPostIncrDecr : : Emit ( VMFunctionBuilder * build )
{
assert ( Token = = TK_Incr | | Token = = TK_Decr ) ;
assert ( ValueType = = Base - > ValueType & & IsNumeric ( ) ) ;
int zero = build - > GetConstantInt ( 0 ) ;
int regtype = ValueType - > GetRegType ( ) ;
ExpEmit pointer = Base - > Emit ( build ) ;
2016-10-20 23:12:54 +00:00
if ( ! pointer . Target )
{
2016-10-26 09:30:30 +00:00
ExpEmit out ( build , regtype ) ;
2016-10-20 23:12:54 +00:00
build - > Emit ( ValueType - > GetLoadOp ( ) , out . RegNum , pointer . RegNum , zero ) ;
2016-10-26 09:30:30 +00:00
ExpEmit assign ( build , regtype ) ;
if ( regtype = = REGT_INT )
{
build - > Emit ( ( Token = = TK_Incr ) ? OP_ADD_RK : OP_SUB_RK , assign . RegNum , out . RegNum , build - > GetConstantInt ( 1 ) ) ;
}
else
{
build - > Emit ( ( Token = = TK_Incr ) ? OP_ADDF_RK : OP_SUBF_RK , assign . RegNum , out . RegNum , build - > GetConstantFloat ( 1. ) ) ;
}
build - > Emit ( ValueType - > GetStoreOp ( ) , pointer . RegNum , out . RegNum , zero ) ;
pointer . Free ( build ) ;
assign . Free ( build ) ;
return out ;
2016-10-20 23:12:54 +00:00
}
2016-10-26 09:30:30 +00:00
else if ( NeedResult )
2016-08-05 15:33:29 +00:00
{
2016-10-26 09:30:30 +00:00
ExpEmit out ( build , regtype ) ;
if ( regtype = = REGT_INT )
{
build - > Emit ( OP_MOVE , out . RegNum , pointer . RegNum ) ;
build - > Emit ( ( Token = = TK_Incr ) ? OP_ADD_RK : OP_SUB_RK , pointer . RegNum , pointer . RegNum , build - > GetConstantInt ( 1 ) ) ;
}
else
{
build - > Emit ( OP_MOVEF , out . RegNum , pointer . RegNum ) ;
build - > Emit ( ( Token = = TK_Incr ) ? OP_ADDF_RK : OP_SUBF_RK , pointer . RegNum , pointer . RegNum , build - > GetConstantFloat ( 1. ) ) ;
}
pointer . Free ( build ) ;
return out ;
2016-08-05 15:33:29 +00:00
}
else
{
2016-10-26 09:30:30 +00:00
if ( regtype = = REGT_INT )
{
build - > Emit ( ( Token = = TK_Incr ) ? OP_ADD_RK : OP_SUB_RK , pointer . RegNum , pointer . RegNum , build - > GetConstantInt ( 1 ) ) ;
}
else
{
build - > Emit ( ( Token = = TK_Incr ) ? OP_ADDF_RK : OP_SUBF_RK , pointer . RegNum , pointer . RegNum , build - > GetConstantFloat ( 1. ) ) ;
}
pointer . Free ( build ) ;
return ExpEmit ( ) ;
2016-10-20 23:12:54 +00:00
}
2016-08-05 15:33:29 +00:00
}
2016-07-31 22:32:02 +00:00
//==========================================================================
//
// FxAssign
//
//==========================================================================
2016-10-24 15:18:20 +00:00
FxAssign : : FxAssign ( FxExpression * base , FxExpression * right , bool ismodify )
2016-10-25 14:53:14 +00:00
: FxExpression ( EFX_Assign , base - > ScriptPosition ) , Base ( base ) , Right ( right ) , IsBitWrite ( - 1 ) , IsModifyAssign ( ismodify )
2016-07-31 22:32:02 +00:00
{
AddressRequested = false ;
AddressWritable = false ;
}
FxAssign : : ~ FxAssign ( )
{
SAFE_DELETE ( Base ) ;
SAFE_DELETE ( Right ) ;
}
2016-10-24 15:18:20 +00:00
/* I don't think we should allow constructs like (a = b) = c;...
2016-10-17 18:33:35 +00:00
bool FxAssign : : RequestAddress ( bool * writable )
2016-07-31 22:32:02 +00:00
{
AddressRequested = true ;
2016-10-17 18:33:35 +00:00
if ( writable ! = nullptr ) * writable = AddressWritable ;
return true ;
2016-07-31 22:32:02 +00:00
}
2016-10-24 15:18:20 +00:00
*/
2016-07-31 22:32:02 +00:00
FxExpression * FxAssign : : Resolve ( FCompileContext & ctx )
{
CHECKRESOLVED ( ) ;
SAFE_RESOLVE ( Base , ctx ) ;
ValueType = Base - > ValueType ;
2016-08-01 01:32:02 +00:00
SAFE_RESOLVE ( Right , ctx ) ;
2016-10-24 15:18:20 +00:00
if ( IsModifyAssign & & Base - > ValueType = = TypeBool & & Right - > ValueType ! = TypeBool )
{
// If the modify operation resulted in a type promotion from bool to int, this must be blocked.
// (this means, for bool, only &=, ^= and |= are allowed, although DECORATE is more lax.)
ScriptPosition . Message ( MSG_ERROR , " Invalid modify/assign operation with a boolean operand " ) ;
delete this ;
return nullptr ;
}
2016-10-21 17:18:39 +00:00
if ( Base - > IsNumeric ( ) & & Right - > IsNumeric ( ) )
2016-07-31 22:32:02 +00:00
{
2016-10-21 17:18:39 +00:00
if ( Right - > ValueType ! = ValueType )
{
if ( ValueType = = TypeBool )
{
Right = new FxBoolCast ( Right ) ;
}
else if ( ValueType - > GetRegType ( ) = = REGT_INT )
{
Right = new FxIntCast ( Right , ctx . FromDecorate ) ;
}
else
{
Right = new FxFloatCast ( Right ) ;
}
SAFE_RESOLVE ( Right , ctx ) ;
}
}
else if ( Base - > ValueType = = Right - > ValueType )
{
if ( Base - > ValueType - > IsKindOf ( RUNTIME_CLASS ( PArray ) ) )
{
ScriptPosition . Message ( MSG_ERROR , " Cannot assign arrays " ) ;
delete this ;
return nullptr ;
}
if ( Base - > ValueType - > IsKindOf ( RUNTIME_CLASS ( PStruct ) ) )
{
ScriptPosition . Message ( MSG_ERROR , " Struct assignment not implemented yet " ) ;
delete this ;
return nullptr ;
}
// Both types are the same so this is ok.
}
else if ( ( Base - > ValueType = = TypeState | | Base - > ValueType - > IsKindOf ( RUNTIME_CLASS ( PPointer ) ) ) & & Right - > ValueType = = TypeNullPtr )
{
// null pointers can be assigned to any other pointer
}
else if ( Base - > ValueType - > IsKindOf ( RUNTIME_CLASS ( PClassPointer ) ) )
{
// class pointers may be assignable so add a cast which performs a check.
Right = new FxClassTypeCast ( static_cast < PClassPointer * > ( ValueType ) , Right ) ;
SAFE_RESOLVE ( Right , ctx ) ;
}
else if ( Base - > ValueType = = TypeString & & ( Right - > ValueType = = TypeName | | Right - > ValueType = = TypeSound ) )
{
Right = new FxStringCast ( Right ) ;
SAFE_RESOLVE ( Right , ctx ) ;
}
else if ( Base - > ValueType = = TypeName & & Right - > ValueType = = TypeString )
{
Right = new FxNameCast ( Right ) ;
SAFE_RESOLVE ( Right , ctx ) ;
}
else
{
ScriptPosition . Message ( MSG_ERROR , " Assignment between incompatible types. " ) ;
2016-07-31 22:32:02 +00:00
delete this ;
return nullptr ;
}
2016-10-17 18:33:35 +00:00
if ( ! Base - > RequestAddress ( & AddressWritable ) | | ! AddressWritable )
2016-07-31 22:32:02 +00:00
{
ScriptPosition . Message ( MSG_ERROR , " Expression must be a modifiable value " ) ;
delete this ;
return nullptr ;
}
2016-10-24 15:18:20 +00:00
// Special case: Assignment to a bitfield.
if ( Base - > ExprType = = EFX_StructMember | | Base - > ExprType = = EFX_ClassMember )
{
auto f = static_cast < FxStructMember * > ( Base ) - > membervar ;
if ( f - > BitValue ! = - 1 & & ! ( f - > Flags & VARF_ReadOnly ) )
{
IsBitWrite = f - > BitValue ;
return this ;
}
}
2016-07-31 22:32:02 +00:00
return this ;
}
ExpEmit FxAssign : : Emit ( VMFunctionBuilder * build )
{
2016-10-21 08:09:01 +00:00
static const BYTE loadops [ ] = { OP_LK , OP_LKF , OP_LKS , OP_LKP } ;
2016-10-21 17:18:39 +00:00
assert ( ValueType = = Base - > ValueType ) ;
2016-07-31 22:32:02 +00:00
assert ( ValueType - > GetRegType ( ) = = Right - > ValueType - > GetRegType ( ) ) ;
2016-10-21 08:09:01 +00:00
ExpEmit pointer = Base - > Emit ( build ) ;
Address = pointer ;
2016-10-21 12:18:31 +00:00
ExpEmit result = Right - > Emit ( build ) ;
assert ( result . RegType < = REGT_TYPE ) ;
2016-10-20 23:12:54 +00:00
if ( pointer . Target )
2016-07-31 22:32:02 +00:00
{
2016-10-20 23:12:54 +00:00
if ( result . Konst )
{
build - > Emit ( loadops [ result . RegType ] , pointer . RegNum , result . RegNum ) ;
}
else
{
build - > Emit ( Right - > ValueType - > GetMoveOp ( ) , pointer . RegNum , result . RegNum ) ;
}
2016-07-31 22:32:02 +00:00
}
2016-10-20 23:12:54 +00:00
else
{
if ( result . Konst )
{
ExpEmit temp ( build , result . RegType ) ;
build - > Emit ( loadops [ result . RegType ] , temp . RegNum , result . RegNum ) ;
result . Free ( build ) ;
result = temp ;
}
2016-07-31 22:32:02 +00:00
2016-10-24 15:18:20 +00:00
if ( IsBitWrite = = - 1 )
{
build - > Emit ( ValueType - > GetStoreOp ( ) , pointer . RegNum , result . RegNum , build - > GetConstantInt ( 0 ) ) ;
}
else
{
build - > Emit ( OP_SBIT , pointer . RegNum , result . RegNum , 1 < < IsBitWrite ) ;
}
2016-10-20 23:12:54 +00:00
}
2016-07-31 22:32:02 +00:00
if ( AddressRequested )
{
result . Free ( build ) ;
return pointer ;
}
pointer . Free ( build ) ;
return result ;
}
2016-08-01 01:32:02 +00:00
//==========================================================================
//
// FxAssignSelf
//
//==========================================================================
FxAssignSelf : : FxAssignSelf ( const FScriptPosition & pos )
2016-10-22 23:14:49 +00:00
: FxExpression ( EFX_AssignSelf , pos )
2016-08-01 01:32:02 +00:00
{
Assignment = nullptr ;
}
FxExpression * FxAssignSelf : : Resolve ( FCompileContext & ctx )
{
CHECKRESOLVED ( ) ;
// This should never happen if FxAssignSelf is used correctly
assert ( Assignment ! = nullptr ) ;
ValueType = Assignment - > ValueType ;
return this ;
}
ExpEmit FxAssignSelf : : Emit ( VMFunctionBuilder * build )
{
2016-10-24 15:18:20 +00:00
assert ( ValueType = = Assignment - > ValueType ) ;
2016-08-01 01:32:02 +00:00
ExpEmit pointer = Assignment - > Address ; // FxAssign should have already emitted it
2016-10-20 23:12:54 +00:00
if ( ! pointer . Target )
{
ExpEmit out ( build , ValueType - > GetRegType ( ) ) ;
2016-10-24 15:18:20 +00:00
if ( Assignment - > IsBitWrite ! = - 1 )
{
build - > Emit ( OP_LBIT , out . RegNum , pointer . RegNum , 1 < < Assignment - > IsBitWrite ) ;
}
else
{
build - > Emit ( ValueType - > GetLoadOp ( ) , out . RegNum , pointer . RegNum , build - > GetConstantInt ( 0 ) ) ;
}
2016-10-20 23:12:54 +00:00
return out ;
}
else
{
return pointer ;
}
2016-08-01 01:32:02 +00:00
}
2016-03-01 15:47:10 +00:00
//==========================================================================
//
//
//
//==========================================================================
FxBinary : : FxBinary ( int o , FxExpression * l , FxExpression * r )
2016-10-22 23:14:49 +00:00
: FxExpression ( EFX_Binary , l - > ScriptPosition )
2016-03-01 15:47:10 +00:00
{
Operator = o ;
left = l ;
right = r ;
}
//==========================================================================
//
//
//
//==========================================================================
FxBinary : : ~ FxBinary ( )
{
SAFE_DELETE ( left ) ;
SAFE_DELETE ( right ) ;
}
//==========================================================================
//
//
//
//==========================================================================
bool FxBinary : : ResolveLR ( FCompileContext & ctx , bool castnumeric )
{
RESOLVE ( left , ctx ) ;
RESOLVE ( right , ctx ) ;
if ( ! left | | ! right )
{
delete this ;
return false ;
}
2016-07-04 00:19:52 +00:00
if ( left - > ValueType = = TypeBool & & right - > ValueType = = TypeBool )
{
2016-10-24 15:18:20 +00:00
if ( Operator = = ' & ' | | Operator = = ' | ' | | Operator = = ' ^ ' | | ctx . FromDecorate )
{
ValueType = TypeBool ;
}
else
{
ValueType = TypeSInt32 ; // math operations on bools result in integers.
}
2016-07-04 00:19:52 +00:00
}
2016-10-18 22:35:34 +00:00
else if ( left - > ValueType = = TypeName & & right - > ValueType = = TypeName )
2016-03-01 15:47:10 +00:00
{
2016-10-24 15:18:20 +00:00
// pointers can only be compared for equality.
if ( Operator = = TK_Eq | | Operator = = TK_Neq )
{
ValueType = TypeBool ;
return true ;
}
else
{
ScriptPosition . Message ( MSG_ERROR , " Invalid operation for names " ) ;
delete this ;
return false ;
}
2016-03-01 15:47:10 +00:00
}
2016-10-18 22:35:34 +00:00
else if ( left - > IsNumeric ( ) & & right - > IsNumeric ( ) )
2016-03-01 15:47:10 +00:00
{
2016-10-18 22:35:34 +00:00
if ( left - > ValueType - > GetRegType ( ) = = REGT_INT & & right - > ValueType - > GetRegType ( ) = = REGT_INT )
{
ValueType = TypeSInt32 ;
}
else
{
ValueType = TypeFloat64 ;
}
2016-03-01 15:47:10 +00:00
}
2016-10-21 22:50:04 +00:00
else if ( left - > ValueType - > GetRegType ( ) = = REGT_POINTER )
2016-03-01 15:47:10 +00:00
{
2016-10-21 22:50:04 +00:00
if ( left - > ValueType = = right - > ValueType | | right - > ValueType = = TypeNullPtr | | left - > ValueType = = TypeNullPtr | |
AreCompatiblePointerTypes ( left - > ValueType , right - > ValueType ) )
{
// pointers can only be compared for equality.
2016-10-24 15:18:20 +00:00
if ( Operator = = TK_Eq | | Operator = = TK_Neq )
{
ValueType = TypeBool ;
return true ;
}
else
{
ScriptPosition . Message ( MSG_ERROR , " Invalid operation for pointers " ) ;
delete this ;
return false ;
}
2016-10-21 22:50:04 +00:00
}
2016-03-01 15:47:10 +00:00
}
else
{
2016-10-24 15:18:20 +00:00
// To check: It may be that this could pass in DECORATE, although setting TypeVoid here would pretty much prevent that.
ScriptPosition . Message ( MSG_ERROR , " Incompatible operator " ) ;
delete this ;
return false ;
2016-03-01 15:47:10 +00:00
}
2016-10-21 22:50:04 +00:00
assert ( ValueType > nullptr & & ValueType < ( PType * ) 0xfffffffffffffff ) ;
2016-03-01 15:47:10 +00:00
if ( castnumeric )
{
// later!
}
return true ;
}
void FxBinary : : Promote ( FCompileContext & ctx )
{
if ( left - > ValueType - > GetRegType ( ) = = REGT_FLOAT & & right - > ValueType - > GetRegType ( ) = = REGT_INT )
{
right = ( new FxFloatCast ( right ) ) - > Resolve ( ctx ) ;
}
else if ( left - > ValueType - > GetRegType ( ) = = REGT_INT & & right - > ValueType - > GetRegType ( ) = = REGT_FLOAT )
{
left = ( new FxFloatCast ( left ) ) - > Resolve ( ctx ) ;
}
}
//==========================================================================
//
//
//
//==========================================================================
FxAddSub : : FxAddSub ( int o , FxExpression * l , FxExpression * r )
: FxBinary ( o , l , r )
{
}
//==========================================================================
//
//
//
//==========================================================================
FxExpression * FxAddSub : : Resolve ( FCompileContext & ctx )
{
CHECKRESOLVED ( ) ;
if ( ! ResolveLR ( ctx , true ) ) return NULL ;
if ( ! IsNumeric ( ) )
{
ScriptPosition . Message ( MSG_ERROR , " Numeric type expected " ) ;
delete this ;
2016-10-24 15:18:20 +00:00
return nullptr ;
2016-03-01 15:47:10 +00:00
}
else if ( left - > isConstant ( ) & & right - > isConstant ( ) )
{
if ( ValueType - > GetRegType ( ) = = REGT_FLOAT )
{
double v ;
double v1 = static_cast < FxConstant * > ( left ) - > GetValue ( ) . GetFloat ( ) ;
double v2 = static_cast < FxConstant * > ( right ) - > GetValue ( ) . GetFloat ( ) ;
v = Operator = = ' + ' ? v1 + v2 :
Operator = = ' - ' ? v1 - v2 : 0 ;
FxExpression * e = new FxConstant ( v , ScriptPosition ) ;
delete this ;
return e ;
}
else
{
int v ;
int v1 = static_cast < FxConstant * > ( left ) - > GetValue ( ) . GetInt ( ) ;
int v2 = static_cast < FxConstant * > ( right ) - > GetValue ( ) . GetInt ( ) ;
v = Operator = = ' + ' ? v1 + v2 :
Operator = = ' - ' ? v1 - v2 : 0 ;
FxExpression * e = new FxConstant ( v , ScriptPosition ) ;
delete this ;
return e ;
}
}
Promote ( ctx ) ;
return this ;
}
//==========================================================================
//
//
//
//==========================================================================
ExpEmit FxAddSub : : Emit ( VMFunctionBuilder * build )
{
assert ( Operator = = ' + ' | | Operator = = ' - ' ) ;
ExpEmit op1 = left - > Emit ( build ) ;
ExpEmit op2 = right - > Emit ( build ) ;
if ( Operator = = ' + ' )
{
// Since addition is commutative, only the second operand may be a constant.
if ( op1 . Konst )
{
swapvalues ( op1 , op2 ) ;
}
assert ( ! op1 . Konst ) ;
op1 . Free ( build ) ;
op2 . Free ( build ) ;
if ( ValueType - > GetRegType ( ) = = REGT_FLOAT )
{
assert ( op1 . RegType = = REGT_FLOAT & & op2 . RegType = = REGT_FLOAT ) ;
ExpEmit to ( build , REGT_FLOAT ) ;
build - > Emit ( op2 . Konst ? OP_ADDF_RK : OP_ADDF_RR , to . RegNum , op1 . RegNum , op2 . RegNum ) ;
return to ;
}
else
{
assert ( ValueType - > GetRegType ( ) = = REGT_INT ) ;
assert ( op1 . RegType = = REGT_INT & & op2 . RegType = = REGT_INT ) ;
ExpEmit to ( build , REGT_INT ) ;
build - > Emit ( op2 . Konst ? OP_ADD_RK : OP_ADD_RR , to . RegNum , op1 . RegNum , op2 . RegNum ) ;
return to ;
}
}
else
{
// Subtraction is not commutative, so either side may be constant (but not both).
assert ( ! op1 . Konst | | ! op2 . Konst ) ;
op1 . Free ( build ) ;
op2 . Free ( build ) ;
if ( ValueType - > GetRegType ( ) = = REGT_FLOAT )
{
assert ( op1 . RegType = = REGT_FLOAT & & op2 . RegType = = REGT_FLOAT ) ;
ExpEmit to ( build , REGT_FLOAT ) ;
build - > Emit ( op1 . Konst ? OP_SUBF_KR : op2 . Konst ? OP_SUBF_RK : OP_SUBF_RR ,
to . RegNum , op1 . RegNum , op2 . RegNum ) ;
return to ;
}
else
{
assert ( ValueType - > GetRegType ( ) = = REGT_INT ) ;
assert ( op1 . RegType = = REGT_INT & & op2 . RegType = = REGT_INT ) ;
ExpEmit to ( build , REGT_INT ) ;
build - > Emit ( op1 . Konst ? OP_SUB_KR : op2 . Konst ? OP_SUB_RK : OP_SUB_RR ,
to . RegNum , op1 . RegNum , op2 . RegNum ) ;
return to ;
}
}
}
//==========================================================================
//
//
//
//==========================================================================
FxMulDiv : : FxMulDiv ( int o , FxExpression * l , FxExpression * r )
: FxBinary ( o , l , r )
{
}
//==========================================================================
//
//
//
//==========================================================================
FxExpression * FxMulDiv : : Resolve ( FCompileContext & ctx )
{
CHECKRESOLVED ( ) ;
if ( ! ResolveLR ( ctx , true ) ) return NULL ;
if ( ! IsNumeric ( ) )
{
ScriptPosition . Message ( MSG_ERROR , " Numeric type expected " ) ;
delete this ;
return NULL ;
}
else if ( left - > isConstant ( ) & & right - > isConstant ( ) )
{
if ( ValueType - > GetRegType ( ) = = REGT_FLOAT )
{
double v ;
double v1 = static_cast < FxConstant * > ( left ) - > GetValue ( ) . GetFloat ( ) ;
double v2 = static_cast < FxConstant * > ( right ) - > GetValue ( ) . GetFloat ( ) ;
if ( Operator ! = ' * ' & & v2 = = 0 )
{
ScriptPosition . Message ( MSG_ERROR , " Division by 0 " ) ;
delete this ;
return NULL ;
}
v = Operator = = ' * ' ? v1 * v2 :
Operator = = ' / ' ? v1 / v2 :
Operator = = ' % ' ? fmod ( v1 , v2 ) : 0 ;
FxExpression * e = new FxConstant ( v , ScriptPosition ) ;
delete this ;
return e ;
}
else
{
int v ;
int v1 = static_cast < FxConstant * > ( left ) - > GetValue ( ) . GetInt ( ) ;
int v2 = static_cast < FxConstant * > ( right ) - > GetValue ( ) . GetInt ( ) ;
if ( Operator ! = ' * ' & & v2 = = 0 )
{
ScriptPosition . Message ( MSG_ERROR , " Division by 0 " ) ;
delete this ;
return NULL ;
}
v = Operator = = ' * ' ? v1 * v2 :
Operator = = ' / ' ? v1 / v2 :
Operator = = ' % ' ? v1 % v2 : 0 ;
FxExpression * e = new FxConstant ( v , ScriptPosition ) ;
delete this ;
return e ;
}
}
Promote ( ctx ) ;
return this ;
}
//==========================================================================
//
//
//
//==========================================================================
ExpEmit FxMulDiv : : Emit ( VMFunctionBuilder * build )
{
ExpEmit op1 = left - > Emit ( build ) ;
ExpEmit op2 = right - > Emit ( build ) ;
if ( Operator = = ' * ' )
{
// Multiplication is commutative, so only the second operand may be constant.
if ( op1 . Konst )
{
swapvalues ( op1 , op2 ) ;
}
assert ( ! op1 . Konst ) ;
op1 . Free ( build ) ;
op2 . Free ( build ) ;
if ( ValueType - > GetRegType ( ) = = REGT_FLOAT )
{
assert ( op1 . RegType = = REGT_FLOAT & & op2 . RegType = = REGT_FLOAT ) ;
ExpEmit to ( build , REGT_FLOAT ) ;
build - > Emit ( op2 . Konst ? OP_MULF_RK : OP_MULF_RR , to . RegNum , op1 . RegNum , op2 . RegNum ) ;
return to ;
}
else
{
assert ( ValueType - > GetRegType ( ) = = REGT_INT ) ;
assert ( op1 . RegType = = REGT_INT & & op2 . RegType = = REGT_INT ) ;
ExpEmit to ( build , REGT_INT ) ;
build - > Emit ( op2 . Konst ? OP_MUL_RK : OP_MUL_RR , to . RegNum , op1 . RegNum , op2 . RegNum ) ;
return to ;
}
}
else
{
// Division is not commutative, so either side may be constant (but not both).
assert ( ! op1 . Konst | | ! op2 . Konst ) ;
assert ( Operator = = ' % ' | | Operator = = ' / ' ) ;
op1 . Free ( build ) ;
op2 . Free ( build ) ;
if ( ValueType - > GetRegType ( ) = = REGT_FLOAT )
{
assert ( op1 . RegType = = REGT_FLOAT & & op2 . RegType = = REGT_FLOAT ) ;
ExpEmit to ( build , REGT_FLOAT ) ;
build - > Emit ( Operator = = ' / ' ? ( op1 . Konst ? OP_DIVF_KR : op2 . Konst ? OP_DIVF_RK : OP_DIVF_RR )
: ( op1 . Konst ? OP_MODF_KR : op2 . Konst ? OP_MODF_RK : OP_MODF_RR ) ,
to . RegNum , op1 . RegNum , op2 . RegNum ) ;
return to ;
}
else
{
assert ( ValueType - > GetRegType ( ) = = REGT_INT ) ;
assert ( op1 . RegType = = REGT_INT & & op2 . RegType = = REGT_INT ) ;
ExpEmit to ( build , REGT_INT ) ;
build - > Emit ( Operator = = ' / ' ? ( op1 . Konst ? OP_DIV_KR : op2 . Konst ? OP_DIV_RK : OP_DIV_RR )
: ( op1 . Konst ? OP_MOD_KR : op2 . Konst ? OP_MOD_RK : OP_MOD_RR ) ,
to . RegNum , op1 . RegNum , op2 . RegNum ) ;
return to ;
}
}
}
//==========================================================================
//
//
//
//==========================================================================
2016-10-17 13:17:48 +00:00
FxPow : : FxPow ( FxExpression * l , FxExpression * r )
: FxBinary ( TK_MulMul , new FxFloatCast ( l ) , new FxFloatCast ( r ) )
{
}
//==========================================================================
//
//
//
//==========================================================================
FxExpression * FxPow : : Resolve ( FCompileContext & ctx )
{
CHECKRESOLVED ( ) ;
2016-10-21 17:18:39 +00:00
if ( ! ResolveLR ( ctx , true ) ) return nullptr ;
2016-10-17 13:17:48 +00:00
2016-10-21 17:18:39 +00:00
if ( ! IsNumeric ( ) )
{
ScriptPosition . Message ( MSG_ERROR , " Numeric type expected " ) ;
delete this ;
return nullptr ;
}
2016-10-17 13:17:48 +00:00
if ( left - > isConstant ( ) & & right - > isConstant ( ) )
{
double v1 = static_cast < FxConstant * > ( left ) - > GetValue ( ) . GetFloat ( ) ;
double v2 = static_cast < FxConstant * > ( right ) - > GetValue ( ) . GetFloat ( ) ;
return new FxConstant ( g_pow ( v1 , v2 ) , left - > ScriptPosition ) ;
}
return this ;
}
//==========================================================================
//
//
//
//==========================================================================
ExpEmit FxPow : : Emit ( VMFunctionBuilder * build )
{
ExpEmit op1 = left - > Emit ( build ) ;
ExpEmit op2 = right - > Emit ( build ) ;
// Pow is not commutative, so either side may be constant (but not both).
assert ( ! op1 . Konst | | ! op2 . Konst ) ;
op1 . Free ( build ) ;
op2 . Free ( build ) ;
assert ( op1 . RegType = = REGT_FLOAT & & op2 . RegType = = REGT_FLOAT ) ;
ExpEmit to ( build , REGT_FLOAT ) ;
build - > Emit ( ( op1 . Konst ? OP_POWF_KR : op2 . Konst ? OP_POWF_RK : OP_POWF_RR ) , to . RegNum , op1 . RegNum , op2 . RegNum ) ;
return to ;
}
//==========================================================================
//
//
//
//==========================================================================
2016-03-01 15:47:10 +00:00
FxCompareRel : : FxCompareRel ( int o , FxExpression * l , FxExpression * r )
: FxBinary ( o , l , r )
{
}
//==========================================================================
//
//
//
//==========================================================================
FxExpression * FxCompareRel : : Resolve ( FCompileContext & ctx )
{
CHECKRESOLVED ( ) ;
if ( ! ResolveLR ( ctx , true ) ) return NULL ;
if ( ! IsNumeric ( ) )
{
ScriptPosition . Message ( MSG_ERROR , " Numeric type expected " ) ;
delete this ;
return NULL ;
}
else if ( left - > isConstant ( ) & & right - > isConstant ( ) )
{
int v ;
if ( ValueType - > GetRegType ( ) = = REGT_FLOAT )
{
double v1 = static_cast < FxConstant * > ( left ) - > GetValue ( ) . GetFloat ( ) ;
double v2 = static_cast < FxConstant * > ( right ) - > GetValue ( ) . GetFloat ( ) ;
v = Operator = = ' < ' ? v1 < v2 :
Operator = = ' > ' ? v1 > v2 :
Operator = = TK_Geq ? v1 > = v2 :
Operator = = TK_Leq ? v1 < = v2 : 0 ;
}
else
{
int v1 = static_cast < FxConstant * > ( left ) - > GetValue ( ) . GetInt ( ) ;
int v2 = static_cast < FxConstant * > ( right ) - > GetValue ( ) . GetInt ( ) ;
v = Operator = = ' < ' ? v1 < v2 :
Operator = = ' > ' ? v1 > v2 :
Operator = = TK_Geq ? v1 > = v2 :
Operator = = TK_Leq ? v1 < = v2 : 0 ;
}
FxExpression * e = new FxConstant ( v , ScriptPosition ) ;
delete this ;
return e ;
}
Promote ( ctx ) ;
2016-07-04 00:19:52 +00:00
ValueType = TypeBool ;
2016-03-01 15:47:10 +00:00
return this ;
}
//==========================================================================
//
//
//
//==========================================================================
ExpEmit FxCompareRel : : Emit ( VMFunctionBuilder * build )
{
ExpEmit op1 = left - > Emit ( build ) ;
ExpEmit op2 = right - > Emit ( build ) ;
assert ( op1 . RegType = = op2 . RegType ) ;
assert ( op1 . RegType = = REGT_INT | | op1 . RegType = = REGT_FLOAT ) ;
assert ( ! op1 . Konst | | ! op2 . Konst ) ;
assert ( Operator = = ' < ' | | Operator = = ' > ' | | Operator = = TK_Geq | | Operator = = TK_Leq ) ;
static const VM_UBYTE InstrMap [ ] [ 4 ] =
{
{ OP_LT_RR , OP_LTF_RR , 0 } , // <
{ OP_LE_RR , OP_LEF_RR , 1 } , // >
{ OP_LT_RR , OP_LTF_RR , 1 } , // >=
{ OP_LE_RR , OP_LEF_RR , 0 } // <=
} ;
int instr , check , index ;
ExpEmit to ( build , REGT_INT ) ;
index = Operator = = ' < ' ? 0 :
Operator = = ' > ' ? 1 :
Operator = = TK_Geq ? 2 : 3 ;
instr = InstrMap [ index ] [ op1 . RegType = = REGT_INT ? 0 : 1 ] ;
check = InstrMap [ index ] [ 2 ] ;
if ( op2 . Konst )
{
instr + = 1 ;
}
else
{
op2 . Free ( build ) ;
}
if ( op1 . Konst )
{
instr + = 2 ;
}
else
{
op1 . Free ( build ) ;
}
2016-07-04 00:19:52 +00:00
// See FxBoolCast for comments, since it's the same thing.
2016-03-01 15:47:10 +00:00
build - > Emit ( OP_LI , to . RegNum , 0 , 0 ) ;
build - > Emit ( instr , check , op1 . RegNum , op2 . RegNum ) ;
build - > Emit ( OP_JMP , 1 ) ;
build - > Emit ( OP_LI , to . RegNum , 1 ) ;
return to ;
}
//==========================================================================
//
//
//
//==========================================================================
FxCompareEq : : FxCompareEq ( int o , FxExpression * l , FxExpression * r )
: FxBinary ( o , l , r )
{
}
//==========================================================================
//
//
//
//==========================================================================
FxExpression * FxCompareEq : : Resolve ( FCompileContext & ctx )
{
CHECKRESOLVED ( ) ;
if ( ! ResolveLR ( ctx , true ) ) return NULL ;
if ( ! left | | ! right )
{
delete this ;
return NULL ;
}
2016-10-18 22:35:34 +00:00
if ( ! IsNumeric ( ) & & ! IsPointer ( ) & & ValueType ! = TypeName )
2016-03-01 15:47:10 +00:00
{
ScriptPosition . Message ( MSG_ERROR , " Numeric type expected " ) ;
delete this ;
return NULL ;
}
2016-10-22 07:31:37 +00:00
if ( Operator = = TK_ApproxEq & & ValueType - > GetRegType ( ) ! = REGT_FLOAT ) Operator = TK_Eq ;
2016-03-01 15:47:10 +00:00
if ( left - > isConstant ( ) & & right - > isConstant ( ) )
{
int v ;
if ( ValueType - > GetRegType ( ) = = REGT_FLOAT )
{
double v1 = static_cast < FxConstant * > ( left ) - > GetValue ( ) . GetFloat ( ) ;
double v2 = static_cast < FxConstant * > ( right ) - > GetValue ( ) . GetFloat ( ) ;
2016-10-22 07:31:37 +00:00
v = Operator = = TK_Eq ? v1 = = v2 : Operator = = TK_Neq ? v1 ! = v2 : fabs ( v1 - v2 ) < VM_EPSILON ;
2016-03-01 15:47:10 +00:00
}
else
{
int v1 = static_cast < FxConstant * > ( left ) - > GetValue ( ) . GetInt ( ) ;
int v2 = static_cast < FxConstant * > ( right ) - > GetValue ( ) . GetInt ( ) ;
v = Operator = = TK_Eq ? v1 = = v2 : v1 ! = v2 ;
}
FxExpression * e = new FxConstant ( v , ScriptPosition ) ;
delete this ;
return e ;
}
Promote ( ctx ) ;
2016-07-04 00:19:52 +00:00
ValueType = TypeBool ;
2016-03-01 15:47:10 +00:00
return this ;
}
//==========================================================================
//
//
//
//==========================================================================
ExpEmit FxCompareEq : : Emit ( VMFunctionBuilder * build )
{
ExpEmit op1 = left - > Emit ( build ) ;
ExpEmit op2 = right - > Emit ( build ) ;
assert ( op1 . RegType = = op2 . RegType ) ;
assert ( op1 . RegType = = REGT_INT | | op1 . RegType = = REGT_FLOAT | | op1 . RegType = = REGT_POINTER ) ;
int instr ;
// Only the second operand may be constant.
if ( op1 . Konst )
{
swapvalues ( op1 , op2 ) ;
}
assert ( ! op1 . Konst ) ;
ExpEmit to ( build , REGT_INT ) ;
instr = op1 . RegType = = REGT_INT ? OP_EQ_R :
op1 . RegType = = REGT_FLOAT ? OP_EQF_R :
OP_EQA_R ;
op1 . Free ( build ) ;
if ( ! op2 . Konst )
{
op2 . Free ( build ) ;
}
else
{
instr + = 1 ;
}
// See FxUnaryNotBoolean for comments, since it's the same thing.
build - > Emit ( OP_LI , to . RegNum , 0 , 0 ) ;
2016-10-22 07:31:37 +00:00
build - > Emit ( instr , Operator = = TK_ApproxEq ? CMP_APPROX : Operator ! = TK_Eq , op1 . RegNum , op2 . RegNum ) ;
2016-03-01 15:47:10 +00:00
build - > Emit ( OP_JMP , 1 ) ;
build - > Emit ( OP_LI , to . RegNum , 1 ) ;
return to ;
}
//==========================================================================
//
//
//
//==========================================================================
FxBinaryInt : : FxBinaryInt ( int o , FxExpression * l , FxExpression * r )
: FxBinary ( o , l , r )
{
ValueType = TypeSInt32 ;
}
//==========================================================================
//
//
//
//==========================================================================
FxExpression * FxBinaryInt : : Resolve ( FCompileContext & ctx )
{
CHECKRESOLVED ( ) ;
if ( ! ResolveLR ( ctx , false ) ) return NULL ;
2016-10-17 13:52:29 +00:00
if ( ValueType - > GetRegType ( ) = = REGT_FLOAT & & ctx . FromDecorate )
2016-03-01 15:47:10 +00:00
{
2016-10-17 13:52:29 +00:00
// For DECORATE which allows floats here. ZScript does not.
2016-03-01 15:47:10 +00:00
if ( left - > ValueType - > GetRegType ( ) ! = REGT_INT )
{
2016-10-15 19:35:31 +00:00
left = new FxIntCast ( left , ctx . FromDecorate ) ;
2016-03-01 15:47:10 +00:00
left = left - > Resolve ( ctx ) ;
}
if ( right - > ValueType - > GetRegType ( ) ! = REGT_INT )
{
2016-10-15 19:35:31 +00:00
right = new FxIntCast ( right , ctx . FromDecorate ) ;
2016-03-01 15:47:10 +00:00
right = right - > Resolve ( ctx ) ;
}
if ( left = = NULL | | right = = NULL )
{
delete this ;
return NULL ;
}
ValueType = TypeSInt32 ;
}
if ( ValueType - > GetRegType ( ) ! = REGT_INT )
{
ScriptPosition . Message ( MSG_ERROR , " Integer type expected " ) ;
delete this ;
return NULL ;
}
else if ( left - > isConstant ( ) & & right - > isConstant ( ) )
{
int v1 = static_cast < FxConstant * > ( left ) - > GetValue ( ) . GetInt ( ) ;
int v2 = static_cast < FxConstant * > ( right ) - > GetValue ( ) . GetInt ( ) ;
FxExpression * e = new FxConstant (
Operator = = TK_LShift ? v1 < < v2 :
Operator = = TK_RShift ? v1 > > v2 :
Operator = = TK_URShift ? int ( ( unsigned int ) ( v1 ) > > v2 ) :
Operator = = ' & ' ? v1 & v2 :
Operator = = ' | ' ? v1 | v2 :
Operator = = ' ^ ' ? v1 ^ v2 : 0 , ScriptPosition ) ;
delete this ;
return e ;
}
return this ;
}
//==========================================================================
//
//
//
//==========================================================================
ExpEmit FxBinaryInt : : Emit ( VMFunctionBuilder * build )
{
assert ( left - > ValueType - > GetRegType ( ) = = REGT_INT ) ;
assert ( right - > ValueType - > GetRegType ( ) = = REGT_INT ) ;
static const VM_UBYTE InstrMap [ ] [ 4 ] =
{
{ OP_SLL_RR , OP_SLL_KR , OP_SLL_RI } , // TK_LShift
{ OP_SRA_RR , OP_SRA_KR , OP_SRA_RI } , // TK_RShift
{ OP_SRL_RR , OP_SRL_KR , OP_SRL_RI } , // TK_URShift
{ OP_AND_RR , 0 , OP_AND_RK } , // '&'
{ OP_OR_RR , 0 , OP_OR_RK } , // '|'
{ OP_XOR_RR , 0 , OP_XOR_RK } , // '^'
} ;
int index , instr , rop ;
ExpEmit op1 , op2 ;
index = Operator = = TK_LShift ? 0 :
Operator = = TK_RShift ? 1 :
Operator = = TK_URShift ? 2 :
Operator = = ' & ' ? 3 :
Operator = = ' | ' ? 4 :
Operator = = ' ^ ' ? 5 : - 1 ;
assert ( index > = 0 ) ;
op1 = left - > Emit ( build ) ;
if ( index < 3 )
{ // Shift instructions use right-hand immediates instead of constant registers.
if ( right - > isConstant ( ) )
{
rop = static_cast < FxConstant * > ( right ) - > GetValue ( ) . GetInt ( ) ;
op2 . Konst = true ;
}
else
{
op2 = right - > Emit ( build ) ;
assert ( ! op2 . Konst ) ;
op2 . Free ( build ) ;
rop = op2 . RegNum ;
}
}
else
{ // The other operators only take a constant on the right-hand side.
op2 = right - > Emit ( build ) ;
if ( op1 . Konst )
{
swapvalues ( op1 , op2 ) ;
}
assert ( ! op1 . Konst ) ;
rop = op2 . RegNum ;
op2 . Free ( build ) ;
}
if ( ! op1 . Konst )
{
op1 . Free ( build ) ;
if ( ! op2 . Konst )
{
instr = InstrMap [ index ] [ 0 ] ;
}
else
{
instr = InstrMap [ index ] [ 2 ] ;
}
}
else
{
assert ( ! op2 . Konst ) ;
instr = InstrMap [ index ] [ 1 ] ;
}
assert ( instr ! = 0 ) ;
ExpEmit to ( build , REGT_INT ) ;
build - > Emit ( instr , to . RegNum , op1 . RegNum , rop ) ;
return to ;
}
//==========================================================================
//
//
//
//==========================================================================
2016-10-21 14:35:07 +00:00
FxLtGtEq : : FxLtGtEq ( FxExpression * l , FxExpression * r )
: FxBinary ( TK_LtGtEq , l , r )
{
ValueType = TypeSInt32 ;
}
//==========================================================================
//
//
//
//==========================================================================
FxExpression * FxLtGtEq : : Resolve ( FCompileContext & ctx )
{
CHECKRESOLVED ( ) ;
2016-10-24 15:18:20 +00:00
if ( ! ResolveLR ( ctx , true ) ) return NULL ;
2016-10-21 14:35:07 +00:00
if ( ! left - > IsNumeric ( ) | | ! right - > IsNumeric ( ) )
{
ScriptPosition . Message ( MSG_ERROR , " <>= expects two numeric operands " ) ;
delete this ;
return nullptr ;
}
if ( left - > ValueType - > GetRegType ( ) ! = right - > ValueType - > GetRegType ( ) )
{
if ( left - > ValueType - > GetRegType ( ) = = REGT_INT )
{
left = new FxFloatCast ( left ) ;
SAFE_RESOLVE ( left , ctx ) ;
}
if ( right - > ValueType - > GetRegType ( ) = = REGT_INT )
{
right = new FxFloatCast ( left ) ;
SAFE_RESOLVE ( left , ctx ) ;
}
}
else if ( left - > isConstant ( ) & & right - > isConstant ( ) )
{
// let's cut this short and always compare doubles. For integers the result will be exactly the same as with an integer comparison, either signed or unsigned.
auto v1 = static_cast < FxConstant * > ( left ) - > GetValue ( ) . GetFloat ( ) ;
auto v2 = static_cast < FxConstant * > ( right ) - > GetValue ( ) . GetFloat ( ) ;
auto e = new FxConstant ( v1 < v2 ? - 1 : v1 > v2 ? 1 : 0 , ScriptPosition ) ;
delete this ;
return e ;
}
return this ;
}
//==========================================================================
//
//
//
//==========================================================================
ExpEmit FxLtGtEq : : Emit ( VMFunctionBuilder * build )
{
ExpEmit op1 = left - > Emit ( build ) ;
ExpEmit op2 = right - > Emit ( build ) ;
assert ( op1 . RegType = = op2 . RegType ) ;
assert ( op1 . RegType = = REGT_INT | | op1 . RegType = = REGT_FLOAT ) ;
assert ( ! op1 . Konst | | ! op2 . Konst ) ;
ExpEmit to ( build , REGT_INT ) ;
int instr = op1 . RegType = = REGT_INT ? ( left - > ValueType = = TypeUInt32 ? OP_LTU_RR : OP_LT_RR ) : OP_LTF_RR ;
if ( op1 . Konst ) instr + = 2 ;
if ( op2 . Konst ) instr + + ;
build - > Emit ( OP_LI , to . RegNum , 1 ) ; // default to 1
build - > Emit ( instr , 0 , op1 . RegNum , op2 . RegNum ) ; // if (left < right)
auto j1 = build - > Emit ( OP_JMP , 1 ) ;
build - > Emit ( OP_LI , to . RegNum , - 1 ) ; // result is -1
auto j2 = build - > Emit ( OP_JMP , 1 ) ; // jump to end
build - > BackpatchToHere ( j1 ) ;
build - > Emit ( instr + OP_LE_RR - OP_LT_RR , 0 , op1 . RegNum , op2 . RegNum ) ; // if (left == right)
auto j3 = build - > Emit ( OP_JMP , 1 ) ;
build - > Emit ( OP_LI , to . RegNum , 0 ) ; // result is 0
build - > BackpatchToHere ( j2 ) ;
build - > BackpatchToHere ( j3 ) ;
return to ;
}
//==========================================================================
//
//
//
//==========================================================================
2016-03-01 15:47:10 +00:00
FxBinaryLogical : : FxBinaryLogical ( int o , FxExpression * l , FxExpression * r )
2016-10-22 23:14:49 +00:00
: FxExpression ( EFX_BinaryLogical , l - > ScriptPosition )
2016-03-01 15:47:10 +00:00
{
Operator = o ;
left = l ;
right = r ;
2016-07-04 00:19:52 +00:00
ValueType = TypeBool ;
2016-03-01 15:47:10 +00:00
}
//==========================================================================
//
//
//
//==========================================================================
FxBinaryLogical : : ~ FxBinaryLogical ( )
{
SAFE_DELETE ( left ) ;
SAFE_DELETE ( right ) ;
}
//==========================================================================
//
//
//
//==========================================================================
FxExpression * FxBinaryLogical : : Resolve ( FCompileContext & ctx )
{
CHECKRESOLVED ( ) ;
2016-07-04 00:19:52 +00:00
RESOLVE ( left , ctx ) ;
RESOLVE ( right , ctx ) ;
ABORT ( right & & left ) ;
if ( left - > ValueType ! = TypeBool )
2016-03-01 15:47:10 +00:00
{
2016-07-04 00:19:52 +00:00
left = new FxBoolCast ( left ) ;
SAFE_RESOLVE ( left , ctx ) ;
}
if ( right - > ValueType ! = TypeBool )
{
right = new FxBoolCast ( right ) ;
SAFE_RESOLVE ( right , ctx ) ;
2016-03-01 15:47:10 +00:00
}
int b_left = - 1 , b_right = - 1 ;
if ( left - > isConstant ( ) ) b_left = static_cast < FxConstant * > ( left ) - > GetValue ( ) . GetBool ( ) ;
if ( right - > isConstant ( ) ) b_right = static_cast < FxConstant * > ( right ) - > GetValue ( ) . GetBool ( ) ;
// Do some optimizations. This will throw out all sub-expressions that are not
// needed to retrieve the final result.
if ( Operator = = TK_AndAnd )
{
if ( b_left = = 0 | | b_right = = 0 )
{
2016-07-04 00:19:52 +00:00
FxExpression * x = new FxConstant ( true , ScriptPosition ) ;
2016-03-01 15:47:10 +00:00
delete this ;
return x ;
}
else if ( b_left = = 1 & & b_right = = 1 )
{
2016-07-04 00:19:52 +00:00
FxExpression * x = new FxConstant ( false , ScriptPosition ) ;
2016-03-01 15:47:10 +00:00
delete this ;
return x ;
}
else if ( b_left = = 1 )
{
FxExpression * x = right ;
right = NULL ;
delete this ;
return x ;
}
else if ( b_right = = 1 )
{
FxExpression * x = left ;
left = NULL ;
delete this ;
return x ;
}
}
else if ( Operator = = TK_OrOr )
{
if ( b_left = = 1 | | b_right = = 1 )
{
2016-07-04 00:19:52 +00:00
FxExpression * x = new FxConstant ( true , ScriptPosition ) ;
2016-03-01 15:47:10 +00:00
delete this ;
return x ;
}
if ( b_left = = 0 & & b_right = = 0 )
{
2016-07-04 00:19:52 +00:00
FxExpression * x = new FxConstant ( false , ScriptPosition ) ;
2016-03-01 15:47:10 +00:00
delete this ;
return x ;
}
else if ( b_left = = 0 )
{
FxExpression * x = right ;
right = NULL ;
delete this ;
return x ;
}
else if ( b_right = = 0 )
{
FxExpression * x = left ;
left = NULL ;
delete this ;
return x ;
}
}
return this ;
}
//==========================================================================
//
//
//
//==========================================================================
ExpEmit FxBinaryLogical : : Emit ( VMFunctionBuilder * build )
{
// This is not the "right" way to do these, but it works for now.
// (Problem: No information sharing is done between nodes to reduce the
// code size if you have something like a1 && a2 && a3 && ... && an.)
assert ( left - > ValueType - > GetRegType ( ) = = REGT_INT & & right - > ValueType - > GetRegType ( ) = = REGT_INT ) ;
ExpEmit op1 = left - > Emit ( build ) ;
assert ( ! op1 . Konst ) ;
int zero = build - > GetConstantInt ( 0 ) ;
op1 . Free ( build ) ;
if ( Operator = = TK_AndAnd )
{
build - > Emit ( OP_EQ_K , 1 , op1 . RegNum , zero ) ;
// If op1 is 0, skip evaluation of op2.
size_t patchspot = build - > Emit ( OP_JMP , 0 , 0 , 0 ) ;
// Evaluate op2.
ExpEmit op2 = right - > Emit ( build ) ;
assert ( ! op2 . Konst ) ;
op2 . Free ( build ) ;
ExpEmit to ( build , REGT_INT ) ;
build - > Emit ( OP_EQ_K , 1 , op2 . RegNum , zero ) ;
build - > Emit ( OP_JMP , 2 ) ;
build - > Emit ( OP_LI , to . RegNum , 1 ) ;
build - > Emit ( OP_JMP , 1 ) ;
size_t target = build - > Emit ( OP_LI , to . RegNum , 0 ) ;
build - > Backpatch ( patchspot , target ) ;
return to ;
}
else
{
assert ( Operator = = TK_OrOr ) ;
build - > Emit ( OP_EQ_K , 0 , op1 . RegNum , zero ) ;
// If op1 is not 0, skip evaluation of op2.
size_t patchspot = build - > Emit ( OP_JMP , 0 , 0 , 0 ) ;
// Evaluate op2.
ExpEmit op2 = right - > Emit ( build ) ;
assert ( ! op2 . Konst ) ;
op2 . Free ( build ) ;
ExpEmit to ( build , REGT_INT ) ;
build - > Emit ( OP_EQ_K , 0 , op2 . RegNum , zero ) ;
build - > Emit ( OP_JMP , 2 ) ;
build - > Emit ( OP_LI , to . RegNum , 0 ) ;
build - > Emit ( OP_JMP , 1 ) ;
size_t target = build - > Emit ( OP_LI , to . RegNum , 1 ) ;
build - > Backpatch ( patchspot , target ) ;
return to ;
}
}
//==========================================================================
//
//
//
//==========================================================================
FxConditional : : FxConditional ( FxExpression * c , FxExpression * t , FxExpression * f )
2016-10-22 23:14:49 +00:00
: FxExpression ( EFX_Conditional , c - > ScriptPosition )
2016-03-01 15:47:10 +00:00
{
condition = c ;
truex = t ;
falsex = f ;
}
//==========================================================================
//
//
//
//==========================================================================
FxConditional : : ~ FxConditional ( )
{
SAFE_DELETE ( condition ) ;
SAFE_DELETE ( truex ) ;
SAFE_DELETE ( falsex ) ;
}
//==========================================================================
//
//
//
//==========================================================================
FxExpression * FxConditional : : Resolve ( FCompileContext & ctx )
{
CHECKRESOLVED ( ) ;
2016-07-04 00:19:52 +00:00
RESOLVE ( condition , ctx ) ;
2016-03-01 15:47:10 +00:00
RESOLVE ( truex , ctx ) ;
RESOLVE ( falsex , ctx ) ;
ABORT ( condition & & truex & & falsex ) ;
2016-07-04 00:19:52 +00:00
if ( truex - > ValueType = = TypeBool & & falsex - > ValueType = = TypeBool )
ValueType = TypeBool ;
else if ( truex - > ValueType - > GetRegType ( ) = = REGT_INT & & falsex - > ValueType - > GetRegType ( ) = = REGT_INT )
2016-03-01 15:47:10 +00:00
ValueType = TypeSInt32 ;
else if ( truex - > IsNumeric ( ) & & falsex - > IsNumeric ( ) )
ValueType = TypeFloat64 ;
//else if (truex->ValueType != falsex->ValueType)
2016-07-04 00:19:52 +00:00
if ( condition - > ValueType ! = TypeBool )
{
condition = new FxBoolCast ( condition ) ;
SAFE_RESOLVE ( condition , ctx ) ;
}
2016-03-01 15:47:10 +00:00
if ( condition - > isConstant ( ) )
{
ExpVal condval = static_cast < FxConstant * > ( condition ) - > GetValue ( ) ;
bool result = condval . GetBool ( ) ;
FxExpression * e = result ? truex : falsex ;
delete ( result ? falsex : truex ) ;
falsex = truex = NULL ;
delete this ;
return e ;
}
if ( ValueType - > GetRegType ( ) = = REGT_FLOAT )
{
if ( truex - > ValueType - > GetRegType ( ) ! = REGT_FLOAT )
{
truex = new FxFloatCast ( truex ) ;
RESOLVE ( truex , ctx ) ;
}
if ( falsex - > ValueType - > GetRegType ( ) ! = REGT_FLOAT )
{
falsex = new FxFloatCast ( falsex ) ;
RESOLVE ( falsex , ctx ) ;
}
}
return this ;
}
//==========================================================================
//
//
//
//==========================================================================
ExpEmit FxConditional : : Emit ( VMFunctionBuilder * build )
{
2016-07-04 17:54:26 +00:00
size_t truejump , falsejump ;
2016-03-01 15:47:10 +00:00
ExpEmit out ;
// The true and false expressions ought to be assigned to the
// same temporary instead of being copied to it. Oh well; good enough
// for now.
ExpEmit cond = condition - > Emit ( build ) ;
assert ( cond . RegType = = REGT_INT & & ! cond . Konst ) ;
// Test condition.
build - > Emit ( OP_EQ_K , 1 , cond . RegNum , build - > GetConstantInt ( 0 ) ) ;
2016-07-04 17:54:26 +00:00
falsejump = build - > Emit ( OP_JMP , 0 ) ;
2016-03-01 15:47:10 +00:00
// Evaluate true expression.
if ( truex - > isConstant ( ) & & truex - > ValueType - > GetRegType ( ) = = REGT_INT )
{
out = ExpEmit ( build , REGT_INT ) ;
build - > EmitLoadInt ( out . RegNum , static_cast < FxConstant * > ( truex ) - > GetValue ( ) . GetInt ( ) ) ;
}
else
{
ExpEmit trueop = truex - > Emit ( build ) ;
if ( trueop . Konst )
{
assert ( trueop . RegType = = REGT_FLOAT ) ;
out = ExpEmit ( build , REGT_FLOAT ) ;
build - > Emit ( OP_LKF , out . RegNum , trueop . RegNum ) ;
}
else
{
// Use the register returned by the true condition as the
// target for the false condition.
out = trueop ;
}
}
2016-07-04 17:54:26 +00:00
// Make sure to skip the false path.
truejump = build - > Emit ( OP_JMP , 0 ) ;
2016-03-01 15:47:10 +00:00
// Evaluate false expression.
2016-07-04 17:54:26 +00:00
build - > BackpatchToHere ( falsejump ) ;
2016-03-01 15:47:10 +00:00
if ( falsex - > isConstant ( ) & & falsex - > ValueType - > GetRegType ( ) = = REGT_INT )
{
build - > EmitLoadInt ( out . RegNum , static_cast < FxConstant * > ( falsex ) - > GetValue ( ) . GetInt ( ) ) ;
}
else
{
ExpEmit falseop = falsex - > Emit ( build ) ;
if ( falseop . Konst )
{
assert ( falseop . RegType = = REGT_FLOAT ) ;
build - > Emit ( OP_LKF , out . RegNum , falseop . RegNum ) ;
}
else
{
// Move result from the register returned by "false" to the one
// returned by "true" so that only one register is returned by
// this tree.
falseop . Free ( build ) ;
if ( falseop . RegType = = REGT_INT )
{
build - > Emit ( OP_MOVE , out . RegNum , falseop . RegNum , 0 ) ;
}
else
{
assert ( falseop . RegType = = REGT_FLOAT ) ;
build - > Emit ( OP_MOVEF , out . RegNum , falseop . RegNum , 0 ) ;
}
}
}
2016-07-04 17:54:26 +00:00
build - > BackpatchToHere ( truejump ) ;
2016-03-01 15:47:10 +00:00
return out ;
}
//==========================================================================
//
//
//
//==========================================================================
FxAbs : : FxAbs ( FxExpression * v )
2016-10-22 23:14:49 +00:00
: FxExpression ( EFX_Abs , v - > ScriptPosition )
2016-03-01 15:47:10 +00:00
{
val = v ;
ValueType = v - > ValueType ;
}
//==========================================================================
//
//
//
//==========================================================================
FxAbs : : ~ FxAbs ( )
{
SAFE_DELETE ( val ) ;
}
//==========================================================================
//
//
//
//==========================================================================
FxExpression * FxAbs : : Resolve ( FCompileContext & ctx )
{
CHECKRESOLVED ( ) ;
SAFE_RESOLVE ( val , ctx ) ;
if ( ! val - > IsNumeric ( ) )
{
ScriptPosition . Message ( MSG_ERROR , " Numeric type expected " ) ;
delete this ;
return NULL ;
}
else if ( val - > isConstant ( ) )
{
ExpVal value = static_cast < FxConstant * > ( val ) - > GetValue ( ) ;
switch ( value . Type - > GetRegType ( ) )
{
case REGT_INT :
value . Int = abs ( value . Int ) ;
break ;
case REGT_FLOAT :
value . Float = fabs ( value . Float ) ;
break ;
default :
// shouldn't happen
delete this ;
return NULL ;
}
FxExpression * x = new FxConstant ( value , ScriptPosition ) ;
delete this ;
return x ;
}
ValueType = val - > ValueType ;
return this ;
}
//==========================================================================
//
//
//
//==========================================================================
ExpEmit FxAbs : : Emit ( VMFunctionBuilder * build )
{
ExpEmit absofsteal = val - > Emit ( build ) ;
assert ( ! absofsteal . Konst ) ;
ExpEmit out ( build , absofsteal . RegType ) ;
if ( absofsteal . RegType = = REGT_INT )
{
build - > Emit ( OP_ABS , out . RegNum , absofsteal . RegNum , 0 ) ;
}
else
{
assert ( absofsteal . RegType = = REGT_FLOAT ) ;
build - > Emit ( OP_FLOP , out . RegNum , absofsteal . RegNum , FLOP_ABS ) ;
}
return out ;
}
2016-04-19 04:06:17 +00:00
//==========================================================================
//
//
//
//==========================================================================
FxATan2 : : FxATan2 ( FxExpression * y , FxExpression * x , const FScriptPosition & pos )
2016-10-22 23:14:49 +00:00
: FxExpression ( EFX_ATan2 , pos )
2016-04-19 04:06:17 +00:00
{
yval = y ;
xval = x ;
}
//==========================================================================
//
//
//
//==========================================================================
FxATan2 : : ~ FxATan2 ( )
{
SAFE_DELETE ( yval ) ;
SAFE_DELETE ( xval ) ;
}
//==========================================================================
//
//
//
//==========================================================================
FxExpression * FxATan2 : : Resolve ( FCompileContext & ctx )
{
CHECKRESOLVED ( ) ;
SAFE_RESOLVE ( yval , ctx ) ;
SAFE_RESOLVE ( xval , ctx ) ;
if ( ! yval - > IsNumeric ( ) | | ! xval - > IsNumeric ( ) )
{
ScriptPosition . Message ( MSG_ERROR , " numeric value expected for parameter " ) ;
delete this ;
return NULL ;
}
if ( yval - > isConstant ( ) & & xval - > isConstant ( ) )
{
double y = static_cast < FxConstant * > ( yval ) - > GetValue ( ) . GetFloat ( ) ;
double x = static_cast < FxConstant * > ( xval ) - > GetValue ( ) . GetFloat ( ) ;
FxExpression * z = new FxConstant ( g_atan2 ( y , x ) * ( 180 / M_PI ) , ScriptPosition ) ;
delete this ;
return z ;
}
if ( yval - > ValueType - > GetRegType ( ) ! = REGT_FLOAT & & ! yval - > isConstant ( ) )
{
yval = new FxFloatCast ( yval ) ;
}
if ( xval - > ValueType - > GetRegType ( ) ! = REGT_FLOAT & & ! xval - > isConstant ( ) )
{
xval = new FxFloatCast ( xval ) ;
}
ValueType = TypeFloat64 ;
return this ;
}
//==========================================================================
//
//
//
//==========================================================================
ExpEmit FxATan2 : : Emit ( VMFunctionBuilder * build )
{
ExpEmit yreg = ToReg ( build , yval ) ;
ExpEmit xreg = ToReg ( build , xval ) ;
yreg . Free ( build ) ;
xreg . Free ( build ) ;
ExpEmit out ( build , REGT_FLOAT ) ;
build - > Emit ( OP_ATAN2 , out . RegNum , yreg . RegNum , xreg . RegNum ) ;
return out ;
}
//==========================================================================
//
// The atan2 opcode only takes registers as parameters, so any constants
// must be loaded into registers first.
//
//==========================================================================
ExpEmit FxATan2 : : ToReg ( VMFunctionBuilder * build , FxExpression * val )
{
if ( val - > isConstant ( ) )
{
ExpEmit reg ( build , REGT_FLOAT ) ;
build - > Emit ( OP_LKF , reg . RegNum , build - > GetConstantFloat ( static_cast < FxConstant * > ( val ) - > GetValue ( ) . GetFloat ( ) ) ) ;
return reg ;
}
return val - > Emit ( build ) ;
}
2016-03-01 15:47:10 +00:00
//==========================================================================
//
//
//
//==========================================================================
FxMinMax : : FxMinMax ( TArray < FxExpression * > & expr , FName type , const FScriptPosition & pos )
2016-10-22 23:14:49 +00:00
: FxExpression ( EFX_MinMax , pos ) , Type ( type )
2016-03-01 15:47:10 +00:00
{
assert ( expr . Size ( ) > 0 ) ;
assert ( type = = NAME_Min | | type = = NAME_Max ) ;
choices . Resize ( expr . Size ( ) ) ;
for ( unsigned i = 0 ; i < expr . Size ( ) ; + + i )
{
choices [ i ] = expr [ i ] ;
}
}
//==========================================================================
//
//
//
//==========================================================================
FxExpression * FxMinMax : : Resolve ( FCompileContext & ctx )
{
unsigned int i ;
int intcount , floatcount ;
CHECKRESOLVED ( ) ;
// Determine if float or int
intcount = floatcount = 0 ;
for ( i = 0 ; i < choices . Size ( ) ; + + i )
{
RESOLVE ( choices [ i ] , ctx ) ;
ABORT ( choices [ i ] ) ;
if ( choices [ i ] - > ValueType - > GetRegType ( ) = = REGT_FLOAT )
{
floatcount + + ;
}
2016-10-15 22:12:33 +00:00
else if ( choices [ i ] - > ValueType - > GetRegType ( ) = = REGT_INT & & choices [ i ] - > ValueType ! = TypeName )
2016-03-01 15:47:10 +00:00
{
intcount + + ;
}
else
{
ScriptPosition . Message ( MSG_ERROR , " Arguments must be of type int or float " ) ;
delete this ;
return NULL ;
}
}
if ( floatcount ! = 0 )
{
ValueType = TypeFloat64 ;
if ( intcount ! = 0 )
{ // There are some ints that need to be cast to floats
for ( i = 0 ; i < choices . Size ( ) ; + + i )
{
if ( choices [ i ] - > ValueType - > GetRegType ( ) = = REGT_INT )
{
choices [ i ] = new FxFloatCast ( choices [ i ] ) ;
RESOLVE ( choices [ i ] , ctx ) ;
ABORT ( choices [ i ] ) ;
}
}
}
}
else
{
ValueType = TypeSInt32 ;
}
// If at least two arguments are constants, they can be solved now.
// Look for first constant
for ( i = 0 ; i < choices . Size ( ) ; + + i )
{
if ( choices [ i ] - > isConstant ( ) )
{
ExpVal best = static_cast < FxConstant * > ( choices [ i ] ) - > GetValue ( ) ;
// Compare against remaining constants, which are removed.
// The best value gets stored in this one.
for ( unsigned j = i + 1 ; j < choices . Size ( ) ; )
{
if ( ! choices [ j ] - > isConstant ( ) )
{
j + + ;
}
else
{
ExpVal value = static_cast < FxConstant * > ( choices [ j ] ) - > GetValue ( ) ;
assert ( value . Type = = ValueType ) ;
if ( Type = = NAME_Min )
{
if ( value . Type - > GetRegType ( ) = = REGT_FLOAT )
{
if ( value . Float < best . Float )
{
best . Float = value . Float ;
}
}
else
{
if ( value . Int < best . Int )
{
best . Int = value . Int ;
}
}
}
else
{
if ( value . Type - > GetRegType ( ) = = REGT_FLOAT )
{
if ( value . Float > best . Float )
{
best . Float = value . Float ;
}
}
else
{
if ( value . Int > best . Int )
{
best . Int = value . Int ;
}
}
}
delete choices [ j ] ;
choices [ j ] = NULL ;
choices . Delete ( j ) ;
}
}
FxExpression * x = new FxConstant ( best , ScriptPosition ) ;
if ( i = = 0 & & choices . Size ( ) = = 1 )
{ // Every choice was constant
delete this ;
return x ;
}
delete choices [ i ] ;
choices [ i ] = x ;
break ;
}
}
return this ;
}
//==========================================================================
//
//
//
//==========================================================================
static void EmitLoad ( VMFunctionBuilder * build , const ExpEmit resultreg , const ExpVal & value )
{
if ( resultreg . RegType = = REGT_FLOAT )
{
build - > Emit ( OP_LKF , resultreg . RegNum , build - > GetConstantFloat ( value . GetFloat ( ) ) ) ;
}
else
{
build - > EmitLoadInt ( resultreg . RegNum , value . GetInt ( ) ) ;
}
}
ExpEmit FxMinMax : : Emit ( VMFunctionBuilder * build )
{
unsigned i ;
int opcode , opA ;
assert ( choices . Size ( ) > 0 ) ;
assert ( OP_LTF_RK = = OP_LTF_RR + 1 ) ;
assert ( OP_LT_RK = = OP_LT_RR + 1 ) ;
assert ( OP_LEF_RK = = OP_LEF_RR + 1 ) ;
assert ( OP_LE_RK = = OP_LE_RR + 1 ) ;
if ( Type = = NAME_Min )
{
opcode = ValueType - > GetRegType ( ) = = REGT_FLOAT ? OP_LEF_RR : OP_LE_RR ;
opA = 1 ;
}
else
{
opcode = ValueType - > GetRegType ( ) = = REGT_FLOAT ? OP_LTF_RR : OP_LT_RR ;
opA = 0 ;
}
ExpEmit bestreg ;
// Get first value into a register. This will also be the result register.
if ( choices [ 0 ] - > isConstant ( ) )
{
bestreg = ExpEmit ( build , ValueType - > GetRegType ( ) ) ;
EmitLoad ( build , bestreg , static_cast < FxConstant * > ( choices [ 0 ] ) - > GetValue ( ) ) ;
}
else
{
bestreg = choices [ 0 ] - > Emit ( build ) ;
}
// Compare every choice. Better matches get copied to the bestreg.
for ( i = 1 ; i < choices . Size ( ) ; + + i )
{
ExpEmit checkreg = choices [ i ] - > Emit ( build ) ;
assert ( checkreg . RegType = = bestreg . RegType ) ;
build - > Emit ( opcode + checkreg . Konst , opA , bestreg . RegNum , checkreg . RegNum ) ;
build - > Emit ( OP_JMP , 1 ) ;
if ( checkreg . Konst )
{
build - > Emit ( bestreg . RegType = = REGT_FLOAT ? OP_LKF : OP_LK , bestreg . RegNum , checkreg . RegNum ) ;
}
else
{
build - > Emit ( bestreg . RegType = = REGT_FLOAT ? OP_MOVEF : OP_MOVE , bestreg . RegNum , checkreg . RegNum , 0 ) ;
checkreg . Free ( build ) ;
}
}
return bestreg ;
}
//==========================================================================
//
//
//
//==========================================================================
2016-10-15 19:35:31 +00:00
FxRandom : : FxRandom ( FRandom * r , FxExpression * mi , FxExpression * ma , const FScriptPosition & pos , bool nowarn )
2016-10-22 23:14:49 +00:00
: FxExpression ( EFX_Random , pos )
2016-03-01 15:47:10 +00:00
{
2016-08-02 16:50:34 +00:00
EmitTail = false ;
2016-03-01 15:47:10 +00:00
if ( mi ! = NULL & & ma ! = NULL )
{
2016-10-15 19:35:31 +00:00
min = new FxIntCast ( mi , nowarn ) ;
max = new FxIntCast ( ma , nowarn ) ;
2016-03-01 15:47:10 +00:00
}
else min = max = NULL ;
rng = r ;
ValueType = TypeSInt32 ;
}
//==========================================================================
//
//
//
//==========================================================================
FxRandom : : ~ FxRandom ( )
{
SAFE_DELETE ( min ) ;
SAFE_DELETE ( max ) ;
}
//==========================================================================
//
//
//
//==========================================================================
2016-08-02 16:50:34 +00:00
PPrototype * FxRandom : : ReturnProto ( )
{
EmitTail = true ;
return FxExpression : : ReturnProto ( ) ;
}
//==========================================================================
//
//
//
//==========================================================================
2016-03-01 15:47:10 +00:00
FxExpression * FxRandom : : Resolve ( FCompileContext & ctx )
{
CHECKRESOLVED ( ) ;
if ( min & & max )
{
RESOLVE ( min , ctx ) ;
RESOLVE ( max , ctx ) ;
ABORT ( min & & max ) ;
assert ( min - > ValueType = = ValueType ) ;
assert ( max - > ValueType = = ValueType ) ;
}
return this ;
} ;
//==========================================================================
//
//
//
//==========================================================================
int DecoRandom ( VMFrameStack * stack , VMValue * param , int numparam , VMReturn * ret , int numret )
{
assert ( numparam > = 1 & & numparam < = 3 ) ;
FRandom * rng = reinterpret_cast < FRandom * > ( param [ 0 ] . a ) ;
if ( numparam = = 1 )
{
2016-08-02 16:50:34 +00:00
ACTION_RETURN_INT ( ( * rng ) ( ) ) ;
2016-03-01 15:47:10 +00:00
}
else if ( numparam = = 2 )
{
int maskval = param [ 1 ] . i ;
2016-08-02 16:50:34 +00:00
ACTION_RETURN_INT ( rng - > Random2 ( maskval ) ) ;
2016-03-01 15:47:10 +00:00
}
else if ( numparam = = 3 )
{
int min = param [ 1 ] . i , max = param [ 2 ] . i ;
if ( max < min )
{
swapvalues ( max , min ) ;
}
2016-08-02 16:50:34 +00:00
ACTION_RETURN_INT ( ( * rng ) ( max - min + 1 ) + min ) ;
2016-03-01 15:47:10 +00:00
}
2016-08-02 16:50:34 +00:00
// Shouldn't happen
return 0 ;
2016-03-01 15:47:10 +00:00
}
ExpEmit FxRandom : : Emit ( VMFunctionBuilder * build )
{
// Call DecoRandom to generate a random number.
VMFunction * callfunc ;
PSymbol * sym = FindDecorateBuiltinFunction ( NAME_DecoRandom , DecoRandom ) ;
assert ( sym - > IsKindOf ( RUNTIME_CLASS ( PSymbolVMFunction ) ) ) ;
assert ( ( ( PSymbolVMFunction * ) sym ) - > Function ! = NULL ) ;
callfunc = ( ( PSymbolVMFunction * ) sym ) - > Function ;
2016-08-02 16:50:34 +00:00
int opcode = ( EmitTail ? OP_TAIL_K : OP_CALL_K ) ;
2016-03-01 15:47:10 +00:00
build - > Emit ( OP_PARAM , 0 , REGT_POINTER | REGT_KONST , build - > GetConstantAddress ( rng , ATAG_RNG ) ) ;
if ( min ! = NULL & & max ! = NULL )
{
EmitParameter ( build , min , ScriptPosition ) ;
EmitParameter ( build , max , ScriptPosition ) ;
2016-08-02 16:50:34 +00:00
build - > Emit ( opcode , build - > GetConstantAddress ( callfunc , ATAG_OBJECT ) , 3 , 1 ) ;
2016-03-01 15:47:10 +00:00
}
else
{
2016-08-02 16:50:34 +00:00
build - > Emit ( opcode , build - > GetConstantAddress ( callfunc , ATAG_OBJECT ) , 1 , 1 ) ;
2016-03-01 15:47:10 +00:00
}
2016-08-02 16:50:34 +00:00
if ( EmitTail )
{
ExpEmit call ;
call . Final = true ;
return call ;
}
2016-03-01 15:47:10 +00:00
ExpEmit out ( build , REGT_INT ) ;
build - > Emit ( OP_RESULT , 0 , REGT_INT , out . RegNum ) ;
return out ;
}
//==========================================================================
//
//
//
//==========================================================================
2016-10-15 19:35:31 +00:00
FxRandomPick : : FxRandomPick ( FRandom * r , TArray < FxExpression * > & expr , bool floaty , const FScriptPosition & pos , bool nowarn )
2016-10-22 23:14:49 +00:00
: FxExpression ( EFX_RandomPick , pos )
2016-03-01 15:47:10 +00:00
{
assert ( expr . Size ( ) > 0 ) ;
choices . Resize ( expr . Size ( ) ) ;
for ( unsigned int index = 0 ; index < expr . Size ( ) ; index + + )
{
if ( floaty )
{
choices [ index ] = new FxFloatCast ( expr [ index ] ) ;
}
else
{
2016-10-15 19:35:31 +00:00
choices [ index ] = new FxIntCast ( expr [ index ] , nowarn ) ;
2016-03-01 15:47:10 +00:00
}
}
rng = r ;
if ( floaty )
{
ValueType = TypeFloat64 ;
}
else
{
ValueType = TypeSInt32 ;
}
}
//==========================================================================
//
//
//
//==========================================================================
FxRandomPick : : ~ FxRandomPick ( )
{
}
//==========================================================================
//
//
//
//==========================================================================
FxExpression * FxRandomPick : : Resolve ( FCompileContext & ctx )
{
CHECKRESOLVED ( ) ;
for ( unsigned int index = 0 ; index < choices . Size ( ) ; index + + )
{
RESOLVE ( choices [ index ] , ctx ) ;
ABORT ( choices [ index ] ) ;
assert ( choices [ index ] - > ValueType = = ValueType ) ;
}
return this ;
} ;
//==========================================================================
//
// FxPick :: Emit
//
// The expression:
// a = pick[rng](i_0, i_1, i_2, ..., i_n)
// [where i_x is a complete expression and not just a value]
// is syntactic sugar for:
//
// switch(random[rng](0, n)) {
// case 0: a = i_0;
// case 1: a = i_1;
// case 2: a = i_2;
// ...
// case n: a = i_n;
// }
//
//==========================================================================
ExpEmit FxRandomPick : : Emit ( VMFunctionBuilder * build )
{
unsigned i ;
assert ( choices . Size ( ) > 0 ) ;
// Call DecoRandom to generate a random number.
VMFunction * callfunc ;
PSymbol * sym = FindDecorateBuiltinFunction ( NAME_DecoRandom , DecoRandom ) ;
assert ( sym - > IsKindOf ( RUNTIME_CLASS ( PSymbolVMFunction ) ) ) ;
assert ( ( ( PSymbolVMFunction * ) sym ) - > Function ! = NULL ) ;
callfunc = ( ( PSymbolVMFunction * ) sym ) - > Function ;
build - > Emit ( OP_PARAM , 0 , REGT_POINTER | REGT_KONST , build - > GetConstantAddress ( rng , ATAG_RNG ) ) ;
build - > EmitParamInt ( 0 ) ;
build - > EmitParamInt ( choices . Size ( ) - 1 ) ;
build - > Emit ( OP_CALL_K , build - > GetConstantAddress ( callfunc , ATAG_OBJECT ) , 3 , 1 ) ;
ExpEmit resultreg ( build , REGT_INT ) ;
build - > Emit ( OP_RESULT , 0 , REGT_INT , resultreg . RegNum ) ;
build - > Emit ( OP_IJMP , resultreg . RegNum , 0 ) ;
// Free the result register now. The simple code generation algorithm should
// automatically pick it as the destination register for each case.
resultreg . Free ( build ) ;
// For floating point results, we need to get a new register, since we can't
// reuse the integer one used to store the random result.
if ( ValueType - > GetRegType ( ) = = REGT_FLOAT )
{
resultreg = ExpEmit ( build , REGT_FLOAT ) ;
resultreg . Free ( build ) ;
}
// Allocate space for the jump table.
size_t jumptable = build - > Emit ( OP_JMP , 0 ) ;
for ( i = 1 ; i < choices . Size ( ) ; + + i )
{
build - > Emit ( OP_JMP , 0 ) ;
}
// Emit each case
TArray < size_t > finishes ( choices . Size ( ) - 1 ) ;
for ( unsigned i = 0 ; i < choices . Size ( ) ; + + i )
{
build - > BackpatchToHere ( jumptable + i ) ;
if ( choices [ i ] - > isConstant ( ) )
{
EmitLoad ( build , resultreg , static_cast < FxConstant * > ( choices [ i ] ) - > GetValue ( ) ) ;
}
else
{
ExpEmit casereg = choices [ i ] - > Emit ( build ) ;
if ( casereg . RegNum ! = resultreg . RegNum )
{ // The result of the case is in a different register from what
// was expected. Copy it to the one we wanted.
resultreg . Reuse ( build ) ; // This is really just for the assert in Reuse()
build - > Emit ( ValueType - > GetRegType ( ) = = REGT_INT ? OP_MOVE : OP_MOVEF , resultreg . RegNum , casereg . RegNum , 0 ) ;
resultreg . Free ( build ) ;
}
// Free this register so the remaining cases can use it.
casereg . Free ( build ) ;
}
// All but the final case needs a jump to the end of the expression's code.
if ( i + 1 < choices . Size ( ) )
{
size_t loc = build - > Emit ( OP_JMP , 0 ) ;
finishes . Push ( loc ) ;
}
}
// Backpatch each case (except the last, since it ends here) to jump to here.
for ( i = 0 ; i < choices . Size ( ) - 1 ; + + i )
{
build - > BackpatchToHere ( finishes [ i ] ) ;
}
// The result register needs to be in-use when we return.
// It should have been freed earlier, so restore its in-use flag.
resultreg . Reuse ( build ) ;
return resultreg ;
}
//==========================================================================
//
//
//
//==========================================================================
FxFRandom : : FxFRandom ( FRandom * r , FxExpression * mi , FxExpression * ma , const FScriptPosition & pos )
2016-10-15 19:35:31 +00:00
: FxRandom ( r , NULL , NULL , pos , true )
2016-03-01 15:47:10 +00:00
{
if ( mi ! = NULL & & ma ! = NULL )
{
min = new FxFloatCast ( mi ) ;
max = new FxFloatCast ( ma ) ;
}
ValueType = TypeFloat64 ;
2016-10-22 23:14:49 +00:00
ExprType = EFX_FRandom ;
2016-03-01 15:47:10 +00:00
}
//==========================================================================
//
//
//
//==========================================================================
int DecoFRandom ( VMFrameStack * stack , VMValue * param , int numparam , VMReturn * ret , int numret )
{
assert ( numparam = = 1 | | numparam = = 3 ) ;
FRandom * rng = reinterpret_cast < FRandom * > ( param [ 0 ] . a ) ;
int random = ( * rng ) ( 0x40000000 ) ;
double frandom = random / double ( 0x40000000 ) ;
if ( numparam = = 3 )
{
double min = param [ 1 ] . f , max = param [ 2 ] . f ;
if ( max < min )
{
swapvalues ( max , min ) ;
}
2016-08-02 16:50:34 +00:00
ACTION_RETURN_FLOAT ( frandom * ( max - min ) + min ) ;
2016-03-01 15:47:10 +00:00
}
else
{
2016-08-02 16:50:34 +00:00
ACTION_RETURN_FLOAT ( frandom ) ;
2016-03-01 15:47:10 +00:00
}
}
ExpEmit FxFRandom : : Emit ( VMFunctionBuilder * build )
{
// Call the DecoFRandom function to generate a floating point random number..
VMFunction * callfunc ;
PSymbol * sym = FindDecorateBuiltinFunction ( NAME_DecoFRandom , DecoFRandom ) ;
assert ( sym - > IsKindOf ( RUNTIME_CLASS ( PSymbolVMFunction ) ) ) ;
assert ( ( ( PSymbolVMFunction * ) sym ) - > Function ! = NULL ) ;
callfunc = ( ( PSymbolVMFunction * ) sym ) - > Function ;
2016-08-02 16:50:34 +00:00
int opcode = ( EmitTail ? OP_TAIL_K : OP_CALL_K ) ;
2016-03-01 15:47:10 +00:00
build - > Emit ( OP_PARAM , 0 , REGT_POINTER | REGT_KONST , build - > GetConstantAddress ( rng , ATAG_RNG ) ) ;
if ( min ! = NULL & & max ! = NULL )
{
EmitParameter ( build , min , ScriptPosition ) ;
EmitParameter ( build , max , ScriptPosition ) ;
2016-08-02 16:50:34 +00:00
build - > Emit ( opcode , build - > GetConstantAddress ( callfunc , ATAG_OBJECT ) , 3 , 1 ) ;
2016-03-01 15:47:10 +00:00
}
else
{
2016-08-02 16:50:34 +00:00
build - > Emit ( opcode , build - > GetConstantAddress ( callfunc , ATAG_OBJECT ) , 1 , 1 ) ;
}
if ( EmitTail )
{
ExpEmit call ;
call . Final = true ;
return call ;
2016-03-01 15:47:10 +00:00
}
2016-08-02 16:50:34 +00:00
2016-03-01 15:47:10 +00:00
ExpEmit out ( build , REGT_FLOAT ) ;
build - > Emit ( OP_RESULT , 0 , REGT_FLOAT , out . RegNum ) ;
return out ;
}
//==========================================================================
//
//
//
//==========================================================================
2016-10-15 19:35:31 +00:00
FxRandom2 : : FxRandom2 ( FRandom * r , FxExpression * m , const FScriptPosition & pos , bool nowarn )
2016-10-22 23:14:49 +00:00
: FxExpression ( EFX_Random2 , pos )
2016-03-01 15:47:10 +00:00
{
2016-08-02 16:50:34 +00:00
EmitTail = false ;
2016-03-01 15:47:10 +00:00
rng = r ;
2016-10-15 19:35:31 +00:00
if ( m ) mask = new FxIntCast ( m , nowarn ) ;
2016-03-01 15:47:10 +00:00
else mask = new FxConstant ( - 1 , pos ) ;
ValueType = TypeSInt32 ;
}
//==========================================================================
//
//
//
//==========================================================================
FxRandom2 : : ~ FxRandom2 ( )
{
SAFE_DELETE ( mask ) ;
}
//==========================================================================
//
//
//
//==========================================================================
2016-08-02 16:50:34 +00:00
PPrototype * FxRandom2 : : ReturnProto ( )
{
EmitTail = true ;
return FxExpression : : ReturnProto ( ) ;
}
//==========================================================================
//
//
//
//==========================================================================
2016-03-01 15:47:10 +00:00
FxExpression * FxRandom2 : : Resolve ( FCompileContext & ctx )
{
CHECKRESOLVED ( ) ;
SAFE_RESOLVE ( mask , ctx ) ;
return this ;
}
//==========================================================================
//
//
//
//==========================================================================
ExpEmit FxRandom2 : : Emit ( VMFunctionBuilder * build )
{
// Call the DecoRandom function to generate the random number.
VMFunction * callfunc ;
PSymbol * sym = FindDecorateBuiltinFunction ( NAME_DecoRandom , DecoRandom ) ;
assert ( sym - > IsKindOf ( RUNTIME_CLASS ( PSymbolVMFunction ) ) ) ;
assert ( ( ( PSymbolVMFunction * ) sym ) - > Function ! = NULL ) ;
callfunc = ( ( PSymbolVMFunction * ) sym ) - > Function ;
2016-08-02 16:50:34 +00:00
int opcode = ( EmitTail ? OP_TAIL_K : OP_CALL_K ) ;
2016-03-01 15:47:10 +00:00
build - > Emit ( OP_PARAM , 0 , REGT_POINTER | REGT_KONST , build - > GetConstantAddress ( rng , ATAG_RNG ) ) ;
EmitParameter ( build , mask , ScriptPosition ) ;
2016-08-02 16:50:34 +00:00
build - > Emit ( opcode , build - > GetConstantAddress ( callfunc , ATAG_OBJECT ) , 2 , 1 ) ;
if ( EmitTail )
{
ExpEmit call ;
call . Final = true ;
return call ;
}
2016-03-01 15:47:10 +00:00
ExpEmit out ( build , REGT_INT ) ;
build - > Emit ( OP_RESULT , 0 , REGT_INT , out . RegNum ) ;
return out ;
}
//==========================================================================
//
//
//
//==========================================================================
FxIdentifier : : FxIdentifier ( FName name , const FScriptPosition & pos )
2016-10-22 23:14:49 +00:00
: FxExpression ( EFX_Identifier , pos )
2016-03-01 15:47:10 +00:00
{
Identifier = name ;
}
//==========================================================================
//
//
//
//==========================================================================
FxExpression * FxIdentifier : : Resolve ( FCompileContext & ctx )
{
PSymbol * sym ;
2016-10-15 15:40:27 +00:00
FxExpression * newex = nullptr ;
2016-03-01 15:47:10 +00:00
int num ;
CHECKRESOLVED ( ) ;
2016-10-15 15:40:27 +00:00
2016-10-20 23:12:54 +00:00
// Local variables have highest priority.
FxLocalVariableDeclaration * local = ctx . FindLocalVariable ( Identifier ) ;
if ( local ! = nullptr )
{
auto x = new FxLocalVariable ( local , ScriptPosition ) ;
delete this ;
return x - > Resolve ( ctx ) ;
}
2016-10-15 15:40:27 +00:00
// Ugh, the horror. Constants need to be taken from the owning class, but members from the self class to catch invalid accesses here...
2016-03-01 15:47:10 +00:00
// see if the current class (if valid) defines something with this name.
2016-10-15 15:40:27 +00:00
PSymbolTable * symtbl ;
if ( ( sym = ctx . FindInClass ( Identifier , symtbl ) ) ! = nullptr )
2016-03-01 15:47:10 +00:00
{
if ( sym - > IsKindOf ( RUNTIME_CLASS ( PSymbolConst ) ) )
{
ScriptPosition . Message ( MSG_DEBUGLOG , " Resolving name '%s' as class constant \n " , Identifier . GetChars ( ) ) ;
newex = FxConstant : : MakeConstant ( sym , ScriptPosition ) ;
}
else if ( sym - > IsKindOf ( RUNTIME_CLASS ( PField ) ) )
{
2016-10-21 15:41:39 +00:00
if ( ! ctx . Function )
{
ScriptPosition . Message ( MSG_ERROR , " Cannot resolve class member outside a function " , sym - > SymbolName . GetChars ( ) ) ;
delete this ;
return nullptr ;
}
2016-03-01 15:47:10 +00:00
PField * vsym = static_cast < PField * > ( sym ) ;
2016-10-15 15:40:27 +00:00
// We have 4 cases to consider here:
// 1. The symbol is a static/meta member (not implemented yet) which is always accessible.
// 2. This is a static function
// 3. This is an action function with a restricted self pointer
// 4. This is a normal member or unrestricted action function.
2016-10-26 11:22:36 +00:00
if ( ( vsym - > Flags & VARF_Deprecated ) & & ! ctx . FromDecorate )
2016-10-15 15:40:27 +00:00
{
2016-10-21 15:41:39 +00:00
ScriptPosition . Message ( MSG_WARNING , " Accessing deprecated member variable %s " , sym - > SymbolName . GetChars ( ) ) ;
2016-10-15 15:40:27 +00:00
}
if ( ( vsym - > Flags & VARF_Private ) & & symtbl ! = & ctx . Class - > Symbols )
{
2016-10-21 15:41:39 +00:00
ScriptPosition . Message ( MSG_ERROR , " Private member %s not accessible " , sym - > SymbolName . GetChars ( ) ) ;
2016-10-15 15:40:27 +00:00
delete this ;
return nullptr ;
}
if ( vsym - > Flags & VARF_Static )
{
// todo. For now these cannot be defined so let's just exit.
2016-10-21 15:41:39 +00:00
ScriptPosition . Message ( MSG_ERROR , " Static members not implemented yet. " ) ;
2016-10-15 15:40:27 +00:00
delete this ;
return nullptr ;
}
if ( ctx . Function - > Variants [ 0 ] . SelfClass = = nullptr )
{
ScriptPosition . Message ( MSG_ERROR , " Unable to access class member from static function " ) ;
delete this ;
return nullptr ;
}
2016-10-21 15:41:39 +00:00
if ( ctx . Function - > Variants [ 0 ] . SelfClass ! = ctx . Class )
2016-10-15 15:40:27 +00:00
{
2016-10-21 15:41:39 +00:00
// Check if the restricted class can access it.
PSymbol * sym2 ;
if ( ( sym2 = ctx . FindInSelfClass ( Identifier , symtbl ) ) ! = nullptr )
2016-10-15 15:40:27 +00:00
{
2016-10-21 15:41:39 +00:00
if ( sym ! = sym2 )
2016-10-15 15:40:27 +00:00
{
2016-10-21 15:41:39 +00:00
ScriptPosition . Message ( MSG_ERROR , " Member variable of %s not accessible through restricted self pointer " , ctx . Class - > TypeName . GetChars ( ) ) ;
delete this ;
return nullptr ;
2016-10-15 15:40:27 +00:00
}
}
2016-10-21 15:41:39 +00:00
}
2016-03-01 15:47:10 +00:00
ScriptPosition . Message ( MSG_DEBUGLOG , " Resolving name '%s' as member variable, index %d \n " , Identifier . GetChars ( ) , vsym - > Offset ) ;
newex = new FxClassMember ( ( new FxSelf ( ScriptPosition ) ) - > Resolve ( ctx ) , vsym , ScriptPosition ) ;
}
else
{
ScriptPosition . Message ( MSG_ERROR , " Invalid member identifier '%s' \n " , Identifier . GetChars ( ) ) ;
2016-10-21 15:41:39 +00:00
delete this ;
return nullptr ;
2016-03-01 15:47:10 +00:00
}
}
2016-10-15 15:40:27 +00:00
2016-03-01 15:47:10 +00:00
// now check the global identifiers.
else if ( ( sym = ctx . FindGlobal ( Identifier ) ) ! = NULL )
{
if ( sym - > IsKindOf ( RUNTIME_CLASS ( PSymbolConst ) ) )
{
ScriptPosition . Message ( MSG_DEBUGLOG , " Resolving name '%s' as global constant \n " , Identifier . GetChars ( ) ) ;
newex = FxConstant : : MakeConstant ( sym , ScriptPosition ) ;
}
else
{
ScriptPosition . Message ( MSG_ERROR , " Invalid global identifier '%s' \n " , Identifier . GetChars ( ) ) ;
}
}
// and line specials
else if ( ( num = P_FindLineSpecial ( Identifier , NULL , NULL ) ) )
{
ScriptPosition . Message ( MSG_DEBUGLOG , " Resolving name '%s' as line special %d \n " , Identifier . GetChars ( ) , num ) ;
newex = new FxConstant ( num , ScriptPosition ) ;
}
else
{
ScriptPosition . Message ( MSG_ERROR , " Unknown identifier '%s' " , Identifier . GetChars ( ) ) ;
2016-10-21 15:41:39 +00:00
delete this ;
return nullptr ;
}
delete this ;
return newex ? newex - > Resolve ( ctx ) : nullptr ;
}
//==========================================================================
//
//
//
//==========================================================================
FxMemberIdentifier : : FxMemberIdentifier ( FxExpression * left , FName name , const FScriptPosition & pos )
: FxIdentifier ( name , pos )
{
Object = left ;
2016-10-22 23:14:49 +00:00
ExprType = EFX_MemberIdentifier ;
2016-10-21 15:41:39 +00:00
}
//==========================================================================
//
//
//
//==========================================================================
FxExpression * FxMemberIdentifier : : Resolve ( FCompileContext & ctx )
{
PSymbol * sym ;
FxExpression * newex = nullptr ;
CHECKRESOLVED ( ) ;
SAFE_RESOLVE ( Object , ctx ) ;
2016-10-21 22:50:04 +00:00
if ( Object - > ValueType - > IsKindOf ( RUNTIME_CLASS ( PPointer ) ) )
2016-10-21 15:41:39 +00:00
{
PSymbolTable * symtbl ;
2016-10-21 22:50:04 +00:00
auto ptype = static_cast < PPointer * > ( Object - > ValueType ) - > PointedType ;
2016-10-24 11:18:13 +00:00
if ( ptype - > IsKindOf ( RUNTIME_CLASS ( PStruct ) ) ) // PClass is a child class of PStruct so this covers both.
2016-10-21 15:41:39 +00:00
{
2016-10-24 11:18:13 +00:00
PStruct * cls = static_cast < PStruct * > ( ptype ) ;
bool isclass = cls - > IsKindOf ( RUNTIME_CLASS ( PClass ) ) ;
2016-10-21 22:50:04 +00:00
if ( ( sym = cls - > Symbols . FindSymbolInTable ( Identifier , symtbl ) ) ! = nullptr )
2016-10-21 15:41:39 +00:00
{
2016-10-21 22:50:04 +00:00
if ( sym - > IsKindOf ( RUNTIME_CLASS ( PSymbolConst ) ) )
2016-10-21 15:41:39 +00:00
{
2016-10-24 11:18:13 +00:00
ScriptPosition . Message ( MSG_DEBUGLOG , " Resolving name '%s' as %s constant \n " , Identifier . GetChars ( ) , isclass ? " class " : " struct " ) ;
2016-10-21 22:50:04 +00:00
newex = FxConstant : : MakeConstant ( sym , ScriptPosition ) ;
2016-10-21 15:41:39 +00:00
}
2016-10-21 22:50:04 +00:00
else if ( sym - > IsKindOf ( RUNTIME_CLASS ( PField ) ) )
2016-10-21 15:41:39 +00:00
{
2016-10-21 22:50:04 +00:00
PField * vsym = static_cast < PField * > ( sym ) ;
// We have 4 cases to consider here:
// 1. The symbol is a static/meta member (not implemented yet) which is always accessible.
// 2. This is a static function
// 3. This is an action function with a restricted self pointer
// 4. This is a normal member or unrestricted action function.
if ( vsym - > Flags & VARF_Deprecated )
{
ScriptPosition . Message ( MSG_WARNING , " Accessing deprecated member variable %s " , vsym - > SymbolName . GetChars ( ) ) ;
}
if ( ( vsym - > Flags & VARF_Private ) & & symtbl ! = & ctx . Class - > Symbols )
{
ScriptPosition . Message ( MSG_ERROR , " Private member %s not accessible " , vsym - > SymbolName . GetChars ( ) ) ;
delete this ;
return nullptr ;
}
if ( vsym - > Flags & VARF_Static )
{
// todo. For now these cannot be defined so let's just exit.
ScriptPosition . Message ( MSG_ERROR , " Static members not implemented yet. " ) ;
delete this ;
return nullptr ;
}
2016-10-24 11:18:13 +00:00
auto x = isclass ? new FxClassMember ( Object , vsym , ScriptPosition ) : new FxStructMember ( Object , vsym , ScriptPosition ) ;
2016-10-21 15:41:39 +00:00
delete this ;
2016-10-21 22:50:04 +00:00
return x - > Resolve ( ctx ) ;
2016-10-21 15:41:39 +00:00
}
2016-10-21 22:50:04 +00:00
else
2016-10-21 15:41:39 +00:00
{
2016-10-21 22:50:04 +00:00
ScriptPosition . Message ( MSG_ERROR , " Invalid member identifier '%s' \n " , Identifier . GetChars ( ) ) ;
2016-10-21 15:41:39 +00:00
delete this ;
return nullptr ;
}
}
else
{
2016-10-21 22:50:04 +00:00
ScriptPosition . Message ( MSG_ERROR , " Unknown identifier '%s' " , Identifier . GetChars ( ) ) ;
2016-10-21 15:41:39 +00:00
delete this ;
return nullptr ;
}
}
2016-03-01 15:47:10 +00:00
}
2016-10-21 15:41:39 +00:00
else if ( Object - > ValueType - > IsA ( RUNTIME_CLASS ( PStruct ) ) )
{
2016-10-24 11:18:13 +00:00
if ( ( sym = Object - > ValueType - > Symbols . FindSymbol ( Identifier , false ) ) ! = nullptr )
{
if ( sym - > IsKindOf ( RUNTIME_CLASS ( PSymbolConst ) ) )
{
ScriptPosition . Message ( MSG_DEBUGLOG , " Resolving name '%s' as struct constant \n " , Identifier . GetChars ( ) ) ;
newex = FxConstant : : MakeConstant ( sym , ScriptPosition ) ;
}
else if ( sym - > IsKindOf ( RUNTIME_CLASS ( PField ) ) )
{
PField * vsym = static_cast < PField * > ( sym ) ;
if ( vsym - > Flags & VARF_Deprecated )
{
ScriptPosition . Message ( MSG_WARNING , " Accessing deprecated member variable %s " , vsym - > SymbolName . GetChars ( ) ) ;
}
auto x = new FxStructMember ( Object , vsym , ScriptPosition ) ;
delete this ;
return x - > Resolve ( ctx ) ;
}
else
{
ScriptPosition . Message ( MSG_ERROR , " Invalid member identifier '%s' \n " , Identifier . GetChars ( ) ) ;
delete this ;
return nullptr ;
}
}
else
{
ScriptPosition . Message ( MSG_ERROR , " Unknown identifier '%s' " , Identifier . GetChars ( ) ) ;
delete this ;
return nullptr ;
}
2016-10-21 15:41:39 +00:00
}
ScriptPosition . Message ( MSG_ERROR , " Left side of %s is not a struct or class " , Identifier . GetChars ( ) ) ;
2016-03-01 15:47:10 +00:00
delete this ;
2016-10-21 15:41:39 +00:00
return nullptr ;
2016-03-01 15:47:10 +00:00
}
2016-10-20 23:12:54 +00:00
//==========================================================================
//
//
//
//==========================================================================
FxLocalVariable : : FxLocalVariable ( FxLocalVariableDeclaration * var , const FScriptPosition & sc )
2016-10-22 23:14:49 +00:00
: FxExpression ( EFX_LocalVariable , sc )
2016-10-20 23:12:54 +00:00
{
Variable = var ;
2016-10-21 08:09:01 +00:00
ValueType = var - > ValueType ;
2016-10-20 23:12:54 +00:00
AddressRequested = false ;
}
FxExpression * FxLocalVariable : : Resolve ( FCompileContext & ctx )
{
CHECKRESOLVED ( ) ;
return this ;
}
bool FxLocalVariable : : RequestAddress ( bool * writable )
{
AddressRequested = true ;
if ( writable ! = nullptr ) * writable = ! ( Variable - > VarFlags & VARF_ReadOnly ) ;
return true ;
}
ExpEmit FxLocalVariable : : Emit ( VMFunctionBuilder * build )
{
ExpEmit ret ( Variable - > RegNum , Variable - > ValueType - > GetRegType ( ) , false , true ) ;
if ( AddressRequested ) ret . Target = true ;
return ret ;
}
2016-03-01 15:47:10 +00:00
//==========================================================================
//
//
//
//==========================================================================
FxSelf : : FxSelf ( const FScriptPosition & pos )
2016-10-22 23:14:49 +00:00
: FxExpression ( EFX_Self , pos )
2016-03-01 15:47:10 +00:00
{
}
//==========================================================================
//
//
//
//==========================================================================
FxExpression * FxSelf : : Resolve ( FCompileContext & ctx )
{
CHECKRESOLVED ( ) ;
2016-10-15 15:40:27 +00:00
if ( ctx . Function = = nullptr | | ctx . Function - > Variants [ 0 ] . SelfClass = = nullptr )
2016-03-01 15:47:10 +00:00
{
ScriptPosition . Message ( MSG_ERROR , " self used outside of a member function " ) ;
delete this ;
2016-10-15 15:40:27 +00:00
return nullptr ;
2016-03-01 15:47:10 +00:00
}
2016-10-15 15:40:27 +00:00
ValueType = NewPointer ( ctx . Function - > Variants [ 0 ] . SelfClass ) ;
2016-03-01 15:47:10 +00:00
return this ;
}
//==========================================================================
//
//
//
//==========================================================================
ExpEmit FxSelf : : Emit ( VMFunctionBuilder * build )
{
// self is always the first pointer passed to the function
2016-10-20 23:12:54 +00:00
return ExpEmit ( 0 , REGT_POINTER , false , true ) ;
2016-03-01 15:47:10 +00:00
}
//==========================================================================
//
//
//
//==========================================================================
2016-10-24 11:18:13 +00:00
FxStructMember : : FxStructMember ( FxExpression * x , PField * mem , const FScriptPosition & pos )
: FxExpression ( EFX_StructMember , pos )
2016-03-01 15:47:10 +00:00
{
classx = x ;
membervar = mem ;
AddressRequested = false ;
2016-10-24 11:18:13 +00:00
AddressWritable = true ; // must be true unless classx tells us otherwise if requested.
2016-03-01 15:47:10 +00:00
}
//==========================================================================
//
//
//
//==========================================================================
2016-10-24 11:18:13 +00:00
FxStructMember : : ~ FxStructMember ( )
2016-03-01 15:47:10 +00:00
{
SAFE_DELETE ( classx ) ;
}
//==========================================================================
//
//
//
//==========================================================================
2016-10-24 11:18:13 +00:00
bool FxStructMember : : RequestAddress ( bool * writable )
2016-03-01 15:47:10 +00:00
{
AddressRequested = true ;
2016-10-24 11:18:13 +00:00
if ( writable ! = nullptr ) * writable = AddressWritable & & ! ( membervar - > Flags & VARF_ReadOnly ) ;
2016-10-17 18:33:35 +00:00
return true ;
2016-03-01 15:47:10 +00:00
}
//==========================================================================
//
//
//
//==========================================================================
2016-10-24 11:18:13 +00:00
FxExpression * FxStructMember : : Resolve ( FCompileContext & ctx )
2016-03-01 15:47:10 +00:00
{
CHECKRESOLVED ( ) ;
SAFE_RESOLVE ( classx , ctx ) ;
2016-10-24 11:18:13 +00:00
if ( classx - > ValueType - > IsKindOf ( RUNTIME_CLASS ( PPointer ) ) )
2016-03-01 15:47:10 +00:00
{
2016-10-24 11:18:13 +00:00
PPointer * ptrtype = dyn_cast < PPointer > ( classx - > ValueType ) ;
2016-10-24 15:18:20 +00:00
if ( ptrtype = = nullptr | | ! ptrtype - > PointedType - > IsKindOf ( RUNTIME_CLASS ( PStruct ) ) )
2016-10-24 11:18:13 +00:00
{
2016-10-24 15:18:20 +00:00
ScriptPosition . Message ( MSG_ERROR , " Member variable requires a struct or class object. " ) ;
2016-10-24 11:18:13 +00:00
delete this ;
return nullptr ;
}
}
2016-10-24 15:18:20 +00:00
else if ( classx - > ValueType - > IsA ( RUNTIME_CLASS ( PStruct ) ) ) // Classes can never be used as value types so we do not have to consider that case.
2016-10-24 11:18:13 +00:00
{
// if this is a struct within a class or another struct we can simplify the expression by creating a new PField with a cumulative offset.
if ( classx - > ExprType = = EFX_ClassMember | | classx - > ExprType = = EFX_StructMember )
{
auto parentfield = static_cast < FxStructMember * > ( classx ) - > membervar ;
// PFields are garbage collected so this will be automatically taken care of later.
2016-10-24 15:18:20 +00:00
auto newfield = new PField ( membervar - > SymbolName , membervar - > Type , membervar - > Flags | parentfield - > Flags , membervar - > Offset + parentfield - > Offset , membervar - > BitValue ) ;
2016-10-24 11:18:13 +00:00
static_cast < FxStructMember * > ( classx ) - > membervar = newfield ;
classx - > isresolved = false ; // re-resolve the parent so it can also check if it can be optimized away.
auto x = classx - > Resolve ( ctx ) ;
classx = nullptr ;
return x ;
}
else
{
if ( ! ( classx - > RequestAddress ( & AddressWritable ) ) )
{
ScriptPosition . Message ( MSG_ERROR , " unable to dereference left side of %s " , membervar - > SymbolName . GetChars ( ) ) ;
delete this ;
return nullptr ;
}
}
2016-03-01 15:47:10 +00:00
}
ValueType = membervar - > Type ;
return this ;
}
2016-10-24 11:18:13 +00:00
ExpEmit FxStructMember : : Emit ( VMFunctionBuilder * build )
2016-03-01 15:47:10 +00:00
{
ExpEmit obj = classx - > Emit ( build ) ;
assert ( obj . RegType = = REGT_POINTER ) ;
2016-08-05 15:33:29 +00:00
if ( obj . Konst )
{
// If the situation where we are dereferencing a constant
// pointer is common, then it would probably be worthwhile
// to add new opcodes for those. But as of right now, I
// don't expect it to be a particularly common case.
ExpEmit newobj ( build , REGT_POINTER ) ;
build - > Emit ( OP_LKP , newobj . RegNum , obj . RegNum ) ;
obj = newobj ;
}
2016-03-01 15:47:10 +00:00
if ( AddressRequested )
{
if ( membervar - > Offset = = 0 )
{
return obj ;
}
obj . Free ( build ) ;
ExpEmit out ( build , REGT_POINTER ) ;
build - > Emit ( OP_ADDA_RK , out . RegNum , obj . RegNum , build - > GetConstantInt ( ( int ) membervar - > Offset ) ) ;
return out ;
}
int offsetreg = build - > GetConstantInt ( ( int ) membervar - > Offset ) ;
2016-08-05 15:33:29 +00:00
ExpEmit loc ( build , membervar - > Type - > GetRegType ( ) ) ;
2016-03-01 15:47:10 +00:00
2016-10-24 15:18:20 +00:00
if ( membervar - > BitValue = = - 1 )
{
build - > Emit ( membervar - > Type - > GetLoadOp ( ) , loc . RegNum , obj . RegNum , offsetreg ) ;
}
else
{
ExpEmit out ( build , REGT_POINTER ) ;
build - > Emit ( OP_ADDA_RK , out . RegNum , obj . RegNum , offsetreg ) ;
build - > Emit ( OP_LBIT , loc . RegNum , out . RegNum , 1 < < membervar - > BitValue ) ;
out . Free ( build ) ;
}
2016-03-01 15:47:10 +00:00
obj . Free ( build ) ;
return loc ;
}
2016-10-24 11:18:13 +00:00
//==========================================================================
//
2016-10-24 15:18:20 +00:00
// not really needed at the moment but may become useful with meta properties
// and some other class-specific extensions.
2016-10-24 11:18:13 +00:00
//
//==========================================================================
FxClassMember : : FxClassMember ( FxExpression * x , PField * mem , const FScriptPosition & pos )
: FxStructMember ( x , mem , pos )
{
ExprType = EFX_ClassMember ;
//if (classx->IsDefaultObject()) Readonly=true;
}
//==========================================================================
//
//
//
//==========================================================================
2016-03-01 15:47:10 +00:00
FxArrayElement : : FxArrayElement ( FxExpression * base , FxExpression * _index )
2016-10-22 23:14:49 +00:00
: FxExpression ( EFX_ArrayElement , base - > ScriptPosition )
2016-03-01 15:47:10 +00:00
{
Array = base ;
index = _index ;
2016-08-05 15:33:29 +00:00
AddressRequested = false ;
AddressWritable = false ;
2016-03-01 15:47:10 +00:00
}
//==========================================================================
//
//
//
//==========================================================================
FxArrayElement : : ~ FxArrayElement ( )
{
SAFE_DELETE ( Array ) ;
SAFE_DELETE ( index ) ;
}
//==========================================================================
//
//
//
//==========================================================================
2016-10-17 18:33:35 +00:00
bool FxArrayElement : : RequestAddress ( bool * writable )
2016-03-01 15:47:10 +00:00
{
AddressRequested = true ;
2016-10-17 18:33:35 +00:00
if ( writable ! = nullptr ) * writable = AddressWritable ;
return true ;
2016-03-01 15:47:10 +00:00
}
//==========================================================================
//
//
//
//==========================================================================
FxExpression * FxArrayElement : : Resolve ( FCompileContext & ctx )
{
CHECKRESOLVED ( ) ;
SAFE_RESOLVE ( Array , ctx ) ;
SAFE_RESOLVE ( index , ctx ) ;
if ( index - > ValueType - > GetRegType ( ) = = REGT_FLOAT /* lax */ )
{
// DECORATE allows floats here so cast them to int.
2016-10-15 19:35:31 +00:00
index = new FxIntCast ( index , ctx . FromDecorate ) ;
2016-03-01 15:47:10 +00:00
index = index - > Resolve ( ctx ) ;
if ( index = = NULL )
{
delete this ;
return NULL ;
}
}
2016-10-17 18:33:35 +00:00
if ( index - > ValueType - > GetRegType ( ) ! = REGT_INT & & index - > ValueType ! = TypeName )
2016-03-01 15:47:10 +00:00
{
ScriptPosition . Message ( MSG_ERROR , " Array index must be integer " ) ;
delete this ;
return NULL ;
}
PArray * arraytype = dyn_cast < PArray > ( Array - > ValueType ) ;
if ( arraytype = = NULL )
{
ScriptPosition . Message ( MSG_ERROR , " '[]' can only be used with arrays. " ) ;
delete this ;
return NULL ;
}
2016-08-11 10:22:37 +00:00
if ( index - > isConstant ( ) )
{
unsigned indexval = static_cast < FxConstant * > ( index ) - > GetValue ( ) . GetInt ( ) ;
if ( indexval > = arraytype - > ElementCount )
{
ScriptPosition . Message ( MSG_ERROR , " Array index out of bounds " ) ;
delete this ;
return NULL ;
}
}
2016-03-01 15:47:10 +00:00
ValueType = arraytype - > ElementType ;
2016-04-03 21:07:51 +00:00
if ( ValueType - > GetRegType ( ) ! = REGT_INT & & ValueType - > GetRegType ( ) ! = REGT_FLOAT )
2016-03-01 15:47:10 +00:00
{
// int arrays only for now
2016-04-03 21:07:51 +00:00
ScriptPosition . Message ( MSG_ERROR , " Only numeric arrays are supported. " ) ;
2016-03-01 15:47:10 +00:00
delete this ;
return NULL ;
}
2016-10-17 18:33:35 +00:00
if ( ! Array - > RequestAddress ( & AddressWritable ) )
{
ScriptPosition . Message ( MSG_ERROR , " Unable to dereference array. " ) ;
delete this ;
return NULL ;
}
2016-03-01 15:47:10 +00:00
return this ;
}
//==========================================================================
//
// in its current state this won't be able to do more than handle the args array.
//
//==========================================================================
ExpEmit FxArrayElement : : Emit ( VMFunctionBuilder * build )
{
ExpEmit start = Array - > Emit ( build ) ;
2016-04-03 21:07:51 +00:00
PArray * const arraytype = static_cast < PArray * > ( Array - > ValueType ) ;
2016-08-05 15:33:29 +00:00
ExpEmit dest ( build , arraytype - > ElementType - > GetRegType ( ) ) ;
2016-04-03 21:07:51 +00:00
2016-03-01 15:47:10 +00:00
if ( start . Konst )
{
ExpEmit tmpstart ( build , REGT_POINTER ) ;
build - > Emit ( OP_LKP , tmpstart . RegNum , start . RegNum ) ;
2016-08-05 15:33:29 +00:00
start . Free ( build ) ;
2016-03-01 15:47:10 +00:00
start = tmpstart ;
}
if ( index - > isConstant ( ) )
{
unsigned indexval = static_cast < FxConstant * > ( index ) - > GetValue ( ) . GetInt ( ) ;
2016-08-11 10:47:17 +00:00
assert ( indexval < arraytype - > ElementCount & & " Array index out of bounds " ) ;
2016-04-03 21:07:51 +00:00
indexval * = arraytype - > ElementSize ;
2016-08-05 15:33:29 +00:00
if ( AddressRequested )
{
if ( indexval ! = 0 )
{
build - > Emit ( OP_ADDA_RK , start . RegNum , start . RegNum , build - > GetConstantInt ( indexval ) ) ;
}
}
else
{
build - > Emit ( arraytype - > ElementType - > GetLoadOp ( ) , dest . RegNum ,
start . RegNum , build - > GetConstantInt ( indexval ) ) ;
}
2016-03-01 15:47:10 +00:00
}
else
{
ExpEmit indexv ( index - > Emit ( build ) ) ;
2016-04-03 21:07:51 +00:00
int shiftbits = 0 ;
2016-04-03 22:45:04 +00:00
while ( 1u < < shiftbits < arraytype - > ElementSize )
2016-04-03 21:07:51 +00:00
{
shiftbits + + ;
}
2016-04-24 02:00:35 +00:00
assert ( 1u < < shiftbits = = arraytype - > ElementSize & & " Element sizes other than power of 2 are not implemented " ) ;
2016-04-03 21:07:51 +00:00
build - > Emit ( OP_BOUND , indexv . RegNum , arraytype - > ElementCount ) ;
if ( shiftbits > 0 )
{
build - > Emit ( OP_SLL_RI , indexv . RegNum , indexv . RegNum , shiftbits ) ;
}
2016-08-05 15:33:29 +00:00
if ( AddressRequested )
{
build - > Emit ( OP_ADDA_RR , start . RegNum , start . RegNum , indexv . RegNum ) ;
}
else
{
build - > Emit ( arraytype - > ElementType - > GetLoadOp ( ) + 1 , // added 1 to use the *_R version that
dest . RegNum , start . RegNum , indexv . RegNum ) ; // takes the offset from a register
}
2016-03-01 15:47:10 +00:00
indexv . Free ( build ) ;
}
2016-08-05 15:33:29 +00:00
if ( AddressRequested )
{
dest . Free ( build ) ;
return start ;
}
2016-03-01 15:47:10 +00:00
start . Free ( build ) ;
return dest ;
}
//==========================================================================
//
//
//
//==========================================================================
2016-10-15 23:08:02 +00:00
FxFunctionCall : : FxFunctionCall ( FName methodname , FName rngname , FArgumentList * args , const FScriptPosition & pos )
2016-10-22 23:14:49 +00:00
: FxExpression ( EFX_FunctionCall , pos )
2016-03-01 15:47:10 +00:00
{
MethodName = methodname ;
2016-10-15 23:08:02 +00:00
RNG = nullptr ;
2016-03-01 15:47:10 +00:00
ArgList = args ;
2016-10-15 23:08:02 +00:00
if ( rngname ! = NAME_None )
{
switch ( MethodName )
{
case NAME_Random :
case NAME_FRandom :
case NAME_RandomPick :
case NAME_FRandomPick :
case NAME_Random2 :
RNG = FRandom : : StaticFindRNG ( rngname . GetChars ( ) ) ;
break ;
default :
pos . Message ( MSG_ERROR , " Cannot use named RNGs with %s " , MethodName . GetChars ( ) ) ;
break ;
}
}
2016-03-01 15:47:10 +00:00
}
//==========================================================================
//
//
//
//==========================================================================
FxFunctionCall : : ~ FxFunctionCall ( )
{
SAFE_DELETE ( ArgList ) ;
}
//==========================================================================
//
2016-10-15 22:12:33 +00:00
// Check function that gets called
//
//==========================================================================
static bool CheckArgSize ( FName fname , FArgumentList * args , int min , int max , FScriptPosition & sc )
{
int s = args ? args - > Size ( ) : 0 ;
if ( s < min )
{
sc . Message ( MSG_ERROR , " Insufficient arguments in call to %s, expected %d, got %d " , fname . GetChars ( ) , min , s ) ;
return false ;
}
else if ( s > max & & max > = 0 )
{
sc . Message ( MSG_ERROR , " Too many arguments in call to %s, expected %d, got %d " , fname . GetChars ( ) , min , s ) ;
return false ;
}
return true ;
}
//==========================================================================
//
//
2016-03-01 15:47:10 +00:00
//
//==========================================================================
FxExpression * FxFunctionCall : : Resolve ( FCompileContext & ctx )
{
2016-10-15 15:40:27 +00:00
ABORT ( ctx . Class ) ;
2016-10-22 10:10:19 +00:00
bool error = false ;
2016-10-15 18:16:44 +00:00
2016-10-22 10:10:19 +00:00
PFunction * afd = FindClassMemberFunction ( ctx . Class , ctx . Class , MethodName , ScriptPosition , & error ) ;
if ( error )
{
delete this ;
return nullptr ;
}
2016-10-15 18:16:44 +00:00
2016-10-13 22:40:20 +00:00
if ( afd ! = nullptr )
{
2016-10-22 10:10:19 +00:00
if ( ctx . Function - > Variants [ 0 ] . Flags & VARF_Static & & ! ( afd - > Variants [ 0 ] . Flags & VARF_Static ) )
{
ScriptPosition . Message ( MSG_ERROR , " Call to non-static function %s from a static context " , MethodName . GetChars ( ) ) ;
delete this ;
return nullptr ;
}
auto self = ! ( afd - > Variants [ 0 ] . Flags & VARF_Static ) ? new FxSelf ( ScriptPosition ) : nullptr ;
auto x = new FxVMFunctionCall ( self , afd , ArgList , ScriptPosition , false ) ;
2016-10-14 19:15:46 +00:00
ArgList = nullptr ;
delete this ;
return x - > Resolve ( ctx ) ;
2016-10-13 22:40:20 +00:00
}
2016-03-01 15:47:10 +00:00
for ( size_t i = 0 ; i < countof ( FxFlops ) ; + + i )
{
if ( MethodName = = FxFlops [ i ] . Name )
{
FxExpression * x = new FxFlopFunctionCall ( i , ArgList , ScriptPosition ) ;
2016-10-15 18:16:44 +00:00
ArgList = nullptr ;
2016-03-01 15:47:10 +00:00
delete this ;
return x - > Resolve ( ctx ) ;
}
}
int min , max , special ;
if ( MethodName = = NAME_ACS_NamedExecuteWithResult | | MethodName = = NAME_CallACS )
{
special = - ACS_ExecuteWithResult ;
min = 1 ;
max = 5 ;
}
else
{
special = P_FindLineSpecial ( MethodName . GetChars ( ) , & min , & max ) ;
}
if ( special ! = 0 & & min > = 0 )
{
int paramcount = ArgList ? ArgList - > Size ( ) : 0 ;
if ( paramcount < min )
{
ScriptPosition . Message ( MSG_ERROR , " Not enough parameters for '%s' (expected %d, got %d) " ,
MethodName . GetChars ( ) , min , paramcount ) ;
delete this ;
2016-10-15 18:16:44 +00:00
return nullptr ;
2016-03-01 15:47:10 +00:00
}
else if ( paramcount > max )
{
ScriptPosition . Message ( MSG_ERROR , " too many parameters for '%s' (expected %d, got %d) " ,
MethodName . GetChars ( ) , max , paramcount ) ;
delete this ;
2016-10-15 18:16:44 +00:00
return nullptr ;
2016-03-01 15:47:10 +00:00
}
2016-10-15 18:16:44 +00:00
FxExpression * self = ( ctx . Function & & ctx . Function - > Variants [ 0 ] . Flags & VARF_Method ) ? new FxSelf ( ScriptPosition ) : nullptr ;
FxExpression * x = new FxActionSpecialCall ( self , special , ArgList , ScriptPosition ) ;
ArgList = nullptr ;
2016-03-01 15:47:10 +00:00
delete this ;
return x - > Resolve ( ctx ) ;
}
2016-10-15 23:08:02 +00:00
// Last but not least: Check builtins. The random functions can take a named RNG if specified.
2016-10-15 22:12:33 +00:00
// Note that for all builtins the used arguments have to be nulled in the ArgList so that they won't get deleted before they get used.
FxExpression * func = nullptr ;
switch ( MethodName )
{
case NAME_Random :
if ( CheckArgSize ( NAME_Random , ArgList , 2 , 2 , ScriptPosition ) )
{
2016-10-15 23:08:02 +00:00
func = new FxRandom ( RNG , ( * ArgList ) [ 0 ] , ( * ArgList ) [ 1 ] , ScriptPosition , ctx . FromDecorate ) ;
2016-10-15 22:12:33 +00:00
( * ArgList ) [ 0 ] = ( * ArgList ) [ 1 ] = nullptr ;
}
break ;
case NAME_FRandom :
if ( CheckArgSize ( NAME_FRandom , ArgList , 2 , 2 , ScriptPosition ) )
{
2016-10-15 23:08:02 +00:00
func = new FxFRandom ( RNG , ( * ArgList ) [ 0 ] , ( * ArgList ) [ 1 ] , ScriptPosition ) ;
2016-10-15 22:12:33 +00:00
( * ArgList ) [ 0 ] = ( * ArgList ) [ 1 ] = nullptr ;
}
break ;
case NAME_RandomPick :
case NAME_FRandomPick :
if ( CheckArgSize ( MethodName , ArgList , 1 , - 1 , ScriptPosition ) )
{
2016-10-15 23:08:02 +00:00
func = new FxRandomPick ( RNG , * ArgList , MethodName = = NAME_FRandomPick , ScriptPosition , ctx . FromDecorate ) ;
2016-10-16 08:59:12 +00:00
for ( auto & i : * ArgList ) i = nullptr ;
2016-10-15 22:12:33 +00:00
}
break ;
case NAME_Random2 :
2016-10-16 08:59:12 +00:00
if ( CheckArgSize ( NAME_Random2 , ArgList , 0 , 1 , ScriptPosition ) )
2016-10-15 22:12:33 +00:00
{
2016-10-16 08:59:12 +00:00
func = new FxRandom2 ( RNG , ArgList - > Size ( ) = = 0 ? nullptr : ( * ArgList ) [ 0 ] , ScriptPosition , ctx . FromDecorate ) ;
if ( ArgList - > Size ( ) > 0 ) ( * ArgList ) [ 0 ] = nullptr ;
2016-10-15 22:12:33 +00:00
}
break ;
case NAME_Min :
case NAME_Max :
if ( CheckArgSize ( MethodName , ArgList , 2 , - 1 , ScriptPosition ) )
{
func = new FxMinMax ( * ArgList , MethodName , ScriptPosition ) ;
2016-10-16 08:59:12 +00:00
for ( auto & i : * ArgList ) i = nullptr ;
2016-10-15 22:12:33 +00:00
}
break ;
case NAME_Clamp :
if ( CheckArgSize ( MethodName , ArgList , 3 , 3 , ScriptPosition ) )
{
TArray < FxExpression * > pass ;
pass . Resize ( 2 ) ;
pass [ 0 ] = ( * ArgList ) [ 0 ] ;
pass [ 1 ] = ( * ArgList ) [ 1 ] ;
pass [ 0 ] = new FxMinMax ( pass , NAME_Max , ScriptPosition ) ;
pass [ 1 ] = ( * ArgList ) [ 2 ] ;
func = new FxMinMax ( * ArgList , NAME_Min , ScriptPosition ) ;
( * ArgList ) [ 0 ] = ( * ArgList ) [ 1 ] = ( * ArgList ) [ 2 ] = nullptr ;
}
break ;
case NAME_Abs :
if ( CheckArgSize ( MethodName , ArgList , 1 , 1 , ScriptPosition ) )
{
func = new FxAbs ( ( * ArgList ) [ 0 ] ) ;
( * ArgList ) [ 0 ] = nullptr ;
}
break ;
case NAME_ATan2 :
case NAME_VectorAngle :
if ( CheckArgSize ( MethodName , ArgList , 2 , 2 , ScriptPosition ) )
{
func = MethodName = = NAME_ATan2 ? new FxATan2 ( ( * ArgList ) [ 0 ] , ( * ArgList ) [ 1 ] , ScriptPosition ) : new FxATan2 ( ( * ArgList ) [ 1 ] , ( * ArgList ) [ 0 ] , ScriptPosition ) ;
( * ArgList ) [ 0 ] = ( * ArgList ) [ 1 ] = nullptr ;
}
break ;
default :
break ;
}
if ( func ! = nullptr )
{
delete this ;
return func - > Resolve ( ctx ) ;
}
2016-03-01 15:47:10 +00:00
ScriptPosition . Message ( MSG_ERROR , " Call to unknown function '%s' " , MethodName . GetChars ( ) ) ;
delete this ;
2016-10-15 18:16:44 +00:00
return nullptr ;
2016-03-01 15:47:10 +00:00
}
2016-10-22 10:10:19 +00:00
//==========================================================================
//
//
//
//==========================================================================
FxMemberFunctionCall : : FxMemberFunctionCall ( FxExpression * self , FName methodname , FArgumentList * args , const FScriptPosition & pos )
2016-10-22 23:14:49 +00:00
: FxExpression ( EFX_MemberFunctionCall , pos )
2016-10-22 10:10:19 +00:00
{
Self = self ;
MethodName = methodname ;
ArgList = args ;
}
//==========================================================================
//
//
//
//==========================================================================
FxMemberFunctionCall : : ~ FxMemberFunctionCall ( )
{
SAFE_DELETE ( Self ) ;
SAFE_DELETE ( ArgList ) ;
}
//==========================================================================
//
//
//
//==========================================================================
FxExpression * FxMemberFunctionCall : : Resolve ( FCompileContext & ctx )
{
ABORT ( ctx . Class ) ;
SAFE_RESOLVE ( Self , ctx ) ;
PClass * cls ;
bool staticonly = false ;
if ( Self - > ValueType - > IsKindOf ( RUNTIME_CLASS ( PClassPointer ) ) )
{
cls = static_cast < PClassPointer * > ( Self - > ValueType ) - > ClassRestriction ;
staticonly = true ;
}
else if ( Self - > ValueType - > IsKindOf ( RUNTIME_CLASS ( PPointer ) ) )
{
auto ptype = static_cast < PPointer * > ( Self - > ValueType ) - > PointedType ;
if ( ptype - > IsKindOf ( RUNTIME_CLASS ( PClass ) ) )
{
cls = static_cast < PClass * > ( ptype ) ;
}
else
{
ScriptPosition . Message ( MSG_ERROR , " Left hand side of %s must point to a class object \n " , MethodName . GetChars ( ) ) ;
delete this ;
return nullptr ;
}
}
else
{
ScriptPosition . Message ( MSG_ERROR , " Invalid expression on left hand side of %s \n " , MethodName . GetChars ( ) ) ;
delete this ;
return nullptr ;
}
bool error = false ;
PFunction * afd = FindClassMemberFunction ( cls , cls , MethodName , ScriptPosition , & error ) ;
if ( error )
{
delete this ;
return nullptr ;
}
if ( afd = = nullptr )
{
ScriptPosition . Message ( MSG_ERROR , " Unknown function %s \n " , MethodName . GetChars ( ) ) ;
delete this ;
return nullptr ;
}
if ( staticonly & & ! ( afd - > Variants [ 0 ] . Flags & VARF_Static ) )
{
if ( ! ctx . Class - > IsDescendantOf ( cls ) )
{
ScriptPosition . Message ( MSG_ERROR , " Cannot call non-static function %s::%s from here \n " , cls - > TypeName . GetChars ( ) , MethodName . GetChars ( ) ) ;
delete this ;
return nullptr ;
}
// If this is a qualified call to a parent class function, let it through (but this needs to disable virtual calls later.)
}
// do not pass the self pointer to static functions.
auto self = ! ( afd - > Variants [ 0 ] . Flags & VARF_Static ) ? Self : nullptr ;
auto x = new FxVMFunctionCall ( self , afd , ArgList , ScriptPosition , staticonly ) ;
ArgList = nullptr ;
2016-10-22 17:43:53 +00:00
if ( Self = = self ) Self = nullptr ;
2016-10-22 10:10:19 +00:00
delete this ;
return x - > Resolve ( ctx ) ;
}
2016-03-01 15:47:10 +00:00
//==========================================================================
//
// FxActionSpecialCall
//
// If special is negative, then the first argument will be treated as a
// name for ACS_NamedExecuteWithResult.
//
//==========================================================================
FxActionSpecialCall : : FxActionSpecialCall ( FxExpression * self , int special , FArgumentList * args , const FScriptPosition & pos )
2016-10-22 23:14:49 +00:00
: FxExpression ( EFX_ActionSpecialCall , pos )
2016-03-01 15:47:10 +00:00
{
Self = self ;
Special = special ;
ArgList = args ;
2016-08-02 16:50:34 +00:00
EmitTail = false ;
2016-03-01 15:47:10 +00:00
}
//==========================================================================
//
//
//
//==========================================================================
FxActionSpecialCall : : ~ FxActionSpecialCall ( )
{
SAFE_DELETE ( Self ) ;
SAFE_DELETE ( ArgList ) ;
}
//==========================================================================
//
//
//
//==========================================================================
2016-08-02 16:50:34 +00:00
PPrototype * FxActionSpecialCall : : ReturnProto ( )
{
EmitTail = true ;
return FxExpression : : ReturnProto ( ) ;
}
//==========================================================================
//
//
//
//==========================================================================
2016-03-01 15:47:10 +00:00
FxExpression * FxActionSpecialCall : : Resolve ( FCompileContext & ctx )
{
CHECKRESOLVED ( ) ;
bool failed = false ;
2016-10-15 18:16:44 +00:00
SAFE_RESOLVE_OPT ( Self , ctx ) ;
2016-03-01 15:47:10 +00:00
if ( ArgList ! = NULL )
{
for ( unsigned i = 0 ; i < ArgList - > Size ( ) ; i + + )
{
( * ArgList ) [ i ] = ( * ArgList ) [ i ] - > Resolve ( ctx ) ;
if ( ( * ArgList ) [ i ] = = NULL ) failed = true ;
if ( Special < 0 & & i = = 0 )
{
if ( ( * ArgList ) [ i ] - > ValueType ! = TypeName )
{
ScriptPosition . Message ( MSG_ERROR , " Name expected for parameter %d " , i ) ;
failed = true ;
}
}
else if ( ( * ArgList ) [ i ] - > ValueType - > GetRegType ( ) ! = REGT_INT )
{
if ( ( * ArgList ) [ i ] - > ValueType - > GetRegType ( ) = = REGT_FLOAT /* lax */ )
{
2016-10-15 19:35:31 +00:00
( * ArgList ) [ i ] = new FxIntCast ( ( * ArgList ) [ i ] , ctx . FromDecorate ) ;
2016-03-01 15:47:10 +00:00
}
else
{
ScriptPosition . Message ( MSG_ERROR , " Integer expected for parameter %d " , i ) ;
failed = true ;
}
}
}
if ( failed )
{
delete this ;
return NULL ;
}
}
ValueType = TypeSInt32 ;
return this ;
}
//==========================================================================
//
//
//
//==========================================================================
int DecoCallLineSpecial ( VMFrameStack * stack , VMValue * param , int numparam , VMReturn * ret , int numret )
{
2016-03-04 14:11:20 +00:00
assert ( numparam > 2 & & numparam < 8 ) ;
2016-03-01 15:47:10 +00:00
assert ( param [ 0 ] . Type = = REGT_INT ) ;
assert ( param [ 1 ] . Type = = REGT_POINTER ) ;
int v [ 5 ] = { 0 } ;
for ( int i = 2 ; i < numparam ; + + i )
{
v [ i - 2 ] = param [ i ] . i ;
}
2016-08-02 16:50:34 +00:00
ACTION_RETURN_INT ( P_ExecuteSpecial ( param [ 0 ] . i , NULL , reinterpret_cast < AActor * > ( param [ 1 ] . a ) , false , v [ 0 ] , v [ 1 ] , v [ 2 ] , v [ 3 ] , v [ 4 ] ) ) ;
2016-03-01 15:47:10 +00:00
}
ExpEmit FxActionSpecialCall : : Emit ( VMFunctionBuilder * build )
{
unsigned i = 0 ;
build - > Emit ( OP_PARAMI , abs ( Special ) ) ; // pass special number
2016-10-15 18:16:44 +00:00
// fixme: This really should use the Self pointer that got passed to this class instead of just using the first argument from the function.
// Once static functions are possible, or specials can be called through a member access operator this won't work anymore.
build - > Emit ( OP_PARAM , 0 , REGT_POINTER , 0 ) ; // pass self
2016-03-01 15:47:10 +00:00
if ( ArgList ! = NULL )
{
for ( ; i < ArgList - > Size ( ) ; + + i )
{
FxExpression * argex = ( * ArgList ) [ i ] ;
if ( Special < 0 & & i = = 0 )
{
assert ( argex - > ValueType = = TypeName ) ;
assert ( argex - > isConstant ( ) ) ;
build - > EmitParamInt ( - static_cast < FxConstant * > ( argex ) - > GetValue ( ) . GetName ( ) ) ;
}
else
{
assert ( argex - > ValueType - > GetRegType ( ) = = REGT_INT ) ;
if ( argex - > isConstant ( ) )
{
build - > EmitParamInt ( static_cast < FxConstant * > ( argex ) - > GetValue ( ) . GetInt ( ) ) ;
}
else
{
ExpEmit arg ( argex - > Emit ( build ) ) ;
build - > Emit ( OP_PARAM , 0 , arg . RegType , arg . RegNum ) ;
arg . Free ( build ) ;
}
}
}
}
// Call the DecoCallLineSpecial function to perform the desired special.
VMFunction * callfunc ;
PSymbol * sym = FindDecorateBuiltinFunction ( NAME_DecoCallLineSpecial , DecoCallLineSpecial ) ;
assert ( sym - > IsKindOf ( RUNTIME_CLASS ( PSymbolVMFunction ) ) ) ;
assert ( ( ( PSymbolVMFunction * ) sym ) - > Function ! = NULL ) ;
callfunc = ( ( PSymbolVMFunction * ) sym ) - > Function ;
2016-08-02 16:50:34 +00:00
if ( EmitTail )
{
build - > Emit ( OP_TAIL_K , build - > GetConstantAddress ( callfunc , ATAG_OBJECT ) , 2 + i , 0 ) ;
ExpEmit call ;
call . Final = true ;
return call ;
}
2016-03-01 15:47:10 +00:00
ExpEmit dest ( build , REGT_INT ) ;
build - > Emit ( OP_CALL_K , build - > GetConstantAddress ( callfunc , ATAG_OBJECT ) , 2 + i , 1 ) ;
build - > Emit ( OP_RESULT , 0 , REGT_INT , dest . RegNum ) ;
return dest ;
}
//==========================================================================
//
// FxVMFunctionCall
//
//==========================================================================
2016-10-22 10:10:19 +00:00
FxVMFunctionCall : : FxVMFunctionCall ( FxExpression * self , PFunction * func , FArgumentList * args , const FScriptPosition & pos , bool novirtual )
2016-10-22 23:14:49 +00:00
: FxExpression ( EFX_VMFunctionCall , pos )
2016-03-01 15:47:10 +00:00
{
2016-10-22 10:10:19 +00:00
Self = self ;
2016-03-01 15:47:10 +00:00
Function = func ;
ArgList = args ;
2016-08-02 16:50:34 +00:00
EmitTail = false ;
2016-10-22 10:10:19 +00:00
NoVirtual = novirtual ;
2016-03-01 15:47:10 +00:00
}
//==========================================================================
//
//
//
//==========================================================================
FxVMFunctionCall : : ~ FxVMFunctionCall ( )
{
SAFE_DELETE ( ArgList ) ;
}
2016-08-02 16:50:34 +00:00
//==========================================================================
//
//
//
//==========================================================================
PPrototype * FxVMFunctionCall : : ReturnProto ( )
{
EmitTail = true ;
2016-10-15 12:36:08 +00:00
return Function - > Variants [ 0 ] . Proto ;
2016-08-02 16:50:34 +00:00
}
//==========================================================================
//
//
//
//==========================================================================
VMFunction * FxVMFunctionCall : : GetDirectFunction ( )
{
2016-10-22 14:35:48 +00:00
// If this return statement calls a non-virtual function with no arguments,
2016-08-02 16:50:34 +00:00
// then it can be a "direct" function. That is, the DECORATE
// definition can call that function directly without wrapping
// it inside VM code.
2016-10-22 14:35:48 +00:00
if ( ( ArgList ? ArgList - > Size ( ) : 0 ) = = 0 & & ! ( Function - > Variants [ 0 ] . Flags & VARF_Virtual ) )
2016-08-02 16:50:34 +00:00
{
return Function - > Variants [ 0 ] . Implementation ;
}
return nullptr ;
}
2016-03-01 15:47:10 +00:00
//==========================================================================
//
// FxVMFunctionCall :: Resolve
//
//==========================================================================
FxExpression * FxVMFunctionCall : : Resolve ( FCompileContext & ctx )
{
CHECKRESOLVED ( ) ;
2016-10-22 10:10:19 +00:00
SAFE_RESOLVE_OPT ( Self , ctx ) ;
2016-03-01 15:47:10 +00:00
bool failed = false ;
2016-10-15 12:36:08 +00:00
auto proto = Function - > Variants [ 0 ] . Proto ;
2016-10-14 19:15:46 +00:00
auto argtypes = proto - > ArgumentTypes ;
2016-10-22 14:35:48 +00:00
int implicit = Function - > GetImplicitArgs ( ) ;
2016-03-01 15:47:10 +00:00
2016-10-22 10:10:19 +00:00
// This should never happen.
if ( Self = = nullptr & & ! ( Function - > Variants [ 0 ] . Flags & VARF_Static ) )
{
ScriptPosition . Message ( MSG_ERROR , " Call to non-static function without a self pointer " ) ;
delete this ;
return nullptr ;
}
2016-03-01 15:47:10 +00:00
if ( ArgList ! = NULL )
{
2016-10-26 12:15:11 +00:00
bool foundvarargs = false ;
PType * type = nullptr ;
2016-03-01 15:47:10 +00:00
for ( unsigned i = 0 ; i < ArgList - > Size ( ) ; i + + )
{
2016-10-26 12:15:11 +00:00
// Varargs must all have the same type as the last typed argument. A_Jump is the only function using it.
if ( ! foundvarargs )
{
if ( argtypes [ i + implicit ] = = nullptr ) foundvarargs = true ;
else type = argtypes [ i + implicit ] ;
}
assert ( type ! = nullptr ) ;
FxExpression * x = new FxTypeCast ( ( * ArgList ) [ i ] , type , false ) ;
2016-10-16 20:32:52 +00:00
x = x - > Resolve ( ctx ) ;
failed | = ( x = = nullptr ) ;
( * ArgList ) [ i ] = x ;
2016-03-01 15:47:10 +00:00
}
}
if ( failed )
{
delete this ;
return NULL ;
}
2016-10-14 19:15:46 +00:00
TArray < PType * > & rets = proto - > ReturnTypes ;
2016-03-01 15:47:10 +00:00
if ( rets . Size ( ) > 0 )
{
ValueType = rets [ 0 ] ;
}
2016-08-02 16:50:34 +00:00
else
{
ValueType = TypeVoid ;
}
2016-03-01 15:47:10 +00:00
return this ;
}
//==========================================================================
//
2016-10-22 10:10:19 +00:00
//
2016-03-01 15:47:10 +00:00
//
//==========================================================================
ExpEmit FxVMFunctionCall : : Emit ( VMFunctionBuilder * build )
{
2016-10-02 03:29:57 +00:00
assert ( ( build - > IsActionFunc & & build - > Registers [ REGT_POINTER ] . GetMostUsed ( ) > = NAP ) | |
2016-10-22 10:10:19 +00:00
( ! build - > IsActionFunc & & build - > Registers [ REGT_POINTER ] . GetMostUsed ( ) > = 1 ) ) ;
2016-08-02 16:50:34 +00:00
int count = ( ArgList ? ArgList - > Size ( ) : 0 ) ;
2016-03-01 15:47:10 +00:00
if ( count = = 1 )
{
ExpEmit reg ;
2016-08-02 16:50:34 +00:00
if ( CheckEmitCast ( build , EmitTail , reg ) )
2016-03-01 15:47:10 +00:00
{
return reg ;
}
}
2016-10-12 15:50:23 +00:00
2016-10-22 15:49:08 +00:00
// Emit code to pass implied parameters
if ( Function - > Variants [ 0 ] . Flags & VARF_Method )
2016-03-01 15:47:10 +00:00
{
2016-10-22 15:49:08 +00:00
assert ( Self ! = nullptr ) ;
ExpEmit selfemit = Self - > Emit ( build ) ;
assert ( selfemit . RegType = = REGT_POINTER ) ;
build - > Emit ( OP_PARAM , 0 , selfemit . RegType , selfemit . RegNum ) ;
count + = 1 ;
2016-03-01 15:47:10 +00:00
}
2016-10-22 15:49:08 +00:00
if ( Function - > Variants [ 0 ] . Flags & VARF_Action )
2016-03-01 15:47:10 +00:00
{
2016-10-22 15:49:08 +00:00
static_assert ( NAP = = 3 , " This code needs to be updated if NAP changes " ) ;
if ( build - > IsActionFunc )
2016-10-02 03:29:57 +00:00
{
2016-10-22 15:49:08 +00:00
build - > Emit ( OP_PARAM , 0 , REGT_POINTER , 1 ) ;
build - > Emit ( OP_PARAM , 0 , REGT_POINTER , 2 ) ;
2016-10-02 03:29:57 +00:00
}
2016-10-22 15:49:08 +00:00
else
2016-10-02 03:29:57 +00:00
{
2016-10-22 15:49:08 +00:00
int null = build - > GetConstantAddress ( nullptr , ATAG_GENERIC ) ;
build - > Emit ( OP_PARAM , 0 , REGT_POINTER | REGT_KONST , null ) ;
build - > Emit ( OP_PARAM , 0 , REGT_POINTER | REGT_KONST , null ) ;
2016-10-02 03:29:57 +00:00
}
2016-10-22 15:49:08 +00:00
count + = 2 ;
2016-03-01 15:47:10 +00:00
}
2016-10-12 15:50:23 +00:00
2016-03-01 15:47:10 +00:00
// Emit code to pass explicit parameters
if ( ArgList ! = NULL )
{
for ( unsigned i = 0 ; i < ArgList - > Size ( ) ; + + i )
{
EmitParameter ( build , ( * ArgList ) [ i ] , ScriptPosition ) ;
}
}
// Get a constant register for this function
VMFunction * vmfunc = Function - > Variants [ 0 ] . Implementation ;
int funcaddr = build - > GetConstantAddress ( vmfunc , ATAG_OBJECT ) ;
// Emit the call
2016-08-02 16:50:34 +00:00
if ( EmitTail )
2016-03-01 15:47:10 +00:00
{ // Tail call
build - > Emit ( OP_TAIL_K , funcaddr , count , 0 ) ;
2016-08-02 16:50:34 +00:00
ExpEmit call ;
call . Final = true ;
return call ;
2016-03-01 15:47:10 +00:00
}
else if ( vmfunc - > Proto - > ReturnTypes . Size ( ) > 0 )
{ // Call, expecting one result
ExpEmit reg ( build , vmfunc - > Proto - > ReturnTypes [ 0 ] - > GetRegType ( ) ) ;
build - > Emit ( OP_CALL_K , funcaddr , count , 1 ) ;
build - > Emit ( OP_RESULT , 0 , reg . RegType , reg . RegNum ) ;
return reg ;
}
else
{ // Call, expecting no results
build - > Emit ( OP_CALL_K , funcaddr , count , 0 ) ;
return ExpEmit ( ) ;
}
}
//==========================================================================
//
// If calling one of the casting kludge functions, don't bother calling the
// function; just use the parameter directly. Returns true if this was a
// kludge function, false otherwise.
//
//==========================================================================
bool FxVMFunctionCall : : CheckEmitCast ( VMFunctionBuilder * build , bool returnit , ExpEmit & reg )
{
FName funcname = Function - > SymbolName ;
if ( funcname = = NAME___decorate_internal_int__ | |
funcname = = NAME___decorate_internal_bool__ | |
2016-03-01 17:36:15 +00:00
funcname = = NAME___decorate_internal_state__ | |
funcname = = NAME___decorate_internal_float__ )
2016-03-01 15:47:10 +00:00
{
FxExpression * arg = ( * ArgList ) [ 0 ] ;
if ( returnit )
{
if ( arg - > isConstant ( ) & &
( funcname = = NAME___decorate_internal_int__ | |
funcname = = NAME___decorate_internal_bool__ ) )
{ // Use immediate version for integers in range
build - > EmitRetInt ( 0 , true , static_cast < FxConstant * > ( arg ) - > GetValue ( ) . Int ) ;
}
else
{
ExpEmit where = arg - > Emit ( build ) ;
build - > Emit ( OP_RET , RET_FINAL , where . RegType | ( where . Konst ? REGT_KONST : 0 ) , where . RegNum ) ;
where . Free ( build ) ;
}
reg = ExpEmit ( ) ;
2016-08-02 16:50:34 +00:00
reg . Final = true ;
2016-03-01 15:47:10 +00:00
}
else
{
reg = arg - > Emit ( build ) ;
}
return true ;
}
return false ;
}
//==========================================================================
//
//
//
//==========================================================================
FxFlopFunctionCall : : FxFlopFunctionCall ( size_t index , FArgumentList * args , const FScriptPosition & pos )
2016-10-22 23:14:49 +00:00
: FxExpression ( EFX_FlopFunctionCall , pos )
2016-03-01 15:47:10 +00:00
{
assert ( index < countof ( FxFlops ) & & " FLOP index out of range " ) ;
Index = ( int ) index ;
ArgList = args ;
}
//==========================================================================
//
//
//
//==========================================================================
FxFlopFunctionCall : : ~ FxFlopFunctionCall ( )
{
SAFE_DELETE ( ArgList ) ;
}
FxExpression * FxFlopFunctionCall : : Resolve ( FCompileContext & ctx )
{
CHECKRESOLVED ( ) ;
if ( ArgList = = NULL | | ArgList - > Size ( ) ! = 1 )
{
ScriptPosition . Message ( MSG_ERROR , " %s only has one parameter " , FName ( FxFlops [ Index ] . Name ) . GetChars ( ) ) ;
delete this ;
return NULL ;
}
( * ArgList ) [ 0 ] = ( * ArgList ) [ 0 ] - > Resolve ( ctx ) ;
if ( ( * ArgList ) [ 0 ] = = NULL )
{
delete this ;
return NULL ;
}
if ( ! ( * ArgList ) [ 0 ] - > IsNumeric ( ) )
{
ScriptPosition . Message ( MSG_ERROR , " numeric value expected for parameter " ) ;
delete this ;
return NULL ;
}
if ( ( * ArgList ) [ 0 ] - > isConstant ( ) )
{
double v = static_cast < FxConstant * > ( ( * ArgList ) [ 0 ] ) - > GetValue ( ) . GetFloat ( ) ;
v = FxFlops [ Index ] . Evaluate ( v ) ;
FxExpression * x = new FxConstant ( v , ScriptPosition ) ;
delete this ;
return x ;
}
if ( ( * ArgList ) [ 0 ] - > ValueType - > GetRegType ( ) = = REGT_INT )
{
( * ArgList ) [ 0 ] = new FxFloatCast ( ( * ArgList ) [ 0 ] ) ;
}
ValueType = TypeFloat64 ;
return this ;
}
//==========================================================================
//
//
//==========================================================================
ExpEmit FxFlopFunctionCall : : Emit ( VMFunctionBuilder * build )
{
ExpEmit v = ( * ArgList ) [ 0 ] - > Emit ( build ) ;
assert ( ! v . Konst & & v . RegType = = REGT_FLOAT ) ;
build - > Emit ( OP_FLOP , v . RegNum , v . RegNum , FxFlops [ Index ] . Flop ) ;
return v ;
}
//==========================================================================
//
2016-10-19 23:09:35 +00:00
// FxSequence :: Resolve
2016-03-01 15:47:10 +00:00
//
//==========================================================================
2016-10-19 23:09:35 +00:00
FxExpression * FxSequence : : Resolve ( FCompileContext & ctx )
2016-03-01 15:47:10 +00:00
{
CHECKRESOLVED ( ) ;
for ( unsigned i = 0 ; i < Expressions . Size ( ) ; + + i )
{
if ( NULL = = ( Expressions [ i ] = Expressions [ i ] - > Resolve ( ctx ) ) )
{
delete this ;
2016-10-19 17:05:48 +00:00
return nullptr ;
2016-03-01 15:47:10 +00:00
}
}
return this ;
}
//==========================================================================
//
2016-10-19 23:09:35 +00:00
// FxSequence :: Emit
2016-03-01 15:47:10 +00:00
//
//==========================================================================
2016-10-19 23:09:35 +00:00
ExpEmit FxSequence : : Emit ( VMFunctionBuilder * build )
2016-03-01 15:47:10 +00:00
{
for ( unsigned i = 0 ; i < Expressions . Size ( ) ; + + i )
{
ExpEmit v = Expressions [ i ] - > Emit ( build ) ;
// Throw away any result. We don't care about it.
v . Free ( build ) ;
}
return ExpEmit ( ) ;
}
//==========================================================================
//
2016-10-19 23:09:35 +00:00
// FxSequence :: GetDirectFunction
2016-03-01 15:47:10 +00:00
//
//==========================================================================
2016-10-19 23:09:35 +00:00
VMFunction * FxSequence : : GetDirectFunction ( )
2016-03-01 15:47:10 +00:00
{
if ( Expressions . Size ( ) = = 1 )
{
return Expressions [ 0 ] - > GetDirectFunction ( ) ;
}
return NULL ;
}
2016-10-19 23:09:35 +00:00
//==========================================================================
//
// FxCompoundStatement :: Resolve
//
//==========================================================================
FxExpression * FxCompoundStatement : : Resolve ( FCompileContext & ctx )
{
auto outer = ctx . Block ;
Outer = ctx . Block ;
ctx . Block = this ;
auto x = FxSequence : : Resolve ( ctx ) ;
ctx . Block = outer ;
2016-10-21 08:09:01 +00:00
return x ;
2016-10-19 23:09:35 +00:00
}
//==========================================================================
//
// FxCompoundStatement :: Emit
//
//==========================================================================
ExpEmit FxCompoundStatement : : Emit ( VMFunctionBuilder * build )
{
auto e = FxSequence : : Emit ( build ) ;
// Release all local variables in this block.
for ( auto l : LocalVars )
{
l - > Release ( build ) ;
}
return e ;
}
2016-10-19 17:05:48 +00:00
//==========================================================================
//
// FxCompoundStatement :: FindLocalVariable
//
// Looks for a variable name in any of the containing compound statements
2016-10-20 14:55:12 +00:00
// This does a simple linear search on each block's variables.
// The lists here normally don't get large enough to justify something more complex.
2016-10-19 17:05:48 +00:00
//
//==========================================================================
2016-10-20 14:55:12 +00:00
FxLocalVariableDeclaration * FxCompoundStatement : : FindLocalVariable ( FName name , FCompileContext & ctx )
2016-10-19 17:05:48 +00:00
{
auto block = this ;
while ( block ! = nullptr )
{
for ( auto l : block - > LocalVars )
{
if ( l - > Name = = name )
{
return l ;
}
}
block = block - > Outer ;
}
2016-10-20 14:55:12 +00:00
// finally check the context for function arguments
for ( auto arg : ctx . FunctionArgs )
{
if ( arg - > Name = = name )
{
return arg ;
}
}
2016-10-19 17:05:48 +00:00
return nullptr ;
}
//==========================================================================
//
// FxCompoundStatement :: CheckLocalVariable
//
// Checks if the current block already contains a local variable
// of the given name.
//
//==========================================================================
bool FxCompoundStatement : : CheckLocalVariable ( FName name )
{
for ( auto l : LocalVars )
{
if ( l - > Name = = name )
{
return true ;
}
}
return false ;
}
2016-10-23 10:00:25 +00:00
//==========================================================================
//
// FxSwitchStatement
//
//==========================================================================
FxSwitchStatement : : FxSwitchStatement ( FxExpression * cond , FArgumentList * content , const FScriptPosition & pos )
: FxExpression ( EFX_SwitchStatement , pos )
{
Condition = new FxIntCast ( cond , false ) ;
Content = content ;
}
FxSwitchStatement : : ~ FxSwitchStatement ( )
{
SAFE_DELETE ( Condition ) ;
SAFE_DELETE ( Content ) ;
}
FxExpression * FxSwitchStatement : : Resolve ( FCompileContext & ctx )
{
CHECKRESOLVED ( ) ;
SAFE_RESOLVE ( Condition , ctx ) ;
if ( Content = = nullptr | | Content - > Size ( ) = = 0 )
{
ScriptPosition . Message ( MSG_WARNING , " Empty switch statement " ) ;
if ( Condition - > isConstant ( ) )
{
return new FxNop ( ScriptPosition ) ;
}
else
{
// The condition may have a side effect so it should be processed (possible to-do: Analyze all nodes in there and delete if not.)
auto x = Condition ;
Condition = nullptr ;
delete this ;
2016-10-26 09:30:30 +00:00
x - > NeedResult = false ;
return x ;
2016-10-23 10:00:25 +00:00
}
}
for ( auto & line : * Content )
{
// Do not resolve breaks, they need special treatment inside switch blocks.
if ( line - > ExprType ! = EFX_JumpStatement | | static_cast < FxJumpStatement * > ( line ) - > Token ! = TK_Break )
{
SAFE_RESOLVE ( line , ctx ) ;
2016-10-26 09:30:30 +00:00
line - > NeedResult = false ;
2016-10-23 10:00:25 +00:00
}
}
if ( Condition - > isConstant ( ) )
{
ScriptPosition . Message ( MSG_WARNING , " Case expression is constant " ) ;
auto & content = * Content ;
2016-10-25 15:07:19 +00:00
int defaultindex = - 1 ;
int defaultbreak = - 1 ;
int caseindex = - 1 ;
int casebreak = - 1 ;
2016-10-23 10:00:25 +00:00
// look for a case label with a matching value
for ( unsigned i = 0 ; i < content . Size ( ) ; i + + )
{
if ( content [ i ] ! = nullptr )
{
if ( content [ i ] - > ExprType = = EFX_CaseStatement )
{
auto casestmt = static_cast < FxCaseStatement * > ( content [ i ] ) ;
if ( casestmt - > Condition = = nullptr ) defaultindex = i ;
else if ( casestmt - > CaseValue = = static_cast < FxConstant * > ( Condition ) - > GetValue ( ) . GetInt ( ) ) caseindex = i ;
}
if ( content [ i ] - > ExprType = = EFX_JumpStatement & & static_cast < FxJumpStatement * > ( content [ i ] ) - > Token = = TK_Break )
{
if ( defaultindex > = 0 & & defaultbreak < 0 ) defaultbreak = i ;
if ( caseindex > = 0 & & casebreak < 0 )
{
casebreak = i ;
break ; // when we find this we do not need to look any further.
}
}
}
}
if ( caseindex < 0 )
{
caseindex = defaultindex ;
casebreak = defaultbreak ;
}
if ( caseindex > 0 & & casebreak - caseindex > 1 )
{
auto seq = new FxSequence ( ScriptPosition ) ;
2016-10-25 15:07:19 +00:00
for ( int i = caseindex + 1 ; i < casebreak ; i + + )
2016-10-23 10:00:25 +00:00
{
if ( content [ i ] ! = nullptr & & content [ i ] - > ExprType ! = EFX_CaseStatement )
{
seq - > Add ( content [ i ] ) ;
content [ i ] = nullptr ;
}
}
delete this ;
return seq - > Resolve ( ctx ) ;
}
delete this ;
return new FxNop ( ScriptPosition ) ;
}
int mincase = INT_MAX ;
int maxcase = INT_MIN ;
for ( auto line : * Content )
{
if ( line - > ExprType = = EFX_CaseStatement )
{
auto casestmt = static_cast < FxCaseStatement * > ( line ) ;
if ( casestmt - > Condition ! = nullptr )
{
CaseAddr ca = { casestmt - > CaseValue , 0 } ;
CaseAddresses . Push ( ca ) ;
if ( ca . casevalue < mincase ) mincase = ca . casevalue ;
if ( ca . casevalue > maxcase ) maxcase = ca . casevalue ;
}
}
}
return this ;
}
ExpEmit FxSwitchStatement : : Emit ( VMFunctionBuilder * build )
{
assert ( Condition ! = nullptr ) ;
ExpEmit emit = Condition - > Emit ( build ) ;
assert ( emit . RegType = = REGT_INT ) ;
// todo:
// - sort jump table by value.
// - optimize the switch dispatcher to run in native code instead of executing each single branch instruction on its own.
// e.g.: build->Emit(OP_SWITCH, emit.RegNum, build->GetConstantInt(CaseAddresses.Size());
for ( auto & ca : CaseAddresses )
{
if ( ca . casevalue > = 0 & & ca . casevalue < = 0xffff )
{
build - > Emit ( OP_TEST , emit . RegNum , ( VM_SHALF ) ca . casevalue ) ;
}
else if ( ca . casevalue < 0 & & ca . casevalue > = - 0xffff )
{
build - > Emit ( OP_TESTN , emit . RegNum , ( VM_SHALF ) - ca . casevalue ) ;
}
else
{
build - > Emit ( OP_EQ_K , 1 , emit . RegNum , build - > GetConstantInt ( ca . casevalue ) ) ;
}
ca . jumpaddress = build - > Emit ( OP_JMP , 0 ) ;
}
size_t DefaultAddress = build - > Emit ( OP_JMP , 0 ) ;
TArray < size_t > BreakAddresses ;
for ( auto line : * Content )
{
switch ( line - > ExprType )
{
case EFX_CaseStatement :
if ( static_cast < FxCaseStatement * > ( line ) - > Condition ! = nullptr )
{
for ( auto & ca : CaseAddresses )
{
if ( ca . casevalue = = static_cast < FxCaseStatement * > ( line ) - > CaseValue )
{
build - > BackpatchToHere ( ca . jumpaddress ) ;
break ;
}
}
}
else
{
build - > BackpatchToHere ( DefaultAddress ) ;
}
break ;
case EFX_JumpStatement :
if ( static_cast < FxJumpStatement * > ( line ) - > Token = = TK_Break )
{
BreakAddresses . Push ( build - > Emit ( OP_JMP , 0 ) ) ;
break ;
}
// fall through for continue.
default :
line - > Emit ( build ) ;
break ;
}
}
for ( auto addr : BreakAddresses )
{
build - > BackpatchToHere ( addr ) ;
}
return ExpEmit ( ) ;
}
//==========================================================================
//
// FxCaseStatement
//
//==========================================================================
FxCaseStatement : : FxCaseStatement ( FxExpression * cond , const FScriptPosition & pos )
: FxExpression ( EFX_CaseStatement , pos )
{
Condition = cond ? new FxIntCast ( cond , false ) : nullptr ;
}
FxCaseStatement : : ~ FxCaseStatement ( )
{
SAFE_DELETE ( Condition ) ;
}
FxExpression * FxCaseStatement : : Resolve ( FCompileContext & ctx )
{
CHECKRESOLVED ( ) ;
SAFE_RESOLVE_OPT ( Condition , ctx ) ;
if ( Condition ! = nullptr )
{
if ( ! Condition - > isConstant ( ) )
{
ScriptPosition . Message ( MSG_ERROR , " Case label must be a constant value " ) ;
delete this ;
return nullptr ;
}
CaseValue = static_cast < FxConstant * > ( Condition ) - > GetValue ( ) . GetInt ( ) ;
}
return this ;
}
2016-03-01 15:47:10 +00:00
//==========================================================================
//
// FxIfStatement
//
//==========================================================================
FxIfStatement : : FxIfStatement ( FxExpression * cond , FxExpression * true_part ,
FxExpression * false_part , const FScriptPosition & pos )
2016-10-22 23:14:49 +00:00
: FxExpression ( EFX_IfStatement , pos )
2016-03-01 15:47:10 +00:00
{
Condition = cond ;
WhenTrue = true_part ;
WhenFalse = false_part ;
2016-10-26 09:30:30 +00:00
if ( WhenTrue ! = nullptr ) WhenTrue - > NeedResult = false ;
if ( WhenFalse ! = nullptr ) WhenFalse - > NeedResult = false ;
2016-03-01 15:47:10 +00:00
assert ( cond ! = NULL ) ;
}
FxIfStatement : : ~ FxIfStatement ( )
{
SAFE_DELETE ( Condition ) ;
SAFE_DELETE ( WhenTrue ) ;
SAFE_DELETE ( WhenFalse ) ;
}
FxExpression * FxIfStatement : : Resolve ( FCompileContext & ctx )
{
CHECKRESOLVED ( ) ;
2016-07-04 00:19:52 +00:00
if ( WhenTrue = = nullptr & & WhenFalse = = nullptr )
2016-03-01 15:47:10 +00:00
{ // We don't do anything either way, so disappear
delete this ;
2016-07-04 00:19:52 +00:00
return nullptr ;
2016-03-01 15:47:10 +00:00
}
2016-07-04 00:19:52 +00:00
SAFE_RESOLVE ( Condition , ctx ) ;
if ( Condition - > ValueType ! = TypeBool )
{
2016-10-23 13:30:58 +00:00
Condition = new FxBoolCast ( Condition , false ) ;
2016-07-04 00:19:52 +00:00
SAFE_RESOLVE ( Condition , ctx ) ;
}
if ( WhenTrue ! = nullptr )
2016-03-01 15:47:10 +00:00
{
WhenTrue = WhenTrue - > Resolve ( ctx ) ;
ABORT ( WhenTrue ) ;
}
2016-07-04 00:19:52 +00:00
if ( WhenFalse ! = nullptr )
2016-03-01 15:47:10 +00:00
{
WhenFalse = WhenFalse - > Resolve ( ctx ) ;
ABORT ( WhenFalse ) ;
}
2016-07-04 00:19:52 +00:00
2016-03-01 15:47:10 +00:00
ValueType = TypeVoid ;
if ( Condition - > isConstant ( ) )
{
ExpVal condval = static_cast < FxConstant * > ( Condition ) - > GetValue ( ) ;
bool result = condval . GetBool ( ) ;
FxExpression * e = result ? WhenTrue : WhenFalse ;
delete ( result ? WhenFalse : WhenTrue ) ;
WhenTrue = WhenFalse = NULL ;
2016-04-07 18:31:12 +00:00
if ( e = = NULL ) e = new FxNop ( ScriptPosition ) ; // create a dummy if this statement gets completely removed by optimizing out the constant parts.
2016-03-01 15:47:10 +00:00
delete this ;
return e ;
}
2016-07-04 00:19:52 +00:00
2016-03-01 15:47:10 +00:00
return this ;
}
ExpEmit FxIfStatement : : Emit ( VMFunctionBuilder * build )
{
ExpEmit v ;
size_t jumpspot ;
FxExpression * path1 , * path2 ;
int condcheck ;
// This is pretty much copied from FxConditional, except we don't
// keep any results.
ExpEmit cond = Condition - > Emit ( build ) ;
2016-10-23 13:30:58 +00:00
assert ( cond . RegType ! = REGT_STRING & & ! cond . Konst ) ;
2016-03-01 15:47:10 +00:00
if ( WhenTrue ! = NULL )
{
path1 = WhenTrue ;
path2 = WhenFalse ;
condcheck = 1 ;
}
else
{
// When there is only a false path, reverse the condition so we can
// treat it as a true path.
assert ( WhenFalse ! = NULL ) ;
path1 = WhenFalse ;
path2 = NULL ;
condcheck = 0 ;
}
// Test condition.
2016-10-23 13:30:58 +00:00
switch ( cond . RegType )
{
default :
case REGT_INT :
build - > Emit ( OP_EQ_K , condcheck , cond . RegNum , build - > GetConstantInt ( 0 ) ) ;
break ;
case REGT_FLOAT :
build - > Emit ( OP_EQF_K , condcheck , cond . RegNum , build - > GetConstantFloat ( 0 ) ) ;
break ;
case REGT_POINTER :
build - > Emit ( OP_EQA_K , condcheck , cond . RegNum , build - > GetConstantAddress ( nullptr , ATAG_GENERIC ) ) ;
break ;
}
2016-03-01 15:47:10 +00:00
jumpspot = build - > Emit ( OP_JMP , 0 ) ;
cond . Free ( build ) ;
// Evaluate first path
v = path1 - > Emit ( build ) ;
v . Free ( build ) ;
if ( path2 ! = NULL )
{
size_t path1jump = build - > Emit ( OP_JMP , 0 ) ;
// Evaluate second path
build - > BackpatchToHere ( jumpspot ) ;
v = path2 - > Emit ( build ) ;
v . Free ( build ) ;
jumpspot = path1jump ;
}
build - > BackpatchToHere ( jumpspot ) ;
return ExpEmit ( ) ;
}
2016-10-15 08:34:26 +00:00
//==========================================================================
//
2016-10-19 12:36:54 +00:00
// FxLoopStatement :: Resolve
//
// saves the loop pointer in the context and sets this object as the current loop
// so that continues and breaks always resolve to the innermost loop.
2016-10-15 08:34:26 +00:00
//
//==========================================================================
2016-10-19 12:36:54 +00:00
FxExpression * FxLoopStatement : : Resolve ( FCompileContext & ctx )
2016-10-15 08:34:26 +00:00
{
2016-10-19 12:36:54 +00:00
auto outer = ctx . Loop ;
ctx . Loop = this ;
auto x = DoResolve ( ctx ) ;
ctx . Loop = outer ;
return x ;
}
void FxLoopStatement : : Backpatch ( VMFunctionBuilder * build , size_t loopstart , size_t loopend )
{
// Give a proper address to any break/continue statement within this loop.
for ( unsigned int i = 0 ; i < Jumps . Size ( ) ; i + + )
2016-10-15 08:34:26 +00:00
{
2016-10-19 12:36:54 +00:00
if ( Jumps [ i ] - > Token = = TK_Break )
2016-10-15 08:34:26 +00:00
{
2016-10-19 12:36:54 +00:00
build - > Backpatch ( Jumps [ i ] - > Address , loopend ) ;
}
else
{ // Continue statement.
build - > Backpatch ( Jumps [ i ] - > Address , loopstart ) ;
2016-10-15 08:34:26 +00:00
}
}
}
2016-07-25 04:38:02 +00:00
//==========================================================================
//
// FxWhileLoop
//
//==========================================================================
FxWhileLoop : : FxWhileLoop ( FxExpression * condition , FxExpression * code , const FScriptPosition & pos )
2016-10-22 23:14:49 +00:00
: FxLoopStatement ( EFX_WhileLoop , pos ) , Condition ( condition ) , Code ( code )
2016-07-25 04:38:02 +00:00
{
ValueType = TypeVoid ;
}
FxWhileLoop : : ~ FxWhileLoop ( )
{
SAFE_DELETE ( Condition ) ;
SAFE_DELETE ( Code ) ;
}
2016-10-19 12:36:54 +00:00
FxExpression * FxWhileLoop : : DoResolve ( FCompileContext & ctx )
2016-07-25 04:38:02 +00:00
{
CHECKRESOLVED ( ) ;
SAFE_RESOLVE ( Condition , ctx ) ;
SAFE_RESOLVE_OPT ( Code , ctx ) ;
if ( Condition - > ValueType ! = TypeBool )
{
Condition = new FxBoolCast ( Condition ) ;
SAFE_RESOLVE ( Condition , ctx ) ;
}
if ( Condition - > isConstant ( ) )
{
if ( static_cast < FxConstant * > ( Condition ) - > GetValue ( ) . GetBool ( ) = = false )
{ // Nothing happens
FxExpression * nop = new FxNop ( ScriptPosition ) ;
delete this ;
return nop ;
}
else if ( Code = = nullptr )
{ // "while (true) { }"
// Someone could be using this for testing.
ScriptPosition . Message ( MSG_WARNING , " Infinite empty loop " ) ;
}
}
return this ;
}
ExpEmit FxWhileLoop : : Emit ( VMFunctionBuilder * build )
{
assert ( Condition - > ValueType = = TypeBool ) ;
size_t loopstart , loopend ;
size_t jumpspot ;
// Evaluate the condition and execute/break out of the loop.
loopstart = build - > GetAddress ( ) ;
if ( ! Condition - > isConstant ( ) )
{
ExpEmit cond = Condition - > Emit ( build ) ;
build - > Emit ( OP_TEST , cond . RegNum , 0 ) ;
jumpspot = build - > Emit ( OP_JMP , 0 ) ;
cond . Free ( build ) ;
}
else assert ( static_cast < FxConstant * > ( Condition ) - > GetValue ( ) . GetBool ( ) = = true ) ;
// Execute the loop's content.
if ( Code ! = nullptr )
{
ExpEmit code = Code - > Emit ( build ) ;
code . Free ( build ) ;
}
// Loop back.
build - > Backpatch ( build - > Emit ( OP_JMP , 0 ) , loopstart ) ;
loopend = build - > GetAddress ( ) ;
if ( ! Condition - > isConstant ( ) )
{
build - > Backpatch ( jumpspot , loopend ) ;
}
2016-10-19 12:36:54 +00:00
Backpatch ( build , loopstart , loopend ) ;
2016-07-27 15:14:54 +00:00
return ExpEmit ( ) ;
}
//==========================================================================
//
// FxDoWhileLoop
//
//==========================================================================
FxDoWhileLoop : : FxDoWhileLoop ( FxExpression * condition , FxExpression * code , const FScriptPosition & pos )
2016-10-22 23:14:49 +00:00
: FxLoopStatement ( EFX_DoWhileLoop , pos ) , Condition ( condition ) , Code ( code )
2016-07-27 15:14:54 +00:00
{
ValueType = TypeVoid ;
}
FxDoWhileLoop : : ~ FxDoWhileLoop ( )
{
SAFE_DELETE ( Condition ) ;
SAFE_DELETE ( Code ) ;
}
2016-10-19 12:36:54 +00:00
FxExpression * FxDoWhileLoop : : DoResolve ( FCompileContext & ctx )
2016-07-27 15:14:54 +00:00
{
CHECKRESOLVED ( ) ;
SAFE_RESOLVE ( Condition , ctx ) ;
SAFE_RESOLVE_OPT ( Code , ctx ) ;
if ( Condition - > ValueType ! = TypeBool )
{
Condition = new FxBoolCast ( Condition ) ;
SAFE_RESOLVE ( Condition , ctx ) ;
}
if ( Condition - > isConstant ( ) )
{
if ( static_cast < FxConstant * > ( Condition ) - > GetValue ( ) . GetBool ( ) = = false )
{ // The code executes once, if any.
2016-10-19 12:36:54 +00:00
if ( Jumps . Size ( ) = = 0 )
2016-07-27 15:14:54 +00:00
{ // We would still have to handle the jumps however.
FxExpression * e = Code ;
if ( e = = nullptr ) e = new FxNop ( ScriptPosition ) ;
Code = nullptr ;
delete this ;
return e ;
}
}
else if ( Code = = nullptr )
{ // "do { } while (true);"
// Someone could be using this for testing.
ScriptPosition . Message ( MSG_WARNING , " Infinite empty loop " ) ;
}
}
return this ;
}
ExpEmit FxDoWhileLoop : : Emit ( VMFunctionBuilder * build )
{
assert ( Condition - > ValueType = = TypeBool ) ;
size_t loopstart , loopend ;
size_t codestart ;
// Execute the loop's content.
codestart = build - > GetAddress ( ) ;
if ( Code ! = nullptr )
{
ExpEmit code = Code - > Emit ( build ) ;
code . Free ( build ) ;
}
// Evaluate the condition and execute/break out of the loop.
loopstart = build - > GetAddress ( ) ;
if ( ! Condition - > isConstant ( ) )
{
ExpEmit cond = Condition - > Emit ( build ) ;
build - > Emit ( OP_TEST , cond . RegNum , 1 ) ;
cond . Free ( build ) ;
build - > Backpatch ( build - > Emit ( OP_JMP , 0 ) , codestart ) ;
}
else if ( static_cast < FxConstant * > ( Condition ) - > GetValue ( ) . GetBool ( ) = = true )
{ // Always looping
build - > Backpatch ( build - > Emit ( OP_JMP , 0 ) , codestart ) ;
}
loopend = build - > GetAddress ( ) ;
2016-10-19 12:36:54 +00:00
Backpatch ( build , loopstart , loopend ) ;
2016-07-28 15:36:05 +00:00
return ExpEmit ( ) ;
}
//==========================================================================
//
// FxForLoop
//
//==========================================================================
FxForLoop : : FxForLoop ( FxExpression * init , FxExpression * condition , FxExpression * iteration , FxExpression * code , const FScriptPosition & pos )
2016-10-22 23:14:49 +00:00
: FxLoopStatement ( EFX_ForLoop , pos ) , Init ( init ) , Condition ( condition ) , Iteration ( iteration ) , Code ( code )
2016-07-28 15:36:05 +00:00
{
ValueType = TypeVoid ;
2016-10-26 09:30:30 +00:00
if ( Iteration ! = nullptr ) Iteration - > NeedResult = false ;
if ( Code ! = nullptr ) Code - > NeedResult = false ;
2016-07-28 15:36:05 +00:00
}
FxForLoop : : ~ FxForLoop ( )
{
SAFE_DELETE ( Init ) ;
SAFE_DELETE ( Condition ) ;
SAFE_DELETE ( Iteration ) ;
SAFE_DELETE ( Code ) ;
}
2016-10-19 12:36:54 +00:00
FxExpression * FxForLoop : : DoResolve ( FCompileContext & ctx )
2016-07-28 15:36:05 +00:00
{
CHECKRESOLVED ( ) ;
SAFE_RESOLVE_OPT ( Init , ctx ) ;
SAFE_RESOLVE_OPT ( Condition , ctx ) ;
SAFE_RESOLVE_OPT ( Iteration , ctx ) ;
SAFE_RESOLVE_OPT ( Code , ctx ) ;
if ( Condition ! = nullptr )
{
if ( Condition - > ValueType ! = TypeBool )
{
Condition = new FxBoolCast ( Condition ) ;
SAFE_RESOLVE ( Condition , ctx ) ;
}
if ( Condition - > isConstant ( ) )
{
if ( static_cast < FxConstant * > ( Condition ) - > GetValue ( ) . GetBool ( ) = = false )
{ // Nothing happens
FxExpression * nop = new FxNop ( ScriptPosition ) ;
delete this ;
return nop ;
}
else
{ // "for (..; true; ..)"
delete Condition ;
Condition = nullptr ;
}
}
}
if ( Condition = = nullptr & & Code = = nullptr )
{ // "for (..; ; ..) { }"
// Someone could be using this for testing.
ScriptPosition . Message ( MSG_WARNING , " Infinite empty loop " ) ;
}
return this ;
}
ExpEmit FxForLoop : : Emit ( VMFunctionBuilder * build )
{
assert ( ( Condition & & Condition - > ValueType = = TypeBool & & ! Condition - > isConstant ( ) ) | | Condition = = nullptr ) ;
size_t loopstart , loopend ;
size_t codestart ;
size_t jumpspot ;
2016-10-19 12:36:54 +00:00
// Init statement (only used by DECORATE. ZScript is pulling it before the loop statement and enclosing the entire loop in a compound statement so that Init can have local variables.)
2016-07-28 15:36:05 +00:00
if ( Init ! = nullptr )
{
ExpEmit init = Init - > Emit ( build ) ;
init . Free ( build ) ;
}
// Evaluate the condition and execute/break out of the loop.
codestart = build - > GetAddress ( ) ;
if ( Condition ! = nullptr )
{
ExpEmit cond = Condition - > Emit ( build ) ;
build - > Emit ( OP_TEST , cond . RegNum , 0 ) ;
cond . Free ( build ) ;
jumpspot = build - > Emit ( OP_JMP , 0 ) ;
}
// Execute the loop's content.
if ( Code ! = nullptr )
{
ExpEmit code = Code - > Emit ( build ) ;
code . Free ( build ) ;
}
// Iteration statement.
loopstart = build - > GetAddress ( ) ;
if ( Iteration ! = nullptr )
{
ExpEmit iter = Iteration - > Emit ( build ) ;
iter . Free ( build ) ;
}
build - > Backpatch ( build - > Emit ( OP_JMP , 0 ) , codestart ) ;
// End of loop.
loopend = build - > GetAddress ( ) ;
if ( Condition ! = nullptr )
{
build - > Backpatch ( jumpspot , loopend ) ;
}
2016-10-19 12:36:54 +00:00
Backpatch ( build , loopstart , loopend ) ;
2016-07-25 04:38:02 +00:00
return ExpEmit ( ) ;
}
2016-07-26 21:57:26 +00:00
//==========================================================================
//
// FxJumpStatement
//
//==========================================================================
FxJumpStatement : : FxJumpStatement ( int token , const FScriptPosition & pos )
2016-10-22 23:14:49 +00:00
: FxExpression ( EFX_JumpStatement , pos ) , Token ( token )
2016-07-26 21:57:26 +00:00
{
ValueType = TypeVoid ;
}
FxExpression * FxJumpStatement : : Resolve ( FCompileContext & ctx )
{
CHECKRESOLVED ( ) ;
2016-10-19 12:36:54 +00:00
if ( ctx . Loop ! = nullptr )
{
ctx . Loop - > Jumps . Push ( this ) ;
return this ;
}
else
{
ScriptPosition . Message ( MSG_ERROR , " '%s' outside of a loop " , Token = = TK_Break ? " break " : " continue " ) ;
delete this ;
return nullptr ;
}
2016-07-26 21:57:26 +00:00
}
ExpEmit FxJumpStatement : : Emit ( VMFunctionBuilder * build )
{
Address = build - > Emit ( OP_JMP , 0 ) ;
return ExpEmit ( ) ;
}
2016-03-01 15:47:10 +00:00
//==========================================================================
//
//==========================================================================
2016-08-02 16:50:34 +00:00
FxReturnStatement : : FxReturnStatement ( FxExpression * value , const FScriptPosition & pos )
2016-10-22 23:14:49 +00:00
: FxExpression ( EFX_ReturnStatement , pos ) , Value ( value )
2016-03-01 15:47:10 +00:00
{
2016-08-02 16:50:34 +00:00
ValueType = TypeVoid ;
2016-03-01 15:47:10 +00:00
}
FxReturnStatement : : ~ FxReturnStatement ( )
{
2016-08-02 16:50:34 +00:00
SAFE_DELETE ( Value ) ;
2016-03-01 15:47:10 +00:00
}
FxExpression * FxReturnStatement : : Resolve ( FCompileContext & ctx )
{
CHECKRESOLVED ( ) ;
2016-08-02 16:50:34 +00:00
SAFE_RESOLVE_OPT ( Value , ctx ) ;
PPrototype * retproto ;
if ( Value = = nullptr )
2016-03-01 15:47:10 +00:00
{
2016-08-02 16:50:34 +00:00
TArray < PType * > none ( 0 ) ;
retproto = NewPrototype ( none , none ) ;
2016-03-01 15:47:10 +00:00
}
2016-08-02 16:50:34 +00:00
else
{
retproto = Value - > ReturnProto ( ) ;
}
ctx . CheckReturn ( retproto , ScriptPosition ) ;
2016-03-01 15:47:10 +00:00
return this ;
}
ExpEmit FxReturnStatement : : Emit ( VMFunctionBuilder * build )
{
2016-08-14 17:23:28 +00:00
ExpEmit out ( 0 , REGT_NIL ) ;
2016-08-02 16:50:34 +00:00
// If we return nothing, use a regular RET opcode.
// Otherwise just return the value we're given.
if ( Value = = nullptr )
2016-03-01 15:47:10 +00:00
{
build - > Emit ( OP_RET , RET_FINAL , REGT_NIL , 0 ) ;
}
else
{
2016-08-14 17:23:28 +00:00
out = Value - > Emit ( build ) ;
2016-08-02 16:50:34 +00:00
// Check if it is a function call that simplified itself
// into a tail call in which case we don't emit anything.
2016-08-14 17:23:28 +00:00
if ( ! out . Final )
2016-08-02 16:50:34 +00:00
{
if ( Value - > ValueType = = TypeVoid )
{ // Nothing is returned.
build - > Emit ( OP_RET , RET_FINAL , REGT_NIL , 0 ) ;
}
else
{
2016-08-14 17:23:28 +00:00
build - > Emit ( OP_RET , RET_FINAL , out . RegType | ( out . Konst ? REGT_KONST : 0 ) , out . RegNum ) ;
2016-08-02 16:50:34 +00:00
}
}
2016-03-01 15:47:10 +00:00
}
2016-08-02 16:50:34 +00:00
out . Final = true ;
return out ;
2016-03-01 15:47:10 +00:00
}
VMFunction * FxReturnStatement : : GetDirectFunction ( )
{
2016-08-02 16:50:34 +00:00
if ( Value ! = nullptr )
2016-03-01 15:47:10 +00:00
{
2016-08-02 16:50:34 +00:00
return Value - > GetDirectFunction ( ) ;
2016-03-01 15:47:10 +00:00
}
2016-08-02 16:50:34 +00:00
return nullptr ;
2016-03-01 15:47:10 +00:00
}
//==========================================================================
//
//==========================================================================
2016-10-16 17:42:22 +00:00
FxClassTypeCast : : FxClassTypeCast ( PClassPointer * dtype , FxExpression * x )
2016-10-22 23:14:49 +00:00
: FxExpression ( EFX_ClassTypeCast , x - > ScriptPosition )
2016-03-01 15:47:10 +00:00
{
2016-10-16 17:42:22 +00:00
ValueType = dtype ;
desttype = dtype - > ClassRestriction ;
2016-03-01 15:47:10 +00:00
basex = x ;
}
//==========================================================================
//
//
//
//==========================================================================
FxClassTypeCast : : ~ FxClassTypeCast ( )
{
SAFE_DELETE ( basex ) ;
}
//==========================================================================
//
//
//
//==========================================================================
FxExpression * FxClassTypeCast : : Resolve ( FCompileContext & ctx )
{
CHECKRESOLVED ( ) ;
SAFE_RESOLVE ( basex , ctx ) ;
2016-10-16 17:42:22 +00:00
2016-10-21 17:18:39 +00:00
if ( basex - > ValueType = = TypeNullPtr )
{
basex - > ValueType = ValueType ;
auto x = basex ;
basex = nullptr ;
delete this ;
return x ;
}
2016-10-22 17:51:24 +00:00
auto to = static_cast < PClassPointer * > ( ValueType ) ;
2016-10-16 17:42:22 +00:00
if ( basex - > ValueType - > GetClass ( ) = = RUNTIME_CLASS ( PClassPointer ) )
{
auto from = static_cast < PClassPointer * > ( basex - > ValueType ) ;
if ( from - > ClassRestriction - > IsDescendantOf ( to - > ClassRestriction ) )
{
basex - > ValueType = to ;
auto x = basex ;
basex = nullptr ;
delete this ;
return x ;
}
ScriptPosition . Message ( MSG_ERROR , " Cannot convert from %s to %s: Incompatible class types " , from - > ClassRestriction - > TypeName . GetChars ( ) , to - > ClassRestriction - > TypeName . GetChars ( ) ) ;
delete this ;
return nullptr ;
}
2016-03-01 15:47:10 +00:00
2016-10-16 17:42:22 +00:00
if ( basex - > ValueType ! = TypeName & & basex - > ValueType ! = TypeString )
2016-03-01 15:47:10 +00:00
{
2016-10-16 20:32:52 +00:00
ScriptPosition . Message ( MSG_ERROR , " Cannot convert %s to class type " , basex - > ValueType - > DescriptiveName ( ) ) ;
2016-03-01 15:47:10 +00:00
delete this ;
return NULL ;
}
if ( basex - > isConstant ( ) )
{
FName clsname = static_cast < FxConstant * > ( basex ) - > GetValue ( ) . GetName ( ) ;
PClass * cls = NULL ;
if ( clsname ! = NAME_None )
{
cls = PClass : : FindClass ( clsname ) ;
if ( cls = = NULL )
{
/* lax */
// Since this happens in released WADs it must pass without a terminal error... :(
2016-06-08 08:56:11 +00:00
ScriptPosition . Message ( MSG_OPTERROR ,
2016-03-01 15:47:10 +00:00
" Unknown class name '%s' " ,
clsname . GetChars ( ) , desttype - > TypeName . GetChars ( ) ) ;
}
else
{
if ( ! cls - > IsDescendantOf ( desttype ) )
{
ScriptPosition . Message ( MSG_ERROR , " class '%s' is not compatible with '%s' " , clsname . GetChars ( ) , desttype - > TypeName . GetChars ( ) ) ;
delete this ;
return NULL ;
}
ScriptPosition . Message ( MSG_DEBUG , " resolving '%s' as class name " , clsname . GetChars ( ) ) ;
}
}
2016-10-22 17:51:24 +00:00
FxExpression * x = new FxConstant ( cls , to , ScriptPosition ) ;
2016-03-01 15:47:10 +00:00
delete this ;
return x ;
}
2016-10-16 17:42:22 +00:00
if ( basex - > ValueType = = TypeString )
{
basex = new FxNameCast ( basex ) ;
}
2016-03-01 15:47:10 +00:00
return this ;
}
//==========================================================================
//
//
//
//==========================================================================
int DecoNameToClass ( VMFrameStack * stack , VMValue * param , int numparam , VMReturn * ret , int numret )
{
assert ( numparam = = 2 ) ;
assert ( numret = = 1 ) ;
assert ( param [ 0 ] . Type = = REGT_INT ) ;
assert ( param [ 1 ] . Type = = REGT_POINTER ) ;
assert ( ret - > RegType = = REGT_POINTER ) ;
FName clsname = ENamedName ( param [ 0 ] . i ) ;
const PClass * cls = PClass : : FindClass ( clsname ) ;
const PClass * desttype = reinterpret_cast < PClass * > ( param [ 0 ] . a ) ;
if ( ! cls - > IsDescendantOf ( desttype ) )
{
Printf ( " class '%s' is not compatible with '%s' " , clsname . GetChars ( ) , desttype - > TypeName . GetChars ( ) ) ;
cls = NULL ;
}
ret - > SetPointer ( const_cast < PClass * > ( cls ) , ATAG_OBJECT ) ;
return 1 ;
}
ExpEmit FxClassTypeCast : : Emit ( VMFunctionBuilder * build )
{
if ( basex - > ValueType ! = TypeName )
{
return ExpEmit ( build - > GetConstantAddress ( NULL , ATAG_OBJECT ) , REGT_POINTER , true ) ;
}
ExpEmit clsname = basex - > Emit ( build ) ;
assert ( ! clsname . Konst ) ;
ExpEmit dest ( build , REGT_POINTER ) ;
build - > Emit ( OP_PARAM , 0 , clsname . RegType , clsname . RegNum ) ;
build - > Emit ( OP_PARAM , 0 , REGT_POINTER | REGT_KONST , build - > GetConstantAddress ( const_cast < PClass * > ( desttype ) , ATAG_OBJECT ) ) ;
// Call the DecoNameToClass function to convert from 'name' to class.
VMFunction * callfunc ;
PSymbol * sym = FindDecorateBuiltinFunction ( NAME_DecoNameToClass , DecoNameToClass ) ;
assert ( sym - > IsKindOf ( RUNTIME_CLASS ( PSymbolVMFunction ) ) ) ;
assert ( ( ( PSymbolVMFunction * ) sym ) - > Function ! = NULL ) ;
callfunc = ( ( PSymbolVMFunction * ) sym ) - > Function ;
build - > Emit ( OP_CALL_K , build - > GetConstantAddress ( callfunc , ATAG_OBJECT ) , 2 , 1 ) ;
build - > Emit ( OP_RESULT , 0 , REGT_POINTER , dest . RegNum ) ;
clsname . Free ( build ) ;
return dest ;
}
//==========================================================================
//
//
//
//==========================================================================
FxExpression * FxStateByIndex : : Resolve ( FCompileContext & ctx )
{
CHECKRESOLVED ( ) ;
2016-08-04 17:45:24 +00:00
ABORT ( ctx . Class ) ;
2016-10-12 22:53:59 +00:00
auto aclass = dyn_cast < PClassActor > ( ctx . Class ) ;
2016-08-04 17:45:24 +00:00
2016-10-12 22:53:59 +00:00
// This expression type can only be used from DECORATE, so there's no need to consider the possibility of calling it from a non-actor.
assert ( aclass ! = nullptr & & aclass - > NumOwnedStates > 0 ) ;
if ( aclass - > NumOwnedStates < = index )
2016-03-01 15:47:10 +00:00
{
ScriptPosition . Message ( MSG_ERROR , " %s: Attempt to jump to non existing state index %d " ,
2016-07-26 21:57:26 +00:00
ctx . Class - > TypeName . GetChars ( ) , index ) ;
2016-03-01 15:47:10 +00:00
delete this ;
return NULL ;
}
2016-10-12 22:53:59 +00:00
FxExpression * x = new FxConstant ( aclass - > OwnedStates + index , ScriptPosition ) ;
2016-03-01 15:47:10 +00:00
delete this ;
return x ;
}
2016-08-05 15:26:33 +00:00
//==========================================================================
//
//
//
//==========================================================================
FxRuntimeStateIndex : : FxRuntimeStateIndex ( FxExpression * index )
2016-10-22 23:14:49 +00:00
: FxExpression ( EFX_RuntimeStateIndex , index - > ScriptPosition ) , Index ( index )
2016-08-05 15:26:33 +00:00
{
2016-08-02 16:50:34 +00:00
EmitTail = false ;
2016-08-05 15:26:33 +00:00
ValueType = TypeState ;
}
FxRuntimeStateIndex : : ~ FxRuntimeStateIndex ( )
{
SAFE_DELETE ( Index ) ;
}
2016-08-02 16:50:34 +00:00
PPrototype * FxRuntimeStateIndex : : ReturnProto ( )
{
EmitTail = true ;
return FxExpression : : ReturnProto ( ) ;
}
2016-08-05 15:26:33 +00:00
FxExpression * FxRuntimeStateIndex : : Resolve ( FCompileContext & ctx )
{
CHECKRESOLVED ( ) ;
SAFE_RESOLVE ( Index , ctx ) ;
if ( ! Index - > IsNumeric ( ) )
{
ScriptPosition . Message ( MSG_ERROR , " Numeric type expected " ) ;
delete this ;
return nullptr ;
}
else if ( Index - > ValueType - > GetRegType ( ) ! = REGT_INT )
{ // Float.
2016-10-15 19:35:31 +00:00
Index = new FxIntCast ( Index , ctx . FromDecorate ) ;
2016-08-05 15:26:33 +00:00
SAFE_RESOLVE ( Index , ctx ) ;
}
return this ;
}
2016-10-04 07:28:19 +00:00
static bool VerifyJumpTarget ( AActor * stateowner , FStateParamInfo * stateinfo , int index )
{
PClassActor * cls = stateowner - > GetClass ( ) ;
2016-10-15 08:34:26 +00:00
if ( stateinfo - > mCallingState ! = nullptr )
2016-10-04 07:28:19 +00:00
{
while ( cls ! = RUNTIME_CLASS ( AActor ) )
{
// both calling and target state need to belong to the same class.
if ( cls - > OwnsState ( stateinfo - > mCallingState ) )
{
return cls - > OwnsState ( stateinfo - > mCallingState + index ) ;
2016-10-15 08:34:26 +00:00
}
2016-10-04 07:28:19 +00:00
// We can safely assume the ParentClass is of type PClassActor
// since we stop when we see the Actor base class.
cls = static_cast < PClassActor * > ( cls - > ParentClass ) ;
}
}
return false ;
}
2016-08-05 15:26:33 +00:00
static int DecoHandleRuntimeState ( VMFrameStack * stack , VMValue * param , int numparam , VMReturn * ret , int numret )
{
PARAM_PROLOGUE ;
PARAM_OBJECT ( stateowner , AActor ) ;
PARAM_POINTER ( stateinfo , FStateParamInfo ) ;
PARAM_INT ( index ) ;
2016-10-04 07:28:19 +00:00
if ( index = = 0 | | ! VerifyJumpTarget ( stateowner , stateinfo , index ) )
2016-08-05 15:26:33 +00:00
{
// Null is returned if the location was invalid which means that no jump will be performed
// if used as return value
// 0 always meant the same thing so we handle it here for compatibility
2016-08-02 16:50:34 +00:00
ACTION_RETURN_STATE ( nullptr ) ;
2016-08-05 15:26:33 +00:00
}
else
{
2016-08-02 16:50:34 +00:00
ACTION_RETURN_STATE ( stateinfo - > mCallingState + index ) ;
2016-08-05 15:26:33 +00:00
}
}
ExpEmit FxRuntimeStateIndex : : Emit ( VMFunctionBuilder * build )
{
2016-10-02 03:31:07 +00:00
assert ( build - > IsActionFunc & & build - > Registers [ REGT_POINTER ] . GetMostUsed ( ) > = 3 & &
" FxRuntimeStateIndex is only valid inside action functions " ) ;
2016-08-05 15:26:33 +00:00
ExpEmit out ( build , REGT_POINTER ) ;
build - > Emit ( OP_PARAM , 0 , REGT_POINTER , 1 ) ; // stateowner
build - > Emit ( OP_PARAM , 0 , REGT_POINTER , 2 ) ; // stateinfo
ExpEmit id = Index - > Emit ( build ) ;
build - > Emit ( OP_PARAM , 0 , REGT_INT | ( id . Konst ? REGT_KONST : 0 ) , id . RegNum ) ; // index
VMFunction * callfunc ;
PSymbol * sym ;
2016-03-01 15:47:10 +00:00
2016-08-05 15:26:33 +00:00
sym = FindDecorateBuiltinFunction ( NAME_DecoHandleRuntimeState , DecoHandleRuntimeState ) ;
assert ( sym - > IsKindOf ( RUNTIME_CLASS ( PSymbolVMFunction ) ) ) ;
assert ( ( ( PSymbolVMFunction * ) sym ) - > Function ! = nullptr ) ;
callfunc = ( ( PSymbolVMFunction * ) sym ) - > Function ;
2016-08-02 16:50:34 +00:00
if ( EmitTail )
{
build - > Emit ( OP_TAIL_K , build - > GetConstantAddress ( callfunc , ATAG_OBJECT ) , 3 , 1 ) ;
out . Final = true ;
}
else
{
build - > Emit ( OP_CALL_K , build - > GetConstantAddress ( callfunc , ATAG_OBJECT ) , 3 , 1 ) ;
build - > Emit ( OP_RESULT , 0 , REGT_POINTER , out . RegNum ) ;
}
2016-08-05 15:26:33 +00:00
return out ;
}
2016-03-01 15:47:10 +00:00
//==========================================================================
//
//
//
//==========================================================================
FxMultiNameState : : FxMultiNameState ( const char * _statestring , const FScriptPosition & pos )
2016-10-22 23:14:49 +00:00
: FxExpression ( EFX_MultiNameState , pos )
2016-03-01 15:47:10 +00:00
{
FName scopename ;
FString statestring = _statestring ;
int scopeindex = statestring . IndexOf ( " :: " ) ;
if ( scopeindex > = 0 )
{
scopename = FName ( statestring , scopeindex , false ) ;
statestring = statestring . Right ( statestring . Len ( ) - scopeindex - 2 ) ;
}
else
{
2016-10-16 20:32:52 +00:00
scopename = NAME_None ;
2016-03-01 15:47:10 +00:00
}
names = MakeStateNameList ( statestring ) ;
names . Insert ( 0 , scopename ) ;
scope = NULL ;
}
//==========================================================================
//
//
//
//==========================================================================
FxExpression * FxMultiNameState : : Resolve ( FCompileContext & ctx )
{
CHECKRESOLVED ( ) ;
2016-08-04 17:45:24 +00:00
ABORT ( ctx . Class ) ;
2016-03-01 15:47:10 +00:00
if ( names [ 0 ] = = NAME_None )
{
scope = NULL ;
}
else if ( names [ 0 ] = = NAME_Super )
{
2016-07-26 21:57:26 +00:00
scope = dyn_cast < PClassActor > ( ctx . Class - > ParentClass ) ;
2016-03-01 15:47:10 +00:00
}
else
{
scope = PClass : : FindActor ( names [ 0 ] ) ;
if ( scope = = NULL )
{
ScriptPosition . Message ( MSG_ERROR , " Unknown class '%s' in state label " , names [ 0 ] . GetChars ( ) ) ;
delete this ;
return NULL ;
}
2016-10-15 15:40:27 +00:00
else if ( ! scope - > IsAncestorOf ( ctx . Class ) )
2016-03-01 15:47:10 +00:00
{
2016-10-15 15:40:27 +00:00
ScriptPosition . Message ( MSG_ERROR , " '%s' is not an ancestor of '%s' " , names [ 0 ] . GetChars ( ) , ctx . Class - > TypeName . GetChars ( ) ) ;
2016-03-01 15:47:10 +00:00
delete this ;
return NULL ;
}
}
if ( scope ! = NULL )
{
FState * destination = NULL ;
// If the label is class specific we can resolve it right here
if ( names [ 1 ] ! = NAME_None )
{
destination = scope - > FindState ( names . Size ( ) - 1 , & names [ 1 ] , false ) ;
if ( destination = = NULL )
{
2016-06-08 08:56:11 +00:00
ScriptPosition . Message ( MSG_OPTERROR , " Unknown state jump destination " ) ;
2016-03-01 15:47:10 +00:00
/* lax */
return this ;
}
}
FxExpression * x = new FxConstant ( destination , ScriptPosition ) ;
delete this ;
return x ;
}
names . Delete ( 0 ) ;
names . ShrinkToFit ( ) ;
ValueType = TypeState ;
return this ;
}
//==========================================================================
//
//
//
//==========================================================================
static int DoFindState ( VMFrameStack * stack , VMValue * param , int numparam , VMReturn * ret , FName * names , int numnames )
{
PARAM_OBJECT_AT ( 0 , self , AActor ) ;
FState * state = self - > GetClass ( ) - > FindState ( numparam - 1 , names ) ;
if ( state = = NULL )
{
const char * dot = " " ;
Printf ( " Jump target ' " ) ;
for ( int i = 0 ; i < numparam - 1 ; i + + )
{
Printf ( " %s%s " , dot , names [ i ] . GetChars ( ) ) ;
dot = " . " ;
}
Printf ( " ' not found in %s \n " , self - > GetClass ( ) - > TypeName . GetChars ( ) ) ;
}
ret - > SetPointer ( state , ATAG_STATE ) ;
return 1 ;
}
// Find a state with any number of dots in its name.
2016-10-14 19:15:46 +00:00
int BuiltinFindMultiNameState ( VMFrameStack * stack , VMValue * param , int numparam , VMReturn * ret , int numret )
2016-03-01 15:47:10 +00:00
{
assert ( numparam > 1 ) ;
assert ( numret = = 1 ) ;
assert ( ret - > RegType = = REGT_POINTER ) ;
FName * names = ( FName * ) alloca ( ( numparam - 1 ) * sizeof ( FName ) ) ;
for ( int i = 1 ; i < numparam ; + + i )
{
PARAM_NAME_AT ( i , zaname ) ;
names [ i - 1 ] = zaname ;
}
return DoFindState ( stack , param , numparam , ret , names , numparam - 1 ) ;
}
// Find a state without any dots in its name.
2016-10-14 19:15:46 +00:00
int BuiltinFindSingleNameState ( VMFrameStack * stack , VMValue * param , int numparam , VMReturn * ret , int numret )
2016-03-01 15:47:10 +00:00
{
assert ( numparam = = 2 ) ;
assert ( numret = = 1 ) ;
assert ( ret - > RegType = = REGT_POINTER ) ;
PARAM_NAME_AT ( 1 , zaname ) ;
return DoFindState ( stack , param , numparam , ret , & zaname , 1 ) ;
}
ExpEmit FxMultiNameState : : Emit ( VMFunctionBuilder * build )
{
ExpEmit dest ( build , REGT_POINTER ) ;
2016-10-02 03:31:07 +00:00
if ( build - > IsActionFunc )
{
build - > Emit ( OP_PARAM , 0 , REGT_POINTER , 1 ) ; // pass stateowner
}
else
{
build - > Emit ( OP_PARAM , 0 , REGT_POINTER , 0 ) ; // pass self
}
2016-03-01 15:47:10 +00:00
for ( unsigned i = 0 ; i < names . Size ( ) ; + + i )
{
build - > EmitParamInt ( names [ i ] ) ;
}
2016-10-14 19:15:46 +00:00
// For one name, use the BuiltinFindSingleNameState function. For more than
// one name, use the BuiltinFindMultiNameState function.
2016-03-01 15:47:10 +00:00
VMFunction * callfunc ;
PSymbol * sym ;
if ( names . Size ( ) = = 1 )
{
2016-10-14 19:15:46 +00:00
sym = FindDecorateBuiltinFunction ( NAME_BuiltinFindSingleNameState , BuiltinFindSingleNameState ) ;
2016-03-01 15:47:10 +00:00
}
else
{
2016-10-14 19:15:46 +00:00
sym = FindDecorateBuiltinFunction ( NAME_BuiltinFindMultiNameState , BuiltinFindMultiNameState ) ;
2016-03-01 15:47:10 +00:00
}
assert ( sym - > IsKindOf ( RUNTIME_CLASS ( PSymbolVMFunction ) ) ) ;
assert ( ( ( PSymbolVMFunction * ) sym ) - > Function ! = NULL ) ;
callfunc = ( ( PSymbolVMFunction * ) sym ) - > Function ;
build - > Emit ( OP_CALL_K , build - > GetConstantAddress ( callfunc , ATAG_OBJECT ) , names . Size ( ) + 1 , 1 ) ;
build - > Emit ( OP_RESULT , 0 , REGT_POINTER , dest . RegNum ) ;
return dest ;
}
2016-10-19 17:05:48 +00:00
//==========================================================================
//
// declares a single local variable (no arrays)
//
//==========================================================================
2016-10-20 23:12:54 +00:00
FxLocalVariableDeclaration : : FxLocalVariableDeclaration ( PType * type , FName name , FxExpression * initval , int varflags , const FScriptPosition & p )
2016-10-22 23:14:49 +00:00
: FxExpression ( EFX_LocalVariableDeclaration , p )
2016-10-19 17:05:48 +00:00
{
ValueType = type ;
2016-10-20 23:12:54 +00:00
VarFlags = varflags ;
2016-10-19 17:05:48 +00:00
Name = name ;
Init = initval = = nullptr ? nullptr : new FxTypeCast ( initval , type , false ) ;
}
FxExpression * FxLocalVariableDeclaration : : Resolve ( FCompileContext & ctx )
{
CHECKRESOLVED ( ) ;
SAFE_RESOLVE_OPT ( Init , ctx ) ;
if ( ctx . Block = = nullptr )
{
ScriptPosition . Message ( MSG_ERROR , " Variable declaration outside compound statement " ) ;
delete this ;
return nullptr ;
}
ctx . Block - > LocalVars . Push ( this ) ;
return this ;
}
ExpEmit FxLocalVariableDeclaration : : Emit ( VMFunctionBuilder * build )
{
if ( Init = = nullptr )
{
RegNum = build - > Registers [ ValueType - > GetRegType ( ) ] . Get ( 1 ) ;
}
else
{
ExpEmit emitval = Init - > Emit ( build ) ;
int regtype = emitval . RegType ;
if ( regtype < REGT_INT | | regtype > REGT_TYPE )
{
ScriptPosition . Message ( MSG_ERROR , " Attempted to assign a non-value " ) ;
return ExpEmit ( ) ;
}
if ( emitval . Konst )
{
auto constval = static_cast < FxConstant * > ( Init ) ;
RegNum = build - > Registers [ regtype ] . Get ( 1 ) ;
switch ( regtype )
{
default :
case REGT_INT :
2016-10-26 09:30:30 +00:00
build - > Emit ( OP_LK , RegNum , build - > GetConstantInt ( constval - > GetValue ( ) . GetInt ( ) ) ) ;
2016-10-19 17:05:48 +00:00
break ;
case REGT_FLOAT :
2016-10-26 09:30:30 +00:00
build - > Emit ( OP_LKF , RegNum , build - > GetConstantFloat ( constval - > GetValue ( ) . GetFloat ( ) ) ) ;
2016-10-19 17:05:48 +00:00
break ;
case REGT_POINTER :
2016-10-26 09:30:30 +00:00
build - > Emit ( OP_LKP , RegNum , build - > GetConstantAddress ( constval - > GetValue ( ) . GetPointer ( ) , ATAG_GENERIC ) ) ;
2016-10-19 17:05:48 +00:00
break ;
case REGT_STRING :
2016-10-26 09:30:30 +00:00
build - > Emit ( OP_LKS , RegNum , build - > GetConstantString ( constval - > GetValue ( ) . GetString ( ) ) ) ;
2016-10-19 17:05:48 +00:00
}
emitval . Free ( build ) ;
}
else
{
// take over the register that got allocated while emitting the Init expression.
RegNum = emitval . RegNum ;
}
}
return ExpEmit ( ) ;
}
void FxLocalVariableDeclaration : : Release ( VMFunctionBuilder * build )
{
// Release the register after the containing block gets closed
assert ( RegNum ! = - 1 ) ;
build - > Registers [ ValueType - > GetRegType ( ) ] . Return ( RegNum , 1 ) ;
}