as_compiler.cpp

Go to the documentation of this file.
00001 /*
00002    AngelCode Scripting Library
00003    Copyright (c) 2003-2009 Andreas Jonsson
00004 
00005    This software is provided 'as-is', without any express or implied
00006    warranty. In no event will the authors be held liable for any
00007    damages arising from the use of this software.
00008 
00009    Permission is granted to anyone to use this software for any
00010    purpose, including commercial applications, and to alter it and
00011    redistribute it freely, subject to the following restrictions:
00012 
00013    1. The origin of this software must not be misrepresented; you
00014       must not claim that you wrote the original software. If you use
00015       this software in a product, an acknowledgment in the product
00016       documentation would be appreciated but is not required.
00017 
00018    2. Altered source versions must be plainly marked as such, and
00019       must not be misrepresented as being the original software.
00020 
00021    3. This notice may not be removed or altered from any source
00022       distribution.
00023 
00024    The original version of this library can be located at:
00025    http://www.angelcode.com/angelscript/
00026 
00027    Andreas Jonsson
00028    andreas@angelcode.com
00029 */
00030 
00031 
00032 //
00033 // as_compiler.cpp
00034 //
00035 // The class that does the actual compilation of the functions
00036 //
00037 
00038 #include <math.h> // fmodf()
00039 
00040 #include "as_config.h"
00041 #include "as_compiler.h"
00042 #include "as_tokendef.h"
00043 #include "as_tokenizer.h"
00044 #include "as_string_util.h"
00045 #include "as_texts.h"
00046 #include "as_parser.h"
00047 
00048 BEGIN_AS_NAMESPACE
00049 
00050 #ifdef AS_DEPRECATED
00051 // deprecated since 2009-07-20, 2.17.0
00052 // map token to behaviour
00053 const int behave_dual_token[] =
00054 {
00055     ttPlus,               asBEHAVE_ADD,
00056     ttMinus,              asBEHAVE_SUBTRACT,
00057     ttStar,               asBEHAVE_MULTIPLY,
00058     ttSlash,              asBEHAVE_DIVIDE,
00059     ttPercent,            asBEHAVE_MODULO,
00060     ttEqual,              asBEHAVE_EQUAL,
00061     ttNotEqual,           asBEHAVE_NOTEQUAL,
00062     ttLessThan,           asBEHAVE_LESSTHAN,
00063     ttGreaterThan,        asBEHAVE_GREATERTHAN,
00064     ttLessThanOrEqual,    asBEHAVE_LEQUAL,
00065     ttGreaterThanOrEqual, asBEHAVE_GEQUAL,
00066     ttBitOr,              asBEHAVE_BIT_OR,
00067     ttAmp,                asBEHAVE_BIT_AND,
00068     ttBitXor,             asBEHAVE_BIT_XOR,
00069     ttBitShiftLeft,       asBEHAVE_BIT_SLL,
00070     ttBitShiftRight,      asBEHAVE_BIT_SRL,
00071     ttBitShiftRightArith, asBEHAVE_BIT_SRA,
00072     ttAssignment,         asBEHAVE_ASSIGNMENT,
00073     ttAddAssign,          asBEHAVE_ADD_ASSIGN,
00074     ttSubAssign,          asBEHAVE_SUB_ASSIGN,
00075     ttMulAssign,          asBEHAVE_MUL_ASSIGN,
00076     ttDivAssign,          asBEHAVE_DIV_ASSIGN,
00077     ttModAssign,          asBEHAVE_MOD_ASSIGN,
00078     ttOrAssign,           asBEHAVE_OR_ASSIGN,
00079     ttAndAssign,          asBEHAVE_AND_ASSIGN,
00080     ttXorAssign,          asBEHAVE_XOR_ASSIGN,
00081     ttShiftLeftAssign,    asBEHAVE_SLL_ASSIGN,
00082     ttShiftRightLAssign,  asBEHAVE_SRL_ASSIGN,
00083     ttShiftRightAAssign,  asBEHAVE_SRA_ASSIGN
00084 };
00085 
00086 const int num_dual_tokens = sizeof(behave_dual_token)/sizeof(int)/2;
00087 #endif
00088 
00089 asCCompiler::asCCompiler(asCScriptEngine *engine) : byteCode(engine)
00090 {
00091     builder = 0;
00092     script = 0;
00093 
00094     variables = 0;
00095     isProcessingDeferredParams = false;
00096     noCodeOutput = 0;
00097 }
00098 
00099 asCCompiler::~asCCompiler()
00100 {
00101     while( variables )
00102     {
00103         asCVariableScope *var = variables;
00104         variables = variables->parent;
00105 
00106         asDELETE(var,asCVariableScope);
00107     }
00108 }
00109 
00110 void asCCompiler::Reset(asCBuilder *builder, asCScriptCode *script, asCScriptFunction *outFunc)
00111 {
00112     this->builder = builder;
00113     this->engine = builder->engine;
00114     this->script = script;
00115     this->outFunc = outFunc;
00116 
00117     hasCompileErrors = false;
00118 
00119     m_isConstructor = false;
00120     m_isConstructorCalled = false;
00121 
00122     nextLabel = 0;
00123     breakLabels.SetLength(0);
00124     continueLabels.SetLength(0);
00125 
00126     byteCode.ClearAll();
00127     objVariableTypes.SetLength(0);
00128     objVariablePos.SetLength(0);
00129 
00130     globalExpression = false;
00131 }
00132 
00133 int asCCompiler::CompileDefaultConstructor(asCBuilder *builder, asCScriptCode *script, asCScriptFunction *outFunc)
00134 {
00135     Reset(builder, script, outFunc);
00136 
00137     // If the class is derived from another, then the base class' default constructor must be called
00138     if( outFunc->objectType->derivedFrom )
00139     {
00140         // Call the base class' default constructor
00141         byteCode.InstrSHORT(asBC_PSF, 0);
00142         byteCode.Instr(asBC_RDSPTR);
00143         byteCode.Call(asBC_CALL, outFunc->objectType->derivedFrom->beh.construct, AS_PTR_SIZE);
00144     }
00145 
00146     // Pop the object pointer from the stack
00147     byteCode.Ret(AS_PTR_SIZE);
00148 
00149     byteCode.Finalize();
00150 
00151     // Copy byte code to the function
00152     outFunc->byteCode.SetLength(byteCode.GetSize());
00153     byteCode.Output(outFunc->byteCode.AddressOf());
00154     outFunc->AddReferences();
00155     outFunc->stackNeeded = byteCode.largestStackUsed;
00156     outFunc->lineNumbers = byteCode.lineNumbers;
00157     outFunc->objVariablePos = objVariablePos;
00158     outFunc->objVariableTypes = objVariableTypes;
00159 
00160 #ifdef AS_DEBUG
00161     // DEBUG: output byte code
00162     byteCode.DebugOutput(("__" + outFunc->objectType->name + "_" + outFunc->name + "__dc.txt").AddressOf(), builder->module, engine);
00163 #endif
00164 
00165     return 0;
00166 }
00167 
00168 int asCCompiler::CompileFactory(asCBuilder *builder, asCScriptCode *script, asCScriptFunction *outFunc)
00169 {
00170     Reset(builder, script, outFunc);
00171 
00172     unsigned int n;
00173 
00174     // Find the corresponding constructor
00175     asCDataType dt = asCDataType::CreateObject(outFunc->returnType.GetObjectType(), false);
00176     int constructor = 0;
00177     for( n = 0; n < dt.GetBehaviour()->factories.GetLength(); n++ )
00178     {
00179         if( dt.GetBehaviour()->factories[n] == outFunc->id )
00180         {
00181             constructor = dt.GetBehaviour()->constructors[n];
00182             break;
00183         }
00184     }
00185 
00186     // Allocate the class and instanciate it with the constructor
00187     int varOffset = AllocateVariable(dt, true);
00188 
00189     byteCode.Push(AS_PTR_SIZE);
00190     byteCode.InstrSHORT(asBC_PSF, (short)varOffset);
00191 
00192     // Copy all arguments to the top of the stack
00193     int argDwords = (int)outFunc->GetSpaceNeededForArguments();
00194     for( int a = argDwords-1; a >= 0; a-- )
00195         byteCode.InstrSHORT(asBC_PshV4, short(-a));
00196 
00197     byteCode.Alloc(asBC_ALLOC, dt.GetObjectType(), constructor, argDwords + AS_PTR_SIZE);
00198 
00199     // Return a handle to the newly created object
00200     byteCode.InstrSHORT(asBC_LOADOBJ, (short)varOffset);
00201 
00202     byteCode.Pop(AS_PTR_SIZE);
00203     byteCode.Ret(argDwords);
00204 
00205     byteCode.Finalize();
00206 
00207     // Store the instantiated object as variable so it will be cleaned up on exception
00208     objVariableTypes.PushLast(variableAllocations[0].GetObjectType());
00209     objVariablePos.PushLast(GetVariableOffset(0));
00210 
00211     // Copy byte code to the function
00212     outFunc->byteCode.SetLength(byteCode.GetSize());
00213     byteCode.Output(outFunc->byteCode.AddressOf());
00214     outFunc->AddReferences();
00215     outFunc->stackNeeded = byteCode.largestStackUsed;
00216     outFunc->lineNumbers = byteCode.lineNumbers;
00217     outFunc->objVariablePos = objVariablePos;
00218     outFunc->objVariableTypes = objVariableTypes;
00219 
00220     // Tell the virtual machine not to clean up parameters on exception
00221     outFunc->dontCleanUpOnException = true;
00222 
00223 /*
00224 #ifdef AS_DEBUG
00225     // DEBUG: output byte code
00226     asCString args;
00227     args.Format("%d", outFunc->parameterTypes.GetLength());
00228     byteCode.DebugOutput(("__" + outFunc->name + "__factory" + args + ".txt").AddressOf(), builder->module, engine);
00229 #endif
00230 */
00231     return 0;
00232 }
00233 
00234 int asCCompiler::CompileTemplateFactoryStub(asCBuilder *builder, int trueFactoryId, asCObjectType *objType, asCScriptFunction *outFunc)
00235 {
00236     Reset(builder, 0, outFunc);
00237 
00238     asCScriptFunction *descr = builder->GetFunctionDescription(trueFactoryId);
00239 
00240     byteCode.InstrPTR(asBC_OBJTYPE, objType);
00241     byteCode.Call(asBC_CALLSYS, trueFactoryId, descr->GetSpaceNeededForArguments());
00242     byteCode.Ret(outFunc->GetSpaceNeededForArguments());
00243 
00244     byteCode.Finalize();
00245 
00246     // Copy byte code to the function
00247     outFunc->byteCode.SetLength(byteCode.GetSize());
00248     byteCode.Output(outFunc->byteCode.AddressOf());
00249     outFunc->AddReferences();
00250     outFunc->stackNeeded = byteCode.largestStackUsed;
00251     outFunc->lineNumbers = byteCode.lineNumbers;
00252     outFunc->objVariablePos = objVariablePos;
00253     outFunc->objVariableTypes = objVariableTypes;
00254 
00255     // Tell the virtual machine not to clean up the object on exception
00256     outFunc->dontCleanUpOnException = true;
00257 
00258     return 0;
00259 }
00260 
00261 int asCCompiler::CompileFunction(asCBuilder *builder, asCScriptCode *script, asCScriptNode *func, asCScriptFunction *outFunc)
00262 {
00263     Reset(builder, script, outFunc);
00264 
00265     int stackPos = 0;
00266     if( outFunc->objectType )
00267         stackPos = -AS_PTR_SIZE; // The first parameter is the pointer to the object
00268 
00269     // Reserve a label for the cleanup code
00270     nextLabel++;
00271 
00272     // Add the first variable scope, which the parameters and
00273     // variables declared in the outermost statement block is
00274     // part of.
00275     AddVariableScope();
00276 
00277     //----------------------------------------------
00278     // Examine return type
00279     bool isDestructor = false;
00280     asCDataType returnType;
00281     if( func->firstChild->nodeType == snDataType )
00282     {
00283         returnType = builder->CreateDataTypeFromNode(func->firstChild, script);
00284         returnType = builder->ModifyDataTypeFromNode(returnType, func->firstChild->next, script, 0, 0);
00285 
00286         // Make sure the return type is instanciable or is void
00287         if( !returnType.CanBeInstanciated() &&
00288             returnType != asCDataType::CreatePrimitive(ttVoid, false) )
00289         {
00290             asCString str;
00291             str.Format(TXT_DATA_TYPE_CANT_BE_s, returnType.Format().AddressOf());
00292             Error(str.AddressOf(), func->firstChild);
00293         }
00294 
00295         // TODO: Add support for returning references
00296         // The script language doesn't support returning references yet
00297         if( returnType.IsReference() )
00298         {
00299             Error(TXT_SCRIPT_FUNCTIONS_DOESNT_SUPPORT_RETURN_REF, func->firstChild);
00300         }
00301     }
00302     else
00303     {
00304         returnType = asCDataType::CreatePrimitive(ttVoid, false);
00305         if( func->firstChild->tokenType == ttBitNot )
00306             isDestructor = true;
00307         else
00308             m_isConstructor = true;
00309     }
00310 
00311     //----------------------------------------------
00312     // Declare parameters
00313     // Find first parameter
00314     asCScriptNode *node = func->firstChild;
00315     while( node && node->nodeType != snParameterList )
00316         node = node->next;
00317 
00318     // Register parameters from last to first, otherwise they will be destroyed in the wrong order
00319     asCVariableScope vs(0);
00320 
00321     if( node ) node = node->firstChild;
00322     while( node )
00323     {
00324         // Get the parameter type
00325         asCDataType type = builder->CreateDataTypeFromNode(node, script);
00326 
00327         asETypeModifiers inoutFlag = asTM_NONE;
00328         type = builder->ModifyDataTypeFromNode(type, node->next, script, &inoutFlag, 0);
00329 
00330         // Is the data type allowed?
00331         if( (type.IsReference() && inoutFlag != asTM_INOUTREF && !type.CanBeInstanciated()) ||
00332             (!type.IsReference() && !type.CanBeInstanciated()) )
00333         {
00334             asCString str;
00335             str.Format(TXT_PARAMETER_CANT_BE_s, type.Format().AddressOf());
00336             Error(str.AddressOf(), node);
00337         }
00338 
00339         // If the parameter has a name then declare it as variable
00340         node = node->next->next;
00341         if( node && node->nodeType == snIdentifier )
00342         {
00343             asCString name(&script->code[node->tokenPos], node->tokenLength);
00344 
00345             if( vs.DeclareVariable(name.AddressOf(), type, stackPos) < 0 )
00346                 Error(TXT_PARAMETER_ALREADY_DECLARED, node);
00347 
00348             outFunc->AddVariable(name, type, stackPos);
00349 
00350             node = node->next;
00351         }
00352         else
00353             vs.DeclareVariable("", type, stackPos);
00354 
00355         // Move to next parameter
00356         stackPos -= type.GetSizeOnStackDWords();
00357     }
00358 
00359     int n;
00360     for( n = (int)vs.variables.GetLength() - 1; n >= 0; n-- )
00361     {
00362         variables->DeclareVariable(vs.variables[n]->name.AddressOf(), vs.variables[n]->type, vs.variables[n]->stackOffset);
00363     }
00364 
00365     // Is the return type allowed?
00366     if( (returnType.GetSizeOnStackDWords() == 0 && returnType != asCDataType::CreatePrimitive(ttVoid, false)) ||
00367         (returnType.IsReference() && !returnType.CanBeInstanciated()) )
00368     {
00369         asCString str;
00370         str.Format(TXT_RETURN_CANT_BE_s, returnType.Format().AddressOf());
00371         Error(str.AddressOf(), node);
00372     }
00373 
00374     variables->DeclareVariable("return", returnType, stackPos);
00375 
00376     //--------------------------------------------
00377     // Compile the statement block
00378 
00379     // We need to parse the statement block now
00380 
00381     // TODO: memory: We can parse the statement block one statement at a time, thus save even more memory
00382     asCParser parser(builder);
00383     int r = parser.ParseStatementBlock(script, func->lastChild);
00384     if( r < 0 ) return -1;
00385     asCScriptNode *block = parser.GetScriptNode();
00386 
00387     bool hasReturn;
00388     asCByteCode bc(engine);
00389     LineInstr(&bc, func->lastChild->tokenPos);
00390     CompileStatementBlock(block, false, &hasReturn, &bc);
00391     LineInstr(&bc, func->lastChild->tokenPos + func->lastChild->tokenLength);
00392 
00393     // Make sure there is a return in all paths (if not return type is void)
00394     if( returnType != asCDataType::CreatePrimitive(ttVoid, false) )
00395     {
00396         if( hasReturn == false )
00397             Error(TXT_NOT_ALL_PATHS_RETURN, func->lastChild);
00398     }
00399 
00400     //------------------------------------------------
00401     // Concatenate the bytecode
00402 
00403     // Insert a JitEntry at the start of the function for JIT compilers
00404     byteCode.InstrWORD(asBC_JitEntry, 0);
00405 
00406     // Count total variable size
00407     int varSize = GetVariableOffset((int)variableAllocations.GetLength()) - 1;
00408     byteCode.Push(varSize);
00409 
00410     if( outFunc->objectType )
00411     {
00412         // Call the base class' default constructor unless called manually in the code
00413         if( m_isConstructor && !m_isConstructorCalled && outFunc->objectType->derivedFrom )
00414         {
00415             byteCode.InstrSHORT(asBC_PSF, 0);
00416             byteCode.Instr(asBC_RDSPTR);
00417             byteCode.Call(asBC_CALL, outFunc->objectType->derivedFrom->beh.construct, AS_PTR_SIZE);
00418         }
00419 
00420         // Increase the reference for the object pointer, so that it is guaranteed to live during the entire call
00421         // TODO: optimize: This is probably not necessary for constructors as no outside reference to the object is created yet
00422         byteCode.InstrSHORT(asBC_PSF, 0);
00423         byteCode.Instr(asBC_RDSPTR);
00424         byteCode.Call(asBC_CALLSYS, outFunc->objectType->beh.addref, AS_PTR_SIZE);
00425     }
00426 
00427     // Add the code for the statement block
00428     byteCode.AddCode(&bc);
00429 
00430     // Deallocate all local variables
00431     for( n = (int)variables->variables.GetLength() - 1; n >= 0; n-- )
00432     {
00433         sVariable *v = variables->variables[n];
00434         if( v->stackOffset > 0 )
00435         {
00436             // Call variables destructors
00437             if( v->name != "return" && v->name != "return address" )
00438                 CallDestructor(v->type, v->stackOffset, &byteCode);
00439 
00440             DeallocateVariable(v->stackOffset);
00441         }
00442     }
00443 
00444     // This is the label that return statements jump to
00445     // in order to exit the function
00446     byteCode.Label(0);
00447 
00448     // Release the object pointer again
00449     if( outFunc->objectType )
00450     {
00451         byteCode.InstrSHORT(asBC_PSF, 0);
00452         byteCode.InstrPTR(asBC_FREE, outFunc->objectType);
00453     }
00454 
00455     // Call destructors for function parameters
00456     for( n = (int)variables->variables.GetLength() - 1; n >= 0; n-- )
00457     {
00458         sVariable *v = variables->variables[n];
00459         if( v->stackOffset <= 0 )
00460         {
00461             // Call variable destructors here, for variables not yet destroyed
00462             if( v->name != "return" && v->name != "return address" )
00463                 CallDestructor(v->type, v->stackOffset, &byteCode);
00464         }
00465 
00466         // Do not deallocate parameters
00467     }
00468 
00469     // If there are compile errors, there is no reason to build the final code
00470     if( hasCompileErrors ) return -1;
00471 
00472     // At this point there should be no variables allocated
00473     asASSERT(variableAllocations.GetLength() == freeVariables.GetLength());
00474 
00475     // Remove the variable scope
00476     RemoveVariableScope();
00477 
00478     byteCode.Pop(varSize);
00479 
00480     byteCode.Ret(-stackPos);
00481 
00482     // Tell the bytecode which variables are temporary
00483     for( n = 0; n < (signed)variableIsTemporary.GetLength(); n++ )
00484     {
00485         if( variableIsTemporary[n] )
00486             byteCode.DefineTemporaryVariable(GetVariableOffset(n));
00487     }
00488 
00489     // Finalize the bytecode
00490     byteCode.Finalize();
00491 
00492     // Compile the list of object variables for the exception handler
00493     for( n = 0; n < (int)variableAllocations.GetLength(); n++ )
00494     {
00495         if( variableAllocations[n].IsObject() && !variableAllocations[n].IsReference() )
00496         {
00497             objVariableTypes.PushLast(variableAllocations[n].GetObjectType());
00498             objVariablePos.PushLast(GetVariableOffset(n));
00499         }
00500     }
00501 
00502     if( hasCompileErrors ) return -1;
00503 
00504     // Copy byte code to the function
00505     outFunc->byteCode.SetLength(byteCode.GetSize());
00506     byteCode.Output(outFunc->byteCode.AddressOf());
00507     outFunc->AddReferences();
00508     outFunc->stackNeeded = byteCode.largestStackUsed;
00509     outFunc->lineNumbers = byteCode.lineNumbers;
00510     outFunc->objVariablePos = objVariablePos;
00511     outFunc->objVariableTypes = objVariableTypes;
00512 
00513 #ifdef AS_DEBUG
00514     // DEBUG: output byte code
00515     if( outFunc->objectType )
00516         byteCode.DebugOutput(("__" + outFunc->objectType->name + "_" + outFunc->name + ".txt").AddressOf(), builder->module, engine);
00517     else
00518         byteCode.DebugOutput(("__" + outFunc->name + ".txt").AddressOf(), builder->module, engine);
00519 #endif
00520 
00521     return 0;
00522 }
00523 
00524 
00525 
00526 
00527 
00528 int asCCompiler::CallDefaultConstructor(asCDataType &type, int offset, asCByteCode *bc, asCScriptNode *node, bool isGlobalVar)
00529 {
00530     // Call constructor for the data type
00531     if( type.IsObject() && !type.IsObjectHandle() )
00532     {
00533         if( type.GetObjectType()->flags & asOBJ_REF )
00534         {
00535             asSExprContext ctx(engine);
00536 
00537             int func = 0;
00538             asSTypeBehaviour *beh = type.GetBehaviour();
00539             if( beh ) func = beh->factory;
00540 
00541             if( func > 0 )
00542             {
00543                 if( !isGlobalVar )
00544                 {
00545                     // Call factory and store the handle in the given variable
00546                     PerformFunctionCall(func, &ctx, false, 0, type.GetObjectType(), true, offset);
00547 
00548                     // Pop the reference left by the function call
00549                     ctx.bc.Pop(AS_PTR_SIZE);
00550                 }
00551                 else
00552                 {
00553                     // Call factory
00554                     PerformFunctionCall(func, &ctx, false, 0, type.GetObjectType());
00555 
00556                     // Store the returned handle in the global variable
00557                     ctx.bc.Instr(asBC_RDSPTR);
00558                     ctx.bc.InstrWORD(asBC_PGA, (asWORD)builder->module->GetGlobalVarPtrIndex(offset));
00559                     ctx.bc.InstrPTR(asBC_REFCPY, type.GetObjectType());
00560                     ctx.bc.Pop(AS_PTR_SIZE);
00561                     ReleaseTemporaryVariable(ctx.type.stackOffset, &ctx.bc);
00562                 }
00563 
00564                 bc->AddCode(&ctx.bc);
00565             }
00566             else
00567             {
00568                 asCString str;
00569                 str.Format(TXT_NO_DEFAULT_CONSTRUCTOR_FOR_s, type.GetObjectType()->GetName());
00570                 Error(str.AddressOf(), node);
00571                 //Class has no default constructor.
00572                 return -1;
00573             }
00574         }
00575         else
00576         {
00577             if( isGlobalVar )
00578                 bc->InstrWORD(asBC_PGA, (asWORD)builder->module->GetGlobalVarPtrIndex(offset));
00579             else
00580                 bc->InstrSHORT(asBC_PSF, (short)offset);
00581 
00582             asSTypeBehaviour *beh = type.GetBehaviour();
00583 
00584             int func = 0;
00585             if( beh ) func = beh->construct;
00586 
00587             // TODO: Should give error if the value type doesn't have a default constructor and isn't a POD type
00588 
00589             bc->Alloc(asBC_ALLOC, type.GetObjectType(), func, AS_PTR_SIZE);
00590         }
00591     }
00592     return 0;
00593 }
00594 
00595 void asCCompiler::CallDestructor(asCDataType &type, int offset, asCByteCode *bc)
00596 {
00597     if( !type.IsReference() )
00598     {
00599         // Call destructor for the data type
00600         if( type.IsObject() )
00601         {
00602             // Free the memory
00603             bc->InstrSHORT(asBC_PSF, (short)offset);
00604             bc->InstrPTR(asBC_FREE, type.GetObjectType());
00605         }
00606     }
00607 }
00608 
00609 void asCCompiler::LineInstr(asCByteCode *bc, size_t pos)
00610 {
00611     int r, c;
00612     script->ConvertPosToRowCol(pos, &r, &c);
00613     bc->Line(r, c);
00614 }
00615 
00616 void asCCompiler::CompileStatementBlock(asCScriptNode *block, bool ownVariableScope, bool *hasReturn, asCByteCode *bc)
00617 {
00618     *hasReturn = false;
00619     bool isFinished = false;
00620     bool hasWarned = false;
00621 
00622     if( ownVariableScope )
00623         AddVariableScope();
00624 
00625     asCScriptNode *node = block->firstChild;
00626     while( node )
00627     {
00628         if( !hasWarned && (*hasReturn || isFinished) )
00629         {
00630             hasWarned = true;
00631             Warning(TXT_UNREACHABLE_CODE, node);
00632         }
00633 
00634         if( node->nodeType == snBreak || node->nodeType == snContinue )
00635             isFinished = true;
00636 
00637         asCByteCode statement(engine);
00638         if( node->nodeType == snDeclaration )
00639             CompileDeclaration(node, &statement);
00640         else
00641             CompileStatement(node, hasReturn, &statement);
00642 
00643         LineInstr(bc, node->tokenPos);
00644         bc->AddCode(&statement);
00645 
00646         if( !hasCompileErrors )
00647             asASSERT( tempVariables.GetLength() == 0 );
00648 
00649         node = node->next;
00650     }
00651 
00652     if( ownVariableScope )
00653     {
00654 
00655         // Deallocate variables in this block, in reverse order
00656         for( int n = (int)variables->variables.GetLength() - 1; n >= 0; n-- )
00657         {
00658             sVariable *v = variables->variables[n];
00659 
00660             // Call variable destructors here, for variables not yet destroyed
00661             // If the block is terminated with a break, continue, or
00662             // return the variables are already destroyed
00663             if( !isFinished && !*hasReturn )
00664                 CallDestructor(v->type, v->stackOffset, bc);
00665 
00666             // Don't deallocate function parameters
00667             if( v->stackOffset > 0 )
00668                 DeallocateVariable(v->stackOffset);
00669         }
00670 
00671         RemoveVariableScope();
00672     }
00673 }
00674 
00675 int asCCompiler::CompileGlobalVariable(asCBuilder *builder, asCScriptCode *script, asCScriptNode *node, sGlobalVariableDescription *gvar)
00676 {
00677     Reset(builder, script, 0);
00678     globalExpression = true;
00679 
00680     // Add a variable scope (even though variables can't be declared)
00681     AddVariableScope();
00682 
00683     asSExprContext ctx(engine);
00684 
00685     gvar->isPureConstant = false;
00686 
00687     // Parse the initialization nodes
00688     asCParser parser(builder);
00689     if( node )
00690     {
00691         int r = parser.ParseGlobalVarInit(script, node);
00692         if( r < 0 )
00693             return r;
00694 
00695         node = parser.GetScriptNode();
00696     }
00697 
00698     // Compile the expression
00699     if( node && node->nodeType == snArgList )
00700     {
00701         // Make sure that it is a registered type, and that it isn't a pointer
00702         if( gvar->datatype.GetObjectType() == 0 || gvar->datatype.IsObjectHandle() )
00703         {
00704             Error(TXT_MUST_BE_OBJECT, node);
00705         }
00706         else
00707         {
00708             // Compile the arguments
00709             asCArray<asSExprContext *> args;
00710             if( CompileArgumentList(node, args) >= 0 )
00711             {
00712                 // Find all constructors
00713                 asCArray<int> funcs;
00714                 asSTypeBehaviour *beh = gvar->datatype.GetBehaviour();
00715                 if( beh )
00716                 {
00717                     if( gvar->datatype.GetObjectType()->flags & asOBJ_REF )
00718                         funcs = beh->factories;
00719                     else
00720                         funcs = beh->constructors;
00721                 }
00722 
00723                 asCString str = gvar->datatype.Format();
00724                 MatchFunctions(funcs, args, node, str.AddressOf());
00725 
00726                 if( funcs.GetLength() == 1 )
00727                 {
00728                     if( gvar->datatype.GetObjectType()->flags & asOBJ_REF )
00729                     {
00730                         MakeFunctionCall(&ctx, funcs[0], 0, args, node);
00731 
00732                         // Store the returned handle in the global variable
00733                         ctx.bc.Instr(asBC_RDSPTR);
00734                         ctx.bc.InstrWORD(asBC_PGA, (asWORD)builder->module->GetGlobalVarPtrIndex(gvar->index));
00735                         ctx.bc.InstrPTR(asBC_REFCPY, gvar->datatype.GetObjectType());
00736                         ctx.bc.Pop(AS_PTR_SIZE);
00737                         ReleaseTemporaryVariable(ctx.type.stackOffset, &ctx.bc);
00738                     }
00739                     else
00740                     {
00741                         // TODO: This reference is open while evaluating the arguments. We must fix this
00742                         ctx.bc.InstrWORD(asBC_PGA, (asWORD)builder->module->GetGlobalVarPtrIndex(gvar->index));
00743 
00744                         PrepareFunctionCall(funcs[0], &ctx.bc, args);
00745                         MoveArgsToStack(funcs[0], &ctx.bc, args, false);
00746 
00747                         PerformFunctionCall(funcs[0], &ctx, true, &args, gvar->datatype.GetObjectType());
00748                     }
00749                 }
00750             }
00751 
00752             // Cleanup
00753             for( asUINT n = 0; n < args.GetLength(); n++ )
00754                 if( args[n] )
00755                 {
00756                     asDELETE(args[n],asSExprContext);
00757                 }
00758         }
00759     }
00760     else if( node && node->nodeType == snInitList )
00761     {
00762         asCTypeInfo ti;
00763         ti.Set(gvar->datatype);
00764         ti.isVariable = false;
00765         ti.isTemporary = false;
00766         ti.stackOffset = (short)gvar->index;
00767 
00768         CompileInitList(&ti, node, &ctx.bc);
00769 
00770         node = node->next;
00771     }
00772     else
00773     {
00774         // Call constructor for all data types
00775         if( gvar->datatype.IsObject() && !gvar->datatype.IsObjectHandle() )
00776         {
00777             CallDefaultConstructor(gvar->datatype, gvar->index, &ctx.bc, gvar->idNode, true);
00778         }
00779 
00780         if( node )
00781         {
00782             asSExprContext expr(engine);
00783             int r = CompileAssignment(node, &expr); if( r < 0 ) return r;
00784 
00785             if( gvar->datatype.IsPrimitive() )
00786             {
00787                 if( gvar->datatype.IsReadOnly() && expr.type.isConstant )
00788                 {
00789                     ImplicitConversion(&expr, gvar->datatype, node, asIC_IMPLICIT_CONV);
00790 
00791                     gvar->isPureConstant = true;
00792                     gvar->constantValue = expr.type.qwordValue;
00793                 }
00794 
00795                 asSExprContext lctx(engine);
00796                 lctx.type.Set(gvar->datatype);
00797                 lctx.type.dataType.MakeReference(true);
00798                 lctx.type.dataType.MakeReadOnly(false);
00799 
00800                 // If it is an enum value that is being compiled, then
00801                 // we skip this, as the bytecode won't be used anyway
00802                 if( !gvar->isEnumValue )
00803                     lctx.bc.InstrWORD(asBC_LDG, (asWORD)builder->module->GetGlobalVarPtrIndex(gvar->index));
00804 
00805                 DoAssignment(&ctx, &lctx, &expr, node, node, ttAssignment, node);
00806             }
00807             else
00808             {
00809                 asSExprContext lexpr(engine);
00810                 lexpr.type.Set(gvar->datatype);
00811                 lexpr.type.dataType.MakeReference(true);
00812                 lexpr.type.dataType.MakeReadOnly(false);
00813                 lexpr.type.stackOffset = -1;
00814 
00815                 if( gvar->datatype.IsObjectHandle() )
00816                     lexpr.type.isExplicitHandle = true;
00817 
00818                 lexpr.bc.InstrWORD(asBC_PGA, (asWORD)builder->module->GetGlobalVarPtrIndex(gvar->index));
00819 
00820                 // If left expression resolves into a registered type
00821                 // check if the assignment operator is overloaded, and check
00822                 // the type of the right hand expression. If none is found
00823                 // the default action is a direct copy if it is the same type
00824                 // and a simple assignment.
00825                 bool assigned = false;
00826                 if( lexpr.type.dataType.IsObject() && !lexpr.type.isExplicitHandle )
00827                 {
00828                     assigned = CompileOverloadedDualOperator(node, &lexpr, &expr, &ctx);
00829                     if( assigned )
00830                     {
00831                         // Pop the resulting value
00832                         ctx.bc.Pop(ctx.type.dataType.GetSizeOnStackDWords());
00833 
00834                         // Release the argument
00835                         ProcessDeferredParams(&ctx);
00836                     }
00837                 }
00838 
00839 #ifdef AS_DEPRECATED
00840                 if( !assigned )
00841                 {
00842                     asSTypeBehaviour *beh = 0;
00843                     if( !lexpr.type.isExplicitHandle )
00844                         beh = lexpr.type.dataType.GetBehaviour();
00845                     if( beh )
00846                     {
00847                         // Find the matching overloaded operators
00848                         int op = asBEHAVE_ASSIGNMENT;
00849                         asCArray<int> ops;
00850                         asUINT n;
00851                         for( n = 0; n < beh->operators.GetLength(); n += 2 )
00852                         {
00853                             if( op == beh->operators[n] )
00854                                 ops.PushLast(beh->operators[n+1]);
00855                         }
00856 
00857                         asCArray<int> match;
00858                         MatchArgument(ops, match, &expr.type, 0);
00859 
00860                         if( match.GetLength() > 0 )
00861                             assigned = true;
00862 
00863                         if( match.GetLength() == 1 )
00864                         {
00865                             // If it is an array, both sides must have the same subtype
00866                             if( lexpr.type.dataType.IsArrayType() )
00867                                 if( !lexpr.type.dataType.IsEqualExceptRefAndConst(expr.type.dataType) )
00868                                     Error(TXT_BOTH_MUST_BE_SAME, node);
00869 
00870                             asCScriptFunction *descr = engine->scriptFunctions[match[0]];
00871 
00872                             // Add code for arguments
00873                             MergeExprContexts(&ctx, &expr);
00874 
00875                             PrepareArgument(&descr->parameterTypes[0], &expr, node, true, descr->inOutFlags[0]);
00876                             MergeExprContexts(&ctx, &expr);
00877 
00878 
00879                             asCArray<asSExprContext*> args;
00880                             args.PushLast(&expr);
00881                             MoveArgsToStack(match[0], &ctx.bc, args, false);
00882 
00883                             // Add the code for the object
00884                             ctx.bc.AddCode(&lexpr.bc);
00885                             ctx.bc.Instr(asBC_RDSPTR);
00886 
00887                             PerformFunctionCall(match[0], &ctx, false, &args);
00888 
00889                             ctx.bc.Pop(ctx.type.dataType.GetSizeOnStackDWords());
00890 
00891                             ProcessDeferredParams(&ctx);
00892                         }
00893                         else if( match.GetLength() > 1 )
00894                         {
00895                             Error(TXT_MORE_THAN_ONE_MATCHING_OP, node);
00896                         }
00897                     }
00898                 }
00899 #endif
00900                 if( !assigned )
00901                 {
00902                     PrepareForAssignment(&lexpr.type.dataType, &expr, node);
00903 
00904                     // If the expression is constant and the variable also is constant
00905                     // then mark the variable as pure constant. This will allow the compiler
00906                     // to optimize expressions with this variable.
00907                     if( gvar->datatype.IsReadOnly() && expr.type.isConstant )
00908                     {
00909                         gvar->isPureConstant = true;
00910                         gvar->constantValue = expr.type.qwordValue;
00911                     }
00912 
00913                     // Add expression code to bytecode
00914                     MergeExprContexts(&ctx, &expr);
00915 
00916                     // Add byte code for storing value of expression in variable
00917                     ctx.bc.InstrWORD(asBC_PGA, (asWORD)builder->module->GetGlobalVarPtrIndex(gvar->index));
00918 
00919                     PerformAssignment(&lexpr.type, &expr.type, &ctx.bc, node);
00920 
00921                     // Release temporary variables used by expression
00922                     ReleaseTemporaryVariable(expr.type, &ctx.bc);
00923 
00924                     ctx.bc.Pop(expr.type.dataType.GetSizeOnStackDWords());
00925                 }
00926             }
00927         }
00928     }
00929 
00930     // Concatenate the bytecode
00931     int varSize = GetVariableOffset((int)variableAllocations.GetLength()) - 1;
00932 
00933     // We need to push zeroes on the stack to guarantee
00934     // that temporary object handles are clear
00935     int n;
00936     for( n = 0; n < varSize; n++ )
00937         byteCode.InstrINT(asBC_PshC4, 0);
00938 
00939     byteCode.AddCode(&ctx.bc);
00940 
00941     // Deallocate variables in this block, in reverse order
00942     for( n = (int)variables->variables.GetLength() - 1; n >= 0; --n )
00943     {
00944         sVariable *v = variables->variables[n];
00945 
00946         // Call variable destructors here, for variables not yet destroyed
00947         CallDestructor(v->type, v->stackOffset, &byteCode);
00948 
00949         DeallocateVariable(v->stackOffset);
00950     }
00951 
00952     if( hasCompileErrors ) return -1;
00953 
00954     // At this point there should be no variables allocated
00955     asASSERT(variableAllocations.GetLength() == freeVariables.GetLength());
00956 
00957     // Remove the variable scope again
00958     RemoveVariableScope();
00959 
00960     byteCode.Pop(varSize);
00961 
00962     return 0;
00963 }
00964 
00965 void asCCompiler::PrepareArgument(asCDataType *paramType, asSExprContext *ctx, asCScriptNode *node, bool isFunction, int refType, asCArray<int> *reservedVars)
00966 {
00967     asCDataType param = *paramType;
00968     if( paramType->GetTokenType() == ttQuestion )
00969     {
00970         // Since the function is expecting a var type ?, then we don't want to convert the argument to anything else
00971         param = ctx->type.dataType;
00972         param.MakeHandle(ctx->type.isExplicitHandle);
00973         param.MakeReference(paramType->IsReference());
00974         param.MakeReadOnly(paramType->IsReadOnly());
00975     }
00976     else
00977         param = *paramType;
00978 
00979     asCDataType dt = param;
00980 
00981     // Need to protect arguments by reference
00982     if( isFunction && dt.IsReference() )
00983     {
00984         if( paramType->GetTokenType() == ttQuestion )
00985         {
00986             asCByteCode tmpBC(engine);
00987 
00988             // Place the type id on the stack as a hidden parameter
00989             tmpBC.InstrDWORD(asBC_TYPEID, engine->GetTypeIdFromDataType(param));
00990 
00991             // Insert the code before the expression code
00992             tmpBC.AddCode(&ctx->bc);
00993             ctx->bc.AddCode(&tmpBC);
00994         }
00995 
00996         // Allocate a temporary variable of the same type as the argument
00997         dt.MakeReference(false);
00998         dt.MakeReadOnly(false);
00999 
01000         int offset;
01001         if( refType == 1 ) // &in
01002         {
01003             ProcessPropertyGetAccessor(ctx, node);
01004 
01005             // If the reference is const, then it is not necessary to make a copy if the value already is a variable
01006             // Even if the same variable is passed in another argument as non-const then there is no problem
01007             if( dt.IsPrimitive() || dt.IsNullHandle() )
01008             {
01009                 IsVariableInitialized(&ctx->type, node);
01010 
01011                 if( ctx->type.dataType.IsReference() ) ConvertToVariable(ctx);
01012                 ImplicitConversion(ctx, dt, node, asIC_IMPLICIT_CONV, true, reservedVars);
01013 
01014                 if( !(param.IsReadOnly() && ctx->type.isVariable) )
01015                     ConvertToTempVariable(ctx);
01016 
01017                 PushVariableOnStack(ctx, true);
01018                 ctx->type.dataType.MakeReadOnly(param.IsReadOnly());
01019             }
01020             else
01021             {
01022                 IsVariableInitialized(&ctx->type, node);
01023 
01024                 ImplicitConversion(ctx, param, node, asIC_IMPLICIT_CONV, true, reservedVars);
01025 
01026                 if( !ctx->type.dataType.IsEqualExceptRef(param) )
01027                 {
01028                     asCString str;
01029                     str.Format(TXT_CANT_IMPLICITLY_CONVERT_s_TO_s, ctx->type.dataType.Format().AddressOf(), param.Format().AddressOf());
01030                     Error(str.AddressOf(), node);
01031 
01032                     ctx->type.Set(param);
01033                 }
01034 
01035                 // If the argument already is a temporary
01036                 // variable we don't need to allocate another
01037 
01038                 // If the parameter is read-only and the object already is a local
01039                 // variable then it is not necessary to make a copy either
01040                 if( !ctx->type.isTemporary && !(param.IsReadOnly() && ctx->type.isVariable))
01041                 {
01042                     // Make sure the variable is not used in the expression
01043                     asCArray<int> vars;
01044                     ctx->bc.GetVarsUsed(vars);
01045                     if( reservedVars ) vars.Concatenate(*reservedVars);
01046                     offset = AllocateVariableNotIn(dt, true, &vars);
01047 
01048                     // Allocate and construct the temporary object
01049                     asCByteCode tmpBC(engine);
01050                     CallDefaultConstructor(dt, offset, &tmpBC, node);
01051 
01052                     // Insert the code before the expression code
01053                     tmpBC.AddCode(&ctx->bc);
01054                     ctx->bc.AddCode(&tmpBC);
01055 
01056                     // Assign the evaluated expression to the temporary variable
01057                     PrepareForAssignment(&dt, ctx, node);
01058 
01059                     dt.MakeReference(true);
01060                     asCTypeInfo type;
01061                     type.Set(dt);
01062                     type.isTemporary = true;
01063                     type.stackOffset = (short)offset;
01064 
01065                     if( dt.IsObjectHandle() )
01066                         type.isExplicitHandle = true;
01067 
01068                     ctx->bc.InstrSHORT(asBC_PSF, (short)offset);
01069 
01070                     PerformAssignment(&type, &ctx->type, &ctx->bc, node);
01071 
01072                     ctx->bc.Pop(ctx->type.dataType.GetSizeOnStackDWords());
01073 
01074                     ReleaseTemporaryVariable(ctx->type, &ctx->bc);
01075 
01076                     ctx->type = type;
01077 
01078                     ctx->bc.InstrSHORT(asBC_PSF, (short)offset);
01079                     if( dt.IsObject() && !dt.IsObjectHandle() )
01080                         ctx->bc.Instr(asBC_RDSPTR);
01081 
01082                     if( paramType->IsReadOnly() )
01083                         ctx->type.dataType.MakeReadOnly(true);
01084                 }
01085             }
01086         }
01087         else if( refType == 2 ) // &out
01088         {
01089             // Make sure the variable is not used in the expression
01090             asCArray<int> vars;
01091             ctx->bc.GetVarsUsed(vars);
01092             if( reservedVars ) vars.Concatenate(*reservedVars);
01093             offset = AllocateVariableNotIn(dt, true, &vars);
01094 
01095             if( dt.IsPrimitive() )
01096             {
01097                 ctx->type.SetVariable(dt, offset, true);
01098                 PushVariableOnStack(ctx, true);
01099             }
01100             else
01101             {
01102                 // Allocate and construct the temporary object
01103                 asCByteCode tmpBC(engine);
01104                 CallDefaultConstructor(dt, offset, &tmpBC, node);
01105 
01106                 // Insert the code before the expression code
01107                 tmpBC.AddCode(&ctx->bc);
01108                 ctx->bc.AddCode(&tmpBC);
01109 
01110                 dt.MakeReference((!dt.IsObject() || dt.IsObjectHandle()));
01111                 asCTypeInfo type;
01112                 type.Set(dt);
01113                 type.isTemporary = true;
01114                 type.stackOffset = (short)offset;
01115 
01116                 ctx->type = type;
01117 
01118                 ctx->bc.InstrSHORT(asBC_PSF, (short)offset);
01119                 if( dt.IsObject() && !dt.IsObjectHandle() )
01120                     ctx->bc.Instr(asBC_RDSPTR);
01121             }
01122 
01123             // After the function returns the temporary variable will
01124             // be assigned to the expression, if it is a valid lvalue
01125         }
01126         else if( refType == asTM_INOUTREF )
01127         {
01128             // Literal constants cannot be passed to inout ref arguments
01129             if( !ctx->type.isVariable && ctx->type.isConstant )
01130             {
01131                 Error(TXT_NOT_VALID_REFERENCE, node);
01132             }
01133 
01134             // Only objects that support object handles
01135             // can be guaranteed to be safe. Local variables are
01136             // already safe, so there is no need to add an extra
01137             // references
01138             if( !engine->ep.allowUnsafeReferences &&
01139                 !ctx->type.isVariable &&
01140                 ctx->type.dataType.IsObject() &&
01141                 !ctx->type.dataType.IsObjectHandle() &&
01142                 ctx->type.dataType.GetBehaviour()->addref &&
01143                 ctx->type.dataType.GetBehaviour()->release )
01144             {
01145                 // Store a handle to the object as local variable
01146                 asSExprContext tmp(engine);
01147                 asCDataType dt = ctx->type.dataType;
01148                 dt.MakeHandle(true);
01149                 dt.MakeReference(false);
01150 
01151                 asCArray<int> vars;
01152                 ctx->bc.GetVarsUsed(vars);
01153                 if( reservedVars ) vars.Concatenate(*reservedVars);
01154                 offset = AllocateVariableNotIn(dt, true, &vars);
01155 
01156                 // Copy the handle
01157                 if( !ctx->type.dataType.IsObjectHandle() && ctx->type.dataType.IsReference() )
01158                     ctx->bc.Instr(asBC_RDSPTR);
01159                 ctx->bc.InstrWORD(asBC_PSF, (asWORD)offset);
01160                 ctx->bc.InstrPTR(asBC_REFCPY, ctx->type.dataType.GetObjectType());
01161                 ctx->bc.Pop(AS_PTR_SIZE);
01162                 ctx->bc.InstrWORD(asBC_PSF, (asWORD)offset);
01163 
01164                 dt.MakeHandle(false);
01165                 dt.MakeReference(true);
01166 
01167                 // Release previous temporary variable stored in the context (if any)
01168                 if( ctx->type.isTemporary )
01169                 {
01170                     ReleaseTemporaryVariable(ctx->type.stackOffset, &ctx->bc);
01171                 }
01172 
01173                 ctx->type.SetVariable(dt, offset, true);
01174             }
01175 
01176             // Make sure the reference to the value is on the stack
01177             if( ctx->type.dataType.IsObject() && ctx->type.dataType.IsReference() )
01178                 Dereference(ctx, true);
01179             else if( ctx->type.isVariable )
01180                 ctx->bc.InstrSHORT(asBC_PSF, ctx->type.stackOffset);
01181             else if( ctx->type.dataType.IsPrimitive() )
01182                 ctx->bc.Instr(asBC_PshRPtr);
01183         }
01184     }
01185     else
01186     {
01187         ProcessPropertyGetAccessor(ctx, node);
01188 
01189         if( dt.IsPrimitive() )
01190         {
01191             IsVariableInitialized(&ctx->type, node);
01192 
01193             if( ctx->type.dataType.IsReference() ) ConvertToVariable(ctx);
01194 
01195             // Implicitly convert primitives to the parameter type
01196             ImplicitConversion(ctx, dt, node, asIC_IMPLICIT_CONV, true, reservedVars);
01197 
01198             if( ctx->type.isVariable )
01199             {
01200                 PushVariableOnStack(ctx, dt.IsReference());
01201             }
01202             else if( ctx->type.isConstant )
01203             {
01204                 ConvertToVariable(ctx);
01205                 PushVariableOnStack(ctx, dt.IsReference());
01206             }
01207         }
01208         else
01209         {
01210             IsVariableInitialized(&ctx->type, node);
01211 
01212             // Implicitly convert primitives to the parameter type
01213             ImplicitConversion(ctx, dt, node, asIC_IMPLICIT_CONV, true, reservedVars);
01214 
01215             // Was the conversion successful?
01216             if( !ctx->type.dataType.IsEqualExceptRef(dt) )
01217             {
01218                 asCString str;
01219                 str.Format(TXT_CANT_IMPLICITLY_CONVERT_s_TO_s, ctx->type.dataType.Format().AddressOf(), dt.Format().AddressOf());
01220                 Error(str.AddressOf(), node);
01221 
01222                 ctx->type.Set(dt);
01223             }
01224 
01225             if( dt.IsObjectHandle() )
01226                 ctx->type.isExplicitHandle = true;
01227 
01228             if( dt.IsObject() )
01229             {
01230                 if( !dt.IsReference() )
01231                 {
01232                     // Objects passed by value must be placed in temporary variables
01233                     // so that they are guaranteed to not be referenced anywhere else
01234                     PrepareTemporaryObject(node, ctx, reservedVars);
01235 
01236                     // The implicit conversion shouldn't convert the object to
01237                     // non-reference yet. It will be dereferenced just before the call.
01238                     // Otherwise the object might be missed by the exception handler.
01239                     dt.MakeReference(true);
01240                 }
01241                 else
01242                 {
01243                     // An object passed by reference should place the pointer to
01244                     // the object on the stack.
01245                     dt.MakeReference(false);
01246                 }
01247             }
01248         }
01249     }
01250 
01251     // Don't put any pointer on the stack yet
01252     if( param.IsReference() || param.IsObject() )
01253     {
01254         // &inout parameter may leave the reference on the stack already
01255         if( refType != 3 )
01256         {
01257             ctx->bc.Pop(AS_PTR_SIZE);
01258             ctx->bc.InstrSHORT(asBC_VAR, ctx->type.stackOffset);
01259         }
01260 
01261         ProcessDeferredParams(ctx);
01262     }
01263 }
01264 
01265 void asCCompiler::PrepareFunctionCall(int funcID, asCByteCode *bc, asCArray<asSExprContext *> &args)
01266 {
01267     // When a match has been found, compile the final byte code using correct parameter types
01268     asCScriptFunction *descr = builder->GetFunctionDescription(funcID);
01269 
01270     // Add code for arguments
01271     asSExprContext e(engine);
01272     int n;
01273     for( n = (int)args.GetLength()-1; n >= 0; n-- )
01274     {
01275         // Make sure PrepareArgument doesn't use any variable that is already
01276         // being used by any of the following argument expressions
01277         asCArray<int> reservedVars;
01278         for( int m = n-1; m >= 0; m-- )
01279             args[m]->bc.GetVarsUsed(reservedVars);
01280 
01281         PrepareArgument2(&e, args[n], &descr->parameterTypes[n], true, descr->inOutFlags[n], &reservedVars);
01282     }
01283 
01284     bc->AddCode(&e.bc);
01285 }
01286 
01287 void asCCompiler::MoveArgsToStack(int funcID, asCByteCode *bc, asCArray<asSExprContext *> &args, bool addOneToOffset)
01288 {
01289     asCScriptFunction *descr = builder->GetFunctionDescription(funcID);
01290 
01291     int offset = 0;
01292     if( addOneToOffset )
01293         offset += AS_PTR_SIZE;
01294 
01295     // Move the objects that are sent by value to the stack just before the call
01296     for( asUINT n = 0; n < descr->parameterTypes.GetLength(); n++ )
01297     {
01298         if( descr->parameterTypes[n].IsReference() )
01299         {
01300             if( descr->parameterTypes[n].IsObject() && !descr->parameterTypes[n].IsObjectHandle() )
01301             {
01302                 if( descr->inOutFlags[n] != asTM_INOUTREF )
01303                     bc->InstrWORD(asBC_GETOBJREF, (asWORD)offset);
01304                 if( args[n]->type.dataType.IsObjectHandle() )
01305                     bc->InstrWORD(asBC_ChkNullS, (asWORD)offset);
01306             }
01307             else if( descr->inOutFlags[n] != asTM_INOUTREF )
01308             {
01309                 if( descr->parameterTypes[n].GetTokenType() == ttQuestion &&
01310                     args[n]->type.dataType.IsObject() && !args[n]->type.dataType.IsObjectHandle() )
01311                 {
01312                     // Send the object as a reference to the object, 
01313                     // and not to the variable holding the object
01314                     bc->InstrWORD(asBC_GETOBJREF, (asWORD)offset);
01315                 }
01316                 else
01317                     bc->InstrWORD(asBC_GETREF, (asWORD)offset);
01318             }
01319         }
01320         else if( descr->parameterTypes[n].IsObject() )
01321         {
01322             bc->InstrWORD(asBC_GETOBJ, (asWORD)offset);
01323 
01324             // The temporary variable must not be freed as it will no longer hold an object
01325             DeallocateVariable(args[n]->type.stackOffset);
01326             args[n]->type.isTemporary = false;
01327         }
01328 
01329         offset += descr->parameterTypes[n].GetSizeOnStackDWords();
01330     }
01331 }
01332 
01333 int asCCompiler::CompileArgumentList(asCScriptNode *node, asCArray<asSExprContext*> &args)
01334 {
01335     asASSERT(node->nodeType == snArgList);
01336 
01337     // Count arguments
01338     asCScriptNode *arg = node->firstChild;
01339     int argCount = 0;
01340     while( arg )
01341     {
01342         argCount++;
01343         arg = arg->next;
01344     }
01345 
01346     // Prepare the arrays
01347     args.SetLength(argCount);
01348     int n;
01349     for( n = 0; n < argCount; n++ )
01350         args[n] = 0;
01351 
01352     n = argCount-1;
01353 
01354     // Compile the arguments in reverse order (as they will be pushed on the stack)
01355     bool anyErrors = false;
01356     arg = node->lastChild;
01357     while( arg )
01358     {
01359         asSExprContext expr(engine);
01360         int r = CompileAssignment(arg, &expr);
01361         if( r < 0 ) anyErrors = true;
01362 
01363         args[n] = asNEW(asSExprContext)(engine);
01364         MergeExprContexts(args[n], &expr);
01365         args[n]->type = expr.type;
01366         args[n]->property_get = expr.property_get;
01367         args[n]->property_set = expr.property_set;
01368         args[n]->property_const = expr.property_const;
01369         args[n]->exprNode = arg;
01370 
01371         n--;
01372         arg = arg->prev;
01373     }
01374 
01375     return anyErrors ? -1 : 0;
01376 }
01377 
01378 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)
01379 {
01380     asUINT n;
01381     if( funcs.GetLength() > 0 )
01382     {
01383         // Check the number of parameters in the found functions
01384         for( n = 0; n < funcs.GetLength(); ++n )
01385         {
01386             asCScriptFunction *desc = builder->GetFunctionDescription(funcs[n]);
01387 
01388             if( desc->parameterTypes.GetLength() != args.GetLength() )
01389             {
01390                 // remove it from the list
01391                 if( n == funcs.GetLength()-1 )
01392                     funcs.PopLast();
01393                 else
01394                     funcs[n] = funcs.PopLast();
01395                 n--;
01396             }
01397         }
01398 
01399         // Match functions with the parameters, and discard those that do not match
01400         asCArray<int> matchingFuncs = funcs;
01401 
01402         for( n = 0; n < args.GetLength(); ++n )
01403         {
01404             asCArray<int> tempFuncs;
01405             MatchArgument(funcs, tempFuncs, &args[n]->type, n, allowObjectConstruct);
01406 
01407             // Intersect the found functions with the list of matching functions
01408             for( asUINT f = 0; f < matchingFuncs.GetLength(); f++ )
01409             {
01410                 asUINT c;
01411                 for( c = 0; c < tempFuncs.GetLength(); c++ )
01412                 {
01413                     if( matchingFuncs[f] == tempFuncs[c] )
01414                         break;
01415                 }
01416 
01417                 // Was the function a match?
01418                 if( c == tempFuncs.GetLength() )
01419                 {
01420                     // No, remove it from the list
01421                     if( f == matchingFuncs.GetLength()-1 )
01422                         matchingFuncs.PopLast();
01423                     else
01424                         matchingFuncs[f] = matchingFuncs.PopLast();
01425                     f--;
01426                 }
01427             }
01428         }
01429 
01430         funcs = matchingFuncs;
01431     }
01432 
01433     if( !isConstMethod )
01434         FilterConst(funcs);
01435 
01436     if( funcs.GetLength() != 1 && !silent )
01437     {
01438         // Build a readable string of the function with parameter types
01439         asCString str;
01440         if( scope != "" )
01441         {
01442             if( scope == "::" )
01443                 str = scope;
01444             else
01445                 str = scope + "::";
01446         }
01447         str += name;
01448         str += "(";
01449         if( args.GetLength() )
01450             str += args[0]->type.dataType.Format();
01451         for( n = 1; n < args.GetLength(); n++ )
01452             str += ", " + args[n]->type.dataType.Format();
01453         str += ")";
01454 
01455         if( isConstMethod )
01456             str += " const";
01457 
01458         if( objectType && scope == "" )
01459             str = objectType->name + "::" + str;
01460 
01461         if( funcs.GetLength() == 0 )
01462         {
01463             str.Format(TXT_NO_MATCHING_SIGNATURES_TO_s, str.AddressOf());
01464             Error(str.AddressOf(), node);
01465         }
01466         else
01467         {
01468             str.Format(TXT_MULTIPLE_MATCHING_SIGNATURES_TO_s, str.AddressOf());
01469             Error(str.AddressOf(), node);
01470     
01471             PrintMatchingFuncs(funcs, node);
01472         }
01473     }
01474 }
01475 
01476 void asCCompiler::CompileDeclaration(asCScriptNode *decl, asCByteCode *bc)
01477 {
01478     // Get the data type
01479     asCDataType type = builder->CreateDataTypeFromNode(decl->firstChild, script);
01480 
01481     // Declare all variables in this declaration
01482     asCScriptNode *node = decl->firstChild->next;
01483     while( node )
01484     {
01485         // Is the type allowed?
01486         if( !type.CanBeInstanciated() )
01487         {
01488             asCString str;
01489             // TODO: Change to "'type' cannot be declared as variable"
01490             str.Format(TXT_DATA_TYPE_CANT_BE_s, type.Format().AddressOf());
01491             Error(str.AddressOf(), node);
01492 
01493             // Use int instead to avoid further problems
01494             type = asCDataType::CreatePrimitive(ttInt, false);
01495         }
01496 
01497         // Get the name of the identifier
01498         asCString name(&script->code[node->tokenPos], node->tokenLength);
01499 
01500         // Verify that the name isn't used by a dynamic data type
01501         if( engine->GetObjectType(name.AddressOf()) != 0 )
01502         {
01503             asCString str;
01504             str.Format(TXT_ILLEGAL_VARIABLE_NAME_s, name.AddressOf());
01505             Error(str.AddressOf(), node);
01506         }
01507 
01508         int offset = AllocateVariable(type, false);
01509         if( variables->DeclareVariable(name.AddressOf(), type, offset) < 0 )
01510         {
01511             asCString str;
01512             str.Format(TXT_s_ALREADY_DECLARED, name.AddressOf());
01513             Error(str.AddressOf(), node);
01514         }
01515 
01516         outFunc->AddVariable(name, type, offset);
01517 
01518         // Keep the node for the variable decl
01519         asCScriptNode *varNode = node;
01520 
01521         node = node->next;
01522         if( node && node->nodeType == snArgList )
01523         {
01524             // Make sure that it is a registered type, and that is isn't a pointer
01525             if( type.GetObjectType() == 0 || type.IsObjectHandle() )
01526             {
01527                 Error(TXT_MUST_BE_OBJECT, node);
01528             }
01529             else
01530             {
01531                 // Compile the arguments
01532                 asCArray<asSExprContext *> args;
01533 
01534                 if( CompileArgumentList(node, args) >= 0 )
01535                 {
01536                     // Find all constructors
01537                     asCArray<int> funcs;
01538                     asSTypeBehaviour *beh = type.GetBehaviour();
01539                     if( beh )
01540                     {
01541                         if( type.GetObjectType()->flags & asOBJ_REF )
01542                             funcs = beh->factories;
01543                         else
01544                             funcs = beh->constructors;
01545                     }
01546 
01547                     asCString str = type.Format();
01548                     MatchFunctions(funcs, args, node, str.AddressOf());
01549 
01550                     if( funcs.GetLength() == 1 )
01551                     {
01552                         sVariable *v = variables->GetVariable(name.AddressOf());
01553                         asSExprContext ctx(engine);
01554                         if( v->type.GetObjectType()->flags & asOBJ_REF )
01555                         {
01556                             MakeFunctionCall(&ctx, funcs[0], 0, args, node, true, v->stackOffset);
01557 
01558                             // Pop the reference left by the function call
01559                             ctx.bc.Pop(AS_PTR_SIZE);
01560                         }
01561                         else
01562                         {
01563                             ctx.bc.InstrSHORT(asBC_VAR, (short)v->stackOffset);
01564 
01565                             PrepareFunctionCall(funcs[0], &ctx.bc, args);
01566                             MoveArgsToStack(funcs[0], &ctx.bc, args, false);
01567 
01568                             int offset = 0;
01569                             asCScriptFunction *descr = builder->GetFunctionDescription(funcs[0]);
01570                             for( asUINT n = 0; n < args.GetLength(); n++ )
01571                                 offset += descr->parameterTypes[n].GetSizeOnStackDWords();
01572 
01573                             ctx.bc.InstrWORD(asBC_GETREF, (asWORD)offset);
01574 
01575                             PerformFunctionCall(funcs[0], &ctx, true, &args, type.GetObjectType());
01576                         }
01577                         bc->AddCode(&ctx.bc);
01578                     }
01579                 }
01580 
01581                 // Cleanup
01582                 for( asUINT n = 0; n < args.GetLength(); n++ )
01583                     if( args[n] )
01584                     {
01585                         asDELETE(args[n],asSExprContext);
01586                     }
01587             }
01588 
01589             node = node->next;
01590         }
01591         else if( node && node->nodeType == snInitList )
01592         {
01593             sVariable *v = variables->GetVariable(name.AddressOf());
01594 
01595             asCTypeInfo ti;
01596             ti.Set(type);
01597             ti.isVariable = true;
01598             ti.isTemporary = false;
01599             ti.stackOffset = (short)v->stackOffset;
01600 
01601             CompileInitList(&ti, node, bc);
01602 
01603             node = node->next;
01604         }
01605         else
01606         {
01607             asSExprContext ctx(engine);
01608 
01609             // Call the default constructor here
01610             CallDefaultConstructor(type, offset, &ctx.bc, varNode);
01611 
01612             // Is the variable initialized?
01613             if( node && node->nodeType == snAssignment )
01614             {
01615                 // Compile the expression
01616                 asSExprContext expr(engine);
01617                 int r = CompileAssignment(node, &expr);
01618                 if( r >= 0 )
01619                 {
01620                     if( type.IsPrimitive() )
01621                     {
01622                         if( type.IsReadOnly() && expr.type.isConstant )
01623                         {
01624                             ImplicitConversion(&expr, type, node, asIC_IMPLICIT_CONV);
01625 
01626                             sVariable *v = variables->GetVariable(name.AddressOf());
01627                             v->isPureConstant = true;
01628                             v->constantValue = expr.type.qwordValue;
01629                         }
01630 
01631                         asSExprContext lctx(engine);
01632                         lctx.type.SetVariable(type, offset, false);
01633                         lctx.type.dataType.MakeReadOnly(false);
01634 
01635                         DoAssignment(&ctx, &lctx, &expr, node, node, ttAssignment, node);
01636                     }
01637                     else
01638                     {
01639                         // TODO: We can use a copy constructor here
01640 
01641                         asSExprContext lexpr(engine);
01642                         lexpr.type.Set(type);
01643                         lexpr.type.dataType.MakeReference(true);
01644                         // Allow initialization of constant variables
01645                         lexpr.type.dataType.MakeReadOnly(false);
01646 
01647                         if( type.IsObjectHandle() )
01648                             lexpr.type.isExplicitHandle = true;
01649 
01650                         sVariable *v = variables->GetVariable(name.AddressOf());
01651                         lexpr.bc.InstrSHORT(asBC_PSF, (short)v->stackOffset);
01652                         lexpr.type.stackOffset = (short)v->stackOffset;
01653 
01654 
01655                         // If left expression resolves into a registered type
01656                         // check if the assignment operator is overloaded, and check
01657                         // the type of the right hand expression. If none is found
01658                         // the default action is a direct copy if it is the same type
01659                         // and a simple assignment.
01660                         bool assigned = false;
01661                         if( lexpr.type.dataType.IsObject() && !lexpr.type.isExplicitHandle )
01662                         {
01663                             assigned = CompileOverloadedDualOperator(node, &lexpr, &expr, &ctx);
01664                             if( assigned )
01665                             {
01666                                 // Pop the resulting value
01667                                 ctx.bc.Pop(ctx.type.dataType.GetSizeOnStackDWords());
01668 
01669                                 // Release the argument
01670                                 ProcessDeferredParams(&ctx);
01671                             }
01672                         }
01673 #ifdef AS_DEPRECATED
01674                         if( !assigned )
01675                         {
01676                             asSTypeBehaviour *beh = 0;
01677                             if( !lexpr.type.isExplicitHandle )
01678                                 beh = lexpr.type.dataType.GetBehaviour();
01679                             bool assigned = false;
01680                             if( beh )
01681                             {
01682                                 // Find the matching overloaded operators
01683                                 int op = asBEHAVE_ASSIGNMENT;
01684                                 asCArray<int> ops;
01685                                 asUINT n;
01686                                 for( n = 0; n < beh->operators.GetLength(); n += 2 )
01687                                 {
01688                                     if( op == beh->operators[n] )
01689                                         ops.PushLast(beh->operators[n+1]);
01690                                 }
01691 
01692                                 asCArray<int> match;
01693                                 MatchArgument(ops, match, &expr.type, 0);
01694 
01695                                 if( match.GetLength() > 0 )
01696                                     assigned = true;
01697 
01698                                 if( match.GetLength() == 1 )
01699                                 {
01700                                     // If it is an array, both sides must have the same subtype
01701                                     if( lexpr.type.dataType.IsArrayType() )
01702                                         if( !lexpr.type.dataType.IsEqualExceptRefAndConst(expr.type.dataType) )
01703                                             Error(TXT_BOTH_MUST_BE_SAME, node);
01704 
01705                                     asCScriptFunction *descr = engine->scriptFunctions[match[0]];
01706 
01707                                     // Add code for arguments
01708                                     MergeExprContexts(&ctx, &expr);
01709 
01710                                     PrepareArgument(&descr->parameterTypes[0], &expr, node, true, descr->inOutFlags[0]);
01711                                     MergeExprContexts(&ctx, &expr);
01712 
01713                                     asCArray<asSExprContext*> args;
01714                                     args.PushLast(&expr);
01715                                     MoveArgsToStack(match[0], &ctx.bc, args, false);
01716 
01717                                     // Add the code for the object
01718                                     ctx.bc.AddCode(&lexpr.bc);
01719                                     ctx.bc.Instr(asBC_RDSPTR);
01720 
01721                                     PerformFunctionCall(match[0], &ctx, false, &args);
01722 
01723                                     ctx.bc.Pop(ctx.type.dataType.GetSizeOnStackDWords());
01724 
01725                                     ProcessDeferredParams(&ctx);
01726                                 }
01727                                 else if( match.GetLength() > 1 )
01728                                 {
01729                                     Error(TXT_MORE_THAN_ONE_MATCHING_OP, node);
01730                                 }
01731                             }
01732                         }
01733 #endif
01734                         if( !assigned )
01735                         {
01736                             PrepareForAssignment(&lexpr.type.dataType, &expr, node);
01737 
01738                             // If the expression is constant and the variable also is constant
01739                             // then mark the variable as pure constant. This will allow the compiler
01740                             // to optimize expressions with this variable.
01741                             if( v->type.IsReadOnly() && expr.type.isConstant )
01742                             {
01743                                 v->isPureConstant = true;
01744                                 v->constantValue = expr.type.qwordValue;
01745                             }
01746 
01747                             // Add expression code to bytecode
01748                             MergeExprContexts(&ctx, &expr);
01749 
01750                             // Add byte code for storing value of expression in variable
01751                             ctx.bc.AddCode(&lexpr.bc);
01752                             lexpr.type.stackOffset = (short)v->stackOffset;
01753 
01754                             PerformAssignment(&lexpr.type, &expr.type, &ctx.bc, node->prev);
01755 
01756                             // Release temporary variables used by expression
01757                             ReleaseTemporaryVariable(expr.type, &ctx.bc);
01758 
01759                             ctx.bc.Pop(expr.type.dataType.GetSizeOnStackDWords());
01760 
01761                             ProcessDeferredParams(&ctx);
01762                         }
01763                     }
01764                 }
01765 
01766                 node = node->next;
01767             }
01768 
01769             bc->AddCode(&ctx.bc);
01770 
01771             // TODO: Can't this leave deferred output params without being compiled?
01772         }
01773     }
01774 }
01775 
01776 void asCCompiler::CompileInitList(asCTypeInfo *var, asCScriptNode *node, asCByteCode *bc)
01777 {
01778     if( var->dataType.IsArrayType() && !var->dataType.IsObjectHandle() )
01779     {
01780         // Count the number of elements and initialize the array with the correct size
01781         int countElements = 0;
01782         asCScriptNode *el = node->firstChild;
01783         while( el )
01784         {
01785             countElements++;
01786             el = el->next;
01787         }
01788 
01789         // Construct the array with the size elements
01790 
01791         // Find the constructor that takes an uint
01792         asCArray<int> funcs;
01793         if( var->dataType.GetObjectType()->flags & asOBJ_REF )
01794             funcs = var->dataType.GetBehaviour()->factories;
01795         else
01796             funcs = var->dataType.GetBehaviour()->constructors;
01797 
01798         asCArray<asSExprContext *> args;
01799         asSExprContext arg1(engine);
01800         arg1.bc.InstrDWORD(asBC_PshC4, countElements);
01801         arg1.type.Set(asCDataType::CreatePrimitive(ttUInt, false));
01802         args.PushLast(&arg1);
01803 
01804         asCString str = var->dataType.Format();
01805         MatchFunctions(funcs, args, node, str.AddressOf());
01806 
01807         if( funcs.GetLength() == 1 )
01808         {
01809             asSExprContext ctx(engine);
01810 
01811             if( var->dataType.GetObjectType()->flags & asOBJ_REF )
01812             {
01813                 PrepareFunctionCall(funcs[0], &ctx.bc, args);
01814                 MoveArgsToStack(funcs[0], &ctx.bc, args, false);
01815 
01816                 if( var->isVariable )
01817                 {
01818                     // Call factory and store the handle in the given variable
01819                     PerformFunctionCall(funcs[0], &ctx, false, &args, 0, true, var->stackOffset);
01820                     ctx.bc.Pop(AS_PTR_SIZE);
01821                 }
01822                 else
01823                 {
01824                     PerformFunctionCall(funcs[0], &ctx, false, &args);
01825 
01826                     // Store the returned handle in the global variable
01827                     ctx.bc.Instr(asBC_RDSPTR);
01828                     ctx.bc.InstrWORD(asBC_PGA, (asWORD)builder->module->GetGlobalVarPtrIndex(var->stackOffset));
01829                     ctx.bc.InstrPTR(asBC_REFCPY, var->dataType.GetObjectType());
01830                     ctx.bc.Pop(AS_PTR_SIZE);
01831                     ReleaseTemporaryVariable(ctx.type.stackOffset, &ctx.bc);
01832                 }
01833             }
01834             else
01835             {
01836                 if( var->isVariable )
01837                     ctx.bc.InstrSHORT(asBC_PSF, var->stackOffset);
01838                 else
01839                     ctx.bc.InstrWORD(asBC_PGA, (asWORD)builder->module->GetGlobalVarPtrIndex(var->stackOffset));
01840 
01841                 PrepareFunctionCall(funcs[0], &ctx.bc, args);
01842                 MoveArgsToStack(funcs[0], &ctx.bc, args, false);
01843 
01844                 PerformFunctionCall(funcs[0], &ctx, true, &args, var->dataType.GetObjectType());
01845             }
01846 
01847             bc->AddCode(&ctx.bc);
01848         }
01849         else
01850             return;
01851 
01852         // Find the indexing operator that is not read-only that will be used for all elements
01853         asCDataType retType;
01854         retType = var->dataType.GetSubType();
01855         retType.MakeReference(true);
01856         retType.MakeReadOnly(false);
01857         int funcId = 0;
01858         asSTypeBehaviour *beh = var->dataType.GetBehaviour();
01859         for( asUINT n = 0; n < beh->operators.GetLength(); n += 2 )
01860         {
01861             if( asBEHAVE_INDEX == beh->operators[n] )
01862             {
01863                 asCScriptFunction *desc = builder->GetFunctionDescription(beh->operators[n+1]);
01864                 if( !desc->isReadOnly &&
01865                      desc->parameterTypes.GetLength() == 1 &&
01866                      (desc->parameterTypes[0] == asCDataType::CreatePrimitive(ttUInt, false) ||
01867                       desc->parameterTypes[0] == asCDataType::CreatePrimitive(ttInt,  false)) &&
01868                      desc->returnType == retType )
01869                 {
01870                     funcId = beh->operators[n+1];
01871                     break;
01872                 }
01873             }
01874         }
01875 
01876         if( funcId == 0 )
01877         {
01878             Error(TXT_NO_APPROPRIATE_INDEX_OPERATOR, node);
01879             return;
01880         }
01881 
01882         asUINT index = 0;
01883         el = node->firstChild;
01884         while( el )
01885         {
01886             if( el->nodeType == snAssignment || el->nodeType == snInitList )
01887             {
01888                 asSExprContext lctx(engine);
01889                 asSExprContext rctx(engine);
01890 
01891                 if( el->nodeType == snAssignment )
01892                 {
01893                     // Compile the assignment expression
01894                     CompileAssignment(el, &rctx);
01895                 }
01896                 else if( el->nodeType == snInitList )
01897                 {
01898                     int offset = AllocateVariable(var->dataType.GetSubType(), true);
01899 
01900                     rctx.type.Set(var->dataType.GetSubType());
01901                     rctx.type.isVariable = true;
01902                     rctx.type.isTemporary = true;
01903                     rctx.type.stackOffset = (short)offset;
01904 
01905                     CompileInitList(&rctx.type, el, &rctx.bc);
01906 
01907                     // Put the object on the stack
01908                     rctx.bc.InstrSHORT(asBC_PSF, rctx.type.stackOffset);
01909 
01910                     // It is a reference that we place on the stack
01911                     rctx.type.dataType.MakeReference(true);
01912                 }
01913 
01914                 // Compile the lvalue
01915                 lctx.bc.InstrDWORD(asBC_PshC4, index);
01916                 if( var->isVariable )
01917                     lctx.bc.InstrSHORT(asBC_PSF, var->stackOffset);
01918                 else
01919                     lctx.bc.InstrWORD(asBC_PGA, (asWORD)builder->module->GetGlobalVarPtrIndex(var->stackOffset));
01920                 lctx.bc.Instr(asBC_RDSPTR);
01921                 lctx.bc.Call(asBC_CALLSYS, funcId, 1+AS_PTR_SIZE);
01922 
01923                 if( !var->dataType.GetSubType().IsPrimitive() )
01924                     lctx.bc.Instr(asBC_PshRPtr);
01925 
01926                 lctx.type.Set(var->dataType.GetSubType());
01927 
01928                 if( !lctx.type.dataType.IsObject() || lctx.type.dataType.IsObjectHandle() )
01929                     lctx.type.dataType.MakeReference(true);
01930 
01931                 // If the element type is handles, then we're expected to do handle assignments
01932                 if( lctx.type.dataType.IsObjectHandle() )
01933                     lctx.type.isExplicitHandle = true;
01934 
01935                 asSExprContext ctx(engine);
01936                 DoAssignment(&ctx, &lctx, &rctx, el, el, ttAssignment, el);
01937 
01938                 if( !lctx.type.dataType.IsPrimitive() )
01939                     ctx.bc.Pop(AS_PTR_SIZE);
01940 
01941                 // Release temporary variables used by expression
01942                 ReleaseTemporaryVariable(ctx.type, &ctx.bc);
01943 
01944                 ProcessDeferredParams(&ctx);
01945 
01946                 bc->AddCode(&ctx.bc);
01947             }
01948 
01949             el = el->next;
01950             index++;
01951         }
01952     }
01953     else
01954     {
01955         asCString str;
01956         str.Format(TXT_INIT_LIST_CANNOT_BE_USED_WITH_s, var->dataType.Format().AddressOf());
01957         Error(str.AddressOf(), node);
01958     }
01959 }
01960 
01961 void asCCompiler::CompileStatement(asCScriptNode *statement, bool *hasReturn, asCByteCode *bc)
01962 {
01963     *hasReturn = false;
01964 
01965     if( statement->nodeType == snStatementBlock )
01966         CompileStatementBlock(statement, true, hasReturn, bc);
01967     else if( statement->nodeType == snIf )
01968         CompileIfStatement(statement, hasReturn, bc);
01969     else if( statement->nodeType == snFor )
01970         CompileForStatement(statement, bc);
01971     else if( statement->nodeType == snWhile )
01972         CompileWhileStatement(statement, bc);
01973     else if( statement->nodeType == snDoWhile )
01974         CompileDoWhileStatement(statement, bc);
01975     else if( statement->nodeType == snExpressionStatement )
01976         CompileExpressionStatement(statement, bc);
01977     else if( statement->nodeType == snBreak )
01978         CompileBreakStatement(statement, bc);
01979     else if( statement->nodeType == snContinue )
01980         CompileContinueStatement(statement, bc);
01981     else if( statement->nodeType == snSwitch )
01982         CompileSwitchStatement(statement, hasReturn, bc);
01983     else if( statement->nodeType == snReturn )
01984     {
01985         CompileReturnStatement(statement, bc);
01986         *hasReturn = true;
01987     }
01988 }
01989 
01990 void asCCompiler::CompileSwitchStatement(asCScriptNode *snode, bool *, asCByteCode *bc)
01991 {
01992     // TODO: inheritance: Must guarantee that all options in the switch case call a constructor, or that none call it.
01993 
01994     // Reserve label for break statements
01995     int breakLabel = nextLabel++;
01996     breakLabels.PushLast(breakLabel);
01997 
01998     // Add a variable scope that will be used by CompileBreak
01999     // to know where to stop deallocating variables
02000     AddVariableScope(true, false);
02001 
02002     //---------------------------
02003     // Compile the switch expression
02004     //-------------------------------
02005 
02006     // Compile the switch expression
02007     asSExprContext expr(engine);
02008     CompileAssignment(snode->firstChild, &expr);
02009 
02010     // Verify that the expression is a primitive type
02011     if( !expr.type.dataType.IsIntegerType() && !expr.type.dataType.IsUnsignedType() && !expr.type.dataType.IsEnumType() )
02012     {
02013         Error(TXT_SWITCH_MUST_BE_INTEGRAL, snode->firstChild);
02014         return;
02015     }
02016 
02017     // TODO: Need to support 64bit
02018     // Convert the expression to a 32bit variable
02019     asCDataType to;
02020     if( expr.type.dataType.IsIntegerType() || expr.type.dataType.IsEnumType() )
02021         to.SetTokenType(ttInt);
02022     else if( expr.type.dataType.IsUnsignedType() )
02023         to.SetTokenType(ttUInt);
02024     ImplicitConversion(&expr, to, snode->firstChild, asIC_IMPLICIT_CONV, true);
02025 
02026     ConvertToVariable(&expr);
02027     int offset = expr.type.stackOffset;
02028 
02029     //-------------------------------
02030     // Determine case values and labels
02031     //--------------------------------
02032 
02033     // Remember the first label so that we can later pass the
02034     // correct label to each CompileCase()
02035     int firstCaseLabel = nextLabel;
02036     int defaultLabel = 0;
02037 
02038     asCArray<int> caseValues;
02039     asCArray<int> caseLabels;
02040 
02041     // Compile all case comparisons and make them jump to the right label
02042     asCScriptNode *cnode = snode->firstChild->next;
02043     while( cnode )
02044     {
02045         // Each case should have a constant expression
02046         if( cnode->firstChild && cnode->firstChild->nodeType == snExpression )
02047         {
02048             // Compile expression
02049             asSExprContext c(engine);
02050             CompileExpression(cnode->firstChild, &c);
02051 
02052             // Verify that the result is a constant
02053             if( !c.type.isConstant )
02054                 Error(TXT_SWITCH_CASE_MUST_BE_CONSTANT, cnode->firstChild);
02055 
02056             // Verify that the result is an integral number
02057             if( !c.type.dataType.IsIntegerType() && !c.type.dataType.IsUnsignedType() && !c.type.dataType.IsEnumType() )
02058                 Error(TXT_SWITCH_MUST_BE_INTEGRAL, cnode->firstChild);
02059 
02060             ImplicitConversion(&c, to, cnode->firstChild, asIC_IMPLICIT_CONV, true);
02061 
02062             // Has this case been declared already?
02063             if( caseValues.IndexOf(c.type.intValue) >= 0 )
02064             {
02065                 Error(TXT_DUPLICATE_SWITCH_CASE, cnode->firstChild);
02066             }
02067 
02068             // TODO: Optimize: We can insert the numbers sorted already
02069 
02070             // Store constant for later use
02071             caseValues.PushLast(c.type.intValue);
02072 
02073             // Reserve label for this case
02074             caseLabels.PushLast(nextLabel++);
02075         }
02076         else
02077         {
02078             // Is default the last case?
02079             if( cnode->next )
02080             {
02081                 Error(TXT_DEFAULT_MUST_BE_LAST, cnode);
02082                 break;
02083             }
02084 
02085             // Reserve label for this case
02086             defaultLabel = nextLabel++;
02087         }
02088 
02089         cnode = cnode->next;
02090     }
02091 
02092     // check for empty switch
02093     if (caseValues.GetLength() == 0)
02094     {
02095         Error(TXT_EMPTY_SWITCH, snode);
02096         return;
02097     }
02098 
02099     if( defaultLabel == 0 )
02100         defaultLabel = breakLabel;
02101 
02102     //---------------------------------
02103     // Output the optimized case comparisons
02104     // with jumps to the case code
02105     //------------------------------------
02106 
02107     // Sort the case values by increasing value. Do the sort together with the labels
02108     // A simple bubble sort is sufficient since we don't expect a huge number of values
02109     for( asUINT fwd = 1; fwd < caseValues.GetLength(); fwd++ )
02110     {
02111         for( int bck = fwd - 1; bck >= 0; bck-- )
02112         {
02113             int bckp = bck + 1;
02114             if( caseValues[bck] > caseValues[bckp] )
02115             {
02116                 // Swap the values in both arrays
02117                 int swap = caseValues[bckp];
02118                 caseValues[bckp] = caseValues[bck];
02119                 caseValues[bck] = swap;
02120 
02121                 swap = caseLabels[bckp];
02122                 caseLabels[bckp] = caseLabels[bck];
02123                 caseLabels[bck] = swap;
02124             }
02125             else
02126                 break;
02127         }
02128     }
02129 
02130     // Find ranges of consecutive numbers
02131     asCArray<int> ranges;
02132     ranges.PushLast(0);
02133     asUINT n;
02134     for( n = 1; n < caseValues.GetLength(); ++n )
02135     {
02136         // We can join numbers that are less than 5 numbers
02137         // apart since the output code will still be smaller
02138         if( caseValues[n] > caseValues[n-1] + 5 )
02139             ranges.PushLast(n);
02140     }
02141 
02142     // If the value is larger than the largest case value, jump to default
02143     int tmpOffset = AllocateVariable(asCDataType::CreatePrimitive(ttInt, false), true);
02144     expr.bc.InstrSHORT_DW(asBC_SetV4, (short)tmpOffset, caseValues[caseValues.GetLength()-1]);
02145     expr.bc.InstrW_W(asBC_CMPi, offset, tmpOffset);
02146     expr.bc.InstrDWORD(asBC_JP, defaultLabel);
02147     ReleaseTemporaryVariable(tmpOffset, &expr.bc);
02148 
02149     // TODO: optimize: We could possibly optimize this even more by doing a
02150     //                 binary search instead of a linear search through the ranges
02151 
02152     // For each range
02153     int range;
02154     for( range = 0; range < (int)ranges.GetLength(); range++ )
02155     {
02156         // Find the largest value in this range
02157         int maxRange = caseValues[ranges[range]];
02158         int index = ranges[range];
02159         for( ; (index < (int)caseValues.GetLength()) && (caseValues[index] <= maxRange + 5); index++ )
02160             maxRange = caseValues[index];
02161 
02162         // If there are only 2 numbers then it is better to compare them directly
02163         if( index - ranges[range] > 2 )
02164         {
02165             // If the value is smaller than the smallest case value in the range, jump to default
02166             tmpOffset = AllocateVariable(asCDataType::CreatePrimitive(ttInt, false), true);
02167             expr.bc.InstrSHORT_DW(asBC_SetV4, (short)tmpOffset, caseValues[ranges[range]]);
02168             expr.bc.InstrW_W(asBC_CMPi, offset, tmpOffset);
02169             expr.bc.InstrDWORD(asBC_JS, defaultLabel);
02170             ReleaseTemporaryVariable(tmpOffset, &expr.bc);
02171 
02172             int nextRangeLabel = nextLabel++;
02173             // If this is the last range we don't have to make this test
02174             if( range < (int)ranges.GetLength() - 1 )
02175             {
02176                 // If the value is larger than the largest case value in the range, jump to the next range
02177                 tmpOffset = AllocateVariable(asCDataType::CreatePrimitive(ttInt, false), true);
02178                 expr.bc.InstrSHORT_DW(asBC_SetV4, (short)tmpOffset, maxRange);
02179                 expr.bc.InstrW_W(asBC_CMPi, offset, tmpOffset);
02180                 expr.bc.InstrDWORD(asBC_JP, nextRangeLabel);
02181                 ReleaseTemporaryVariable(tmpOffset, &expr.bc);
02182             }
02183 
02184             // Jump forward according to the value
02185             tmpOffset = AllocateVariable(asCDataType::CreatePrimitive(ttInt, false), true);
02186             expr.bc.InstrSHORT_DW(asBC_SetV4, (short)tmpOffset, caseValues[ranges[range]]);
02187             expr.bc.InstrW_W_W(asBC_SUBi, tmpOffset, offset, tmpOffset);
02188             ReleaseTemporaryVariable(tmpOffset, &expr.bc);
02189             expr.bc.JmpP(tmpOffset, maxRange - caseValues[ranges[range]]);
02190 
02191             // Add the list of jumps to the correct labels (any holes, jump to default)
02192             index = ranges[range];
02193             for( int n = caseValues[index]; n <= maxRange; n++ )
02194             {
02195                 if( caseValues[index] == n )
02196                     expr.bc.InstrINT(asBC_JMP, caseLabels[index++]);
02197                 else
02198                     expr.bc.InstrINT(asBC_JMP, defaultLabel);
02199             }
02200 
02201             expr.bc.Label((short)nextRangeLabel);
02202         }
02203         else
02204         {
02205             // Simply make a comparison with each value
02206             int n;
02207             for( n = ranges[range]; n < index; ++n )
02208             {
02209                 tmpOffset = AllocateVariable(asCDataType::CreatePrimitive(ttInt, false), true);
02210                 expr.bc.InstrSHORT_DW(asBC_SetV4, (short)tmpOffset, caseValues[n]);
02211                 expr.bc.InstrW_W(asBC_CMPi, offset, tmpOffset);
02212                 expr.bc.InstrDWORD(asBC_JZ, caseLabels[n]);
02213                 ReleaseTemporaryVariable(tmpOffset, &expr.bc);
02214             }
02215         }
02216     }
02217 
02218     // Catch any value that falls trough
02219     expr.bc.InstrINT(asBC_JMP, defaultLabel);
02220 
02221     // Release the temporary variable previously stored
02222     ReleaseTemporaryVariable(expr.type, &expr.bc);
02223 
02224     //----------------------------------
02225     // Output case implementations
02226     //----------------------------------
02227 
02228     // Compile case implementations, each one with the label before it
02229     cnode = snode->firstChild->next;
02230     while( cnode )
02231     {
02232         // Each case should have a constant expression
02233         if( cnode->firstChild && cnode->firstChild->nodeType == snExpression )
02234         {
02235             expr.bc.Label((short)firstCaseLabel++);
02236 
02237             CompileCase(cnode->firstChild->next, &expr.bc);
02238         }
02239         else
02240         {
02241             expr.bc.Label((short)defaultLabel);
02242 
02243             // Is default the last case?
02244             if( cnode->next )
02245             {
02246                 // We've already reported this error
02247                 break;
02248             }
02249 
02250             CompileCase(cnode->firstChild, &expr.bc);
02251         }
02252 
02253         cnode = cnode->next;
02254     }
02255 
02256     //--------------------------------
02257 
02258     bc->AddCode(&expr.bc);
02259 
02260     // Add break label
02261     bc->Label((short)breakLabel);
02262 
02263     breakLabels.PopLast();
02264     RemoveVariableScope();
02265 }
02266 
02267 void asCCompiler::CompileCase(asCScriptNode *node, asCByteCode *bc)
02268 {
02269     bool isFinished = false;
02270     bool hasReturn = false;
02271     while( node )
02272     {
02273         if( hasReturn || isFinished )
02274         {
02275             Warning(TXT_UNREACHABLE_CODE, node);
02276             break;
02277         }
02278 
02279         if( node->nodeType == snBreak || node->nodeType == snContinue )
02280             isFinished = true;
02281 
02282         asCByteCode statement(engine);
02283         CompileStatement(node, &hasReturn, &statement);
02284 
02285         LineInstr(bc, node->tokenPos);
02286         bc->AddCode(&statement);
02287 
02288         if( !hasCompileErrors )
02289             asASSERT( tempVariables.GetLength() == 0 );
02290 
02291         node = node->next;
02292     }
02293 }
02294 
02295 void asCCompiler::CompileIfStatement(asCScriptNode *inode, bool *hasReturn, asCByteCode *bc)
02296 {
02297     // We will use one label for the if statement
02298     // and possibly another for the else statement
02299     int afterLabel = nextLabel++;
02300 
02301     // Compile the expression
02302     asSExprContext expr(engine);
02303     CompileAssignment(inode->firstChild, &expr);
02304     if( !expr.type.dataType.IsEqualExceptRefAndConst(asCDataType::CreatePrimitive(ttBool, true)) )
02305     {
02306         Error(TXT_EXPR_MUST_BE_BOOL, inode->firstChild);
02307         expr.type.SetConstantDW(asCDataType::CreatePrimitive(ttBool, true), 1);
02308     }
02309 
02310     if( expr.type.dataType.IsReference() ) ConvertToVariable(&expr);
02311     ProcessDeferredParams(&expr);
02312 
02313     if( !expr.type.isConstant )
02314     {
02315         ProcessPropertyGetAccessor(&expr, inode);
02316 
02317         ConvertToVariable(&expr);
02318 
02319         // Add byte code from the expression
02320         bc->AddCode(&expr.bc);
02321 
02322         // Add a test
02323         bc->InstrSHORT(asBC_CpyVtoR4, expr.type.stackOffset);
02324         bc->Instr(asBC_ClrHi);
02325         bc->InstrDWORD(asBC_JZ, afterLabel);
02326         ReleaseTemporaryVariable(expr.type, bc);
02327     }
02328     else if( expr.type.dwordValue == 0 )
02329     {
02330         // Jump to the else case
02331         bc->InstrINT(asBC_JMP, afterLabel);
02332 
02333         // TODO: Should we warn?
02334     }
02335 
02336     // Compile the if statement
02337     bool origIsConstructorCalled = m_isConstructorCalled;
02338 
02339     bool hasReturn1;
02340     asCByteCode ifBC(engine);
02341     CompileStatement(inode->firstChild->next, &hasReturn1, &ifBC);
02342 
02343     // Add the byte code
02344     LineInstr(bc, inode->firstChild->next->tokenPos);
02345     bc->AddCode(&ifBC);
02346 
02347     if( inode->firstChild->next->nodeType == snExpressionStatement && inode->firstChild->next->firstChild == 0 )
02348     {
02349         // Don't allow  if( expr );
02350         Error(TXT_IF_WITH_EMPTY_STATEMENT, inode->firstChild->next);
02351     }
02352 
02353     // If one of the statements call the constructor, the other must as well
02354     // otherwise it is possible the constructor is never called
02355     bool constructorCall1 = false;
02356     bool constructorCall2 = false;
02357     if( !origIsConstructorCalled && m_isConstructorCalled )
02358         constructorCall1 = true;
02359 
02360     // Do we have an else statement?
02361     if( inode->firstChild->next != inode->lastChild )
02362     {
02363         // Reset the constructor called flag so the else statement can call the constructor too
02364         m_isConstructorCalled = origIsConstructorCalled;
02365 
02366         int afterElse = 0;
02367         if( !hasReturn1 )
02368         {
02369             afterElse = nextLabel++;
02370 
02371             // Add jump to after the else statement
02372             bc->InstrINT(asBC_JMP, afterElse);
02373         }
02374 
02375         // Add label for the else statement
02376         bc->Label((short)afterLabel);
02377 
02378         bool hasReturn2;
02379         asCByteCode elseBC(engine);
02380         CompileStatement(inode->lastChild, &hasReturn2, &elseBC);
02381 
02382         // Add byte code for the else statement
02383         LineInstr(bc, inode->lastChild->tokenPos);
02384         bc->AddCode(&elseBC);
02385 
02386         if( inode->lastChild->nodeType == snExpressionStatement && inode->lastChild->firstChild == 0 )
02387         {
02388             // Don't allow  if( expr ) {} else;
02389             Error(TXT_ELSE_WITH_EMPTY_STATEMENT, inode->lastChild);
02390         }
02391 
02392         if( !hasReturn1 )
02393         {
02394             // Add label for the end of else statement
02395             bc->Label((short)afterElse);
02396         }
02397 
02398         // The if statement only has return if both alternatives have
02399         *hasReturn = hasReturn1 && hasReturn2;
02400 
02401         if( !origIsConstructorCalled && m_isConstructorCalled )
02402             constructorCall2 = true;
02403     }
02404     else
02405     {
02406         // Add label for the end of if statement
02407         bc->Label((short)afterLabel);
02408         *hasReturn = false;
02409     }
02410 
02411     // Make sure both or neither conditions call a constructor
02412     if( (constructorCall1 && !constructorCall2) ||
02413         (constructorCall2 && !constructorCall1) )
02414     {
02415         Error(TXT_BOTH_CONDITIONS_MUST_CALL_CONSTRUCTOR, inode);
02416     }
02417 
02418     m_isConstructorCalled = origIsConstructorCalled || constructorCall1 || constructorCall2;
02419 }
02420 
02421 void asCCompiler::CompileForStatement(asCScriptNode *fnode, asCByteCode *bc)
02422 {
02423     // Add a variable scope that will be used by CompileBreak/Continue to know where to stop deallocating variables
02424     AddVariableScope(true, true);
02425 
02426     // We will use three labels for the for loop
02427     int beforeLabel = nextLabel++;
02428     int afterLabel = nextLabel++;
02429     int continueLabel = nextLabel++;
02430 
02431     continueLabels.PushLast(continueLabel);
02432     breakLabels.PushLast(afterLabel);
02433 
02434     //---------------------------------------
02435     // Compile the initialization statement
02436     asCByteCode initBC(engine);
02437     if( fnode->firstChild->nodeType == snDeclaration )
02438         CompileDeclaration(fnode->firstChild, &initBC);
02439     else
02440         CompileExpressionStatement(fnode->firstChild, &initBC);
02441 
02442     //-----------------------------------
02443     // Compile the condition statement
02444     asSExprContext expr(engine);
02445     asCScriptNode *second = fnode->firstChild->next;
02446     if( second->firstChild )
02447     {
02448         int r = CompileAssignment(second->firstChild, &expr);
02449         if( r >= 0 )
02450         {
02451             if( !expr.type.dataType.IsEqualExceptRefAndConst(asCDataType::CreatePrimitive(ttBool, true)) )
02452                 Error(TXT_EXPR_MUST_BE_BOOL, second);
02453             else
02454             {
02455                 if( expr.type.dataType.IsReference() ) ConvertToVariable(&expr);
02456                 ProcessDeferredParams(&expr);
02457 
02458                 // If expression is false exit the loop
02459                 ConvertToVariable(&expr);
02460                 expr.bc.InstrSHORT(asBC_CpyVtoR4, expr.type.stackOffset);
02461                 expr.bc.Instr(asBC_ClrHi);
02462                 expr.bc.InstrDWORD(asBC_JZ, afterLabel);
02463                 ReleaseTemporaryVariable(expr.type, &expr.bc);
02464             }
02465         }
02466     }
02467 
02468     //---------------------------
02469     // Compile the increment statement
02470     asCByteCode nextBC(engine);
02471     asCScriptNode *third = second->next;
02472     if( third->nodeType == snExpressionStatement )
02473         CompileExpressionStatement(third, &nextBC);
02474 
02475     //------------------------------
02476     // Compile loop statement
02477     bool hasReturn;
02478     asCByteCode forBC(engine);
02479     CompileStatement(fnode->lastChild, &hasReturn, &forBC);
02480 
02481     //-------------------------------
02482     // Join the code pieces
02483     bc->AddCode(&initBC);
02484     bc->Label((short)beforeLabel);
02485 
02486     // Add a suspend bytecode inside the loop to guarantee
02487     // that the application can suspend the execution
02488     bc->Instr(asBC_SUSPEND);
02489     bc->InstrWORD(asBC_JitEntry, 0);
02490 
02491 
02492     bc->AddCode(&expr.bc);
02493     LineInstr(bc, fnode->lastChild->tokenPos);
02494     bc->AddCode(&forBC);
02495     bc->Label((short)continueLabel);
02496     bc->AddCode(&nextBC);
02497     bc->InstrINT(asBC_JMP, beforeLabel);
02498     bc->Label((short)afterLabel);
02499 
02500     continueLabels.PopLast();
02501     breakLabels.PopLast();
02502 
02503     // Deallocate variables in this block, in reverse order
02504     for( int n = (int)variables->variables.GetLength() - 1; n >= 0; n-- )
02505     {
02506         sVariable *v = variables->variables[n];
02507 
02508         // Call variable destructors here, for variables not yet destroyed
02509         CallDestructor(v->type, v->stackOffset, bc);
02510 
02511         // Don't deallocate function parameters
02512         if( v->stackOffset > 0 )
02513             DeallocateVariable(v->stackOffset);
02514     }
02515 
02516     RemoveVariableScope();
02517 }
02518 
02519 void asCCompiler::CompileWhileStatement(asCScriptNode *wnode, asCByteCode *bc)
02520 {
02521     // Add a variable scope that will be used by CompileBreak/Continue to know where to stop deallocating variables
02522     AddVariableScope(true, true);
02523 
02524     // We will use two labels for the while loop
02525     int beforeLabel = nextLabel++;
02526     int afterLabel = nextLabel++;
02527 
02528     continueLabels.PushLast(beforeLabel);
02529     breakLabels.PushLast(afterLabel);
02530 
02531     // Add label before the expression
02532     bc->Label((short)beforeLabel);
02533 
02534     // Compile expression
02535     asSExprContext expr(engine);
02536     CompileAssignment(wnode->firstChild, &expr);
02537     if( !expr.type.dataType.IsEqualExceptRefAndConst(asCDataType::CreatePrimitive(ttBool, true)) )
02538         Error(TXT_EXPR_MUST_BE_BOOL, wnode->firstChild);
02539 
02540     if( expr.type.dataType.IsReference() ) ConvertToVariable(&expr);
02541     ProcessDeferredParams(&expr);
02542 
02543     // Add byte code for the expression
02544     ConvertToVariable(&expr);
02545     bc->AddCode(&expr.bc);
02546 
02547     // Jump to end of statement if expression is false
02548     bc->InstrSHORT(asBC_CpyVtoR4, expr.type.stackOffset);
02549     bc->Instr(asBC_ClrHi);
02550     bc->InstrDWORD(asBC_JZ, afterLabel);
02551     ReleaseTemporaryVariable(expr.type, bc);
02552 
02553     // Add a suspend bytecode inside the loop to guarantee
02554     // that the application can suspend the execution
02555     bc->Instr(asBC_SUSPEND);
02556     bc->InstrWORD(asBC_JitEntry, 0);
02557 
02558     // Compile statement
02559     bool hasReturn;
02560     asCByteCode whileBC(engine);
02561     CompileStatement(wnode->lastChild, &hasReturn, &whileBC);
02562 
02563     // Add byte code for the statement
02564     LineInstr(bc, wnode->lastChild->tokenPos);
02565     bc->AddCode(&whileBC);
02566 
02567     // Jump to the expression
02568     bc->InstrINT(asBC_JMP, beforeLabel);
02569 
02570     // Add label after the statement
02571     bc->Label((short)afterLabel);
02572 
02573     continueLabels.PopLast();
02574     breakLabels.PopLast();
02575 
02576     RemoveVariableScope();
02577 }
02578 
02579 void asCCompiler::CompileDoWhileStatement(asCScriptNode *wnode, asCByteCode *bc)
02580 {
02581     // Add a variable scope that will be used by CompileBreak/Continue to know where to stop deallocating variables
02582     AddVariableScope(true, true);
02583 
02584     // We will use two labels for the while loop
02585     int beforeLabel = nextLabel++;
02586     int beforeTest = nextLabel++;
02587     int afterLabel = nextLabel++;
02588 
02589     continueLabels.PushLast(beforeTest);
02590     breakLabels.PushLast(afterLabel);
02591 
02592     // Add label before the statement
02593     bc->Label((short)beforeLabel);
02594 
02595     // Compile statement
02596     bool hasReturn;
02597     asCByteCode whileBC(engine);
02598     CompileStatement(wnode->firstChild, &hasReturn, &whileBC);
02599 
02600     // Add byte code for the statement
02601     LineInstr(bc, wnode->firstChild->tokenPos);
02602     bc->AddCode(&whileBC);
02603 
02604     // Add label before the expression
02605     bc->Label((short)beforeTest);
02606 
02607     // Add a suspend bytecode inside the loop to guarantee
02608     // that the application can suspend the execution
02609     bc->Instr(asBC_SUSPEND);
02610     bc->InstrWORD(asBC_JitEntry, 0);
02611 
02612     // Add a line instruction
02613     LineInstr(bc, wnode->lastChild->tokenPos);
02614 
02615     // Compile expression
02616     asSExprContext expr(engine);
02617     CompileAssignment(wnode->lastChild, &expr);
02618     if( !expr.type.dataType.IsEqualExceptRefAndConst(asCDataType::CreatePrimitive(ttBool, true)) )
02619         Error(TXT_EXPR_MUST_BE_BOOL, wnode->firstChild);
02620 
02621     if( expr.type.dataType.IsReference() ) ConvertToVariable(&expr);
02622     ProcessDeferredParams(&expr);
02623 
02624     // Add byte code for the expression
02625     ConvertToVariable(&expr);
02626     bc->AddCode(&expr.bc);
02627 
02628     // Jump to next iteration if expression is true
02629     bc->InstrSHORT(asBC_CpyVtoR4, expr.type.stackOffset);
02630     bc->Instr(asBC_ClrHi);
02631     bc->InstrDWORD(asBC_JNZ, beforeLabel);
02632     ReleaseTemporaryVariable(expr.type, bc);
02633 
02634     // Add label after the statement
02635     bc->Label((short)afterLabel);
02636 
02637     continueLabels.PopLast();
02638     breakLabels.PopLast();
02639 
02640     RemoveVariableScope();
02641 }
02642 
02643 void asCCompiler::CompileBreakStatement(asCScriptNode *node, asCByteCode *bc)
02644 {
02645     if( breakLabels.GetLength() == 0 )
02646     {
02647         Error(TXT_INVALID_BREAK, node);
02648         return;
02649     }
02650 
02651     // Add destructor calls for all variables that will go out of scope
02652     asCVariableScope *vs = variables;
02653     while( !vs->isBreakScope )
02654     {
02655         for( int n = (int)vs->variables.GetLength() - 1; n >= 0; n-- )
02656             CallDestructor(vs->variables[n]->type, vs->variables[n]->stackOffset, bc);
02657 
02658         vs = vs->parent;
02659     }
02660 
02661     bc->InstrINT(asBC_JMP, breakLabels[breakLabels.GetLength()-1]);
02662 }
02663 
02664 void asCCompiler::CompileContinueStatement(asCScriptNode *node, asCByteCode *bc)
02665 {
02666     if( continueLabels.GetLength() == 0 )
02667     {
02668         Error(TXT_INVALID_CONTINUE, node);
02669         return;
02670     }
02671 
02672     // Add destructor calls for all variables that will go out of scope
02673     asCVariableScope *vs = variables;
02674     while( !vs->isContinueScope )
02675     {
02676         for( int n = (int)vs->variables.GetLength() - 1; n >= 0; n-- )
02677             CallDestructor(vs->variables[n]->type, vs->variables[n]->stackOffset, bc);
02678 
02679         vs = vs->parent;
02680     }
02681 
02682     bc->InstrINT(asBC_JMP, continueLabels[continueLabels.GetLength()-1]);
02683 }
02684 
02685 void asCCompiler::CompileExpressionStatement(asCScriptNode *enode, asCByteCode *bc)
02686 {
02687     if( enode->firstChild )
02688     {
02689         // Compile the expression
02690         asSExprContext expr(engine);
02691         CompileAssignment(enode->firstChild, &expr);
02692 
02693         // Pop the value from the stack
02694         if( !expr.type.dataType.IsPrimitive() )
02695             expr.bc.Pop(expr.type.dataType.GetSizeOnStackDWords());
02696 
02697         // Release temporary variables used by expression
02698         ReleaseTemporaryVariable(expr.type, &expr.bc);
02699 
02700         ProcessDeferredParams(&expr);
02701 
02702         bc->AddCode(&expr.bc);
02703     }
02704 }
02705 
02706 void asCCompiler::PrepareTemporaryObject(asCScriptNode *node, asSExprContext *ctx, asCArray<int> *reservedVars)
02707 {
02708     // If the object already is stored in temporary variable then nothing needs to be done
02709     if( ctx->type.isTemporary ) return;
02710 
02711     // Allocate temporary variable
02712     asCDataType dt = ctx->type.dataType;
02713     dt.MakeReference(false);
02714     dt.MakeReadOnly(false);
02715 
02716     int offset = AllocateVariableNotIn(dt, true, reservedVars);
02717 
02718     // Allocate and construct the temporary object
02719     CallDefaultConstructor(dt, offset, &ctx->bc, node);
02720 
02721     // Assign the object to the temporary variable
02722     asCTypeInfo lvalue;
02723     dt.MakeReference(true);
02724     lvalue.Set(dt);
02725     lvalue.isTemporary = true;
02726     lvalue.stackOffset = (short)offset;
02727     lvalue.isVariable = true;
02728     lvalue.isExplicitHandle = ctx->type.isExplicitHandle;
02729 
02730     PrepareForAssignment(&lvalue.dataType, ctx, node);
02731 
02732     ctx->bc.InstrSHORT(asBC_PSF, (short)offset);
02733     PerformAssignment(&lvalue, &ctx->type, &ctx->bc, node);
02734 
02735     // Pop the original reference
02736     ctx->bc.Pop(AS_PTR_SIZE);
02737 
02738     // Push the reference to the temporary variable on the stack
02739     ctx->bc.InstrSHORT(asBC_PSF, (short)offset);
02740     lvalue.dataType.MakeReference(true);
02741 
02742     ctx->type = lvalue;
02743 }
02744 
02745 void asCCompiler::CompileReturnStatement(asCScriptNode *rnode, asCByteCode *bc)
02746 {
02747     // Get return type and location
02748     sVariable *v = variables->GetVariable("return");
02749     if( v->type.GetSizeOnStackDWords() > 0 )
02750     {
02751         // Is there an expression?
02752         if( rnode->firstChild )
02753         {
02754             // Compile the expression
02755             asSExprContext expr(engine);
02756             int r = CompileAssignment(rnode->firstChild, &expr);
02757             if( r >= 0 )
02758             {
02759                 // Prepare the value for assignment
02760                 IsVariableInitialized(&expr.type, rnode->firstChild);
02761 
02762                 if( v->type.IsPrimitive() )
02763                 {
02764                     if( expr.type.dataType.IsReference() ) ConvertToVariable(&expr);
02765 
02766                     // Implicitly convert the value to the return type
02767                     ImplicitConversion(&expr, v->type, rnode->firstChild, asIC_IMPLICIT_CONV);
02768 
02769                     // Verify that the conversion was successful
02770                     if( expr.type.dataType != v->type )
02771                     {
02772                         asCString str;
02773                         str.Format(TXT_NO_CONVERSION_s_TO_s, expr.type.dataType.Format().AddressOf(), v->type.Format().AddressOf());
02774                         Error(str.AddressOf(), rnode);
02775                         r = -1;
02776                     }
02777                     else
02778                     {
02779                         ConvertToVariable(&expr);
02780                         ReleaseTemporaryVariable(expr.type, &expr.bc);
02781 
02782                         // Load the variable in the register
02783                         if( v->type.GetSizeOnStackDWords() == 1 )
02784                             expr.bc.InstrSHORT(asBC_CpyVtoR4, expr.type.stackOffset);
02785                         else
02786                             expr.bc.InstrSHORT(asBC_CpyVtoR8, expr.type.stackOffset);
02787                     }
02788                 }
02789                 else if( v->type.IsObject() )
02790                 {
02791                     PrepareArgument(&v->type, &expr, rnode->firstChild);
02792 
02793                     // Pop the reference to the temporary variable again
02794                     expr.bc.Pop(AS_PTR_SIZE);
02795 
02796                     // Load the object pointer into the object register
02797                     // LOADOBJ also clears the address in the variable
02798                     expr.bc.InstrSHORT(asBC_LOADOBJ, expr.type.stackOffset);
02799 
02800                     // LOADOBJ cleared the address in the variable so the object will not be freed
02801                     // here, but the temporary variable must still be freed
02802 
02803                     // TODO: optimize: Since there is nothing in the variable anymore, 
02804                     //                 there is no need to call asBC_FREE on it. 
02805                 }
02806 
02807                 // Release temporary variables used by expression
02808                 ReleaseTemporaryVariable(expr.type, &expr.bc);
02809 
02810                 bc->AddCode(&expr.bc);
02811             }
02812         }
02813         else
02814             Error(TXT_MUST_RETURN_VALUE, rnode);
02815     }
02816     else
02817         if( rnode->firstChild )
02818             Error(TXT_CANT_RETURN_VALUE, rnode);
02819 
02820     // Call destructor on all variables except for the function parameters
02821     asCVariableScope *vs = variables;
02822     while( vs )
02823     {
02824         for( int n = (int)vs->variables.GetLength() - 1; n >= 0; n-- )
02825             if( vs->variables[n]->stackOffset > 0 )
02826                 CallDestructor(vs->variables[n]->type, vs->variables[n]->stackOffset, bc);
02827 
02828         vs = vs->parent;
02829     }
02830 
02831     // Jump to the end of the function
02832     bc->InstrINT(asBC_JMP, 0);
02833 }
02834 
02835 void asCCompiler::AddVariableScope(bool isBreakScope, bool isContinueScope)
02836 {
02837     variables = asNEW(asCVariableScope)(variables);
02838     variables->isBreakScope    = isBreakScope;
02839     variables->isContinueScope = isContinueScope;
02840 }
02841 
02842 void asCCompiler::RemoveVariableScope()
02843 {
02844     if( variables )
02845     {
02846         asCVariableScope *var = variables;
02847         variables = variables->parent;
02848         asDELETE(var,asCVariableScope);
02849     }
02850 }
02851 
02852 void asCCompiler::Error(const char *msg, asCScriptNode *node)
02853 {
02854     asCString str;
02855 
02856     int r, c;
02857     script->ConvertPosToRowCol(node->tokenPos, &r, &c);
02858 
02859     builder->WriteError(script->name.AddressOf(), msg, r, c);
02860 
02861     hasCompileErrors = true;
02862 }
02863 
02864 void asCCompiler::Warning(const char *msg, asCScriptNode *node)
02865 {
02866     asCString str;
02867 
02868     int r, c;
02869     script->ConvertPosToRowCol(node->tokenPos, &r, &c);
02870 
02871     builder->WriteWarning(script->name.AddressOf(), msg, r, c);
02872 }
02873 
02874 void asCCompiler::PrintMatchingFuncs(asCArray<int> &funcs, asCScriptNode *node)
02875 {
02876     int r, c;
02877     script->ConvertPosToRowCol(node->tokenPos, &r, &c);
02878 
02879     for( unsigned int n = 0; n < funcs.GetLength(); n++ )
02880     {
02881         asIScriptFunction *func = engine->scriptFunctions[funcs[n]];
02882 
02883         builder->WriteInfo(script->name.AddressOf(), func->GetDeclaration(true), r, c, false);
02884     }
02885 }
02886 
02887 int asCCompiler::AllocateVariable(const asCDataType &type, bool isTemporary)
02888 {
02889     return AllocateVariableNotIn(type, isTemporary, 0);
02890 }
02891 
02892 int asCCompiler::AllocateVariableNotIn(const asCDataType &type, bool isTemporary, asCArray<int> *vars)
02893 {
02894     asCDataType t(type);
02895 
02896     if( t.IsPrimitive() && t.GetSizeOnStackDWords() == 1 )
02897         t.SetTokenType(ttInt);
02898 
02899     if( t.IsPrimitive() && t.GetSizeOnStackDWords() == 2 )
02900         t.SetTokenType(ttDouble);
02901 
02902     // Find a free location with the same type
02903     for( asUINT n = 0; n < freeVariables.GetLength(); n++ )
02904     {
02905         int slot = freeVariables[n];
02906         if( variableAllocations[slot].IsEqualExceptConst(t) && variableIsTemporary[slot] == isTemporary )
02907         {
02908             // We can't return by slot, must count variable sizes
02909             int offset = GetVariableOffset(slot);
02910 
02911             // Verify that it is not in the list of used variables
02912             bool isUsed = false;
02913             if( vars )
02914             {
02915                 for( asUINT m = 0; m < vars->GetLength(); m++ )
02916                 {
02917                     if( offset == (*vars)[m] )
02918                     {
02919                         isUsed = true;
02920                         break;
02921                     }
02922                 }
02923             }
02924 
02925             if( !isUsed )
02926             {
02927                 if( n != freeVariables.GetLength() - 1 )
02928                     freeVariables[n] = freeVariables.PopLast();
02929                 else
02930                     freeVariables.PopLast();
02931 
02932                 if( isTemporary )
02933                     tempVariables.PushLast(offset);
02934 
02935                 return offset;
02936             }
02937         }
02938     }
02939 
02940     variableAllocations.PushLast(t);
02941     variableIsTemporary.PushLast(isTemporary);
02942 
02943     int offset = GetVariableOffset((int)variableAllocations.GetLength()-1);
02944 
02945     if( isTemporary )
02946         tempVariables.PushLast(offset);
02947 
02948     return offset;
02949 }
02950 
02951 int asCCompiler::GetVariableOffset(int varIndex)
02952 {
02953     // Return offset to the last dword on the stack
02954     int varOffset = 1;
02955     for( int n = 0; n < varIndex; n++ )
02956         varOffset += variableAllocations[n].GetSizeOnStackDWords();
02957 
02958     if( varIndex < (int)variableAllocations.GetLength() )
02959     {
02960         int size = variableAllocations[varIndex].GetSizeOnStackDWords();
02961         if( size > 1 )
02962             varOffset += size-1;
02963     }
02964 
02965     return varOffset;
02966 }
02967 
02968 int asCCompiler::GetVariableSlot(int offset)
02969 {
02970     int varOffset = 1;
02971     for( asUINT n = 0; n < variableAllocations.GetLength(); n++ )
02972     {
02973         varOffset += -1 + variableAllocations[n].GetSizeOnStackDWords();
02974         if( varOffset == offset )
02975         {
02976             return n;
02977         }
02978         varOffset++;
02979     }
02980 
02981     return -1;
02982 }
02983 
02984 void asCCompiler::DeallocateVariable(int offset)
02985 {
02986     // Remove temporary variable
02987     int n;
02988     for( n = 0; n < (int)tempVariables.GetLength(); n++ )
02989     {
02990         if( offset == tempVariables[n] )
02991         {
02992             if( n == (int)tempVariables.GetLength()-1 )
02993                 tempVariables.PopLast();
02994             else
02995                 tempVariables[n] = tempVariables.PopLast();
02996             break;
02997         }
02998     }
02999 
03000     n = GetVariableSlot(offset);
03001     if( n != -1 )
03002     {
03003         freeVariables.PushLast(n);
03004         return;
03005     }
03006 
03007     // We might get here if the variable was implicitly declared
03008     // because it was use before a formal declaration, in this case
03009     // the offset is 0x7FFF
03010 
03011     asASSERT(offset == 0x7FFF);
03012 }
03013 
03014 void asCCompiler::ReleaseTemporaryVariable(asCTypeInfo &t, asCByteCode *bc)
03015 {
03016     if( t.isTemporary )
03017     {
03018         if( bc )
03019         {
03020             // We need to call the destructor on the true variable type
03021             int n = GetVariableSlot(t.stackOffset);
03022             asCDataType dt = variableAllocations[n];
03023 
03024             // Call destructor
03025             CallDestructor(dt, t.stackOffset, bc);
03026         }
03027 
03028         DeallocateVariable(t.stackOffset);
03029         t.isTemporary = false;
03030     }
03031 }
03032 
03033 void asCCompiler::ReleaseTemporaryVariable(int offset, asCByteCode *bc)
03034 {
03035     if( bc )
03036     {
03037         // We need to call the destructor on the true variable type
03038         int n = GetVariableSlot(offset);
03039         asCDataType dt = variableAllocations[n];
03040 
03041         // Call destructor
03042         CallDestructor(dt, offset, bc);
03043     }
03044 
03045     DeallocateVariable(offset);
03046 }
03047 
03048 void asCCompiler::Dereference(asSExprContext *ctx, bool generateCode)
03049 {
03050     if( ctx->type.dataType.IsReference() )
03051     {
03052         if( ctx->type.dataType.IsObject() )
03053         {
03054             ctx->type.dataType.MakeReference(false);
03055             if( generateCode )
03056             {
03057                 ctx->bc.Instr(asBC_CHKREF);
03058                 ctx->bc.Instr(asBC_RDSPTR);
03059             }
03060         }
03061         else
03062         {
03063             // This should never happen as primitives are treated differently
03064             asASSERT(false);
03065         }
03066     }
03067 }
03068 
03069 
03070 bool asCCompiler::IsVariableInitialized(asCTypeInfo *type, asCScriptNode *node)
03071 {
03072     // Temporary variables are assumed to be initialized
03073     if( type->isTemporary ) return true;
03074 
03075     // Verify that it is a variable
03076     if( !type->isVariable ) return true;
03077 
03078     // Find the variable
03079     sVariable *v = variables->GetVariableByOffset(type->stackOffset);
03080 
03081     // The variable isn't found if it is a constant, in which case it is guaranteed to be initialized
03082     if( v == 0 ) return true;
03083 
03084     if( v->isInitialized ) return true;
03085 
03086     // Complex types don't need this test
03087     if( v->type.IsObject() ) return true;
03088 
03089     // Mark as initialized so that the user will not be bothered again
03090     v->isInitialized = true;
03091 
03092     // Write warning
03093     asCString str;
03094     str.Format(TXT_s_NOT_INITIALIZED, (const char *)v->name.AddressOf());
03095     Warning(str.AddressOf(), node);
03096 
03097     return false;
03098 }
03099 
03100 void asCCompiler::PrepareOperand(asSExprContext *ctx, asCScriptNode *node)
03101 {
03102     // Check if the variable is initialized (if it indeed is a variable)
03103     IsVariableInitialized(&ctx->type, node);
03104 
03105     asCDataType to = ctx->type.dataType;
03106     to.MakeReference(false);
03107 
03108     ImplicitConversion(ctx, to, node, asIC_IMPLICIT_CONV);
03109 
03110     ProcessDeferredParams(ctx);
03111 }
03112 
03113 void asCCompiler::PrepareForAssignment(asCDataType *lvalue, asSExprContext *rctx, asCScriptNode *node, asSExprContext *lvalueExpr)
03114 {
03115     ProcessPropertyGetAccessor(rctx, node);
03116 
03117     // Make sure the rvalue is initialized if it is a variable
03118     IsVariableInitialized(&rctx->type, node);
03119 
03120     if( lvalue->IsPrimitive() )
03121     {
03122         if( rctx->type.dataType.IsPrimitive() )
03123         {
03124             if( rctx->type.dataType.IsReference() )
03125             {
03126                 // Cannot do implicit conversion of references so we first convert the reference to a variable
03127                 ConvertToVariableNotIn(rctx, lvalueExpr);
03128             }
03129         }
03130 
03131         // Implicitly convert the value to the right type
03132         asCArray<int> usedVars;
03133         if( lvalueExpr ) lvalueExpr->bc.GetVarsUsed(usedVars);
03134         ImplicitConversion(rctx, *lvalue, node, asIC_IMPLICIT_CONV, true, &usedVars);
03135 
03136         // Check data type
03137         if( !lvalue->IsEqualExceptRefAndConst(rctx->type.dataType) )
03138         {
03139             asCString str;
03140             str.Format(TXT_CANT_IMPLICITLY_CONVERT_s_TO_s, rctx->type.dataType.Format().AddressOf(), lvalue->Format().AddressOf());
03141             Error(str.AddressOf(), node);
03142 
03143             rctx->type.SetDummy();
03144         }
03145 
03146         // Make sure the rvalue is a variable
03147         if( !rctx->type.isVariable )
03148             ConvertToVariableNotIn(rctx, lvalueExpr);
03149     }
03150     else
03151     {
03152         asCDataType to = *lvalue;
03153         to.MakeReference(false);
03154 
03155         // TODO: ImplicitConversion should know to do this by itself
03156         // First convert to a handle which will to a reference cast
03157         if( !lvalue->IsObjectHandle() &&
03158             (lvalue->GetObjectType()->flags & asOBJ_SCRIPT_OBJECT) )
03159             to.MakeHandle(true);
03160 
03161         // Don't allow the implicit conversion to create an object
03162         ImplicitConversion(rctx, to, node, asIC_IMPLICIT_CONV, true, 0, false);
03163 
03164         if( !lvalue->IsObjectHandle() &&
03165             (lvalue->GetObjectType()->flags & asOBJ_SCRIPT_OBJECT) )
03166         {
03167             // Then convert to a reference, which will validate the handle
03168             to.MakeHandle(false);
03169             ImplicitConversion(rctx, to, node, asIC_IMPLICIT_CONV, true, 0, false);
03170         }
03171 
03172         // Check data type
03173         if( !lvalue->IsEqualExceptRefAndConst(rctx->type.dataType) )
03174         {
03175             asCString str;
03176             str.Format(TXT_CANT_IMPLICITLY_CONVERT_s_TO_s, rctx->type.dataType.Format().AddressOf(), lvalue->Format().AddressOf());
03177             Error(str.AddressOf(), node);
03178         }
03179         else
03180         {
03181             // If the assignment will be made with the copy behaviour then the rvalue must not be a reference
03182             if( lvalue->IsObject() )
03183                 asASSERT(!rctx->type.dataType.IsReference());
03184         }
03185     }
03186 }
03187 
03188 bool asCCompiler::IsLValue(asCTypeInfo &type)
03189 {
03190     if( type.dataType.IsReadOnly() ) return false;
03191     if( !type.dataType.IsObject() && !type.isVariable && !type.dataType.IsReference() ) return false;
03192     if( type.isTemporary ) return false;
03193     return true;
03194 }
03195 
03196 void asCCompiler::PerformAssignment(asCTypeInfo *lvalue, asCTypeInfo *rvalue, asCByteCode *bc, asCScriptNode *node)
03197 {
03198     if( lvalue->dataType.IsReadOnly() )
03199         Error(TXT_REF_IS_READ_ONLY, node);
03200 
03201     if( lvalue->dataType.IsPrimitive() )
03202     {
03203         if( lvalue->isVariable )
03204         {
03205             // Copy the value between the variables directly
03206             if( lvalue->dataType.GetSizeInMemoryDWords() == 1 )
03207                 bc->InstrW_W(asBC_CpyVtoV4, lvalue->stackOffset, rvalue->stackOffset);
03208             else
03209                 bc->InstrW_W(asBC_CpyVtoV8, lvalue->stackOffset, rvalue->stackOffset);
03210 
03211             // Mark variable as initialized
03212             sVariable *v = variables->GetVariableByOffset(lvalue->stackOffset);
03213             if( v ) v->isInitialized = true;
03214         }
03215         else if( lvalue->dataType.IsReference() )
03216         {
03217             // Copy the value of the variable to the reference in the register
03218             int s = lvalue->dataType.GetSizeInMemoryBytes();
03219             if( s == 1 )
03220                 bc->InstrSHORT(asBC_WRTV1, rvalue->stackOffset);
03221             else if( s == 2 )
03222                 bc->InstrSHORT(asBC_WRTV2, rvalue->stackOffset);
03223             else if( s == 4 )
03224                 bc->InstrSHORT(asBC_WRTV4, rvalue->stackOffset);
03225             else if( s == 8 )
03226                 bc->InstrSHORT(asBC_WRTV8, rvalue->stackOffset);
03227         }
03228         else
03229         {
03230             Error(TXT_NOT_VALID_LVALUE, node);
03231             return;
03232         }
03233     }
03234     else if( !lvalue->isExplicitHandle )
03235     {
03236         // TODO: Call the assignment operator, or do a BC_COPY if none exist
03237 
03238         asSExprContext ctx(engine);
03239         ctx.type = *lvalue;
03240         Dereference(&ctx, true);
03241         *lvalue = ctx.type;
03242         bc->AddCode(&ctx.bc);
03243 
03244         // TODO: Can't this leave deferred output params unhandled?
03245 
03246         // TODO: Should find the opAssign method that implements the default copy behaviour.
03247         //       The beh->copy member will be removed.
03248         asSTypeBehaviour *beh = lvalue->dataType.GetBehaviour();
03249         if( beh->copy )
03250         {
03251             // Call the copy operator
03252             bc->Call(asBC_CALLSYS, (asDWORD)beh->copy, 2*AS_PTR_SIZE);
03253             bc->Instr(asBC_PshRPtr);
03254         }
03255         else
03256         {
03257             // Default copy operator
03258             if( lvalue->dataType.GetSizeInMemoryDWords() == 0 ||
03259                 !(lvalue->dataType.GetObjectType()->flags & asOBJ_POD) )
03260             {
03261                 Error(TXT_NO_DEFAULT_COPY_OP, node);
03262             }
03263 
03264             // Copy larger data types from a reference
03265             bc->InstrWORD(asBC_COPY, (asWORD)lvalue->dataType.GetSizeInMemoryDWords());
03266         }
03267     }
03268     else
03269     {
03270         // TODO: The object handle can be stored in a variable as well
03271         if( !lvalue->dataType.IsReference() )
03272         {
03273             Error(TXT_NOT_VALID_REFERENCE, node);
03274             return;
03275         }
03276 
03277         // TODO: Convert to register based
03278         bc->InstrPTR(asBC_REFCPY, lvalue->dataType.GetObjectType());
03279 
03280         // Mark variable as initialized
03281         if( variables )
03282         {
03283             sVariable *v = variables->GetVariableByOffset(lvalue->stackOffset);
03284             if( v ) v->isInitialized = true;
03285         }
03286     }
03287 }
03288 
03289 bool asCCompiler::CompileRefCast(asSExprContext *ctx, const asCDataType &to, bool isExplicit, asCScriptNode *node, bool generateCode)
03290 {
03291     bool conversionDone = false;
03292 
03293     asCArray<int> ops;
03294     asUINT n;
03295 
03296     if( ctx->type.dataType.GetObjectType()->flags & asOBJ_SCRIPT_OBJECT )
03297     {
03298         // We need it to be a reference
03299         if( !ctx->type.dataType.IsReference() )
03300         {
03301             asCDataType to = ctx->type.dataType;
03302             to.MakeReference(true);
03303             ImplicitConversion(ctx, to, 0, isExplicit ? asIC_EXPLICIT_REF_CAST : asIC_IMPLICIT_CONV, generateCode);
03304         }
03305 
03306         if( isExplicit )
03307         {
03308             // Allow dynamic cast between object handles (only for script objects).
03309             // At run time this may result in a null handle,
03310             // which when used will throw an exception
03311             conversionDone = true;
03312             if( generateCode )
03313             {
03314                 ctx->bc.InstrDWORD(asBC_Cast, engine->GetTypeIdFromDataType(to));
03315 
03316                 // Allocate a temporary variable for the returned object
03317                 int returnOffset = AllocateVariable(to, true);
03318 
03319                 // Move the pointer from the object register to the temporary variable
03320                 ctx->bc.InstrSHORT(asBC_STOREOBJ, (short)returnOffset);
03321 
03322                 ctx->bc.InstrSHORT(asBC_PSF, (short)returnOffset);
03323 
03324                 ReleaseTemporaryVariable(ctx->type, &ctx->bc);
03325 
03326                 ctx->type.SetVariable(to, returnOffset, true);
03327                 ctx->type.dataType.MakeReference(true);
03328             }
03329             else
03330             {
03331                 ctx->type.dataType = to;
03332                 ctx->type.dataType.MakeReference(true);
03333             }
03334         }
03335         else
03336         {
03337             if( ctx->type.dataType.GetObjectType()->DerivesFrom(to.GetObjectType()) )
03338             {
03339                 conversionDone = true;
03340                 ctx->type.dataType.SetObjectType(to.GetObjectType());
03341             }
03342         }
03343     }
03344     else
03345     {
03346         // Find a suitable registered behaviour
03347         asSTypeBehaviour *beh = &ctx->type.dataType.GetObjectType()->beh;
03348         for( n = 0; n < beh->operators.GetLength(); n+= 2 )
03349         {
03350             if( (isExplicit && asBEHAVE_REF_CAST == beh->operators[n]) ||
03351                 asBEHAVE_IMPLICIT_REF_CAST == beh->operators[n] )
03352             {
03353                 int funcId = beh->operators[n+1];
03354 
03355                 // Is the operator for the output type?
03356                 asCScriptFunction *func = engine->scriptFunctions[funcId];
03357                 if( func->returnType.GetObjectType() != to.GetObjectType() )
03358                     continue;
03359 
03360                 ops.PushLast(funcId);
03361             }
03362         }
03363 
03364         // Should only have one behaviour for each output type
03365         if( ops.GetLength() == 1 )
03366         {
03367             if( generateCode )
03368             {
03369                 // Merge the bytecode so that it forms obj.castBehave()
03370                 asCTypeInfo objType = ctx->type;
03371                 asCArray<asSExprContext *> args;
03372                 MakeFunctionCall(ctx, ops[0], objType.dataType.GetObjectType(), args, node);
03373 
03374                 // Since we're receiving a handle, we can release the original variable
03375                 ReleaseTemporaryVariable(objType, &ctx->bc);
03376             }
03377             else
03378             {
03379                 asCScriptFunction *func = engine->scriptFunctions[ops[0]];
03380                 ctx->type.Set(func->returnType);
03381             }
03382         }
03383         else if( ops.GetLength() > 1 )
03384         {
03385             // It shouldn't be possible to have more than one, should it?
03386             asASSERT( false );
03387         }
03388 #ifdef AS_DEPRECATED
03389 // deprecated since 2009-07-20, 2.17.0
03390         else
03391         {
03392             // Find a suitable registered behaviour
03393             for( n = 0; n < engine->globalBehaviours.operators.GetLength(); n+= 2 )
03394             {
03395                 if( (isExplicit && asBEHAVE_REF_CAST == engine->globalBehaviours.operators[n]) ||
03396                     asBEHAVE_IMPLICIT_REF_CAST == engine->globalBehaviours.operators[n] )
03397                 {
03398                     int funcId = engine->globalBehaviours.operators[n+1];
03399 
03400                     // Is the operator for the input type?
03401                     asCScriptFunction *func = engine->scriptFunctions[funcId];
03402                     if( func->parameterTypes[0].GetObjectType() != ctx->type.dataType.GetObjectType() )
03403                         continue;
03404 
03405                     // Is the operator for the output type?
03406                     if( func->returnType.GetObjectType() != to.GetObjectType() )
03407                         continue;
03408 
03409                     // Find the config group for the global function
03410                     asCConfigGroup *group = engine->FindConfigGroupForFunction(funcId);
03411                     if( !group || group->HasModuleAccess(builder->module->name.AddressOf()) )
03412                         ops.PushLast(funcId);
03413                 }
03414             }
03415 
03416             // Find the best match for the argument
03417             asCArray<int> ops1;
03418             MatchArgument(ops, ops1, &ctx->type, 0);
03419 
03420             // Did we find a unique suitable operator?
03421             if( ops1.GetLength() == 1 )
03422             {
03423                 conversionDone = true;
03424                 asCScriptFunction *descr = engine->scriptFunctions[ops1[0]];
03425 
03426                 if( generateCode )
03427                 {
03428                     // Add code for argument
03429                     asSExprContext expr(engine);
03430                     MergeExprContexts(&expr, ctx);
03431                     expr.type = ctx->type;
03432                     PrepareArgument2(ctx, &expr, &descr->parameterTypes[0], true, descr->inOutFlags[0]);
03433 
03434                     asCArray<asSExprContext*> args(1);
03435                     args.PushLast(&expr);
03436 
03437                     MoveArgsToStack(descr->id, &ctx->bc, args, false);
03438 
03439                     PerformFunctionCall(descr->id, ctx, false, &args);
03440                 }
03441                 else
03442                 {
03443                     ctx->type.Set(descr->returnType);
03444                 }
03445             }
03446         }
03447 #endif
03448     }
03449 
03450     return conversionDone;
03451 }
03452 
03453 
03454 // TODO: Re-think the implementation for implicit conversions
03455 //       It's currently inefficient and may at times generate unneeded copies of objects
03456 //       There are also too many different code paths to test, each working slightly differently
03457 //
03458 //       Reference and handle-of should be treated last
03459 //
03460 //       - The following conversion categories needs to be implemented in separate functions
03461 //         - primitive to primitive
03462 //         - primitive to value type
03463 //         - primitive to reference type
03464 //         - value type to value type
03465 //         - value type to primitive
03466 //         - value type to reference type
03467 //         - reference type to reference type
03468 //         - reference type to primitive
03469 //         - reference type to value type
03470 //
03471 //       Explicit conversion and implicit conversion should use the same functions, only with a flag to enable/disable conversions
03472 //
03473 //       If the conversion fails, the type in the asSExprContext must not be modified. This
03474 //       causes problems where the conversion is partially done and the compiler continues with
03475 //       another option.
03476 
03477 void asCCompiler::ImplicitConvPrimitiveToPrimitive(asSExprContext *ctx, const asCDataType &to, asCScriptNode *node, EImplicitConv convType, bool generateCode, asCArray<int> *reservedVars)
03478 {
03479     // Start by implicitly converting constant values
03480     if( ctx->type.isConstant )
03481         ImplicitConversionConstant(ctx, to, node, convType);
03482 
03483     if( to == ctx->type.dataType )
03484         return;
03485 
03486     // After the constant value has been converted we have the following possibilities
03487 
03488     // Allow implicit conversion between numbers
03489     if( generateCode )
03490     {
03491         // Convert smaller types to 32bit first
03492         int s = ctx->type.dataType.GetSizeInMemoryBytes();
03493         if( s < 4 )
03494         {
03495             ConvertToTempVariableNotIn(ctx, reservedVars);
03496             if( ctx->type.dataType.IsIntegerType() )
03497             {
03498                 if( s == 1 )
03499                     ctx->bc.InstrSHORT(asBC_sbTOi, ctx->type.stackOffset);
03500                 else if( s == 2 )
03501                     ctx->bc.InstrSHORT(asBC_swTOi, ctx->type.stackOffset);
03502                 ctx->type.dataType.SetTokenType(ttInt);
03503             }
03504             else if( ctx->type.dataType.IsUnsignedType() )
03505             {
03506                 if( s == 1 )
03507                     ctx->bc.InstrSHORT(asBC_ubTOi, ctx->type.stackOffset);
03508                 else if( s == 2 )
03509                     ctx->bc.InstrSHORT(asBC_uwTOi, ctx->type.stackOffset);
03510                 ctx->type.dataType.SetTokenType(ttUInt);
03511             }
03512         }
03513 
03514         if( (to.IsIntegerType() && to.GetSizeInMemoryDWords() == 1) ||
03515             (to.IsEnumType() && convType == asIC_EXPLICIT_VAL_CAST) )
03516         {
03517             if( ctx->type.dataType.IsIntegerType() ||
03518                 ctx->type.dataType.IsUnsignedType() ||
03519                 ctx->type.dataType.IsEnumType() )
03520             {
03521                 if( ctx->type.dataType.GetSizeInMemoryDWords() == 1 )
03522                 {
03523                     ctx->type.dataType.SetTokenType(to.GetTokenType());
03524                     ctx->type.dataType.SetObjectType(to.GetObjectType());
03525                 }
03526                 else
03527                 {
03528                     ConvertToTempVariableNotIn(ctx, reservedVars);
03529                     ReleaseTemporaryVariable(ctx->type, &ctx->bc);
03530                     int offset = AllocateVariableNotIn(to, true, reservedVars);
03531                     ctx->bc.InstrW_W(asBC_i64TOi, offset, ctx->type.stackOffset);
03532                     ctx->type.SetVariable(to, offset, true);
03533                 }
03534             }
03535             else if( ctx->type.dataType.IsFloatType() )
03536             {
03537                 ConvertToTempVariableNotIn(ctx, reservedVars);
03538                 ctx->bc.InstrSHORT(asBC_fTOi, ctx->type.stackOffset);
03539                 ctx->type.dataType.SetTokenType(to.GetTokenType());
03540                 ctx->type.dataType.SetObjectType(to.GetObjectType());
03541             }
03542             else if( ctx->type.dataType.IsDoubleType() )
03543             {
03544                 ConvertToTempVariableNotIn(ctx, reservedVars);
03545                 ReleaseTemporaryVariable(ctx->type, &ctx->bc);
03546                 int offset = AllocateVariableNotIn(to, true, reservedVars);
03547                 ctx->bc.InstrW_W(asBC_dTOi, offset, ctx->type.stackOffset);
03548                 ctx->type.SetVariable(to, offset, true);
03549             }
03550 
03551             // Convert to smaller integer if necessary
03552             int s = to.GetSizeInMemoryBytes();
03553             if( s < 4 )
03554             {
03555                 ConvertToTempVariableNotIn(ctx, reservedVars);
03556                 if( s == 1 )
03557                     ctx->bc.InstrSHORT(asBC_iTOb, ctx->type.stackOffset);
03558                 else if( s == 2 )
03559                     ctx->bc.InstrSHORT(asBC_iTOw, ctx->type.stackOffset);
03560             }
03561         }
03562         if( to.IsIntegerType() && to.GetSizeInMemoryDWords() == 2 )
03563         {
03564             if( ctx->type.dataType.IsIntegerType() ||
03565                 ctx->type.dataType.IsUnsignedType() ||
03566                 ctx->type.dataType.IsEnumType() )
03567             {
03568                 if( ctx->type.dataType.GetSizeInMemoryDWords() == 2 )
03569                 {
03570                     ctx->type.dataType.SetTokenType(to.GetTokenType());
03571                     ctx->type.dataType.SetObjectType(to.GetObjectType());
03572                 }
03573                 else
03574                 {
03575                     ConvertToTempVariableNotIn(ctx, reservedVars);
03576                     ReleaseTemporaryVariable(ctx->type, &ctx->bc);
03577                     int offset = AllocateVariableNotIn(to, true, reservedVars);
03578                     if( ctx->type.dataType.IsUnsignedType() )
03579                         ctx->bc.InstrW_W(asBC_uTOi64, offset, ctx->type.stackOffset);
03580                     else
03581                         ctx->bc.InstrW_W(asBC_iTOi64, offset, ctx->type.stackOffset);
03582                     ctx->type.SetVariable(to, offset, true);
03583                 }
03584             }
03585             else if( ctx->type.dataType.IsFloatType() )
03586             {
03587                 ConvertToTempVariableNotIn(ctx, reservedVars);
03588                 ReleaseTemporaryVariable(ctx->type, &ctx->bc);
03589                 int offset = AllocateVariableNotIn(to, true, reservedVars);
03590                 ctx->bc.InstrW_W(asBC_fTOi64, offset, ctx->type.stackOffset);
03591                 ctx->type.SetVariable(to, offset, true);
03592             }
03593             else if( ctx->type.dataType.IsDoubleType() )
03594             {
03595                 ConvertToTempVariableNotIn(ctx, reservedVars);
03596                 ctx->bc.InstrSHORT(asBC_dTOi64, ctx->type.stackOffset);
03597                 ctx->type.dataType.SetTokenType(to.GetTokenType());
03598                 ctx->type.dataType.SetObjectType(to.GetObjectType());
03599             }
03600         }
03601         else if( to.IsUnsignedType() && to.GetSizeInMemoryDWords() == 1  )
03602         {
03603             if( ctx->type.dataType.IsIntegerType() ||
03604                 ctx->type.dataType.IsUnsignedType() ||
03605                 ctx->type.dataType.IsEnumType() )
03606             {
03607                 if( ctx->type.dataType.GetSizeInMemoryDWords() == 1 )
03608                 {
03609                     ctx->type.dataType.SetTokenType(to.GetTokenType());
03610                     ctx->type.dataType.SetObjectType(to.GetObjectType());
03611                 }
03612                 else
03613                 {
03614                     ConvertToTempVariableNotIn(ctx, reservedVars);
03615                     ReleaseTemporaryVariable(ctx->type, &ctx->bc);
03616                     int offset = AllocateVariableNotIn(to, true, reservedVars);
03617                     ctx->bc.InstrW_W(asBC_i64TOi, offset, ctx->type.stackOffset);
03618                     ctx->type.SetVariable(to, offset, true);
03619                 }
03620             }
03621             else if( ctx->type.dataType.IsFloatType() )
03622             {
03623                 ConvertToTempVariableNotIn(ctx, reservedVars);
03624                 ctx->bc.InstrSHORT(asBC_fTOu, ctx->type.stackOffset);
03625                 ctx->type.dataType.SetTokenType(to.GetTokenType());
03626                 ctx->type.dataType.SetObjectType(to.GetObjectType());
03627             }
03628             else if( ctx->type.dataType.IsDoubleType() )
03629             {
03630                 ConvertToTempVariableNotIn(ctx, reservedVars);
03631                 ReleaseTemporaryVariable(ctx->type, &ctx->bc);
03632                 int offset = AllocateVariableNotIn(to, true, reservedVars);
03633                 ctx->bc.InstrW_W(asBC_dTOu, offset, ctx->type.stackOffset);
03634                 ctx->type.SetVariable(to, offset, true);
03635             }
03636 
03637             // Convert to smaller integer if necessary
03638             int s = to.GetSizeInMemoryBytes();
03639             if( s < 4 )
03640             {
03641                 ConvertToTempVariableNotIn(ctx, reservedVars);
03642                 if( s == 1 )
03643                     ctx->bc.InstrSHORT(asBC_iTOb, ctx->type.stackOffset);
03644                 else if( s == 2 )
03645                     ctx->bc.InstrSHORT(asBC_iTOw, ctx->type.stackOffset);
03646             }
03647         }
03648         if( to.IsUnsignedType() && to.GetSizeInMemoryDWords() == 2 )
03649         {
03650             if( ctx->type.dataType.IsIntegerType() ||
03651                 ctx->type.dataType.IsUnsignedType() ||
03652                 ctx->type.dataType.IsEnumType() )
03653             {
03654                 if( ctx->type.dataType.GetSizeInMemoryDWords() == 2 )
03655                 {
03656                     ctx->type.dataType.SetTokenType(to.GetTokenType());
03657                     ctx->type.dataType.SetObjectType(to.GetObjectType());
03658                 }
03659                 else
03660                 {
03661                     ConvertToTempVariableNotIn(ctx, reservedVars);
03662                     ReleaseTemporaryVariable(ctx->type, &ctx->bc);
03663                     int offset = AllocateVariableNotIn(to, true, reservedVars);
03664                     if( ctx->type.dataType.IsUnsignedType() )
03665                         ctx->bc.InstrW_W(asBC_uTOi64, offset, ctx->type.stackOffset);
03666                     else
03667                         ctx->bc.InstrW_W(asBC_iTOi64, offset, ctx->type.stackOffset);
03668                     ctx->type.SetVariable(to, offset, true);
03669                 }
03670             }
03671             else if( ctx->type.dataType.IsFloatType() )
03672             {
03673                 ConvertToTempVariableNotIn(ctx, reservedVars);
03674                 ReleaseTemporaryVariable(ctx->type, &ctx->bc);
03675                 int offset = AllocateVariableNotIn(to, true, reservedVars);
03676                 ctx->bc.InstrW_W(asBC_fTOu64, offset, ctx->type.stackOffset);
03677                 ctx->type.SetVariable(to, offset, true);
03678             }
03679             else if( ctx->type.dataType.IsDoubleType() )
03680             {
03681                 ConvertToTempVariableNotIn(ctx, reservedVars);
03682                 ctx->bc.InstrSHORT(asBC_dTOu64, ctx->type.stackOffset);
03683                 ctx->type.dataType.SetTokenType(to.GetTokenType());
03684                 ctx->type.dataType.SetObjectType(to.GetObjectType());
03685             }
03686         }
03687         else if( to.IsFloatType() )
03688         {
03689             if( (ctx->type.dataType.IsIntegerType() || ctx->type.dataType.IsEnumType()) && ctx->type.dataType.GetSizeInMemoryDWords() == 1 )
03690             {
03691                 ConvertToTempVariableNotIn(ctx, reservedVars);
03692                 ctx->bc.InstrSHORT(asBC_iTOf, ctx->type.stackOffset);
03693                 ctx->type.dataType.SetTokenType(to.GetTokenType());
03694                 ctx->type.dataType.SetObjectType(to.GetObjectType());
03695             }
03696             else if( ctx->type.dataType.IsIntegerType() && ctx->type.dataType.GetSizeInMemoryDWords() == 2 )
03697             {
03698                 ConvertToTempVariableNotIn(ctx, reservedVars);
03699                 ReleaseTemporaryVariable(ctx->type, &ctx->bc);
03700                 int offset = AllocateVariableNotIn(to, true, reservedVars);
03701                 ctx->bc.InstrW_W(asBC_i64TOf, offset, ctx->type.stackOffset);
03702                 ctx->type.SetVariable(to, offset, true);
03703             }
03704             else if( ctx->type.dataType.IsUnsignedType() && ctx->type.dataType.GetSizeInMemoryDWords() == 1 )
03705             {
03706                 ConvertToTempVariableNotIn(ctx, reservedVars);
03707                 ctx->bc.InstrSHORT(asBC_uTOf, ctx->type.stackOffset);
03708                 ctx->type.dataType.SetTokenType(to.GetTokenType());
03709                 ctx->type.dataType.SetObjectType(to.GetObjectType());
03710             }
03711             else if( ctx->type.dataType.IsUnsignedType() && ctx->type.dataType.GetSizeInMemoryDWords() == 2 )
03712             {
03713                 ConvertToTempVariableNotIn(ctx, reservedVars);
03714                 ReleaseTemporaryVariable(ctx->type, &ctx->bc);
03715                 int offset = AllocateVariableNotIn(to, true, reservedVars);
03716                 ctx->bc.InstrW_W(asBC_u64TOf, offset, ctx->type.stackOffset);
03717                 ctx->type.SetVariable(to, offset, true);
03718             }
03719             else if( ctx->type.dataType.IsDoubleType() )
03720             {
03721                 ConvertToTempVariableNotIn(ctx, reservedVars);
03722                 ReleaseTemporaryVariable(ctx->type, &ctx->bc);
03723                 int offset = AllocateVariableNotIn(to, true, reservedVars);
03724                 ctx->bc.InstrW_W(asBC_dTOf, offset, ctx->type.stackOffset);
03725                 ctx->type.SetVariable(to, offset, true);
03726             }
03727         }
03728         else if( to.IsDoubleType() )
03729         {
03730             if( (ctx->type.dataType.IsIntegerType() || ctx->type.dataType.IsEnumType()) && ctx->type.dataType.GetSizeInMemoryDWords() == 1 )
03731             {
03732                 ConvertToTempVariableNotIn(ctx, reservedVars);
03733                 ReleaseTemporaryVariable(ctx->type, &ctx->bc);
03734                 int offset = AllocateVariableNotIn(to, true, reservedVars);
03735                 ctx->bc.InstrW_W(asBC_iTOd, offset, ctx->type.stackOffset);
03736                 ctx->type.SetVariable(to, offset, true);
03737             }
03738             else if( ctx->type.dataType.IsIntegerType() && ctx->type.dataType.GetSizeInMemoryDWords() == 2 )
03739             {
03740                 ConvertToTempVariableNotIn(ctx, reservedVars);
03741                 ctx->bc.InstrSHORT(asBC_i64TOd, ctx->type.stackOffset);
03742                 ctx->type.dataType.SetTokenType(to.GetTokenType());
03743                 ctx->type.dataType.SetObjectType(to.GetObjectType());
03744             }
03745             else if( ctx->type.dataType.IsUnsignedType() && ctx->type.dataType.GetSizeInMemoryDWords() == 1 )
03746             {
03747                 ConvertToTempVariableNotIn(ctx, reservedVars);
03748                 ReleaseTemporaryVariable(ctx->type, &ctx->bc);
03749                 int offset = AllocateVariableNotIn(to, true, reservedVars);
03750                 ctx->bc.InstrW_W(asBC_uTOd, offset, ctx->type.stackOffset);
03751                 ctx->type.SetVariable(to, offset, true);
03752             }
03753             else if( ctx->type.dataType.IsUnsignedType() && ctx->type.dataType.GetSizeInMemoryDWords() == 2 )
03754             {
03755                 ConvertToTempVariableNotIn(ctx, reservedVars);
03756                 ctx->bc.InstrSHORT(asBC_u64TOd, ctx->type.stackOffset);
03757                 ctx->type.dataType.SetTokenType(to.GetTokenType());
03758                 ctx->type.dataType.SetObjectType(to.GetObjectType());
03759             }
03760             else if( ctx->type.dataType.IsFloatType() )
03761             {
03762                 ConvertToTempVariableNotIn(ctx, reservedVars);
03763                 ReleaseTemporaryVariable(ctx->type, &ctx->bc);
03764                 int offset = AllocateVariableNotIn(to, true, reservedVars);
03765                 ctx->bc.InstrW_W(asBC_fTOd, offset, ctx->type.stackOffset);
03766                 ctx->type.SetVariable(to, offset, true);
03767             }
03768         }
03769     }
03770     else
03771     {
03772         if( (to.IsIntegerType() || to.IsUnsignedType() ||
03773              to.IsFloatType()   || to.IsDoubleType() ||
03774              (to.IsEnumType() && convType == asIC_EXPLICIT_VAL_CAST)) &&
03775             (ctx->type.dataType.IsIntegerType() || ctx->type.dataType.IsUnsignedType() ||
03776              ctx->type.dataType.IsFloatType()   || ctx->type.dataType.IsDoubleType() ||
03777              ctx->type.dataType.IsEnumType()) )
03778         {
03779             ctx->type.dataType.SetTokenType(to.GetTokenType());
03780             ctx->type.dataType.SetObjectType(to.GetObjectType());
03781         }
03782     }
03783 
03784     // Primitive types on the stack, can be const or non-const
03785     ctx->type.dataType.MakeReadOnly(to.IsReadOnly());
03786 }
03787 
03788 void asCCompiler::ImplicitConversion(asSExprContext *ctx, const asCDataType &to, asCScriptNode *node, EImplicitConv convType, bool generateCode, asCArray<int> *reservedVars, bool allowObjectConstruct)
03789 {
03790     // No conversion from void to any other type
03791     if( ctx->type.dataType.GetTokenType() == ttVoid )
03792         return;
03793 
03794     // Do we want a var type?
03795     if( to.GetTokenType() == ttQuestion )
03796     {
03797         // Any type can be converted to a var type, but only when not generating code
03798         asASSERT( !generateCode );
03799 
03800         ctx->type.dataType = to;
03801 
03802         return;
03803     }
03804     // Do we want a primitive?
03805     else if( to.IsPrimitive() )
03806     {
03807         if( !ctx->type.dataType.IsPrimitive() )
03808             ImplicitConvObjectToPrimitive(ctx, to, node, convType, generateCode, reservedVars);
03809         else
03810             ImplicitConvPrimitiveToPrimitive(ctx, to, node, convType, generateCode, reservedVars);
03811     }
03812     else // The target is a complex type
03813     {
03814         if( ctx->type.dataType.IsPrimitive() )
03815             ImplicitConvPrimitiveToObject(ctx, to, node, convType, generateCode, reservedVars, allowObjectConstruct);
03816         else
03817             ImplicitConvObjectToObject(ctx, to, node, convType, generateCode, reservedVars, allowObjectConstruct);
03818     }
03819 }
03820 
03821 void asCCompiler::ImplicitConvObjectToPrimitive(asSExprContext *ctx, const asCDataType &to, asCScriptNode *node, EImplicitConv convType, bool generateCode, asCArray<int> *reservedVars)
03822 {
03823     if( ctx->type.isExplicitHandle )
03824     {
03825         // An explicit handle cannot be converted to a primitive
03826         if( convType != asIC_IMPLICIT_CONV && node )
03827         {
03828             asCString str;
03829             str.Format(TXT_CANT_IMPLICITLY_CONVERT_s_TO_s, ctx->type.dataType.Format().AddressOf(), to.Format().AddressOf());
03830             Error(str.AddressOf(), node);
03831         }
03832         return;
03833     }
03834 
03835     // TODO: Must use the const cast behaviour if the object is read-only
03836 
03837     // Find matching value cast behaviours
03838     // Here we're only interested in those that convert the type to a primitive type
03839     asCArray<int> funcs;
03840     asSTypeBehaviour *beh = ctx->type.dataType.GetBehaviour();
03841     if( beh )
03842     {
03843         if( convType == asIC_EXPLICIT_VAL_CAST )
03844         {
03845             for( unsigned int n = 0; n < beh->operators.GetLength(); n += 2 )
03846             {
03847                 // accept both implicit and explicit cast
03848                 if( (beh->operators[n] == asBEHAVE_VALUE_CAST ||
03849                      beh->operators[n] == asBEHAVE_IMPLICIT_VALUE_CAST) &&
03850                     builder->GetFunctionDescription(beh->operators[n+1])->returnType.IsPrimitive() )
03851                     funcs.PushLast(beh->operators[n+1]);
03852             }
03853         }
03854         else
03855         {
03856             for( unsigned int n = 0; n < beh->operators.GetLength(); n += 2 )
03857             {
03858                 // accept only implicit cast
03859                 if( beh->operators[n] == asBEHAVE_IMPLICIT_VALUE_CAST &&
03860                     builder->GetFunctionDescription(beh->operators[n+1])->returnType.IsPrimitive() )
03861                     funcs.PushLast(beh->operators[n+1]);
03862             }
03863         }
03864     }
03865 
03866     // This matrix describes the priorities of the types to search for, for each target type
03867     // The first column is the target type, the priorities goes from left to right
03868     eTokenType matchMtx[10][10] =
03869     {
03870         {ttDouble, ttFloat,  ttInt64,  ttUInt64, ttInt,    ttUInt,   ttInt16,  ttUInt16, ttInt8,   ttUInt8},
03871         {ttFloat,  ttDouble, ttInt64,  ttUInt64, ttInt,    ttUInt,   ttInt16,  ttUInt16, ttInt8,   ttUInt8},
03872         {ttInt64,  ttUInt64, ttInt,    ttUInt,   ttInt16,  ttUInt16, ttInt8,   ttUInt8,  ttDouble, ttFloat},
03873         {ttUInt64, ttInt64,  ttUInt,   ttInt,    ttUInt16, ttInt16,  ttUInt8,  ttInt8,   ttDouble, ttFloat},
03874         {ttInt,    ttUInt,   ttInt64,  ttUInt64, ttInt16,  ttUInt16, ttInt8,   ttUInt8,  ttDouble, ttFloat},
03875         {ttUInt,   ttInt,    ttUInt64, ttInt64,  ttUInt16, ttInt16,  ttUInt8,  ttInt8,   ttDouble, ttFloat},
03876         {ttInt16,  ttUInt16, ttInt,    ttUInt,   ttInt64,  ttUInt64, ttInt8,   ttUInt8,  ttDouble, ttFloat},
03877         {ttUInt16, ttInt16,  ttUInt,   ttInt,    ttUInt64, ttInt64,  ttUInt8,  ttInt8,   ttDouble, ttFloat},
03878         {ttInt8,   ttUInt8,  ttInt16,  ttUInt16, ttInt,    ttUInt,   ttInt64,  ttUInt64, ttDouble, ttFloat},
03879         {ttUInt8,  ttInt8,   ttUInt16, ttInt16,  ttUInt,   ttInt,    ttUInt64, ttInt64,  ttDouble, ttFloat},
03880     };
03881 
03882     // Which row to use?
03883     eTokenType *row = 0;
03884     for( unsigned int type = 0; type < 10; type++ )
03885     {
03886         if( to.GetTokenType() == matchMtx[type][0] )
03887         {
03888             row = &matchMtx[type][0];
03889             break;
03890         }
03891     }
03892 
03893     // Find the best matching cast operator
03894     int funcId = 0;
03895     if( row )
03896     {
03897         asCDataType target(to);
03898 
03899         // Priority goes from left to right in the matrix
03900         for( unsigned int attempt = 0; attempt < 10 && funcId == 0; attempt++ )
03901         {
03902             target.SetTokenType(row[attempt]);
03903             for( unsigned int n = 0; n < funcs.GetLength(); n++ )
03904             {
03905                 asCScriptFunction *descr = builder->GetFunctionDescription(funcs[n]);
03906                 if( descr->returnType.IsEqualExceptConst(target) )
03907                 {
03908                     funcId = funcs[n];
03909                     break;
03910                 }
03911             }
03912         }
03913     }
03914 
03915     // Did we find a suitable function?
03916     if( funcId != 0 )
03917     {
03918         asCScriptFunction *descr = builder->GetFunctionDescription(funcId);
03919         if( generateCode )
03920         {
03921             asCTypeInfo objType = ctx->type;
03922 
03923             Dereference(ctx, true);
03924 
03925             PerformFunctionCall(funcId, ctx);
03926 
03927             ReleaseTemporaryVariable(objType, &ctx->bc);
03928         }
03929         else
03930             ctx->type.Set(descr->returnType);
03931 
03932         // Allow one more implicit conversion to another primitive type
03933         ImplicitConversion(ctx, to, node, convType, generateCode, reservedVars, false);
03934     }
03935     else
03936     {
03937         if( convType != asIC_IMPLICIT_CONV && node )
03938         {
03939             asCString str;
03940             str.Format(TXT_CANT_IMPLICITLY_CONVERT_s_TO_s, ctx->type.dataType.Format().AddressOf(), to.Format().AddressOf());
03941             Error(str.AddressOf(), node);
03942         }
03943     }
03944 }
03945 
03946 void asCCompiler::ImplicitConvObjectToObject(asSExprContext *ctx, const asCDataType &to, asCScriptNode *node, EImplicitConv convType, bool generateCode, asCArray<int> *reservedVars, bool allowObjectConstruct)
03947 {
03948     // Convert null to any object type handle, but not to a non-handle type
03949     if( ctx->type.IsNullConstant() )
03950     {
03951         if( to.IsObjectHandle() )
03952             ctx->type.dataType = to;
03953 
03954         return;
03955     }
03956 
03957     // First attempt to convert the base type without instanciating another instance
03958     if( to.GetObjectType() != ctx->type.dataType.GetObjectType() )
03959     {
03960         // If the to type is an interface and the from type implements it, then we can convert it immediately
03961         if( ctx->type.dataType.GetObjectType()->Implements(to.GetObjectType()) )
03962         {
03963             ctx->type.dataType.SetObjectType(to.GetObjectType());
03964         }
03965 
03966         // If the to type is a class and the from type derives from it, then we can convert it immediately
03967         if( ctx->type.dataType.GetObjectType()->DerivesFrom(to.GetObjectType()) )
03968         {
03969             ctx->type.dataType.SetObjectType(to.GetObjectType());
03970         }
03971 
03972         // If the types are not equal yet, then we may still be able to find a reference cast
03973         if( ctx->type.dataType.GetObjectType() != to.GetObjectType() )
03974         {
03975             // A ref cast must not remove the constness
03976             bool isConst = false;
03977             if( (ctx->type.dataType.IsObjectHandle() && ctx->type.dataType.IsHandleToConst()) ||
03978                 (!ctx->type.dataType.IsObjectHandle() && ctx->type.dataType.IsReadOnly()) )
03979                 isConst = true;
03980 
03981             // We may still be able to find an implicit ref cast behaviour
03982             CompileRefCast(ctx, to, convType == asIC_EXPLICIT_REF_CAST, node, generateCode);
03983 
03984             ctx->type.dataType.MakeHandleToConst(isConst);
03985         }
03986     }
03987 
03988     // If the base type is still different, and we are allowed to instance
03989     // another object then we can try an implicit value cast
03990     if( to.GetObjectType() != ctx->type.dataType.GetObjectType() && allowObjectConstruct )
03991     {
03992         // TODO: Implement support for implicit constructor/factory
03993 
03994         asCArray<int> funcs;
03995         asSTypeBehaviour *beh = ctx->type.dataType.GetBehaviour();
03996         if( beh )
03997         {
03998             if( convType == asIC_EXPLICIT_VAL_CAST )
03999             {
04000                 for( unsigned int n = 0; n < beh->operators.GetLength(); n += 2 )
04001                 {
04002                     // accept both implicit and explicit cast
04003                     if( (beh->operators[n] == asBEHAVE_VALUE_CAST ||
04004                          beh->operators[n] == asBEHAVE_IMPLICIT_VALUE_CAST) &&
04005                         builder->GetFunctionDescription(beh->operators[n+1])->returnType.GetObjectType() == to.GetObjectType() )
04006                         funcs.PushLast(beh->operators[n+1]);
04007                 }
04008             }
04009             else
04010             {
04011                 for( unsigned int n = 0; n < beh->operators.GetLength(); n += 2 )
04012                 {
04013                     // accept only implicit cast
04014                     if( beh->operators[n] == asBEHAVE_IMPLICIT_VALUE_CAST &&
04015                         builder->GetFunctionDescription(beh->operators[n+1])->returnType.GetObjectType() == to.GetObjectType() )
04016                         funcs.PushLast(beh->operators[n+1]);
04017                 }
04018             }
04019         }
04020 
04021         // TODO: If there are multiple valid value casts, then we must choose the most appropriate one
04022         asASSERT( funcs.GetLength() <= 1 );
04023 
04024         if( funcs.GetLength() == 1 )
04025         {
04026             asCScriptFunction *f = builder->GetFunctionDescription(funcs[0]);
04027             if( generateCode )
04028             {
04029                 asCTypeInfo objType = ctx->type;
04030                 Dereference(ctx, true);
04031                 PerformFunctionCall(funcs[0], ctx);
04032                 ReleaseTemporaryVariable(objType, &ctx->bc);
04033             }
04034             else
04035                 ctx->type.Set(f->returnType);
04036         }
04037     }
04038 
04039     // If we still haven't converted the base type to the correct type, then there is no need to continue
04040     if( to.GetObjectType() != ctx->type.dataType.GetObjectType() )
04041         return;
04042 
04043 
04044     // TODO: The below code can probably be improved even further. It should first convert the type to
04045     //       object handle or non-object handle, and only after that convert to reference or non-reference
04046 
04047     if( to.IsObjectHandle() )
04048     {
04049         // An object type can be directly converted to a handle of the same type
04050         if( ctx->type.dataType.SupportHandles() )
04051         {
04052             ctx->type.dataType.MakeHandle(true);
04053         }
04054 
04055         if( ctx->type.dataType.IsObjectHandle() )
04056             ctx->type.dataType.MakeReadOnly(to.IsReadOnly());
04057 
04058         if( to.IsHandleToConst() && ctx->type.dataType.IsObjectHandle() )
04059             ctx->type.dataType.MakeHandleToConst(true);
04060     }
04061 
04062     if( !to.IsReference() )
04063     {
04064         if( ctx->type.dataType.IsReference() )
04065         {
04066             Dereference(ctx, generateCode);
04067 
04068             // TODO: Can't this leave unhandled deferred output params?
04069         }
04070 
04071         if( to.IsObjectHandle() )
04072         {
04073             // TODO: If the type is handle, then we can't use IsReadOnly to determine the constness of the basetype
04074 
04075             // If the rvalue is a handle to a const object, then
04076             // the lvalue must also be a handle to a const object
04077             if( ctx->type.dataType.IsReadOnly() && !to.IsReadOnly() )
04078             {
04079                 if( convType != asIC_IMPLICIT_CONV )
04080                 {
04081                     asASSERT(node);
04082                     asCString str;
04083                     str.Format(TXT_CANT_IMPLICITLY_CONVERT_s_TO_s, ctx->type.dataType.Format().AddressOf(), to.Format().AddressOf());
04084                     Error(str.AddressOf(), node);
04085                 }
04086             }
04087         }
04088         else
04089         {
04090             if( ctx->type.dataType.IsObjectHandle() && !ctx->type.isExplicitHandle )
04091             {
04092                 if( generateCode )
04093                     ctx->bc.Instr(asBC_CHKREF);
04094 
04095                 ctx->type.dataType.MakeHandle(false);
04096             }
04097 
04098             // A const object can be converted to a non-const object through a copy
04099             if( ctx->type.dataType.IsReadOnly() && !to.IsReadOnly() &&
04100                 allowObjectConstruct )
04101             {
04102                 // Does the object type allow a copy to be made?
04103                 if( ctx->type.dataType.CanBeCopied() )
04104                 {
04105                     if( generateCode )
04106                     {
04107                         // Make a temporary object with the copy
04108                         PrepareTemporaryObject(node, ctx, reservedVars);
04109                     }
04110                     else
04111                         ctx->type.dataType.MakeReadOnly(false);
04112                 }
04113             }
04114 
04115             // A non-const object can be converted to a const object directly
04116             if( !ctx->type.dataType.IsReadOnly() && to.IsReadOnly() )
04117             {
04118                 ctx->type.dataType.MakeReadOnly(true);
04119             }
04120         }
04121     }
04122     else // to.IsReference()
04123     {
04124         if( ctx->type.dataType.IsReference() )
04125         {
04126             // A reference to a handle can be converted to a reference to an object
04127             // by first reading the address, then verifying that it is not null, then putting the address back on the stack
04128             if( !to.IsObjectHandle() && ctx->type.dataType.IsObjectHandle() && !ctx->type.isExplicitHandle )
04129             {
04130                 ctx->type.dataType.MakeHandle(false);
04131                 if( generateCode )
04132                     ctx->bc.Instr(asBC_ChkRefS);
04133             }
04134 
04135             // A reference to a non-const can be converted to a reference to a const
04136             if( to.IsReadOnly() )
04137                 ctx->type.dataType.MakeReadOnly(true);
04138             else if( ctx->type.dataType.IsReadOnly() )
04139             {
04140                 // A reference to a const can be converted to a reference to a
04141                 // non-const by copying the object to a temporary variable
04142                 ctx->type.dataType.MakeReadOnly(false);
04143 
04144                 if( generateCode )
04145                 {
04146                     // Allocate a temporary variable
04147                     asSExprContext lctx(engine);
04148                     asCDataType dt = ctx->type.dataType;
04149                     dt.MakeReference(false);
04150                     int offset = AllocateVariableNotIn(dt, true, reservedVars);
04151                     lctx.type = ctx->type;
04152                     lctx.type.isTemporary = true;
04153                     lctx.type.stackOffset = (short)offset;
04154 
04155                     CallDefaultConstructor(lctx.type.dataType, offset, &lctx.bc, node);
04156 
04157                     // Build the right hand expression
04158                     asSExprContext rctx(engine);
04159                     rctx.type = ctx->type;
04160                     rctx.bc.AddCode(&lctx.bc);
04161                     rctx.bc.AddCode(&ctx->bc);
04162 
04163                     // Build the left hand expression
04164                     lctx.bc.InstrSHORT(asBC_PSF, (short)offset);
04165 
04166                     // DoAssignment doesn't allow assignment to temporary variable,
04167                     // so we temporarily set the type as non-temporary.
04168                     lctx.type.isTemporary = false;
04169 
04170                     DoAssignment(ctx, &lctx, &rctx, node, node, ttAssignment, node);
04171 
04172                     // If the original const object was a temporary variable, then
04173                     // that needs to be released now
04174                     ProcessDeferredParams(ctx);
04175 
04176                     ctx->type = lctx.type;
04177                     ctx->type.isTemporary = true;
04178                 }
04179             }
04180         }
04181         else
04182         {
04183             if( generateCode )
04184             {
04185                 asCTypeInfo type;
04186                 type.Set(ctx->type.dataType);
04187 
04188                 // Allocate a temporary variable
04189                 int offset = AllocateVariableNotIn(type.dataType, true, reservedVars);
04190                 type.isTemporary = true;
04191                 type.stackOffset = (short)offset;
04192                 if( type.dataType.IsObjectHandle() )
04193                     type.isExplicitHandle = true;
04194 
04195                 CallDefaultConstructor(type.dataType, offset, &ctx->bc, node);
04196                 type.dataType.MakeReference(true);
04197 
04198                 PrepareForAssignment(&type.dataType, ctx, node);
04199 
04200                 ctx->bc.InstrSHORT(asBC_PSF, type.stackOffset);
04201 
04202                 // If the input type is read-only we'll need to temporarily
04203                 // remove this constness, otherwise the assignment will fail
04204                 bool typeIsReadOnly = type.dataType.IsReadOnly();
04205                 type.dataType.MakeReadOnly(false);
04206                 PerformAssignment(&type, &ctx->type, &ctx->bc, node);
04207                 type.dataType.MakeReadOnly(typeIsReadOnly);
04208 
04209                 ctx->bc.Pop(ctx->type.dataType.GetSizeOnStackDWords());
04210 
04211                 ReleaseTemporaryVariable(ctx->type, &ctx->bc);
04212 
04213                 ctx->bc.InstrSHORT(asBC_PSF, type.stackOffset);
04214 
04215                 ctx->type = type;
04216             }
04217 
04218             // A non-reference can be converted to a reference,
04219             // by putting the value in a temporary variable
04220             ctx->type.dataType.MakeReference(true);
04221 
04222             // Since it is a new temporary variable it doesn't have to be const
04223             ctx->type.dataType.MakeReadOnly(to.IsReadOnly());
04224         }
04225     }
04226 }
04227 
04228 void asCCompiler::ImplicitConvPrimitiveToObject(asSExprContext * /*ctx*/, const asCDataType & /*to*/, asCScriptNode * /*node*/, EImplicitConv /*isExplicit*/, bool /*generateCode*/, asCArray<int> * /*reservedVars*/, bool /*allowObjectConstruct*/)
04229 {
04230 /*
04231 
04232     if( allowObjectConstruct )
04233     {
04234         // Check for the existance of any implicit constructor/factory
04235         // behaviours to construct the desired object from the primitive
04236 
04237         // TODO: Implement implicit constructor/factory
04238 
04239         asCArray<int> funcs;
04240         asSTypeBehaviour *beh = to.GetBehaviour();
04241         if( beh )
04242         {
04243             // TODO: Add implicit conversion to object types via contructor/factory
04244 
04245             // Find the implicit constructor calls
04246             for( int n = 0; n < beh->operators.GetLength(); n += 2 )
04247                 if( beh->operators[n] == asBEHAVE_IMPLICIT_CONSTRUCT ||
04248                     beh->operators[n] == asBEHAVE_IMPLICIT_FACTORY )
04249                     funcs.PushLast(beh->operators[n+1]);
04250         }
04251 
04252         // Compile the arguments
04253         asCArray<asSExprContext *> args;
04254         asCArray<asCTypeInfo> temporaryVariables;
04255 
04256         args.PushLast(ctx);
04257 
04258         MatchFunctions(funcs, args, node, to.GetObjectType()->name.AddressOf(), NULL, false, true, false);
04259 
04260         // Verify that we found 1 matching function
04261         if( funcs.GetLength() == 1 )
04262         {
04263             asCTypeInfo tempObj;
04264             tempObj.dataType = to;
04265             tempObj.dataType.MakeReference(true);
04266             tempObj.isTemporary = true;
04267             tempObj.isVariable = true;
04268 
04269             if( generateCode )
04270             {
04271                 tempObj.stackOffset = (short)AllocateVariable(to, true);
04272 
04273                 asSExprContext tmp(engine);
04274 
04275                 if( tempObj.dataType.GetObjectType()->flags & asOBJ_REF )
04276                 {
04277                     PrepareFunctionCall(funcs[0], &tmp.bc, args);
04278                     MoveArgsToStack(funcs[0], &tmp.bc, args, false);
04279 
04280                     // Call factory and store handle in the variable
04281                     PerformFunctionCall(funcs[0], &tmp, false, &args, 0, true, tempObj.stackOffset);
04282 
04283                     tmp.type = tempObj;
04284                 }
04285                 else
04286                 {
04287                     // Push the address of the object on the stack
04288                     tmp.bc.InstrSHORT(asBC_VAR, tempObj.stackOffset);
04289 
04290                     PrepareFunctionCall(funcs[0], &tmp.bc, args);
04291                     MoveArgsToStack(funcs[0], &tmp.bc, args, false);
04292 
04293                     int offset = 0;
04294                     for( asUINT n = 0; n < args.GetLength(); n++ )
04295                         offset += args[n]->type.dataType.GetSizeOnStackDWords();
04296 
04297                     tmp.bc.InstrWORD(asBC_GETREF, (asWORD)offset);
04298 
04299                     PerformFunctionCall(funcs[0], &tmp, true, &args, tempObj.dataType.GetObjectType());
04300 
04301                     // The constructor doesn't return anything,
04302                     // so we have to manually inform the type of
04303                     // the return value
04304                     tmp.type = tempObj;
04305 
04306                     // Push the address of the object on the stack again
04307                     tmp.bc.InstrSHORT(asBC_PSF, tempObj.stackOffset);
04308                 }
04309 
04310                 // Copy the newly generated code to the input context
04311                 // ctx is already empty, since it was merged as part of argument expression
04312                 asASSERT(ctx->bc.GetLastInstr() == -1);
04313                 MergeExprContexts(ctx, &tmp);
04314             }
04315 
04316             ctx->type = tempObj;
04317         }
04318     }
04319 */
04320 }
04321 
04322 void asCCompiler::ImplicitConversionConstant(asSExprContext *from, const asCDataType &to, asCScriptNode *node, EImplicitConv convType)
04323 {
04324     asASSERT(from->type.isConstant);
04325 
04326     // TODO: node should be the node of the value that is
04327     // converted (not the operator that provokes the implicit
04328     // conversion)
04329 
04330     // If the base type is correct there is no more to do
04331     if( to.IsEqualExceptRefAndConst(from->type.dataType) ) return;
04332 
04333     // References cannot be constants
04334     if( from->type.dataType.IsReference() ) return;
04335 
04336     // Arrays can't be constants
04337     if( to.IsArrayType() ) return;
04338 
04339     if( (to.IsIntegerType() && to.GetSizeInMemoryDWords() == 1) ||
04340         (to.IsEnumType() && convType == asIC_EXPLICIT_VAL_CAST) )
04341     {
04342         if( from->type.dataType.IsFloatType() ||
04343             from->type.dataType.IsDoubleType() ||
04344             from->type.dataType.IsUnsignedType() ||
04345             from->type.dataType.IsIntegerType() ||
04346             from->type.dataType.IsEnumType() )
04347         {
04348             // Transform the value
04349             // Float constants can be implicitly converted to int
04350             if( from->type.dataType.IsFloatType() )
04351             {
04352                 float fc = from->type.floatValue;
04353                 int ic = int(fc);
04354 
04355                 if( float(ic) != fc )
04356                 {
04357                     if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_NOT_EXACT, node);
04358                 }
04359 
04360                 from->type.intValue = ic;
04361             }
04362             // Double constants can be implicitly converted to int
04363             else if( from->type.dataType.IsDoubleType() )
04364             {
04365                 double fc = from->type.doubleValue;
04366                 int ic = int(fc);
04367 
04368                 if( double(ic) != fc )
04369                 {
04370                     if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_NOT_EXACT, node);
04371                 }
04372 
04373                 from->type.intValue = ic;
04374             }
04375             else if( from->type.dataType.IsUnsignedType() && from->type.dataType.GetSizeInMemoryDWords() == 1 )
04376             {
04377                 // Verify that it is possible to convert to signed without getting negative
04378                 if( from->type.intValue < 0 )
04379                 {
04380                     if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_CHANGE_SIGN, node);
04381                 }
04382 
04383                 // Convert to 32bit
04384                 if( from->type.dataType.GetSizeInMemoryBytes() == 1 )
04385                     from->type.intValue = from->type.byteValue;
04386                 else if( from->type.dataType.GetSizeInMemoryBytes() == 2 )
04387                     from->type.intValue = from->type.wordValue;
04388             }
04389             else if( from->type.dataType.IsUnsignedType() && from->type.dataType.GetSizeInMemoryDWords() == 2 )
04390             {
04391                 // Convert to 32bit
04392                 from->type.intValue = int(from->type.qwordValue);
04393             }
04394             else if( from->type.dataType.IsIntegerType() &&
04395                     from->type.dataType.GetSizeInMemoryBytes() < 4 )
04396             {
04397                 // Convert to 32bit
04398                 if( from->type.dataType.GetSizeInMemoryBytes() == 1 )
04399                     from->type.intValue = (signed char)from->type.byteValue;
04400                 else if( from->type.dataType.GetSizeInMemoryBytes() == 2 )
04401                     from->type.intValue = (short)from->type.wordValue;
04402             }
04403             else if( from->type.dataType.IsEnumType() )
04404             {
04405                 // Enum type is already an integer type
04406             }
04407 
04408             // Set the resulting type
04409             if( to.IsEnumType() )
04410                 from->type.dataType = to;
04411             else
04412                 from->type.dataType = asCDataType::CreatePrimitive(ttInt, true);
04413         }
04414 
04415         // Check if a downsize is necessary
04416         if( to.IsIntegerType() &&
04417             from->type.dataType.IsIntegerType() &&
04418             from->type.dataType.GetSizeInMemoryBytes() > to.GetSizeInMemoryBytes() )
04419         {
04420             // Verify if it is possible
04421             if( to.GetSizeInMemoryBytes() == 1 )
04422             {
04423                 if( char(from->type.intValue) != from->type.intValue )
04424                     if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_VALUE_TOO_LARGE_FOR_TYPE, node);
04425 
04426                 from->type.byteValue = char(from->type.intValue);
04427             }
04428             else if( to.GetSizeInMemoryBytes() == 2 )
04429             {
04430                 if( short(from->type.intValue) != from->type.intValue )
04431                     if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_VALUE_TOO_LARGE_FOR_TYPE, node);
04432 
04433                 from->type.wordValue = short(from->type.intValue);
04434             }
04435 
04436             from->type.dataType.SetTokenType(to.GetTokenType());
04437         }
04438     }
04439     else if( to.IsIntegerType() && to.GetSizeInMemoryDWords() == 2 )
04440     {
04441         // Float constants can be implicitly converted to int
04442         if( from->type.dataType.IsFloatType() )
04443         {
04444             float fc = from->type.floatValue;
04445             asINT64 ic = asINT64(fc);
04446 
04447             if( float(ic) != fc )
04448             {
04449                 if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_NOT_EXACT, node);
04450             }
04451 
04452             from->type.dataType = asCDataType::CreatePrimitive(ttInt64, true);
04453             from->type.qwordValue = ic;
04454         }
04455         // Double constants can be implicitly converted to int
04456         else if( from->type.dataType.IsDoubleType() )
04457         {
04458             double fc = from->type.doubleValue;
04459             asINT64 ic = asINT64(fc);
04460 
04461             if( double(ic) != fc )
04462             {
04463                 if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_NOT_EXACT, node);
04464             }
04465 
04466             from->type.dataType = asCDataType::CreatePrimitive(ttInt64, true);
04467             from->type.qwordValue = ic;
04468         }
04469         else if( from->type.dataType.IsUnsignedType() )
04470         {
04471             // Convert to 64bit
04472             if( from->type.dataType.GetSizeInMemoryBytes() == 1 )
04473                 from->type.qwordValue = from->type.byteValue;
04474             else if( from->type.dataType.GetSizeInMemoryBytes() == 2 )
04475                 from->type.qwordValue = from->type.wordValue;
04476             else if( from->type.dataType.GetSizeInMemoryBytes() == 4 )
04477                 from->type.qwordValue = from->type.dwordValue;
04478             else if( from->type.dataType.GetSizeInMemoryBytes() == 8 )
04479             {
04480                 if( asINT64(from->type.qwordValue) < 0 )
04481                 {
04482                     if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_CHANGE_SIGN, node);
04483                 }
04484             }
04485 
04486             from->type.dataType = asCDataType::CreatePrimitive(ttInt64, true);
04487         }
04488         else if( from->type.dataType.IsEnumType() )
04489         {
04490             from->type.qwordValue = from->type.intValue;
04491             from->type.dataType = asCDataType::CreatePrimitive(ttInt64, true);
04492         }
04493         else if( from->type.dataType.IsIntegerType() )
04494         {
04495             // Convert to 64bit
04496             if( from->type.dataType.GetSizeInMemoryBytes() == 1 )
04497                 from->type.qwordValue = (signed char)from->type.byteValue;
04498             else if( from->type.dataType.GetSizeInMemoryBytes() == 2 )
04499                 from->type.qwordValue = (short)from->type.wordValue;
04500             else if( from->type.dataType.GetSizeInMemoryBytes() == 4 )
04501                 from->type.qwordValue = from->type.intValue;
04502 
04503             from->type.dataType = asCDataType::CreatePrimitive(ttInt64, true);
04504         }
04505     }
04506     else if( to.IsUnsignedType() && to.GetSizeInMemoryDWords() == 1 )
04507     {
04508         if( from->type.dataType.IsFloatType() )
04509         {
04510             float fc = from->type.floatValue;
04511             int uic = int(fc);
04512 
04513             if( float(uic) != fc )
04514             {
04515                 if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_NOT_EXACT, node);
04516             }
04517             else if( uic < 0 )
04518             {
04519                 if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_CHANGE_SIGN, node);
04520             }
04521 
04522             from->type.dataType = asCDataType::CreatePrimitive(ttInt, true);
04523             from->type.intValue = uic;
04524 
04525             // Try once more, in case of a smaller type
04526             ImplicitConversionConstant(from, to, node, convType);
04527         }
04528         else if( from->type.dataType.IsDoubleType() )
04529         {
04530             double fc = from->type.doubleValue;
04531             int uic = int(fc);
04532 
04533             if( double(uic) != fc )
04534             {
04535                 if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_NOT_EXACT, node);
04536             }
04537 
04538             from->type.dataType = asCDataType::CreatePrimitive(ttInt, true);
04539             from->type.intValue = uic;
04540 
04541             // Try once more, in case of a smaller type
04542             ImplicitConversionConstant(from, to, node, convType);
04543         }
04544         else if( from->type.dataType.IsEnumType() )
04545         {
04546             from->type.dataType = asCDataType::CreatePrimitive(ttUInt, true);
04547 
04548             // Try once more, in case of a smaller type
04549             ImplicitConversionConstant(from, to, node, convType);
04550         }
04551         else if( from->type.dataType.IsIntegerType() )
04552         {
04553             // Verify that it is possible to convert to unsigned without loosing negative
04554             if( from->type.intValue < 0 )
04555             {
04556                 if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_CHANGE_SIGN, node);
04557             }
04558 
04559             // Convert to 32bit
04560             if( from->type.dataType.GetSizeInMemoryBytes() == 1 )
04561                 from->type.intValue = (signed char)from->type.byteValue;
04562             else if( from->type.dataType.GetSizeInMemoryBytes() == 2 )
04563                 from->type.intValue = (short)from->type.wordValue;
04564 
04565             from->type.dataType = asCDataType::CreatePrimitive(ttUInt, true);
04566 
04567             // Try once more, in case of a smaller type
04568             ImplicitConversionConstant(from, to, node, convType);
04569         }
04570         else if( from->type.dataType.IsUnsignedType() &&
04571                  from->type.dataType.GetSizeInMemoryBytes() < 4 )
04572         {
04573             // Convert to 32bit
04574             if( from->type.dataType.GetSizeInMemoryBytes() == 1 )
04575                 from->type.dwordValue = from->type.byteValue;
04576             else if( from->type.dataType.GetSizeInMemoryBytes() == 2 )
04577                 from->type.dwordValue = from->type.wordValue;
04578 
04579             from->type.dataType = asCDataType::CreatePrimitive(ttUInt, true);
04580 
04581             // Try once more, in case of a smaller type
04582             ImplicitConversionConstant(from, to, node, convType);
04583         }
04584         else if( from->type.dataType.IsUnsignedType() &&
04585                  from->type.dataType.GetSizeInMemoryBytes() > to.GetSizeInMemoryBytes() )
04586         {
04587             // Verify if it is possible
04588             if( to.GetSizeInMemoryBytes() == 1 )
04589             {
04590                 if( asBYTE(from->type.dwordValue) != from->type.dwordValue )
04591                     if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_VALUE_TOO_LARGE_FOR_TYPE, node);
04592 
04593                 from->type.byteValue = asBYTE(from->type.dwordValue);
04594             }
04595             else if( to.GetSizeInMemoryBytes() == 2 )
04596             {
04597                 if( asWORD(from->type.dwordValue) != from->type.dwordValue )
04598                     if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_VALUE_TOO_LARGE_FOR_TYPE, node);
04599 
04600                 from->type.wordValue = asWORD(from->type.dwordValue);
04601             }
04602 
04603             from->type.dataType.SetTokenType(to.GetTokenType());
04604         }
04605     }
04606     else if( to.IsUnsignedType() && to.GetSizeInMemoryDWords() == 2 )
04607     {
04608         if( from->type.dataType.IsFloatType() )
04609         {
04610             float fc = from->type.floatValue;
04611             // Convert first to int64 then to uint64 to avoid negative float becoming 0 on gnuc base compilers
04612             asQWORD uic = asQWORD(asINT64(fc));
04613 
04614             // TODO: MSVC6 doesn't permit UINT64 to double
04615             if( float((signed)uic) != fc )
04616             {
04617                 if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_NOT_EXACT, node);
04618             }
04619 
04620             from->type.dataType = asCDataType::CreatePrimitive(ttUInt64, true);
04621             from->type.qwordValue = uic;
04622         }
04623         else if( from->type.dataType.IsDoubleType() )
04624         {
04625             double fc = from->type.doubleValue;
04626             // Convert first to int64 then to uint64 to avoid negative float becoming 0 on gnuc base compilers
04627             asQWORD uic = asQWORD(asINT64(fc));
04628 
04629             // TODO: MSVC6 doesn't permit UINT64 to double
04630             if( double((signed)uic) != fc )
04631             {
04632                 if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_NOT_EXACT, node);
04633             }
04634 
04635             from->type.dataType = asCDataType::CreatePrimitive(ttUInt64, true);
04636             from->type.qwordValue = uic;
04637         }
04638         else if( from->type.dataType.IsEnumType() )
04639         {
04640             from->type.qwordValue = (asINT64)from->type.intValue;
04641             from->type.dataType = asCDataType::CreatePrimitive(ttUInt64, true);
04642         }
04643         else if( from->type.dataType.IsIntegerType() && from->type.dataType.GetSizeInMemoryDWords() == 1 )
04644         {
04645             // Convert to 64bit
04646             if( from->type.dataType.GetSizeInMemoryBytes() == 1 )
04647                 from->type.qwordValue = (asINT64)(signed char)from->type.byteValue;
04648             else if( from->type.dataType.GetSizeInMemoryBytes() == 2 )
04649                 from->type.qwordValue = (asINT64)(short)from->type.wordValue;
04650             else if( from->type.dataType.GetSizeInMemoryBytes() == 4 )
04651                 from->type.qwordValue = (asINT64)from->type.intValue;
04652 
04653             // Verify that it is possible to convert to unsigned without loosing negative
04654             if( asINT64(from->type.qwordValue) < 0 )
04655             {
04656                 if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_CHANGE_SIGN, node);
04657             }
04658 
04659             from->type.dataType = asCDataType::CreatePrimitive(ttUInt64, true);
04660         }
04661         else if( from->type.dataType.IsIntegerType() && from->type.dataType.GetSizeInMemoryDWords() == 2 )
04662         {
04663             // Verify that it is possible to convert to unsigned without loosing negative
04664             if( asINT64(from->type.qwordValue) < 0 )
04665             {
04666                 if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_CHANGE_SIGN, node);
04667             }
04668 
04669             from->type.dataType = asCDataType::CreatePrimitive(ttUInt64, true);
04670         }
04671         else if( from->type.dataType.IsUnsignedType() )
04672         {
04673             // Convert to 64bit
04674             if( from->type.dataType.GetSizeInMemoryBytes() == 1 )
04675                 from->type.qwordValue = from->type.byteValue;
04676             else if( from->type.dataType.GetSizeInMemoryBytes() == 2 )
04677                 from->type.qwordValue = from->type.wordValue;
04678             else if( from->type.dataType.GetSizeInMemoryBytes() == 4 )
04679                 from->type.qwordValue = from->type.dwordValue;
04680 
04681             from->type.dataType = asCDataType::CreatePrimitive(ttUInt64, true);
04682         }
04683     }
04684     else if( to.IsFloatType() )
04685     {
04686         if( from->type.dataType.IsDoubleType() )
04687         {
04688             double ic = from->type.doubleValue;
04689             float fc = float(ic);
04690 
04691             if( double(fc) != ic )
04692             {
04693                 asCString str;
04694                 str.Format(TXT_POSSIBLE_LOSS_OF_PRECISION);
04695                 if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(str.AddressOf(), node);
04696             }
04697 
04698             from->type.dataType.SetTokenType(to.GetTokenType());
04699             from->type.floatValue = fc;
04700         }
04701         else if( from->type.dataType.IsEnumType() )
04702         {
04703             float fc = float(from->type.intValue);
04704 
04705             if( int(fc) != from->type.intValue )
04706             {
04707                 if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_NOT_EXACT, node);
04708             }
04709 
04710             from->type.dataType.SetTokenType(to.GetTokenType());
04711             from->type.floatValue = fc;
04712         }
04713         else if( from->type.dataType.IsIntegerType() && from->type.dataType.GetSizeInMemoryDWords() == 1 )
04714         {
04715             // Must properly convert value in case the from value is smaller
04716             int ic;
04717             if( from->type.dataType.GetSizeInMemoryBytes() == 1 )
04718                 ic = (signed char)from->type.byteValue;
04719             else if( from->type.dataType.GetSizeInMemoryBytes() == 2 )
04720                 ic = (short)from->type.wordValue;
04721             else
04722                 ic = from->type.intValue;
04723             float fc = float(ic);
04724 
04725             if( int(fc) != ic )
04726             {
04727                 if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_NOT_EXACT, node);
04728             }
04729 
04730             from->type.dataType.SetTokenType(to.GetTokenType());
04731             from->type.floatValue = fc;
04732         }
04733         else if( from->type.dataType.IsIntegerType() && from->type.dataType.GetSizeInMemoryDWords() == 2 )
04734         {
04735             float fc = float(asINT64(from->type.qwordValue));
04736             if( asINT64(fc) != asINT64(from->type.qwordValue) )
04737             {
04738                 if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_NOT_EXACT, node);
04739             }
04740 
04741             from->type.dataType.SetTokenType(to.GetTokenType());
04742             from->type.floatValue = fc;
04743         }
04744         else if( from->type.dataType.IsUnsignedType() && from->type.dataType.GetSizeInMemoryDWords() == 1 )
04745         {
04746             // Must properly convert value in case the from value is smaller
04747             unsigned int uic;
04748             if( from->type.dataType.GetSizeInMemoryBytes() == 1 )
04749                 uic = from->type.byteValue;
04750             else if( from->type.dataType.GetSizeInMemoryBytes() == 2 )
04751                 uic = from->type.wordValue;
04752             else
04753                 uic = from->type.dwordValue;
04754             float fc = float(uic);
04755 
04756             if( (unsigned int)(fc) != uic )
04757             {
04758                 if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_NOT_EXACT, node);
04759             }
04760 
04761             from->type.dataType.SetTokenType(to.GetTokenType());
04762             from->type.floatValue = fc;
04763         }
04764         else if( from->type.dataType.IsUnsignedType() && from->type.dataType.GetSizeInMemoryDWords() == 2 )
04765         {
04766             // TODO: MSVC6 doesn't permit UINT64 to double
04767             float fc = float((signed)from->type.qwordValue);
04768 
04769             if( asQWORD(fc) != from->type.qwordValue )
04770             {
04771                 if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_NOT_EXACT, node);
04772             }
04773 
04774             from->type.dataType.SetTokenType(to.GetTokenType());
04775             from->type.floatValue = fc;
04776         }
04777     }
04778     else if( to.IsDoubleType() )
04779     {
04780         if( from->type.dataType.IsFloatType() )
04781         {
04782             float ic = from->type.floatValue;
04783             double fc = double(ic);
04784 
04785             // Don't check for float->double
04786         //  if( float(fc) != ic )
04787         //  {
04788         //      acCString str;
04789         //      str.Format(TXT_NOT_EXACT_g_g_g, ic, fc, float(fc));
04790         //      if( !isExplicit ) Warning(str, node);
04791         //  }
04792 
04793             from->type.dataType.SetTokenType(to.GetTokenType());
04794             from->type.doubleValue = fc;
04795         }
04796         else if( from->type.dataType.IsEnumType() )
04797         {
04798             double fc = double(from->type.intValue);
04799 
04800             if( int(fc) != from->type.intValue )
04801             {
04802                 if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_NOT_EXACT, node);
04803             }
04804 
04805             from->type.dataType.SetTokenType(to.GetTokenType());
04806             from->type.doubleValue = fc;
04807         }
04808         else if( from->type.dataType.IsIntegerType() && from->type.dataType.GetSizeInMemoryDWords() == 1 )
04809         {
04810             // Must properly convert value in case the from value is smaller
04811             int ic;
04812             if( from->type.dataType.GetSizeInMemoryBytes() == 1 )
04813                 ic = (signed char)from->type.byteValue;
04814             else if( from->type.dataType.GetSizeInMemoryBytes() == 2 )
04815                 ic = (short)from->type.wordValue;
04816             else
04817                 ic = from->type.intValue;
04818             double fc = double(ic);
04819 
04820             if( int(fc) != ic )
04821             {
04822                 if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_NOT_EXACT, node);
04823             }
04824 
04825             from->type.dataType.SetTokenType(to.GetTokenType());
04826             from->type.doubleValue = fc;
04827         }
04828         else if( from->type.dataType.IsIntegerType() && from->type.dataType.GetSizeInMemoryDWords() == 2 )
04829         {
04830             double fc = double(asINT64(from->type.qwordValue));
04831 
04832             if( asINT64(fc) != asINT64(from->type.qwordValue) )
04833             {
04834                 if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_NOT_EXACT, node);
04835             }
04836 
04837             from->type.dataType.SetTokenType(to.GetTokenType());
04838             from->type.doubleValue = fc;
04839         }
04840         else if( from->type.dataType.IsUnsignedType() && from->type.dataType.GetSizeInMemoryDWords() == 1 )
04841         {
04842             // Must properly convert value in case the from value is smaller
04843             unsigned int uic;
04844             if( from->type.dataType.GetSizeInMemoryBytes() == 1 )
04845                 uic = from->type.byteValue;
04846             else if( from->type.dataType.GetSizeInMemoryBytes() == 2 )
04847                 uic = from->type.wordValue;
04848             else
04849                 uic = from->type.dwordValue;
04850             double fc = double(uic);
04851 
04852             if( (unsigned int)(fc) != uic )
04853             {
04854                 if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_NOT_EXACT, node);
04855             }
04856 
04857             from->type.dataType.SetTokenType(to.GetTokenType());
04858             from->type.doubleValue = fc;
04859         }
04860         else if( from->type.dataType.IsUnsignedType() && from->type.dataType.GetSizeInMemoryDWords() == 2 )
04861         {
04862             // TODO: MSVC6 doesn't permit UINT64 to double
04863             double fc = double((signed)from->type.qwordValue);
04864 
04865             if( asQWORD(fc) != from->type.qwordValue )
04866             {
04867                 if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_NOT_EXACT, node);
04868             }
04869 
04870             from->type.dataType.SetTokenType(to.GetTokenType());
04871             from->type.doubleValue = fc;
04872         }
04873     }
04874 }
04875 
04876 int asCCompiler::DoAssignment(asSExprContext *ctx, asSExprContext *lctx, asSExprContext *rctx, asCScriptNode *lexpr, asCScriptNode *rexpr, int op, asCScriptNode *opNode)
04877 {
04878     // Implicit handle types should always be treated as handles in assignments
04879     if (lctx->type.dataType.GetObjectType() && (lctx->type.dataType.GetObjectType()->flags & asOBJ_IMPLICIT_HANDLE) )
04880     {
04881         lctx->type.dataType.MakeHandle(true);
04882         lctx->type.isExplicitHandle = true;
04883     }
04884 
04885     // If the left hand expression is a property accessor, then that should be used
04886     // to do the assignment instead of the ordinary operator. The exception is when
04887     // the property accessor is for a handle property, and the operation is a value
04888     // assignment.
04889     if( (lctx->property_get || lctx->property_set) &&
04890         !(lctx->type.dataType.IsObjectHandle() && !lctx->type.isExplicitHandle) )
04891     {
04892         if( op != ttAssignment )
04893         {
04894             // TODO: getset: We may actually be able to support this, if we can 
04895             //               guarantee that the object reference will stay valid 
04896             //               between the calls to the get and set accessors.
04897 
04898             // Compound assignments are not allowed for properties
04899             Error(TXT_COMPOUND_ASGN_WITH_PROP, opNode);
04900             return -1;
04901         }
04902 
04903         MergeExprContexts(ctx, lctx);
04904         ctx->property_get = lctx->property_get;
04905         ctx->property_set = lctx->property_set;
04906         ctx->property_const = lctx->property_const;
04907 
04908         return ProcessPropertySetAccessor(ctx, rctx, opNode);
04909     }
04910 
04911     if( lctx->type.dataType.IsPrimitive() )
04912     {
04913         if( op != ttAssignment )
04914         {
04915             // Compute the operator before the assignment
04916             asCTypeInfo lvalue = lctx->type;
04917 
04918             if( lctx->type.isTemporary && !lctx->type.isVariable )
04919             {
04920                 // The temporary variable must not be freed until the
04921                 // assignment has been performed. lvalue still holds
04922                 // the information about the temporary variable
04923                 lctx->type.isTemporary = false;
04924             }
04925 
04926             asSExprContext o(engine);
04927             CompileOperator(opNode, lctx, rctx, &o);
04928             MergeExprContexts(rctx, &o);
04929             rctx->type = o.type;
04930 
04931             // Convert the rvalue to the right type and validate it
04932             PrepareForAssignment(&lvalue.dataType, rctx, rexpr);
04933 
04934             MergeExprContexts(ctx, rctx);
04935             lctx->type = lvalue;
04936 
04937             // The lvalue continues the same, either it was a variable, or a reference in the register
04938         }
04939         else
04940         {
04941             // Convert the rvalue to the right type and validate it
04942             PrepareForAssignment(&lctx->type.dataType, rctx, rexpr, lctx);
04943 
04944             MergeExprContexts(ctx, rctx);
04945             MergeExprContexts(ctx, lctx);
04946         }
04947 
04948         ReleaseTemporaryVariable(rctx->type, &ctx->bc);
04949 
04950         PerformAssignment(&lctx->type, &rctx->type, &ctx->bc, opNode);
04951 
04952         ctx->type = lctx->type;
04953     }
04954     else if( lctx->type.isExplicitHandle )
04955     {
04956         // Verify that the left hand value isn't a temporary variable
04957         if( lctx->type.isTemporary )
04958         {
04959             Error(TXT_REF_IS_TEMP, lexpr);
04960             return -1;
04961         }
04962 
04963         // Object handles don't have any compound assignment operators
04964         if( op != ttAssignment )
04965         {
04966             asCString str;
04967             str.Format(TXT_ILLEGAL_OPERATION_ON_s, lctx->type.dataType.Format().AddressOf());
04968             Error(str.AddressOf(), lexpr);
04969             return -1;
04970         }
04971 
04972         asCDataType dt = lctx->type.dataType;
04973         dt.MakeReference(false);
04974 
04975         PrepareArgument(&dt, rctx, rexpr, true, 1);
04976         if( !dt.IsEqualExceptRefAndConst(rctx->type.dataType) )
04977         {
04978             asCString str;
04979             str.Format(TXT_CANT_IMPLICITLY_CONVERT_s_TO_s, rctx->type.dataType.Format().AddressOf(), lctx->type.dataType.Format().AddressOf());
04980             Error(str.AddressOf(), rexpr);
04981             return -1;
04982         }
04983 
04984         MergeExprContexts(ctx, rctx);
04985         MergeExprContexts(ctx, lctx);
04986 
04987         ctx->bc.InstrWORD(asBC_GETOBJREF, AS_PTR_SIZE);
04988 
04989         PerformAssignment(&lctx->type, &rctx->type, &ctx->bc, opNode);
04990 
04991         ReleaseTemporaryVariable(rctx->type, &ctx->bc);
04992 
04993         ctx->type = rctx->type;
04994     }
04995     else // if( lctx->type.dataType.IsObject() )
04996     {
04997         // Verify that the left hand value isn't a temporary variable
04998         if( lctx->type.isTemporary )
04999         {
05000             Error(TXT_REF_IS_TEMP, lexpr);
05001             return -1;
05002         }
05003 
05004         if( lctx->type.dataType.IsObjectHandle() && !lctx->type.isExplicitHandle )
05005         {
05006             // Convert the handle to a object reference
05007             asCDataType to;
05008             to = lctx->type.dataType;
05009             to.MakeHandle(false);
05010             ImplicitConversion(lctx, to, lexpr, asIC_IMPLICIT_CONV);
05011         }
05012 
05013         // Check for overloaded assignment operator
05014         if( CompileOverloadedDualOperator(opNode, lctx, rctx, ctx) )
05015         {
05016             // An overloaded assignment operator was found (or a compilation error occured)
05017             return 0;
05018         }
05019 
05020 #ifdef AS_DEPRECATED
05021 // deprecated since 2009-07-20, 2.17.0
05022         // If left expression resolves into a registered type
05023         // check if the assignment operator is overloaded, and check
05024         // the type of the right hand expression. If none is found
05025         // the default action is a direct copy if it is the same type
05026         // and a simple assignment.
05027         asSTypeBehaviour *beh = lctx->type.dataType.GetBehaviour();
05028         asASSERT(beh);
05029 
05030         int behaviour = TokenToBehaviour(op);
05031 
05032         // Find the matching overloaded operators
05033         asCArray<int> ops;
05034         asUINT n;
05035         for( n = 0; n < beh->operators.GetLength(); n += 2 )
05036         {
05037             if( behaviour == beh->operators[n] )
05038                 ops.PushLast(beh->operators[n+1]);
05039         }
05040 
05041         asCArray<int> match;
05042         MatchArgument(ops, match, &rctx->type, 0);
05043 
05044         if( match.GetLength() == 1 )
05045         {
05046             // We must verify that the lvalue isn't const
05047             if( lctx->type.dataType.IsReadOnly() )
05048             {
05049                 Error(TXT_REF_IS_READ_ONLY, lexpr);
05050                 return -1;
05051             }
05052 
05053             // Prepare the rvalue
05054             asCScriptFunction *descr = engine->scriptFunctions[match[0]];
05055             PrepareArgument(&descr->parameterTypes[0], rctx, rexpr, true, descr->inOutFlags[0]);
05056 
05057             if( rctx->type.isTemporary && lctx->bc.IsVarUsed(rctx->type.stackOffset) )
05058             {
05059                 // Release the current temporary variable
05060                 ReleaseTemporaryVariable(rctx->type, 0);
05061 
05062                 asCArray<int> usedVars;
05063                 lctx->bc.GetVarsUsed(usedVars);
05064                 rctx->bc.GetVarsUsed(usedVars);
05065 
05066                 asCDataType dt = rctx->type.dataType;
05067                 dt.MakeReference(false);
05068                 int newOffset = AllocateVariableNotIn(dt, true, &usedVars);
05069 
05070                 rctx->bc.ExchangeVar(rctx->type.stackOffset, newOffset);
05071                 rctx->type.stackOffset = (short)newOffset;
05072                 rctx->type.isTemporary = true;
05073                 rctx->type.isVariable = true;
05074             }
05075 
05076             // Add code for arguments
05077             MergeExprContexts(ctx, rctx);
05078 
05079             // Add the code for the object
05080             Dereference(lctx, true);
05081             MergeExprContexts(ctx, lctx);
05082 
05083             asCArray<asSExprContext*> args;
05084             args.PushLast(rctx);
05085             MoveArgsToStack(match[0], &ctx->bc, args, true);
05086 
05087             PerformFunctionCall(match[0], ctx, false, &args);
05088 
05089             return 0;
05090         }
05091         else if( match.GetLength() > 1 )
05092         {
05093             Error(TXT_MORE_THAN_ONE_MATCHING_OP, opNode);
05094 
05095             ctx->type.Set(lctx->type.dataType);
05096 
05097             return -1;
05098         }
05099 #endif
05100 
05101         // No registered operator was found. In case the operation is a direct
05102         // assignment and the rvalue is the same type as the lvalue, then we can
05103         // still use the byte-for-byte copy to do the assignment
05104 
05105         if( op != ttAssignment )
05106         {
05107             asCString str;
05108             str.Format(TXT_ILLEGAL_OPERATION_ON_s, lctx->type.dataType.Format().AddressOf());
05109             Error(str.AddressOf(), lexpr);
05110             return -1;
05111         }
05112 
05113         // Implicitly convert the rvalue to the type of the lvalue
05114         asCDataType dt = lctx->type.dataType;
05115         PrepareArgument(&dt, rctx, rexpr, true, 1);
05116         if( !dt.IsEqualExceptRefAndConst(rctx->type.dataType) )
05117         {
05118             asCString str;
05119             str.Format(TXT_CANT_IMPLICITLY_CONVERT_s_TO_s, rctx->type.dataType.Format().AddressOf(), lctx->type.dataType.Format().AddressOf());
05120             Error(str.AddressOf(), rexpr);
05121             return -1;
05122         }
05123 
05124         MergeExprContexts(ctx, rctx);
05125         MergeExprContexts(ctx, lctx);
05126 
05127         ctx->bc.InstrWORD(asBC_GETOBJREF, AS_PTR_SIZE);
05128 
05129         PerformAssignment(&lctx->type, &rctx->type, &ctx->bc, opNode);
05130 
05131         ReleaseTemporaryVariable(rctx->type, &ctx->bc);
05132 
05133         ctx->type = lctx->type;
05134     }
05135 
05136     return 0;
05137 }
05138 
05139 int asCCompiler::CompileAssignment(asCScriptNode *expr, asSExprContext *ctx)
05140 {
05141     asCScriptNode *lexpr = expr->firstChild;
05142     if( lexpr->next )
05143     {
05144         if( globalExpression )
05145         {
05146             Error(TXT_ASSIGN_IN_GLOBAL_EXPR, expr);
05147             ctx->type.SetDummy();
05148             return -1;
05149         }
05150 
05151         // Compile the two expression terms
05152         asSExprContext lctx(engine), rctx(engine);
05153         int rr = CompileAssignment(lexpr->next->next, &rctx);
05154         int lr = CompileCondition(lexpr, &lctx);
05155 
05156         if( lr >= 0 && rr >= 0 )
05157             return DoAssignment(ctx, &lctx, &rctx, lexpr, lexpr->next->next, lexpr->next->tokenType, lexpr->next);
05158 
05159         // Since the operands failed, the assignment was not computed
05160         ctx->type.SetDummy();
05161         return -1;
05162     }
05163 
05164     return CompileCondition(lexpr, ctx);
05165 }
05166 
05167 int asCCompiler::CompileCondition(asCScriptNode *expr, asSExprContext *ctx)
05168 {
05169     asCTypeInfo ctype;
05170 
05171     // Compile the conditional expression
05172     asCScriptNode *cexpr = expr->firstChild;
05173     if( cexpr->next )
05174     {
05175         //-------------------------------
05176         // Compile the condition
05177         asSExprContext e(engine);
05178         int r = CompileExpression(cexpr, &e);
05179         if( r < 0 )
05180             e.type.SetConstantB(asCDataType::CreatePrimitive(ttBool, true), true);
05181         if( r >= 0 && !e.type.dataType.IsEqualExceptRefAndConst(asCDataType::CreatePrimitive(ttBool, true)) )
05182         {
05183             Error(TXT_EXPR_MUST_BE_BOOL, cexpr);
05184             e.type.SetConstantB(asCDataType::CreatePrimitive(ttBool, true), true);
05185         }
05186         ctype = e.type;
05187 
05188         ProcessPropertyGetAccessor(&e, cexpr);
05189 
05190         if( e.type.dataType.IsReference() ) ConvertToVariable(&e);
05191         ProcessDeferredParams(&e);
05192 
05193         //-------------------------------
05194         // Compile the left expression
05195         asSExprContext le(engine);
05196         int lr = CompileAssignment(cexpr->next, &le);
05197 
05198         //-------------------------------
05199         // Compile the right expression
05200         asSExprContext re(engine);
05201         int rr = CompileAssignment(cexpr->next->next, &re);
05202 
05203         if( lr >= 0 && rr >= 0 )
05204         {
05205             ProcessPropertyGetAccessor(&le, cexpr->next);
05206             ProcessPropertyGetAccessor(&re, cexpr->next->next);
05207 
05208             bool isExplicitHandle = le.type.isExplicitHandle || re.type.isExplicitHandle;
05209 
05210             // Allow a 0 in the first case to be implicitly converted to the second type
05211             if( le.type.isConstant && le.type.intValue == 0 && le.type.dataType.IsUnsignedType() )
05212             {
05213                 asCDataType to = re.type.dataType;
05214                 to.MakeReference(false);
05215                 to.MakeReadOnly(true);
05216                 ImplicitConversionConstant(&le, to, cexpr->next, asIC_IMPLICIT_CONV);
05217             }
05218 
05219             //---------------------------------
05220             // Output the byte code
05221             int afterLabel = nextLabel++;
05222             int elseLabel = nextLabel++;
05223 
05224             // If left expression is void, then we don't need to store the result
05225             if( le.type.dataType.IsEqualExceptConst(asCDataType::CreatePrimitive(ttVoid, false)) )
05226             {
05227                 // Put the code for the condition expression on the output
05228                 MergeExprContexts(ctx, &e);
05229 
05230                 // Added the branch decision
05231                 ctx->type = e.type;
05232                 ConvertToVariable(ctx);
05233                 ctx->bc.InstrSHORT(asBC_CpyVtoR4, ctx->type.stackOffset);
05234                 ctx->bc.Instr(asBC_ClrHi);
05235                 ctx->bc.InstrDWORD(asBC_JZ, elseLabel);
05236                 ReleaseTemporaryVariable(ctx->type, &ctx->bc);
05237 
05238                 // Add the left expression
05239                 MergeExprContexts(ctx, &le);
05240                 ctx->bc.InstrINT(asBC_JMP, afterLabel);
05241 
05242                 // Add the right expression
05243                 ctx->bc.Label((short)elseLabel);
05244                 MergeExprContexts(ctx, &re);
05245                 ctx->bc.Label((short)afterLabel);
05246 
05247                 // Make sure both expressions have the same type
05248                 if( le.type.dataType != re.type.dataType )
05249                     Error(TXT_BOTH_MUST_BE_SAME, expr);
05250 
05251                 // Set the type of the result
05252                 ctx->type = le.type;
05253             }
05254             else
05255             {
05256                 // Allocate temporary variable and copy the result to that one
05257                 asCTypeInfo temp;
05258                 temp = le.type;
05259                 temp.dataType.MakeReference(false);
05260                 temp.dataType.MakeReadOnly(false);
05261                 // Make sure the variable isn't used in the initial expression
05262                 asCArray<int> vars;
05263                 e.bc.GetVarsUsed(vars);
05264                 int offset = AllocateVariableNotIn(temp.dataType, true, &vars);
05265                 temp.SetVariable(temp.dataType, offset, true);
05266 
05267                 CallDefaultConstructor(temp.dataType, offset, &ctx->bc, expr);
05268 
05269                 // Put the code for the condition expression on the output
05270                 MergeExprContexts(ctx, &e);
05271 
05272                 // Added the branch decision
05273                 ctx->type = e.type;
05274                 ConvertToVariable(ctx);
05275                 ctx->bc.InstrSHORT(asBC_CpyVtoR4, ctx->type.stackOffset);
05276                 ctx->bc.Instr(asBC_ClrHi);
05277                 ctx->bc.InstrDWORD(asBC_JZ, elseLabel);
05278                 ReleaseTemporaryVariable(ctx->type, &ctx->bc);
05279 
05280                 // Assign the result of the left expression to the temporary variable
05281                 asCTypeInfo rtemp;
05282                 rtemp = temp;
05283                 if( rtemp.dataType.IsObjectHandle() )
05284                     rtemp.isExplicitHandle = true;
05285 
05286                 PrepareForAssignment(&rtemp.dataType, &le, cexpr->next);
05287                 MergeExprContexts(ctx, &le);
05288 
05289                 if( !rtemp.dataType.IsPrimitive() )
05290                 {
05291                     ctx->bc.InstrSHORT(asBC_PSF, (short)offset);
05292                     rtemp.dataType.MakeReference(true);
05293                 }
05294                 PerformAssignment(&rtemp, &le.type, &ctx->bc, cexpr->next);
05295                 if( !rtemp.dataType.IsPrimitive() )
05296                     ctx->bc.Pop(le.type.dataType.GetSizeOnStackDWords()); // Pop the original value
05297 
05298                 // Release the old temporary variable
05299                 ReleaseTemporaryVariable(le.type, &ctx->bc);
05300 
05301                 ctx->bc.InstrINT(asBC_JMP, afterLabel);
05302 
05303                 // Start of the right expression
05304                 ctx->bc.Label((short)elseLabel);
05305 
05306                 // Copy the result to the same temporary variable
05307                 PrepareForAssignment(&rtemp.dataType, &re, cexpr->next);
05308                 MergeExprContexts(ctx, &re);
05309 
05310                 if( !rtemp.dataType.IsPrimitive() )
05311                 {
05312                     ctx->bc.InstrSHORT(asBC_PSF, (short)offset);
05313                     rtemp.dataType.MakeReference(true);
05314                 }
05315                 PerformAssignment(&rtemp, &re.type, &ctx->bc, cexpr->next);
05316                 if( !rtemp.dataType.IsPrimitive() )
05317                     ctx->bc.Pop(le.type.dataType.GetSizeOnStackDWords()); // Pop the original value
05318 
05319                 // Release the old temporary variable
05320                 ReleaseTemporaryVariable(re.type, &ctx->bc);
05321 
05322                 ctx->bc.Label((short)afterLabel);
05323 
05324                 // Make sure both expressions have the same type
05325                 if( le.type.dataType != re.type.dataType )
05326                     Error(TXT_BOTH_MUST_BE_SAME, expr);
05327 
05328                 // Set the temporary variable as output
05329                 ctx->type = rtemp;
05330                 ctx->type.isExplicitHandle = isExplicitHandle;
05331 
05332                 if( !ctx->type.dataType.IsPrimitive() )
05333                 {
05334                     ctx->bc.InstrSHORT(asBC_PSF, (short)offset);
05335                     ctx->type.dataType.MakeReference(true);
05336                 }
05337 
05338                 // Make sure the output isn't marked as being a literal constant
05339                 ctx->type.isConstant = false;
05340             }
05341         }
05342         else
05343         {
05344             ctx->type.SetDummy();
05345             return -1;
05346         }
05347     }
05348     else
05349         return CompileExpression(cexpr, ctx);
05350 
05351     return 0;
05352 }
05353 
05354 int asCCompiler::CompileExpression(asCScriptNode *expr, asSExprContext *ctx)
05355 {
05356     asASSERT(expr->nodeType == snExpression);
05357 
05358     // Count the nodes
05359     int count = 0;
05360     asCScriptNode *node = expr->firstChild;
05361     while( node )
05362     {
05363         count++;
05364         node = node->next;
05365     }
05366 
05367     // Convert to polish post fix, i.e: a+b => ab+
05368     asCArray<asCScriptNode *> stack(count);
05369     asCArray<asCScriptNode *> stack2(count);
05370     asCArray<asCScriptNode *> postfix(count);
05371 
05372     node = expr->firstChild;
05373     while( node )
05374     {
05375         int precedence = GetPrecedence(node);
05376 
05377         while( stack.GetLength() > 0 &&
05378                precedence <= GetPrecedence(stack[stack.GetLength()-1]) )
05379             stack2.PushLast(stack.PopLast());
05380 
05381         stack.PushLast(node);
05382 
05383         node = node->next;
05384     }
05385 
05386     while( stack.GetLength() > 0 )
05387         stack2.PushLast(stack.PopLast());
05388 
05389     // We need to swap operands so that the left
05390     // operand is always computed before the right
05391     SwapPostFixOperands(stack2, postfix);
05392 
05393     // Compile the postfix formatted expression
05394     return CompilePostFixExpression(&postfix, ctx);
05395 }
05396 
05397 void asCCompiler::SwapPostFixOperands(asCArray<asCScriptNode *> &postfix, asCArray<asCScriptNode *> &target)
05398 {
05399     if( postfix.GetLength() == 0 ) return;
05400 
05401     asCScriptNode *node = postfix.PopLast();
05402     if( node->nodeType == snExprTerm )
05403     {
05404         target.PushLast(node);
05405         return;
05406     }
05407 
05408     SwapPostFixOperands(postfix, target);
05409     SwapPostFixOperands(postfix, target);
05410 
05411     target.PushLast(node);
05412 }
05413 
05414 int asCCompiler::CompilePostFixExpression(asCArray<asCScriptNode *> *postfix, asSExprContext *ctx)
05415 {
05416     // Shouldn't send any byte code
05417     asASSERT(ctx->bc.GetLastInstr() == -1);
05418 
05419     // Pop the last node
05420     asCScriptNode *node = postfix->PopLast();
05421     ctx->exprNode = node;
05422 
05423     // If term, compile the term
05424     if( node->nodeType == snExprTerm )
05425         return CompileExpressionTerm(node, ctx);
05426 
05427     // Compile the two expression terms
05428     asSExprContext r(engine), l(engine);
05429 
05430     int ret;
05431     ret = CompilePostFixExpression(postfix, &l); if( ret < 0 ) return ret;
05432     ret = CompilePostFixExpression(postfix, &r); if( ret < 0 ) return ret;
05433 
05434     // Compile the operation
05435     return CompileOperator(node, &l, &r, ctx);
05436 }
05437 
05438 int asCCompiler::CompileExpressionTerm(asCScriptNode *node, asSExprContext *ctx)
05439 {
05440     // Shouldn't send any byte code
05441     asASSERT(ctx->bc.GetLastInstr() == -1);
05442 
05443     // Set the type as a dummy by default, in case of any compiler errors
05444     ctx->type.SetDummy();
05445 
05446     // Compile the value node
05447     asCScriptNode *vnode = node->firstChild;
05448     while( vnode->nodeType != snExprValue )
05449         vnode = vnode->next;
05450 
05451     asSExprContext v(engine);
05452     int r = CompileExpressionValue(vnode, &v); if( r < 0 ) return r;
05453 
05454     // Compile post fix operators
05455     asCScriptNode *pnode = vnode->next;
05456     while( pnode )
05457     {
05458         // TODO: getset: Previous value is accessed as get
05459 
05460         r = CompileExpressionPostOp(pnode, &v); if( r < 0 ) return r;
05461         pnode = pnode->next;
05462     }
05463 
05464     // Compile pre fix operators
05465     pnode = vnode->prev;
05466     while( pnode )
05467     {
05468         // TODO: getset: Previous value is accessed as get
05469 
05470         r = CompileExpressionPreOp(pnode, &v); if( r < 0 ) return r;
05471         pnode = pnode->prev;
05472     }
05473 
05474     // Return the byte code and final type description
05475     MergeExprContexts(ctx, &v);
05476 
05477     ctx->type = v.type;
05478     ctx->property_get = v.property_get;
05479     ctx->property_set = v.property_set;
05480     ctx->property_const = v.property_const;
05481 
05482     return 0;
05483 }
05484 
05485 int asCCompiler::CompileExpressionValue(asCScriptNode *node, asSExprContext *ctx)
05486 {
05487     // Shouldn't receive any byte code
05488     asASSERT(ctx->bc.GetLastInstr() == -1);
05489 
05490     asCScriptNode *vnode = node->firstChild;
05491     if( vnode->nodeType == snVariableAccess )
05492     {
05493         // Determine the scope resolution of the variable
05494         asCString scope = GetScopeFromNode(vnode);
05495 
05496         // Determine the name of the variable
05497         vnode = vnode->lastChild;
05498         asASSERT(vnode->nodeType == snIdentifier );
05499         asCString name(&script->code[vnode->tokenPos], vnode->tokenLength);
05500 
05501         sVariable *v = 0;
05502         if( scope == "" )
05503             v = variables->GetVariable(name.AddressOf());
05504         if( v == 0 )
05505         {
05506             // It is not a local variable or parameter
05507             bool found = false;
05508 
05509             // Is it a class member?
05510             if( outFunc && outFunc->objectType && scope == "" )
05511             {
05512                 if( name == THIS_TOKEN )
05513                 {
05514                     asCDataType dt = asCDataType::CreateObject(outFunc->objectType, outFunc->isReadOnly);
05515 
05516                     // The object pointer is located at stack position 0
05517                     ctx->bc.InstrSHORT(asBC_PSF, 0);
05518                     ctx->type.SetVariable(dt, 0, false);
05519                     ctx->type.dataType.MakeReference(true);
05520 
05521                     found = true;
05522                 }
05523 
05524                 if( !found )
05525                 {
05526                     // See if there are any matching property accessors
05527                     asSExprContext access(engine);
05528                     access.type.Set(asCDataType::CreateObject(outFunc->objectType, outFunc->isReadOnly));
05529                     int r = FindPropertyAccessor(name, &access, node);
05530                     if( r < 0 ) return -1;
05531                     if( access.property_get || access.property_set )
05532                     {
05533                         // Prepare the bytecode for the member access
05534                         ctx->bc.InstrSHORT(asBC_PSF, 0);
05535                         ctx->type.SetVariable(asCDataType::CreateObject(outFunc->objectType, outFunc->isReadOnly), 0, false);
05536                         ctx->type = access.type;
05537                         ctx->property_get = access.property_get;
05538                         ctx->property_set = access.property_set;
05539                         ctx->property_const = access.property_const;
05540 
05541                         found = true;
05542                     }
05543                 }
05544 
05545                 if( !found )
05546                 {
05547                     asCDataType dt = asCDataType::CreateObject(outFunc->objectType, false);
05548                     asCObjectProperty *prop = builder->GetObjectProperty(dt, name.AddressOf());
05549                     if( prop )
05550                     {
05551                         // The object pointer is located at stack position 0
05552                         ctx->bc.InstrSHORT(asBC_PSF, 0);
05553                         ctx->type.SetVariable(dt, 0, false);
05554                         ctx->type.dataType.MakeReference(true);
05555 
05556                         Dereference(ctx, true);
05557 
05558                         // TODO: This is the same as what is in CompileExpressionPostOp
05559                         // Put the offset on the stack
05560                         ctx->bc.InstrINT(asBC_ADDSi, prop->byteOffset);
05561 
05562                         if( prop->type.IsReference() )
05563                             ctx->bc.Instr(asBC_RDSPTR);
05564 
05565                         // Reference to primitive must be stored in the temp register
05566                         if( prop->type.IsPrimitive() )
05567                         {
05568                             // The ADD offset command should store the reference in the register directly
05569                             ctx->bc.Instr(asBC_PopRPtr);
05570                         }
05571 
05572                         // Set the new type (keeping info about temp variable)
05573                         ctx->type.dataType = prop->type;
05574                         ctx->type.dataType.MakeReference(true);
05575                         ctx->type.isVariable = false;
05576 
05577                         if( ctx->type.dataType.IsObject() && !ctx->type.dataType.IsObjectHandle() )
05578                         {
05579                             // Objects that are members are not references
05580                             ctx->type.dataType.MakeReference(false);
05581                         }
05582 
05583                         // If the object reference is const, the property will also be const
05584                         ctx->type.dataType.MakeReadOnly(outFunc->isReadOnly);
05585 
05586                         found = true;
05587                     }
05588                 }
05589             }
05590 
05591             // Is it a global property?
05592             if( !found && (scope == "" || scope == "::") )
05593             {
05594                 bool isCompiled = true;
05595                 bool isPureConstant = false;
05596                 asQWORD constantValue;
05597                 asCGlobalProperty *prop = builder->GetGlobalProperty(name.AddressOf(), &isCompiled, &isPureConstant, &constantValue);
05598                 if( prop )
05599                 {
05600                     found = true;
05601 
05602                     // Verify that the global property has been compiled already
05603                     if( isCompiled )
05604                     {
05605                         if( ctx->type.dataType.GetObjectType() && (ctx->type.dataType.GetObjectType()->flags & asOBJ_IMPLICIT_HANDLE) )
05606                         {
05607                             ctx->type.dataType.MakeHandle(true);
05608                             ctx->type.isExplicitHandle = true;
05609                         }
05610 
05611                         // If the global property is a pure constant
05612                         // we can allow the compiler to optimize it. Pure
05613                         // constants are global constant variables that were
05614                         // initialized by literal constants.
05615                         if( isPureConstant )
05616                             ctx->type.SetConstantQW(prop->type, constantValue);
05617                         else
05618                         {
05619                             ctx->type.Set(prop->type);
05620                             ctx->type.dataType.MakeReference(true);
05621 
05622                             // Push the address of the variable on the stack
05623                             if( ctx->type.dataType.IsPrimitive() )
05624                                 ctx->bc.InstrWORD(asBC_LDG, (asWORD)builder->module->GetGlobalVarPtrIndex(prop->id));
05625                             else
05626                                 ctx->bc.InstrWORD(asBC_PGA, (asWORD)builder->module->GetGlobalVarPtrIndex(prop->id));
05627                         }
05628                     }
05629                     else
05630                     {
05631                         asCString str;
05632                         str.Format(TXT_UNINITIALIZED_GLOBAL_VAR_s, prop->name.AddressOf());
05633                         Error(str.AddressOf(), vnode);
05634                         return -1;
05635                     }
05636                 }
05637             }
05638 
05639             if( !found )
05640             {
05641                 asCObjectType *scopeType = 0;
05642                 if( scope != "" )
05643                 {
05644                     // resolve the type before the scope
05645                     scopeType = builder->GetObjectType( scope.AddressOf() );
05646                 }
05647 
05648                 // Is it an enum value?
05649                 asDWORD value = 0;
05650                 asCDataType dt;
05651                 if( scopeType && builder->GetEnumValueFromObjectType(scopeType, name.AddressOf(), dt, value) )
05652                 {
05653                     // scoped enum value found
05654                     found = true;
05655                 }
05656                 else if( scope == "" && !engine->ep.requireEnumScope )
05657                 {
05658                     // look for the enum value with no namespace
05659                     int e = builder->GetEnumValue(name.AddressOf(), dt, value);
05660                     if( e )
05661                     {
05662                         found = true;
05663                         if( e == 2 )
05664                         {
05665                             Error(TXT_FOUND_MULTIPLE_ENUM_VALUES, vnode);
05666                         }
05667                     }
05668                 }
05669 
05670                 if( found )
05671                 {
05672                     // an enum value was resolved
05673                     ctx->type.SetConstantDW(dt, value);
05674                 }
05675             }
05676 
05677             if( !found )
05678             {
05679                 // Prepend the scope to the name for the error message
05680                 if( scope != "" && scope != "::" )
05681                     scope += "::";
05682                 scope += name;
05683 
05684                 asCString str;
05685                 str.Format(TXT_s_NOT_DECLARED, scope.AddressOf());
05686                 Error(str.AddressOf(), vnode);
05687 
05688                 // Give dummy value
05689                 ctx->type.SetDummy();
05690 
05691                 // Declare the variable now so that it will not be reported again
05692                 variables->DeclareVariable(name.AddressOf(), asCDataType::CreatePrimitive(ttInt, false), 0x7FFF);
05693 
05694                 // Mark the variable as initialized so that the user will not be bother by it again
05695                 sVariable *v = variables->GetVariable(name.AddressOf());
05696                 asASSERT(v);
05697                 if( v ) v->isInitialized = true;
05698 
05699                 return -1;
05700             }
05701         }
05702         else
05703         {
05704             // It is a variable or parameter
05705 
05706             if( v->isPureConstant )
05707                 ctx->type.SetConstantQW(v->type, v->constantValue);
05708             else
05709             {
05710                 if( v->type.IsPrimitive() )
05711                 {
05712                     if( v->type.IsReference() )
05713                     {
05714                         // Copy the reference into the register
05715 #if AS_PTR_SIZE == 1
05716                         ctx->bc.InstrSHORT(asBC_CpyVtoR4, (short)v->stackOffset);
05717 #else
05718                         ctx->bc.InstrSHORT(asBC_CpyVtoR8, (short)v->stackOffset);
05719 #endif
05720                         ctx->type.Set(v->type);
05721                     }
05722                     else
05723                         ctx->type.SetVariable(v->type, v->stackOffset, false);
05724                 }
05725                 else
05726                 {
05727                     ctx->bc.InstrSHORT(asBC_PSF, (short)v->stackOffset);
05728                     ctx->type.SetVariable(v->type, v->stackOffset, false);
05729                     ctx->type.dataType.MakeReference(true);
05730 
05731                     // Implicitly dereference handle parameters sent by reference
05732                     if( v->type.IsReference() && (!v->type.IsObject() || v->type.IsObjectHandle()) )
05733                         ctx->bc.Instr(asBC_RDSPTR);
05734                 }
05735             }
05736         }
05737     }
05738     else if( vnode->nodeType == snConstant )
05739     {
05740         if( vnode->tokenType == ttIntConstant )
05741         {
05742             asCString value(&script->code[vnode->tokenPos], vnode->tokenLength);
05743 
05744             asQWORD val = asStringScanUInt64(value.AddressOf(), 10, 0);
05745 
05746             // Do we need 64 bits?
05747             if( val>>32 )
05748                 ctx->type.SetConstantQW(asCDataType::CreatePrimitive(ttUInt64, true), val);
05749             else
05750                 ctx->type.SetConstantDW(asCDataType::CreatePrimitive(ttUInt, true), asDWORD(val));
05751         }
05752         else if( vnode->tokenType == ttBitsConstant )
05753         {
05754             asCString value(&script->code[vnode->tokenPos+2], vnode->tokenLength-2);
05755 
05756             // TODO: Check for overflow
05757             asQWORD val = asStringScanUInt64(value.AddressOf(), 16, 0);
05758 
05759             // Do we need 64 bits?
05760             if( val>>32 )
05761                 ctx->type.SetConstantQW(asCDataType::CreatePrimitive(ttUInt64, true), val);
05762             else
05763                 ctx->type.SetConstantDW(asCDataType::CreatePrimitive(ttUInt, true), asDWORD(val));
05764         }
05765         else if( vnode->tokenType == ttFloatConstant )
05766         {
05767             asCString value(&script->code[vnode->tokenPos], vnode->tokenLength);
05768 
05769             // TODO: Check for overflow
05770 
05771             size_t numScanned;
05772             float v = float(