|
@@ -163,7 +163,7 @@ int asCCompiler::CompileDefaultConstructor(asCBuilder *builder, asCScriptCode *s
|
|
|
|
|
|
|
|
// Count total variable size
|
|
// Count total variable size
|
|
|
int varSize = GetVariableOffset((int)variableAllocations.GetLength()) - 1;
|
|
int varSize = GetVariableOffset((int)variableAllocations.GetLength()) - 1;
|
|
|
- outFunc->variableSpace = varSize;
|
|
|
|
|
|
|
+ outFunc->scriptData->variableSpace = varSize;
|
|
|
|
|
|
|
|
FinalizeFunction();
|
|
FinalizeFunction();
|
|
|
|
|
|
|
@@ -197,7 +197,7 @@ int asCCompiler::CompileFactory(asCBuilder *builder, asCScriptCode *script, asCS
|
|
|
// Allocate the class and instanciate it with the constructor
|
|
// Allocate the class and instanciate it with the constructor
|
|
|
int varOffset = AllocateVariable(dt, true);
|
|
int varOffset = AllocateVariable(dt, true);
|
|
|
|
|
|
|
|
- outFunc->variableSpace = AS_PTR_SIZE;
|
|
|
|
|
|
|
+ outFunc->scriptData->variableSpace = AS_PTR_SIZE;
|
|
|
byteCode.InstrSHORT(asBC_PSF, (short)varOffset);
|
|
byteCode.InstrSHORT(asBC_PSF, (short)varOffset);
|
|
|
|
|
|
|
|
// Copy all arguments to the top of the stack
|
|
// Copy all arguments to the top of the stack
|
|
@@ -254,6 +254,7 @@ void asCCompiler::FinalizeFunction()
|
|
|
{
|
|
{
|
|
|
TimeIt("asCCompiler::FinalizeFunction");
|
|
TimeIt("asCCompiler::FinalizeFunction");
|
|
|
|
|
|
|
|
|
|
+ asASSERT( outFunc->scriptData );
|
|
|
asUINT n;
|
|
asUINT n;
|
|
|
|
|
|
|
|
// Finalize the bytecode
|
|
// Finalize the bytecode
|
|
@@ -269,43 +270,43 @@ void asCCompiler::FinalizeFunction()
|
|
|
{
|
|
{
|
|
|
if( variableIsOnHeap[n] )
|
|
if( variableIsOnHeap[n] )
|
|
|
{
|
|
{
|
|
|
- outFunc->objVariableTypes.PushLast(variableAllocations[n].GetObjectType());
|
|
|
|
|
- outFunc->funcVariableTypes.PushLast(variableAllocations[n].GetFuncDef());
|
|
|
|
|
- outFunc->objVariablePos.PushLast(GetVariableOffset(n));
|
|
|
|
|
|
|
+ outFunc->scriptData->objVariableTypes.PushLast(variableAllocations[n].GetObjectType());
|
|
|
|
|
+ outFunc->scriptData->funcVariableTypes.PushLast(variableAllocations[n].GetFuncDef());
|
|
|
|
|
+ outFunc->scriptData->objVariablePos.PushLast(GetVariableOffset(n));
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
- outFunc->objVariablesOnHeap = asUINT(outFunc->objVariablePos.GetLength());
|
|
|
|
|
|
|
+ outFunc->scriptData->objVariablesOnHeap = asUINT(outFunc->scriptData->objVariablePos.GetLength());
|
|
|
for( n = 0; n < variableAllocations.GetLength(); n++ )
|
|
for( n = 0; n < variableAllocations.GetLength(); n++ )
|
|
|
{
|
|
{
|
|
|
if( variableAllocations[n].IsObject() && !variableAllocations[n].IsReference() )
|
|
if( variableAllocations[n].IsObject() && !variableAllocations[n].IsReference() )
|
|
|
{
|
|
{
|
|
|
if( !variableIsOnHeap[n] )
|
|
if( !variableIsOnHeap[n] )
|
|
|
{
|
|
{
|
|
|
- outFunc->objVariableTypes.PushLast(variableAllocations[n].GetObjectType());
|
|
|
|
|
- outFunc->funcVariableTypes.PushLast(variableAllocations[n].GetFuncDef());
|
|
|
|
|
- outFunc->objVariablePos.PushLast(GetVariableOffset(n));
|
|
|
|
|
|
|
+ outFunc->scriptData->objVariableTypes.PushLast(variableAllocations[n].GetObjectType());
|
|
|
|
|
+ outFunc->scriptData->funcVariableTypes.PushLast(variableAllocations[n].GetFuncDef());
|
|
|
|
|
+ outFunc->scriptData->objVariablePos.PushLast(GetVariableOffset(n));
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Copy byte code to the function
|
|
// Copy byte code to the function
|
|
|
- asASSERT( outFunc->byteCode.GetLength() == 0 );
|
|
|
|
|
- outFunc->byteCode.SetLength(byteCode.GetSize());
|
|
|
|
|
- byteCode.Output(outFunc->byteCode.AddressOf());
|
|
|
|
|
|
|
+ asASSERT( outFunc->scriptData->byteCode.GetLength() == 0 );
|
|
|
|
|
+ outFunc->scriptData->byteCode.SetLength(byteCode.GetSize());
|
|
|
|
|
+ byteCode.Output(outFunc->scriptData->byteCode.AddressOf());
|
|
|
outFunc->AddReferences();
|
|
outFunc->AddReferences();
|
|
|
- outFunc->stackNeeded = byteCode.largestStackUsed + outFunc->variableSpace;
|
|
|
|
|
- outFunc->lineNumbers = byteCode.lineNumbers;
|
|
|
|
|
|
|
+ outFunc->scriptData->stackNeeded = byteCode.largestStackUsed + outFunc->scriptData->variableSpace;
|
|
|
|
|
+ outFunc->scriptData->lineNumbers = byteCode.lineNumbers;
|
|
|
|
|
|
|
|
// Extract the script section indexes too if there are any entries that are different from the function's script section
|
|
// Extract the script section indexes too if there are any entries that are different from the function's script section
|
|
|
- int lastIdx = outFunc->scriptSectionIdx;
|
|
|
|
|
|
|
+ int lastIdx = outFunc->scriptData->scriptSectionIdx;
|
|
|
for( n = 0; n < byteCode.sectionIdxs.GetLength(); n++ )
|
|
for( n = 0; n < byteCode.sectionIdxs.GetLength(); n++ )
|
|
|
{
|
|
{
|
|
|
if( byteCode.sectionIdxs[n] != lastIdx )
|
|
if( byteCode.sectionIdxs[n] != lastIdx )
|
|
|
{
|
|
{
|
|
|
lastIdx = byteCode.sectionIdxs[n];
|
|
lastIdx = byteCode.sectionIdxs[n];
|
|
|
- outFunc->sectionIdxs.PushLast(byteCode.lineNumbers[n*2]);
|
|
|
|
|
- outFunc->sectionIdxs.PushLast(lastIdx);
|
|
|
|
|
|
|
+ outFunc->scriptData->sectionIdxs.PushLast(byteCode.lineNumbers[n*2]);
|
|
|
|
|
+ outFunc->scriptData->sectionIdxs.PushLast(lastIdx);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
@@ -391,7 +392,7 @@ int asCCompiler::SetupParametersAndReturnVariable(asCArray<asCString> ¶meter
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Add marker for variable declaration
|
|
// Add marker for variable declaration
|
|
|
- byteCode.VarDecl((int)outFunc->variables.GetLength());
|
|
|
|
|
|
|
+ byteCode.VarDecl((int)outFunc->scriptData->variables.GetLength());
|
|
|
outFunc->AddVariable(name, type, stackPos);
|
|
outFunc->AddVariable(name, type, stackPos);
|
|
|
}
|
|
}
|
|
|
else
|
|
else
|
|
@@ -630,7 +631,7 @@ int asCCompiler::CompileFunction(asCBuilder *builder, asCScriptCode *script, asC
|
|
|
|
|
|
|
|
// Count total variable size
|
|
// Count total variable size
|
|
|
int varSize = GetVariableOffset((int)variableAllocations.GetLength()) - 1;
|
|
int varSize = GetVariableOffset((int)variableAllocations.GetLength()) - 1;
|
|
|
- outFunc->variableSpace = varSize;
|
|
|
|
|
|
|
+ outFunc->scriptData->variableSpace = varSize;
|
|
|
|
|
|
|
|
// Deallocate all local variables
|
|
// Deallocate all local variables
|
|
|
int n;
|
|
int n;
|
|
@@ -807,7 +808,7 @@ int asCCompiler::CallCopyConstructor(asCDataType &type, int offset, bool isObjec
|
|
|
return -1;
|
|
return -1;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-int asCCompiler::CallDefaultConstructor(asCDataType &type, int offset, bool isObjectOnHeap, asCByteCode *bc, asCScriptNode *node, int isVarGlobOrMem, bool deferDest)
|
|
|
|
|
|
|
+int asCCompiler::CallDefaultConstructor(asCDataType &type, int offset, bool isObjectOnHeap, asCByteCode *bc, asCScriptNode *node, int isVarGlobOrMem, bool derefDest)
|
|
|
{
|
|
{
|
|
|
if( !type.IsObject() || type.IsObjectHandle() )
|
|
if( !type.IsObject() || type.IsObjectHandle() )
|
|
|
return 0;
|
|
return 0;
|
|
@@ -901,7 +902,7 @@ int asCCompiler::CallDefaultConstructor(asCDataType &type, int offset, bool isOb
|
|
|
{
|
|
{
|
|
|
// Call the constructor as a normal function
|
|
// Call the constructor as a normal function
|
|
|
bc->InstrSHORT(asBC_PSF, (short)offset);
|
|
bc->InstrSHORT(asBC_PSF, (short)offset);
|
|
|
- if( deferDest )
|
|
|
|
|
|
|
+ if( derefDest )
|
|
|
bc->Instr(asBC_RDSPtr);
|
|
bc->Instr(asBC_RDSPtr);
|
|
|
asSExprContext ctx(engine);
|
|
asSExprContext ctx(engine);
|
|
|
PerformFunctionCall(func, &ctx, false, 0, type.GetObjectType());
|
|
PerformFunctionCall(func, &ctx, false, 0, type.GetObjectType());
|
|
@@ -951,6 +952,10 @@ void asCCompiler::CallDestructor(asCDataType &type, int offset, bool isObjectOnH
|
|
|
// Call destructor for the data type
|
|
// Call destructor for the data type
|
|
|
if( type.IsObject() )
|
|
if( type.IsObject() )
|
|
|
{
|
|
{
|
|
|
|
|
+ // Nothing is done for list pattern types, as this is taken care of by the CompileInitList method
|
|
|
|
|
+ if( type.GetObjectType()->flags & asOBJ_LIST_PATTERN )
|
|
|
|
|
+ return;
|
|
|
|
|
+
|
|
|
if( isObjectOnHeap || type.IsObjectHandle() )
|
|
if( isObjectOnHeap || type.IsObjectHandle() )
|
|
|
{
|
|
{
|
|
|
// Free the memory
|
|
// Free the memory
|
|
@@ -1108,7 +1113,7 @@ int asCCompiler::CompileGlobalVariable(asCBuilder *builder, asCScriptCode *scrip
|
|
|
LineInstr(&byteCode, pos);
|
|
LineInstr(&byteCode, pos);
|
|
|
|
|
|
|
|
// Reserve space for all local variables
|
|
// Reserve space for all local variables
|
|
|
- outFunc->variableSpace = varSize;
|
|
|
|
|
|
|
+ outFunc->scriptData->variableSpace = varSize;
|
|
|
|
|
|
|
|
ctx.bc.OptimizeLocally(tempVariableOffsets);
|
|
ctx.bc.OptimizeLocally(tempVariableOffsets);
|
|
|
|
|
|
|
@@ -1148,7 +1153,7 @@ int asCCompiler::CompileGlobalVariable(asCBuilder *builder, asCScriptCode *scrip
|
|
|
void asCCompiler::DetermineSingleFunc(asSExprContext *ctx, asCScriptNode *node)
|
|
void asCCompiler::DetermineSingleFunc(asSExprContext *ctx, asCScriptNode *node)
|
|
|
{
|
|
{
|
|
|
// Don't do anything if this is not a deferred global function
|
|
// Don't do anything if this is not a deferred global function
|
|
|
- if( ctx->methodName == "" || ctx->type.dataType.GetObjectType() != 0 )
|
|
|
|
|
|
|
+ if( !ctx->IsGlobalFunc() )
|
|
|
return;
|
|
return;
|
|
|
|
|
|
|
|
// Determine the namespace
|
|
// Determine the namespace
|
|
@@ -1212,9 +1217,10 @@ void asCCompiler::PrepareArgument(asCDataType *paramType, asSExprContext *ctx, a
|
|
|
|
|
|
|
|
// Since the function is expecting a var type ?, then we don't want to convert the argument to anything else
|
|
// Since the function is expecting a var type ?, then we don't want to convert the argument to anything else
|
|
|
param = ctx->type.dataType;
|
|
param = ctx->type.dataType;
|
|
|
- param.MakeHandle(ctx->type.isExplicitHandle);
|
|
|
|
|
|
|
+ param.MakeHandle(ctx->type.isExplicitHandle || ctx->type.IsNullConstant());
|
|
|
|
|
|
|
|
- // Reference types will always be passed as handles to ? parameters
|
|
|
|
|
|
|
+ // If value assign is disabled for reference types, then make
|
|
|
|
|
+ // sure to always pass the handle to ? parameters
|
|
|
if( builder->engine->ep.disallowValueAssignForRefType &&
|
|
if( builder->engine->ep.disallowValueAssignForRefType &&
|
|
|
ctx->type.dataType.GetObjectType() && (ctx->type.dataType.GetObjectType()->flags & asOBJ_REF) && !(ctx->type.dataType.GetObjectType()->flags & asOBJ_SCOPED) )
|
|
ctx->type.dataType.GetObjectType() && (ctx->type.dataType.GetObjectType()->flags & asOBJ_REF) && !(ctx->type.dataType.GetObjectType()->flags & asOBJ_SCOPED) )
|
|
|
{
|
|
{
|
|
@@ -1328,7 +1334,12 @@ void asCCompiler::PrepareArgument(asCDataType *paramType, asSExprContext *ctx, a
|
|
|
|
|
|
|
|
ReleaseTemporaryVariable(ctx->type, &ctx->bc);
|
|
ReleaseTemporaryVariable(ctx->type, &ctx->bc);
|
|
|
|
|
|
|
|
- ctx->type = type;
|
|
|
|
|
|
|
+ ctx->type.Set(dt);
|
|
|
|
|
+ ctx->type.isTemporary = true;
|
|
|
|
|
+ ctx->type.stackOffset = offset;
|
|
|
|
|
+ if( dt.IsObjectHandle() )
|
|
|
|
|
+ ctx->type.isExplicitHandle = true;
|
|
|
|
|
+ ctx->type.dataType.MakeReference(false);
|
|
|
|
|
|
|
|
ctx->bc.InstrSHORT(asBC_PSF, (short)offset);
|
|
ctx->bc.InstrSHORT(asBC_PSF, (short)offset);
|
|
|
if( dt.IsObject() && !dt.IsObjectHandle() )
|
|
if( dt.IsObject() && !dt.IsObjectHandle() )
|
|
@@ -1462,7 +1473,7 @@ void asCCompiler::PrepareArgument(asCDataType *paramType, asSExprContext *ctx, a
|
|
|
// Make sure the reference to the value is on the stack
|
|
// Make sure the reference to the value is on the stack
|
|
|
// For objects, the reference needs to be dereferenced so the pointer on the stack is to the actual object
|
|
// For objects, the reference needs to be dereferenced so the pointer on the stack is to the actual object
|
|
|
// For handles, the reference shouldn't be changed because the pointer on the stack should be to the handle
|
|
// For handles, the reference shouldn't be changed because the pointer on the stack should be to the handle
|
|
|
- if( ctx->type.dataType.IsObject() && ctx->type.dataType.IsReference() && !paramType->IsObjectHandle() )
|
|
|
|
|
|
|
+ if( ctx->type.dataType.IsObject() && ctx->type.dataType.IsReference() && !param.IsObjectHandle() )
|
|
|
Dereference(ctx, true);
|
|
Dereference(ctx, true);
|
|
|
else if( ctx->type.isVariable && !ctx->type.dataType.IsObject() )
|
|
else if( ctx->type.isVariable && !ctx->type.dataType.IsObject() )
|
|
|
ctx->bc.InstrSHORT(asBC_PSF, ctx->type.stackOffset);
|
|
ctx->bc.InstrSHORT(asBC_PSF, ctx->type.stackOffset);
|
|
@@ -2074,7 +2085,7 @@ void asCCompiler::CompileDeclaration(asCScriptNode *decl, asCByteCode *bc)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Add marker that the variable has been declared
|
|
// Add marker that the variable has been declared
|
|
|
- bc->VarDecl((int)outFunc->variables.GetLength());
|
|
|
|
|
|
|
+ bc->VarDecl((int)outFunc->scriptData->variables.GetLength());
|
|
|
outFunc->AddVariable(name, type, offset);
|
|
outFunc->AddVariable(name, type, offset);
|
|
|
|
|
|
|
|
// Keep the node for the variable decl
|
|
// Keep the node for the variable decl
|
|
@@ -2435,34 +2446,50 @@ void asCCompiler::CompileInitList(asCTypeInfo *var, asCScriptNode *node, asCByte
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // Count the number of elements and initialize the array with the correct size
|
|
|
|
|
- int countElements = 0;
|
|
|
|
|
- asCScriptNode *el = node->firstChild;
|
|
|
|
|
- while( el )
|
|
|
|
|
- {
|
|
|
|
|
- countElements++;
|
|
|
|
|
- el = el->next;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // Construct the array with the size elements
|
|
|
|
|
-
|
|
|
|
|
- // TODO: value on stack: This needs to support value types on the stack as well
|
|
|
|
|
|
|
+ // Construct the buffer with the elements
|
|
|
|
|
|
|
|
// Find the list factory
|
|
// Find the list factory
|
|
|
// TODO: initlist: Add support for value types as well
|
|
// TODO: initlist: Add support for value types as well
|
|
|
int funcId = var->dataType.GetBehaviour()->listFactory;
|
|
int funcId = var->dataType.GetBehaviour()->listFactory;
|
|
|
|
|
+ asASSERT( engine->scriptFunctions[funcId]->listPattern );
|
|
|
|
|
+
|
|
|
|
|
+ // TODO: runtime optimize: A future optimization should be to use the stack space directly
|
|
|
|
|
+ // for small buffers so that the dynamic allocation is skipped
|
|
|
|
|
+
|
|
|
|
|
+ // Create a new special object type for the lists. Both asCRestore and the
|
|
|
|
|
+ // context exception handler will need this to know how to parse the buffer.
|
|
|
|
|
+ asCObjectType *listPatternType = engine->GetListPatternType(funcId);
|
|
|
|
|
+
|
|
|
|
|
+ // Allocate a temporary variable to hold the pointer to the buffer
|
|
|
|
|
+ int bufferVar = AllocateVariable(asCDataType::CreateObject(listPatternType, false), true);
|
|
|
|
|
+ asUINT bufferSize = 0;
|
|
|
|
|
|
|
|
|
|
+ // Evaluate all elements of the list
|
|
|
|
|
+ asSExprContext valueExpr(engine);
|
|
|
|
|
+ asCScriptNode *el = node;
|
|
|
|
|
+ asSListPatternNode *patternNode = engine->scriptFunctions[listPatternType->templateSubTypes[0].GetBehaviour()->listFactory]->listPattern;
|
|
|
|
|
+ int r = CompileInitListElement(patternNode, el, engine->GetTypeIdFromDataType(asCDataType::CreateObject(listPatternType, false)), bufferVar, bufferSize, valueExpr.bc);
|
|
|
|
|
+ asASSERT( r || patternNode == 0 );
|
|
|
|
|
+ UNUSED_VAR(r);
|
|
|
|
|
+
|
|
|
|
|
+ // After all values have been evaluated we know the final size of the buffer
|
|
|
|
|
+ asSExprContext allocExpr(engine);
|
|
|
|
|
+ allocExpr.bc.InstrSHORT_DW(asBC_AllocMem, bufferVar, bufferSize);
|
|
|
|
|
+
|
|
|
|
|
+ // Merge the bytecode into the final sequence
|
|
|
|
|
+ bc->AddCode(&allocExpr.bc);
|
|
|
|
|
+ bc->AddCode(&valueExpr.bc);
|
|
|
|
|
+
|
|
|
|
|
+ // The object itself is the last to be created and will receive the pointer to the buffer
|
|
|
asCArray<asSExprContext *> args;
|
|
asCArray<asSExprContext *> args;
|
|
|
asSExprContext arg1(engine);
|
|
asSExprContext arg1(engine);
|
|
|
- arg1.bc.InstrDWORD(asBC_PshC4, countElements);
|
|
|
|
|
|
|
+ bc->InstrSHORT(asBC_PshVPtr, bufferVar);
|
|
|
arg1.type.Set(asCDataType::CreatePrimitive(ttUInt, false));
|
|
arg1.type.Set(asCDataType::CreatePrimitive(ttUInt, false));
|
|
|
|
|
+ arg1.type.dataType.MakeReference(true);
|
|
|
args.PushLast(&arg1);
|
|
args.PushLast(&arg1);
|
|
|
|
|
|
|
|
asSExprContext ctx(engine);
|
|
asSExprContext ctx(engine);
|
|
|
|
|
|
|
|
- PrepareFunctionCall(funcId, &ctx.bc, args);
|
|
|
|
|
- MoveArgsToStack(funcId, &ctx.bc, args, false);
|
|
|
|
|
-
|
|
|
|
|
if( var->isVariable )
|
|
if( var->isVariable )
|
|
|
{
|
|
{
|
|
|
asASSERT( isVarGlobOrMem == 0 );
|
|
asASSERT( isVarGlobOrMem == 0 );
|
|
@@ -2493,110 +2520,188 @@ void asCCompiler::CompileInitList(asCTypeInfo *var, asCScriptNode *node, asCByte
|
|
|
ReleaseTemporaryVariable(ctx.type.stackOffset, &ctx.bc);
|
|
ReleaseTemporaryVariable(ctx.type.stackOffset, &ctx.bc);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-
|
|
|
|
|
bc->AddCode(&ctx.bc);
|
|
bc->AddCode(&ctx.bc);
|
|
|
|
|
|
|
|
- // TODO: initlist: Should we have a special indexing operator for this? How can we support
|
|
|
|
|
- // initialization lists with different types for different elements? Maybe
|
|
|
|
|
- // by using the variable arguments the initialization can be done with one
|
|
|
|
|
- // call, passing all the elements as arguments. The registered function can
|
|
|
|
|
- // then traverse them however it wants.
|
|
|
|
|
-
|
|
|
|
|
- // Find the indexing operator that is not read-only that will be used for all elements
|
|
|
|
|
- asCDataType retType;
|
|
|
|
|
- retType = var->dataType.GetSubType();
|
|
|
|
|
- retType.MakeReference(true);
|
|
|
|
|
- retType.MakeReadOnly(false);
|
|
|
|
|
- funcId = 0;
|
|
|
|
|
- for( asUINT n = 0; n < var->dataType.GetObjectType()->methods.GetLength(); n++ )
|
|
|
|
|
- {
|
|
|
|
|
- asCScriptFunction *desc = builder->GetFunctionDescription(var->dataType.GetObjectType()->methods[n]);
|
|
|
|
|
- if( !desc->isReadOnly &&
|
|
|
|
|
- desc->parameterTypes.GetLength() == 1 &&
|
|
|
|
|
- (desc->parameterTypes[0] == asCDataType::CreatePrimitive(ttUInt, false) ||
|
|
|
|
|
- desc->parameterTypes[0] == asCDataType::CreatePrimitive(ttInt, false)) &&
|
|
|
|
|
- desc->returnType == retType &&
|
|
|
|
|
- desc->name == "opIndex" )
|
|
|
|
|
- {
|
|
|
|
|
- funcId = var->dataType.GetObjectType()->methods[n];
|
|
|
|
|
- break;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ // Free the temporary buffer. The FREE instruction will make sure to destroy
|
|
|
|
|
+ // each element in the buffer so there is no need to do this manually
|
|
|
|
|
+ bc->InstrW_PTR(asBC_FREE, bufferVar, listPatternType);
|
|
|
|
|
+ ReleaseTemporaryVariable(bufferVar, bc);
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
- if( funcId == 0 )
|
|
|
|
|
|
|
+int asCCompiler::CompileInitListElement(asSListPatternNode *&patternNode, asCScriptNode *&valueNode, int bufferTypeId, short bufferVar, asUINT &bufferSize, asCByteCode &byteCode)
|
|
|
|
|
+{
|
|
|
|
|
+ if( patternNode->type == asLPT_START )
|
|
|
{
|
|
{
|
|
|
- Error(TXT_NO_APPROPRIATE_INDEX_OPERATOR, node);
|
|
|
|
|
- return;
|
|
|
|
|
|
|
+ if( valueNode->nodeType != snInitList )
|
|
|
|
|
+ {
|
|
|
|
|
+ Error(TXT_EXPECTED_LIST, valueNode);
|
|
|
|
|
+ return -1;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Compile all values until asLPT_END
|
|
|
|
|
+ patternNode = patternNode->next;
|
|
|
|
|
+ asCScriptNode *node = valueNode->firstChild;
|
|
|
|
|
+ while( patternNode->type != asLPT_END )
|
|
|
|
|
+ {
|
|
|
|
|
+ if( node == 0 )
|
|
|
|
|
+ {
|
|
|
|
|
+ Error(TXT_NOT_ENOUGH_VALUES_FOR_LIST, valueNode);
|
|
|
|
|
+ return -1;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ int r = CompileInitListElement(patternNode, node, bufferTypeId, bufferVar, bufferSize, byteCode);
|
|
|
|
|
+ if( r < 0 ) return r;
|
|
|
|
|
+ asASSERT( patternNode );
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if( node )
|
|
|
|
|
+ {
|
|
|
|
|
+ Error(TXT_TOO_MANY_VALUES_FOR_LIST, valueNode);
|
|
|
|
|
+ return -1;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Move to the next node
|
|
|
|
|
+ valueNode = valueNode->next;
|
|
|
|
|
+ patternNode = patternNode->next;
|
|
|
}
|
|
}
|
|
|
|
|
+ else if( patternNode->type == asLPT_REPEAT )
|
|
|
|
|
+ {
|
|
|
|
|
+ // The following values will be repeated N times
|
|
|
|
|
+ patternNode = patternNode->next;
|
|
|
|
|
+
|
|
|
|
|
+ // Keep track of the patternNode so it can be reset
|
|
|
|
|
+ asSListPatternNode *nextNode = patternNode;
|
|
|
|
|
+
|
|
|
|
|
+ // The first dword will hold the number of elements in the list
|
|
|
|
|
+ asDWORD currSize = bufferSize;
|
|
|
|
|
+ bufferSize += 4;
|
|
|
|
|
+ asUINT countElements = 0;
|
|
|
|
|
+
|
|
|
|
|
+ asSExprContext ctx(engine);
|
|
|
|
|
+ while( valueNode )
|
|
|
|
|
+ {
|
|
|
|
|
+ patternNode = nextNode;
|
|
|
|
|
+ int r = CompileInitListElement(patternNode, valueNode, bufferTypeId, bufferVar, bufferSize, ctx.bc);
|
|
|
|
|
+ if( r < 0 ) return r;
|
|
|
|
|
+
|
|
|
|
|
+ countElements++;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // The first dword in the buffer will hold the number of elements
|
|
|
|
|
+ byteCode.InstrSHORT_DW_DW(asBC_SetListSize, bufferVar, currSize, countElements);
|
|
|
|
|
|
|
|
- asUINT index = 0;
|
|
|
|
|
- el = node->firstChild;
|
|
|
|
|
- while( el )
|
|
|
|
|
|
|
+ // Add the values
|
|
|
|
|
+ byteCode.AddCode(&ctx.bc);
|
|
|
|
|
+ }
|
|
|
|
|
+ else if( patternNode->type == asLPT_TYPE )
|
|
|
{
|
|
{
|
|
|
- if( el->nodeType == snAssignment || el->nodeType == snInitList )
|
|
|
|
|
|
|
+ // TODO: list: Values on the list must be aligned to 32bit boundaries, except if the type
|
|
|
|
|
+ // is smaller than 32bit.
|
|
|
|
|
+
|
|
|
|
|
+ // Determine the size of the element
|
|
|
|
|
+ asUINT size = 0;
|
|
|
|
|
+
|
|
|
|
|
+ asCDataType dt = reinterpret_cast<asSListPatternDataTypeNode*>(patternNode)->dataType;
|
|
|
|
|
+
|
|
|
|
|
+ if( valueNode->nodeType == snAssignment || valueNode->nodeType == snInitList )
|
|
|
{
|
|
{
|
|
|
asSExprContext lctx(engine);
|
|
asSExprContext lctx(engine);
|
|
|
asSExprContext rctx(engine);
|
|
asSExprContext rctx(engine);
|
|
|
|
|
|
|
|
- if( el->nodeType == snAssignment )
|
|
|
|
|
|
|
+ if( valueNode->nodeType == snAssignment )
|
|
|
{
|
|
{
|
|
|
// Compile the assignment expression
|
|
// Compile the assignment expression
|
|
|
- CompileAssignment(el, &rctx);
|
|
|
|
|
|
|
+ CompileAssignment(valueNode, &rctx);
|
|
|
|
|
+
|
|
|
|
|
+ if( dt.GetTokenType() == ttQuestion )
|
|
|
|
|
+ {
|
|
|
|
|
+ // We now know the type
|
|
|
|
|
+ dt = rctx.type.dataType;
|
|
|
|
|
+ dt.MakeReadOnly(false);
|
|
|
|
|
+
|
|
|
|
|
+ // Place the type id in the buffer
|
|
|
|
|
+ byteCode.InstrSHORT_DW_DW(asBC_SetListType, bufferVar, bufferSize, engine->GetTypeIdFromDataType(dt));
|
|
|
|
|
+ bufferSize += 4;
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
- else if( el->nodeType == snInitList )
|
|
|
|
|
|
|
+ else if( valueNode->nodeType == snInitList )
|
|
|
{
|
|
{
|
|
|
- int offset = AllocateVariable(var->dataType.GetSubType(), true);
|
|
|
|
|
|
|
+ if( dt.GetTokenType() == ttQuestion )
|
|
|
|
|
+ {
|
|
|
|
|
+ // Can't use init lists with var type as it is not possible to determine what type should be allocated
|
|
|
|
|
+ asCString str;
|
|
|
|
|
+ str.Format(TXT_INIT_LIST_CANNOT_BE_USED_WITH_s, "?");
|
|
|
|
|
+ Error(str.AddressOf(), valueNode);
|
|
|
|
|
+ rctx.type.SetDummy();
|
|
|
|
|
+ dt = rctx.type.dataType;
|
|
|
|
|
+ }
|
|
|
|
|
+ else
|
|
|
|
|
+ {
|
|
|
|
|
+ // Allocate a temporary variable that will be initialized with the list
|
|
|
|
|
+ int offset = AllocateVariable(dt, true);
|
|
|
|
|
|
|
|
- rctx.type.Set(var->dataType.GetSubType());
|
|
|
|
|
- rctx.type.isVariable = true;
|
|
|
|
|
- rctx.type.isTemporary = true;
|
|
|
|
|
- rctx.type.stackOffset = (short)offset;
|
|
|
|
|
|
|
+ rctx.type.Set(dt);
|
|
|
|
|
+ rctx.type.isVariable = true;
|
|
|
|
|
+ rctx.type.isTemporary = true;
|
|
|
|
|
+ rctx.type.stackOffset = (short)offset;
|
|
|
|
|
|
|
|
- CompileInitList(&rctx.type, el, &rctx.bc, 0);
|
|
|
|
|
|
|
+ CompileInitList(&rctx.type, valueNode, &rctx.bc, 0);
|
|
|
|
|
|
|
|
- // Put the object on the stack
|
|
|
|
|
- rctx.bc.InstrSHORT(asBC_PSF, rctx.type.stackOffset);
|
|
|
|
|
|
|
+ // Put the object on the stack
|
|
|
|
|
+ rctx.bc.InstrSHORT(asBC_PSF, rctx.type.stackOffset);
|
|
|
|
|
|
|
|
- // It is a reference that we place on the stack
|
|
|
|
|
- rctx.type.dataType.MakeReference(true);
|
|
|
|
|
|
|
+ // It is a reference that we place on the stack
|
|
|
|
|
+ rctx.type.dataType.MakeReference(true);
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Compile the lvalue
|
|
// Compile the lvalue
|
|
|
- lctx.bc.InstrDWORD(asBC_PshC4, index);
|
|
|
|
|
- if( var->isVariable )
|
|
|
|
|
- lctx.bc.InstrSHORT(asBC_PSF, var->stackOffset);
|
|
|
|
|
- else
|
|
|
|
|
|
|
+ lctx.bc.InstrSHORT_DW(asBC_PshListElmnt, bufferVar, bufferSize);
|
|
|
|
|
+ lctx.type.Set(dt);
|
|
|
|
|
+ lctx.type.isLValue = true;
|
|
|
|
|
+ if( dt.IsPrimitive() )
|
|
|
{
|
|
{
|
|
|
- // TODO: runtime optimize: should copy a handle to a local variable to avoid
|
|
|
|
|
- // accessing the global variable or class member for each element
|
|
|
|
|
- if( isVarGlobOrMem == 1 )
|
|
|
|
|
- lctx.bc.InstrPTR(asBC_PGA, engine->globalProperties[var->stackOffset]->GetAddressOfValue());
|
|
|
|
|
- else
|
|
|
|
|
- {
|
|
|
|
|
- lctx.bc.InstrSHORT(asBC_PSF, 0);
|
|
|
|
|
- lctx.bc.Instr(asBC_RDSPtr);
|
|
|
|
|
- lctx.bc.InstrSHORT_DW(asBC_ADDSi, (short)var->stackOffset, engine->GetTypeIdFromDataType(asCDataType::CreateObject(outFunc->objectType, false)));
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ lctx.bc.Instr(asBC_PopRPtr);
|
|
|
|
|
+ lctx.type.dataType.MakeReference(true);
|
|
|
}
|
|
}
|
|
|
- lctx.bc.Instr(asBC_RDSPtr);
|
|
|
|
|
- lctx.bc.Call(asBC_CALLSYS, funcId, 1+AS_PTR_SIZE);
|
|
|
|
|
-
|
|
|
|
|
- if( !var->dataType.GetSubType().IsPrimitive() )
|
|
|
|
|
- lctx.bc.Instr(asBC_PshRPtr);
|
|
|
|
|
-
|
|
|
|
|
- lctx.type.Set(var->dataType.GetSubType());
|
|
|
|
|
-
|
|
|
|
|
- if( !lctx.type.dataType.IsObject() || lctx.type.dataType.IsObjectHandle() )
|
|
|
|
|
|
|
+ else if( dt.IsObjectHandle() ||
|
|
|
|
|
+ dt.GetObjectType()->flags & asOBJ_REF )
|
|
|
|
|
+ {
|
|
|
|
|
+ lctx.type.isExplicitHandle = true;
|
|
|
lctx.type.dataType.MakeReference(true);
|
|
lctx.type.dataType.MakeReference(true);
|
|
|
|
|
+ }
|
|
|
|
|
+ else
|
|
|
|
|
+ {
|
|
|
|
|
+ asASSERT( dt.GetObjectType()->flags & asOBJ_VALUE );
|
|
|
|
|
|
|
|
- // If the element type is handles, then we're expected to do handle assignments
|
|
|
|
|
- if( lctx.type.dataType.IsObjectHandle() )
|
|
|
|
|
- lctx.type.isExplicitHandle = true;
|
|
|
|
|
|
|
+ // Make sure the object has been constructed before the assignment
|
|
|
|
|
+ // TODO: runtime optimize: Use copy constructor instead of assignment to initialize the objects
|
|
|
|
|
+ asSTypeBehaviour *beh = dt.GetBehaviour();
|
|
|
|
|
+ int func = 0;
|
|
|
|
|
+ if( beh ) func = beh->construct;
|
|
|
|
|
+ if( func == 0 && (dt.GetObjectType()->flags & asOBJ_POD) == 0 )
|
|
|
|
|
+ {
|
|
|
|
|
+ asCString str;
|
|
|
|
|
+ // TODO: funcdef: asCDataType should have a GetTypeName()
|
|
|
|
|
+ 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, valueNode);
|
|
|
|
|
+ }
|
|
|
|
|
+ else if( func )
|
|
|
|
|
+ {
|
|
|
|
|
+ // Call the constructor as a normal function
|
|
|
|
|
+ byteCode.InstrSHORT_DW(asBC_PshListElmnt, bufferVar, bufferSize);
|
|
|
|
|
|
|
|
- lctx.type.isLValue = true;
|
|
|
|
|
|
|
+ asSExprContext ctx(engine);
|
|
|
|
|
+ PerformFunctionCall(func, &ctx, false, 0, dt.GetObjectType());
|
|
|
|
|
+ byteCode.AddCode(&ctx.bc);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
asSExprContext ctx(engine);
|
|
asSExprContext ctx(engine);
|
|
|
- DoAssignment(&ctx, &lctx, &rctx, el, el, ttAssignment, el);
|
|
|
|
|
|
|
+ DoAssignment(&ctx, &lctx, &rctx, valueNode, valueNode, ttAssignment, valueNode);
|
|
|
|
|
|
|
|
if( !lctx.type.dataType.IsPrimitive() )
|
|
if( !lctx.type.dataType.IsPrimitive() )
|
|
|
ctx.bc.Instr(asBC_PopPtr);
|
|
ctx.bc.Instr(asBC_PopPtr);
|
|
@@ -2606,12 +2711,41 @@ void asCCompiler::CompileInitList(asCTypeInfo *var, asCScriptNode *node, asCByte
|
|
|
|
|
|
|
|
ProcessDeferredParams(&ctx);
|
|
ProcessDeferredParams(&ctx);
|
|
|
|
|
|
|
|
- bc->AddCode(&ctx.bc);
|
|
|
|
|
|
|
+ byteCode.AddCode(&ctx.bc);
|
|
|
}
|
|
}
|
|
|
|
|
+ else
|
|
|
|
|
+ {
|
|
|
|
|
+ // There is no specific value so we need to fill it with a default value
|
|
|
|
|
+ // TODO: list: For value types with default constructor we need to call the constructor
|
|
|
|
|
+
|
|
|
|
|
+ if( dt.GetTokenType() == ttQuestion )
|
|
|
|
|
+ {
|
|
|
|
|
+ // Place the type id for a null handle in the buffer
|
|
|
|
|
+ byteCode.InstrSHORT_DW_DW(asBC_SetListType, bufferVar, bufferSize, 0);
|
|
|
|
|
+ bufferSize += 4;
|
|
|
|
|
|
|
|
- el = el->next;
|
|
|
|
|
- index++;
|
|
|
|
|
|
|
+ dt = asCDataType::CreateNullHandle();
|
|
|
|
|
+
|
|
|
|
|
+ // No need to initialize the handle as the buffer is already initialized with zeroes
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Determine size of the element
|
|
|
|
|
+ if( dt.IsPrimitive() || (!dt.IsNullHandle() && (dt.GetObjectType()->flags & asOBJ_VALUE)) )
|
|
|
|
|
+ size = dt.GetSizeInMemoryBytes();
|
|
|
|
|
+ else
|
|
|
|
|
+ size = AS_PTR_SIZE*4;
|
|
|
|
|
+ asASSERT( size <= 4 || (size & 0x3) == 0 );
|
|
|
|
|
+
|
|
|
|
|
+ // Move to the next element
|
|
|
|
|
+ bufferSize += size;
|
|
|
|
|
+ patternNode = patternNode->next;
|
|
|
|
|
+ valueNode = valueNode->next;
|
|
|
}
|
|
}
|
|
|
|
|
+ else
|
|
|
|
|
+ asASSERT( false );
|
|
|
|
|
+
|
|
|
|
|
+ return 0;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void asCCompiler::CompileStatement(asCScriptNode *statement, bool *hasReturn, asCByteCode *bc)
|
|
void asCCompiler::CompileStatement(asCScriptNode *statement, bool *hasReturn, asCByteCode *bc)
|
|
@@ -3465,10 +3599,8 @@ void asCCompiler::PrepareTemporaryObject(asCScriptNode *node, asSExprContext *ct
|
|
|
|
|
|
|
|
asCTypeInfo lvalue;
|
|
asCTypeInfo lvalue;
|
|
|
lvalue.Set(dt);
|
|
lvalue.Set(dt);
|
|
|
- lvalue.isTemporary = true;
|
|
|
|
|
- lvalue.stackOffset = (short)offset;
|
|
|
|
|
- lvalue.isVariable = true;
|
|
|
|
|
lvalue.isExplicitHandle = ctx->type.isExplicitHandle;
|
|
lvalue.isExplicitHandle = ctx->type.isExplicitHandle;
|
|
|
|
|
+ bool isExplicitHandle = ctx->type.isExplicitHandle;
|
|
|
|
|
|
|
|
if( !dt.IsObjectHandle() &&
|
|
if( !dt.IsObjectHandle() &&
|
|
|
dt.GetObjectType() && (dt.GetBehaviour()->copyconstruct || dt.GetBehaviour()->copyfactory) )
|
|
dt.GetObjectType() && (dt.GetBehaviour()->copyconstruct || dt.GetBehaviour()->copyfactory) )
|
|
@@ -3498,8 +3630,12 @@ void asCCompiler::PrepareTemporaryObject(asCScriptNode *node, asSExprContext *ct
|
|
|
Error(TXT_FAILED_TO_CREATE_TEMP_OBJ, node);
|
|
Error(TXT_FAILED_TO_CREATE_TEMP_OBJ, node);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ // Release any temp that may have been created as the result of opAssign
|
|
|
|
|
+ ReleaseTemporaryVariable(lvalue, &ctx->bc);
|
|
|
|
|
+
|
|
|
// Pop the original reference
|
|
// Pop the original reference
|
|
|
- ctx->bc.Instr(asBC_PopPtr);
|
|
|
|
|
|
|
+ if( !lvalue.dataType.IsPrimitive() )
|
|
|
|
|
+ ctx->bc.Instr(asBC_PopPtr);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// If the expression was holding off on releasing a
|
|
// If the expression was holding off on releasing a
|
|
@@ -3510,9 +3646,13 @@ void asCCompiler::PrepareTemporaryObject(asCScriptNode *node, asSExprContext *ct
|
|
|
|
|
|
|
|
// Push the reference to the temporary variable on the stack
|
|
// Push the reference to the temporary variable on the stack
|
|
|
ctx->bc.InstrSHORT(asBC_PSF, (short)offset);
|
|
ctx->bc.InstrSHORT(asBC_PSF, (short)offset);
|
|
|
- lvalue.dataType.MakeReference(IsVariableOnHeap(offset));
|
|
|
|
|
|
|
|
|
|
- ctx->type = lvalue;
|
|
|
|
|
|
|
+ ctx->type.Set(dt);
|
|
|
|
|
+ ctx->type.isTemporary = true;
|
|
|
|
|
+ ctx->type.stackOffset = (short)offset;
|
|
|
|
|
+ ctx->type.isVariable = true;
|
|
|
|
|
+ ctx->type.isExplicitHandle = isExplicitHandle;
|
|
|
|
|
+ ctx->type.dataType.MakeReference(IsVariableOnHeap(offset));
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void asCCompiler::CompileReturnStatement(asCScriptNode *rnode, asCByteCode *bc)
|
|
void asCCompiler::CompileReturnStatement(asCScriptNode *rnode, asCByteCode *bc)
|
|
@@ -4263,20 +4403,23 @@ int asCCompiler::PerformAssignment(asCTypeInfo *lvalue, asCTypeInfo *rvalue, asC
|
|
|
// TODO: Should find the opAssign method that implements the default copy behaviour.
|
|
// TODO: Should find the opAssign method that implements the default copy behaviour.
|
|
|
// The beh->copy member will be removed.
|
|
// The beh->copy member will be removed.
|
|
|
asSTypeBehaviour *beh = lvalue->dataType.GetBehaviour();
|
|
asSTypeBehaviour *beh = lvalue->dataType.GetBehaviour();
|
|
|
- if( beh->copy )
|
|
|
|
|
- {
|
|
|
|
|
- // Call the copy operator
|
|
|
|
|
- asCScriptFunction *descr = builder->GetFunctionDescription(beh->copy);
|
|
|
|
|
- if( descr->funcType == asFUNC_VIRTUAL )
|
|
|
|
|
- bc->Call(asBC_CALLINTF, beh->copy, 2*AS_PTR_SIZE);
|
|
|
|
|
- else if( descr->funcType == asFUNC_SCRIPT )
|
|
|
|
|
- bc->Call(asBC_CALL, beh->copy, 2*AS_PTR_SIZE);
|
|
|
|
|
- else
|
|
|
|
|
- {
|
|
|
|
|
- asASSERT( descr->funcType == asFUNC_SYSTEM );
|
|
|
|
|
- bc->Call(asBC_CALLSYS, beh->copy, 2*AS_PTR_SIZE);
|
|
|
|
|
- }
|
|
|
|
|
- asASSERT( descr->returnType.IsReference() );
|
|
|
|
|
|
|
+ if( beh->copy && beh->copy != engine->scriptTypeBehaviours.beh.copy )
|
|
|
|
|
+ {
|
|
|
|
|
+ asSExprContext res(engine);
|
|
|
|
|
+ PerformFunctionCall(beh->copy, &res, false, 0, lvalue->dataType.GetObjectType());
|
|
|
|
|
+
|
|
|
|
|
+ bc->AddCode(&res.bc);
|
|
|
|
|
+ *lvalue = res.type;
|
|
|
|
|
+ }
|
|
|
|
|
+ else if( beh->copy == engine->scriptTypeBehaviours.beh.copy )
|
|
|
|
|
+ {
|
|
|
|
|
+ // Call the default copy operator for script classes
|
|
|
|
|
+ // This is done differently because the default copy operator
|
|
|
|
|
+ // is registered as returning int&, but in reality it returns
|
|
|
|
|
+ // a reference to the object.
|
|
|
|
|
+ // TODO: Avoid this special case by implementing a copystub for
|
|
|
|
|
+ // script classes that uses the default copy operator
|
|
|
|
|
+ bc->Call(asBC_CALLSYS, beh->copy, 2*AS_PTR_SIZE);
|
|
|
bc->Instr(asBC_PshRPtr);
|
|
bc->Instr(asBC_PshRPtr);
|
|
|
}
|
|
}
|
|
|
else
|
|
else
|
|
@@ -5253,6 +5396,24 @@ asUINT asCCompiler::ImplicitConvObjectToObject(asSExprContext *ctx, const asCDat
|
|
|
{
|
|
{
|
|
|
if( generateCode )
|
|
if( generateCode )
|
|
|
{
|
|
{
|
|
|
|
|
+ // If the ASHANDLE receives a variable type parameter, then we need to
|
|
|
|
|
+ // make sure the expression is treated as a handle and not as a value
|
|
|
|
|
+ asCScriptFunction *func = engine->scriptFunctions[funcs[0]];
|
|
|
|
|
+ if( func->parameterTypes[0].GetTokenType() == ttQuestion )
|
|
|
|
|
+ {
|
|
|
|
|
+ if( !ctx->type.isExplicitHandle )
|
|
|
|
|
+ {
|
|
|
|
|
+ asCDataType toHandle = ctx->type.dataType;
|
|
|
|
|
+ toHandle.MakeHandle(true);
|
|
|
|
|
+ toHandle.MakeReference(true);
|
|
|
|
|
+ toHandle.MakeHandleToConst(ctx->type.dataType.IsReadOnly());
|
|
|
|
|
+ ImplicitConversion(ctx, toHandle, node, asIC_IMPLICIT_CONV, true, false);
|
|
|
|
|
+
|
|
|
|
|
+ asASSERT( ctx->type.dataType.IsObjectHandle() );
|
|
|
|
|
+ }
|
|
|
|
|
+ ctx->type.isExplicitHandle = true;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
// TODO: This should really reuse the code from CompileConstructCall
|
|
// TODO: This should really reuse the code from CompileConstructCall
|
|
|
|
|
|
|
|
// Allocate the new object
|
|
// Allocate the new object
|
|
@@ -5958,11 +6119,18 @@ void asCCompiler::ImplicitConversionConstant(asSExprContext *from, const asCData
|
|
|
else if( from->type.dataType.IsIntegerType() )
|
|
else if( from->type.dataType.IsIntegerType() )
|
|
|
{
|
|
{
|
|
|
// Verify that it is possible to convert to unsigned without loosing negative
|
|
// Verify that it is possible to convert to unsigned without loosing negative
|
|
|
- if( from->type.intValue < 0 )
|
|
|
|
|
|
|
+ if( (from->type.dataType.GetSizeInMemoryBytes() > 4 && asINT64(from->type.qwordValue) < 0) ||
|
|
|
|
|
+ (from->type.dataType.GetSizeInMemoryBytes() <= 4 && from->type.intValue < 0) )
|
|
|
{
|
|
{
|
|
|
if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_CHANGE_SIGN, node);
|
|
if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_CHANGE_SIGN, node);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ // Check if any data is lost
|
|
|
|
|
+ if( from->type.dataType.GetSizeInMemoryBytes() > 4 && (from->type.qwordValue >> 32) != 0 && (from->type.qwordValue >> 32) != 0xFFFFFFFF )
|
|
|
|
|
+ {
|
|
|
|
|
+ if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_VALUE_TOO_LARGE_FOR_TYPE, node);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
// Convert to 32bit
|
|
// Convert to 32bit
|
|
|
if( from->type.dataType.GetSizeInMemoryBytes() == 1 )
|
|
if( from->type.dataType.GetSizeInMemoryBytes() == 1 )
|
|
|
from->type.intValue = (signed char)from->type.byteValue;
|
|
from->type.intValue = (signed char)from->type.byteValue;
|
|
@@ -6258,7 +6426,7 @@ int asCCompiler::DoAssignment(asSExprContext *ctx, asSExprContext *lctx, asSExpr
|
|
|
{
|
|
{
|
|
|
// Don't allow any operators on expressions that take address of class method
|
|
// Don't allow any operators on expressions that take address of class method
|
|
|
// If methodName is set but the type is not an object, then it is a global function
|
|
// If methodName is set but the type is not an object, then it is a global function
|
|
|
- if( lctx->methodName != "" || (rctx->type.dataType.GetObjectType() && rctx->methodName != "") )
|
|
|
|
|
|
|
+ if( lctx->methodName != "" || rctx->IsClassMethod() )
|
|
|
{
|
|
{
|
|
|
Error(TXT_INVALID_OP_ON_METHOD, opNode);
|
|
Error(TXT_INVALID_OP_ON_METHOD, opNode);
|
|
|
return -1;
|
|
return -1;
|
|
@@ -6624,7 +6792,7 @@ int asCCompiler::CompileCondition(asCScriptNode *expr, asSExprContext *ctx)
|
|
|
if( lr >= 0 && rr >= 0 )
|
|
if( lr >= 0 && rr >= 0 )
|
|
|
{
|
|
{
|
|
|
// Don't allow any operators on expressions that take address of class method
|
|
// Don't allow any operators on expressions that take address of class method
|
|
|
- if( le.methodName != "" || re.methodName != "" )
|
|
|
|
|
|
|
+ if( le.IsClassMethod() || re.IsClassMethod() )
|
|
|
{
|
|
{
|
|
|
Error(TXT_INVALID_OP_ON_METHOD, expr);
|
|
Error(TXT_INVALID_OP_ON_METHOD, expr);
|
|
|
return -1;
|
|
return -1;
|
|
@@ -6692,8 +6860,16 @@ int asCCompiler::CompileCondition(asCScriptNode *expr, asSExprContext *ctx)
|
|
|
temp = le.type;
|
|
temp = le.type;
|
|
|
temp.dataType.MakeReference(false);
|
|
temp.dataType.MakeReference(false);
|
|
|
temp.dataType.MakeReadOnly(false);
|
|
temp.dataType.MakeReadOnly(false);
|
|
|
- // Make sure the variable isn't used in the initial expression
|
|
|
|
|
- int offset = AllocateVariableNotIn(temp.dataType, true, false, &e);
|
|
|
|
|
|
|
+
|
|
|
|
|
+ // Make sure the variable isn't used in any of the expressions,
|
|
|
|
|
+ // as it would be overwritten which may cause crashes or less visible bugs
|
|
|
|
|
+ int l = int(reservedVariables.GetLength());
|
|
|
|
|
+ e.bc.GetVarsUsed(reservedVariables);
|
|
|
|
|
+ le.bc.GetVarsUsed(reservedVariables);
|
|
|
|
|
+ re.bc.GetVarsUsed(reservedVariables);
|
|
|
|
|
+ int offset = AllocateVariable(temp.dataType, true, false);
|
|
|
|
|
+ reservedVariables.SetLength(l);
|
|
|
|
|
+
|
|
|
temp.SetVariable(temp.dataType, offset, true);
|
|
temp.SetVariable(temp.dataType, offset, true);
|
|
|
|
|
|
|
|
// TODO: copy: Use copy constructor if available. See PrepareTemporaryObject()
|
|
// TODO: copy: Use copy constructor if available. See PrepareTemporaryObject()
|
|
@@ -6725,8 +6901,10 @@ int asCCompiler::CompileCondition(asCScriptNode *expr, asSExprContext *ctx)
|
|
|
ctx->bc.InstrSHORT(asBC_PSF, (short)offset);
|
|
ctx->bc.InstrSHORT(asBC_PSF, (short)offset);
|
|
|
rtemp.dataType.MakeReference(IsVariableOnHeap(offset));
|
|
rtemp.dataType.MakeReference(IsVariableOnHeap(offset));
|
|
|
}
|
|
}
|
|
|
- PerformAssignment(&rtemp, &le.type, &ctx->bc, cexpr->next);
|
|
|
|
|
- if( !rtemp.dataType.IsPrimitive() )
|
|
|
|
|
|
|
+ asCTypeInfo result;
|
|
|
|
|
+ result = rtemp;
|
|
|
|
|
+ PerformAssignment(&result, &le.type, &ctx->bc, cexpr->next);
|
|
|
|
|
+ if( !result.dataType.IsPrimitive() )
|
|
|
ctx->bc.Instr(asBC_PopPtr); // Pop the original value (always a pointer)
|
|
ctx->bc.Instr(asBC_PopPtr); // Pop the original value (always a pointer)
|
|
|
|
|
|
|
|
// Release the old temporary variable
|
|
// Release the old temporary variable
|
|
@@ -6746,8 +6924,9 @@ int asCCompiler::CompileCondition(asCScriptNode *expr, asSExprContext *ctx)
|
|
|
ctx->bc.InstrSHORT(asBC_PSF, (short)offset);
|
|
ctx->bc.InstrSHORT(asBC_PSF, (short)offset);
|
|
|
rtemp.dataType.MakeReference(IsVariableOnHeap(offset));
|
|
rtemp.dataType.MakeReference(IsVariableOnHeap(offset));
|
|
|
}
|
|
}
|
|
|
- PerformAssignment(&rtemp, &re.type, &ctx->bc, cexpr->next);
|
|
|
|
|
- if( !rtemp.dataType.IsPrimitive() )
|
|
|
|
|
|
|
+ result = rtemp;
|
|
|
|
|
+ PerformAssignment(&result, &re.type, &ctx->bc, cexpr->next);
|
|
|
|
|
+ if( !result.dataType.IsPrimitive() )
|
|
|
ctx->bc.Instr(asBC_PopPtr); // Pop the original value (always a pointer)
|
|
ctx->bc.Instr(asBC_PopPtr); // Pop the original value (always a pointer)
|
|
|
|
|
|
|
|
// Release the old temporary variable
|
|
// Release the old temporary variable
|
|
@@ -7257,9 +7436,7 @@ int asCCompiler::CompileVariableAccess(const asCString &name, const asCString &s
|
|
|
|
|
|
|
|
// Defer the evaluation of which function until it is actually used
|
|
// Defer the evaluation of which function until it is actually used
|
|
|
// Store the namespace and name of the function for later
|
|
// Store the namespace and name of the function for later
|
|
|
- ctx->type.SetNullConstant();
|
|
|
|
|
- // Clear the explicit handle so that the script writer is allowed to explicitly set it
|
|
|
|
|
- ctx->type.isExplicitHandle = false;
|
|
|
|
|
|
|
+ ctx->type.SetUndefinedFuncHandle(engine);
|
|
|
ctx->methodName = ns ? ns->name + "::" + name : name;
|
|
ctx->methodName = ns ? ns->name + "::" + name : name;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
@@ -7410,7 +7587,9 @@ int asCCompiler::CompileExpressionValue(asCScriptNode *node, asSExprContext *ctx
|
|
|
asQWORD val = asStringScanUInt64(value.AddressOf(), 10, 0);
|
|
asQWORD val = asStringScanUInt64(value.AddressOf(), 10, 0);
|
|
|
|
|
|
|
|
// Do we need 64 bits?
|
|
// Do we need 64 bits?
|
|
|
- if( val>>32 )
|
|
|
|
|
|
|
+ // If the 31st bit is set we'll treat the value as a signed 64bit number to avoid
|
|
|
|
|
+ // incorrect warnings about changing signs if the value is assigned to a 64bit variable
|
|
|
|
|
+ if( val>>31 )
|
|
|
{
|
|
{
|
|
|
// Only if the value uses the last bit of a 64bit word do we consider the number unsigned
|
|
// Only if the value uses the last bit of a 64bit word do we consider the number unsigned
|
|
|
if( val>>63 )
|
|
if( val>>63 )
|
|
@@ -7593,6 +7772,11 @@ int asCCompiler::CompileExpressionValue(asCScriptNode *node, asSExprContext *ctx
|
|
|
// Implement the cast operator
|
|
// Implement the cast operator
|
|
|
CompileConversion(vnode, ctx);
|
|
CompileConversion(vnode, ctx);
|
|
|
}
|
|
}
|
|
|
|
|
+ else if( vnode->nodeType == snUndefined && vnode->tokenType == ttVoid )
|
|
|
|
|
+ {
|
|
|
|
|
+ // This is a void expression
|
|
|
|
|
+ ctx->type.SetVoidExpression();
|
|
|
|
|
+ }
|
|
|
else
|
|
else
|
|
|
asASSERT(false);
|
|
asASSERT(false);
|
|
|
|
|
|
|
@@ -7924,14 +8108,14 @@ void asCCompiler::CompileConversion(asCScriptNode *node, asSExprContext *ctx)
|
|
|
ProcessPropertyGetAccessor(&expr, node);
|
|
ProcessPropertyGetAccessor(&expr, node);
|
|
|
|
|
|
|
|
// Don't allow any operators on expressions that take address of class method
|
|
// Don't allow any operators on expressions that take address of class method
|
|
|
- if( expr.methodName != "" )
|
|
|
|
|
|
|
+ if( expr.IsClassMethod() )
|
|
|
{
|
|
{
|
|
|
Error(TXT_INVALID_OP_ON_METHOD, node);
|
|
Error(TXT_INVALID_OP_ON_METHOD, node);
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // We don't want a reference
|
|
|
|
|
- if( expr.type.dataType.IsReference() )
|
|
|
|
|
|
|
+ // We don't want a reference for conversion casts
|
|
|
|
|
+ if( convType == asIC_EXPLICIT_VAL_CAST && expr.type.dataType.IsReference() )
|
|
|
{
|
|
{
|
|
|
if( expr.type.dataType.IsObject() )
|
|
if( expr.type.dataType.IsObject() )
|
|
|
Dereference(&expr, true);
|
|
Dereference(&expr, true);
|
|
@@ -8100,11 +8284,11 @@ void asCCompiler::ProcessDeferredParams(asSExprContext *ctx)
|
|
|
{
|
|
{
|
|
|
// We must still evaluate the expression
|
|
// We must still evaluate the expression
|
|
|
MergeExprBytecode(ctx, expr);
|
|
MergeExprBytecode(ctx, expr);
|
|
|
- if( !expr->type.isConstant || expr->type.IsNullConstant() )
|
|
|
|
|
|
|
+ if( !expr->type.IsVoidExpression() && (!expr->type.isConstant || expr->type.IsNullConstant()) )
|
|
|
ctx->bc.Instr(asBC_PopPtr);
|
|
ctx->bc.Instr(asBC_PopPtr);
|
|
|
|
|
|
|
|
- // 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) )
|
|
|
|
|
|
|
+ // Give a warning, except if the argument is void, null or 0 which indicate the argument is really to be ignored
|
|
|
|
|
+ if( !expr->type.IsVoidExpression() && !expr->type.IsNullConstant() && !(expr->type.isConstant && expr->type.qwordValue == 0) )
|
|
|
Warning(TXT_ARG_NOT_LVALUE, outParam.argNode);
|
|
Warning(TXT_ARG_NOT_LVALUE, outParam.argNode);
|
|
|
|
|
|
|
|
ReleaseTemporaryVariable(outParam.argType, &ctx->bc);
|
|
ReleaseTemporaryVariable(outParam.argType, &ctx->bc);
|
|
@@ -8560,8 +8744,8 @@ int asCCompiler::CompileFunctionCall(asCScriptNode *node, asSExprContext *ctx, a
|
|
|
|
|
|
|
|
if( CompileArgumentList(node->lastChild, args) >= 0 )
|
|
if( CompileArgumentList(node->lastChild, args) >= 0 )
|
|
|
{
|
|
{
|
|
|
- // Special case: Allow calling func(void) with a void expression.
|
|
|
|
|
- if( args.GetLength() == 1 && args[0]->type.dataType == asCDataType::CreatePrimitive(ttVoid, false) )
|
|
|
|
|
|
|
+ // Special case: Allow calling func(void) with an expression that evaluates to no datatype, but isn't exactly 'void'
|
|
|
|
|
+ if( args.GetLength() == 1 && args[0]->type.dataType == asCDataType::CreatePrimitive(ttVoid, false) && !args[0]->type.IsVoidExpression() )
|
|
|
{
|
|
{
|
|
|
// Evaluate the expression before the function call
|
|
// Evaluate the expression before the function call
|
|
|
MergeExprBytecode(ctx, args[0]);
|
|
MergeExprBytecode(ctx, args[0]);
|
|
@@ -8694,12 +8878,19 @@ int asCCompiler::CompileExpressionPreOp(asCScriptNode *node, asSExprContext *ctx
|
|
|
int op = node->tokenType;
|
|
int op = node->tokenType;
|
|
|
|
|
|
|
|
// Don't allow any prefix operators except handle on expressions that take address of class method
|
|
// Don't allow any prefix operators except handle on expressions that take address of class method
|
|
|
- if( ctx->methodName != "" && op != ttHandle )
|
|
|
|
|
|
|
+ if( ctx->IsClassMethod() && op != ttHandle )
|
|
|
{
|
|
{
|
|
|
Error(TXT_INVALID_OP_ON_METHOD, node);
|
|
Error(TXT_INVALID_OP_ON_METHOD, node);
|
|
|
return -1;
|
|
return -1;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ // Don't allow any operators on void expressions
|
|
|
|
|
+ if( ctx->type.IsVoidExpression() )
|
|
|
|
|
+ {
|
|
|
|
|
+ Error(TXT_VOID_CANT_BE_OPERAND, node);
|
|
|
|
|
+ return -1;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
IsVariableInitialized(&ctx->type, node);
|
|
IsVariableInitialized(&ctx->type, node);
|
|
|
|
|
|
|
|
if( op == ttHandle )
|
|
if( op == ttHandle )
|
|
@@ -8715,41 +8906,51 @@ int asCCompiler::CompileExpressionPreOp(asCScriptNode *node, asSExprContext *ctx
|
|
|
}
|
|
}
|
|
|
else
|
|
else
|
|
|
{
|
|
{
|
|
|
- // 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_NOCOUNT)) ||
|
|
|
|
|
- (ctx->type.dataType.GetObjectType()->flags & asOBJ_ASHANDLE)) )
|
|
|
|
|
|
|
+ // Don't allow taking handle of a handle, i.e. @@
|
|
|
|
|
+ if( ctx->type.isExplicitHandle )
|
|
|
{
|
|
{
|
|
|
Error(TXT_OBJECT_HANDLE_NOT_SUPPORTED, node);
|
|
Error(TXT_OBJECT_HANDLE_NOT_SUPPORTED, node);
|
|
|
return -1;
|
|
return -1;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // Objects that are not local variables are not references
|
|
|
|
|
- // 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)) )
|
|
|
|
|
|
|
+ // @null is allowed even though it is implicit
|
|
|
|
|
+ if( !ctx->type.IsNullConstant() )
|
|
|
{
|
|
{
|
|
|
- Error(TXT_NOT_VALID_REFERENCE, node);
|
|
|
|
|
- return -1;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ // Verify that the type allow its handle to be taken
|
|
|
|
|
+ if( !ctx->type.dataType.IsObject() ||
|
|
|
|
|
+ !(((ctx->type.dataType.GetObjectType()->beh.addref && ctx->type.dataType.GetObjectType()->beh.release) || (ctx->type.dataType.GetObjectType()->flags & asOBJ_NOCOUNT)) ||
|
|
|
|
|
+ (ctx->type.dataType.GetObjectType()->flags & asOBJ_ASHANDLE)) )
|
|
|
|
|
+ {
|
|
|
|
|
+ Error(TXT_OBJECT_HANDLE_NOT_SUPPORTED, node);
|
|
|
|
|
+ return -1;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- // Convert the expression to a handle
|
|
|
|
|
- if( !ctx->type.dataType.IsObjectHandle() && !(ctx->type.dataType.GetObjectType()->flags & asOBJ_ASHANDLE) )
|
|
|
|
|
- {
|
|
|
|
|
- asCDataType to = ctx->type.dataType;
|
|
|
|
|
- to.MakeHandle(true);
|
|
|
|
|
- to.MakeReference(true);
|
|
|
|
|
- to.MakeHandleToConst(ctx->type.dataType.IsReadOnly());
|
|
|
|
|
- ImplicitConversion(ctx, to, node, asIC_IMPLICIT_CONV, true, false);
|
|
|
|
|
|
|
+ // Objects that are not local variables are not references
|
|
|
|
|
+ // 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;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- asASSERT( ctx->type.dataType.IsObjectHandle() );
|
|
|
|
|
- }
|
|
|
|
|
- else if( ctx->type.dataType.GetObjectType()->flags & asOBJ_ASHANDLE )
|
|
|
|
|
- {
|
|
|
|
|
- // For the ASHANDLE type we'll simply set the expression as a handle
|
|
|
|
|
- ctx->type.dataType.MakeHandle(true);
|
|
|
|
|
|
|
+ // Convert the expression to a handle
|
|
|
|
|
+ if( !ctx->type.dataType.IsObjectHandle() && !(ctx->type.dataType.GetObjectType()->flags & asOBJ_ASHANDLE) )
|
|
|
|
|
+ {
|
|
|
|
|
+ asCDataType to = ctx->type.dataType;
|
|
|
|
|
+ to.MakeHandle(true);
|
|
|
|
|
+ to.MakeReference(true);
|
|
|
|
|
+ to.MakeHandleToConst(ctx->type.dataType.IsReadOnly());
|
|
|
|
|
+ ImplicitConversion(ctx, to, node, asIC_IMPLICIT_CONV, true, false);
|
|
|
|
|
+
|
|
|
|
|
+ asASSERT( ctx->type.dataType.IsObjectHandle() );
|
|
|
|
|
+ }
|
|
|
|
|
+ else if( ctx->type.dataType.GetObjectType()->flags & asOBJ_ASHANDLE )
|
|
|
|
|
+ {
|
|
|
|
|
+ // For the ASHANDLE type we'll simply set the expression as a handle
|
|
|
|
|
+ ctx->type.dataType.MakeHandle(true);
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -9539,12 +9740,19 @@ void asCCompiler::ProcessPropertyGetAccessor(asSExprContext *ctx, asCScriptNode
|
|
|
int asCCompiler::CompileExpressionPostOp(asCScriptNode *node, asSExprContext *ctx)
|
|
int asCCompiler::CompileExpressionPostOp(asCScriptNode *node, asSExprContext *ctx)
|
|
|
{
|
|
{
|
|
|
// Don't allow any postfix operators on expressions that take address of class method
|
|
// Don't allow any postfix operators on expressions that take address of class method
|
|
|
- if( ctx->methodName != "" )
|
|
|
|
|
|
|
+ if( ctx->IsClassMethod() )
|
|
|
{
|
|
{
|
|
|
Error(TXT_INVALID_OP_ON_METHOD, node);
|
|
Error(TXT_INVALID_OP_ON_METHOD, node);
|
|
|
return -1;
|
|
return -1;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ // Don't allow any operators on void expressions
|
|
|
|
|
+ if( ctx->type.IsVoidExpression() )
|
|
|
|
|
+ {
|
|
|
|
|
+ Error(TXT_VOID_CANT_BE_OPERAND, node);
|
|
|
|
|
+ return -1;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
// Check if the variable is initialized (if it indeed is a variable)
|
|
// Check if the variable is initialized (if it indeed is a variable)
|
|
|
IsVariableInitialized(&ctx->type, node);
|
|
IsVariableInitialized(&ctx->type, node);
|
|
|
|
|
|
|
@@ -10096,6 +10304,14 @@ asUINT asCCompiler::MatchArgument(asCArray<int> &funcs, asCArray<asSOverloadCand
|
|
|
if( (int)desc->parameterTypes.GetLength() <= paramNum )
|
|
if( (int)desc->parameterTypes.GetLength() <= paramNum )
|
|
|
continue;
|
|
continue;
|
|
|
|
|
|
|
|
|
|
+ // void expressions can match any out parameter, but nothing else
|
|
|
|
|
+ if( argExpr->type.IsVoidExpression() )
|
|
|
|
|
+ {
|
|
|
|
|
+ if( desc->inOutFlags[paramNum] == asTM_OUTREF )
|
|
|
|
|
+ matches.PushLast(asSOverloadCandidate(funcs[n], 0));
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
// Can we make the match by implicit conversion?
|
|
// Can we make the match by implicit conversion?
|
|
|
asSExprContext ti(engine);
|
|
asSExprContext ti(engine);
|
|
|
ti.type = argExpr->type;
|
|
ti.type = argExpr->type;
|
|
@@ -10566,16 +10782,24 @@ void asCCompiler::MakeFunctionCall(asSExprContext *ctx, int funcId, asCObjectTyp
|
|
|
int asCCompiler::CompileOperator(asCScriptNode *node, asSExprContext *lctx, asSExprContext *rctx, asSExprContext *ctx)
|
|
int asCCompiler::CompileOperator(asCScriptNode *node, asSExprContext *lctx, asSExprContext *rctx, asSExprContext *ctx)
|
|
|
{
|
|
{
|
|
|
// Don't allow any operators on expressions that take address of class method, but allow it on global functions
|
|
// Don't allow any operators on expressions that take address of class method, but allow it on global functions
|
|
|
- if( (lctx->methodName != "" && lctx->type.dataType.GetObjectType()) || (rctx->methodName != "" && rctx->type.dataType.GetObjectType()) )
|
|
|
|
|
|
|
+ if( (lctx->IsClassMethod()) || (rctx->IsClassMethod()) )
|
|
|
{
|
|
{
|
|
|
Error(TXT_INVALID_OP_ON_METHOD, node);
|
|
Error(TXT_INVALID_OP_ON_METHOD, node);
|
|
|
return -1;
|
|
return -1;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ // Don't allow any operators on void expressions
|
|
|
|
|
+ if( lctx->type.IsVoidExpression() || rctx->type.IsVoidExpression() )
|
|
|
|
|
+ {
|
|
|
|
|
+ Error(TXT_VOID_CANT_BE_OPERAND, node);
|
|
|
|
|
+ return -1;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
IsVariableInitialized(&lctx->type, node);
|
|
IsVariableInitialized(&lctx->type, node);
|
|
|
IsVariableInitialized(&rctx->type, node);
|
|
IsVariableInitialized(&rctx->type, node);
|
|
|
|
|
|
|
|
if( lctx->type.isExplicitHandle || rctx->type.isExplicitHandle ||
|
|
if( lctx->type.isExplicitHandle || rctx->type.isExplicitHandle ||
|
|
|
|
|
+ lctx->type.IsNullConstant() || rctx->type.IsNullConstant() ||
|
|
|
node->tokenType == ttIs || node->tokenType == ttNotIs )
|
|
node->tokenType == ttIs || node->tokenType == ttNotIs )
|
|
|
{
|
|
{
|
|
|
CompileOperatorOnHandles(node, lctx, rctx, ctx);
|
|
CompileOperatorOnHandles(node, lctx, rctx, ctx);
|
|
@@ -11911,10 +12135,10 @@ void asCCompiler::CompileOperatorOnHandles(asCScriptNode *node, asSExprContext *
|
|
|
ReleaseTemporaryVariable(offset, 0);
|
|
ReleaseTemporaryVariable(offset, 0);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // Warn if not both operands are explicit handles
|
|
|
|
|
|
|
+ // Warn if not both operands are explicit handles or null handles
|
|
|
if( (node->tokenType == ttEqual || node->tokenType == ttNotEqual) &&
|
|
if( (node->tokenType == ttEqual || node->tokenType == ttNotEqual) &&
|
|
|
- ((!lctx->type.isExplicitHandle && !(lctx->type.dataType.GetObjectType() && (lctx->type.dataType.GetObjectType()->flags & asOBJ_IMPLICIT_HANDLE))) ||
|
|
|
|
|
- (!rctx->type.isExplicitHandle && !(rctx->type.dataType.GetObjectType() && (rctx->type.dataType.GetObjectType()->flags & asOBJ_IMPLICIT_HANDLE)))) )
|
|
|
|
|
|
|
+ ((!(lctx->type.isExplicitHandle || lctx->type.IsNullConstant()) && !(lctx->type.dataType.GetObjectType() && (lctx->type.dataType.GetObjectType()->flags & asOBJ_IMPLICIT_HANDLE))) ||
|
|
|
|
|
+ (!(rctx->type.isExplicitHandle || rctx->type.IsNullConstant()) && !(rctx->type.dataType.GetObjectType() && (rctx->type.dataType.GetObjectType()->flags & asOBJ_IMPLICIT_HANDLE)))) )
|
|
|
{
|
|
{
|
|
|
Warning(TXT_HANDLE_COMPARISON, node);
|
|
Warning(TXT_HANDLE_COMPARISON, node);
|
|
|
}
|
|
}
|