PageRenderTime 62ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 1ms

/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
Possible License(s): GPL-3.0, CC-BY-SA-3.0, LGPL-2.0, 0BSD, Unlicense, GPL-2.0, AGPL-1.0, BSD-3-Clause, LGPL-2.1, LGPL-3.0

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

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