|
|
@@ -97,9 +97,26 @@ void asCCompiler::Reset(asCBuilder *builder, asCScriptCode *script, asCScriptFun
|
|
|
byteCode.ClearAll();
|
|
|
}
|
|
|
|
|
|
-int asCCompiler::CompileDefaultConstructor(asCBuilder *builder, asCScriptCode *script, asCScriptFunction *outFunc)
|
|
|
+int asCCompiler::CompileDefaultConstructor(asCBuilder *builder, asCScriptCode *script, asCScriptNode *node, asCScriptFunction *outFunc)
|
|
|
{
|
|
|
Reset(builder, script, outFunc);
|
|
|
+
|
|
|
+ // Make sure all the class members can be initialized with default constructors
|
|
|
+ for( asUINT n = 0; n < outFunc->objectType->properties.GetLength(); n++ )
|
|
|
+ {
|
|
|
+ asCDataType &dt = outFunc->objectType->properties[n]->type;
|
|
|
+ if( dt.IsObject() && !dt.IsObjectHandle() &&
|
|
|
+ (((dt.GetObjectType()->flags & asOBJ_REF) && dt.GetObjectType()->beh.factory == 0) ||
|
|
|
+ ((dt.GetObjectType()->flags & asOBJ_VALUE) && !(dt.GetObjectType()->flags & asOBJ_POD) && dt.GetObjectType()->beh.construct == 0)) )
|
|
|
+ {
|
|
|
+ asCString str;
|
|
|
+ if( dt.GetFuncDef() )
|
|
|
+ str.Format(TXT_NO_DEFAULT_CONSTRUCTOR_FOR_s, dt.GetFuncDef()->GetName());
|
|
|
+ else
|
|
|
+ str.Format(TXT_NO_DEFAULT_CONSTRUCTOR_FOR_s, dt.GetObjectType()->GetName());
|
|
|
+ Error(str.AddressOf(), node);
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
// If the class is derived from another, then the base class' default constructor must be called
|
|
|
if( outFunc->objectType->derivedFrom )
|
|
|
@@ -515,7 +532,7 @@ int asCCompiler::CallCopyConstructor(asCDataType &type, int offset, bool isObjec
|
|
|
return 0;
|
|
|
|
|
|
// CallCopyConstructor should not be called for object handles.
|
|
|
- asASSERT(!type.IsObjectHandle() || (type.GetObjectType() && (type.GetObjectType()->flags & asOBJ_ASHANDLE)) );
|
|
|
+ asASSERT( !type.IsObjectHandle() );
|
|
|
|
|
|
asCArray<asSExprContext*> args;
|
|
|
args.PushLast(arg);
|
|
|
@@ -620,8 +637,7 @@ int asCCompiler::CallCopyConstructor(asCDataType &type, int offset, bool isObjec
|
|
|
|
|
|
int asCCompiler::CallDefaultConstructor(asCDataType &type, int offset, bool isObjectOnHeap, asCByteCode *bc, asCScriptNode *node, bool isGlobalVar, bool deferDest)
|
|
|
{
|
|
|
- if( !type.IsObject() ||
|
|
|
- (type.IsObjectHandle() && !(type.GetObjectType()->flags & asOBJ_ASHANDLE)) )
|
|
|
+ if( !type.IsObject() || type.IsObjectHandle() )
|
|
|
return 0;
|
|
|
|
|
|
if( type.GetObjectType()->flags & asOBJ_REF )
|
|
|
@@ -725,8 +741,7 @@ void asCCompiler::CallDestructor(asCDataType &type, int offset, bool isObjectOnH
|
|
|
// Call destructor for the data type
|
|
|
if( type.IsObject() )
|
|
|
{
|
|
|
- // ASHANDLE is really a value type and shouldn't be deallocated. Just the destructor should be called
|
|
|
- if( isObjectOnHeap || (type.IsObjectHandle() && !(type.GetObjectType()->flags & asOBJ_ASHANDLE)) )
|
|
|
+ if( isObjectOnHeap || type.IsObjectHandle() )
|
|
|
{
|
|
|
// Free the memory
|
|
|
bc->InstrW_PTR(asBC_FREE, (short)offset, type.GetObjectType());
|
|
|
@@ -973,7 +988,7 @@ int asCCompiler::CompileGlobalVariable(asCBuilder *builder, asCScriptCode *scrip
|
|
|
// just the copy constructor. Only if no appropriate constructor is
|
|
|
// available should the assignment operator be used.
|
|
|
|
|
|
- if( (!gvar->datatype.IsObjectHandle() || gvar->datatype.GetObjectType()->flags & asOBJ_ASHANDLE) )
|
|
|
+ if( !gvar->datatype.IsObjectHandle() )
|
|
|
{
|
|
|
// Call the default constructor to have a valid object for the assignment
|
|
|
CallDefaultConstructor(gvar->datatype, gvar->index, true, &ctx.bc, gvar->idNode, true);
|
|
|
@@ -997,6 +1012,7 @@ int asCCompiler::CompileGlobalVariable(asCBuilder *builder, asCScriptCode *scrip
|
|
|
// the default action is a direct copy if it is the same type
|
|
|
// and a simple assignment.
|
|
|
bool assigned = false;
|
|
|
+ // Even though an ASHANDLE can be an explicit handle the assignment needs to be treated by the overloaded operator
|
|
|
if( lexpr.type.dataType.IsObject() && (!lexpr.type.isExplicitHandle || (lexpr.type.dataType.GetObjectType()->flags & asOBJ_ASHANDLE)) )
|
|
|
{
|
|
|
assigned = CompileOverloadedDualOperator(node, &lexpr, &expr, &ctx);
|
|
|
@@ -1038,7 +1054,7 @@ int asCCompiler::CompileGlobalVariable(asCBuilder *builder, asCScriptCode *scrip
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
- else if( gvar->datatype.IsObject() && (!gvar->datatype.IsObjectHandle() || gvar->datatype.GetObjectType()->flags & asOBJ_ASHANDLE) )
|
|
|
+ else if( gvar->datatype.IsObject() && !gvar->datatype.IsObjectHandle() )
|
|
|
{
|
|
|
// Call the default constructor in case no explicit initialization is given
|
|
|
CallDefaultConstructor(gvar->datatype, gvar->index, true, &ctx.bc, gvar->idNode, true);
|
|
|
@@ -1540,13 +1556,7 @@ void asCCompiler::MoveArgsToStack(int funcId, asCByteCode *bc, asCArray<asSExprC
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
- if( args[n]->type.dataType.GetObjectType() &&
|
|
|
- (args[n]->type.dataType.GetObjectType()->flags & asOBJ_ASHANDLE) &&
|
|
|
- args[n]->type.isVariable &&
|
|
|
- IsVariableOnHeap(args[n]->type.stackOffset) )
|
|
|
- bc->InstrWORD(asBC_GETOBJREF, (asWORD)offset);
|
|
|
- else
|
|
|
- bc->InstrWORD(asBC_GETREF, (asWORD)offset);
|
|
|
+ bc->InstrWORD(asBC_GETREF, (asWORD)offset);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
@@ -1685,10 +1695,12 @@ int asCCompiler::CompileDefaultArgs(asCScriptNode *node, asCArray<asSExprContext
|
|
|
return anyErrors ? -1 : 0;
|
|
|
}
|
|
|
|
|
|
-void asCCompiler::MatchFunctions(asCArray<int> &funcs, asCArray<asSExprContext*> &args, asCScriptNode *node, const char *name, asCObjectType *objectType, bool isConstMethod, bool silent, bool allowObjectConstruct, const asCString &scope)
|
|
|
+asUINT asCCompiler::MatchFunctions(asCArray<int> &funcs, asCArray<asSExprContext*> &args, asCScriptNode *node, const char *name, asCObjectType *objectType, bool isConstMethod, bool silent, bool allowObjectConstruct, const asCString &scope)
|
|
|
{
|
|
|
asCArray<int> origFuncs = funcs; // Keep the original list for error message
|
|
|
|
|
|
+ asUINT cost = 0;
|
|
|
+
|
|
|
asUINT n;
|
|
|
if( funcs.GetLength() > 0 )
|
|
|
{
|
|
|
@@ -1730,7 +1742,7 @@ void asCCompiler::MatchFunctions(asCArray<int> &funcs, asCArray<asSExprContext*>
|
|
|
for( n = 0; n < args.GetLength(); ++n )
|
|
|
{
|
|
|
asCArray<int> tempFuncs;
|
|
|
- MatchArgument(funcs, tempFuncs, &args[n]->type, n, allowObjectConstruct);
|
|
|
+ cost += MatchArgument(funcs, tempFuncs, &args[n]->type, n, allowObjectConstruct);
|
|
|
|
|
|
// Intersect the found functions with the list of matching functions
|
|
|
for( asUINT f = 0; f < matchingFuncs.GetLength(); f++ )
|
|
|
@@ -1809,6 +1821,8 @@ void asCCompiler::MatchFunctions(asCArray<int> &funcs, asCArray<asSExprContext*>
|
|
|
PrintMatchingFuncs(funcs, node);
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ return cost;
|
|
|
}
|
|
|
|
|
|
void asCCompiler::CompileDeclaration(asCScriptNode *decl, asCByteCode *bc)
|
|
|
@@ -2038,6 +2052,7 @@ void asCCompiler::CompileDeclaration(asCScriptNode *decl, asCByteCode *bc)
|
|
|
// the default action is a direct copy if it is the same type
|
|
|
// and a simple assignment.
|
|
|
bool assigned = false;
|
|
|
+ // Even though an ASHANDLE can be an explicit handle the overloaded operator needs to be called
|
|
|
if( lexpr.type.dataType.IsObject() && (!lexpr.type.isExplicitHandle || (lexpr.type.dataType.GetObjectType()->flags & asOBJ_ASHANDLE)) )
|
|
|
{
|
|
|
assigned = CompileOverloadedDualOperator(node, &lexpr, &expr, &ctx);
|
|
|
@@ -3097,7 +3112,7 @@ void asCCompiler::PrepareTemporaryObject(asCScriptNode *node, asSExprContext *ct
|
|
|
lvalue.isVariable = true;
|
|
|
lvalue.isExplicitHandle = ctx->type.isExplicitHandle;
|
|
|
|
|
|
- if( (!dt.IsObjectHandle() || (dt.GetObjectType() && (dt.GetObjectType()->flags & asOBJ_ASHANDLE))) &&
|
|
|
+ if( !dt.IsObjectHandle() &&
|
|
|
dt.GetObjectType() && (dt.GetBehaviour()->copyconstruct || dt.GetBehaviour()->copyfactory) )
|
|
|
{
|
|
|
PrepareForAssignment(&lvalue.dataType, ctx, node, true);
|
|
|
@@ -3259,7 +3274,7 @@ void asCCompiler::CompileReturnStatement(asCScriptNode *rnode, asCByteCode *bc)
|
|
|
// need to load it into the register
|
|
|
if( !expr.type.dataType.IsPrimitive() )
|
|
|
{
|
|
|
- if( (!expr.type.dataType.IsObjectHandle() || (expr.type.dataType.GetObjectType()->flags & asOBJ_ASHANDLE)) &&
|
|
|
+ if( !expr.type.dataType.IsObjectHandle() &&
|
|
|
expr.type.dataType.IsReference() )
|
|
|
expr.bc.Instr(asBC_RDSPTR);
|
|
|
|
|
|
@@ -4142,25 +4157,46 @@ bool asCCompiler::CompileRefCast(asSExprContext *ctx, const asCDataType &to, boo
|
|
|
}
|
|
|
|
|
|
|
|
|
-void asCCompiler::ImplicitConvPrimitiveToPrimitive(asSExprContext *ctx, const asCDataType &toOrig, asCScriptNode *node, EImplicitConv convType, bool generateCode)
|
|
|
+asUINT asCCompiler::ImplicitConvPrimitiveToPrimitive(asSExprContext *ctx, const asCDataType &toOrig, asCScriptNode *node, EImplicitConv convType, bool generateCode)
|
|
|
{
|
|
|
asCDataType to = toOrig;
|
|
|
to.MakeReference(false);
|
|
|
asASSERT( !ctx->type.dataType.IsReference() );
|
|
|
|
|
|
+ // Maybe no conversion is needed
|
|
|
+ if( to.IsEqualExceptConst(ctx->type.dataType) )
|
|
|
+ {
|
|
|
+ // A primitive is const or not
|
|
|
+ ctx->type.dataType.MakeReadOnly(to.IsReadOnly());
|
|
|
+ return asCC_NO_CONV;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Determine the cost of this conversion
|
|
|
+ asUINT cost = asCC_NO_CONV;
|
|
|
+ if( (to.IsIntegerType() || to.IsUnsignedType()) && (ctx->type.dataType.IsFloatType() || ctx->type.dataType.IsDoubleType()) )
|
|
|
+ cost = asCC_INT_FLOAT_CONV;
|
|
|
+ else if( (to.IsFloatType() || to.IsDoubleType()) && (ctx->type.dataType.IsIntegerType() || ctx->type.dataType.IsUnsignedType() || ctx->type.dataType.IsEnumType()) )
|
|
|
+ cost = asCC_INT_FLOAT_CONV;
|
|
|
+ else if( to.IsUnsignedType() && ctx->type.dataType.IsIntegerType() )
|
|
|
+ cost = asCC_SIGNED_CONV;
|
|
|
+ else if( to.IsIntegerType() && (ctx->type.dataType.IsUnsignedType() || ctx->type.dataType.IsEnumType()) )
|
|
|
+ cost = asCC_SIGNED_CONV;
|
|
|
+ else if( to.GetSizeInMemoryBytes() || ctx->type.dataType.GetSizeInMemoryBytes() )
|
|
|
+ cost = asCC_PRIMITIVE_SIZE_CONV;
|
|
|
+
|
|
|
// Start by implicitly converting constant values
|
|
|
if( ctx->type.isConstant )
|
|
|
+ {
|
|
|
ImplicitConversionConstant(ctx, to, node, convType);
|
|
|
-
|
|
|
- // A primitive is const or not
|
|
|
- ctx->type.dataType.MakeReadOnly(to.IsReadOnly());
|
|
|
-
|
|
|
- if( to == ctx->type.dataType )
|
|
|
- return;
|
|
|
+ ctx->type.dataType.MakeReadOnly(to.IsReadOnly());
|
|
|
+ return cost;
|
|
|
+ }
|
|
|
|
|
|
// Allow implicit conversion between numbers
|
|
|
if( generateCode )
|
|
|
{
|
|
|
+ // When generating the code the decision has already been made, so we don't bother determining the cost
|
|
|
+
|
|
|
// Convert smaller types to 32bit first
|
|
|
int s = ctx->type.dataType.GetSizeInMemoryBytes();
|
|
|
if( s < 4 )
|
|
|
@@ -4456,16 +4492,17 @@ void asCCompiler::ImplicitConvPrimitiveToPrimitive(asSExprContext *ctx, const as
|
|
|
|
|
|
// Primitive types on the stack, can be const or non-const
|
|
|
ctx->type.dataType.MakeReadOnly(to.IsReadOnly());
|
|
|
+ return cost;
|
|
|
}
|
|
|
|
|
|
-void asCCompiler::ImplicitConversion(asSExprContext *ctx, const asCDataType &to, asCScriptNode *node, EImplicitConv convType, bool generateCode, bool allowObjectConstruct)
|
|
|
+asUINT asCCompiler::ImplicitConversion(asSExprContext *ctx, const asCDataType &to, asCScriptNode *node, EImplicitConv convType, bool generateCode, bool allowObjectConstruct)
|
|
|
{
|
|
|
asASSERT( ctx->type.dataType.GetTokenType() != ttUnrecognizedToken ||
|
|
|
ctx->type.dataType.IsNullHandle() );
|
|
|
|
|
|
// No conversion from void to any other type
|
|
|
if( ctx->type.dataType.GetTokenType() == ttVoid )
|
|
|
- return;
|
|
|
+ return asCC_NO_CONV;
|
|
|
|
|
|
// Do we want a var type?
|
|
|
if( to.GetTokenType() == ttQuestion )
|
|
|
@@ -4475,26 +4512,28 @@ void asCCompiler::ImplicitConversion(asSExprContext *ctx, const asCDataType &to,
|
|
|
|
|
|
ctx->type.dataType = to;
|
|
|
|
|
|
- return;
|
|
|
+ return asCC_VARIABLE_CONV;
|
|
|
}
|
|
|
// Do we want a primitive?
|
|
|
else if( to.IsPrimitive() )
|
|
|
{
|
|
|
if( !ctx->type.dataType.IsPrimitive() )
|
|
|
- ImplicitConvObjectToPrimitive(ctx, to, node, convType, generateCode);
|
|
|
+ return ImplicitConvObjectToPrimitive(ctx, to, node, convType, generateCode);
|
|
|
else
|
|
|
- ImplicitConvPrimitiveToPrimitive(ctx, to, node, convType, generateCode);
|
|
|
+ return ImplicitConvPrimitiveToPrimitive(ctx, to, node, convType, generateCode);
|
|
|
}
|
|
|
else // The target is a complex type
|
|
|
{
|
|
|
if( ctx->type.dataType.IsPrimitive() )
|
|
|
- ImplicitConvPrimitiveToObject(ctx, to, node, convType, generateCode, allowObjectConstruct);
|
|
|
+ return ImplicitConvPrimitiveToObject(ctx, to, node, convType, generateCode, allowObjectConstruct);
|
|
|
else if( ctx->type.IsNullConstant() || ctx->type.dataType.GetObjectType() )
|
|
|
- ImplicitConvObjectToObject(ctx, to, node, convType, generateCode, allowObjectConstruct);
|
|
|
+ return ImplicitConvObjectToObject(ctx, to, node, convType, generateCode, allowObjectConstruct);
|
|
|
}
|
|
|
+
|
|
|
+ return asCC_NO_CONV;
|
|
|
}
|
|
|
|
|
|
-void asCCompiler::ImplicitConvObjectToPrimitive(asSExprContext *ctx, const asCDataType &to, asCScriptNode *node, EImplicitConv convType, bool generateCode)
|
|
|
+asUINT asCCompiler::ImplicitConvObjectToPrimitive(asSExprContext *ctx, const asCDataType &to, asCScriptNode *node, EImplicitConv convType, bool generateCode)
|
|
|
{
|
|
|
if( ctx->type.isExplicitHandle )
|
|
|
{
|
|
|
@@ -4505,7 +4544,7 @@ void asCCompiler::ImplicitConvObjectToPrimitive(asSExprContext *ctx, const asCDa
|
|
|
str.Format(TXT_CANT_IMPLICITLY_CONVERT_s_TO_s, ctx->type.dataType.Format().AddressOf(), to.Format().AddressOf());
|
|
|
Error(str.AddressOf(), node);
|
|
|
}
|
|
|
- return;
|
|
|
+ return asCC_NO_CONV;
|
|
|
}
|
|
|
|
|
|
// TODO: Must use the const cast behaviour if the object is read-only
|
|
|
@@ -4606,7 +4645,7 @@ void asCCompiler::ImplicitConvObjectToPrimitive(asSExprContext *ctx, const asCDa
|
|
|
ctx->type.Set(descr->returnType);
|
|
|
|
|
|
// Allow one more implicit conversion to another primitive type
|
|
|
- ImplicitConversion(ctx, to, node, convType, generateCode, false);
|
|
|
+ return asCC_OBJ_TO_PRIMITIVE_CONV + ImplicitConversion(ctx, to, node, convType, generateCode, false);
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
@@ -4617,19 +4656,22 @@ void asCCompiler::ImplicitConvObjectToPrimitive(asSExprContext *ctx, const asCDa
|
|
|
Error(str.AddressOf(), node);
|
|
|
}
|
|
|
}
|
|
|
-}
|
|
|
|
|
|
+ return asCC_NO_CONV;
|
|
|
+}
|
|
|
|
|
|
|
|
|
-void asCCompiler::ImplicitConvObjectToObject(asSExprContext *ctx, const asCDataType &to, asCScriptNode *node, EImplicitConv convType, bool generateCode, bool allowObjectConstruct)
|
|
|
+asUINT asCCompiler::ImplicitConvObjectRef(asSExprContext *ctx, const asCDataType &to, asCScriptNode *node, EImplicitConv convType, bool generateCode)
|
|
|
{
|
|
|
// Convert null to any object type handle, but not to a non-handle type
|
|
|
if( ctx->type.IsNullConstant() )
|
|
|
{
|
|
|
if( to.IsObjectHandle() )
|
|
|
+ {
|
|
|
ctx->type.dataType = to;
|
|
|
-
|
|
|
- return;
|
|
|
+ return asCC_REF_CONV;
|
|
|
+ }
|
|
|
+ return asCC_NO_CONV;
|
|
|
}
|
|
|
|
|
|
asASSERT(ctx->type.dataType.GetObjectType());
|
|
|
@@ -4641,16 +4683,16 @@ void asCCompiler::ImplicitConvObjectToObject(asSExprContext *ctx, const asCDataT
|
|
|
if( ctx->type.dataType.GetObjectType()->Implements(to.GetObjectType()) )
|
|
|
{
|
|
|
ctx->type.dataType.SetObjectType(to.GetObjectType());
|
|
|
+ return asCC_REF_CONV;
|
|
|
}
|
|
|
-
|
|
|
// If the to type is a class and the from type derives from it, then we can convert it immediately
|
|
|
- if( ctx->type.dataType.GetObjectType()->DerivesFrom(to.GetObjectType()) )
|
|
|
+ else if( ctx->type.dataType.GetObjectType()->DerivesFrom(to.GetObjectType()) )
|
|
|
{
|
|
|
ctx->type.dataType.SetObjectType(to.GetObjectType());
|
|
|
+ return asCC_REF_CONV;
|
|
|
}
|
|
|
-
|
|
|
// If the types are not equal yet, then we may still be able to find a reference cast
|
|
|
- if( ctx->type.dataType.GetObjectType() != to.GetObjectType() )
|
|
|
+ else if( ctx->type.dataType.GetObjectType() != to.GetObjectType() )
|
|
|
{
|
|
|
// A ref cast must not remove the constness
|
|
|
bool isConst = false;
|
|
|
@@ -4662,12 +4704,36 @@ void asCCompiler::ImplicitConvObjectToObject(asSExprContext *ctx, const asCDataT
|
|
|
CompileRefCast(ctx, to, convType == asIC_EXPLICIT_REF_CAST, node, generateCode);
|
|
|
|
|
|
ctx->type.dataType.MakeHandleToConst(isConst);
|
|
|
+
|
|
|
+ // Was the conversion done?
|
|
|
+ if( ctx->type.dataType.GetObjectType() == to.GetObjectType() )
|
|
|
+ return asCC_REF_CONV;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ // Convert matching function types
|
|
|
+ if( to.GetFuncDef() && ctx->type.dataType.GetFuncDef() &&
|
|
|
+ to.GetFuncDef() != ctx->type.dataType.GetFuncDef() )
|
|
|
+ {
|
|
|
+ asCScriptFunction *toFunc = to.GetFuncDef();
|
|
|
+ asCScriptFunction *fromFunc = ctx->type.dataType.GetFuncDef();
|
|
|
+ if( toFunc->IsSignatureExceptNameEqual(fromFunc) )
|
|
|
+ {
|
|
|
+ ctx->type.dataType.SetFuncDef(toFunc);
|
|
|
+ return asCC_REF_CONV;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return asCC_NO_CONV;
|
|
|
+}
|
|
|
+
|
|
|
+asUINT asCCompiler::ImplicitConvObjectValue(asSExprContext *ctx, const asCDataType &to, asCScriptNode * /*node*/, EImplicitConv convType, bool generateCode)
|
|
|
+{
|
|
|
+ asUINT cost = asCC_NO_CONV;
|
|
|
+
|
|
|
// If the base type is still different, and we are allowed to instance
|
|
|
// another object then we can try an implicit value cast
|
|
|
- if( to.GetObjectType() != ctx->type.dataType.GetObjectType() && allowObjectConstruct )
|
|
|
+ if( to.GetObjectType() != ctx->type.dataType.GetObjectType() )
|
|
|
{
|
|
|
// TODO: Implement support for implicit constructor/factory
|
|
|
|
|
|
@@ -4734,28 +4800,111 @@ void asCCompiler::ImplicitConvObjectToObject(asSExprContext *ctx, const asCDataT
|
|
|
}
|
|
|
else
|
|
|
ctx->type.Set(f->returnType);
|
|
|
+
|
|
|
+ cost = asCC_TO_OBJECT_CONV;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- // If we still haven't converted the base type to the correct type, then there is no need to continue
|
|
|
- if( to.GetObjectType() != ctx->type.dataType.GetObjectType() )
|
|
|
- return;
|
|
|
+ return cost;
|
|
|
+}
|
|
|
|
|
|
- // Convert matching function types
|
|
|
- if( to.GetFuncDef() && ctx->type.dataType.GetFuncDef() &&
|
|
|
- to.GetFuncDef() != ctx->type.dataType.GetFuncDef() )
|
|
|
+asUINT asCCompiler::ImplicitConvObjectToObject(asSExprContext *ctx, const asCDataType &to, asCScriptNode *node, EImplicitConv convType, bool generateCode, bool allowObjectConstruct)
|
|
|
+{
|
|
|
+ // First try a ref cast
|
|
|
+ asUINT cost = ImplicitConvObjectRef(ctx, to, node, convType, generateCode);
|
|
|
+
|
|
|
+ // If the desired type is an asOBJ_ASHANDLE then we'll assume it is allowed to implicitly
|
|
|
+ // construct the object through any of the available constructors
|
|
|
+ if( to.GetObjectType() && (to.GetObjectType()->flags & asOBJ_ASHANDLE) && to.GetObjectType() != ctx->type.dataType.GetObjectType() && allowObjectConstruct )
|
|
|
{
|
|
|
- asCScriptFunction *toFunc = to.GetFuncDef();
|
|
|
- asCScriptFunction *fromFunc = ctx->type.dataType.GetFuncDef();
|
|
|
- if( toFunc->IsSignatureExceptNameEqual(fromFunc) )
|
|
|
+ asCArray<int> funcs;
|
|
|
+ funcs = to.GetObjectType()->beh.constructors;
|
|
|
+
|
|
|
+ asCArray<asSExprContext *> args;
|
|
|
+ args.PushLast(ctx);
|
|
|
+
|
|
|
+ cost = asCC_TO_OBJECT_CONV + MatchFunctions(funcs, args, node, 0, 0, false, true, false);
|
|
|
+
|
|
|
+ // Did we find a matching constructor?
|
|
|
+ if( funcs.GetLength() == 1 )
|
|
|
{
|
|
|
- ctx->type.dataType.SetFuncDef(toFunc);
|
|
|
+ if( generateCode )
|
|
|
+ {
|
|
|
+ // TODO: This should really reuse the code from CompileConstructCall
|
|
|
+
|
|
|
+ // Allocate the new object
|
|
|
+ asCTypeInfo tempObj;
|
|
|
+ tempObj.dataType = to;
|
|
|
+ tempObj.dataType.MakeReference(false);
|
|
|
+ tempObj.stackOffset = (short)AllocateVariable(tempObj.dataType, true);
|
|
|
+ tempObj.dataType.MakeReference(true);
|
|
|
+ tempObj.isTemporary = true;
|
|
|
+ tempObj.isVariable = true;
|
|
|
+
|
|
|
+ bool onHeap = IsVariableOnHeap(tempObj.stackOffset);
|
|
|
+
|
|
|
+ // Push the address of the object on the stack
|
|
|
+ asSExprContext e(engine);
|
|
|
+ if( onHeap )
|
|
|
+ e.bc.InstrSHORT(asBC_VAR, tempObj.stackOffset);
|
|
|
+
|
|
|
+ PrepareFunctionCall(funcs[0], &e.bc, args);
|
|
|
+ MoveArgsToStack(funcs[0], &e.bc, args, false);
|
|
|
+
|
|
|
+ // If the object is allocated on the stack, then call the constructor as a normal function
|
|
|
+ if( onHeap )
|
|
|
+ {
|
|
|
+ int offset = 0;
|
|
|
+ asCScriptFunction *descr = builder->GetFunctionDescription(funcs[0]);
|
|
|
+ offset = descr->parameterTypes[0].GetSizeOnStackDWords();
|
|
|
+
|
|
|
+ e.bc.InstrWORD(asBC_GETREF, (asWORD)offset);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ e.bc.InstrSHORT(asBC_PSF, tempObj.stackOffset);
|
|
|
+
|
|
|
+ PerformFunctionCall(funcs[0], &e, onHeap, &args, tempObj.dataType.GetObjectType());
|
|
|
+
|
|
|
+ // Add tag that the object has been initialized
|
|
|
+ e.bc.ObjInfo(tempObj.stackOffset, asOBJ_INIT);
|
|
|
+
|
|
|
+ // The constructor doesn't return anything,
|
|
|
+ // so we have to manually inform the type of
|
|
|
+ // the return value
|
|
|
+ e.type = tempObj;
|
|
|
+ if( !onHeap )
|
|
|
+ e.type.dataType.MakeReference(false);
|
|
|
+
|
|
|
+ // Push the address of the object on the stack again
|
|
|
+ e.bc.InstrSHORT(asBC_PSF, tempObj.stackOffset);
|
|
|
+
|
|
|
+ MergeExprBytecodeAndType(ctx, &e);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ ctx->type.Set(asCDataType::CreateObject(to.GetObjectType(), false));
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ // If the base type is still different, and we are allowed to instance
|
|
|
+ // another object then we can try an implicit value cast
|
|
|
+ if( to.GetObjectType() != ctx->type.dataType.GetObjectType() && allowObjectConstruct )
|
|
|
+ {
|
|
|
+ // Attempt implicit value cast
|
|
|
+ cost = ImplicitConvObjectValue(ctx, to, node, convType, generateCode);
|
|
|
+ }
|
|
|
+
|
|
|
+ // If we still haven't converted the base type to the correct type, then there is
|
|
|
+ // no need to continue as it is not possible to do the conversion
|
|
|
+ if( to.GetObjectType() != ctx->type.dataType.GetObjectType() )
|
|
|
+ return asCC_NO_CONV;
|
|
|
+
|
|
|
|
|
|
if( to.IsObjectHandle() )
|
|
|
{
|
|
|
+ // There is no extra cost in converting to a handle
|
|
|
+
|
|
|
// reference to handle -> handle
|
|
|
// reference -> handle
|
|
|
// object -> handle
|
|
|
@@ -4808,6 +4957,8 @@ void asCCompiler::ImplicitConvObjectToObject(asSExprContext *ctx, const asCDataT
|
|
|
{
|
|
|
if( generateCode )
|
|
|
{
|
|
|
+ asASSERT( ctx->type.dataType.IsObjectHandle() );
|
|
|
+
|
|
|
// If the input type is a handle, then a simple ref copy is enough
|
|
|
bool isExplicitHandle = ctx->type.isExplicitHandle;
|
|
|
ctx->type.isExplicitHandle = ctx->type.dataType.IsObjectHandle();
|
|
|
@@ -4834,13 +4985,7 @@ void asCCompiler::ImplicitConvObjectToObject(asSExprContext *ctx, const asCDataT
|
|
|
}
|
|
|
else if( !to.IsReference() && ctx->type.dataType.IsReference() )
|
|
|
{
|
|
|
- // ASHANDLE is really a value type, even though it looks
|
|
|
- // like a handle, so we shouldn't dereference it
|
|
|
- if( !(ctx->type.dataType.GetObjectType() && (ctx->type.dataType.GetObjectType()->flags & asOBJ_ASHANDLE)) ||
|
|
|
- (ctx->type.isVariable && IsVariableOnHeap(ctx->type.stackOffset)) )
|
|
|
- Dereference(ctx, generateCode);
|
|
|
- else
|
|
|
- ctx->type.dataType.MakeReference(false);
|
|
|
+ Dereference(ctx, generateCode);
|
|
|
}
|
|
|
}
|
|
|
else
|
|
|
@@ -4876,14 +5021,22 @@ void asCCompiler::ImplicitConvObjectToObject(asSExprContext *ctx, const asCDataT
|
|
|
// In case the object was already in a temporary variable, then the function
|
|
|
// didn't really do anything so we need to remove the constness here
|
|
|
ctx->type.dataType.MakeReadOnly(false);
|
|
|
+
|
|
|
+ // Add the cost for the copy
|
|
|
+ cost += asCC_TO_OBJECT_CONV;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if( ctx->type.dataType.IsReference() )
|
|
|
{
|
|
|
- Dereference(ctx, generateCode);
|
|
|
-
|
|
|
- // TODO: Can't this leave unhandled deferred output params?
|
|
|
+ // This may look strange, but a value type allocated on the stack is already
|
|
|
+ // correct, so nothing should be done other than remove the mark as reference.
|
|
|
+ // For types allocated on the heap, it is necessary to dereference the pointer
|
|
|
+ // that is currently on the stack
|
|
|
+ if( IsVariableOnHeap(ctx->type.stackOffset) )
|
|
|
+ Dereference(ctx, generateCode);
|
|
|
+ else
|
|
|
+ ctx->type.dataType.MakeReference(false);
|
|
|
}
|
|
|
|
|
|
// A non-const object can be converted to a const object directly
|
|
|
@@ -4931,6 +5084,9 @@ void asCCompiler::ImplicitConvObjectToObject(asSExprContext *ctx, const asCDataT
|
|
|
// doesn't have to be made as it is already a unique object
|
|
|
PrepareTemporaryObject(node, ctx);
|
|
|
}
|
|
|
+
|
|
|
+ // Add the cost for the copy
|
|
|
+ cost += asCC_TO_OBJECT_CONV;
|
|
|
}
|
|
|
}
|
|
|
else
|
|
|
@@ -4960,6 +5116,9 @@ void asCCompiler::ImplicitConvObjectToObject(asSExprContext *ctx, const asCDataT
|
|
|
PrepareTemporaryObject(node, ctx);
|
|
|
|
|
|
ctx->type.dataType.MakeReadOnly(typeIsReadOnly);
|
|
|
+
|
|
|
+ // Add the cost for the copy
|
|
|
+ cost += asCC_TO_OBJECT_CONV;
|
|
|
}
|
|
|
|
|
|
// A handle can be converted to a reference, by checking for a null pointer
|
|
|
@@ -4980,18 +5139,21 @@ void asCCompiler::ImplicitConvObjectToObject(asSExprContext *ctx, const asCDataT
|
|
|
ctx->type.dataType.MakeReference(IsVariableOnHeap(ctx->type.stackOffset));
|
|
|
}
|
|
|
|
|
|
- // TODO: If the variable is an object allocated on the stack, this is not true
|
|
|
+ // TODO: If the variable is an object allocated on the stack the following is not true as the copy may not have been made
|
|
|
// Since it is a new temporary variable it doesn't have to be const
|
|
|
ctx->type.dataType.MakeReadOnly(to.IsReadOnly());
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ return cost;
|
|
|
}
|
|
|
|
|
|
-void asCCompiler::ImplicitConvPrimitiveToObject(asSExprContext * /*ctx*/, const asCDataType & /*to*/, asCScriptNode * /*node*/, EImplicitConv /*isExplicit*/, bool /*generateCode*/, bool /*allowObjectConstruct*/)
|
|
|
+asUINT asCCompiler::ImplicitConvPrimitiveToObject(asSExprContext * /*ctx*/, const asCDataType & /*to*/, asCScriptNode * /*node*/, EImplicitConv /*isExplicit*/, bool /*generateCode*/, bool /*allowObjectConstruct*/)
|
|
|
{
|
|
|
// TODO: This function should call the constructor/factory that has been marked as available
|
|
|
// for implicit conversions. The code will likely be similar to CallCopyConstructor()
|
|
|
+ return asCC_NO_CONV;
|
|
|
}
|
|
|
|
|
|
void asCCompiler::ImplicitConversionConstant(asSExprContext *from, const asCDataType &to, asCScriptNode *node, EImplicitConv convType)
|
|
|
@@ -7157,13 +7319,7 @@ void asCCompiler::CompileConversion(asCScriptNode *node, asSExprContext *ctx)
|
|
|
if( expr.type.dataType.IsReference() )
|
|
|
{
|
|
|
if( expr.type.dataType.IsObject() )
|
|
|
- {
|
|
|
- // ASHANDLE is actually a value type, even though it looks like a handle
|
|
|
- // For this reason we shouldn't dereference it, unless it is on the heap
|
|
|
- if( !(expr.type.dataType.GetObjectType()->flags & asOBJ_ASHANDLE) ||
|
|
|
- (expr.type.isVariable && IsVariableOnHeap(expr.type.stackOffset)) )
|
|
|
- Dereference(&expr, true);
|
|
|
- }
|
|
|
+ Dereference(&expr, true);
|
|
|
else
|
|
|
ConvertToVariable(&expr);
|
|
|
}
|
|
|
@@ -7327,8 +7483,8 @@ void asCCompiler::ProcessDeferredParams(asSExprContext *ctx)
|
|
|
if( !expr->type.isConstant || expr->type.IsNullConstant() )
|
|
|
ctx->bc.Pop(expr->type.dataType.GetSizeOnStackDWords());
|
|
|
|
|
|
- // Give a warning, except if the argument is null which indicate the argument is really to be ignored
|
|
|
- if( !expr->type.IsNullConstant() )
|
|
|
+ // Give a warning, except if the argument is null or 0 which indicate the argument is really to be ignored
|
|
|
+ if( !expr->type.IsNullConstant() && !(expr->type.isConstant && expr->type.qwordValue == 0) )
|
|
|
Warning(TXT_ARG_NOT_LVALUE, outParam.argNode);
|
|
|
|
|
|
ReleaseTemporaryVariable(outParam.argType, &ctx->bc);
|
|
|
@@ -7703,21 +7859,25 @@ int asCCompiler::CompileExpressionPreOp(asCScriptNode *node, asSExprContext *ctx
|
|
|
// Verify that the type allow its handle to be taken
|
|
|
if( ctx->type.isExplicitHandle ||
|
|
|
!ctx->type.dataType.IsObject() ||
|
|
|
- !((ctx->type.dataType.GetObjectType()->beh.addref && ctx->type.dataType.GetObjectType()->beh.release) || ctx->type.dataType.GetObjectType()->flags & asOBJ_ASHANDLE) )
|
|
|
+ !((ctx->type.dataType.GetObjectType()->beh.addref && ctx->type.dataType.GetObjectType()->beh.release) ||
|
|
|
+ (ctx->type.dataType.GetObjectType()->flags & asOBJ_ASHANDLE)) )
|
|
|
{
|
|
|
Error(TXT_OBJECT_HANDLE_NOT_SUPPORTED, node);
|
|
|
return -1;
|
|
|
}
|
|
|
|
|
|
// Objects that are not local variables are not references
|
|
|
- if( !ctx->type.dataType.IsReference() && !(ctx->type.dataType.IsObject() && !ctx->type.isVariable) )
|
|
|
+ // Objects allocated on the stack are also not marked as references
|
|
|
+ if( !ctx->type.dataType.IsReference() &&
|
|
|
+ !(ctx->type.dataType.IsObject() && !ctx->type.isVariable) &&
|
|
|
+ !(ctx->type.isVariable && !IsVariableOnHeap(ctx->type.stackOffset)) )
|
|
|
{
|
|
|
Error(TXT_NOT_VALID_REFERENCE, node);
|
|
|
return -1;
|
|
|
}
|
|
|
|
|
|
// If this is really an object then the handle created is a const handle
|
|
|
- bool makeConst = !ctx->type.dataType.IsObjectHandle();
|
|
|
+ bool makeConst = !ctx->type.dataType.IsObjectHandle() && !(ctx->type.dataType.GetObjectType()->flags & asOBJ_ASHANDLE);
|
|
|
|
|
|
// Mark the type as an object handle
|
|
|
ctx->type.dataType.MakeHandle(true);
|
|
|
@@ -8873,19 +9033,12 @@ int asCCompiler::GetPrecedence(asCScriptNode *op)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-int asCCompiler::MatchArgument(asCArray<int> &funcs, asCArray<int> &matches, const asCTypeInfo *argType, int paramNum, bool allowObjectConstruct)
|
|
|
+asUINT asCCompiler::MatchArgument(asCArray<int> &funcs, asCArray<int> &matches, const asCTypeInfo *argType, int paramNum, bool allowObjectConstruct)
|
|
|
{
|
|
|
- bool isExactMatch = false;
|
|
|
- bool isMatchExceptConst = false;
|
|
|
- bool isMatchWithBaseType = false;
|
|
|
- bool isMatchExceptSign = false;
|
|
|
- bool isMatchNotVarType = false;
|
|
|
-
|
|
|
- asUINT n;
|
|
|
-
|
|
|
+ asUINT bestCost = asUINT(-1);
|
|
|
matches.SetLength(0);
|
|
|
|
|
|
- for( n = 0; n < funcs.GetLength(); n++ )
|
|
|
+ for( asUINT n = 0; n < funcs.GetLength(); n++ )
|
|
|
{
|
|
|
asCScriptFunction *desc = builder->GetFunctionDescription(funcs[n]);
|
|
|
|
|
|
@@ -8897,7 +9050,7 @@ int asCCompiler::MatchArgument(asCArray<int> &funcs, asCArray<int> &matches, con
|
|
|
asSExprContext ti(engine);
|
|
|
ti.type = *argType;
|
|
|
if( argType->dataType.IsPrimitive() ) ti.type.dataType.MakeReference(false);
|
|
|
- ImplicitConversion(&ti, desc->parameterTypes[paramNum], 0, asIC_IMPLICIT_CONV, false, allowObjectConstruct);
|
|
|
+ asUINT cost = ImplicitConversion(&ti, desc->parameterTypes[paramNum], 0, asIC_IMPLICIT_CONV, false, allowObjectConstruct);
|
|
|
|
|
|
// If the function parameter is an inout-reference then it must not be possible to call the
|
|
|
// function with an incorrect argument type, even though the type can normally be converted.
|
|
|
@@ -8917,85 +9070,18 @@ int asCCompiler::MatchArgument(asCArray<int> &funcs, asCArray<int> &matches, con
|
|
|
// How well does the argument match the function parameter?
|
|
|
if( desc->parameterTypes[paramNum].IsEqualExceptRef(ti.type.dataType) )
|
|
|
{
|
|
|
- // Is it an exact match?
|
|
|
- if( argType->dataType.IsEqualExceptRef(ti.type.dataType) )
|
|
|
+ if( cost < bestCost )
|
|
|
{
|
|
|
- if( !isExactMatch ) matches.SetLength(0);
|
|
|
-
|
|
|
- isExactMatch = true;
|
|
|
-
|
|
|
- matches.PushLast(funcs[n]);
|
|
|
- continue;
|
|
|
+ matches.SetLength(0);
|
|
|
+ bestCost = cost;
|
|
|
}
|
|
|
|
|
|
- if( !isExactMatch )
|
|
|
- {
|
|
|
- // Is it a match except const?
|
|
|
- if( argType->dataType.IsEqualExceptRefAndConst(ti.type.dataType) )
|
|
|
- {
|
|
|
- if( !isMatchExceptConst ) matches.SetLength(0);
|
|
|
-
|
|
|
- isMatchExceptConst = true;
|
|
|
-
|
|
|
- matches.PushLast(funcs[n]);
|
|
|
- continue;
|
|
|
- }
|
|
|
-
|
|
|
- if( !isMatchExceptConst )
|
|
|
- {
|
|
|
- // Is it a size promotion, e.g. int8 -> int?
|
|
|
- if( argType->dataType.IsSamePrimitiveBaseType(ti.type.dataType) ||
|
|
|
- (argType->dataType.IsEnumType() && ti.type.dataType.IsIntegerType()) )
|
|
|
- {
|
|
|
- if( !isMatchWithBaseType ) matches.SetLength(0);
|
|
|
-
|
|
|
- isMatchWithBaseType = true;
|
|
|
-
|
|
|
- matches.PushLast(funcs[n]);
|
|
|
- continue;
|
|
|
- }
|
|
|
-
|
|
|
- if( !isMatchWithBaseType )
|
|
|
- {
|
|
|
- // Conversion between signed and unsigned integer is better than between integer and float
|
|
|
-
|
|
|
- // Is it a match except for sign?
|
|
|
- if( (argType->dataType.IsIntegerType() && ti.type.dataType.IsUnsignedType()) ||
|
|
|
- (argType->dataType.IsUnsignedType() && ti.type.dataType.IsIntegerType()) ||
|
|
|
- (argType->dataType.IsEnumType() && ti.type.dataType.IsUnsignedType()) )
|
|
|
- {
|
|
|
- if( !isMatchExceptSign ) matches.SetLength(0);
|
|
|
-
|
|
|
- isMatchExceptSign = true;
|
|
|
-
|
|
|
- matches.PushLast(funcs[n]);
|
|
|
- continue;
|
|
|
- }
|
|
|
-
|
|
|
- if( !isMatchExceptSign )
|
|
|
- {
|
|
|
- // If there was any match without a var type it has higher priority
|
|
|
- if( desc->parameterTypes[paramNum].GetTokenType() != ttQuestion )
|
|
|
- {
|
|
|
- if( !isMatchNotVarType ) matches.SetLength(0);
|
|
|
-
|
|
|
- isMatchNotVarType = true;
|
|
|
-
|
|
|
- matches.PushLast(funcs[n]);
|
|
|
- continue;
|
|
|
- }
|
|
|
-
|
|
|
- // Implicit conversion to ?& has the smallest priority
|
|
|
- if( !isMatchNotVarType )
|
|
|
- matches.PushLast(funcs[n]);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
+ if( cost == bestCost )
|
|
|
+ matches.PushLast(funcs[n]);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- return (int)matches.GetLength();
|
|
|
+ return bestCost;
|
|
|
}
|
|
|
|
|
|
void asCCompiler::PrepareArgument2(asSExprContext *ctx, asSExprContext *arg, asCDataType *paramType, bool isFunction, int refType, bool isMakingCopy)
|
|
|
@@ -9303,11 +9389,7 @@ void asCCompiler::MakeFunctionCall(asSExprContext *ctx, int funcId, asCObjectTyp
|
|
|
{
|
|
|
if( objectType )
|
|
|
{
|
|
|
- // The ASHANDLE type is really a value type, so if it is a
|
|
|
- // local variable on the stack it must not be dereferenced
|
|
|
- if( !(objectType->flags & asOBJ_ASHANDLE) ||
|
|
|
- !(ctx->type.isVariable && !IsVariableOnHeap(ctx->type.stackOffset)) )
|
|
|
- Dereference(ctx, true);
|
|
|
+ Dereference(ctx, true);
|
|
|
|
|
|
// This following warning was removed as there may be valid reasons
|
|
|
// for calling non-const methods on temporary objects, and we shouldn't
|
|
|
@@ -10998,7 +11080,7 @@ void asCCompiler::PerformFunctionCall(int funcId, asSExprContext *ctx, bool isCo
|
|
|
{
|
|
|
ctx->bc.Instr(asBC_PshRPtr);
|
|
|
if( descr->returnType.IsObject() &&
|
|
|
- (!descr->returnType.IsObjectHandle() || (descr->returnType.GetObjectType()->flags & asOBJ_ASHANDLE)) )
|
|
|
+ !descr->returnType.IsObjectHandle() )
|
|
|
{
|
|
|
// We are getting the pointer to the object
|
|
|
// not a pointer to a object variable
|