Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion sdk/angelscript/include/angelscript.h
Original file line number Diff line number Diff line change
Expand Up @@ -723,7 +723,7 @@ class asIScriptEngine
virtual int GetDefaultArrayTypeId() const = 0;

// Enums
virtual int RegisterEnum(const char* typeName, const char* underlyingType = "int32") = 0;
virtual int RegisterEnum(const char* typeName, const char* underlyingType = "int32", bool isFlags = false) = 0;
virtual int RegisterEnumValue(const char* type, const char* name, asINT64 value) = 0;
virtual asUINT GetEnumCount() const = 0;
virtual asITypeInfo *GetEnumByIndex(asUINT index) const = 0;
Expand Down Expand Up @@ -1127,6 +1127,7 @@ class asITypeInfo
// Enums
virtual asUINT GetEnumValueCount() const = 0;
virtual const char *GetEnumValueByIndex(asUINT index, asINT64 *outValue) const = 0;
virtual bool IsFlagEnum() const = 0;

#ifdef AS_DEPRECATED
// deprecated since 2025-09-13, 2.39.0
Expand Down
32 changes: 27 additions & 5 deletions sdk/angelscript/source/as_builder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2771,6 +2771,7 @@ void asCBuilder::CompileGlobalVariables()

if( gvar->isEnumValue )
{
asCEnumType *enumType = CastToEnumType(gvar->datatype.GetTypeInfo());
int r;
if( gvar->initializationNode )
{
Expand All @@ -2780,8 +2781,6 @@ void asCBuilder::CompileGlobalVariables()
// Set the namespace that should be used during the compilation
func.nameSpace = gvar->datatype.GetTypeInfo()->nameSpace;

// Temporarily switch the type of the variable to the enums' underlying type so it can be compiled properly
asCEnumType *enumType = CastToEnumType(gvar->datatype.GetTypeInfo());
asCDataType saveType;
saveType = gvar->datatype;
gvar->datatype = enumType->enumType;
Expand All @@ -2794,7 +2793,6 @@ void asCBuilder::CompileGlobalVariables()
else
{
r = 0;

// When there is no assignment the value is the last + 1
asINT64 enumVal = 0;
asCSymbolTable<sGlobalVariableDescription>::iterator prev_it = it;
Expand All @@ -2804,7 +2802,28 @@ void asCBuilder::CompileGlobalVariables()
sGlobalVariableDescription *gvar2 = *prev_it;
if(gvar2->datatype == gvar->datatype )
{
enumVal = asINT64(gvar2->constantValue) + 1;
const asINT64 prevVal = gvar2->constantValue;

if( enumType->isFlags)
{
if ( prevVal <= 0 )
enumVal = 1;
else
{
enumVal = prevVal;
enumVal |= enumVal >> 1;
enumVal |= enumVal >> 2;
enumVal |= enumVal >> 4;
enumVal |= enumVal >> 8;
enumVal |= enumVal >> 16;
enumVal |= enumVal >> 32;
enumVal++;
}
}
else
{
enumVal = prevVal + 1;
}

if( !gvar2->isCompiled )
{
Expand Down Expand Up @@ -4679,6 +4698,7 @@ int asCBuilder::RegisterEnum(asCScriptNode *node, asCScriptCode *file, asSNameSp
// Is it a shared enum?
bool isShared = false;
bool isExternal = false;
bool isFlags = false;
asCEnumType *existingSharedType = 0;
asCScriptNode *tmp = node->firstChild;
while( tmp->nodeType == snIdentifier )
Expand All @@ -4687,6 +4707,8 @@ int asCBuilder::RegisterEnum(asCScriptNode *node, asCScriptCode *file, asSNameSp
isShared = true;
else if (file->TokenEquals(tmp->tokenPos, tmp->tokenLength, EXTERNAL_TOKEN))
isExternal = true;
else if (file->TokenEquals(tmp->tokenPos, tmp->tokenLength, FLAG_TOKEN))
isFlags = true;
else
break;
tmp = tmp->next;
Expand Down Expand Up @@ -4753,7 +4775,7 @@ int asCBuilder::RegisterEnum(asCScriptNode *node, asCScriptCode *file, asSNameSp
}
else
{
st = asNEW(asCEnumType)(engine);
st = asNEW(asCEnumType)(engine,isFlags);
if( st == 0 )
return asOUT_OF_MEMORY;

Expand Down
95 changes: 88 additions & 7 deletions sdk/angelscript/source/as_compiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4916,7 +4916,14 @@ void asCCompiler::CompileIfStatement(asCScriptNode *inode, bool *hasReturn, asCB
// else, allow value types to be converted to bool using 'bool opImplConv()'
else if (expr.type.dataType.GetTypeInfo() && (expr.type.dataType.GetTypeInfo()->GetFlags() & asOBJ_VALUE))
ImplicitConversion(&expr, asCDataType::CreatePrimitive(ttBool, false), inode, asIC_IMPLICIT_CONV);

if(expr.type.dataType.IsEnumType())
{
const asCEnumType *et = CastToEnumType(expr.type.dataType.GetTypeInfo());
if (et->isFlags)
{
ImplicitConversion(&expr, asCDataType::CreatePrimitive(ttBool, false), inode, asIC_IMPLICIT_CONV);
}
}
if (!expr.type.dataType.IsEqualExceptRefAndConst(asCDataType::CreatePrimitive(ttBool, true)))
{
asCString str;
Expand Down Expand Up @@ -7375,9 +7382,21 @@ asUINT asCCompiler::ImplicitConvPrimitiveToPrimitive(asCExprContext *ctx, const
asUINT cost = asCC_NO_CONV;
if (to.IsBooleanType() || ctx->type.dataType.IsBooleanType())
{
// conversions to/from bool are not allowed. A ternary (expr?1:0) should be used instead
// If at any time support for this is added, then remember that booleans can be different size on different platforms
return asCC_NO_CONV;
// allow flagenums to be implicitly converted to bool.
if (to.IsBooleanType() && ctx->type.dataType.IsEnumType())
{
asCEnumType *et = CastToEnumType(ctx->type.dataType.GetTypeInfo());
if (et->isFlags)
cost = asCC_ENUM_DIFF_SIZE_CONV;
else
return asCC_NO_CONV;
}
else
{
// conversions to/from bool are not allowed. A ternary (expr?1:0) should be used instead
// If at any time support for this is added, then remember that booleans can be different size on different platforms
return asCC_NO_CONV;
}
}
else if( (to.IsIntegerType() || to.IsUnsignedType()) && (ctx->type.dataType.IsFloatType() || ctx->type.dataType.IsDoubleType()) )
cost = asCC_FLOAT_TO_INT_CONV;
Expand Down Expand Up @@ -7409,6 +7428,20 @@ asUINT asCCompiler::ImplicitConvPrimitiveToPrimitive(asCExprContext *ctx, const
{
// When generating the code the decision has already been made, so we don't bother determining the cost

if (to.IsBooleanType() && ctx->type.dataType.IsEnumType())
{
if (ctx->type.isConstant)
ctx->type.SetConstantB(to, ctx->type.GetConstantQW() != 0);
else
{
ConvertToTempVariable(ctx);
ctx->bc.InstrSHORT(asBC_iTOb, (short)ctx->type.stackOffset);

ctx->type.dataType = to;
}
return cost; // handled it already
}

// Convert smaller types to 32bit first
int s = ctx->type.dataType.GetSizeInMemoryBytes();
if( s < 4 )
Expand Down Expand Up @@ -9217,9 +9250,30 @@ void asCCompiler::ImplicitConversionConstant(asCExprContext *from, const asCData

// References cannot be constants
if( from->type.dataType.IsReference() ) return;

if((to.IsEnumType() && convType == asIC_EXPLICIT_VAL_CAST))

if(to.IsBooleanType() && from->type.dataType.IsEnumType() == 2)
{
if (from->type.dataType.IsUnsignedType() || from->type.dataType.IsIntegerType())
{
asQWORD qw;

if (from->type.dataType.GetSizeInMemoryBytes() == 1)
qw = from->type.GetConstantB();
else if (from->type.dataType.GetSizeInMemoryBytes() == 2)
qw = from->type.GetConstantW();
else if (from->type.dataType.GetSizeInMemoryBytes() == 4)
qw = from->type.GetConstantDW();
else
qw = from->type.GetConstantQW();

if (to.GetSizeInMemoryBytes() == 1)
from->type.SetConstantB(to, qw != 0);
}
}
else if((to.IsEnumType() && convType == asIC_EXPLICIT_VAL_CAST))
{


if( from->type.dataType.IsFloatType() ||
from->type.dataType.IsDoubleType() ||
from->type.dataType.IsUnsignedType() ||
Expand Down Expand Up @@ -13748,13 +13802,18 @@ int asCCompiler::CompileExpressionPreOp(asCScriptNode *node, asCExprContext *ctx
}
else if( op == ttNot )
{
asCDataType originalType = ctx->type.dataType;
asCEnumType *et = CastToEnumType(originalType.GetTypeInfo());
bool isFlag = (et && et->isFlags);

// If turned on, allow the compiler to use either 'bool opImplConv()' or 'bool opConv()' on the type
if (engine->ep.boolConversionMode == 1)
ImplicitConversion(ctx, asCDataType::CreatePrimitive(ttBool, false), node, asIC_EXPLICIT_VAL_CAST);
// else, allow value types to be converted to bool using 'bool opImplConv()'
else if (ctx->type.dataType.GetTypeInfo() && (ctx->type.dataType.GetTypeInfo()->GetFlags() & asOBJ_VALUE))
ImplicitConversion(ctx, asCDataType::CreatePrimitive(ttBool, false), node, asIC_IMPLICIT_CONV);

else if(isFlag)
ImplicitConversion(ctx, asCDataType::CreatePrimitive(ttBool, false), node, asIC_IMPLICIT_CONV);
if( ctx->type.dataType.IsEqualExceptRefAndConst(asCDataType::CreatePrimitive(ttBool, true)) )
{
if( ctx->type.isConstant )
Expand Down Expand Up @@ -13783,6 +13842,10 @@ int asCCompiler::CompileExpressionPreOp(asCScriptNode *node, asCExprContext *ctx
}
else if( op == ttBitNot )
{
asCDataType originalType = ctx->type.dataType;
asCEnumType *et = CastToEnumType(originalType.GetTypeInfo());
bool isFlag = (et && et->isFlags);

if( ProcessPropertyGetAccessor(ctx, node) < 0 )
return -1;

Expand Down Expand Up @@ -13820,6 +13883,9 @@ int asCCompiler::CompileExpressionPreOp(asCScriptNode *node, asCExprContext *ctx
ctx->type.SetConstantDW(~ctx->type.GetConstantDW());
else
ctx->type.SetConstantQW(~ctx->type.GetConstantQW());

if(isFlag)
ctx->type.dataType = originalType;
return 0;
}

Expand All @@ -13830,6 +13896,9 @@ int asCCompiler::CompileExpressionPreOp(asCScriptNode *node, asCExprContext *ctx
ctx->bc.InstrSHORT(asBC_BNOT, (short)ctx->type.stackOffset);
else
ctx->bc.InstrSHORT(asBC_BNOT64, (short)ctx->type.stackOffset);

if(isFlag)
ctx->type.dataType = originalType;
}
else
{
Expand Down Expand Up @@ -15925,7 +15994,19 @@ int asCCompiler::CompileOperator(asCScriptNode *node, asCExprContext *lctx, asCE
op == ttBitShiftRight || op == ttShiftRightLAssign ||
op == ttBitShiftRightArith || op == ttShiftRightAAssign )
{
asCDataType originalEnumType;
bool isFlags = false;
if (lctx->type.dataType.IsEnumType() &&
rctx->type.dataType.IsEnumType() &&
lctx->type.dataType.GetTypeInfo() == rctx->type.dataType.GetTypeInfo())
{
isFlags = CastToEnumType(lctx->type.dataType.GetTypeInfo())->isFlags;
originalEnumType = lctx->type.dataType;
}
CompileBitwiseOperator(node, lctx, rctx, ctx, op);
if (isFlags) {
ctx->type.dataType = originalEnumType;
}
return 0;
}

Expand Down
8 changes: 5 additions & 3 deletions sdk/angelscript/source/as_datatype.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -741,15 +741,17 @@ asSTypeBehaviour *asCDataType::GetBehaviour() const
return ot ? &ot->beh : 0;
}

bool asCDataType::IsEnumType() const
unsigned int asCDataType::IsEnumType() const
{
// Do a sanity check on the objectType, to verify that we aren't trying to access memory after it has been released
asASSERT(typeInfo == 0 || typeInfo->name.GetLength() < 100);

if (typeInfo && (typeInfo->flags & asOBJ_ENUM))
return true;
{
return CastToEnumType(typeInfo)->isFlags ? 2 : 1;
}

return false;
return 0;
}

END_AS_NAMESPACE
Expand Down
4 changes: 2 additions & 2 deletions sdk/angelscript/source/as_datatype.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@ struct asSNameSpace;

// TODO: refactor: Reference should not be part of the datatype. This should be stored separately, e.g. in asCExprValue
// MakeReference, MakeReadOnly, IsReference, IsReadOnly should be removed

class asCDataType
{
public:
Expand Down Expand Up @@ -100,7 +99,8 @@ class asCDataType
bool IsHandleToAuto() const {return isAuto && isObjectHandle;}
bool IsHandleToConst() const;
bool IsArrayType() const;
bool IsEnumType() const;
// returns 2 if it is a flag enum otherwise return 1
unsigned int IsEnumType() const;
bool IsAnyType() const {return tokenType == ttQuestion;}
bool IsHandleToAsHandleType() const {return isHandleToAsHandleType;}
bool IsAbstractClass() const;
Expand Down
16 changes: 7 additions & 9 deletions sdk/angelscript/source/as_parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2602,12 +2602,13 @@ asCScriptNode *asCParser::ParseScript(bool inBlock)
sToken tStart;
GetToken(&tStart);

// Optimize by skipping tokens 'shared', 'external', 'final', 'abstract' so they don't have to be checked in every condition
// Optimize by skipping tokens 'shared', 'external', 'final', 'abstract' , 'flag' so they don't have to be checked in every condition
sToken t1 = tStart;
while (IdentifierIs(t1, SHARED_TOKEN) ||
IdentifierIs(t1, EXTERNAL_TOKEN) ||
IdentifierIs(t1, FINAL_TOKEN) ||
IdentifierIs(t1, ABSTRACT_TOKEN))
IdentifierIs(t1, ABSTRACT_TOKEN) ||
IdentifierIs(t1,FLAG_TOKEN))
GetToken(&t1);
RewindTo(&tStart);

Expand Down Expand Up @@ -2836,9 +2837,10 @@ asCScriptNode *asCParser::ParseEnumeration()

// Optional 'shared' and 'external' token
GetToken(&token);

while( IdentifierIs(token, SHARED_TOKEN) ||
IdentifierIs(token, EXTERNAL_TOKEN) )
{
IdentifierIs(token, EXTERNAL_TOKEN) ||
IdentifierIs(token, FLAG_TOKEN)) {
RewindTo(&token);
node->AddChildLast(ParseIdentifier());
if( isSyntaxError ) return node;
Expand Down Expand Up @@ -2951,13 +2953,9 @@ asCScriptNode *asCParser::ParseEnumeration()

if( token.type == ttAssignment )
{
asCScriptNode *tmp;

RewindTo(&token);

tmp = SuperficiallyParseVarInit();

node->AddChildLast(tmp);
node->AddChildLast(SuperficiallyParseVarInit());
if( isSyntaxError ) return node;
GetToken(&token);
}
Expand Down
23 changes: 21 additions & 2 deletions sdk/angelscript/source/as_restore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ int asCReader::ReadInner()
module->m_enumTypes.Allocate(count, false);
for( i = 0; i < count && !error; i++ )
{
asCEnumType *et = asNEW(asCEnumType)(engine);
asCEnumType *et = asNEW(asCEnumType)(engine,false);
if( et == 0 )
{
error = true;
Expand Down Expand Up @@ -1634,7 +1634,19 @@ void asCReader::ReadTypeDeclaration(asCTypeInfo *type, int phase, bool *isExtern
// TODO: weak: Should not do this if the class has been declared with 'noweak'
engine->scriptFunctions[ot->beh.getWeakRefFlag]->AddRefInternal();
}

asCEnumType *et = CastToEnumType(type);
if(et)
{
char c;
ReadData(&c,1);
if(c == 'f')
et->isFlags = true;
else if (c != ' ')
{
error = true;
return;
}
}
// external shared flag
if (type->flags & asOBJ_SHARED)
{
Expand Down Expand Up @@ -4567,6 +4579,13 @@ void asCWriter::WriteTypeDeclaration(asCTypeInfo *type, int phase)
// namespace
WriteString(&type->nameSpace->name);

if ((type->flags & asOBJ_ENUM))
{
const asCEnumType* et = CastToEnumType(type);
asASSERT(et != nullptr);
const char c = et->isFlags ? 'f' : ' ';
WriteData(&c, 1);
}
// external shared flag
if ((type->flags & asOBJ_SHARED))
{
Expand Down
Loading