PageRenderTime 164ms CodeModel.GetById 15ms app.highlight 122ms RepoModel.GetById 2ms app.codeStats 2ms

/xbmc/visualizations/Vortex/angelscript/angelscript/source/as_compiler.cpp

http://github.com/xbmc/xbmc
C++ | 9536 lines | 6933 code | 1388 blank | 1215 comment | 2755 complexity | a92e1baea38a8a3db634ec40d054c6da MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1/*
   2   AngelCode Scripting Library
   3   Copyright (c) 2003-2009 Andreas Jonsson
   4
   5   This software is provided 'as-is', without any express or implied
   6   warranty. In no event will the authors be held liable for any
   7   damages arising from the use of this software.
   8
   9   Permission is granted to anyone to use this software for any
  10   purpose, including commercial applications, and to alter it and
  11   redistribute it freely, subject to the following restrictions:
  12
  13   1. The origin of this software must not be misrepresented; you
  14      must not claim that you wrote the original software. If you use
  15      this software in a product, an acknowledgment in the product
  16      documentation would be appreciated but is not required.
  17
  18   2. Altered source versions must be plainly marked as such, and
  19      must not be misrepresented as being the original software.
  20
  21   3. This notice may not be removed or altered from any source
  22      distribution.
  23
  24   The original version of this library can be located at:
  25   http://www.angelcode.com/angelscript/
  26
  27   Andreas Jonsson
  28   andreas@angelcode.com
  29*/
  30
  31
  32//
  33// as_compiler.cpp
  34//
  35// The class that does the actual compilation of the functions
  36//
  37
  38#include <math.h> // fmodf()
  39
  40#include "as_config.h"
  41#include "as_compiler.h"
  42#include "as_tokendef.h"
  43#include "as_tokenizer.h"
  44#include "as_string_util.h"
  45#include "as_texts.h"
  46#include "as_parser.h"
  47
  48BEGIN_AS_NAMESPACE
  49
  50asCCompiler::asCCompiler(asCScriptEngine *engine) : byteCode(engine)
  51{
  52	builder = 0;
  53	script = 0;
  54
  55	variables = 0;
  56	isProcessingDeferredParams = false;
  57	noCodeOutput = 0;
  58}
  59
  60asCCompiler::~asCCompiler()
  61{
  62	while( variables )
  63	{
  64		asCVariableScope *var = variables;
  65		variables = variables->parent;
  66
  67		asDELETE(var,asCVariableScope);
  68	}
  69}
  70
  71void asCCompiler::Reset(asCBuilder *builder, asCScriptCode *script, asCScriptFunction *outFunc)
  72{
  73	this->builder = builder;
  74	this->engine = builder->engine;
  75	this->script = script;
  76	this->outFunc = outFunc;
  77
  78	hasCompileErrors = false;
  79
  80	m_isConstructor = false;
  81	m_isConstructorCalled = false;
  82
  83	nextLabel = 0;
  84	breakLabels.SetLength(0);
  85	continueLabels.SetLength(0);
  86
  87	byteCode.ClearAll();
  88	objVariableTypes.SetLength(0);
  89	objVariablePos.SetLength(0);
  90
  91	globalExpression = false;
  92}
  93
  94int asCCompiler::CompileDefaultConstructor(asCBuilder *builder, asCScriptCode *script, asCScriptFunction *outFunc)
  95{
  96	Reset(builder, script, outFunc);
  97
  98	// If the class is derived from another, then the base class' default constructor must be called
  99	if( outFunc->objectType->derivedFrom )
 100	{
 101		// Call the base class' default constructor
 102		byteCode.InstrSHORT(asBC_PSF, 0);
 103		byteCode.Instr(asBC_RDSPTR);
 104		byteCode.Call(asBC_CALL, outFunc->objectType->derivedFrom->beh.construct, AS_PTR_SIZE);
 105	}
 106
 107	// Pop the object pointer from the stack
 108	byteCode.Ret(AS_PTR_SIZE);
 109
 110	byteCode.Finalize();
 111
 112	// Copy byte code to the function
 113	outFunc->byteCode.SetLength(byteCode.GetSize());
 114	byteCode.Output(outFunc->byteCode.AddressOf());
 115	outFunc->AddReferences();
 116	outFunc->stackNeeded = byteCode.largestStackUsed;
 117	outFunc->lineNumbers = byteCode.lineNumbers;
 118	outFunc->objVariablePos = objVariablePos;
 119	outFunc->objVariableTypes = objVariableTypes;
 120
 121#ifdef AS_DEBUG
 122	// DEBUG: output byte code
 123	byteCode.DebugOutput(("__" + outFunc->objectType->name + "_" + outFunc->name + "__dc.txt").AddressOf(), engine);
 124#endif
 125
 126	return 0;
 127}
 128
 129int asCCompiler::CompileFactory(asCBuilder *builder, asCScriptCode *script, asCScriptFunction *outFunc)
 130{
 131	Reset(builder, script, outFunc);
 132
 133	unsigned int n;
 134
 135	// Find the corresponding constructor
 136	asCDataType dt = asCDataType::CreateObject(outFunc->returnType.GetObjectType(), false);
 137	int constructor = 0;
 138	for( n = 0; n < dt.GetBehaviour()->factories.GetLength(); n++ )
 139	{
 140		if( dt.GetBehaviour()->factories[n] == outFunc->id )
 141		{
 142			constructor = dt.GetBehaviour()->constructors[n];
 143			break;
 144		}
 145	}
 146
 147	// Allocate the class and instanciate it with the constructor
 148	int varOffset = AllocateVariable(dt, true);
 149
 150	byteCode.Push(AS_PTR_SIZE);
 151	byteCode.InstrSHORT(asBC_PSF, (short)varOffset);
 152
 153	// Copy all arguments to the top of the stack
 154	int argDwords = (int)outFunc->GetSpaceNeededForArguments();
 155	for( int a = argDwords-1; a >= 0; a-- )
 156		byteCode.InstrSHORT(asBC_PshV4, short(-a));
 157
 158	byteCode.Alloc(asBC_ALLOC, dt.GetObjectType(), constructor, argDwords + AS_PTR_SIZE);
 159
 160	// Return a handle to the newly created object
 161	byteCode.InstrSHORT(asBC_LOADOBJ, (short)varOffset);
 162
 163	byteCode.Pop(AS_PTR_SIZE);
 164	byteCode.Ret(argDwords);
 165
 166	byteCode.Finalize();
 167
 168	// Store the instantiated object as variable so it will be cleaned up on exception
 169	objVariableTypes.PushLast(variableAllocations[0].GetObjectType());
 170	objVariablePos.PushLast(GetVariableOffset(0));
 171
 172	// Copy byte code to the function
 173	outFunc->byteCode.SetLength(byteCode.GetSize());
 174	byteCode.Output(outFunc->byteCode.AddressOf());
 175	outFunc->AddReferences();
 176	outFunc->stackNeeded = byteCode.largestStackUsed;
 177	outFunc->lineNumbers = byteCode.lineNumbers;
 178	outFunc->objVariablePos = objVariablePos;
 179	outFunc->objVariableTypes = objVariableTypes;
 180
 181	// Tell the virtual machine not to clean up parameters on exception
 182	outFunc->dontCleanUpOnException = true;
 183
 184/*
 185#ifdef AS_DEBUG
 186	// DEBUG: output byte code
 187	asCString args;
 188	args.Format("%d", outFunc->parameterTypes.GetLength());
 189	byteCode.DebugOutput(("__" + outFunc->name + "__factory" + args + ".txt").AddressOf(), engine);
 190#endif
 191*/
 192	return 0;
 193}
 194
 195int asCCompiler::CompileTemplateFactoryStub(asCBuilder *builder, int trueFactoryId, asCObjectType *objType, asCScriptFunction *outFunc)
 196{
 197	Reset(builder, 0, outFunc);
 198
 199	asCScriptFunction *descr = builder->GetFunctionDescription(trueFactoryId);
 200
 201	byteCode.InstrPTR(asBC_OBJTYPE, objType);
 202	byteCode.Call(asBC_CALLSYS, trueFactoryId, descr->GetSpaceNeededForArguments());
 203	byteCode.Ret(outFunc->GetSpaceNeededForArguments());
 204
 205	byteCode.Finalize();
 206
 207	// Copy byte code to the function
 208	outFunc->byteCode.SetLength(byteCode.GetSize());
 209	byteCode.Output(outFunc->byteCode.AddressOf());
 210	outFunc->AddReferences();
 211	outFunc->stackNeeded = byteCode.largestStackUsed;
 212	outFunc->lineNumbers = byteCode.lineNumbers;
 213	outFunc->objVariablePos = objVariablePos;
 214	outFunc->objVariableTypes = objVariableTypes;
 215
 216	// Tell the virtual machine not to clean up the object on exception
 217	outFunc->dontCleanUpOnException = true;
 218
 219	return 0;
 220}
 221
 222int asCCompiler::CompileFunction(asCBuilder *builder, asCScriptCode *script, asCScriptNode *func, asCScriptFunction *outFunc)
 223{
 224	Reset(builder, script, outFunc);
 225	int buildErrors = builder->numErrors;
 226
 227	int stackPos = 0;
 228	if( outFunc->objectType )
 229		stackPos = -AS_PTR_SIZE; // The first parameter is the pointer to the object
 230
 231	// Reserve a label for the cleanup code
 232	nextLabel++;
 233
 234	// Add the first variable scope, which the parameters and
 235	// variables declared in the outermost statement block is
 236	// part of.
 237	AddVariableScope();
 238
 239	//----------------------------------------------
 240	// Examine return type
 241	bool isDestructor = false;
 242	asCDataType returnType;
 243	if( func->firstChild->nodeType == snDataType )
 244	{
 245		returnType = builder->CreateDataTypeFromNode(func->firstChild, script);
 246		returnType = builder->ModifyDataTypeFromNode(returnType, func->firstChild->next, script, 0, 0);
 247
 248		// Make sure the return type is instanciable or is void
 249		if( !returnType.CanBeInstanciated() &&
 250			returnType != asCDataType::CreatePrimitive(ttVoid, false) )
 251		{
 252			asCString str;
 253			str.Format(TXT_DATA_TYPE_CANT_BE_s, returnType.Format().AddressOf());
 254			Error(str.AddressOf(), func->firstChild);
 255		}
 256
 257		// TODO: Add support for returning references
 258		// The script language doesn't support returning references yet
 259		if( returnType.IsReference() )
 260		{
 261			Error(TXT_SCRIPT_FUNCTIONS_DOESNT_SUPPORT_RETURN_REF, func->firstChild);
 262		}
 263	}
 264	else
 265	{
 266		returnType = asCDataType::CreatePrimitive(ttVoid, false);
 267		if( func->firstChild->tokenType == ttBitNot )
 268			isDestructor = true;
 269		else
 270			m_isConstructor = true;
 271	}
 272
 273	//----------------------------------------------
 274	// Declare parameters
 275	// Find first parameter
 276	asCScriptNode *node = func->firstChild;
 277	while( node && node->nodeType != snParameterList )
 278		node = node->next;
 279
 280	// Register parameters from last to first, otherwise they will be destroyed in the wrong order
 281	asCVariableScope vs(0);
 282
 283	if( node ) node = node->firstChild;
 284	while( node )
 285	{
 286		// Get the parameter type
 287		asCDataType type = builder->CreateDataTypeFromNode(node, script);
 288
 289		asETypeModifiers inoutFlag = asTM_NONE;
 290		type = builder->ModifyDataTypeFromNode(type, node->next, script, &inoutFlag, 0);
 291
 292		// Is the data type allowed?
 293		if( (type.IsReference() && inoutFlag != asTM_INOUTREF && !type.CanBeInstanciated()) ||
 294			(!type.IsReference() && !type.CanBeInstanciated()) )
 295		{
 296			asCString str;
 297			str.Format(TXT_PARAMETER_CANT_BE_s, type.Format().AddressOf());
 298			Error(str.AddressOf(), node);
 299		}
 300
 301		// If the parameter has a name then declare it as variable
 302		node = node->next->next;
 303		if( node && node->nodeType == snIdentifier )
 304		{
 305			asCString name(&script->code[node->tokenPos], node->tokenLength);
 306
 307			if( vs.DeclareVariable(name.AddressOf(), type, stackPos) < 0 )
 308				Error(TXT_PARAMETER_ALREADY_DECLARED, node);
 309
 310			outFunc->AddVariable(name, type, stackPos);
 311
 312			node = node->next;
 313		}
 314		else
 315			vs.DeclareVariable("", type, stackPos);
 316
 317		// Move to next parameter
 318		stackPos -= type.GetSizeOnStackDWords();
 319	}
 320
 321	int n;
 322	for( n = (int)vs.variables.GetLength() - 1; n >= 0; n-- )
 323	{
 324		variables->DeclareVariable(vs.variables[n]->name.AddressOf(), vs.variables[n]->type, vs.variables[n]->stackOffset);
 325	}
 326
 327	// Is the return type allowed?
 328	if( (returnType.GetSizeOnStackDWords() == 0 && returnType != asCDataType::CreatePrimitive(ttVoid, false)) ||
 329		(returnType.IsReference() && !returnType.CanBeInstanciated()) )
 330	{
 331		asCString str;
 332		str.Format(TXT_RETURN_CANT_BE_s, returnType.Format().AddressOf());
 333		Error(str.AddressOf(), node);
 334	}
 335
 336	variables->DeclareVariable("return", returnType, stackPos);
 337
 338	//--------------------------------------------
 339	// Compile the statement block
 340
 341	// We need to parse the statement block now
 342
 343	// TODO: memory: We can parse the statement block one statement at a time, thus save even more memory
 344	asCParser parser(builder);
 345	int r = parser.ParseStatementBlock(script, func->lastChild);
 346	if( r < 0 ) return -1;
 347	asCScriptNode *block = parser.GetScriptNode();
 348
 349	bool hasReturn;
 350	asCByteCode bc(engine);
 351	LineInstr(&bc, func->lastChild->tokenPos);
 352	CompileStatementBlock(block, false, &hasReturn, &bc);
 353	LineInstr(&bc, func->lastChild->tokenPos + func->lastChild->tokenLength);
 354
 355	// Make sure there is a return in all paths (if not return type is void)
 356	if( returnType != asCDataType::CreatePrimitive(ttVoid, false) )
 357	{
 358		if( hasReturn == false )
 359			Error(TXT_NOT_ALL_PATHS_RETURN, func->lastChild);
 360	}
 361
 362	//------------------------------------------------
 363	// Concatenate the bytecode
 364
 365	// Insert a JitEntry at the start of the function for JIT compilers
 366	byteCode.InstrWORD(asBC_JitEntry, 0);
 367
 368	// Count total variable size
 369	int varSize = GetVariableOffset((int)variableAllocations.GetLength()) - 1;
 370	byteCode.Push(varSize);
 371
 372	if( outFunc->objectType )
 373	{
 374		// Call the base class' default constructor unless called manually in the code
 375		if( m_isConstructor && !m_isConstructorCalled && outFunc->objectType->derivedFrom )
 376		{
 377			byteCode.InstrSHORT(asBC_PSF, 0);
 378			byteCode.Instr(asBC_RDSPTR);
 379			byteCode.Call(asBC_CALL, outFunc->objectType->derivedFrom->beh.construct, AS_PTR_SIZE);
 380		}
 381
 382		// Increase the reference for the object pointer, so that it is guaranteed to live during the entire call
 383		// TODO: optimize: This is probably not necessary for constructors as no outside reference to the object is created yet
 384		byteCode.InstrSHORT(asBC_PSF, 0);
 385		byteCode.Instr(asBC_RDSPTR);
 386		byteCode.Call(asBC_CALLSYS, outFunc->objectType->beh.addref, AS_PTR_SIZE);
 387	}
 388
 389	// Add the code for the statement block
 390	byteCode.AddCode(&bc);
 391
 392	// Deallocate all local variables
 393	for( n = (int)variables->variables.GetLength() - 1; n >= 0; n-- )
 394	{
 395		sVariable *v = variables->variables[n];
 396		if( v->stackOffset > 0 )
 397		{
 398			// Call variables destructors
 399			if( v->name != "return" && v->name != "return address" )
 400				CallDestructor(v->type, v->stackOffset, &byteCode);
 401
 402			DeallocateVariable(v->stackOffset);
 403		}
 404	}
 405
 406	// This is the label that return statements jump to
 407	// in order to exit the function
 408	byteCode.Label(0);
 409
 410	// Release the object pointer again
 411	if( outFunc->objectType )
 412	{
 413		byteCode.InstrSHORT(asBC_PSF, 0);
 414		byteCode.InstrPTR(asBC_FREE, outFunc->objectType);
 415	}
 416
 417	// Call destructors for function parameters
 418	for( n = (int)variables->variables.GetLength() - 1; n >= 0; n-- )
 419	{
 420		sVariable *v = variables->variables[n];
 421		if( v->stackOffset <= 0 )
 422		{
 423			// Call variable destructors here, for variables not yet destroyed
 424			if( v->name != "return" && v->name != "return address" )
 425				CallDestructor(v->type, v->stackOffset, &byteCode);
 426		}
 427
 428		// Do not deallocate parameters
 429	}
 430
 431	// If there are compile errors, there is no reason to build the final code
 432	if( hasCompileErrors || builder->numErrors != buildErrors ) 
 433	{
 434		// Clear the accessed global properties, so they are not prematurely released
 435		outFunc->globalVarPointers.SetLength(0);
 436		return -1;
 437	}
 438
 439	// At this point there should be no variables allocated
 440	asASSERT(variableAllocations.GetLength() == freeVariables.GetLength());
 441
 442	// Remove the variable scope
 443	RemoveVariableScope();
 444
 445	byteCode.Pop(varSize);
 446
 447	byteCode.Ret(-stackPos);
 448
 449	// Tell the bytecode which variables are temporary
 450	for( n = 0; n < (signed)variableIsTemporary.GetLength(); n++ )
 451	{
 452		if( variableIsTemporary[n] )
 453			byteCode.DefineTemporaryVariable(GetVariableOffset(n));
 454	}
 455
 456	// Finalize the bytecode
 457	byteCode.Finalize();
 458
 459	// Compile the list of object variables for the exception handler
 460	for( n = 0; n < (int)variableAllocations.GetLength(); n++ )
 461	{
 462		if( variableAllocations[n].IsObject() && !variableAllocations[n].IsReference() )
 463		{
 464			objVariableTypes.PushLast(variableAllocations[n].GetObjectType());
 465			objVariablePos.PushLast(GetVariableOffset(n));
 466		}
 467	}
 468
 469	if( hasCompileErrors || builder->numErrors != buildErrors )
 470	{
 471		// Clear the accessed global properties, so they are not prematurely released
 472		outFunc->globalVarPointers.SetLength(0);
 473		return -1;
 474	}
 475
 476	// Copy byte code to the function
 477	outFunc->byteCode.SetLength(byteCode.GetSize());
 478	byteCode.Output(outFunc->byteCode.AddressOf());
 479	outFunc->AddReferences();
 480	outFunc->stackNeeded = byteCode.largestStackUsed;
 481	outFunc->lineNumbers = byteCode.lineNumbers;
 482	outFunc->objVariablePos = objVariablePos;
 483	outFunc->objVariableTypes = objVariableTypes;
 484
 485#ifdef AS_DEBUG
 486// 	// DEBUG: output byte code
 487// 	if( outFunc->objectType )
 488// 		byteCode.DebugOutput(("__" + outFunc->objectType->name + "_" + outFunc->name + ".txt").AddressOf(), engine);
 489// 	else
 490// 		byteCode.DebugOutput(("__" + outFunc->name + ".txt").AddressOf(), engine);
 491#endif
 492
 493	return 0;
 494}
 495
 496
 497
 498
 499
 500int asCCompiler::CallDefaultConstructor(asCDataType &type, int offset, asCByteCode *bc, asCScriptNode *node, bool isGlobalVar)
 501{
 502	// Call constructor for the data type
 503	if( type.IsObject() && !type.IsObjectHandle() )
 504	{
 505		if( type.GetObjectType()->flags & asOBJ_REF )
 506		{
 507			asSExprContext ctx(engine);
 508
 509			int func = 0;
 510			asSTypeBehaviour *beh = type.GetBehaviour();
 511			if( beh ) func = beh->factory;
 512
 513			if( func > 0 )
 514			{
 515				if( !isGlobalVar )
 516				{
 517					// Call factory and store the handle in the given variable
 518					PerformFunctionCall(func, &ctx, false, 0, type.GetObjectType(), true, offset);
 519
 520					// Pop the reference left by the function call
 521					ctx.bc.Pop(AS_PTR_SIZE);
 522				}
 523				else
 524				{
 525					// Call factory
 526					PerformFunctionCall(func, &ctx, false, 0, type.GetObjectType());
 527
 528					// Store the returned handle in the global variable
 529					ctx.bc.Instr(asBC_RDSPTR);
 530					// TODO: global: The global var address should be stored in the instruction directly
 531					ctx.bc.InstrWORD(asBC_PGA, (asWORD)outFunc->GetGlobalVarPtrIndex(offset));
 532					ctx.bc.InstrPTR(asBC_REFCPY, type.GetObjectType());
 533					ctx.bc.Pop(AS_PTR_SIZE);
 534					ReleaseTemporaryVariable(ctx.type.stackOffset, &ctx.bc);
 535				}
 536
 537				bc->AddCode(&ctx.bc);
 538			}
 539			else
 540			{
 541				asCString str;
 542				str.Format(TXT_NO_DEFAULT_CONSTRUCTOR_FOR_s, type.GetObjectType()->GetName());
 543				Error(str.AddressOf(), node);
 544				//Class has no default constructor.
 545				return -1;
 546			}
 547		}
 548		else
 549		{
 550			if( isGlobalVar )
 551				// TODO: global: The global var address should be stored in the instruction directly
 552				bc->InstrWORD(asBC_PGA, (asWORD)outFunc->GetGlobalVarPtrIndex(offset));
 553			else
 554				bc->InstrSHORT(asBC_PSF, (short)offset);
 555
 556			asSTypeBehaviour *beh = type.GetBehaviour();
 557
 558			int func = 0;
 559			if( beh ) func = beh->construct;
 560
 561			// TODO: Should give error if the value type doesn't have a default constructor and isn't a POD type
 562
 563			bc->Alloc(asBC_ALLOC, type.GetObjectType(), func, AS_PTR_SIZE);
 564		}
 565	}
 566	return 0;
 567}
 568
 569void asCCompiler::CallDestructor(asCDataType &type, int offset, asCByteCode *bc)
 570{
 571	if( !type.IsReference() )
 572	{
 573		// Call destructor for the data type
 574		if( type.IsObject() )
 575		{
 576			// Free the memory
 577			bc->InstrSHORT(asBC_PSF, (short)offset);
 578			bc->InstrPTR(asBC_FREE, type.GetObjectType());
 579		}
 580	}
 581}
 582
 583void asCCompiler::LineInstr(asCByteCode *bc, size_t pos)
 584{
 585	int r, c;
 586	script->ConvertPosToRowCol(pos, &r, &c);
 587	bc->Line(r, c);
 588}
 589
 590void asCCompiler::CompileStatementBlock(asCScriptNode *block, bool ownVariableScope, bool *hasReturn, asCByteCode *bc)
 591{
 592	*hasReturn = false;
 593	bool isFinished = false;
 594	bool hasWarned = false;
 595
 596	if( ownVariableScope )
 597		AddVariableScope();
 598
 599	asCScriptNode *node = block->firstChild;
 600	while( node )
 601	{
 602		if( !hasWarned && (*hasReturn || isFinished) )
 603		{
 604			hasWarned = true;
 605			Warning(TXT_UNREACHABLE_CODE, node);
 606		}
 607
 608		if( node->nodeType == snBreak || node->nodeType == snContinue )
 609			isFinished = true;
 610
 611		asCByteCode statement(engine);
 612		if( node->nodeType == snDeclaration )
 613			CompileDeclaration(node, &statement);
 614		else
 615			CompileStatement(node, hasReturn, &statement);
 616
 617		LineInstr(bc, node->tokenPos);
 618		bc->AddCode(&statement);
 619
 620		if( !hasCompileErrors )
 621			asASSERT( tempVariables.GetLength() == 0 );
 622
 623		node = node->next;
 624	}
 625
 626	if( ownVariableScope )
 627	{
 628
 629		// Deallocate variables in this block, in reverse order
 630		for( int n = (int)variables->variables.GetLength() - 1; n >= 0; n-- )
 631		{
 632			sVariable *v = variables->variables[n];
 633
 634			// Call variable destructors here, for variables not yet destroyed
 635			// If the block is terminated with a break, continue, or
 636			// return the variables are already destroyed
 637			if( !isFinished && !*hasReturn )
 638				CallDestructor(v->type, v->stackOffset, bc);
 639
 640			// Don't deallocate function parameters
 641			if( v->stackOffset > 0 )
 642				DeallocateVariable(v->stackOffset);
 643		}
 644
 645		RemoveVariableScope();
 646	}
 647}
 648
 649int asCCompiler::CompileGlobalVariable(asCBuilder *builder, asCScriptCode *script, asCScriptNode *node, sGlobalVariableDescription *gvar, asCScriptFunction *outFunc)
 650{
 651	Reset(builder, script, outFunc);
 652	globalExpression = true;
 653
 654	// Add a variable scope (even though variables can't be declared)
 655	AddVariableScope();
 656
 657	asSExprContext ctx(engine);
 658
 659	gvar->isPureConstant = false;
 660
 661	// Parse the initialization nodes
 662	asCParser parser(builder);
 663	if( node )
 664	{
 665		int r = parser.ParseGlobalVarInit(script, node);
 666		if( r < 0 )
 667			return r;
 668
 669		node = parser.GetScriptNode();
 670	}
 671
 672	// Compile the expression
 673	if( node && node->nodeType == snArgList )
 674	{
 675		// Make sure that it is a registered type, and that it isn't a pointer
 676		if( gvar->datatype.GetObjectType() == 0 || gvar->datatype.IsObjectHandle() )
 677		{
 678			Error(TXT_MUST_BE_OBJECT, node);
 679		}
 680		else
 681		{
 682			// Compile the arguments
 683			asCArray<asSExprContext *> args;
 684			if( CompileArgumentList(node, args) >= 0 )
 685			{
 686				// Find all constructors
 687				asCArray<int> funcs;
 688				asSTypeBehaviour *beh = gvar->datatype.GetBehaviour();
 689				if( beh )
 690				{
 691					if( gvar->datatype.GetObjectType()->flags & asOBJ_REF )
 692						funcs = beh->factories;
 693					else
 694						funcs = beh->constructors;
 695				}
 696
 697				asCString str = gvar->datatype.Format();
 698				MatchFunctions(funcs, args, node, str.AddressOf());
 699
 700				if( funcs.GetLength() == 1 )
 701				{
 702					if( gvar->datatype.GetObjectType()->flags & asOBJ_REF )
 703					{
 704						MakeFunctionCall(&ctx, funcs[0], 0, args, node);
 705
 706						// Store the returned handle in the global variable
 707						ctx.bc.Instr(asBC_RDSPTR);
 708						// TODO: global: The global var address should be stored in the instruction directly
 709						ctx.bc.InstrWORD(asBC_PGA, (asWORD)outFunc->GetGlobalVarPtrIndex(gvar->index));
 710						ctx.bc.InstrPTR(asBC_REFCPY, gvar->datatype.GetObjectType());
 711						ctx.bc.Pop(AS_PTR_SIZE);
 712						ReleaseTemporaryVariable(ctx.type.stackOffset, &ctx.bc);
 713					}
 714					else
 715					{
 716						// TODO: This reference is open while evaluating the arguments. We must fix this
 717						// TODO: global: The global var address should be stored in the instruction directly
 718						ctx.bc.InstrWORD(asBC_PGA, (asWORD)outFunc->GetGlobalVarPtrIndex(gvar->index));
 719
 720						PrepareFunctionCall(funcs[0], &ctx.bc, args);
 721						MoveArgsToStack(funcs[0], &ctx.bc, args, false);
 722
 723						PerformFunctionCall(funcs[0], &ctx, true, &args, gvar->datatype.GetObjectType());
 724					}
 725				}
 726			}
 727
 728			// Cleanup
 729			for( asUINT n = 0; n < args.GetLength(); n++ )
 730				if( args[n] )
 731				{
 732					asDELETE(args[n],asSExprContext);
 733				}
 734		}
 735	}
 736	else if( node && node->nodeType == snInitList )
 737	{
 738		asCTypeInfo ti;
 739		ti.Set(gvar->datatype);
 740		ti.isVariable = false;
 741		ti.isTemporary = false;
 742		ti.stackOffset = (short)gvar->index;
 743
 744		CompileInitList(&ti, node, &ctx.bc);
 745
 746		node = node->next;
 747	}
 748	else
 749	{
 750		// Call constructor for all data types
 751		if( gvar->datatype.IsObject() && !gvar->datatype.IsObjectHandle() )
 752		{
 753			CallDefaultConstructor(gvar->datatype, gvar->index, &ctx.bc, gvar->idNode, true);
 754		}
 755
 756		if( node )
 757		{
 758			asSExprContext expr(engine);
 759			int r = CompileAssignment(node, &expr); if( r < 0 ) return r;
 760
 761			if( gvar->datatype.IsPrimitive() )
 762			{
 763				if( gvar->datatype.IsReadOnly() && expr.type.isConstant )
 764				{
 765					ImplicitConversion(&expr, gvar->datatype, node, asIC_IMPLICIT_CONV);
 766
 767					gvar->isPureConstant = true;
 768					gvar->constantValue = expr.type.qwordValue;
 769				}
 770
 771				asSExprContext lctx(engine);
 772				lctx.type.Set(gvar->datatype);
 773				lctx.type.dataType.MakeReference(true);
 774				lctx.type.dataType.MakeReadOnly(false);
 775
 776				// If it is an enum value that is being compiled, then
 777				// we skip this, as the bytecode won't be used anyway
 778				// TODO: global: The global var address should be stored in the instruction directly
 779				if( !gvar->isEnumValue )
 780					lctx.bc.InstrWORD(asBC_LDG, (asWORD)outFunc->GetGlobalVarPtrIndex(gvar->index));
 781
 782				DoAssignment(&ctx, &lctx, &expr, node, node, ttAssignment, node);
 783			}
 784			else
 785			{
 786				asSExprContext lexpr(engine);
 787				lexpr.type.Set(gvar->datatype);
 788				lexpr.type.dataType.MakeReference(true);
 789				lexpr.type.dataType.MakeReadOnly(false);
 790				lexpr.type.stackOffset = -1;
 791
 792				if( gvar->datatype.IsObjectHandle() )
 793					lexpr.type.isExplicitHandle = true;
 794
 795				// TODO: global: The global var address should be stored in the instruction directly
 796				lexpr.bc.InstrWORD(asBC_PGA, (asWORD)outFunc->GetGlobalVarPtrIndex(gvar->index));
 797
 798				// If left expression resolves into a registered type
 799				// check if the assignment operator is overloaded, and check
 800				// the type of the right hand expression. If none is found
 801				// the default action is a direct copy if it is the same type
 802				// and a simple assignment.
 803				bool assigned = false;
 804				if( lexpr.type.dataType.IsObject() && !lexpr.type.isExplicitHandle )
 805				{
 806					assigned = CompileOverloadedDualOperator(node, &lexpr, &expr, &ctx);
 807					if( assigned )
 808					{
 809						// Pop the resulting value
 810						ctx.bc.Pop(ctx.type.dataType.GetSizeOnStackDWords());
 811
 812						// Release the argument
 813						ProcessDeferredParams(&ctx);
 814					}
 815				}
 816
 817				if( !assigned )
 818				{
 819					PrepareForAssignment(&lexpr.type.dataType, &expr, node);
 820
 821					// If the expression is constant and the variable also is constant
 822					// then mark the variable as pure constant. This will allow the compiler
 823					// to optimize expressions with this variable.
 824					if( gvar->datatype.IsReadOnly() && expr.type.isConstant )
 825					{
 826						gvar->isPureConstant = true;
 827						gvar->constantValue = expr.type.qwordValue;
 828					}
 829
 830					// Add expression code to bytecode
 831					MergeExprContexts(&ctx, &expr);
 832
 833					// Add byte code for storing value of expression in variable
 834					// TODO: global: The global var address should be stored in the instruction directly
 835					ctx.bc.InstrWORD(asBC_PGA, (asWORD)outFunc->GetGlobalVarPtrIndex(gvar->index));
 836
 837					PerformAssignment(&lexpr.type, &expr.type, &ctx.bc, node);
 838
 839					// Release temporary variables used by expression
 840					ReleaseTemporaryVariable(expr.type, &ctx.bc);
 841
 842					ctx.bc.Pop(expr.type.dataType.GetSizeOnStackDWords());
 843				}
 844			}
 845		}
 846	}
 847
 848	// Concatenate the bytecode
 849	int varSize = GetVariableOffset((int)variableAllocations.GetLength()) - 1;
 850
 851	// We need to push zeroes on the stack to guarantee
 852	// that temporary object handles are clear
 853	int n;
 854	for( n = 0; n < varSize; n++ )
 855		byteCode.InstrINT(asBC_PshC4, 0);
 856
 857	byteCode.AddCode(&ctx.bc);
 858
 859	// Deallocate variables in this block, in reverse order
 860	for( n = (int)variables->variables.GetLength() - 1; n >= 0; --n )
 861	{
 862		sVariable *v = variables->variables[n];
 863
 864		// Call variable destructors here, for variables not yet destroyed
 865		CallDestructor(v->type, v->stackOffset, &byteCode);
 866
 867		DeallocateVariable(v->stackOffset);
 868	}
 869
 870	if( hasCompileErrors ) return -1;
 871
 872	// At this point there should be no variables allocated
 873	asASSERT(variableAllocations.GetLength() == freeVariables.GetLength());
 874
 875	// Remove the variable scope again
 876	RemoveVariableScope();
 877
 878	if( varSize )
 879		byteCode.Pop(varSize);
 880
 881	return 0;
 882}
 883
 884void asCCompiler::PrepareArgument(asCDataType *paramType, asSExprContext *ctx, asCScriptNode *node, bool isFunction, int refType, asCArray<int> *reservedVars)
 885{
 886	asCDataType param = *paramType;
 887	if( paramType->GetTokenType() == ttQuestion )
 888	{
 889		// Since the function is expecting a var type ?, then we don't want to convert the argument to anything else
 890		param = ctx->type.dataType;
 891		param.MakeHandle(ctx->type.isExplicitHandle);
 892		param.MakeReference(paramType->IsReference());
 893		param.MakeReadOnly(paramType->IsReadOnly());
 894	}
 895	else
 896		param = *paramType;
 897
 898	asCDataType dt = param;
 899
 900	// Need to protect arguments by reference
 901	if( isFunction && dt.IsReference() )
 902	{
 903		if( paramType->GetTokenType() == ttQuestion )
 904		{
 905			asCByteCode tmpBC(engine);
 906
 907			// Place the type id on the stack as a hidden parameter
 908			tmpBC.InstrDWORD(asBC_TYPEID, engine->GetTypeIdFromDataType(param));
 909
 910			// Insert the code before the expression code
 911			tmpBC.AddCode(&ctx->bc);
 912			ctx->bc.AddCode(&tmpBC);
 913		}
 914
 915		// Allocate a temporary variable of the same type as the argument
 916		dt.MakeReference(false);
 917		dt.MakeReadOnly(false);
 918
 919		int offset;
 920		if( refType == 1 ) // &in
 921		{
 922			ProcessPropertyGetAccessor(ctx, node);
 923
 924			// If the reference is const, then it is not necessary to make a copy if the value already is a variable
 925			// Even if the same variable is passed in another argument as non-const then there is no problem
 926			if( dt.IsPrimitive() || dt.IsNullHandle() )
 927			{
 928				IsVariableInitialized(&ctx->type, node);
 929
 930				if( ctx->type.dataType.IsReference() ) ConvertToVariable(ctx);
 931				ImplicitConversion(ctx, dt, node, asIC_IMPLICIT_CONV, true, reservedVars);
 932
 933				if( !(param.IsReadOnly() && ctx->type.isVariable) )
 934					ConvertToTempVariable(ctx);
 935
 936				PushVariableOnStack(ctx, true);
 937				ctx->type.dataType.MakeReadOnly(param.IsReadOnly());
 938			}
 939			else
 940			{
 941				IsVariableInitialized(&ctx->type, node);
 942
 943				ImplicitConversion(ctx, param, node, asIC_IMPLICIT_CONV, true, reservedVars);
 944
 945				if( !ctx->type.dataType.IsEqualExceptRef(param) )
 946				{
 947					asCString str;
 948					str.Format(TXT_CANT_IMPLICITLY_CONVERT_s_TO_s, ctx->type.dataType.Format().AddressOf(), param.Format().AddressOf());
 949					Error(str.AddressOf(), node);
 950
 951					ctx->type.Set(param);
 952				}
 953
 954				// If the argument already is a temporary
 955				// variable we don't need to allocate another
 956
 957				// If the parameter is read-only and the object already is a local
 958				// variable then it is not necessary to make a copy either
 959				if( !ctx->type.isTemporary && !(param.IsReadOnly() && ctx->type.isVariable))
 960				{
 961					// Make sure the variable is not used in the expression
 962					asCArray<int> vars;
 963					ctx->bc.GetVarsUsed(vars);
 964					if( reservedVars ) vars.Concatenate(*reservedVars);
 965					offset = AllocateVariableNotIn(dt, true, &vars);
 966
 967					// Allocate and construct the temporary object
 968					asCByteCode tmpBC(engine);
 969					CallDefaultConstructor(dt, offset, &tmpBC, node);
 970
 971					// Insert the code before the expression code
 972					tmpBC.AddCode(&ctx->bc);
 973					ctx->bc.AddCode(&tmpBC);
 974
 975					// Assign the evaluated expression to the temporary variable
 976					PrepareForAssignment(&dt, ctx, node);
 977
 978					dt.MakeReference(true);
 979					asCTypeInfo type;
 980					type.Set(dt);
 981					type.isTemporary = true;
 982					type.stackOffset = (short)offset;
 983
 984					if( dt.IsObjectHandle() )
 985						type.isExplicitHandle = true;
 986
 987					ctx->bc.InstrSHORT(asBC_PSF, (short)offset);
 988
 989					PerformAssignment(&type, &ctx->type, &ctx->bc, node);
 990
 991					ctx->bc.Pop(ctx->type.dataType.GetSizeOnStackDWords());
 992
 993					ReleaseTemporaryVariable(ctx->type, &ctx->bc);
 994
 995					ctx->type = type;
 996
 997					ctx->bc.InstrSHORT(asBC_PSF, (short)offset);
 998					if( dt.IsObject() && !dt.IsObjectHandle() )
 999						ctx->bc.Instr(asBC_RDSPTR);
1000
1001					if( paramType->IsReadOnly() )
1002						ctx->type.dataType.MakeReadOnly(true);
1003				}
1004			}
1005		}
1006		else if( refType == 2 ) // &out
1007		{
1008			// Make sure the variable is not used in the expression
1009			asCArray<int> vars;
1010			ctx->bc.GetVarsUsed(vars);
1011			if( reservedVars ) vars.Concatenate(*reservedVars);
1012			offset = AllocateVariableNotIn(dt, true, &vars);
1013
1014			if( dt.IsPrimitive() )
1015			{
1016				ctx->type.SetVariable(dt, offset, true);
1017				PushVariableOnStack(ctx, true);
1018			}
1019			else
1020			{
1021				// Allocate and construct the temporary object
1022				asCByteCode tmpBC(engine);
1023				CallDefaultConstructor(dt, offset, &tmpBC, node);
1024
1025				// Insert the code before the expression code
1026				tmpBC.AddCode(&ctx->bc);
1027				ctx->bc.AddCode(&tmpBC);
1028
1029				dt.MakeReference((!dt.IsObject() || dt.IsObjectHandle()));
1030				asCTypeInfo type;
1031				type.Set(dt);
1032				type.isTemporary = true;
1033				type.stackOffset = (short)offset;
1034
1035				ctx->type = type;
1036
1037				ctx->bc.InstrSHORT(asBC_PSF, (short)offset);
1038				if( dt.IsObject() && !dt.IsObjectHandle() )
1039					ctx->bc.Instr(asBC_RDSPTR);
1040			}
1041
1042			// After the function returns the temporary variable will
1043			// be assigned to the expression, if it is a valid lvalue
1044		}
1045		else if( refType == asTM_INOUTREF )
1046		{
1047			// Literal constants cannot be passed to inout ref arguments
1048			if( !ctx->type.isVariable && ctx->type.isConstant )
1049			{
1050				Error(TXT_NOT_VALID_REFERENCE, node);
1051			}
1052
1053			// Only objects that support object handles
1054			// can be guaranteed to be safe. Local variables are
1055			// already safe, so there is no need to add an extra
1056			// references
1057			if( !engine->ep.allowUnsafeReferences &&
1058				!ctx->type.isVariable &&
1059				ctx->type.dataType.IsObject() &&
1060				!ctx->type.dataType.IsObjectHandle() &&
1061				ctx->type.dataType.GetBehaviour()->addref &&
1062				ctx->type.dataType.GetBehaviour()->release )
1063			{
1064				// Store a handle to the object as local variable
1065				asSExprContext tmp(engine);
1066				asCDataType dt = ctx->type.dataType;
1067				dt.MakeHandle(true);
1068				dt.MakeReference(false);
1069
1070				asCArray<int> vars;
1071				ctx->bc.GetVarsUsed(vars);
1072				if( reservedVars ) vars.Concatenate(*reservedVars);
1073				offset = AllocateVariableNotIn(dt, true, &vars);
1074
1075				// Copy the handle
1076				if( !ctx->type.dataType.IsObjectHandle() && ctx->type.dataType.IsReference() )
1077					ctx->bc.Instr(asBC_RDSPTR);
1078				ctx->bc.InstrWORD(asBC_PSF, (asWORD)offset);
1079				ctx->bc.InstrPTR(asBC_REFCPY, ctx->type.dataType.GetObjectType());
1080				ctx->bc.Pop(AS_PTR_SIZE);
1081				ctx->bc.InstrWORD(asBC_PSF, (asWORD)offset);
1082
1083				dt.MakeHandle(false);
1084				dt.MakeReference(true);
1085
1086				// Release previous temporary variable stored in the context (if any)
1087				if( ctx->type.isTemporary )
1088				{
1089					ReleaseTemporaryVariable(ctx->type.stackOffset, &ctx->bc);
1090				}
1091
1092				ctx->type.SetVariable(dt, offset, true);
1093			}
1094
1095			// Make sure the reference to the value is on the stack
1096			if( ctx->type.dataType.IsObject() && ctx->type.dataType.IsReference() )
1097				Dereference(ctx, true);
1098			else if( ctx->type.isVariable )
1099				ctx->bc.InstrSHORT(asBC_PSF, ctx->type.stackOffset);
1100			else if( ctx->type.dataType.IsPrimitive() )
1101				ctx->bc.Instr(asBC_PshRPtr);
1102		}
1103	}
1104	else
1105	{
1106		ProcessPropertyGetAccessor(ctx, node);
1107
1108		if( dt.IsPrimitive() )
1109		{
1110			IsVariableInitialized(&ctx->type, node);
1111
1112			if( ctx->type.dataType.IsReference() ) ConvertToVariable(ctx);
1113
1114			// Implicitly convert primitives to the parameter type
1115			ImplicitConversion(ctx, dt, node, asIC_IMPLICIT_CONV, true, reservedVars);
1116
1117			if( ctx->type.isVariable )
1118			{
1119				PushVariableOnStack(ctx, dt.IsReference());
1120			}
1121			else if( ctx->type.isConstant )
1122			{
1123				ConvertToVariable(ctx);
1124				PushVariableOnStack(ctx, dt.IsReference());
1125			}
1126		}
1127		else
1128		{
1129			IsVariableInitialized(&ctx->type, node);
1130
1131			// Implicitly convert primitives to the parameter type
1132			ImplicitConversion(ctx, dt, node, asIC_IMPLICIT_CONV, true, reservedVars);
1133
1134			// Was the conversion successful?
1135			if( !ctx->type.dataType.IsEqualExceptRef(dt) )
1136			{
1137				asCString str;
1138				str.Format(TXT_CANT_IMPLICITLY_CONVERT_s_TO_s, ctx->type.dataType.Format().AddressOf(), dt.Format().AddressOf());
1139				Error(str.AddressOf(), node);
1140
1141				ctx->type.Set(dt);
1142			}
1143
1144			if( dt.IsObjectHandle() )
1145				ctx->type.isExplicitHandle = true;
1146
1147			if( dt.IsObject() )
1148			{
1149				if( !dt.IsReference() )
1150				{
1151					// Objects passed by value must be placed in temporary variables
1152					// so that they are guaranteed to not be referenced anywhere else
1153					PrepareTemporaryObject(node, ctx, reservedVars);
1154
1155					// The implicit conversion shouldn't convert the object to
1156					// non-reference yet. It will be dereferenced just before the call.
1157					// Otherwise the object might be missed by the exception handler.
1158					dt.MakeReference(true);
1159				}
1160				else
1161				{
1162					// An object passed by reference should place the pointer to
1163					// the object on the stack.
1164					dt.MakeReference(false);
1165				}
1166			}
1167		}
1168	}
1169
1170	// Don't put any pointer on the stack yet
1171	if( param.IsReference() || param.IsObject() )
1172	{
1173		// &inout parameter may leave the reference on the stack already
1174		if( refType != 3 )
1175		{
1176			ctx->bc.Pop(AS_PTR_SIZE);
1177			ctx->bc.InstrSHORT(asBC_VAR, ctx->type.stackOffset);
1178		}
1179
1180		ProcessDeferredParams(ctx);
1181	}
1182}
1183
1184void asCCompiler::PrepareFunctionCall(int funcID, asCByteCode *bc, asCArray<asSExprContext *> &args)
1185{
1186	// When a match has been found, compile the final byte code using correct parameter types
1187	asCScriptFunction *descr = builder->GetFunctionDescription(funcID);
1188
1189	// Add code for arguments
1190	asSExprContext e(engine);
1191	int n;
1192	for( n = (int)args.GetLength()-1; n >= 0; n-- )
1193	{
1194		// Make sure PrepareArgument doesn't use any variable that is already
1195		// being used by any of the following argument expressions
1196		asCArray<int> reservedVars;
1197		for( int m = n-1; m >= 0; m-- )
1198			args[m]->bc.GetVarsUsed(reservedVars);
1199
1200		PrepareArgument2(&e, args[n], &descr->parameterTypes[n], true, descr->inOutFlags[n], &reservedVars);
1201	}
1202
1203	bc->AddCode(&e.bc);
1204}
1205
1206void asCCompiler::MoveArgsToStack(int funcID, asCByteCode *bc, asCArray<asSExprContext *> &args, bool addOneToOffset)
1207{
1208	asCScriptFunction *descr = builder->GetFunctionDescription(funcID);
1209
1210	int offset = 0;
1211	if( addOneToOffset )
1212		offset += AS_PTR_SIZE;
1213
1214	// Move the objects that are sent by value to the stack just before the call
1215	for( asUINT n = 0; n < descr->parameterTypes.GetLength(); n++ )
1216	{
1217		if( descr->parameterTypes[n].IsReference() )
1218		{
1219			if( descr->parameterTypes[n].IsObject() && !descr->parameterTypes[n].IsObjectHandle() )
1220			{
1221				if( descr->inOutFlags[n] != asTM_INOUTREF )
1222					bc->InstrWORD(asBC_GETOBJREF, (asWORD)offset);
1223				if( args[n]->type.dataType.IsObjectHandle() )
1224					bc->InstrWORD(asBC_ChkNullS, (asWORD)offset);
1225			}
1226			else if( descr->inOutFlags[n] != asTM_INOUTREF )
1227			{
1228				if( descr->parameterTypes[n].GetTokenType() == ttQuestion &&
1229					args[n]->type.dataType.IsObject() && !args[n]->type.dataType.IsObjectHandle() )
1230				{
1231					// Send the object as a reference to the object, 
1232					// and not to the variable holding the object
1233					bc->InstrWORD(asBC_GETOBJREF, (asWORD)offset);
1234				}
1235				else
1236					bc->InstrWORD(asBC_GETREF, (asWORD)offset);
1237			}
1238		}
1239		else if( descr->parameterTypes[n].IsObject() )
1240		{
1241			bc->InstrWORD(asBC_GETOBJ, (asWORD)offset);
1242
1243			// The temporary variable must not be freed as it will no longer hold an object
1244			DeallocateVariable(args[n]->type.stackOffset);
1245			args[n]->type.isTemporary = false;
1246		}
1247
1248		offset += descr->parameterTypes[n].GetSizeOnStackDWords();
1249	}
1250}
1251
1252int asCCompiler::CompileArgumentList(asCScriptNode *node, asCArray<asSExprContext*> &args)
1253{
1254	asASSERT(node->nodeType == snArgList);
1255
1256	// Count arguments
1257	asCScriptNode *arg = node->firstChild;
1258	int argCount = 0;
1259	while( arg )
1260	{
1261		argCount++;
1262		arg = arg->next;
1263	}
1264
1265	// Prepare the arrays
1266	args.SetLength(argCount);
1267	int n;
1268	for( n = 0; n < argCount; n++ )
1269		args[n] = 0;
1270
1271	n = argCount-1;
1272
1273	// Compile the arguments in reverse order (as they will be pushed on the stack)
1274	bool anyErrors = false;
1275	arg = node->lastChild;
1276	while( arg )
1277	{
1278		asSExprContext expr(engine);
1279		int r = CompileAssignment(arg, &expr);
1280		if( r < 0 ) anyErrors = true;
1281
1282		args[n] = asNEW(asSExprContext)(engine);
1283		MergeExprContexts(args[n], &expr);
1284		args[n]->type = expr.type;
1285		args[n]->property_get = expr.property_get;
1286		args[n]->property_set = expr.property_set;
1287		args[n]->property_const = expr.property_const;
1288		args[n]->property_handle = expr.property_handle;
1289		args[n]->exprNode = arg;
1290
1291		n--;
1292		arg = arg->prev;
1293	}
1294
1295	return anyErrors ? -1 : 0;
1296}
1297
1298void asCCompiler::MatchFunctions(asCArray<int> &funcs, asCArray<asSExprContext*> &args, asCScriptNode *node, const char *name, asCObjectType *objectType, bool isConstMethod, bool silent, bool allowObjectConstruct, const asCString &scope)
1299{
1300	asCArray<int> origFuncs = funcs; // Keep the original list for error message
1301
1302	asUINT n;
1303	if( funcs.GetLength() > 0 )
1304	{
1305		// Check the number of parameters in the found functions
1306		for( n = 0; n < funcs.GetLength(); ++n )
1307		{
1308			asCScriptFunction *desc = builder->GetFunctionDescription(funcs[n]);
1309
1310			if( desc->parameterTypes.GetLength() != args.GetLength() )
1311			{
1312				// remove it from the list
1313				if( n == funcs.GetLength()-1 )
1314					funcs.PopLast();
1315				else
1316					funcs[n] = funcs.PopLast();
1317				n--;
1318			}
1319		}
1320
1321		// Match functions with the parameters, and discard those that do not match
1322		asCArray<int> matchingFuncs = funcs;
1323
1324		for( n = 0; n < args.GetLength(); ++n )
1325		{
1326			asCArray<int> tempFuncs;
1327			MatchArgument(funcs, tempFuncs, &args[n]->type, n, allowObjectConstruct);
1328
1329			// Intersect the found functions with the list of matching functions
1330			for( asUINT f = 0; f < matchingFuncs.GetLength(); f++ )
1331			{
1332				asUINT c;
1333				for( c = 0; c < tempFuncs.GetLength(); c++ )
1334				{
1335					if( matchingFuncs[f] == tempFuncs[c] )
1336						break;
1337				}
1338
1339				// Was the function a match?
1340				if( c == tempFuncs.GetLength() )
1341				{
1342					// No, remove it from the list
1343					if( f == matchingFuncs.GetLength()-1 )
1344						matchingFuncs.PopLast();
1345					else
1346						matchingFuncs[f] = matchingFuncs.PopLast();
1347					f--;
1348				}
1349			}
1350		}
1351
1352		funcs = matchingFuncs;
1353	}
1354
1355	if( !isConstMethod )
1356		FilterConst(funcs);
1357
1358	if( funcs.GetLength() != 1 && !silent )
1359	{
1360		// Build a readable string of the function with parameter types
1361		asCString str;
1362		if( scope != "" )
1363		{
1364			if( scope == "::" )
1365				str = scope;
1366			else
1367				str = scope + "::";
1368		}
1369		str += name;
1370		str += "(";
1371		if( args.GetLength() )
1372			str += args[0]->type.dataType.Format();
1373		for( n = 1; n < args.GetLength(); n++ )
1374			str += ", " + args[n]->type.dataType.Format();
1375		str += ")";
1376
1377		if( isConstMethod )
1378			str += " const";
1379
1380		if( objectType && scope == "" )
1381			str = objectType->name + "::" + str;
1382
1383		if( funcs.GetLength() == 0 )
1384		{
1385			str.Format(TXT_NO_MATCHING_SIGNATURES_TO_s, str.AddressOf());
1386			Error(str.AddressOf(), node);
1387
1388			// Print the list of candidates
1389			if( origFuncs.GetLength() > 0 )
1390			{
1391				int r, c;
1392				script->ConvertPosToRowCol(node->tokenPos, &r, &c);
1393				builder->WriteInfo(script->name.AddressOf(), TXT_CANDIDATES_ARE, r, c, false);
1394				PrintMatchingFuncs(origFuncs, node);
1395			}
1396		}
1397		else
1398		{
1399			str.Format(TXT_MULTIPLE_MATCHING_SIGNATURES_TO_s, str.AddressOf());
1400			Error(str.AddressOf(), node);
1401	
1402			PrintMatchingFuncs(funcs, node);
1403		}
1404	}
1405}
1406
1407void asCCompiler::CompileDeclaration(asCScriptNode *decl, asCByteCode *bc)
1408{
1409	// Get the data type
1410	asCDataType type = builder->CreateDataTypeFromNode(decl->firstChild, script);
1411
1412	// Declare all variables in this declaration
1413	asCScriptNode *node = decl->firstChild->next;
1414	while( node )
1415	{
1416		// Is the type allowed?
1417		if( !type.CanBeInstanciated() )
1418		{
1419			asCString str;
1420			// TODO: Change to "'type' cannot be declared as variable"
1421			str.Format(TXT_DATA_TYPE_CANT_BE_s, type.Format().AddressOf());
1422			Error(str.AddressOf(), node);
1423
1424			// Use int instead to avoid further problems
1425			type = asCDataType::CreatePrimitive(ttInt, false);
1426		}
1427
1428		// Get the name of the identifier
1429		asCString name(&script->code[node->tokenPos], node->tokenLength);
1430
1431		// Verify that the name isn't used by a dynamic data type
1432		if( engine->GetObjectType(name.AddressOf()) != 0 )
1433		{
1434			asCString str;
1435			str.Format(TXT_ILLEGAL_VARIABLE_NAME_s, name.AddressOf());
1436			Error(str.AddressOf(), node);
1437		}
1438
1439		int offset = AllocateVariable(type, false);
1440		if( variables->DeclareVariable(name.AddressOf(), type, offset) < 0 )
1441		{
1442			asCString str;
1443			str.Format(TXT_s_ALREADY_DECLARED, name.AddressOf());
1444			Error(str.AddressOf(), node);
1445		}
1446
1447		outFunc->AddVariable(name, type, offset);
1448
1449		// Keep the node for the variable decl
1450		asCScriptNode *varNode = node;
1451
1452		node = node->next;
1453		if( node && node->nodeType == snArgList )
1454		{
1455			// Make sure that it is a registered type, and that is isn't a pointer
1456			if( type.GetObjectType() == 0 || type.IsObjectHandle() )
1457			{
1458				Error(TXT_MUST_BE_OBJECT, node);
1459			}
1460			else
1461			{
1462				// Compile the arguments
1463				asCArray<asSExprContext *> args;
1464
1465				if( CompileArgumentList(node, args) >= 0 )
1466				{
1467					// Find all constructors
1468					asCArray<int> funcs;
1469					asSTypeBehaviour *beh = type.GetBehaviour();
1470					if( beh )
1471					{
1472						if( type.GetObjectType()->flags & asOBJ_REF )
1473							funcs = beh->factories;
1474						else
1475							funcs = beh->constructors;
1476					}
1477
1478					asCString str = type.Format();
1479					MatchFunctions(funcs, args, node, str.AddressOf());
1480
1481					if( funcs.GetLength() == 1 )
1482					{
1483						sVariable *v = variables->GetVariable(name.AddressOf());
1484						asSExprContext ctx(engine);
1485						if( v->type.GetObjectType()->flags & asOBJ_REF )
1486						{
1487							MakeFunctionCall(&ctx, funcs[0], 0, args, node, true, v->stackOffset);
1488
1489							// Pop the reference left by the function call
1490							ctx.bc.Pop(AS_PTR_SIZE);
1491						}
1492						else
1493						{
1494							ctx.bc.InstrSHORT(asBC_VAR, (short)v->stackOffset);
1495
1496							PrepareFunctionCall(funcs[0], &ctx.bc, args);
1497							MoveArgsToStack(funcs[0], &ctx.bc, args, false);
1498
1499							int offset = 0;
1500							asCScriptFunction *descr = builder->GetFunctionDescription(funcs[0]);
1501							for( asUINT n = 0; n < args.GetLength(); n++ )
1502								offset += descr->parameterTypes[n].GetSizeOnStackDWords();
1503
1504							ctx.bc.InstrWORD(asBC_GETREF, (asWORD)offset);
1505
1506							PerformFunctionCall(funcs[0], &ctx, true, &args, type.GetObjectType());
1507						}
1508						bc->AddCode(&ctx.bc);
1509					}
1510				}
1511
1512				// Cleanup
1513				for( asUINT n = 0; n < args.GetLength(); n++ )
1514					if( args[n] )
1515					{
1516						asDELETE(args[n],asSExprContext);
1517					}
1518			}
1519
1520			node = node->next;
1521		}
1522		else if( node && node->nodeType == snInitList )
1523		{
1524			sVariable *v = variables->GetVariable(name.AddressOf());
1525
1526			asCTypeInfo ti;
1527			ti.Set(type);
1528			ti.isVariable = true;
1529			ti.isTemporary = false;
1530			ti.stackOffset = (short)v->stackOffset;
1531
1532			CompileInitList(&ti, node, bc);
1533
1534			node = node->next;
1535		}
1536		else
1537		{
1538			asSExprContext ctx(engine);
1539
1540			// Call the default constructor here
1541			CallDefaultConstructor(type, offset, &ctx.bc, varNode);
1542
1543			// Is the variable initialized?
1544			if( node && node->nodeType == snAssignment )
1545			{
1546				// Compile the expression
1547				asSExprContext expr(engine);
1548				int r = CompileAssignment(node, &expr);
1549				if( r >= 0 )
1550				{
1551					if( type.IsPrimitive() )
1552					{
1553						if( type.IsReadOnly() && expr.type.isConstant )
1554						{
1555							ImplicitConversion(&expr, type, node, asIC_IMPLICIT_CONV);
1556
1557							sVariable *v = variables->GetVariable(name.AddressOf());
1558							v->isPureConstant = true;
1559							v->constantValue = expr.type.qwordValue;
1560						}
1561
1562						asSExprContext lctx(engine);
1563						lctx.type.SetVariable(type, offset, false);
1564						lctx.type.dataType.MakeReadOnly(false);
1565
1566						DoAssignment(&ctx, &lctx, &expr, node, node, ttAssignment, node);
1567					}
1568					else
1569					{
1570						// TODO: We can use a copy constructor here
1571
1572						asSExprContext lexpr(engine);
1573						lexpr.type.Set(type);
1574						lexpr.type.dataType.MakeReference(true);
1575						// Allow initialization of constant variables
1576						lexpr.type.dataType.MakeReadOnly(false);
1577
1578						if( type.IsObjectHandle() )
1579							lexpr.type.isExplicitHandle = true;
1580
1581						sVariable *v = variables->GetVariable(name.AddressOf());
1582						lexpr.bc.InstrSHORT(asBC_PSF, (short)v->stackOffset);
1583						lexpr.type.stackOffset = (short)v->stackOffset;
1584
1585
1586						// If left expression resolves into a registered type
1587						// check if the assignment operator is overloaded, and check
1588						// the type of the right hand expression. If none is found
1589						// the default action is a direct copy if it is the same type
1590						// and a simple assignment.
1591						bool assigned = false;
1592						if( lexpr.type.dataType.IsObject() && !lexpr.type.isExplicitHandle )
1593						{
1594							assigned = CompileOverloadedDualOperator(node, &lexpr, &expr, &ctx);
1595							if( assigned )
1596							{
1597								// Pop the resulting value
1598								ctx.bc.Pop(ctx.type.dataType.GetSizeOnStackDWords());
1599
1600								// Release the argument
1601								ProcessDeferredParams(&ctx);
1602							}
1603						}
1604
1605						if( !assigned )
1606						{
1607							PrepareForAssignment(&lexpr.type.dataType, &expr, node);
1608
1609							// If the expression is constant and the variable also is constant
1610							// then mark the variable as pure constant. This will allow the compiler
1611							// to optimize expressions with this variable.
1612							if( v->type.IsReadOnly() && expr.type.isConstant )
1613							{
1614								v->isPureConstant = true;
1615								v->constantValue = expr.type.qwordValue;
1616							}
1617
1618							// Add expression code to bytecode
1619							MergeExprContexts(&ctx, &expr);
1620
1621							// Add byte code for storing value of expression in variable
1622							ctx.bc.AddCode(&lexpr.bc);
1623							lexpr.type.stackOffset = (short)v->stackOffset;
1624
1625							PerformAssignment(&lexpr.type, &expr.type, &ctx.bc, node->prev);
1626
1627							// Release temporary variables used by expression
1628							ReleaseTemporaryVariable(expr.type, &ctx.bc);
1629
1630							ctx.bc.Pop(expr.type.dataType.GetSizeOnStackDWords());
1631
1632							ProcessDeferredParams(&ctx);
1633						}
1634					}
1635				}
1636
1637				node = node->next;
1638			}
1639
1640			bc->AddCode(&ctx.bc);
1641
1642			// TODO: Can't this leave deferred output params without being compiled?
1643		}
1644	}
1645}
1646
1647void asCCompiler::CompileInitList(asCTypeInfo *var, asCScriptNode *node, asCByteCode *bc)
1648{
1649	if( var->dataType.IsArrayType() && !var->dataType.IsObjectHandle() )
1650	{
1651		// Count the number of elements and initialize the array with the correct size
1652		int countElements = 0;
1653		asCScriptNode *el = node->firstChild;
1654		while( el )
1655		{
1656			countElements++;
1657			el = el->next;
1658		}
1659
1660		// Construct the array with the size elements
1661
1662		// Find the constructor that takes an uint
1663		asCArray<int> funcs;
1664		if( var->dataType.GetObjectType()->flags & asOBJ_REF )
1665			funcs = var->dataType.GetBehaviour()->factories;
1666		else
1667			funcs = var->dataType.GetBehaviour()->constructors;
1668
1669		asCArray<asSExprContext *> args;
1670		asSExprContext arg1(engine);
1671		arg1.bc.InstrDWORD(asBC_PshC4, countElements);
1672		arg1.type.Set(asCDataType::CreatePrimitive(ttUInt, false));
1673		args.PushLast(&arg1);
1674
1675		asCString str = var->dataType.Format();
1676		MatchFunctions(funcs, args, node, str.AddressOf());
1677
1678		if( funcs.GetLength() == 1 )
1679		{
1680			asSExprContext ctx(engine);
1681
1682			if( var->dataType.GetObjectType()->flags & asOBJ_REF )
1683			{
1684				PrepareFunctionCall(funcs[0], &ctx.bc, args);
1685				MoveArgsToStack(funcs[0], &ctx.bc, args, false);
1686
1687				if( var->isVariable )
1688				{
1689					// Call factory and store the handle in the given variable
1690					PerformFunctionCall(funcs[0], &ctx, false, &args, 0, true, var->stackOffset);
1691					ctx.bc.Pop(AS_PTR_SIZE);
1692				}
1693				else
1694				{
1695					PerformFunctionCall(funcs[0], &ctx, false, &args);
1696
1697					// Store the returned handle in the global variable
1698					ctx.bc.Instr(asBC_RDSPTR);
1699					// TODO: global: The global var address should be stored in the instruction directly
1700					ctx.bc.InstrWORD(asBC_PGA, (asWORD)outFunc->GetGlobalVarPtrIndex(var->stackOffset));
1701					ctx.bc.InstrPTR(asBC_REFCPY, var->dataType.GetObjectType());
1702					ctx.bc.Pop(AS_PTR_SIZE);
1703					ReleaseTemporaryVariable(ctx.type.stackOffset, &ctx.bc);
1704				}
1705			}
1706			else
1707			{
1708				if( var->isVariable )
1709					ctx.bc.InstrSHORT(asBC_PSF, var->stackOffset);
1710				else
1711					// TODO: global: The global var address should be stored in the instruction directly
1712					ctx.bc.InstrWORD(asBC_PGA, (asWORD)outF

Large files files are truncated, but you can click here to view the full file