PageRenderTime 49ms CodeModel.GetById 23ms RepoModel.GetById 1ms 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
  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)outFunc->GetGlobalVarPtrIndex(var->stackOffset));
  1404. PrepareFunctionCall(funcs[0], &ctx.bc, args);
  1405. MoveArgsToStack(funcs[0], &ctx.bc, args, false);
  1406. PerformFunctionCall(funcs[0], &ctx, true, &args, var->dataType.GetObjectType());
  1407. }
  1408. bc->AddCode(&ctx.bc);
  1409. }
  1410. else
  1411. return;
  1412. // Find the indexing operator that is not read-only that will be used for all elements
  1413. asCDataType retType;
  1414. retType = var->dataType.GetSubType();
  1415. retType.MakeReference(true);
  1416. retType.MakeReadOnly(false);
  1417. int funcId = 0;
  1418. asSTypeBehaviour *beh = var->dataType.GetBehaviour();
  1419. for( asUINT n = 0; n < beh->operators.GetLength(); n += 2 )
  1420. {
  1421. if( asBEHAVE_INDEX == beh->operators[n] )
  1422. {
  1423. asCScriptFunction *desc = builder->GetFunctionDescription(beh->operators[n+1]);
  1424. if( !desc->isReadOnly &&
  1425. desc->parameterTypes.GetLength() == 1 &&
  1426. (desc->parameterTypes[0] == asCDataType::CreatePrimitive(ttUInt, false) ||
  1427. desc->parameterTypes[0] == asCDataType::CreatePrimitive(ttInt, false)) &&
  1428. desc->returnType == retType )
  1429. {
  1430. funcId = beh->operators[n+1];
  1431. break;
  1432. }
  1433. }
  1434. }
  1435. if( funcId == 0 )
  1436. {
  1437. Error(TXT_NO_APPROPRIATE_INDEX_OPERATOR, node);
  1438. return;
  1439. }
  1440. asUINT index = 0;
  1441. el = node->firstChild;
  1442. while( el )
  1443. {
  1444. if( el->nodeType == snAssignment || el->nodeType == snInitList )
  1445. {
  1446. asSExprContext lctx(engine);
  1447. asSExprContext rctx(engine);
  1448. if( el->nodeType == snAssignment )
  1449. {
  1450. // Compile the assignment expression
  1451. CompileAssignment(el, &rctx);
  1452. }
  1453. else if( el->nodeType == snInitList )
  1454. {
  1455. int offset = AllocateVariable(var->dataType.GetSubType(), true);
  1456. rctx.type.Set(var->dataType.GetSubType());
  1457. rctx.type.isVariable = true;
  1458. rctx.type.isTemporary = true;
  1459. rctx.type.stackOffset = (short)offset;
  1460. CompileInitList(&rctx.type, el, &rctx.bc);
  1461. // Put the object on the stack
  1462. rctx.bc.InstrSHORT(asBC_PSF, rctx.type.stackOffset);
  1463. // It is a reference that we place on the stack
  1464. rctx.type.dataType.MakeReference(true);
  1465. }
  1466. // Compile the lvalue
  1467. lctx.bc.InstrDWORD(asBC_PshC4, index);
  1468. if( var->isVariable )
  1469. lctx.bc.InstrSHORT(asBC_PSF, var->stackOffset);
  1470. else
  1471. // TODO: global: The global var address should be stored in the instruction directly
  1472. lctx.bc.InstrWORD(asBC_PGA, (asWORD)outFunc->GetGlobalVarPtrIndex(var->stackOffset));
  1473. lctx.bc.Instr(asBC_RDSPTR);
  1474. lctx.bc.Call(asBC_CALLSYS, funcId, 1+AS_PTR_SIZE);
  1475. if( !var->dataType.GetSubType().IsPrimitive() )
  1476. lctx.bc.Instr(asBC_PshRPtr);
  1477. lctx.type.Set(var->dataType.GetSubType());
  1478. if( !lctx.type.dataType.IsObject() || lctx.type.dataType.IsObjectHandle() )
  1479. lctx.type.dataType.MakeReference(true);
  1480. // If the element type is handles, then we're expected to do handle assignments
  1481. if( lctx.type.dataType.IsObjectHandle() )
  1482. lctx.type.isExplicitHandle = true;
  1483. asSExprContext ctx(engine);
  1484. DoAssignment(&ctx, &lctx, &rctx, el, el, ttAssignment, el);
  1485. if( !lctx.type.dataType.IsPrimitive() )
  1486. ctx.bc.Pop(AS_PTR_SIZE);
  1487. // Release temporary variables used by expression
  1488. ReleaseTemporaryVariable(ctx.type, &ctx.bc);
  1489. ProcessDeferredParams(&ctx);
  1490. bc->AddCode(&ctx.bc);
  1491. }
  1492. el = el->next;
  1493. index++;
  1494. }
  1495. }
  1496. else
  1497. {
  1498. asCString str;
  1499. str.Format(TXT_INIT_LIST_CANNOT_BE_USED_WITH_s, var->dataType.Format().AddressOf());
  1500. Error(str.AddressOf(), node);
  1501. }
  1502. }
  1503. void asCCompiler::CompileStatement(asCScriptNode *statement, bool *hasReturn, asCByteCode *bc)
  1504. {
  1505. *hasReturn = false;
  1506. if( statement->nodeType == snStatementBlock )
  1507. CompileStatementBlock(statement, true, hasReturn, bc);
  1508. else if( statement->nodeType == snIf )
  1509. CompileIfStatement(statement, hasReturn, bc);
  1510. else if( statement->nodeType == snFor )
  1511. CompileForStatement(statement, bc);
  1512. else if( statement->nodeType == snWhile )
  1513. CompileWhileStatement(statement, bc);
  1514. else if( statement->nodeType == snDoWhile )
  1515. CompileDoWhileStatement(statement, bc);
  1516. else if( statement->nodeType == snExpressionStatement )
  1517. CompileExpressionStatement(statement, bc);
  1518. else if( statement->nodeType == snBreak )
  1519. CompileBreakStatement(statement, bc);
  1520. else if( statement->nodeType == snContinue )
  1521. CompileContinueStatement(statement, bc);
  1522. else if( statement->nodeType == snSwitch )
  1523. CompileSwitchStatement(statement, hasReturn, bc);
  1524. else if( statement->nodeType == snReturn )
  1525. {
  1526. CompileReturnStatement(statement, bc);
  1527. *hasReturn = true;
  1528. }
  1529. }
  1530. void asCCompiler::CompileSwitchStatement(asCScriptNode *snode, bool *, asCByteCode *bc)
  1531. {
  1532. // TODO: inheritance: Must guarantee that all options in the switch case call a constructor, or that none call it.
  1533. // Reserve label for break statements
  1534. int breakLabel = nextLabel++;
  1535. breakLabels.PushLast(breakLabel);
  1536. // Add a variable scope that will be used by CompileBreak
  1537. // to know where to stop deallocating variables
  1538. AddVariableScope(true, false);
  1539. //---------------------------
  1540. // Compile the switch expression
  1541. //-------------------------------
  1542. // Compile the switch expression
  1543. asSExprContext expr(engine);
  1544. CompileAssignment(snode->firstChild, &expr);
  1545. // Verify that the expression is a primitive type
  1546. if( !expr.type.dataType.IsIntegerType() && !expr.type.dataType.IsUnsignedType() && !expr.type.dataType.IsEnumType() )
  1547. {
  1548. Error(TXT_SWITCH_MUST_BE_INTEGRAL, snode->firstChild);
  1549. return;
  1550. }
  1551. // TODO: Need to support 64bit
  1552. // Convert the expression to a 32bit variable
  1553. asCDataType to;
  1554. if( expr.type.dataType.IsIntegerType() || expr.type.dataType.IsEnumType() )
  1555. to.SetTokenType(ttInt);
  1556. else if( expr.type.dataType.IsUnsignedType() )
  1557. to.SetTokenType(ttUInt);
  1558. ImplicitConversion(&expr, to, snode->firstChild, asIC_IMPLICIT_CONV, true);
  1559. ConvertToVariable(&expr);
  1560. int offset = expr.type.stackOffset;
  1561. //-------------------------------
  1562. // Determine case values and labels
  1563. //--------------------------------
  1564. // Remember the first label so that we can later pass the
  1565. // correct label to each CompileCase()
  1566. int firstCaseLabel = nextLabel;
  1567. int defaultLabel = 0;
  1568. asCArray<int> caseValues;
  1569. asCArray<int> caseLabels;
  1570. // Compile all case comparisons and make them jump to the right label
  1571. asCScriptNode *cnode = snode->firstChild->next;
  1572. while( cnode )
  1573. {
  1574. // Each case should have a constant expression
  1575. if( cnode->firstChild && cnode->firstChild->nodeType == snExpression )
  1576. {
  1577. // Compile expression
  1578. asSExprContext c(engine);
  1579. CompileExpression(cnode->firstChild, &c);
  1580. // Verify that the result is a constant
  1581. if( !c.type.isConstant )
  1582. Error(TXT_SWITCH_CASE_MUST_BE_CONSTANT, cnode->firstChild);
  1583. // Verify that the result is an integral number
  1584. if( !c.type.dataType.IsIntegerType() && !c.type.dataType.IsUnsignedType() && !c.type.dataType.IsEnumType() )
  1585. Error(TXT_SWITCH_MUST_BE_INTEGRAL, cnode->firstChild);
  1586. ImplicitConversion(&c, to, cnode->firstChild, asIC_IMPLICIT_CONV, true);
  1587. // Has this case been declared already?
  1588. if( caseValues.IndexOf(c.type.intValue) >= 0 )
  1589. {
  1590. Error(TXT_DUPLICATE_SWITCH_CASE, cnode->firstChild);
  1591. }
  1592. // TODO: Optimize: We can insert the numbers sorted already
  1593. // Store constant for later use
  1594. caseValues.PushLast(c.type.intValue);
  1595. // Reserve label for this case
  1596. caseLabels.PushLast(nextLabel++);
  1597. }
  1598. else
  1599. {
  1600. // Is default the last case?
  1601. if( cnode->next )
  1602. {
  1603. Error(TXT_DEFAULT_MUST_BE_LAST, cnode);
  1604. break;
  1605. }
  1606. // Reserve label for this case
  1607. defaultLabel = nextLabel++;
  1608. }
  1609. cnode = cnode->next;
  1610. }
  1611. // check for empty switch
  1612. if (caseValues.GetLength() == 0)
  1613. {
  1614. Error(TXT_EMPTY_SWITCH, snode);
  1615. return;
  1616. }
  1617. if( defaultLabel == 0 )
  1618. defaultLabel = breakLabel;
  1619. //---------------------------------
  1620. // Output the optimized case comparisons
  1621. // with jumps to the case code
  1622. //------------------------------------
  1623. // Sort the case values by increasing value. Do the sort together with the labels
  1624. // A simple bubble sort is sufficient since we don't expect a huge number of values
  1625. for( asUINT fwd = 1; fwd < caseValues.GetLength(); fwd++ )
  1626. {
  1627. for( int bck = fwd - 1; bck >= 0; bck-- )
  1628. {
  1629. int bckp = bck + 1;
  1630. if( caseValues[bck] > caseValues[bckp] )
  1631. {
  1632. // Swap the values in both arrays
  1633. int swap = caseValues[bckp];
  1634. caseValues[bckp] = caseValues[bck];
  1635. caseValues[bck] = swap;
  1636. swap = caseLabels[bckp];
  1637. caseLabels[bckp] = caseLabels[bck];
  1638. caseLabels[bck] = swap;
  1639. }
  1640. else
  1641. break;
  1642. }
  1643. }
  1644. // Find ranges of consecutive numbers
  1645. asCArray<int> ranges;
  1646. ranges.PushLast(0);
  1647. asUINT n;
  1648. for( n = 1; n < caseValues.GetLength(); ++n )
  1649. {
  1650. // We can join numbers that are less than 5 numbers
  1651. // apart since the output code will still be smaller
  1652. if( caseValues[n] > caseValues[n-1] + 5 )
  1653. ranges.PushLast(n);
  1654. }
  1655. // If the value is larger than the largest case value, jump to default
  1656. int tmpOffset = AllocateVariable(asCDataType::CreatePrimitive(ttInt, false), true);
  1657. expr.bc.InstrSHORT_DW(asBC_SetV4, (short)tmpOffset, caseValues[caseValues.GetLength()-1]);
  1658. expr.bc.InstrW_W(asBC_CMPi, offset, tmpOffset);
  1659. expr.bc.InstrDWORD(asBC_JP, defaultLabel);
  1660. ReleaseTemporaryVariable(tmpOffset, &expr.bc);
  1661. // TODO: optimize: We could possibly optimize this even more by doing a
  1662. // binary search instead of a linear search through the ranges
  1663. // For each range
  1664. int range;
  1665. for( range = 0; range < (int)ranges.GetLength(); range++ )
  1666. {
  1667. // Find the largest value in this range
  1668. int maxRange = caseValues[ranges[range]];
  1669. int index = ranges[range];
  1670. for( ; (index < (int)caseValues.GetLength()) && (caseValues[index] <= maxRange + 5); index++ )
  1671. maxRange = caseValues[index];
  1672. // If there are only 2 numbers then it is better to compare them directly
  1673. if( index - ranges[range] > 2 )
  1674. {
  1675. // If the value is smaller than the smallest case value in the range, jump to default
  1676. tmpOffset = AllocateVariable(asCDataType::CreatePrimitive(ttInt, false), true);
  1677. expr.bc.InstrSHORT_DW(asBC_SetV4, (short)tmpOffset, caseValues[ranges[range]]);
  1678. expr.bc.InstrW_W(asBC_CMPi, offset, tmpOffset);
  1679. expr.bc.InstrDWORD(asBC_JS, defaultLabel);
  1680. ReleaseTemporaryVariable(tmpOffset, &expr.bc);
  1681. int nextRangeLabel = nextLabel++;
  1682. // If this is the last range we don't have to make this test
  1683. if( range < (int)ranges.GetLength() - 1 )
  1684. {
  1685. // If the value is larger than the largest case value in the range, jump to the next range
  1686. tmpOffset = AllocateVariable(asCDataType::CreatePrimitive(ttInt, false), true);
  1687. expr.bc.InstrSHORT_DW(asBC_SetV4, (short)tmpOffset, maxRange);
  1688. expr.bc.InstrW_W(asBC_CMPi, offset, tmpOffset);
  1689. expr.bc.InstrDWORD(asBC_JP, nextRangeLabel);
  1690. ReleaseTemporaryVariable(tmpOffset, &expr.bc);
  1691. }
  1692. // Jump forward according to the value
  1693. tmpOffset = AllocateVariable(asCDataType::CreatePrimitive(ttInt, false), true);
  1694. expr.bc.InstrSHORT_DW(asBC_SetV4, (short)tmpOffset, caseValues[ranges[range]]);
  1695. expr.bc.InstrW_W_W(asBC_SUBi, tmpOffset, offset, tmpOffset);
  1696. ReleaseTemporaryVariable(tmpOffset, &expr.bc);
  1697. expr.bc.JmpP(tmpOffset, maxRange - caseValues[ranges[range]]);
  1698. // Add the list of jumps to the correct labels (any holes, jump to default)
  1699. index = ranges[range];
  1700. for( int n = caseValues[index]; n <= maxRange; n++ )
  1701. {
  1702. if( caseValues[index] == n )
  1703. expr.bc.InstrINT(asBC_JMP, caseLabels[index++]);
  1704. else
  1705. expr.bc.InstrINT(asBC_JMP, defaultLabel);
  1706. }
  1707. expr.bc.Label((short)nextRangeLabel);
  1708. }
  1709. else
  1710. {
  1711. // Simply make a comparison with each value
  1712. int n;
  1713. for( n = ranges[range]; n < index; ++n )
  1714. {
  1715. tmpOffset = AllocateVariable(asCDataType::CreatePrimitive(ttInt, false), true);
  1716. expr.bc.InstrSHORT_DW(asBC_SetV4, (short)tmpOffset, caseValues[n]);
  1717. expr.bc.InstrW_W(asBC_CMPi, offset, tmpOffset);
  1718. expr.bc.InstrDWORD(asBC_JZ, caseLabels[n]);
  1719. ReleaseTemporaryVariable(tmpOffset, &expr.bc);
  1720. }
  1721. }
  1722. }
  1723. // Catch any value that falls trough
  1724. expr.bc.InstrINT(asBC_JMP, defaultLabel);
  1725. // Release the temporary variable previously stored
  1726. ReleaseTemporaryVariable(expr.type, &expr.bc);
  1727. //----------------------------------
  1728. // Output case implementations
  1729. //----------------------------------
  1730. // Compile case implementations, each one with the label before it
  1731. cnode = snode->firstChild->next;
  1732. while( cnode )
  1733. {
  1734. // Each case should have a constant expression
  1735. if( cnode->firstChild && cnode->firstChild->nodeType == snExpression )
  1736. {
  1737. expr.bc.Label((short)firstCaseLabel++);
  1738. CompileCase(cnode->firstChild->next, &expr.bc);
  1739. }
  1740. else
  1741. {
  1742. expr.bc.Label((short)defaultLabel);
  1743. // Is default the last case?
  1744. if( cnode->next )
  1745. {
  1746. // We've already reported this error
  1747. break;
  1748. }
  1749. CompileCase(cnode->firstChild, &expr.bc);
  1750. }
  1751. cnode = cnode->next;
  1752. }
  1753. //--------------------------------
  1754. bc->AddCode(&expr.bc);
  1755. // Add break label
  1756. bc->Label((short)breakLabel);
  1757. breakLabels.PopLast();
  1758. RemoveVariableScope();
  1759. }
  1760. void asCCompiler::CompileCase(asCScriptNode *node, asCByteCode *bc)
  1761. {
  1762. bool isFinished = false;
  1763. bool hasReturn = false;
  1764. while( node )
  1765. {
  1766. if( hasReturn || isFinished )
  1767. {
  1768. Warning(TXT_UNREACHABLE_CODE, node);
  1769. break;
  1770. }
  1771. if( node->nodeType == snBreak || node->nodeType == snContinue )
  1772. isFinished = true;
  1773. asCByteCode statement(engine);
  1774. CompileStatement(node, &hasReturn, &statement);
  1775. LineInstr(bc, node->tokenPos);
  1776. bc->AddCode(&statement);
  1777. if( !hasCompileErrors )
  1778. asASSERT( tempVariables.GetLength() == 0 );
  1779. node = node->next;
  1780. }
  1781. }
  1782. void asCCompiler::CompileIfStatement(asCScriptNode *inode, bool *hasReturn, asCByteCode *bc)
  1783. {
  1784. // We will use one label for the if statement
  1785. // and possibly another for the else statement
  1786. int afterLabel = nextLabel++;
  1787. // Compile the expression
  1788. asSExprContext expr(engine);
  1789. CompileAssignment(inode->firstChild, &expr);
  1790. if( !expr.type.dataType.IsEqualExceptRefAndConst(asCDataType::CreatePrimitive(ttBool, true)) )
  1791. {
  1792. Error(TXT_EXPR_MUST_BE_BOOL, inode->firstChild);
  1793. expr.type.SetConstantDW(asCDataType::CreatePrimitive(ttBool, true), 1);
  1794. }
  1795. if( expr.type.dataType.IsReference() ) ConvertToVariable(&expr);
  1796. ProcessDeferredParams(&expr);
  1797. if( !expr.type.isConstant )
  1798. {
  1799. ProcessPropertyGetAccessor(&expr, inode);
  1800. ConvertToVariable(&expr);
  1801. // Add byte code from the expression
  1802. bc->AddCode(&expr.bc);
  1803. // Add a test
  1804. bc->InstrSHORT(asBC_CpyVtoR4, expr.type.stackOffset);
  1805. bc->Instr(asBC_ClrHi);
  1806. bc->InstrDWORD(asBC_JZ, afterLabel);
  1807. ReleaseTemporaryVariable(expr.type, bc);
  1808. }
  1809. else if( expr.type.dwordValue == 0 )
  1810. {
  1811. // Jump to the else case
  1812. bc->InstrINT(asBC_JMP, afterLabel);
  1813. // TODO: Should we warn?
  1814. }
  1815. // Compile the if statement
  1816. bool origIsConstructorCalled = m_isConstructorCalled;
  1817. bool hasReturn1;
  1818. asCByteCode ifBC(engine);
  1819. CompileStatement(inode->firstChild->next, &hasReturn1, &ifBC);
  1820. // Add the byte code
  1821. LineInstr(bc, inode->firstChild->next->tokenPos);
  1822. bc->AddCode(&ifBC);
  1823. if( inode->firstChild->next->nodeType == snExpressionStatement && inode->firstChild->next->firstChild == 0 )
  1824. {
  1825. // Don't allow if( expr );
  1826. Error(TXT_IF_WITH_EMPTY_STATEMENT, inode->firstChild->next);
  1827. }
  1828. // If one of the statements call the constructor, the other must as well
  1829. // otherwise it is possible the constructor is never called
  1830. bool constructorCall1 = false;
  1831. bool constructorCall2 = false;
  1832. if( !origIsConstructorCalled && m_isConstructorCalled )
  1833. constructorCall1 = true;
  1834. // Do we have an else statement?
  1835. if( inode->firstChild->next != inode->lastChild )
  1836. {
  1837. // Reset the constructor called flag so the else statement can call the constructor too
  1838. m_isConstructorCalled = origIsConstructorCalled;
  1839. int afterElse = 0;
  1840. if( !hasReturn1 )
  1841. {
  1842. afterElse = nextLabel++;
  1843. // Add jump to after the else statement
  1844. bc->InstrINT(asBC_JMP, afterElse);
  1845. }
  1846. // Add label for the else statement
  1847. bc->Label((short)afterLabel);
  1848. bool hasReturn2;
  1849. asCByteCode elseBC(engine);
  1850. CompileStatement(inode->lastChild, &hasReturn2, &elseBC);
  1851. // Add byte code for the else statement
  1852. LineInstr(bc, inode->lastChild->tokenPos);
  1853. bc->AddCode(&elseBC);
  1854. if( inode->lastChild->nodeType == snExpressionStatement && inode->lastChild->firstChild == 0 )
  1855. {
  1856. // Don't allow if( expr ) {} else;
  1857. Error(TXT_ELSE_WITH_EMPTY_STATEMENT, inode->lastChild);
  1858. }
  1859. if( !hasReturn1 )
  1860. {
  1861. // Add label for the end of else statement
  1862. bc->Label((short)afterElse);
  1863. }
  1864. // The if statement only has return if both alternatives have
  1865. *hasReturn = hasReturn1 && hasReturn2;
  1866. if( !origIsConstructorCalled && m_isConstructorCalled )
  1867. constructorCall2 = true;
  1868. }
  1869. else
  1870. {
  1871. // Add label for the end of if statement
  1872. bc->Label((short)afterLabel);
  1873. *hasReturn = false;
  1874. }
  1875. // Make sure both or neither conditions call a constructor
  1876. if( (constructorCall1 && !constructorCall2) ||
  1877. (constructorCall2 && !constructorCall1) )
  1878. {
  1879. Error(TXT_BOTH_CONDITIONS_MUST_CALL_CONSTRUCTOR, inode);
  1880. }
  1881. m_isConstructorCalled = origIsConstructorCalled || constructorCall1 || constructorCall2;
  1882. }
  1883. void asCCompiler::CompileForStatement(asCScriptNode *fnode, asCByteCode *bc)
  1884. {
  1885. // Add a variable scope that will be used by CompileBreak/Continue to know where to stop deallocating variables
  1886. AddVariableScope(true, true);
  1887. // We will use three labels for the for loop
  1888. int beforeLabel = nextLabel++;
  1889. int afterLabel = nextLabel++;
  1890. int continueLabel = nextLabel++;
  1891. continueLabels.PushLast(continueLabel);
  1892. breakLabels.PushLast(afterLabel);
  1893. //---------------------------------------
  1894. // Compile the initialization statement
  1895. asCByteCode initBC(engine);
  1896. if( fnode->firstChild->nodeType == snDeclaration )
  1897. CompileDeclaration(fnode->firstChild, &initBC);
  1898. else
  1899. CompileExpressionStatement(fnode->firstChild, &initBC);
  1900. //-----------------------------------
  1901. // Compile the condition statement
  1902. asSExprContext expr(engine);
  1903. asCScriptNode *second = fnode->firstChild->next;
  1904. if( second->firstChild )
  1905. {
  1906. int r = CompileAssignment(second->firstChild, &expr);
  1907. if( r >= 0 )
  1908. {
  1909. if( !expr.type.dataType.IsEqualExceptRefAndConst(asCDataType::CreatePrimitive(ttBool, true)) )
  1910. Error(TXT_EXPR_MUST_BE_BOOL, second);
  1911. else
  1912. {
  1913. if( expr.type.dataType.IsReference() ) ConvertToVariable(&expr);
  1914. ProcessDeferredParams(&expr);
  1915. // If expression is false exit the loop
  1916. ConvertToVariable(&expr);
  1917. expr.bc.InstrSHORT(asBC_CpyVtoR4, expr.type.stackOffset);
  1918. expr.bc.Instr(asBC_ClrHi);
  1919. expr.bc.InstrDWORD(asBC_JZ, afterLabel);
  1920. ReleaseTemporaryVariable(expr.type, &expr.bc);
  1921. }
  1922. }
  1923. }
  1924. //---------------------------
  1925. // Compile the increment statement
  1926. asCByteCode nextBC(engine);
  1927. asCScriptNode *third = second->next;
  1928. if( third->nodeType == snExpressionStatement )
  1929. CompileExpressionStatement(third, &nextBC);
  1930. //------------------------------
  1931. // Compile loop statement
  1932. bool hasReturn;
  1933. asCByteCode forBC(engine);
  1934. CompileStatement(fnode->lastChild, &hasReturn, &forBC);
  1935. //-------------------------------
  1936. // Join the code pieces
  1937. bc->AddCode(&initBC);
  1938. bc->Label((short)beforeLabel);
  1939. // Add a suspend bytecode inside the loop to guarantee
  1940. // that the application can suspend the execution
  1941. bc->Instr(asBC_SUSPEND);
  1942. bc->InstrWORD(asBC_JitEntry, 0);
  1943. bc->AddCode(&expr.bc);
  1944. LineInstr(bc, fnode->lastChild->tokenPos);
  1945. bc->AddCode(&forBC);
  1946. bc->Label((short)continueLabel);
  1947. bc->AddCode(&nextBC);
  1948. bc->InstrINT(asBC_JMP, beforeLabel);
  1949. bc->Label((short)afterLabel);
  1950. continueLabels.PopLast();
  1951. breakLabels.PopLast();
  1952. // Deallocate variables in this block, in reverse order
  1953. for( int n = (int)variables->variables.GetLength() - 1; n >= 0; n-- )
  1954. {
  1955. sVariable *v = variables->variables[n];
  1956. // Call variable destructors here, for variables not yet destroyed
  1957. CallDestructor(v->type, v->stackOffset, bc);
  1958. // Don't deallocate function parameters
  1959. if( v->stackOffset > 0 )
  1960. DeallocateVariable(v->stackOffset);
  1961. }
  1962. RemoveVariableScope();
  1963. }
  1964. void asCCompiler::CompileWhileStatement(asCScriptNode *wnode, asCByteCode *bc)
  1965. {
  1966. // Add a variable scope that will be used by CompileBreak/Continue to know where to stop deallocating variables
  1967. AddVariableScope(true, true);
  1968. // We will use two labels for the while loop
  1969. int beforeLabel = nextLabel++;
  1970. int afterLabel = nextLabel++;
  1971. continueLabels.PushLast(beforeLabel);
  1972. breakLabels.PushLast(afterLabel);
  1973. // Add label before the expression
  1974. bc->Label((short)beforeLabel);
  1975. // Compile expression
  1976. asSExprContext expr(engine);
  1977. CompileAssignment(wnode->firstChild, &expr);
  1978. if( !expr.type.dataType.IsEqualExceptRefAndConst(asCDataType::CreatePrimitive(ttBool, true)) )
  1979. Error(TXT_EXPR_MUST_BE_BOOL, wnode->firstChild);
  1980. if( expr.type.dataType.IsReference() ) ConvertToVariable(&expr);
  1981. ProcessDeferredParams(&expr);
  1982. // Add byte code for the expression
  1983. ConvertToVariable(&expr);
  1984. bc->AddCode(&expr.bc);
  1985. // Jump to end of statement if expression is false
  1986. bc->InstrSHORT(asBC_CpyVtoR4, expr.type.stackOffset);
  1987. bc->Instr(asBC_ClrHi);
  1988. bc->InstrDWORD(asBC_JZ, afterLabel);
  1989. ReleaseTemporaryVariable(expr.type, bc);
  1990. // Add a suspend bytecode inside the loop to guarantee
  1991. // that the application can suspend the execution
  1992. bc->Instr(asBC_SUSPEND);
  1993. bc->InstrWORD(asBC_JitEntry, 0);
  1994. // Compile statement
  1995. bool hasReturn;
  1996. asCByteCode whileBC(engine);
  1997. CompileStatement(wnode->lastChild, &hasReturn, &whileBC);
  1998. // Add byte code for the statement
  1999. LineInstr(bc, wnode->lastChild->tokenPos);
  2000. bc->AddCode(&whileBC);
  2001. // Jump to the expression
  2002. bc->InstrINT(asBC_JMP, beforeLabel);
  2003. // Add label after the statement
  2004. bc->Label((short)afterLabel);
  2005. continueLabels.PopLast();
  2006. breakLabels.PopLast();
  2007. RemoveVariableScope();
  2008. }
  2009. void asCCompiler::CompileDoWhileStatement(asCScriptNode *wnode, asCByteCode *bc)
  2010. {
  2011. // Add a variable scope that will be used by CompileBreak/Continue to know where to stop deallocating variables
  2012. AddVariableScope(true, true);
  2013. // We will use two labels for the while loop
  2014. int beforeLabel = nextLabel++;
  2015. int beforeTest = nextLabel++;
  2016. int afterLabel = nextLabel++;
  2017. continueLabels.PushLast(beforeTest);
  2018. breakLabels.PushLast(afterLabel);
  2019. // Add label before the statement
  2020. bc->Label((short)beforeLabel);
  2021. // Compile statement
  2022. bool hasReturn;
  2023. asCByteCode whileBC(engine);
  2024. CompileStatement(wnode->firstChild, &hasReturn, &whileBC);
  2025. // Add byte code for the statement
  2026. LineInstr(bc, wnode->firstChild->tokenPos);
  2027. bc->AddCode(&whileBC);
  2028. // Add label before the expression
  2029. bc->Label((short)beforeTest);
  2030. // Add a suspend bytecode inside the loop to guarantee
  2031. // that the application can suspend the execution
  2032. bc->Instr(asBC_SUSPEND);
  2033. bc->InstrWORD(asBC_JitEntry, 0);
  2034. // Add a line instruction
  2035. LineInstr(bc, wnode->lastChild->tokenPos);
  2036. // Compile expression
  2037. asSExprContext expr(engine);
  2038. CompileAssignment(wnode->lastChild, &expr);
  2039. if( !expr.type.dataType.IsEqualExceptRefAndConst(asCDataType::CreatePrimitive(ttBool, true)) )
  2040. Error(TXT_EXPR_MUST_BE_BOOL, wnode->firstChild);
  2041. if( expr.type.dataType.IsReference() ) ConvertToVariable(&expr);
  2042. ProcessDeferredParams(&expr);
  2043. // Add byte code for the expression
  2044. ConvertToVariable(&expr);
  2045. bc->AddCode(&expr.bc);
  2046. // Jump to next iteration if expression is true
  2047. bc->InstrSHORT(asBC_CpyVtoR4, expr.type.stackOffset);
  2048. bc->Instr(asBC_ClrHi);
  2049. bc->InstrDWORD(asBC_JNZ, beforeLabel);
  2050. ReleaseTemporaryVariable(expr.type, bc);
  2051. // Add label after the statement
  2052. bc->Label((short)afterLabel);
  2053. continueLabels.PopLast();
  2054. breakLabels.PopLast();
  2055. RemoveVariableScope();
  2056. }
  2057. void asCCompiler::CompileBreakStatement(asCScriptNode *node, asCByteCode *bc)
  2058. {
  2059. if( breakLabels.GetLength() == 0 )
  2060. {
  2061. Error(TXT_INVALID_BREAK, node);
  2062. return;
  2063. }
  2064. // Add destructor calls for all variables that will go out of scope
  2065. asCVariableScope *vs = variables;
  2066. while( !vs->isBreakScope )
  2067. {
  2068. for( int n = (int)vs->variables.GetLength() - 1; n >= 0; n-- )
  2069. CallDestructor(vs->variables[n]->type, vs->variables[n]->stackOffset, bc);
  2070. vs = vs->parent;
  2071. }
  2072. bc->InstrINT(asBC_JMP, breakLabels[breakLabels.GetLength()-1]);
  2073. }
  2074. void asCCompiler::CompileContinueStatement(asCScriptNode *node, asCByteCode *bc)
  2075. {
  2076. if( continueLabels.GetLength() == 0 )
  2077. {
  2078. Error(TXT_INVALID_CONTINUE, node);
  2079. return;
  2080. }
  2081. // Add destructor calls for all variables that will go out of scope
  2082. asCVariableScope *vs = variables;
  2083. while( !vs->isContinueScope )
  2084. {
  2085. for( int n = (int)vs->variables.GetLength() - 1; n >= 0; n-- )
  2086. CallDestructor(vs->variables[n]->type, vs->variables[n]->stackOffset, bc);
  2087. vs = vs->parent;
  2088. }
  2089. bc->InstrINT(asBC_JMP, continueLabels[continueLabels.GetLength()-1]);
  2090. }
  2091. void asCCompiler::CompileExpressionStatement(asCScriptNode *enode, asCByteCode *bc)
  2092. {
  2093. if( enode->firstChild )
  2094. {
  2095. // Compile the expression
  2096. asSExprContext expr(engine);
  2097. CompileAssignment(enode->firstChild, &expr);
  2098. // Pop the value from the stack
  2099. if( !expr.type.dataType.IsPrimitive() )
  2100. expr.bc.Pop(expr.type.dataType.GetSizeOnStackDWords());
  2101. // Release temporary variables used by expression
  2102. ReleaseTemporaryVariable(expr.type, &expr.bc);
  2103. ProcessDeferredParams(&expr);
  2104. bc->AddCode(&expr.bc);
  2105. }
  2106. }
  2107. void asCCompiler::PrepareTemporaryObject(asCScriptNode *node, asSExprContext *ctx, asCArray<int> *reservedVars)
  2108. {
  2109. // If the object already is stored in temporary variable then nothing needs to be done
  2110. if( ctx->type.isTemporary ) return;
  2111. // Allocate temporary variable
  2112. asCDataType dt = ctx->type.dataType;
  2113. dt.MakeReference(false);
  2114. dt.MakeReadOnly(false);
  2115. int offset = AllocateVariableNotIn(dt, true, reservedVars);
  2116. // Allocate and construct the temporary object
  2117. CallDefaultConstructor(dt, offset, &ctx->bc, node);
  2118. // Assign the object to the temporary variable
  2119. asCTypeInfo lvalue;
  2120. dt.MakeReference(true);
  2121. lvalue.Set(dt);
  2122. lvalue.isTemporary = true;
  2123. lvalue.stackOffset = (short)offset;
  2124. lvalue.isVariable = true;
  2125. lvalue.isExplicitHandle = ctx->type.isExplicitHandle;
  2126. PrepareForAssignment(&lvalue.dataType, ctx, node);
  2127. ctx->bc.InstrSHORT(asBC_PSF, (short)offset);
  2128. PerformAssignment(&lvalue, &ctx->type, &ctx->bc, node);
  2129. // Pop the original reference
  2130. ctx->bc.Pop(AS_PTR_SIZE);
  2131. // Push the reference to the temporary variable on the stack
  2132. ctx->bc.InstrSHORT(asBC_PSF, (short)offset);
  2133. lvalue.dataType.MakeReference(true);
  2134. ctx->type = lvalue;
  2135. }
  2136. void asCCompiler::CompileReturnStatement(asCScriptNode *rnode, asCByteCode *bc)
  2137. {
  2138. // Get return type and location
  2139. sVariable *v = variables->GetVariable("return");
  2140. if( v->type.GetSizeOnStackDWords() > 0 )
  2141. {
  2142. // Is there an expression?
  2143. if( rnode->firstChild )
  2144. {
  2145. // Compile the expression
  2146. asSExprContext expr(engine);
  2147. int r = CompileAssignment(rnode->firstChild, &expr);
  2148. if( r >= 0 )
  2149. {
  2150. // Prepare the value for assignment
  2151. IsVariableInitialized(&expr.type, rnode->firstChild);
  2152. if( v->type.IsPrimitive() )
  2153. {
  2154. if( expr.type.dataType.IsReference() ) ConvertToVariable(&expr);
  2155. // Implicitly convert the value to the return type
  2156. ImplicitConversion(&expr, v->type, rnode->firstChild, asIC_IMPLICIT_CONV);
  2157. // Verify that the conversion was successful
  2158. if( expr.type.dataType != v->type )
  2159. {
  2160. asCString str;
  2161. str.Format(TXT_NO_CONVERSION_s_TO_s, expr.type.dataType.Format().AddressOf(), v->type.Format().AddressOf());
  2162. Error(str.AddressOf(), rnode);
  2163. r = -1;
  2164. }
  2165. else
  2166. {
  2167. ConvertToVariable(&expr);
  2168. ReleaseTemporaryVariable(expr.type, &expr.bc);
  2169. // Load the variable in the register
  2170. if( v->type.GetSizeOnStackDWords() == 1 )
  2171. expr.bc.InstrSHORT(asBC_CpyVtoR4, expr.type.stackOffset);
  2172. else
  2173. expr.bc.InstrSHORT(asBC_CpyVtoR8, expr.type.stackOffset);
  2174. }
  2175. }
  2176. else if( v->type.IsObject() )
  2177. {
  2178. PrepareArgument(&v->type, &expr, rnode->firstChild);
  2179. // Pop the reference to the temporary variable again
  2180. expr.bc.Pop(AS_PTR_SIZE);
  2181. // Load the object pointer into the object register
  2182. // LOADOBJ also clears the address in the variable
  2183. expr.bc.InstrSHORT(asBC_LOADOBJ, expr.type.stackOffset);
  2184. // LOADOBJ cleared the address in the variable so the object will not be freed
  2185. // here, but the temporary variable must still be freed
  2186. // TODO: optimize: Since there is nothing in the variable anymore,
  2187. // there is no need to call asBC_FREE on it.
  2188. }
  2189. // Release temporary variables used by expression
  2190. ReleaseTemporaryVariable(expr.type, &expr.bc);
  2191. bc->AddCode(&expr.bc);
  2192. }
  2193. }
  2194. else
  2195. Error(TXT_MUST_RETURN_VALUE, rnode);
  2196. }
  2197. else
  2198. if( rnode->firstChild )
  2199. Error(TXT_CANT_RETURN_VALUE, rnode);
  2200. // Call destructor on all variables except for the function parameters
  2201. asCVariableScope *vs = variables;
  2202. while( vs )
  2203. {
  2204. for( int n = (int)vs->variables.GetLength() - 1; n >= 0; n-- )
  2205. if( vs->variables[n]->stackOffset > 0 )
  2206. CallDestructor(vs->variables[n]->type, vs->variables[n]->stackOffset, bc);
  2207. vs = vs->parent;
  2208. }
  2209. // Jump to the end of the function
  2210. bc->InstrINT(asBC_JMP, 0);
  2211. }
  2212. void asCCompiler::AddVariableScope(bool isBreakScope, bool isContinueScope)
  2213. {
  2214. variables = asNEW(asCVariableScope)(variables);
  2215. variables->isBreakScope = isBreakScope;
  2216. variables->isContinueScope = isContinueScope;
  2217. }
  2218. void asCCompiler::RemoveVariableScope()
  2219. {
  2220. if( variables )
  2221. {
  2222. asCVariableScope *var = variables;
  2223. variables = variables->parent;
  2224. asDELETE(var,asCVariableScope);
  2225. }
  2226. }
  2227. void asCCompiler::Error(const char *msg, asCScriptNode *node)
  2228. {
  2229. asCString str;
  2230. int r, c;
  2231. script->ConvertPosToRowCol(node->tokenPos, &r, &c);
  2232. builder->WriteError(script->name.AddressOf(), msg, r, c);
  2233. hasCompileErrors = true;
  2234. }
  2235. void asCCompiler::Warning(const char *msg, asCScriptNode *node)
  2236. {
  2237. asCString str;
  2238. int r, c;
  2239. script->ConvertPosToRowCol(node->tokenPos, &r, &c);
  2240. builder->WriteWarning(script->name.AddressOf(), msg, r, c);
  2241. }
  2242. void asCCompiler::PrintMatchingFuncs(asCArray<int> &funcs, asCScriptNode *node)
  2243. {
  2244. int r, c;
  2245. script->ConvertPosToRowCol(node->tokenPos, &r, &c);
  2246. for( unsigned int n = 0; n < funcs.GetLength(); n++ )
  2247. {
  2248. asIScriptFunction *func = engine->scriptFunctions[funcs[n]];
  2249. builder->WriteInfo(script->name.AddressOf(), func->GetDeclaration(true), r, c, false);
  2250. }
  2251. }
  2252. int asCCompiler::AllocateVariable(const asCDataType &type, bool isTemporary)
  2253. {
  2254. return AllocateVariableNotIn(type, isTemporary, 0);
  2255. }
  2256. int asCCompiler::AllocateVariableNotIn(const asCDataType &type, bool isTemporary, asCArray<int> *vars)
  2257. {
  2258. asCDataType t(type);
  2259. if( t.IsPrimitive() && t.GetSizeOnStackDWords() == 1 )
  2260. t.SetTokenType(ttInt);
  2261. if( t.IsPrimitive() && t.GetSizeOnStackDWords() == 2 )
  2262. t.SetTokenType(ttDouble);
  2263. // Find a free location with the same type
  2264. for( asUINT n = 0; n < freeVariables.GetLength(); n++ )
  2265. {
  2266. int slot = freeVariables[n];
  2267. if( variableAllocations[slot].IsEqualExceptConst(t) && variableIsTemporary[slot] == isTemporary )
  2268. {
  2269. // We can't return by slot, must count variable sizes
  2270. int offset = GetVariableOffset(slot);
  2271. // Verify that it is not in the list of used variables
  2272. bool isUsed = false;
  2273. if( vars )
  2274. {
  2275. for( asUINT m = 0; m < vars->GetLength(); m++ )
  2276. {
  2277. if( offset == (*vars)[m] )
  2278. {
  2279. isUsed = true;
  2280. break;
  2281. }
  2282. }
  2283. }
  2284. if( !isUsed )
  2285. {
  2286. if( n != freeVariables.GetLength() - 1 )
  2287. freeVariables[n] = freeVariables.PopLast();
  2288. else
  2289. freeVariables.PopLast();
  2290. if( isTemporary )
  2291. tempVariables.PushLast(offset);
  2292. return offset;
  2293. }
  2294. }
  2295. }
  2296. variableAllocations.PushLast(t);
  2297. variableIsTemporary.PushLast(isTemporary);
  2298. int offset = GetVariableOffset((int)variableAllocations.GetLength()-1);
  2299. if( isTemporary )
  2300. tempVariables.PushLast(offset);
  2301. return offset;
  2302. }
  2303. int asCCompiler::GetVariableOffset(int varIndex)
  2304. {
  2305. // Return offset to the last dword on the stack
  2306. int varOffset = 1;
  2307. for( int n = 0; n < varIndex; n++ )
  2308. varOffset += variableAllocations[n].GetSizeOnStackDWords();
  2309. if( varIndex < (int)variableAllocations.GetLength() )
  2310. {
  2311. int size = variableAllocations[varIndex].GetSizeOnStackDWords();
  2312. if( size > 1 )
  2313. varOffset += size-1;
  2314. }
  2315. return varOffset;
  2316. }
  2317. int asCCompiler::GetVariableSlot(int offset)
  2318. {
  2319. int varOffset = 1;
  2320. for( asUINT n = 0; n < variableAllocations.GetLength(); n++ )
  2321. {
  2322. varOffset += -1 + variableAllocations[n].GetSizeOnStackDWords();
  2323. if( varOffset == offset )
  2324. {
  2325. return n;
  2326. }
  2327. varOffset++;
  2328. }
  2329. return -1;
  2330. }
  2331. void asCCompiler::DeallocateVariable(int offset)
  2332. {
  2333. // Remove temporary variable
  2334. int n;
  2335. for( n = 0; n < (int)tempVariables.GetLength(); n++ )
  2336. {
  2337. if( offset == tempVariables[n] )
  2338. {
  2339. if( n == (int)tempVariables.GetLength()-1 )
  2340. tempVariables.PopLast();
  2341. else
  2342. tempVariables[n] = tempVariables.PopLast();
  2343. break;
  2344. }
  2345. }
  2346. n = GetVariableSlot(offset);
  2347. if( n != -1 )
  2348. {
  2349. freeVariables.PushLast(n);
  2350. return;
  2351. }
  2352. // We might get here if the variable was implicitly declared
  2353. // because it was use before a formal declaration, in this case
  2354. // the offset is 0x7FFF
  2355. asASSERT(offset == 0x7FFF);
  2356. }
  2357. void asCCompiler::ReleaseTemporaryVariable(asCTypeInfo &t, asCByteCode *bc)
  2358. {
  2359. if( t.isTemporary )
  2360. {
  2361. if( bc )
  2362. {
  2363. // We need to call the destructor on the true variable type
  2364. int n = GetVariableSlot(t.stackOffset);
  2365. asCDataType dt = variableAllocations[n];
  2366. // Call destructor
  2367. CallDestructor(dt, t.stackOffset, bc);
  2368. }
  2369. DeallocateVariable(t.stackOffset);
  2370. t.isTemporary = false;
  2371. }
  2372. }
  2373. void asCCompiler::ReleaseTemporaryVariable(int offset, asCByteCode *bc)
  2374. {
  2375. if( bc )
  2376. {
  2377. // We need to call the destructor on the true variable type
  2378. int n = GetVariableSlot(offset);
  2379. asCDataType dt = variableAllocations[n];
  2380. // Call destructor
  2381. CallDestructor(dt, offset, bc);
  2382. }
  2383. DeallocateVariable(offset);
  2384. }
  2385. void asCCompiler::Dereference(asSExprContext *ctx, bool generateCode)
  2386. {
  2387. if( ctx->type.dataType.IsReference() )
  2388. {
  2389. if( ctx->type.dataType.IsObject() )
  2390. {
  2391. ctx->type.dataType.MakeReference(false);
  2392. if( generateCode )
  2393. {
  2394. ctx->bc.Instr(asBC_CHKREF);
  2395. ctx->bc.Instr(asBC_RDSPTR);
  2396. }
  2397. }
  2398. else
  2399. {
  2400. // This should never happen as primitives are treated differently
  2401. asASSERT(false);
  2402. }
  2403. }
  2404. }
  2405. bool asCCompiler::IsVariableInitialized(asCTypeInfo *type, asCScriptNode *node)
  2406. {
  2407. // Temporary variables are assumed to be initialized
  2408. if( type->isTemporary ) return true;
  2409. // Verify that it is a variable
  2410. if( !type->isVariable ) return true;
  2411. // Find the variable
  2412. sVariable *v = variables->GetVariableByOffset(type->stackOffset);
  2413. // The variable isn't found if it is a constant, in which case it is guaranteed to be initialized
  2414. if( v == 0 ) return true;
  2415. if( v->isInitialized ) return true;
  2416. // Complex types don't need this test
  2417. if( v->type.IsObject() ) return true;
  2418. // Mark as initialized so that the user will not be bothered again
  2419. v->isInitialized = true;
  2420. // Write warning
  2421. asCString str;
  2422. str.Format(TXT_s_NOT_INITIALIZED, (const char *)v->name.AddressOf());
  2423. Warning(str.AddressOf(), node);
  2424. return false;
  2425. }
  2426. void asCCompiler::PrepareOperand(asSExprContext *ctx, asCScriptNode *node)
  2427. {
  2428. // Check if the variable is initialized (if it indeed is a variable)
  2429. IsVariableInitialized(&ctx->type, node);
  2430. asCDataType to = ctx->type.dataType;
  2431. to.MakeReference(false);
  2432. ImplicitConversion(ctx, to, node, asIC_IMPLICIT_CONV);
  2433. ProcessDeferredParams(ctx);
  2434. }
  2435. void asCCompiler::PrepareForAssignment(asCDataType *lvalue, asSExprContext *rctx, asCScriptNode *node, asSExprContext *lvalueExpr)
  2436. {
  2437. ProcessPropertyGetAccessor(rctx, node);
  2438. // Make sure the rvalue is initialized if it is a variable
  2439. IsVariableInitialized(&rctx->type, node);
  2440. if( lvalue->IsPrimitive() )
  2441. {
  2442. if( rctx->type.dataType.IsPrimitive() )
  2443. {
  2444. if( rctx->type.dataType.IsReference() )
  2445. {
  2446. // Cannot do implicit conversion of references so we first convert the reference to a variable
  2447. ConvertToVariableNotIn(rctx, lvalueExpr);
  2448. }
  2449. }
  2450. // Implicitly convert the value to the right type
  2451. asCArray<int> usedVars;
  2452. if( lvalueExpr ) lvalueExpr->bc.GetVarsUsed(usedVars);
  2453. ImplicitConversion(rctx, *lvalue, node, asIC_IMPLICIT_CONV, true, &usedVars);
  2454. // Check data type
  2455. if( !lvalue->IsEqualExceptRefAndConst(rctx->type.dataType) )
  2456. {
  2457. asCString str;
  2458. str.Format(TXT_CANT_IMPLICITLY_CONVERT_s_TO_s, rctx->type.dataType.Format().AddressOf(), lvalue->Format().AddressOf());
  2459. Error(str.AddressOf(), node);
  2460. rctx->type.SetDummy();
  2461. }
  2462. // Make sure the rvalue is a variable
  2463. if( !rctx->type.isVariable )
  2464. ConvertToVariableNotIn(rctx, lvalueExpr);
  2465. }
  2466. else
  2467. {
  2468. asCDataType to = *lvalue;
  2469. to.MakeReference(false);
  2470. // TODO: ImplicitConversion should know to do this by itself
  2471. // First convert to a handle which will to a reference cast
  2472. if( !lvalue->IsObjectHandle() &&
  2473. (lvalue->GetObjectType()->flags & asOBJ_SCRIPT_OBJECT) )
  2474. to.MakeHandle(true);
  2475. // Don't allow the implicit conversion to create an object
  2476. ImplicitConversion(rctx, to, node, asIC_IMPLICIT_CONV, true, 0, false);
  2477. if( !lvalue->IsObjectHandle() &&
  2478. (lvalue->GetObjectType()->flags & asOBJ_SCRIPT_OBJECT) )
  2479. {
  2480. // Then convert to a reference, which will validate the handle
  2481. to.MakeHandle(false);
  2482. ImplicitConversion(rctx, to, node, asIC_IMPLICIT_CONV, true, 0, false);
  2483. }
  2484. // Check data type
  2485. if( !lvalue->IsEqualExceptRefAndConst(rctx->type.dataType) )
  2486. {
  2487. asCString str;
  2488. str.Format(TXT_CANT_IMPLICITLY_CONVERT_s_TO_s, rctx->type.dataType.Format().AddressOf(), lvalue->Format().AddressOf());
  2489. Error(str.AddressOf(), node);
  2490. }
  2491. else
  2492. {
  2493. // If the assignment will be made with the copy behaviour then the rvalue must not be a reference
  2494. if( lvalue->IsObject() )
  2495. asASSERT(!rctx->type.dataType.IsReference());
  2496. }
  2497. }
  2498. }
  2499. bool asCCompiler::IsLValue(asCTypeInfo &type)
  2500. {
  2501. if( type.dataType.IsReadOnly() ) return false;
  2502. if( !type.dataType.IsObject() && !type.isVariable && !type.dataType.IsReference() ) return false;
  2503. if( type.isTemporary ) return false;
  2504. return true;
  2505. }
  2506. void asCCompiler::PerformAssignment(asCTypeInfo *lvalue, asCTypeInfo *rvalue, asCByteCode *bc, asCScriptNode *node)
  2507. {
  2508. if( lvalue->dataType.IsReadOnly() )
  2509. Error(TXT_REF_IS_READ_ONLY, node);
  2510. if( lvalue->dataType.IsPrimitive() )
  2511. {
  2512. if( lvalue->isVariable )
  2513. {
  2514. // Copy the value between the variables directly
  2515. if( lvalue->dataType.GetSizeInMemoryDWords() == 1 )
  2516. bc->InstrW_W(asBC_CpyVtoV4, lvalue->stackOffset, rvalue->stackOffset);
  2517. else
  2518. bc->InstrW_W(asBC_CpyVtoV8, lvalue->stackOffset, rvalue->stackOffset);
  2519. // Mark variable as initialized
  2520. sVariable *v = variables->GetVariableByOffset(lvalue->stackOffset);
  2521. if( v ) v->isInitialized = true;
  2522. }
  2523. else if( lvalue->dataType.IsReference() )
  2524. {
  2525. // Copy the value of the variable to the reference in the register
  2526. int s = lvalue->dataType.GetSizeInMemoryBytes();
  2527. if( s == 1 )
  2528. bc->InstrSHORT(asBC_WRTV1, rvalue->stackOffset);
  2529. else if( s == 2 )
  2530. bc->InstrSHORT(asBC_WRTV2, rvalue->stackOffset);
  2531. else if( s == 4 )
  2532. bc->InstrSHORT(asBC_WRTV4, rvalue->stackOffset);
  2533. else if( s == 8 )
  2534. bc->InstrSHORT(asBC_WRTV8, rvalue->stackOffset);
  2535. }
  2536. else
  2537. {
  2538. Error(TXT_NOT_VALID_LVALUE, node);
  2539. return;
  2540. }
  2541. }
  2542. else if( !lvalue->isExplicitHandle )
  2543. {
  2544. // TODO: Call the assignment operator, or do a BC_COPY if none exist
  2545. asSExprContext ctx(engine);
  2546. ctx.type = *lvalue;
  2547. Dereference(&ctx, true);
  2548. *lvalue = ctx.type;
  2549. bc->AddCode(&ctx.bc);
  2550. // TODO: Can't this leave deferred output params unhandled?
  2551. // TODO: Should find the opAssign method that implements the default copy behaviour.
  2552. // The beh->copy member will be removed.
  2553. asSTypeBehaviour *beh = lvalue->dataType.GetBehaviour();
  2554. if( beh->copy )
  2555. {
  2556. // Call the copy operator
  2557. bc->Call(asBC_CALLSYS, (asDWORD)beh->copy, 2*AS_PTR_SIZE);
  2558. bc->Instr(asBC_PshRPtr);
  2559. }
  2560. else
  2561. {
  2562. // Default copy operator
  2563. if( lvalue->dataType.GetSizeInMemoryDWords() == 0 ||
  2564. !(lvalue->dataType.GetObjectType()->flags & asOBJ_POD) )
  2565. {
  2566. Error(TXT_NO_DEFAULT_COPY_OP, node);
  2567. }
  2568. // Copy larger data types from a reference
  2569. bc->InstrWORD(asBC_COPY, (asWORD)lvalue->dataType.GetSizeInMemoryDWords());
  2570. }
  2571. }
  2572. else
  2573. {
  2574. // TODO: The object handle can be stored in a variable as well
  2575. if( !lvalue->dataType.IsReference() )
  2576. {
  2577. Error(TXT_NOT_VALID_REFERENCE, node);
  2578. return;
  2579. }
  2580. // TODO: Convert to register based
  2581. bc->InstrPTR(asBC_REFCPY, lvalue->dataType.GetObjectType());
  2582. // Mark variable as initialized
  2583. if( variables )
  2584. {
  2585. sVariable *v = variables->GetVariableByOffset(lvalue->stackOffset);
  2586. if( v ) v->isInitialized = true;
  2587. }
  2588. }
  2589. }
  2590. bool asCCompiler::CompileRefCast(asSExprContext *ctx, const asCDataType &to, bool isExplicit, asCScriptNode *node, bool generateCode)
  2591. {
  2592. bool conversionDone = false;
  2593. asCArray<int> ops;
  2594. asUINT n;
  2595. if( ctx->type.dataType.GetObjectType()->flags & asOBJ_SCRIPT_OBJECT )
  2596. {
  2597. // We need it to be a reference
  2598. if( !ctx->type.dataType.IsReference() )
  2599. {
  2600. asCDataType to = ctx->type.dataType;
  2601. to.MakeReference(true);
  2602. ImplicitConversion(ctx, to, 0, isExplicit ? asIC_EXPLICIT_REF_CAST : asIC_IMPLICIT_CONV, generateCode);
  2603. }
  2604. if( isExplicit )
  2605. {
  2606. // Allow dynamic cast between object handles (only for script objects).
  2607. // At run time this may result in a null handle,
  2608. // which when used will throw an exception
  2609. conversionDone = true;
  2610. if( generateCode )
  2611. {
  2612. ctx->bc.InstrDWORD(asBC_Cast, engine->GetTypeIdFromDataType(to));
  2613. // Allocate a temporary variable for the returned object
  2614. int returnOffset = AllocateVariable(to, true);
  2615. // Move the pointer from the object register to the temporary variable
  2616. ctx->bc.InstrSHORT(asBC_STOREOBJ, (short)returnOffset);
  2617. ctx->bc.InstrSHORT(asBC_PSF, (short)returnOffset);
  2618. ReleaseTemporaryVariable(ctx->type, &ctx->bc);
  2619. ctx->type.SetVariable(to, returnOffset, true);
  2620. ctx->type.dataType.MakeReference(true);
  2621. }
  2622. else
  2623. {
  2624. ctx->type.dataType = to;
  2625. ctx->type.dataType.MakeReference(true);
  2626. }
  2627. }
  2628. else
  2629. {
  2630. if( ctx->type.dataType.GetObjectType()->DerivesFrom(to.GetObjectType()) )
  2631. {
  2632. conversionDone = true;
  2633. ctx->type.dataType.SetObjectType(to.GetObjectType());
  2634. }
  2635. }
  2636. }
  2637. else
  2638. {
  2639. // Find a suitable registered behaviour
  2640. asSTypeBehaviour *beh = &ctx->type.dataType.GetObjectType()->beh;
  2641. for( n = 0; n < beh->operators.GetLength(); n+= 2 )
  2642. {
  2643. if( (isExplicit && asBEHAVE_REF_CAST == beh->operators[n]) ||
  2644. asBEHAVE_IMPLICIT_REF_CAST == beh->operators[n] )
  2645. {
  2646. int funcId = beh->operators[n+1];
  2647. // Is the operator for the output type?
  2648. asCScriptFunction *func = engine->scriptFunctions[funcId];
  2649. if( func->returnType.GetObjectType() != to.GetObjectType() )
  2650. continue;
  2651. ops.PushLast(funcId);
  2652. }
  2653. }
  2654. // Should only have one behaviour for each output type
  2655. if( ops.GetLength() == 1 )
  2656. {
  2657. if( generateCode )
  2658. {
  2659. // Merge the bytecode so that it forms obj.castBehave()
  2660. asCTypeInfo objType = ctx->type;
  2661. asCArray<asSExprContext *> args;
  2662. MakeFunctionCall(ctx, ops[0], objType.dataType.GetObjectType(), args, node);
  2663. // Since we're receiving a handle, we can release the original variable
  2664. ReleaseTemporaryVariable(objType, &ctx->bc);
  2665. }
  2666. else
  2667. {
  2668. asCScriptFunction *func = engine->scriptFunctions[ops[0]];
  2669. ctx->type.Set(func->returnType);
  2670. }
  2671. }
  2672. else if( ops.GetLength() > 1 )
  2673. {
  2674. // It shouldn't be possible to have more than one, should it?
  2675. asASSERT( false );
  2676. }
  2677. }
  2678. return conversionDone;
  2679. }
  2680. // TODO: Re-think the implementation for implicit conversions
  2681. // It's currently inefficient and may at times generate unneeded copies of objects
  2682. // There are also too many different code paths to test, each working slightly differently
  2683. //
  2684. // Reference and handle-of should be treated last
  2685. //
  2686. // - The following conversion categories needs to be implemented in separate functions
  2687. // - primitive to primitive
  2688. // - primitive to value type
  2689. // - primitive to reference type
  2690. // - value type to value type
  2691. // - value type to primitive
  2692. // - value type to reference type
  2693. // - reference type to reference type
  2694. // - reference type to primitive
  2695. // - reference type to value type
  2696. //
  2697. // Explicit conversion and implicit conversion should use the same functions, only with a flag to enable/disable conversions
  2698. //
  2699. // If the conversion fails, the type in the asSExprContext must not be modified. This
  2700. // causes problems where the conversion is partially done and the compiler continues with
  2701. // another option.
  2702. void asCCompiler::ImplicitConvPrimitiveToPrimitive(asSExprContext *ctx, const asCDataType &to, asCScriptNode *node, EImplicitConv convType, bool generateCode, asCArray<int> *reservedVars)
  2703. {
  2704. // Start by implicitly converting constant values
  2705. if( ctx->type.isConstant )
  2706. ImplicitConversionConstant(ctx, to, node, convType);
  2707. if( to == ctx->type.dataType )
  2708. return;
  2709. // After the constant value has been converted we have the following possibilities
  2710. // Allow implicit conversion between numbers
  2711. if( generateCode )
  2712. {
  2713. // Convert smaller types to 32bit first
  2714. int s = ctx->type.dataType.GetSizeInMemoryBytes();
  2715. if( s < 4 )
  2716. {
  2717. ConvertToTempVariableNotIn(ctx, reservedVars);
  2718. if( ctx->type.dataType.IsIntegerType() )
  2719. {
  2720. if( s == 1 )
  2721. ctx->bc.InstrSHORT(asBC_sbTOi, ctx->type.stackOffset);
  2722. else if( s == 2 )
  2723. ctx->bc.InstrSHORT(asBC_swTOi, ctx->type.stackOffset);
  2724. ctx->type.dataType.SetTokenType(ttInt);
  2725. }
  2726. else if( ctx->type.dataType.IsUnsignedType() )
  2727. {
  2728. if( s == 1 )
  2729. ctx->bc.InstrSHORT(asBC_ubTOi, ctx->type.stackOffset);
  2730. else if( s == 2 )
  2731. ctx->bc.InstrSHORT(asBC_uwTOi, ctx->type.stackOffset);
  2732. ctx->type.dataType.SetTokenType(ttUInt);
  2733. }
  2734. }
  2735. if( (to.IsIntegerType() && to.GetSizeInMemoryDWords() == 1) ||
  2736. (to.IsEnumType() && convType == asIC_EXPLICIT_VAL_CAST) )
  2737. {
  2738. if( ctx->type.dataType.IsIntegerType() ||
  2739. ctx->type.dataType.IsUnsignedType() ||
  2740. ctx->type.dataType.IsEnumType() )
  2741. {
  2742. if( ctx->type.dataType.GetSizeInMemoryDWords() == 1 )
  2743. {
  2744. ctx->type.dataType.SetTokenType(to.GetTokenType());
  2745. ctx->type.dataType.SetObjectType(to.GetObjectType());
  2746. }
  2747. else
  2748. {
  2749. ConvertToTempVariableNotIn(ctx, reservedVars);
  2750. ReleaseTemporaryVariable(ctx->type, &ctx->bc);
  2751. int offset = AllocateVariableNotIn(to, true, reservedVars);
  2752. ctx->bc.InstrW_W(asBC_i64TOi, offset, ctx->type.stackOffset);
  2753. ctx->type.SetVariable(to, offset, true);
  2754. }
  2755. }
  2756. else if( ctx->type.dataType.IsFloatType() )
  2757. {
  2758. ConvertToTempVariableNotIn(ctx, reservedVars);
  2759. ctx->bc.InstrSHORT(asBC_fTOi, ctx->type.stackOffset);
  2760. ctx->type.dataType.SetTokenType(to.GetTokenType());
  2761. ctx->type.dataType.SetObjectType(to.GetObjectType());
  2762. }
  2763. else if( ctx->type.dataType.IsDoubleType() )
  2764. {
  2765. ConvertToTempVariableNotIn(ctx, reservedVars);
  2766. ReleaseTemporaryVariable(ctx->type, &ctx->bc);
  2767. int offset = AllocateVariableNotIn(to, true, reservedVars);
  2768. ctx->bc.InstrW_W(asBC_dTOi, offset, ctx->type.stackOffset);
  2769. ctx->type.SetVariable(to, offset, true);
  2770. }
  2771. // Convert to smaller integer if necessary
  2772. int s = to.GetSizeInMemoryBytes();
  2773. if( s < 4 )
  2774. {
  2775. ConvertToTempVariableNotIn(ctx, reservedVars);
  2776. if( s == 1 )
  2777. ctx->bc.InstrSHORT(asBC_iTOb, ctx->type.stackOffset);
  2778. else if( s == 2 )
  2779. ctx->bc.InstrSHORT(asBC_iTOw, ctx->type.stackOffset);
  2780. }
  2781. }
  2782. if( to.IsIntegerType() && to.GetSizeInMemoryDWords() == 2 )
  2783. {
  2784. if( ctx->type.dataType.IsIntegerType() ||
  2785. ctx->type.dataType.IsUnsignedType() ||
  2786. ctx->type.dataType.IsEnumType() )
  2787. {
  2788. if( ctx->type.dataType.GetSizeInMemoryDWords() == 2 )
  2789. {
  2790. ctx->type.dataType.SetTokenType(to.GetTokenType());
  2791. ctx->type.dataType.SetObjectType(to.GetObjectType());
  2792. }
  2793. else
  2794. {
  2795. ConvertToTempVariableNotIn(ctx, reservedVars);
  2796. ReleaseTemporaryVariable(ctx->type, &ctx->bc);
  2797. int offset = AllocateVariableNotIn(to, true, reservedVars);
  2798. if( ctx->type.dataType.IsUnsignedType() )
  2799. ctx->bc.InstrW_W(asBC_uTOi64, offset, ctx->type.stackOffset);
  2800. else
  2801. ctx->bc.InstrW_W(asBC_iTOi64, offset, ctx->type.stackOffset);
  2802. ctx->type.SetVariable(to, offset, true);
  2803. }
  2804. }
  2805. else if( ctx->type.dataType.IsFloatType() )
  2806. {
  2807. ConvertToTempVariableNotIn(ctx, reservedVars);
  2808. ReleaseTemporaryVariable(ctx->type, &ctx->bc);
  2809. int offset = AllocateVariableNotIn(to, true, reservedVars);
  2810. ctx->bc.InstrW_W(asBC_fTOi64, offset, ctx->type.stackOffset);
  2811. ctx->type.SetVariable(to, offset, true);
  2812. }
  2813. else if( ctx->type.dataType.IsDoubleType() )
  2814. {
  2815. ConvertToTempVariableNotIn(ctx, reservedVars);
  2816. ctx->bc.InstrSHORT(asBC_dTOi64, ctx->type.stackOffset);
  2817. ctx->type.dataType.SetTokenType(to.GetTokenType());
  2818. ctx->type.dataType.SetObjectType(to.GetObjectType());
  2819. }
  2820. }
  2821. else if( to.IsUnsignedType() && to.GetSizeInMemoryDWords() == 1 )
  2822. {
  2823. if( ctx->type.dataType.IsIntegerType() ||
  2824. ctx->type.dataType.IsUnsignedType() ||
  2825. ctx->type.dataType.IsEnumType() )
  2826. {
  2827. if( ctx->type.dataType.GetSizeInMemoryDWords() == 1 )
  2828. {
  2829. ctx->type.dataType.SetTokenType(to.GetTokenType());
  2830. ctx->type.dataType.SetObjectType(to.GetObjectType());
  2831. }
  2832. else
  2833. {
  2834. ConvertToTempVariableNotIn(ctx, reservedVars);
  2835. ReleaseTemporaryVariable(ctx->type, &ctx->bc);
  2836. int offset = AllocateVariableNotIn(to, true, reservedVars);
  2837. ctx->bc.InstrW_W(asBC_i64TOi, offset, ctx->type.stackOffset);
  2838. ctx->type.SetVariable(to, offset, true);
  2839. }
  2840. }
  2841. else if( ctx->type.dataType.IsFloatType() )
  2842. {
  2843. ConvertToTempVariableNotIn(ctx, reservedVars);
  2844. ctx->bc.InstrSHORT(asBC_fTOu, ctx->type.stackOffset);
  2845. ctx->type.dataType.SetTokenType(to.GetTokenType());
  2846. ctx->type.dataType.SetObjectType(to.GetObjectType());
  2847. }
  2848. else if( ctx->type.dataType.IsDoubleType() )
  2849. {
  2850. ConvertToTempVariableNotIn(ctx, reservedVars);
  2851. ReleaseTemporaryVariable(ctx->type, &ctx->bc);
  2852. int offset = AllocateVariableNotIn(to, true, reservedVars);
  2853. ctx->bc.InstrW_W(asBC_dTOu, offset, ctx->type.stackOffset);
  2854. ctx->type.SetVariable(to, offset, true);
  2855. }
  2856. // Convert to smaller integer if necessary
  2857. int s = to.GetSizeInMemoryBytes();
  2858. if( s < 4 )
  2859. {
  2860. ConvertToTempVariableNotIn(ctx, reservedVars);
  2861. if( s == 1 )
  2862. ctx->bc.InstrSHORT(asBC_iTOb, ctx->type.stackOffset);
  2863. else if( s == 2 )
  2864. ctx->bc.InstrSHORT(asBC_iTOw, ctx->type.stackOffset);
  2865. }
  2866. }
  2867. if( to.IsUnsignedType() && to.GetSizeInMemoryDWords() == 2 )
  2868. {
  2869. if( ctx->type.dataType.IsIntegerType() ||
  2870. ctx->type.dataType.IsUnsignedType() ||
  2871. ctx->type.dataType.IsEnumType() )
  2872. {
  2873. if( ctx->type.dataType.GetSizeInMemoryDWords() == 2 )
  2874. {
  2875. ctx->type.dataType.SetTokenType(to.GetTokenType());
  2876. ctx->type.dataType.SetObjectType(to.GetObjectType());
  2877. }
  2878. else
  2879. {
  2880. ConvertToTempVariableNotIn(ctx, reservedVars);
  2881. ReleaseTemporaryVariable(ctx->type, &ctx->bc);
  2882. int offset = AllocateVariableNotIn(to, true, reservedVars);
  2883. if( ctx->type.dataType.IsUnsignedType() )
  2884. ctx->bc.InstrW_W(asBC_uTOi64, offset, ctx->type.stackOffset);
  2885. else
  2886. ctx->bc.InstrW_W(asBC_iTOi64, offset, ctx->type.stackOffset);
  2887. ctx->type.SetVariable(to, offset, true);
  2888. }
  2889. }
  2890. else if( ctx->type.dataType.IsFloatType() )
  2891. {
  2892. ConvertToTempVariableNotIn(ctx, reservedVars);
  2893. ReleaseTemporaryVariable(ctx->type, &ctx->bc);
  2894. int offset = AllocateVariableNotIn(to, true, reservedVars);
  2895. ctx->bc.InstrW_W(asBC_fTOu64, offset, ctx->type.stackOffset);
  2896. ctx->type.SetVariable(to, offset, true);
  2897. }
  2898. else if( ctx->type.dataType.IsDoubleType() )
  2899. {
  2900. ConvertToTempVariableNotIn(ctx, reservedVars);
  2901. ctx->bc.InstrSHORT(asBC_dTOu64, ctx->type.stackOffset);
  2902. ctx->type.dataType.SetTokenType(to.GetTokenType());
  2903. ctx->type.dataType.SetObjectType(to.GetObjectType());
  2904. }
  2905. }
  2906. else if( to.IsFloatType() )
  2907. {
  2908. if( (ctx->type.dataType.IsIntegerType() || ctx->type.dataType.IsEnumType()) && ctx->type.dataType.GetSizeInMemoryDWords() == 1 )
  2909. {
  2910. ConvertToTempVariableNotIn(ctx, reservedVars);
  2911. ctx->bc.InstrSHORT(asBC_iTOf, ctx->type.stackOffset);
  2912. ctx->type.dataType.SetTokenType(to.GetTokenType());
  2913. ctx->type.dataType.SetObjectType(to.GetObjectType());
  2914. }
  2915. else if( ctx->type.dataType.IsIntegerType() && ctx->type.dataType.GetSizeInMemoryDWords() == 2 )
  2916. {
  2917. ConvertToTempVariableNotIn(ctx, reservedVars);
  2918. ReleaseTemporaryVariable(ctx->type, &ctx->bc);
  2919. int offset = AllocateVariableNotIn(to, true, reservedVars);
  2920. ctx->bc.InstrW_W(asBC_i64TOf, offset, ctx->type.stackOffset);
  2921. ctx->type.SetVariable(to, offset, true);
  2922. }
  2923. else if( ctx->type.dataType.IsUnsignedType() && ctx->type.dataType.GetSizeInMemoryDWords() == 1 )
  2924. {
  2925. ConvertToTempVariableNotIn(ctx, reservedVars);
  2926. ctx->bc.InstrSHORT(asBC_uTOf, ctx->type.stackOffset);
  2927. ctx->type.dataType.SetTokenType(to.GetTokenType());
  2928. ctx->type.dataType.SetObjectType(to.GetObjectType());
  2929. }
  2930. else if( ctx->type.dataType.IsUnsignedType() && ctx->type.dataType.GetSizeInMemoryDWords() == 2 )
  2931. {
  2932. ConvertToTempVariableNotIn(ctx, reservedVars);
  2933. ReleaseTemporaryVariable(ctx->type, &ctx->bc);
  2934. int offset = AllocateVariableNotIn(to, true, reservedVars);
  2935. ctx->bc.InstrW_W(asBC_u64TOf, offset, ctx->type.stackOffset);
  2936. ctx->type.SetVariable(to, offset, true);
  2937. }
  2938. else if( ctx->type.dataType.IsDoubleType() )
  2939. {
  2940. ConvertToTempVariableNotIn(ctx, reservedVars);
  2941. ReleaseTemporaryVariable(ctx->type, &ctx->bc);
  2942. int offset = AllocateVariableNotIn(to, true, reservedVars);
  2943. ctx->bc.InstrW_W(asBC_dTOf, offset, ctx->type.stackOffset);
  2944. ctx->type.SetVariable(to, offset, true);
  2945. }
  2946. }
  2947. else if( to.IsDoubleType() )
  2948. {
  2949. if( (ctx->type.dataType.IsIntegerType() || ctx->type.dataType.IsEnumType()) && ctx->type.dataType.GetSizeInMemoryDWords() == 1 )
  2950. {
  2951. ConvertToTempVariableNotIn(ctx, reservedVars);
  2952. ReleaseTemporaryVariable(ctx->type, &ctx->bc);
  2953. int offset = AllocateVariableNotIn(to, true, reservedVars);
  2954. ctx->bc.InstrW_W(asBC_iTOd, offset, ctx->type.stackOffset);
  2955. ctx->type.SetVariable(to, offset, true);
  2956. }
  2957. else if( ctx->type.dataType.IsIntegerType() && ctx->type.dataType.GetSizeInMemoryDWords() == 2 )
  2958. {
  2959. ConvertToTempVariableNotIn(ctx, reservedVars);
  2960. ctx->bc.InstrSHORT(asBC_i64TOd, ctx->type.stackOffset);
  2961. ctx->type.dataType.SetTokenType(to.GetTokenType());
  2962. ctx->type.dataType.SetObjectType(to.GetObjectType());
  2963. }
  2964. else if( ctx->type.dataType.IsUnsignedType() && ctx->type.dataType.GetSizeInMemoryDWords() == 1 )
  2965. {
  2966. ConvertToTempVariableNotIn(ctx, reservedVars);
  2967. ReleaseTemporaryVariable(ctx->type, &ctx->bc);
  2968. int offset = AllocateVariableNotIn(to, true, reservedVars);
  2969. ctx->bc.InstrW_W(asBC_uTOd, offset, ctx->type.stackOffset);
  2970. ctx->type.SetVariable(to, offset, true);
  2971. }
  2972. else if( ctx->type.dataType.IsUnsignedType() && ctx->type.dataType.GetSizeInMemoryDWords() == 2 )
  2973. {
  2974. ConvertToTempVariableNotIn(ctx, reservedVars);
  2975. ctx->bc.InstrSHORT(asBC_u64TOd, ctx->type.stackOffset);
  2976. ctx->type.dataType.SetTokenType(to.GetTokenType());
  2977. ctx->type.dataType.SetObjectType(to.GetObjectType());
  2978. }
  2979. else if( ctx->type.dataType.IsFloatType() )
  2980. {
  2981. ConvertToTempVariableNotIn(ctx, reservedVars);
  2982. ReleaseTemporaryVariable(ctx->type, &ctx->bc);
  2983. int offset = AllocateVariableNotIn(to, true, reservedVars);
  2984. ctx->bc.InstrW_W(asBC_fTOd, offset, ctx->type.stackOffset);
  2985. ctx->type.SetVariable(to, offset, true);
  2986. }
  2987. }
  2988. }
  2989. else
  2990. {
  2991. if( (to.IsIntegerType() || to.IsUnsignedType() ||
  2992. to.IsFloatType() || to.IsDoubleType() ||
  2993. (to.IsEnumType() && convType == asIC_EXPLICIT_VAL_CAST)) &&
  2994. (ctx->type.dataType.IsIntegerType() || ctx->type.dataType.IsUnsignedType() ||
  2995. ctx->type.dataType.IsFloatType() || ctx->type.dataType.IsDoubleType() ||
  2996. ctx->type.dataType.IsEnumType()) )
  2997. {
  2998. ctx->type.dataType.SetTokenType(to.GetTokenType());
  2999. ctx->type.dataType.SetObjectType(to.GetObjectType());
  3000. }
  3001. }
  3002. // Primitive types on the stack, can be const or non-const
  3003. ctx->type.dataType.MakeReadOnly(to.IsReadOnly());
  3004. }
  3005. void asCCompiler::ImplicitConversion(asSExprContext *ctx, const asCDataType &to, asCScriptNode *node, EImplicitConv convType, bool generateCode, asCArray<int> *reservedVars, bool allowObjectConstruct)
  3006. {
  3007. // No conversion from void to any other type
  3008. if( ctx->type.dataType.GetTokenType() == ttVoid )
  3009. return;
  3010. // Do we want a var type?
  3011. if( to.GetTokenType() == ttQuestion )
  3012. {
  3013. // Any type can be converted to a var type, but only when not generating code
  3014. asASSERT( !generateCode );
  3015. ctx->type.dataType = to;
  3016. return;
  3017. }
  3018. // Do we want a primitive?
  3019. else if( to.IsPrimitive() )
  3020. {
  3021. if( !ctx->type.dataType.IsPrimitive() )
  3022. ImplicitConvObjectToPrimitive(ctx, to, node, convType, generateCode, reservedVars);
  3023. else
  3024. ImplicitConvPrimitiveToPrimitive(ctx, to, node, convType, generateCode, reservedVars);
  3025. }
  3026. else // The target is a complex type
  3027. {
  3028. if( ctx->type.dataType.IsPrimitive() )
  3029. ImplicitConvPrimitiveToObject(ctx, to, node, convType, generateCode, reservedVars, allowObjectConstruct);
  3030. else
  3031. ImplicitConvObjectToObject(ctx, to, node, convType, generateCode, reservedVars, allowObjectConstruct);
  3032. }
  3033. }
  3034. void asCCompiler::ImplicitConvObjectToPrimitive(asSExprContext *ctx, const asCDataType &to, asCScriptNode *node, EImplicitConv convType, bool generateCode, asCArray<int> *reservedVars)
  3035. {
  3036. if( ctx->type.isExplicitHandle )
  3037. {
  3038. // An explicit handle cannot be converted to a primitive
  3039. if( convType != asIC_IMPLICIT_CONV && node )
  3040. {
  3041. asCString str;
  3042. str.Format(TXT_CANT_IMPLICITLY_CONVERT_s_TO_s, ctx->type.dataType.Format().AddressOf(), to.Format().AddressOf());
  3043. Error(str.AddressOf(), node);
  3044. }
  3045. return;
  3046. }
  3047. // TODO: Must use the const cast behaviour if the object is read-only
  3048. // Find matching value cast behaviours
  3049. // Here we're only interested in those that convert the type to a primitive type
  3050. asCArray<int> funcs;
  3051. asSTypeBehaviour *beh = ctx->type.dataType.GetBehaviour();
  3052. if( beh )
  3053. {
  3054. if( convType == asIC_EXPLICIT_VAL_CAST )
  3055. {
  3056. for( unsigned int n = 0; n < beh->operators.GetLength(); n += 2 )
  3057. {
  3058. // accept both implicit and explicit cast
  3059. if( (beh->operators[n] == asBEHAVE_VALUE_CAST ||
  3060. beh->operators[n] == asBEHAVE_IMPLICIT_VALUE_CAST) &&
  3061. builder->GetFunctionDescription(beh->operators[n+1])->returnType.IsPrimitive() )
  3062. funcs.PushLast(beh->operators[n+1]);
  3063. }
  3064. }
  3065. else
  3066. {
  3067. for( unsigned int n = 0; n < beh->operators.GetLength(); n += 2 )
  3068. {
  3069. // accept only implicit cast
  3070. if( beh->operators[n] == asBEHAVE_IMPLICIT_VALUE_CAST &&
  3071. builder->GetFunctionDescription(beh->operators[n+1])->returnType.IsPrimitive() )
  3072. funcs.PushLast(beh->operators[n+1]);
  3073. }
  3074. }
  3075. }
  3076. // This matrix describes the priorities of the types to search for, for each target type
  3077. // The first column is the target type, the priorities goes from left to right
  3078. eTokenType matchMtx[10][10] =
  3079. {
  3080. {ttDouble, ttFloat, ttInt64, ttUInt64, ttInt, ttUInt, ttInt16, ttUInt16, ttInt8, ttUInt8},
  3081. {ttFloat, ttDouble, ttInt64, ttUInt64, ttInt, ttUInt, ttInt16, ttUInt16, ttInt8, ttUInt8},
  3082. {ttInt64, ttUInt64, ttInt, ttUInt, ttInt16, ttUInt16, ttInt8, ttUInt8, ttDouble, ttFloat},
  3083. {ttUInt64, ttInt64, ttUInt, ttInt, ttUInt16, ttInt16, ttUInt8, ttInt8, ttDouble, ttFloat},
  3084. {ttInt, ttUInt, ttInt64, ttUInt64, ttInt16, ttUInt16, ttInt8, ttUInt8, ttDouble, ttFloat},
  3085. {ttUInt, ttInt, ttUInt64, ttInt64, ttUInt16, ttInt16, ttUInt8, ttInt8, ttDouble, ttFloat},
  3086. {ttInt16, ttUInt16, ttInt, ttUInt, ttInt64, ttUInt64, ttInt8, ttUInt8, ttDouble, ttFloat},
  3087. {ttUInt16, ttInt16, ttUInt, ttInt, ttUInt64, ttInt64, ttUInt8, ttInt8, ttDouble, ttFloat},
  3088. {ttInt8, ttUInt8, ttInt16, ttUInt16, ttInt, ttUInt, ttInt64, ttUInt64, ttDouble, ttFloat},
  3089. {ttUInt8, ttInt8, ttUInt16, ttInt16, ttUInt, ttInt, ttUInt64, ttInt64, ttDouble, ttFloat},
  3090. };
  3091. // Which row to use?
  3092. eTokenType *row = 0;
  3093. for( unsigned int type = 0; type < 10; type++ )
  3094. {
  3095. if( to.GetTokenType() == matchMtx[type][0] )
  3096. {
  3097. row = &matchMtx[type][0];
  3098. break;
  3099. }
  3100. }
  3101. // Find the best matching cast operator
  3102. int funcId = 0;
  3103. if( row )
  3104. {
  3105. asCDataType target(to);
  3106. // Priority goes from left to right in the matrix
  3107. for( unsigned int attempt = 0; attempt < 10 && funcId == 0; attempt++ )
  3108. {
  3109. target.SetTokenType(row[attempt]);
  3110. for( unsigned int n = 0; n < funcs.GetLength(); n++ )
  3111. {
  3112. asCScriptFunction *descr = builder->GetFunctionDescription(funcs[n]);
  3113. if( descr->returnType.IsEqualExceptConst(target) )
  3114. {
  3115. funcId = funcs[n];
  3116. break;
  3117. }
  3118. }
  3119. }
  3120. }
  3121. // Did we find a suitable function?
  3122. if( funcId != 0 )
  3123. {
  3124. asCScriptFunction *descr = builder->GetFunctionDescription(funcId);
  3125. if( generateCode )
  3126. {
  3127. asCTypeInfo objType = ctx->type;
  3128. Dereference(ctx, true);
  3129. PerformFunctionCall(funcId, ctx);
  3130. ReleaseTemporaryVariable(objType, &ctx->bc);
  3131. }
  3132. else
  3133. ctx->type.Set(descr->returnType);
  3134. // Allow one more implicit conversion to another primitive type
  3135. ImplicitConversion(ctx, to, node, convType, generateCode, reservedVars, false);
  3136. }
  3137. else
  3138. {
  3139. if( convType != asIC_IMPLICIT_CONV && node )
  3140. {
  3141. asCString str;
  3142. str.Format(TXT_CANT_IMPLICITLY_CONVERT_s_TO_s, ctx->type.dataType.Format().AddressOf(), to.Format().AddressOf());
  3143. Error(str.AddressOf(), node);
  3144. }
  3145. }
  3146. }
  3147. void asCCompiler::ImplicitConvObjectToObject(asSExprContext *ctx, const asCDataType &to, asCScriptNode *node, EImplicitConv convType, bool generateCode, asCArray<int> *reservedVars, bool allowObjectConstruct)
  3148. {
  3149. // Convert null to any object type handle, but not to a non-handle type
  3150. if( ctx->type.IsNullConstant() )
  3151. {
  3152. if( to.IsObjectHandle() )
  3153. ctx->type.dataType = to;
  3154. return;
  3155. }
  3156. // First attempt to convert the base type without instanciating another instance
  3157. if( to.GetObjectType() != ctx->type.dataType.GetObjectType() )
  3158. {
  3159. // If the to type is an interface and the from type implements it, then we can convert it immediately
  3160. if( ctx->type.dataType.GetObjectType()->Implements(to.GetObjectType()) )
  3161. {
  3162. ctx->type.dataType.SetObjectType(to.GetObjectType());
  3163. }
  3164. // If the to type is a class and the from type derives from it, then we can convert it immediately
  3165. if( ctx->type.dataType.GetObjectType()->DerivesFrom(to.GetObjectType()) )
  3166. {
  3167. ctx->type.dataType.SetObjectType(to.GetObjectType());
  3168. }
  3169. // If the types are not equal yet, then we may still be able to find a reference cast
  3170. if( ctx->type.dataType.GetObjectType() != to.GetObjectType() )
  3171. {
  3172. // A ref cast must not remove the constness
  3173. bool isConst = false;
  3174. if( (ctx->type.dataType.IsObjectHandle() && ctx->type.dataType.IsHandleToConst()) ||
  3175. (!ctx->type.dataType.IsObjectHandle() && ctx->type.dataType.IsReadOnly()) )
  3176. isConst = true;
  3177. // We may still be able to find an implicit ref cast behaviour
  3178. CompileRefCast(ctx, to, convType == asIC_EXPLICIT_REF_CAST, node, generateCode);
  3179. ctx->type.dataType.MakeHandleToConst(isConst);
  3180. }
  3181. }
  3182. // If the base type is still different, and we are allowed to instance
  3183. // another object then we can try an implicit value cast
  3184. if( to.GetObjectType() != ctx->type.dataType.GetObjectType() && allowObjectConstruct )
  3185. {
  3186. // TODO: Implement support for implicit constructor/factory
  3187. asCArray<int> funcs;
  3188. asSTypeBehaviour *beh = ctx->type.dataType.GetBehaviour();
  3189. if( beh )
  3190. {
  3191. if( convType == asIC_EXPLICIT_VAL_CAST )
  3192. {
  3193. for( unsigned int n = 0; n < beh->operators.GetLength(); n += 2 )
  3194. {
  3195. // accept both implicit and explicit cast
  3196. if( (beh->operators[n] == asBEHAVE_VALUE_CAST ||
  3197. beh->operators[n] == asBEHAVE_IMPLICIT_VALUE_CAST) &&
  3198. builder->GetFunctionDescription(beh->operators[n+1])->returnType.GetObjectType() == to.GetObjectType() )
  3199. funcs.PushLast(beh->operators[n+1]);
  3200. }
  3201. }
  3202. else
  3203. {
  3204. for( unsigned int n = 0; n < beh->operators.GetLength(); n += 2 )
  3205. {
  3206. // accept only implicit cast
  3207. if( beh->operators[n] == asBEHAVE_IMPLICIT_VALUE_CAST &&
  3208. builder->GetFunctionDescription(beh->operators[n+1])->returnType.GetObjectType() == to.GetObjectType() )
  3209. funcs.PushLast(beh->operators[n+1]);
  3210. }
  3211. }
  3212. }
  3213. // TODO: If there are multiple valid value casts, then we must choose the most appropriate one
  3214. asASSERT( funcs.GetLength() <= 1 );
  3215. if( funcs.GetLength() == 1 )
  3216. {
  3217. asCScriptFunction *f = builder->GetFunctionDescription(funcs[0]);
  3218. if( generateCode )
  3219. {
  3220. asCTypeInfo objType = ctx->type;
  3221. Dereference(ctx, true);
  3222. PerformFunctionCall(funcs[0], ctx);
  3223. ReleaseTemporaryVariable(objType, &ctx->bc);
  3224. }
  3225. else
  3226. ctx->type.Set(f->returnType);
  3227. }
  3228. }
  3229. // If we still haven't converted the base type to the correct type, then there is no need to continue
  3230. if( to.GetObjectType() != ctx->type.dataType.GetObjectType() )
  3231. return;
  3232. // TODO: The below code can probably be improved even further. It should first convert the type to
  3233. // object handle or non-object handle, and only after that convert to reference or non-reference
  3234. if( to.IsObjectHandle() )
  3235. {
  3236. // An object type can be directly converted to a handle of the same type
  3237. if( ctx->type.dataType.SupportHandles() )
  3238. {
  3239. ctx->type.dataType.MakeHandle(true);
  3240. }
  3241. if( ctx->type.dataType.IsObjectHandle() )
  3242. ctx->type.dataType.MakeReadOnly(to.IsReadOnly());
  3243. if( to.IsHandleToConst() && ctx->type.dataType.IsObjectHandle() )
  3244. ctx->type.dataType.MakeHandleToConst(true);
  3245. }
  3246. if( !to.IsReference() )
  3247. {
  3248. if( ctx->type.dataType.IsReference() )
  3249. {
  3250. Dereference(ctx, generateCode);
  3251. // TODO: Can't this leave unhandled deferred output params?
  3252. }
  3253. if( to.IsObjectHandle() )
  3254. {
  3255. // TODO: If the type is handle, then we can't use IsReadOnly to determine the constness of the basetype
  3256. // If the rvalue is a handle to a const object, then
  3257. // the lvalue must also be a handle to a const object
  3258. if( ctx->type.dataType.IsReadOnly() && !to.IsReadOnly() )
  3259. {
  3260. if( convType != asIC_IMPLICIT_CONV )
  3261. {
  3262. asASSERT(node);
  3263. asCString str;
  3264. str.Format(TXT_CANT_IMPLICITLY_CONVERT_s_TO_s, ctx->type.dataType.Format().AddressOf(), to.Format().AddressOf());
  3265. Error(str.AddressOf(), node);
  3266. }
  3267. }
  3268. }
  3269. else
  3270. {
  3271. if( ctx->type.dataType.IsObjectHandle() && !ctx->type.isExplicitHandle )
  3272. {
  3273. if( generateCode )
  3274. ctx->bc.Instr(asBC_CHKREF);
  3275. ctx->type.dataType.MakeHandle(false);
  3276. }
  3277. // A const object can be converted to a non-const object through a copy
  3278. if( ctx->type.dataType.IsReadOnly() && !to.IsReadOnly() &&
  3279. allowObjectConstruct )
  3280. {
  3281. // Does the object type allow a copy to be made?
  3282. if( ctx->type.dataType.CanBeCopied() )
  3283. {
  3284. if( generateCode )
  3285. {
  3286. // Make a temporary object with the copy
  3287. PrepareTemporaryObject(node, ctx, reservedVars);
  3288. }
  3289. else
  3290. ctx->type.dataType.MakeReadOnly(false);
  3291. }
  3292. }
  3293. // A non-const object can be converted to a const object directly
  3294. if( !ctx->type.dataType.IsReadOnly() && to.IsReadOnly() )
  3295. {
  3296. ctx->type.dataType.MakeReadOnly(true);
  3297. }
  3298. }
  3299. }
  3300. else // to.IsReference()
  3301. {
  3302. if( ctx->type.dataType.IsReference() )
  3303. {
  3304. // A reference to a handle can be converted to a reference to an object
  3305. // by first reading the address, then verifying that it is not null, then putting the address back on the stack
  3306. if( !to.IsObjectHandle() && ctx->type.dataType.IsObjectHandle() && !ctx->type.isExplicitHandle )
  3307. {
  3308. ctx->type.dataType.MakeHandle(false);
  3309. if( generateCode )
  3310. ctx->bc.Instr(asBC_ChkRefS);
  3311. }
  3312. // A reference to a non-const can be converted to a reference to a const
  3313. if( to.IsReadOnly() )
  3314. ctx->type.dataType.MakeReadOnly(true);
  3315. else if( ctx->type.dataType.IsReadOnly() )
  3316. {
  3317. // A reference to a const can be converted to a reference to a
  3318. // non-const by copying the object to a temporary variable
  3319. ctx->type.dataType.MakeReadOnly(false);
  3320. if( generateCode )
  3321. {
  3322. // Allocate a temporary variable
  3323. asSExprContext lctx(engine);
  3324. asCDataType dt = ctx->type.dataType;
  3325. dt.MakeReference(false);
  3326. int offset = AllocateVariableNotIn(dt, true, reservedVars);
  3327. lctx.type = ctx->type;
  3328. lctx.type.isTemporary = true;
  3329. lctx.type.stackOffset = (short)offset;
  3330. CallDefaultConstructor(lctx.type.dataType, offset, &lctx.bc, node);
  3331. // Build the right hand expression
  3332. asSExprContext rctx(engine);
  3333. rctx.type = ctx->type;
  3334. rctx.bc.AddCode(&lctx.bc);
  3335. rctx.bc.AddCode(&ctx->bc);
  3336. // Build the left hand expression
  3337. lctx.bc.InstrSHORT(asBC_PSF, (short)offset);
  3338. // DoAssignment doesn't allow assignment to temporary variable,
  3339. // so we temporarily set the type as non-temporary.
  3340. lctx.type.isTemporary = false;
  3341. DoAssignment(ctx, &lctx, &rctx, node, node, ttAssignment, node);
  3342. // If the original const object was a temporary variable, then
  3343. // that needs to be released now
  3344. ProcessDeferredParams(ctx);
  3345. ctx->type = lctx.type;
  3346. ctx->type.isTemporary = true;
  3347. }
  3348. }
  3349. }
  3350. else
  3351. {
  3352. if( generateCode )
  3353. {
  3354. asCTypeInfo type;
  3355. type.Set(ctx->type.dataType);
  3356. // Allocate a temporary variable
  3357. int offset = AllocateVariableNotIn(type.dataType, true, reservedVars);
  3358. type.isTemporary = true;
  3359. type.stackOffset = (short)offset;
  3360. if( type.dataType.IsObjectHandle() )
  3361. type.isExplicitHandle = true;
  3362. CallDefaultConstructor(type.dataType, offset, &ctx->bc, node);
  3363. type.dataType.MakeReference(true);
  3364. PrepareForAssignment(&type.dataType, ctx, node);
  3365. ctx->bc.InstrSHORT(asBC_PSF, type.stackOffset);
  3366. // If the input type is read-only we'll need to temporarily
  3367. // remove this constness, otherwise the assignment will fail
  3368. bool typeIsReadOnly = type.dataType.IsReadOnly();
  3369. type.dataType.MakeReadOnly(false);
  3370. PerformAssignment(&type, &ctx->type, &ctx->bc, node);
  3371. type.dataType.MakeReadOnly(typeIsReadOnly);
  3372. ctx->bc.Pop(ctx->type.dataType.GetSizeOnStackDWords());
  3373. ReleaseTemporaryVariable(ctx->type, &ctx->bc);
  3374. ctx->bc.InstrSHORT(asBC_PSF, type.stackOffset);
  3375. ctx->type = type;
  3376. }
  3377. // A non-reference can be converted to a reference,
  3378. // by putting the value in a temporary variable
  3379. ctx->type.dataType.MakeReference(true);
  3380. // Since it is a new temporary variable it doesn't have to be const
  3381. ctx->type.dataType.MakeReadOnly(to.IsReadOnly());
  3382. }
  3383. }
  3384. }
  3385. void asCCompiler::ImplicitConvPrimitiveToObject(asSExprContext * /*ctx*/, const asCDataType & /*to*/, asCScriptNode * /*node*/, EImplicitConv /*isExplicit*/, bool /*generateCode*/, asCArray<int> * /*reservedVars*/, bool /*allowObjectConstruct*/)
  3386. {
  3387. /*
  3388. if( allowObjectConstruct )
  3389. {
  3390. // Check for the existance of any implicit constructor/factory
  3391. // behaviours to construct the desired object from the primitive
  3392. // TODO: Implement implicit constructor/factory
  3393. asCArray<int> funcs;
  3394. asSTypeBehaviour *beh = to.GetBehaviour();
  3395. if( beh )
  3396. {
  3397. // TODO: Add implicit conversion to object types via contructor/factory
  3398. // Find the implicit constructor calls
  3399. for( int n = 0; n < beh->operators.GetLength(); n += 2 )
  3400. if( beh->operators[n] == asBEHAVE_IMPLICIT_CONSTRUCT ||
  3401. beh->operators[n] == asBEHAVE_IMPLICIT_FACTORY )
  3402. funcs.PushLast(beh->operators[n+1]);
  3403. }
  3404. // Compile the arguments
  3405. asCArray<asSExprContext *> args;
  3406. asCArray<asCTypeInfo> temporaryVariables;
  3407. args.PushLast(ctx);
  3408. MatchFunctions(funcs, args, node, to.GetObjectType()->name.AddressOf(), NULL, false, true, false);
  3409. // Verify that we found 1 matching function
  3410. if( funcs.GetLength() == 1 )
  3411. {
  3412. asCTypeInfo tempObj;
  3413. tempObj.dataType = to;
  3414. tempObj.dataType.MakeReference(true);
  3415. tempObj.isTemporary = true;
  3416. tempObj.isVariable = true;
  3417. if( generateCode )
  3418. {
  3419. tempObj.stackOffset = (short)AllocateVariable(to, true);
  3420. asSExprContext tmp(engine);
  3421. if( tempObj.dataType.GetObjectType()->flags & asOBJ_REF )
  3422. {
  3423. PrepareFunctionCall(funcs[0], &tmp.bc, args);
  3424. MoveArgsToStack(funcs[0], &tmp.bc, args, false);
  3425. // Call factory and store handle in the variable
  3426. PerformFunctionCall(funcs[0], &tmp, false, &args, 0, true, tempObj.stackOffset);
  3427. tmp.type = tempObj;
  3428. }
  3429. else
  3430. {
  3431. // Push the address of the object on the stack
  3432. tmp.bc.InstrSHORT(asBC_VAR, tempObj.stackOffset);
  3433. PrepareFunctionCall(funcs[0], &tmp.bc, args);
  3434. MoveArgsToStack(funcs[0], &tmp.bc, args, false);
  3435. int offset = 0;
  3436. for( asUINT n = 0; n < args.GetLength(); n++ )
  3437. offset += args[n]->type.dataType.GetSizeOnStackDWords();
  3438. tmp.bc.InstrWORD(asBC_GETREF, (asWORD)offset);
  3439. PerformFunctionCall(funcs[0], &tmp, true, &args, tempObj.dataType.GetObjectType());
  3440. // The constructor doesn't return anything,
  3441. // so we have to manually inform the type of
  3442. // the return value
  3443. tmp.type = tempObj;
  3444. // Push the address of the object on the stack again
  3445. tmp.bc.InstrSHORT(asBC_PSF, tempObj.stackOffset);
  3446. }
  3447. // Copy the newly generated code to the input context
  3448. // ctx is already empty, since it was merged as part of argument expression
  3449. asASSERT(ctx->bc.GetLastInstr() == -1);
  3450. MergeExprContexts(ctx, &tmp);
  3451. }
  3452. ctx->type = tempObj;
  3453. }
  3454. }
  3455. */
  3456. }
  3457. void asCCompiler::ImplicitConversionConstant(asSExprContext *from, const asCDataType &to, asCScriptNode *node, EImplicitConv convType)
  3458. {
  3459. asASSERT(from->type.isConstant);
  3460. // TODO: node should be the node of the value that is
  3461. // converted (not the operator that provokes the implicit
  3462. // conversion)
  3463. // If the base type is correct there is no more to do
  3464. if( to.IsEqualExceptRefAndConst(from->type.dataType) ) return;
  3465. // References cannot be constants
  3466. if( from->type.dataType.IsReference() ) return;
  3467. // Arrays can't be constants
  3468. if( to.IsArrayType() ) return;
  3469. if( (to.IsIntegerType() && to.GetSizeInMemoryDWords() == 1) ||
  3470. (to.IsEnumType() && convType == asIC_EXPLICIT_VAL_CAST) )
  3471. {
  3472. if( from->type.dataType.IsFloatType() ||
  3473. from->type.dataType.IsDoubleType() ||
  3474. from->type.dataType.IsUnsignedType() ||
  3475. from->type.dataType.IsIntegerType() ||
  3476. from->type.dataType.IsEnumType() )
  3477. {
  3478. // Transform the value
  3479. // Float constants can be implicitly converted to int
  3480. if( from->type.dataType.IsFloatType() )
  3481. {
  3482. float fc = from->type.floatValue;
  3483. int ic = int(fc);
  3484. if( float(ic) != fc )
  3485. {
  3486. if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_NOT_EXACT, node);
  3487. }
  3488. from->type.intValue = ic;
  3489. }
  3490. // Double constants can be implicitly converted to int
  3491. else if( from->type.dataType.IsDoubleType() )
  3492. {
  3493. double fc = from->type.doubleValue;
  3494. int ic = int(fc);
  3495. if( double(ic) != fc )
  3496. {
  3497. if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_NOT_EXACT, node);
  3498. }
  3499. from->type.intValue = ic;
  3500. }
  3501. else if( from->type.dataType.IsUnsignedType() && from->type.dataType.GetSizeInMemoryDWords() == 1 )
  3502. {
  3503. // Verify that it is possible to convert to signed without getting negative
  3504. if( from->type.intValue < 0 )
  3505. {
  3506. if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_CHANGE_SIGN, node);
  3507. }
  3508. // Convert to 32bit
  3509. if( from->type.dataType.GetSizeInMemoryBytes() == 1 )
  3510. from->type.intValue = from->type.byteValue;
  3511. else if( from->type.dataType.GetSizeInMemoryBytes() == 2 )
  3512. from->type.intValue = from->type.wordValue;
  3513. }
  3514. else if( from->type.dataType.IsUnsignedType() && from->type.dataType.GetSizeInMemoryDWords() == 2 )
  3515. {
  3516. // Convert to 32bit
  3517. from->type.intValue = int(from->type.qwordValue);
  3518. }
  3519. else if( from->type.dataType.IsIntegerType() &&
  3520. from->type.dataType.GetSizeInMemoryBytes() < 4 )
  3521. {
  3522. // Convert to 32bit
  3523. if( from->type.dataType.GetSizeInMemoryBytes() == 1 )
  3524. from->type.intValue = (signed char)from->type.byteValue;
  3525. else if( from->type.dataType.GetSizeInMemoryBytes() == 2 )
  3526. from->type.intValue = (short)from->type.wordValue;
  3527. }
  3528. else if( from->type.dataType.IsEnumType() )
  3529. {
  3530. // Enum type is already an integer type
  3531. }
  3532. // Set the resulting type
  3533. if( to.IsEnumType() )
  3534. from->type.dataType = to;
  3535. else
  3536. from->type.dataType = asCDataType::CreatePrimitive(ttInt, true);
  3537. }
  3538. // Check if a downsize is necessary
  3539. if( to.IsIntegerType() &&
  3540. from->type.dataType.IsIntegerType() &&
  3541. from->type.dataType.GetSizeInMemoryBytes() > to.GetSizeInMemoryBytes() )
  3542. {
  3543. // Verify if it is possible
  3544. if( to.GetSizeInMemoryBytes() == 1 )
  3545. {
  3546. if( char(from->type.intValue) != from->type.intValue )
  3547. if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_VALUE_TOO_LARGE_FOR_TYPE, node);
  3548. from->type.byteValue = char(from->type.intValue);
  3549. }
  3550. else if( to.GetSizeInMemoryBytes() == 2 )
  3551. {
  3552. if( short(from->type.intValue) != from->type.intValue )
  3553. if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_VALUE_TOO_LARGE_FOR_TYPE, node);
  3554. from->type.wordValue = short(from->type.intValue);
  3555. }
  3556. from->type.dataType.SetTokenType(to.GetTokenType());
  3557. }
  3558. }
  3559. else if( to.IsIntegerType() && to.GetSizeInMemoryDWords() == 2 )
  3560. {
  3561. // Float constants can be implicitly converted to int
  3562. if( from->type.dataType.IsFloatType() )
  3563. {
  3564. float fc = from->type.floatValue;
  3565. asINT64 ic = asINT64(fc);
  3566. if( float(ic) != fc )
  3567. {
  3568. if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_NOT_EXACT, node);
  3569. }
  3570. from->type.dataType = asCDataType::CreatePrimitive(ttInt64, true);
  3571. from->type.qwordValue = ic;
  3572. }
  3573. // Double constants can be implicitly converted to int
  3574. else if( from->type.dataType.IsDoubleType() )
  3575. {
  3576. double fc = from->type.doubleValue;
  3577. asINT64 ic = asINT64(fc);
  3578. if( double(ic) != fc )
  3579. {
  3580. if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_NOT_EXACT, node);
  3581. }
  3582. from->type.dataType = asCDataType::CreatePrimitive(ttInt64, true);
  3583. from->type.qwordValue = ic;
  3584. }
  3585. else if( from->type.dataType.IsUnsignedType() )
  3586. {
  3587. // Convert to 64bit
  3588. if( from->type.dataType.GetSizeInMemoryBytes() == 1 )
  3589. from->type.qwordValue = from->type.byteValue;
  3590. else if( from->type.dataType.GetSizeInMemoryBytes() == 2 )
  3591. from->type.qwordValue = from->type.wordValue;
  3592. else if( from->type.dataType.GetSizeInMemoryBytes() == 4 )
  3593. from->type.qwordValue = from->type.dwordValue;
  3594. else if( from->type.dataType.GetSizeInMemoryBytes() == 8 )
  3595. {
  3596. if( asINT64(from->type.qwordValue) < 0 )
  3597. {
  3598. if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_CHANGE_SIGN, node);
  3599. }
  3600. }
  3601. from->type.dataType = asCDataType::CreatePrimitive(ttInt64, true);
  3602. }
  3603. else if( from->type.dataType.IsEnumType() )
  3604. {
  3605. from->type.qwordValue = from->type.intValue;
  3606. from->type.dataType = asCDataType::CreatePrimitive(ttInt64, true);
  3607. }
  3608. else if( from->type.dataType.IsIntegerType() )
  3609. {
  3610. // Convert to 64bit
  3611. if( from->type.dataType.GetSizeInMemoryBytes() == 1 )
  3612. from->type.qwordValue = (signed char)from->type.byteValue;
  3613. else if( from->type.dataType.GetSizeInMemoryBytes() == 2 )
  3614. from->type.qwordValue = (short)from->type.wordValue;
  3615. else if( from->type.dataType.GetSizeInMemoryBytes() == 4 )
  3616. from->type.qwordValue = from->type.intValue;
  3617. from->type.dataType = asCDataType::CreatePrimitive(ttInt64, true);
  3618. }
  3619. }
  3620. else if( to.IsUnsignedType() && to.GetSizeInMemoryDWords() == 1 )
  3621. {
  3622. if( from->type.dataType.IsFloatType() )
  3623. {
  3624. float fc = from->type.floatValue;
  3625. int uic = int(fc);
  3626. if( float(uic) != fc )
  3627. {
  3628. if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_NOT_EXACT, node);
  3629. }
  3630. else if( uic < 0 )
  3631. {
  3632. if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_CHANGE_SIGN, node);
  3633. }
  3634. from->type.dataType = asCDataType::CreatePrimitive(ttInt, true);
  3635. from->type.intValue = uic;
  3636. // Try once more, in case of a smaller type
  3637. ImplicitConversionConstant(from, to, node, convType);
  3638. }
  3639. else if( from->type.dataType.IsDoubleType() )
  3640. {
  3641. double fc = from->type.doubleValue;
  3642. int uic = int(fc);
  3643. if( double(uic) != fc )
  3644. {
  3645. if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_NOT_EXACT, node);
  3646. }
  3647. from->type.dataType = asCDataType::CreatePrimitive(ttInt, true);
  3648. from->type.intValue = uic;
  3649. // Try once more, in case of a smaller type
  3650. ImplicitConversionConstant(from, to, node, convType);
  3651. }
  3652. else if( from->type.dataType.IsEnumType() )
  3653. {
  3654. from->type.dataType = asCDataType::CreatePrimitive(ttUInt, true);
  3655. // Try once more, in case of a smaller type
  3656. ImplicitConversionConstant(from, to, node, convType);
  3657. }
  3658. else if( from->type.dataType.IsIntegerType() )
  3659. {
  3660. // Verify that it is possible to convert to unsigned without loosing negative
  3661. if( from->type.intValue < 0 )
  3662. {
  3663. if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_CHANGE_SIGN, node);
  3664. }
  3665. // Convert to 32bit
  3666. if( from->type.dataType.GetSizeInMemoryBytes() == 1 )
  3667. from->type.intValue = (signed char)from->type.byteValue;
  3668. else if( from->type.dataType.GetSizeInMemoryBytes() == 2 )
  3669. from->type.intValue = (short)from->type.wordValue;
  3670. from->type.dataType = asCDataType::CreatePrimitive(ttUInt, true);
  3671. // Try once more, in case of a smaller type
  3672. ImplicitConversionConstant(from, to, node, convType);
  3673. }
  3674. else if( from->type.dataType.IsUnsignedType() &&
  3675. from->type.dataType.GetSizeInMemoryBytes() < 4 )
  3676. {
  3677. // Convert to 32bit
  3678. if( from->type.dataType.GetSizeInMemoryBytes() == 1 )
  3679. from->type.dwordValue = from->type.byteValue;
  3680. else if( from->type.dataType.GetSizeInMemoryBytes() == 2 )
  3681. from->type.dwordValue = from->type.wordValue;
  3682. from->type.dataType = asCDataType::CreatePrimitive(ttUInt, true);
  3683. // Try once more, in case of a smaller type
  3684. ImplicitConversionConstant(from, to, node, convType);
  3685. }
  3686. else if( from->type.dataType.IsUnsignedType() &&
  3687. from->type.dataType.GetSizeInMemoryBytes() > to.GetSizeInMemoryBytes() )
  3688. {
  3689. // Verify if it is possible
  3690. if( to.GetSizeInMemoryBytes() == 1 )
  3691. {
  3692. if( asBYTE(from->type.dwordValue) != from->type.dwordValue )
  3693. if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_VALUE_TOO_LARGE_FOR_TYPE, node);
  3694. from->type.byteValue = asBYTE(from->type.dwordValue);
  3695. }
  3696. else if( to.GetSizeInMemoryBytes() == 2 )
  3697. {
  3698. if( asWORD(from->type.dwordValue) != from->type.dwordValue )
  3699. if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_VALUE_TOO_LARGE_FOR_TYPE, node);
  3700. from->type.wordValue = asWORD(from->type.dwordValue);
  3701. }
  3702. from->type.dataType.SetTokenType(to.GetTokenType());
  3703. }
  3704. }
  3705. else if( to.IsUnsignedType() && to.GetSizeInMemoryDWords() == 2 )
  3706. {
  3707. if( from->type.dataType.IsFloatType() )
  3708. {
  3709. float fc = from->type.floatValue;
  3710. // Convert first to int64 then to uint64 to avoid negative float becoming 0 on gnuc base compilers
  3711. asQWORD uic = asQWORD(asINT64(fc));
  3712. // TODO: MSVC6 doesn't permit UINT64 to double
  3713. if( float((signed)uic) != fc )
  3714. {
  3715. if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_NOT_EXACT, node);
  3716. }
  3717. from->type.dataType = asCDataType::CreatePrimitive(ttUInt64, true);
  3718. from->type.qwordValue = uic;
  3719. }
  3720. else if( from->type.dataType.IsDoubleType() )
  3721. {
  3722. double fc = from->type.doubleValue;
  3723. // Convert first to int64 then to uint64 to avoid negative float becoming 0 on gnuc base compilers
  3724. asQWORD uic = asQWORD(asINT64(fc));
  3725. // TODO: MSVC6 doesn't permit UINT64 to double
  3726. if( double((signed)uic) != fc )
  3727. {
  3728. if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_NOT_EXACT, node);
  3729. }
  3730. from->type.dataType = asCDataType::CreatePrimitive(ttUInt64, true);
  3731. from->type.qwordValue = uic;
  3732. }
  3733. else if( from->type.dataType.IsEnumType() )
  3734. {
  3735. from->type.qwordValue = (asINT64)from->type.intValue;
  3736. from->type.dataType = asCDataType::CreatePrimitive(ttUInt64, true);
  3737. }
  3738. else if( from->type.dataType.IsIntegerType() && from->type.dataType.GetSizeInMemoryDWords() == 1 )
  3739. {
  3740. // Convert to 64bit
  3741. if( from->type.dataType.GetSizeInMemoryBytes() == 1 )
  3742. from->type.qwordValue = (asINT64)(signed char)from->type.byteValue;
  3743. else if( from->type.dataType.GetSizeInMemoryBytes() == 2 )
  3744. from->type.qwordValue = (asINT64)(short)from->type.wordValue;
  3745. else if( from->type.dataType.GetSizeInMemoryBytes() == 4 )
  3746. from->type.qwordValue = (asINT64)from->type.intValue;
  3747. // Verify that it is possible to convert to unsigned without loosing negative
  3748. if( asINT64(from->type.qwordValue) < 0 )
  3749. {
  3750. if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_CHANGE_SIGN, node);
  3751. }
  3752. from->type.dataType = asCDataType::CreatePrimitive(ttUInt64, true);
  3753. }
  3754. else if( from->type.dataType.IsIntegerType() && from->type.dataType.GetSizeInMemoryDWords() == 2 )
  3755. {
  3756. // Verify that it is possible to convert to unsigned without loosing negative
  3757. if( asINT64(from->type.qwordValue) < 0 )
  3758. {
  3759. if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_CHANGE_SIGN, node);
  3760. }
  3761. from->type.dataType = asCDataType::CreatePrimitive(ttUInt64, true);
  3762. }
  3763. else if( from->type.dataType.IsUnsignedType() )
  3764. {
  3765. // Convert to 64bit
  3766. if( from->type.dataType.GetSizeInMemoryBytes() == 1 )
  3767. from->type.qwordValue = from->type.byteValue;
  3768. else if( from->type.dataType.GetSizeInMemoryBytes() == 2 )
  3769. from->type.qwordValue = from->type.wordValue;
  3770. else if( from->type.dataType.GetSizeInMemoryBytes() == 4 )
  3771. from->type.qwordValue = from->type.dwordValue;
  3772. from->type.dataType = asCDataType::CreatePrimitive(ttUInt64, true);
  3773. }
  3774. }
  3775. else if( to.IsFloatType() )
  3776. {
  3777. if( from->type.dataType.IsDoubleType() )
  3778. {
  3779. double ic = from->type.doubleValue;
  3780. float fc = float(ic);
  3781. if( double(fc) != ic )
  3782. {
  3783. asCString str;
  3784. str.Format(TXT_POSSIBLE_LOSS_OF_PRECISION);
  3785. if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(str.AddressOf(), node);
  3786. }
  3787. from->type.dataType.SetTokenType(to.GetTokenType());
  3788. from->type.floatValue = fc;
  3789. }
  3790. else if( from->type.dataType.IsEnumType() )
  3791. {
  3792. float fc = float(from->type.intValue);
  3793. if( int(fc) != from->type.intValue )
  3794. {
  3795. if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_NOT_EXACT, node);
  3796. }
  3797. from->type.dataType.SetTokenType(to.GetTokenType());
  3798. from->type.floatValue = fc;
  3799. }
  3800. else if( from->type.dataType.IsIntegerType() && from->type.dataType.GetSizeInMemoryDWords() == 1 )
  3801. {
  3802. // Must properly convert value in case the from value is smaller
  3803. int ic;
  3804. if( from->type.dataType.GetSizeInMemoryBytes() == 1 )
  3805. ic = (signed char)from->type.byteValue;
  3806. else if( from->type.dataType.GetSizeInMemoryBytes() == 2 )
  3807. ic = (short)from->type.wordValue;
  3808. else
  3809. ic = from->type.intValue;
  3810. float fc = float(ic);
  3811. if( int(fc) != ic )
  3812. {
  3813. if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_NOT_EXACT, node);
  3814. }
  3815. from->type.dataType.SetTokenType(to.GetTokenType());
  3816. from->type.floatValue = fc;
  3817. }
  3818. else if( from->type.dataType.IsIntegerType() && from->type.dataType.GetSizeInMemoryDWords() == 2 )
  3819. {
  3820. float fc = float(asINT64(from->type.qwordValue));
  3821. if( asINT64(fc) != asINT64(from->type.qwordValue) )
  3822. {
  3823. if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_NOT_EXACT, node);
  3824. }
  3825. from->type.dataType.SetTokenType(to.GetTokenType());
  3826. from->type.floatValue = fc;
  3827. }
  3828. else if( from->type.dataType.IsUnsignedType() && from->type.dataType.GetSizeInMemoryDWords() == 1 )
  3829. {
  3830. // Must properly convert value in case the from value is smaller
  3831. unsigned int uic;
  3832. if( from->type.dataType.GetSizeInMemoryBytes() == 1 )
  3833. uic = from->type.byteValue;
  3834. else if( from->type.dataType.GetSizeInMemoryBytes() == 2 )
  3835. uic = from->type.wordValue;
  3836. else
  3837. uic = from->type.dwordValue;
  3838. float fc = float(uic);
  3839. if( (unsigned int)(fc) != uic )
  3840. {
  3841. if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_NOT_EXACT, node);
  3842. }
  3843. from->type.dataType.SetTokenType(to.GetTokenType());
  3844. from->type.floatValue = fc;
  3845. }
  3846. else if( from->type.dataType.IsUnsignedType() && from->type.dataType.GetSizeInMemoryDWords() == 2 )
  3847. {
  3848. // TODO: MSVC6 doesn't permit UINT64 to double
  3849. float fc = float((signed)from->type.qwordValue);
  3850. if( asQWORD(fc) != from->type.qwordValue )
  3851. {
  3852. if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_NOT_EXACT, node);
  3853. }
  3854. from->type.dataType.SetTokenType(to.GetTokenType());
  3855. from->type.floatValue = fc;
  3856. }
  3857. }
  3858. else if( to.IsDoubleType() )
  3859. {
  3860. if( from->type.dataType.IsFloatType() )
  3861. {
  3862. float ic = from->type.floatValue;
  3863. double fc = double(ic);
  3864. // Don't check for float->double
  3865. // if( float(fc) != ic )
  3866. // {
  3867. // acCString str;
  3868. // str.Format(TXT_NOT_EXACT_g_g_g, ic, fc, float(fc));
  3869. // if( !isExplicit ) Warning(str, node);
  3870. // }
  3871. from->type.dataType.SetTokenType(to.GetTokenType());
  3872. from->type.doubleValue = fc;
  3873. }
  3874. else if( from->type.dataType.IsEnumType() )
  3875. {
  3876. double fc = double(from->type.intValue);
  3877. if( int(fc) != from->type.intValue )
  3878. {
  3879. if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_NOT_EXACT, node);
  3880. }
  3881. from->type.dataType.SetTokenType(to.GetTokenType());
  3882. from->type.doubleValue = fc;
  3883. }
  3884. else if( from->type.dataType.IsIntegerType() && from->type.dataType.GetSizeInMemoryDWords() == 1 )
  3885. {
  3886. // Must properly convert value in case the from value is smaller
  3887. int ic;
  3888. if( from->type.dataType.GetSizeInMemoryBytes() == 1 )
  3889. ic = (signed char)from->type.byteValue;
  3890. else if( from->type.dataType.GetSizeInMemoryBytes() == 2 )
  3891. ic = (short)from->type.wordValue;
  3892. else
  3893. ic = from->type.intValue;
  3894. double fc = double(ic);
  3895. if( int(fc) != ic )
  3896. {
  3897. if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_NOT_EXACT, node);
  3898. }
  3899. from->type.dataType.SetTokenType(to.GetTokenType());
  3900. from->type.doubleValue = fc;
  3901. }
  3902. else if( from->type.dataType.IsIntegerType() && from->type.dataType.GetSizeInMemoryDWords() == 2 )
  3903. {
  3904. double fc = double(asINT64(from->type.qwordValue));
  3905. if( asINT64(fc) != asINT64(from->type.qwordValue) )
  3906. {
  3907. if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_NOT_EXACT, node);
  3908. }
  3909. from->type.dataType.SetTokenType(to.GetTokenType());
  3910. from->type.doubleValue = fc;
  3911. }
  3912. else if( from->type.dataType.IsUnsignedType() && from->type.dataType.GetSizeInMemoryDWords() == 1 )
  3913. {
  3914. // Must properly convert value in case the from value is smaller
  3915. unsigned int uic;
  3916. if( from->type.dataType.GetSizeInMemoryBytes() == 1 )
  3917. uic = from->type.byteValue;
  3918. else if( from->type.dataType.GetSizeInMemoryBytes() == 2 )
  3919. uic = from->type.wordValue;
  3920. else
  3921. uic = from->type.dwordValue;
  3922. double fc = double(uic);
  3923. if( (unsigned int)(fc) != uic )
  3924. {
  3925. if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_NOT_EXACT, node);
  3926. }
  3927. from->type.dataType.SetTokenType(to.GetTokenType());
  3928. from->type.doubleValue = fc;
  3929. }
  3930. else if( from->type.dataType.IsUnsignedType() && from->type.dataType.GetSizeInMemoryDWords() == 2 )
  3931. {
  3932. // TODO: MSVC6 doesn't permit UINT64 to double
  3933. double fc = double((signed)from->type.qwordValue);
  3934. if( asQWORD(fc) != from->type.qwordValue )
  3935. {
  3936. if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_NOT_EXACT, node);
  3937. }
  3938. from->type.dataType.SetTokenType(to.GetTokenType());
  3939. from->type.doubleValue = fc;
  3940. }
  3941. }
  3942. }
  3943. int asCCompiler::DoAssignment(asSExprContext *ctx, asSExprContext *lctx, asSExprContext *rctx, asCScriptNode *lexpr, asCScriptNode *rexpr, int op, asCScriptNode *opNode)
  3944. {
  3945. // Implicit handle types should always be treated as handles in assignments
  3946. if (lctx->type.dataType.GetObjectType() && (lctx->type.dataType.GetObjectType()->flags & asOBJ_IMPLICIT_HANDLE) )
  3947. {
  3948. lctx->type.dataType.MakeHandle(true);
  3949. lctx->type.isExplicitHandle = true;
  3950. }
  3951. // If the left hand expression is a property accessor, then that should be used
  3952. // to do the assignment instead of the ordinary operator. The exception is when
  3953. // the property accessor is for a handle property, and the operation is a value
  3954. // assignment.
  3955. if( (lctx->property_get || lctx->property_set) &&
  3956. !(lctx->type.dataType.IsObjectHandle() && !lctx->type.isExplicitHandle) )
  3957. {
  3958. if( op != ttAssignment )
  3959. {
  3960. // TODO: getset: We may actually be able to support this, if we can
  3961. // guarantee that the object reference will stay valid
  3962. // between the calls to the get and set accessors.
  3963. // Compound assignments are not allowed for properties
  3964. Error(TXT_COMPOUND_ASGN_WITH_PROP, opNode);
  3965. return -1;
  3966. }
  3967. MergeExprContexts(ctx, lctx);
  3968. ctx->type = lctx->type;
  3969. ctx->property_get = lctx->property_get;
  3970. ctx->property_set = lctx->property_set;
  3971. ctx->property_const = lctx->property_const;
  3972. ctx->property_handle = lctx->property_handle;
  3973. return ProcessPropertySetAccessor(ctx, rctx, opNode);
  3974. }
  3975. if( lctx->type.dataType.IsPrimitive() )
  3976. {
  3977. if( op != ttAssignment )
  3978. {
  3979. // Compute the operator before the assignment
  3980. asCTypeInfo lvalue = lctx->type;
  3981. if( lctx->type.isTemporary && !lctx->type.isVariable )
  3982. {
  3983. // The temporary variable must not be freed until the
  3984. // assignment has been performed. lvalue still holds
  3985. // the information about the temporary variable
  3986. lctx->type.isTemporary = false;
  3987. }
  3988. asSExprContext o(engine);
  3989. CompileOperator(opNode, lctx, rctx, &o);
  3990. MergeExprContexts(rctx, &o);
  3991. rctx->type = o.type;
  3992. // Convert the rvalue to the right type and validate it
  3993. PrepareForAssignment(&lvalue.dataType, rctx, rexpr);
  3994. MergeExprContexts(ctx, rctx);
  3995. lctx->type = lvalue;
  3996. // The lvalue continues the same, either it was a variable, or a reference in the register
  3997. }
  3998. else
  3999. {
  4000. // Convert the rvalue to the right type and validate it
  4001. PrepareForAssignment(&lctx->type.dataType, rctx, rexpr, lctx);
  4002. MergeExprContexts(ctx, rctx);
  4003. MergeExprContexts(ctx, lctx);
  4004. }
  4005. ReleaseTemporaryVariable(rctx->type, &ctx->bc);
  4006. PerformAssignment(&lctx->type, &rctx->type, &ctx->bc, opNode);
  4007. ctx->type = lctx->type;
  4008. }
  4009. else if( lctx->type.isExplicitHandle )
  4010. {
  4011. // Verify that the left hand value isn't a temporary variable
  4012. if( lctx->type.isTemporary )
  4013. {
  4014. Error(TXT_REF_IS_TEMP, lexpr);
  4015. return -1;
  4016. }
  4017. // Object handles don't have any compound assignment operators
  4018. if( op != ttAssignment )
  4019. {
  4020. asCString str;
  4021. str.Format(TXT_ILLEGAL_OPERATION_ON_s, lctx->type.dataType.Format().AddressOf());
  4022. Error(str.AddressOf(), lexpr);
  4023. return -1;
  4024. }
  4025. asCDataType dt = lctx->type.dataType;
  4026. dt.MakeReference(false);
  4027. PrepareArgument(&dt, rctx, rexpr, true, 1);
  4028. if( !dt.IsEqualExceptRefAndConst(rctx->type.dataType) )
  4029. {
  4030. asCString str;
  4031. str.Format(TXT_CANT_IMPLICITLY_CONVERT_s_TO_s, rctx->type.dataType.Format().AddressOf(), lctx->type.dataType.Format().AddressOf());
  4032. Error(str.AddressOf(), rexpr);
  4033. return -1;
  4034. }
  4035. MergeExprContexts(ctx, rctx);
  4036. MergeExprContexts(ctx, lctx);
  4037. ctx->bc.InstrWORD(asBC_GETOBJREF, AS_PTR_SIZE);
  4038. PerformAssignment(&lctx->type, &rctx->type, &ctx->bc, opNode);
  4039. ReleaseTemporaryVariable(rctx->type, &ctx->bc);
  4040. ctx->type = rctx->type;
  4041. }
  4042. else // if( lctx->type.dataType.IsObject() )
  4043. {
  4044. // Verify that the left hand value isn't a temporary variable
  4045. if( lctx->type.isTemporary )
  4046. {
  4047. Error(TXT_REF_IS_TEMP, lexpr);
  4048. return -1;
  4049. }
  4050. if( lctx->type.dataType.IsObjectHandle() && !lctx->type.isExplicitHandle )
  4051. {
  4052. // Convert the handle to a object reference
  4053. asCDataType to;
  4054. to = lctx->type.dataType;
  4055. to.MakeHandle(false);
  4056. ImplicitConversion(lctx, to, lexpr, asIC_IMPLICIT_CONV);
  4057. }
  4058. // Check for overloaded assignment operator
  4059. if( CompileOverloadedDualOperator(opNode, lctx, rctx, ctx) )
  4060. {
  4061. // An overloaded assignment operator was found (or a compilation error occured)
  4062. return 0;
  4063. }
  4064. // No registered operator was found. In case the operation is a direct
  4065. // assignment and the rvalue is the same type as the lvalue, then we can
  4066. // still use the byte-for-byte copy to do the assignment
  4067. if( op != ttAssignment )
  4068. {
  4069. asCString str;
  4070. str.Format(TXT_ILLEGAL_OPERATION_ON_s, lctx->type.dataType.Format().AddressOf());
  4071. Error(str.AddressOf(), lexpr);
  4072. return -1;
  4073. }
  4074. // Implicitly convert the rvalue to the type of the lvalue
  4075. asCDataType dt = lctx->type.dataType;
  4076. PrepareArgument(&dt, rctx, rexpr, true, 1);
  4077. if( !dt.IsEqualExceptRefAndConst(rctx->type.dataType) )
  4078. {
  4079. asCString str;
  4080. str.Format(TXT_CANT_IMPLICITLY_CONVERT_s_TO_s, rctx->type.dataType.Format().AddressOf(), lctx->type.dataType.Format().AddressOf());
  4081. Error(str.AddressOf(), rexpr);
  4082. return -1;
  4083. }
  4084. MergeExprContexts(ctx, rctx);
  4085. MergeExprContexts(ctx, lctx);
  4086. ctx->bc.InstrWORD(asBC_GETOBJREF, AS_PTR_SIZE);
  4087. PerformAssignment(&lctx->type, &rctx->type, &ctx->bc, opNode);
  4088. ReleaseTemporaryVariable(rctx->type, &ctx->bc);
  4089. ctx->type = lctx->type;
  4090. }
  4091. return 0;
  4092. }
  4093. int asCCompiler::CompileAssignment(asCScriptNode *expr, asSExprContext *ctx)
  4094. {
  4095. asCScriptNode *lexpr = expr->firstChild;
  4096. if( lexpr->next )
  4097. {
  4098. if( globalExpression )
  4099. {
  4100. Error(TXT_ASSIGN_IN_GLOBAL_EXPR, expr);
  4101. ctx->type.SetDummy();
  4102. return -1;
  4103. }
  4104. // Compile the two expression terms
  4105. asSExprContext lctx(engine), rctx(engine);
  4106. int rr = CompileAssignment(lexpr->next->next, &rctx);
  4107. int lr = CompileCondition(lexpr, &lctx);
  4108. if( lr >= 0 && rr >= 0 )
  4109. return DoAssignment(ctx, &lctx, &rctx, lexpr, lexpr->next->next, lexpr->next->tokenType, lexpr->next);
  4110. // Since the operands failed, the assignment was not computed
  4111. ctx->type.SetDummy();
  4112. return -1;
  4113. }
  4114. return CompileCondition(lexpr, ctx);
  4115. }
  4116. int asCCompiler::CompileCondition(asCScriptNode *expr, asSExprContext *ctx)
  4117. {
  4118. asCTypeInfo ctype;
  4119. // Compile the conditional expression
  4120. asCScriptNode *cexpr = expr->firstChild;
  4121. if( cexpr->next )
  4122. {
  4123. //-------------------------------
  4124. // Compile the condition
  4125. asSExprContext e(engine);
  4126. int r = CompileExpression(cexpr, &e);
  4127. if( r < 0 )
  4128. e.type.SetConstantB(asCDataType::CreatePrimitive(ttBool, true), true);
  4129. if( r >= 0 && !e.type.dataType.IsEqualExceptRefAndConst(asCDataType::CreatePrimitive(ttBool, true)) )
  4130. {
  4131. Error(TXT_EXPR_MUST_BE_BOOL, cexpr);
  4132. e.type.SetConstantB(asCDataType::CreatePrimitive(ttBool, true), true);
  4133. }
  4134. ctype = e.type;
  4135. ProcessPropertyGetAccessor(&e, cexpr);
  4136. if( e.type.dataType.IsReference() ) ConvertToVariable(&e);
  4137. ProcessDeferredParams(&e);
  4138. //-------------------------------
  4139. // Compile the left expression
  4140. asSExprContext le(engine);
  4141. int lr = CompileAssignment(cexpr->next, &le);
  4142. //-------------------------------
  4143. // Compile the right expression
  4144. asSExprContext re(engine);
  4145. int rr = CompileAssignment(cexpr->next->next, &re);
  4146. if( lr >= 0 && rr >= 0 )
  4147. {
  4148. ProcessPropertyGetAccessor(&le, cexpr->next);
  4149. ProcessPropertyGetAccessor(&re, cexpr->next->next);
  4150. bool isExplicitHandle = le.type.isExplicitHandle || re.type.isExplicitHandle;
  4151. // Allow a 0 in the first case to be implicitly converted to the second type
  4152. if( le.type.isConstant && le.type.intValue == 0 && le.type.dataType.IsUnsignedType() )
  4153. {
  4154. asCDataType to = re.type.dataType;
  4155. to.MakeReference(false);
  4156. to.MakeReadOnly(true);
  4157. ImplicitConversionConstant(&le, to, cexpr->next, asIC_IMPLICIT_CONV);
  4158. }
  4159. //---------------------------------
  4160. // Output the byte code
  4161. int afterLabel = nextLabel++;
  4162. int elseLabel = nextLabel++;
  4163. // If left expression is void, then we don't need to store the result
  4164. if( le.type.dataType.IsEqualExceptConst(asCDataType::CreatePrimitive(ttVoid, false)) )
  4165. {
  4166. // Put the code for the condition expression on the output
  4167. MergeExprContexts(ctx, &e);
  4168. // Added the branch decision
  4169. ctx->type = e.type;
  4170. ConvertToVariable(ctx);
  4171. ctx->bc.InstrSHORT(asBC_CpyVtoR4, ctx->type.stackOffset);
  4172. ctx->bc.Instr(asBC_ClrHi);
  4173. ctx->bc.InstrDWORD(asBC_JZ, elseLabel);
  4174. ReleaseTemporaryVariable(ctx->type, &ctx->bc);
  4175. // Add the left expression
  4176. MergeExprContexts(ctx, &le);
  4177. ctx->bc.InstrINT(asBC_JMP, afterLabel);
  4178. // Add the right expression
  4179. ctx->bc.Label((short)elseLabel);
  4180. MergeExprContexts(ctx, &re);
  4181. ctx->bc.Label((short)afterLabel);
  4182. // Make sure both expressions have the same type
  4183. if( le.type.dataType != re.type.dataType )
  4184. Error(TXT_BOTH_MUST_BE_SAME, expr);
  4185. // Set the type of the result
  4186. ctx->type = le.type;
  4187. }
  4188. else
  4189. {
  4190. // Allocate temporary variable and copy the result to that one
  4191. asCTypeInfo temp;
  4192. temp = le.type;
  4193. temp.dataType.MakeReference(false);
  4194. temp.dataType.MakeReadOnly(false);
  4195. // Make sure the variable isn't used in the initial expression
  4196. asCArray<int> vars;
  4197. e.bc.GetVarsUsed(vars);
  4198. int offset = AllocateVariableNotIn(temp.dataType, true, &vars);
  4199. temp.SetVariable(temp.dataType, offset, true);
  4200. CallDefaultConstructor(temp.dataType, offset, &ctx->bc, expr);
  4201. // Put the code for the condition expression on the output
  4202. MergeExprContexts(ctx, &e);
  4203. // Added the branch decision
  4204. ctx->type = e.type;
  4205. ConvertToVariable(ctx);
  4206. ctx->bc.InstrSHORT(asBC_CpyVtoR4, ctx->type.stackOffset);
  4207. ctx->bc.Instr(asBC_ClrHi);
  4208. ctx->bc.InstrDWORD(asBC_JZ, elseLabel);
  4209. ReleaseTemporaryVariable(ctx->type, &ctx->bc);
  4210. // Assign the result of the left expression to the temporary variable
  4211. asCTypeInfo rtemp;
  4212. rtemp = temp;
  4213. if( rtemp.dataType.IsObjectHandle() )
  4214. rtemp.isExplicitHandle = true;
  4215. PrepareForAssignment(&rtemp.dataType, &le, cexpr->next);
  4216. MergeExprContexts(ctx, &le);
  4217. if( !rtemp.dataType.IsPrimitive() )
  4218. {
  4219. ctx->bc.InstrSHORT(asBC_PSF, (short)offset);
  4220. rtemp.dataType.MakeReference(true);
  4221. }
  4222. PerformAssignment(&rtemp, &le.type, &ctx->bc, cexpr->next);
  4223. if( !rtemp.dataType.IsPrimitive() )
  4224. ctx->bc.Pop(le.type.dataType.GetSizeOnStackDWords()); // Pop the original value
  4225. // Release the old temporary variable
  4226. ReleaseTemporaryVariable(le.type, &ctx->bc);
  4227. ctx->bc.InstrINT(asBC_JMP, afterLabel);
  4228. // Start of the right expression
  4229. ctx->bc.Label((short)elseLabel);
  4230. // Copy the result to the same temporary variable
  4231. PrepareForAssignment(&rtemp.dataType, &re, cexpr->next);
  4232. MergeExprContexts(ctx, &re);
  4233. if( !rtemp.dataType.IsPrimitive() )
  4234. {
  4235. ctx->bc.InstrSHORT(asBC_PSF, (short)offset);
  4236. rtemp.dataType.MakeReference(true);
  4237. }
  4238. PerformAssignment(&rtemp, &re.type, &ctx->bc, cexpr->next);
  4239. if( !rtemp.dataType.IsPrimitive() )
  4240. ctx->bc.Pop(le.type.dataType.GetSizeOnStackDWords()); // Pop the original value
  4241. // Release the old temporary variable
  4242. ReleaseTemporaryVariable(re.type, &ctx->bc);
  4243. ctx->bc.Label((short)afterLabel);
  4244. // Make sure both expressions have the same type
  4245. if( le.type.dataType != re.type.dataType )
  4246. Error(TXT_BOTH_MUST_BE_SAME, expr);
  4247. // Set the temporary variable as output
  4248. ctx->type = rtemp;
  4249. ctx->type.isExplicitHandle = isExplicitHandle;
  4250. if( !ctx->type.dataType.IsPrimitive() )
  4251. {
  4252. ctx->bc.InstrSHORT(asBC_PSF, (short)offset);
  4253. ctx->type.dataType.MakeReference(true);
  4254. }
  4255. // Make sure the output isn't marked as being a literal constant
  4256. ctx->type.isConstant = false;
  4257. }
  4258. }
  4259. else
  4260. {
  4261. ctx->type.SetDummy();
  4262. return -1;
  4263. }
  4264. }
  4265. else
  4266. return CompileExpression(cexpr, ctx);
  4267. return 0;
  4268. }
  4269. int asCCompiler::CompileExpression(asCScriptNode *expr, asSExprContext *ctx)
  4270. {
  4271. asASSERT(expr->nodeType == snExpression);
  4272. // Count the nodes
  4273. int count = 0;
  4274. asCScriptNode *node = expr->firstChild;
  4275. while( node )
  4276. {
  4277. count++;
  4278. node = node->next;
  4279. }
  4280. // Convert to polish post fix, i.e: a+b => ab+
  4281. asCArray<asCScriptNode *> stack(count);
  4282. asCArray<asCScriptNode *> stack2(count);
  4283. asCArray<asCScriptNode *> postfix(count);
  4284. node = expr->firstChild;
  4285. while( node )
  4286. {
  4287. int precedence = GetPrecedence(node);
  4288. while( stack.GetLength() > 0 &&
  4289. precedence <= GetPrecedence(stack[stack.GetLength()-1]) )
  4290. stack2.PushLast(stack.PopLast());
  4291. stack.PushLast(node);
  4292. node = node->next;
  4293. }
  4294. while( stack.GetLength() > 0 )
  4295. stack2.PushLast(stack.PopLast());
  4296. // We need to swap operands so that the left
  4297. // operand is always computed before the right
  4298. SwapPostFixOperands(stack2, postfix);
  4299. // Compile the postfix formatted expression
  4300. return CompilePostFixExpression(&postfix, ctx);
  4301. }
  4302. void asCCompiler::SwapPostFixOperands(asCArray<asCScriptNode *> &postfix, asCArray<asCScriptNode *> &target)
  4303. {
  4304. if( postfix.GetLength() == 0 ) return;
  4305. asCScriptNode *node = postfix.PopLast();
  4306. if( node->nodeType == snExprTerm )
  4307. {
  4308. target.PushLast(node);
  4309. return;
  4310. }
  4311. SwapPostFixOperands(postfix, target);
  4312. SwapPostFixOperands(postfix, target);
  4313. target.PushLast(node);
  4314. }
  4315. int asCCompiler::CompilePostFixExpression(asCArray<asCScriptNode *> *postfix, asSExprContext *ctx)
  4316. {
  4317. // Shouldn't send any byte code
  4318. asASSERT(ctx->bc.GetLastInstr() == -1);
  4319. // Pop the last node
  4320. asCScriptNode *node = postfix->PopLast();
  4321. ctx->exprNode = node;
  4322. // If term, compile the term
  4323. if( node->nodeType == snExprTerm )
  4324. return CompileExpressionTerm(node, ctx);
  4325. // Compile the two expression terms
  4326. asSExprContext r(engine), l(engine);
  4327. int ret;
  4328. ret = CompilePostFixExpression(postfix, &l); if( ret < 0 ) return ret;
  4329. ret = CompilePostFixExpression(postfix, &r); if( ret < 0 ) return ret;
  4330. // Compile the operation
  4331. return CompileOperator(node, &l, &r, ctx);
  4332. }
  4333. int asCCompiler::CompileExpressionTerm(asCScriptNode *node, asSExprContext *ctx)
  4334. {
  4335. // Shouldn't send any byte code
  4336. asASSERT(ctx->bc.GetLastInstr() == -1);
  4337. // Set the type as a dummy by default, in case of any compiler errors
  4338. ctx->type.SetDummy();
  4339. // Compile the value node
  4340. asCScriptNode *vnode = node->firstChild;
  4341. while( vnode->nodeType != snExprValue )
  4342. vnode = vnode->next;
  4343. asSExprContext v(engine);
  4344. int r = CompileExpressionValue(vnode, &v); if( r < 0 ) return r;
  4345. // Compile post fix operators
  4346. asCScriptNode *pnode = vnode->next;
  4347. while( pnode )
  4348. {
  4349. r = CompileExpressionPostOp(pnode, &v); if( r < 0 ) return r;
  4350. pnode = pnode->next;
  4351. }
  4352. // Compile pre fix operators
  4353. pnode = vnode->prev;
  4354. while( pnode )
  4355. {
  4356. r = CompileExpressionPreOp(pnode, &v); if( r < 0 ) return r;
  4357. pnode = pnode->prev;
  4358. }
  4359. // Return the byte code and final type description
  4360. MergeExprContexts(ctx, &v);
  4361. ctx->type = v.type;
  4362. ctx->property_get = v.property_get;
  4363. ctx->property_set = v.property_set;
  4364. ctx->property_const = v.property_const;
  4365. ctx->property_handle = v.property_handle;
  4366. return 0;
  4367. }
  4368. int asCCompiler::CompileExpressionValue(asCScriptNode *node, asSExprContext *ctx)
  4369. {
  4370. // Shouldn't receive any byte code
  4371. asASSERT(ctx->bc.GetLastInstr() == -1);
  4372. asCScriptNode *vnode = node->firstChild;
  4373. if( vnode->nodeType == snVariableAccess )
  4374. {
  4375. // Determine the scope resolution of the variable
  4376. asCString scope = GetScopeFromNode(vnode);
  4377. // Determine the name of the variable
  4378. vnode = vnode->lastChild;
  4379. asASSERT(vnode->nodeType == snIdentifier );
  4380. asCString name(&script->code[vnode->tokenPos], vnode->tokenLength);
  4381. sVariable *v = 0;
  4382. if( scope == "" )
  4383. v = variables->GetVariable(name.AddressOf());
  4384. if( v == 0 )
  4385. {
  4386. // It is not a local variable or parameter
  4387. bool found = false;
  4388. // Is it a class member?
  4389. if( outFunc && outFunc->objectType && scope == "" )
  4390. {
  4391. if( name == THIS_TOKEN )
  4392. {
  4393. asCDataType dt = asCDataType::CreateObject(outFunc->objectType, outFunc->isReadOnly);
  4394. // The object pointer is located at stack position 0
  4395. ctx->bc.InstrSHORT(asBC_PSF, 0);
  4396. ctx->type.SetVariable(dt, 0, false);
  4397. ctx->type.dataType.MakeReference(true);
  4398. found = true;
  4399. }
  4400. if( !found )
  4401. {
  4402. // See if there are any matching property accessors
  4403. asSExprContext access(engine);
  4404. access.type.Set(asCDataType::CreateObject(outFunc->objectType, outFunc->isReadOnly));
  4405. int r = FindPropertyAccessor(name, &access, node);
  4406. if( r < 0 ) return -1;
  4407. if( access.property_get || access.property_set )
  4408. {
  4409. // Prepare the bytecode for the member access
  4410. ctx->bc.InstrSHORT(asBC_PSF, 0);
  4411. ctx->type.SetVariable(asCDataType::CreateObject(outFunc->objectType, outFunc->isReadOnly), 0, false);
  4412. ctx->type = access.type;
  4413. ctx->property_get = access.property_get;
  4414. ctx->property_set = access.property_set;
  4415. ctx->property_const = access.property_const;
  4416. ctx->property_handle = access.property_handle;
  4417. found = true;
  4418. }
  4419. }
  4420. if( !found )
  4421. {
  4422. asCDataType dt = asCDataType::CreateObject(outFunc->objectType, false);
  4423. asCObjectProperty *prop = builder->GetObjectProperty(dt, name.AddressOf());
  4424. if( prop )
  4425. {
  4426. // The object pointer is located at stack position 0
  4427. ctx->bc.InstrSHORT(asBC_PSF, 0);
  4428. ctx->type.SetVariable(dt, 0, false);
  4429. ctx->type.dataType.MakeReference(true);
  4430. Dereference(ctx, true);
  4431. // TODO: This is the same as what is in CompileExpressionPostOp
  4432. // Put the offset on the stack
  4433. ctx->bc.InstrINT(asBC_ADDSi, prop->byteOffset);
  4434. if( prop->type.IsReference() )
  4435. ctx->bc.Instr(asBC_RDSPTR);
  4436. // Reference to primitive must be stored in the temp register
  4437. if( prop->type.IsPrimitive() )
  4438. {
  4439. // The ADD offset command should store the reference in the register directly
  4440. ctx->bc.Instr(asBC_PopRPtr);
  4441. }
  4442. // Set the new type (keeping info about temp variable)
  4443. ctx->type.dataType = prop->type;
  4444. ctx->type.dataType.MakeReference(true);
  4445. ctx->type.isVariable = false;
  4446. if( ctx->type.dataType.IsObject() && !ctx->type.dataType.IsObjectHandle() )
  4447. {
  4448. // Objects that are members are not references
  4449. ctx->type.dataType.MakeReference(false);
  4450. }
  4451. // If the object reference is const, the property will also be const
  4452. ctx->type.dataType.MakeReadOnly(outFunc->isReadOnly);
  4453. found = true;
  4454. }
  4455. }
  4456. }
  4457. // Is it a global property?
  4458. if( !found && (scope == "" || scope == "::") )
  4459. {
  4460. bool isCompiled = true;
  4461. bool isPureConstant = false;
  4462. asQWORD constantValue;
  4463. asCGlobalProperty *prop = builder->GetGlobalProperty(name.AddressOf(), &isCompiled, &isPureConstant, &constantValue);
  4464. if( prop )
  4465. {
  4466. found = true;
  4467. // Verify that the global property has been compiled already
  4468. if( isCompiled )
  4469. {
  4470. if( ctx->type.dataType.GetObjectType() && (ctx->type.dataType.GetObjectType()->flags & asOBJ_IMPLICIT_HANDLE) )
  4471. {
  4472. ctx->type.dataType.MakeHandle(true);
  4473. ctx->type.isExplicitHandle = true;
  4474. }
  4475. // If the global property is a pure constant
  4476. // we can allow the compiler to optimize it. Pure
  4477. // constants are global constant variables that were
  4478. // initialized by literal constants.
  4479. if( isPureConstant )
  4480. ctx->type.SetConstantQW(prop->type, constantValue);
  4481. else
  4482. {
  4483. ctx->type.Set(prop->type);
  4484. ctx->type.dataType.MakeReference(true);
  4485. // Push the address of the variable on the stack
  4486. // TODO: global: The global var address should be stored in the instruction directly
  4487. if( ctx->type.dataType.IsPrimitive() )
  4488. ctx->bc.InstrWORD(asBC_LDG, (asWORD)outFunc->GetGlobalVarPtrIndex(prop->id));
  4489. else
  4490. // TODO: global: The global var address should be stored in the instruction directly
  4491. ctx->bc.InstrWORD(asBC_PGA, (asWORD)outFunc->GetGlobalVarPtrIndex(prop->id));
  4492. }
  4493. }
  4494. else
  4495. {
  4496. asCString str;
  4497. str.Format(TXT_UNINITIALIZED_GLOBAL_VAR_s, prop->name.AddressOf());
  4498. Error(str.AddressOf(), vnode);
  4499. return -1;
  4500. }
  4501. }
  4502. }
  4503. if( !found )
  4504. {
  4505. asCObjectType *scopeType = 0;
  4506. if( scope != "" )
  4507. {
  4508. // resolve the type before the scope
  4509. scopeType = builder->GetObjectType( scope.AddressOf() );
  4510. }
  4511. // Is it an enum value?
  4512. asDWORD value = 0;
  4513. asCDataType dt;
  4514. if( scopeType && builder->GetEnumValueFromObjectType(scopeType, name.AddressOf(), dt, value) )
  4515. {
  4516. // scoped enum value found
  4517. found = true;
  4518. }
  4519. else if( scope == "" && !engine->ep.requireEnumScope )
  4520. {
  4521. // look for the enum value with no namespace
  4522. int e = builder->GetEnumValue(name.AddressOf(), dt, value);
  4523. if( e )
  4524. {
  4525. found = true;
  4526. if( e == 2 )
  4527. {
  4528. Error(TXT_FOUND_MULTIPLE_ENUM_VALUES, vnode);
  4529. }
  4530. }
  4531. }
  4532. if( found )
  4533. {
  4534. // an enum value was resolved
  4535. ctx->type.SetConstantDW(dt, value);
  4536. }
  4537. }
  4538. if( !found )
  4539. {
  4540. // Prepend the scope to the name for the error message
  4541. if( scope != "" && scope != "::" )
  4542. scope += "::";
  4543. scope += name;
  4544. asCString str;
  4545. str.Format(TXT_s_NOT_DECLARED, scope.AddressOf());
  4546. Error(str.AddressOf(), vnode);
  4547. // Give dummy value
  4548. ctx->type.SetDummy();
  4549. // Declare the variable now so that it will not be reported again
  4550. variables->DeclareVariable(name.AddressOf(), asCDataType::CreatePrimitive(ttInt, false), 0x7FFF);
  4551. // Mark the variable as initialized so that the user will not be bother by it again
  4552. sVariable *v = variables->GetVariable(name.AddressOf());
  4553. asASSERT(v);
  4554. if( v ) v->isInitialized = true;
  4555. return -1;
  4556. }
  4557. }
  4558. else
  4559. {
  4560. // It is a variable or parameter
  4561. if( v->isPureConstant )
  4562. ctx->type.SetConstantQW(v->type, v->constantValue);
  4563. else
  4564. {
  4565. if( v->type.IsPrimitive() )
  4566. {
  4567. if( v->type.IsReference() )
  4568. {
  4569. // Copy the reference into the register
  4570. #if AS_PTR_SIZE == 1
  4571. ctx->bc.InstrSHORT(asBC_CpyVtoR4, (short)v->stackOffset);
  4572. #else
  4573. ctx->bc.InstrSHORT(asBC_CpyVtoR8, (short)v->stackOffset);
  4574. #endif
  4575. ctx->type.Set(v->type);
  4576. }
  4577. else
  4578. ctx->type.SetVariable(v->type, v->stackOffset, false);
  4579. }
  4580. else
  4581. {
  4582. ctx->bc.InstrSHORT(asBC_PSF, (short)v->stackOffset);
  4583. ctx->type.SetVariable(v->type, v->stackOffset, false);
  4584. ctx->type.dataType.MakeReference(true);
  4585. // Implicitly dereference handle parameters sent by reference
  4586. if( v->type.IsReference() && (!v->type.IsObject() || v->type.IsObjectHandle()) )
  4587. ctx->bc.Instr(asBC_RDSPTR);
  4588. }
  4589. }
  4590. }
  4591. }
  4592. else if( vnode->nodeType == snConstant )
  4593. {
  4594. if( vnode->tokenType == ttIntConstant )
  4595. {
  4596. asCString value(&script->code[vnode->tokenPos], vnode->tokenLength);
  4597. asQWORD val = asStringScanUInt64(value.AddressOf(), 10, 0);
  4598. // Do we need 64 bits?
  4599. if( val>>32 )
  4600. ctx->type.SetConstantQW(asCDataType::CreatePrimitive(ttUInt64, true), val);
  4601. else
  4602. ctx->type.SetConstantDW(asCDataType::CreatePrimitive(ttUInt, true), asDWORD(val));
  4603. }
  4604. else if( vnode->tokenType == ttBitsConstant )
  4605. {
  4606. asCString value(&script->code[vnode->tokenPos+2], vnode->tokenLength-2);
  4607. // TODO: Check for overflow
  4608. asQWORD val = asStringScanUInt64(value.AddressOf(), 16, 0);
  4609. // Do we need 64 bits?
  4610. if( val>>32 )
  4611. ctx->type.SetConstantQW(asCDataType::CreatePrimitive(ttUInt64, true), val);
  4612. else
  4613. ctx->type.SetConstantDW(asCDataType::CreatePrimitive(ttUInt, true), asDWORD(val));
  4614. }
  4615. else if( vnode->tokenType == ttFloatConstant )
  4616. {
  4617. asCString value(&script->code[vnode->tokenPos], vnode->tokenLength);
  4618. // TODO: Check for overflow
  4619. size_t numScanned;
  4620. float v = float(asStringScanDouble(value.AddressOf(), &numScanned));
  4621. ctx->type.SetConstantF(asCDataType::CreatePrimitive(ttFloat, true), v);
  4622. asASSERT(numScanned == vnode->tokenLength - 1);
  4623. }
  4624. else if( vnode->tokenType == ttDoubleConstant )
  4625. {
  4626. asCString value(&script->code[vnode->tokenPos], vnode->tokenLength);
  4627. // TODO: Check for overflow
  4628. size_t numScanned;
  4629. double v = asStringScanDouble(value.AddressOf(), &numScanned);
  4630. ctx->type.SetConstantD(asCDataType::CreatePrimitive(ttDouble, true), v);
  4631. asASSERT(numScanned == vnode->tokenLength);
  4632. }
  4633. else if( vnode->tokenType == ttTrue ||
  4634. vnode->tokenType == ttFalse )
  4635. {
  4636. #if AS_SIZEOF_BOOL == 1
  4637. ctx->type.SetConstantB(asCDataType::CreatePrimitive(ttBool, true), vnode->tokenType == ttTrue ? VALUE_OF_BOOLEAN_TRUE : 0);
  4638. #else
  4639. ctx->type.SetConstantDW(asCDataType::CreatePrimitive(ttBool, true), vnode->tokenType == ttTrue ? VALUE_OF_BOOLEAN_TRUE : 0);
  4640. #endif
  4641. }
  4642. else if( vnode->tokenType == ttStringConstant ||
  4643. vnode->tokenType == ttMultilineStringConstant ||
  4644. vnode->tokenType == ttHeredocStringConstant )
  4645. {
  4646. asCString str;
  4647. asCScriptNode *snode = vnode->firstChild;
  4648. if( script->code[snode->tokenPos] == '\'' && engine->ep.useCharacterLiterals )
  4649. {
  4650. // Treat the single quoted string as a single character literal
  4651. str.Assign(&script->code[snode->tokenPos+1], snode->tokenLength-2);
  4652. asDWORD val = 0;
  4653. if( str.GetLength() && (unsigned char)str[0] > 127 && engine->ep.scanner == 1 )
  4654. {
  4655. // This is the start of a UTF8 encoded character. We need to decode it
  4656. val = asStringDecodeUTF8(str.AddressOf(), 0);
  4657. if( val == (asDWORD)-1 )
  4658. Error(TXT_INVALID_CHAR_LITERAL, vnode);
  4659. }
  4660. else
  4661. {
  4662. val = ProcessStringConstant(str, snode);
  4663. if( val == (asDWORD)-1 )
  4664. Error(TXT_INVALID_CHAR_LITERAL, vnode);
  4665. }
  4666. ctx->type.SetConstantDW(asCDataType::CreatePrimitive(ttUInt, true), val);
  4667. }
  4668. else
  4669. {
  4670. // Process the string constants
  4671. while( snode )
  4672. {
  4673. asCString cat;
  4674. if( snode->tokenType == ttStringConstant )
  4675. {
  4676. cat.Assign(&script->code[snode->tokenPos+1], snode->tokenLength-2);
  4677. ProcessStringConstant(cat, snode);
  4678. }
  4679. else if( snode->tokenType == ttMultilineStringConstant )
  4680. {
  4681. if( !engine->ep.allowMultilineStrings )
  4682. Error(TXT_MULTILINE_STRINGS_NOT_ALLOWED, snode);
  4683. cat.Assign(&script->code[snode->tokenPos+1], snode->tokenLength-2);
  4684. ProcessStringConstant(cat, snode);
  4685. }
  4686. else if( snode->tokenType == ttHeredocStringConstant )
  4687. {
  4688. cat.Assign(&script->code[snode->tokenPos+3], snode->tokenLength-6);
  4689. ProcessHeredocStringConstant(cat, snode);
  4690. }
  4691. str += cat;
  4692. snode = snode->next;
  4693. }
  4694. // Call the string factory function to create a string object
  4695. asCScriptFunction *descr = engine->stringFactory;
  4696. if( descr == 0 )
  4697. {
  4698. // Error
  4699. Error(TXT_STRINGS_NOT_RECOGNIZED, vnode);
  4700. // Give dummy value
  4701. ctx->type.SetDummy();
  4702. return -1;
  4703. }
  4704. else
  4705. {
  4706. // Register the constant string with the engine
  4707. int id = engine->AddConstantString(str.AddressOf(), str.GetLength());
  4708. ctx->bc.InstrWORD(asBC_STR, (asWORD)id);
  4709. PerformFunctionCall(descr->id, ctx);
  4710. }
  4711. }
  4712. }
  4713. else if( vnode->tokenType == ttNull )
  4714. {
  4715. #ifndef AS_64BIT_PTR
  4716. ctx->bc.InstrDWORD(asBC_PshC4, 0);
  4717. #else
  4718. ctx->bc.InstrQWORD(asBC_PshC8, 0);
  4719. #endif
  4720. ctx->type.SetNullConstant();
  4721. }
  4722. else
  4723. asASSERT(false);
  4724. }
  4725. else if( vnode->nodeType == snFunctionCall )
  4726. {
  4727. bool found = false;
  4728. // Determine the scope resolution
  4729. asCString scope = GetScopeFromNode(vnode);
  4730. if( outFunc && outFunc->objectType && scope != "::" )
  4731. {
  4732. // Check if a class method is being called
  4733. asCScriptNode *nm = vnode->lastChild->prev;
  4734. asCString name;
  4735. name.Assign(&script->code[nm->tokenPos], nm->tokenLength);
  4736. asCArray<int> funcs;
  4737. // If we're compiling a constructor and the name of the function called
  4738. // is 'super' then the base class' constructor is being called.
  4739. // super cannot be called from another scope, i.e. must not be prefixed
  4740. if( m_isConstructor && name == SUPER_TOKEN && nm->prev == 0 )
  4741. {
  4742. // Actually it is the base class' constructor that is being called,
  4743. // but as we won't use the actual function ids here we can take the
  4744. // object's own constructors and avoid the need to check if the
  4745. // object actually derives from any other class
  4746. funcs = outFunc->objectType->beh.constructors;
  4747. // Must not allow calling constructors multiple times
  4748. if( continueLabels.GetLength() > 0 )
  4749. {
  4750. // If a continue label is set we are in a loop
  4751. Error(TXT_CANNOT_CALL_CONSTRUCTOR_IN_LOOPS, vnode);
  4752. }
  4753. else if( breakLabels.GetLength() > 0 )
  4754. {
  4755. // TODO: inheritance: Should eventually allow constructors in switch statements
  4756. // If a break label is set we are either in a loop or a switch statements
  4757. Error(TXT_CANNOT_CALL_CONSTRUCTOR_IN_SWITCH, vnode);
  4758. }
  4759. else if( m_isConstructorCalled )
  4760. {
  4761. Error(TXT_CANNOT_CALL_CONSTRUCTOR_TWICE, vnode);
  4762. }
  4763. m_isConstructorCalled = true;
  4764. }
  4765. else
  4766. builder->GetObjectMethodDescriptions(name.AddressOf(), outFunc->objectType, funcs, false);
  4767. if( funcs.GetLength() )
  4768. {
  4769. asCDataType dt = asCDataType::CreateObject(outFunc->objectType, false);
  4770. // The object pointer is located at stack position 0
  4771. ctx->bc.InstrSHORT(asBC_PSF, 0);
  4772. ctx->type.SetVariable(dt, 0, false);
  4773. ctx->type.dataType.MakeReference(true);
  4774. // TODO: optimize: This adds a CHKREF. Is that really necessary?
  4775. Dereference(ctx, true);
  4776. CompileFunctionCall(vnode, ctx, outFunc->objectType, false, scope);
  4777. found = true;
  4778. }
  4779. }
  4780. if( !found )
  4781. CompileFunctionCall(vnode, ctx, 0, false, scope);
  4782. }
  4783. else if( vnode->nodeType == snConstructCall )
  4784. {
  4785. CompileConstructCall(vnode, ctx);
  4786. }
  4787. else if( vnode->nodeType == snAssignment )
  4788. {
  4789. asSExprContext e(engine);
  4790. CompileAssignment(vnode, &e);
  4791. MergeExprContexts(ctx, &e);
  4792. ctx->type = e.type;
  4793. }
  4794. else if( vnode->nodeType == snCast )
  4795. {
  4796. // Implement the cast operator
  4797. CompileConversion(vnode, ctx);
  4798. }
  4799. else
  4800. asASSERT(false);
  4801. return 0;
  4802. }
  4803. asCString asCCompiler::GetScopeFromNode(asCScriptNode *node)
  4804. {
  4805. asCString scope;
  4806. asCScriptNode *sn = node->firstChild;
  4807. if( sn->tokenType == ttScope )
  4808. {
  4809. // Global scope
  4810. scope = "::";
  4811. sn = sn->next;
  4812. }
  4813. else if( sn->next && sn->next->tokenType == ttScope )
  4814. {
  4815. scope.Assign(&script->code[sn->tokenPos], sn->tokenLength);
  4816. sn = sn->next->next;
  4817. }
  4818. if( scope != "" )
  4819. {
  4820. // We don't support multiple levels of scope yet
  4821. if( sn->next && sn->next->tokenType == ttScope )
  4822. {
  4823. Error(TXT_INVALID_SCOPE, sn->next);
  4824. }
  4825. }
  4826. return scope;
  4827. }
  4828. asUINT asCCompiler::ProcessStringConstant(asCString &cstr, asCScriptNode *node, bool processEscapeSequences)
  4829. {
  4830. int charLiteral = -1;
  4831. // Process escape sequences
  4832. asCArray<char> str((int)cstr.GetLength());
  4833. for( asUINT n = 0; n < cstr.GetLength(); n++ )
  4834. {
  4835. #ifdef AS_DOUBLEBYTE_CHARSET
  4836. // Double-byte charset is only allowed for ASCII and not UTF16 encoded strings
  4837. if( (cstr[n] & 0x80) && engine->ep.scanner == 0 && engine->ep.stringEncoding != 1 )
  4838. {
  4839. // This is the lead character of a double byte character
  4840. // include the trail character without checking it's value.
  4841. str.PushLast(cstr[n]);
  4842. n++;
  4843. str.PushLast(cstr[n]);
  4844. continue;
  4845. }
  4846. #endif
  4847. asUINT val;
  4848. if( processEscapeSequences && cstr[n] == '\\' )
  4849. {
  4850. ++n;
  4851. if( n == cstr.GetLength() )
  4852. {
  4853. if( charLiteral == -1 ) charLiteral = 0;
  4854. return charLiteral;
  4855. }
  4856. // TODO: Consider deprecating use of hexadecimal escape sequences,
  4857. // as they do not guarantee proper unicode sequences
  4858. if( cstr[n] == 'x' || cstr[n] == 'X' )
  4859. {
  4860. ++n;
  4861. if( n == cstr.GetLength() ) break;
  4862. val = 0;
  4863. int c = engine->ep.stringEncoding == 1 ? 4 : 2;
  4864. for( ; c > 0 && n < cstr.GetLength(); c--, n++ )
  4865. {
  4866. if( cstr[n] >= '0' && cstr[n] <= '9' )
  4867. val = val*16 + cstr[n] - '0';
  4868. else if( cstr[n] >= 'a' && cstr[n] <= 'f' )
  4869. val = val*16 + cstr[n] - 'a' + 10;
  4870. else if( cstr[n] >= 'A' && cstr[n] <= 'F' )
  4871. val = val*16 + cstr[n] - 'A' + 10;
  4872. else
  4873. break;
  4874. }
  4875. // Rewind one, since the loop will increment it again
  4876. n--;
  4877. // Hexadecimal escape sequences produce exact value, even if it is not proper unicode chars
  4878. if( engine->ep.stringEncoding == 0 )
  4879. {
  4880. str.PushLast(val);
  4881. }
  4882. else
  4883. {
  4884. #ifndef AS_BIG_ENDIAN
  4885. str.PushLast(val);
  4886. str.PushLast(val>>8);
  4887. #else
  4888. str.PushLast(val>>8);
  4889. str.PushLast(val);
  4890. #endif
  4891. }
  4892. if( charLiteral == -1 ) charLiteral = val;
  4893. continue;
  4894. }
  4895. else if( cstr[n] == 'u' || cstr[n] == 'U' )
  4896. {
  4897. // \u expects 4 hex digits
  4898. // \U expects 8 hex digits
  4899. bool expect2 = cstr[n] == 'u';
  4900. int c = expect2 ? 4 : 8;
  4901. val = 0;
  4902. for( ; c > 0; c-- )
  4903. {
  4904. ++n;
  4905. if( n == cstr.GetLength() ) break;
  4906. if( cstr[n] >= '0' && cstr[n] <= '9' )
  4907. val = val*16 + cstr[n] - '0';
  4908. else if( cstr[n] >= 'a' && cstr[n] <= 'f' )
  4909. val = val*16 + cstr[n] - 'a' + 10;
  4910. else if( cstr[n] >= 'A' && cstr[n] <= 'F' )
  4911. val = val*16 + cstr[n] - 'A' + 10;
  4912. else
  4913. break;
  4914. }
  4915. if( c != 0 )
  4916. {
  4917. // Give warning about invalid code point
  4918. // TODO: Need code position for warning
  4919. asCString msg;
  4920. msg.Format(TXT_INVALID_UNICODE_FORMAT_EXPECTED_d, expect2 ? 4 : 8);
  4921. Warning(msg.AddressOf(), node);
  4922. continue;
  4923. }
  4924. }
  4925. else
  4926. {
  4927. if( cstr[n] == '"' )
  4928. val = '"';
  4929. else if( cstr[n] == '\'' )
  4930. val = '\'';
  4931. else if( cstr[n] == 'n' )
  4932. val = '\n';
  4933. else if( cstr[n] == 'r' )
  4934. val = '\r';
  4935. else if( cstr[n] == 't' )
  4936. val = '\t';
  4937. else if( cstr[n] == '0' )
  4938. val = '\0';
  4939. else if( cstr[n] == '\\' )
  4940. val = '\\';
  4941. else
  4942. {
  4943. // Invalid escape sequence
  4944. Warning(TXT_INVALID_ESCAPE_SEQUENCE, node);
  4945. continue;
  4946. }
  4947. }
  4948. }
  4949. else
  4950. {
  4951. if( engine->ep.scanner == 1 && (cstr[n] & 0x80) )
  4952. {
  4953. unsigned int len;
  4954. val = asStringDecodeUTF8(&cstr[n], &len);
  4955. if( val == 0xFFFFFFFF || len < 0 )
  4956. {
  4957. // Incorrect UTF8 encoding. Use only the first byte
  4958. // TODO: Need code position for warning
  4959. Warning(TXT_INVALID_UNICODE_SEQUENCE_IN_SRC, node);
  4960. val = (unsigned char)cstr[n];
  4961. }
  4962. else
  4963. n += len-1;
  4964. }
  4965. else
  4966. val = (unsigned char)cstr[n];
  4967. }
  4968. // Add the character to the final string
  4969. char encodedValue[5];
  4970. int len;
  4971. if( engine->ep.stringEncoding == 0 )
  4972. {
  4973. len = asStringEncodeUTF8(val, encodedValue);
  4974. }
  4975. else
  4976. {
  4977. len = asStringEncodeUTF16(val, encodedValue);
  4978. }
  4979. if( len < 0 )
  4980. {
  4981. // Give warning about invalid code point
  4982. // TODO: Need code position for warning
  4983. Warning(TXT_INVALID_UNICODE_VALUE, node);
  4984. }
  4985. else
  4986. {
  4987. // Add the encoded value to the final string
  4988. str.Concatenate(encodedValue, len);
  4989. if( charLiteral == -1 ) charLiteral = val;
  4990. }
  4991. }
  4992. cstr.Assign(str.AddressOf(), str.GetLength());
  4993. return charLiteral;
  4994. }
  4995. void asCCompiler::ProcessHeredocStringConstant(asCString &str, asCScriptNode *node)
  4996. {
  4997. // Remove first line if it only contains whitespace
  4998. asUINT start;
  4999. for( start = 0; start < str.GetLength(); start++ )
  5000. {
  5001. if( str[start] == '\n' )
  5002. {
  5003. // Remove the linebreak as well
  5004. start++;
  5005. break;
  5006. }
  5007. if( str[start] != ' ' &&
  5008. str[start] != '\t' &&
  5009. str[start] != '\r' )
  5010. {
  5011. // Don't remove anything
  5012. start = 0;
  5013. break;
  5014. }
  5015. }
  5016. // Remove last line break and the line after that if it only contains whitespaces
  5017. int end;
  5018. for( end = (int)str.GetLength() - 1; end >= 0; end-- )
  5019. {
  5020. if( str[end] == '\n' )
  5021. break;
  5022. if( str[end] != ' ' &&
  5023. str[end] != '\t' &&
  5024. str[end] != '\r' )
  5025. {
  5026. // Don't remove anything
  5027. end = (int)str.GetLength();
  5028. break;
  5029. }
  5030. }
  5031. if( end < 0 ) end = 0;
  5032. asCString tmp;
  5033. tmp.Assign(&str[start], end-start);
  5034. ProcessStringConstant(tmp, node, false);
  5035. str = tmp;
  5036. }
  5037. void asCCompiler::CompileConversion(asCScriptNode *node, asSExprContext *ctx)
  5038. {
  5039. asSExprContext expr(engine);
  5040. asCDataType to;
  5041. bool anyErrors = false;
  5042. EImplicitConv convType;
  5043. if( node->nodeType == snConstructCall )
  5044. {
  5045. convType = asIC_EXPLICIT_VAL_CAST;
  5046. // Verify that there is only one argument
  5047. if( node->lastChild->firstChild == 0 ||
  5048. node->lastChild->firstChild != node->lastChild->lastChild )
  5049. {
  5050. Error(TXT_ONLY_ONE_ARGUMENT_IN_CAST, node->lastChild);
  5051. expr.type.SetDummy();
  5052. anyErrors = true;
  5053. }
  5054. else
  5055. {
  5056. // Compile the expression
  5057. int r = CompileAssignment(node->lastChild->firstChild, &expr);
  5058. if( r < 0 )
  5059. anyErrors = true;
  5060. }
  5061. // Determine the requested type
  5062. to = builder->CreateDataTypeFromNode(node->firstChild, script);
  5063. to.MakeReadOnly(true); // Default to const
  5064. asASSERT(to.IsPrimitive());
  5065. }
  5066. else
  5067. {
  5068. convType = asIC_EXPLICIT_REF_CAST;
  5069. // Compile the expression
  5070. int r = CompileAssignment(node->lastChild, &expr);
  5071. if( r < 0 )
  5072. anyErrors = true;
  5073. else
  5074. {
  5075. // Determine the requested type
  5076. to = builder->CreateDataTypeFromNode(node->firstChild, script);
  5077. to = builder->ModifyDataTypeFromNode(to, node->firstChild->next, script, 0, 0);
  5078. // If the type support object handles, then use it
  5079. if( to.SupportHandles() )
  5080. {
  5081. to.MakeHandle(true);
  5082. }
  5083. else if( !to.IsObjectHandle() )
  5084. {
  5085. // The cast<type> operator can only be used for reference casts
  5086. Error(TXT_ILLEGAL_TARGET_TYPE_FOR_REF_CAST, node->firstChild);
  5087. anyErrors = true;
  5088. }
  5089. }
  5090. }
  5091. if( anyErrors )
  5092. {
  5093. // Assume that the error can be fixed and allow the compilation to continue
  5094. ctx->type.SetConstantDW(to, 0);
  5095. return;
  5096. }
  5097. // We don't want a reference
  5098. if( expr.type.dataType.IsReference() )
  5099. {
  5100. if( expr.type.dataType.IsObject() )
  5101. Dereference(&expr, true);
  5102. else
  5103. ConvertToVariable(&expr);
  5104. }
  5105. ImplicitConversion(&expr, to, node, convType);
  5106. IsVariableInitialized(&expr.type, node);
  5107. // If no type conversion is really tried ignore it
  5108. if( to == expr.type.dataType )
  5109. {
  5110. // This will keep information about constant type
  5111. MergeExprContexts(ctx, &expr);
  5112. ctx->type = expr.type;
  5113. return;
  5114. }
  5115. if( to.IsEqualExceptConst(expr.type.dataType) && to.IsPrimitive() )
  5116. {
  5117. MergeExprContexts(ctx, &expr);
  5118. ctx->type = expr.type;
  5119. ctx->type.dataType.MakeReadOnly(true);
  5120. return;
  5121. }
  5122. // The implicit conversion already does most of the conversions permitted,
  5123. // here we'll only treat those conversions that require an explicit cast.
  5124. bool conversionOK = false;
  5125. if( !expr.type.isConstant )
  5126. {
  5127. if( !expr.type.dataType.IsObject() )
  5128. ConvertToTempVariable(&expr);
  5129. if( to.IsObjectHandle() &&
  5130. expr.type.dataType.IsObjectHandle() &&
  5131. !(!to.IsHandleToConst() && expr.type.dataType.IsHandleToConst()) )
  5132. {
  5133. conversionOK = CompileRefCast(&expr, to, true, node);
  5134. MergeExprContexts(ctx, &expr);
  5135. ctx->type = expr.type;
  5136. }
  5137. }
  5138. if( conversionOK )
  5139. return;
  5140. // Conversion not available
  5141. ctx->type.SetDummy();
  5142. asCString strTo, strFrom;
  5143. strTo = to.Format();
  5144. strFrom = expr.type.dataType.Format();
  5145. asCString msg;
  5146. msg.Format(TXT_NO_CONVERSION_s_TO_s, strFrom.AddressOf(), strTo.AddressOf());
  5147. Error(msg.AddressOf(), node);
  5148. }
  5149. void asCCompiler::AfterFunctionCall(int funcID, asCArray<asSExprContext*> &args, asSExprContext *ctx, bool deferAll)
  5150. {
  5151. asCScriptFunction *descr = builder->GetFunctionDescription(funcID);
  5152. // Parameters that are sent by reference should be assigned
  5153. // to the evaluated expression if it is an lvalue
  5154. // Evaluate the arguments from last to first
  5155. int n = (int)descr->parameterTypes.GetLength() - 1;
  5156. for( ; n >= 0; n-- )
  5157. {
  5158. if( (descr->parameterTypes[n].IsReference() && (descr->inOutFlags[n] & asTM_OUTREF)) ||
  5159. (descr->parameterTypes[n].IsObject() && deferAll) )
  5160. {
  5161. asASSERT( !(descr->parameterTypes[n].IsReference() && (descr->inOutFlags[n] == asTM_OUTREF)) || args[n]->origExpr );
  5162. // For &inout, only store the argument if it is for a temporary variable
  5163. if( engine->ep.allowUnsafeReferences ||
  5164. descr->inOutFlags[n] != asTM_INOUTREF || args[n]->type.isTemporary )
  5165. {
  5166. // Store the argument for later processing
  5167. asSDeferredParam outParam;
  5168. outParam.argNode = args[n]->exprNode;
  5169. outParam.argType = args[n]->type;
  5170. outParam.argInOutFlags = descr->inOutFlags[n];
  5171. outParam.origExpr = args[n]->origExpr;
  5172. ctx->deferredParams.PushLast(outParam);
  5173. }
  5174. }
  5175. else
  5176. {
  5177. // Release the temporary variable now
  5178. ReleaseTemporaryVariable(args[n]->type, &ctx->bc);
  5179. }
  5180. }
  5181. }
  5182. void asCCompiler::ProcessDeferredParams(asSExprContext *ctx)
  5183. {
  5184. if( isProcessingDeferredParams ) return;
  5185. isProcessingDeferredParams = true;
  5186. for( asUINT n = 0; n < ctx->deferredParams.GetLength(); n++ )
  5187. {
  5188. asSDeferredParam outParam = ctx->deferredParams[n];
  5189. if( outParam.argInOutFlags < asTM_OUTREF ) // &in, or not reference
  5190. {
  5191. // Just release the variable
  5192. ReleaseTemporaryVariable(outParam.argType, &ctx->bc);
  5193. }
  5194. else if( outParam.argInOutFlags == asTM_OUTREF )
  5195. {
  5196. asSExprContext *expr = outParam.origExpr;
  5197. if( outParam.argType.dataType.IsObjectHandle() )
  5198. {
  5199. // Implicitly convert the value to a handle
  5200. if( expr->type.dataType.IsObjectHandle() )
  5201. expr->type.isExplicitHandle = true;
  5202. }
  5203. // Verify that the expression result in a lvalue, or a property accessor
  5204. if( IsLValue(expr->type) || expr->property_get || expr->property_set )
  5205. {
  5206. asSExprContext rctx(engine);
  5207. rctx.type = outParam.argType;
  5208. if( rctx.type.dataType.IsPrimitive() )
  5209. rctx.type.dataType.MakeReference(false);
  5210. else
  5211. {
  5212. rctx.bc.InstrSHORT(asBC_PSF, outParam.argType.stackOffset);
  5213. rctx.type.dataType.MakeReference(true);
  5214. if( expr->type.isExplicitHandle )
  5215. rctx.type.isExplicitHandle = true;
  5216. }
  5217. asSExprContext o(engine);
  5218. DoAssignment(&o, expr, &rctx, outParam.argNode, outParam.argNode, ttAssignment, outParam.argNode);
  5219. if( !o.type.dataType.IsPrimitive() ) o.bc.Pop(AS_PTR_SIZE);
  5220. MergeExprContexts(ctx, &o);
  5221. }
  5222. else
  5223. {
  5224. // We must still evaluate the expression
  5225. MergeExprContexts(ctx, expr);
  5226. if( !expr->type.isConstant )
  5227. ctx->bc.Pop(expr->type.dataType.GetSizeOnStackDWords());
  5228. // Give a warning
  5229. Warning(TXT_ARG_NOT_LVALUE, outParam.argNode);
  5230. ReleaseTemporaryVariable(outParam.argType, &ctx->bc);
  5231. }
  5232. ReleaseTemporaryVariable(expr->type, &ctx->bc);
  5233. // Delete the original expression context
  5234. asDELETE(expr,asSExprContext);
  5235. }
  5236. else // &inout
  5237. {
  5238. if( outParam.argType.isTemporary )
  5239. ReleaseTemporaryVariable(outParam.argType, &ctx->bc);
  5240. else if( !outParam.argType.isVariable )
  5241. {
  5242. if( outParam.argType.dataType.IsObject() &&
  5243. outParam.argType.dataType.GetBehaviour()->addref &&
  5244. outParam.argType.dataType.GetBehaviour()->release )
  5245. {
  5246. // Release the object handle that was taken to guarantee the reference
  5247. ReleaseTemporaryVariable(outParam.argType, &ctx->bc);
  5248. }
  5249. }
  5250. }
  5251. }
  5252. ctx->deferredParams.SetLength(0);
  5253. isProcessingDeferredParams = false;
  5254. }
  5255. void asCCompiler::CompileConstructCall(asCScriptNode *node, asSExprContext *ctx)
  5256. {
  5257. // The first node is a datatype node
  5258. asCString name;
  5259. asCTypeInfo tempObj;
  5260. asCArray<int> funcs;
  5261. // It is possible that the name is really a constructor
  5262. asCDataType dt;
  5263. dt = builder->CreateDataTypeFromNode(node->firstChild, script);
  5264. if( dt.IsPrimitive() )
  5265. {
  5266. // This is a cast to a primitive type
  5267. CompileConversion(node, ctx);
  5268. return;
  5269. }
  5270. if( globalExpression )
  5271. {
  5272. Error(TXT_FUNCTION_IN_GLOBAL_EXPR, node);
  5273. // Output dummy code
  5274. ctx->type.SetDummy();
  5275. return;
  5276. }
  5277. // Compile the arguments
  5278. asCArray<asSExprContext *> args;
  5279. asCArray<asCTypeInfo> temporaryVariables;
  5280. if( CompileArgumentList(node->lastChild, args) >= 0 )
  5281. {
  5282. // Check for a value cast behaviour
  5283. if( args.GetLength() == 1 && args[0]->type.dataType.GetObjectType() )
  5284. {
  5285. asSExprContext conv(engine);
  5286. conv.type = args[0]->type;
  5287. ImplicitConversion(&conv, dt, node->lastChild, asIC_EXPLICIT_VAL_CAST, false);
  5288. if( conv.type.dataType.IsEqualExceptRef(dt) )
  5289. {
  5290. ImplicitConversion(args[0], dt, node->lastChild, asIC_EXPLICIT_VAL_CAST);
  5291. ctx->bc.AddCode(&args[0]->bc);
  5292. ctx->type = args[0]->type;
  5293. asDELETE(args[0],asSExprContext);
  5294. return;
  5295. }
  5296. }
  5297. // Check for possible constructor/factory
  5298. name = dt.Format();
  5299. asSTypeBehaviour *beh = dt.GetBehaviour();
  5300. if( !(dt.GetObjectType()->flags & asOBJ_REF) )
  5301. {
  5302. funcs = beh->constructors;
  5303. // Value types and script types are allocated through the constructor
  5304. tempObj.dataType = dt;
  5305. tempObj.stackOffset = (short)AllocateVariable(dt, true);
  5306. tempObj.dataType.MakeReference(true);
  5307. tempObj.isTemporary = true;
  5308. tempObj.isVariable = true;
  5309. // Push the address of the object on the stack
  5310. ctx->bc.InstrSHORT(asBC_VAR, tempObj.stackOffset);
  5311. }
  5312. else
  5313. {
  5314. funcs = beh->factories;
  5315. }
  5316. // Special case: Allow calling func(void) with a void expression.
  5317. if( args.GetLength() == 1 && args[0]->type.dataType == asCDataType::CreatePrimitive(ttVoid, false) )
  5318. {
  5319. // Evaluate the expression before the function call
  5320. MergeExprContexts(ctx, args[0]);
  5321. asDELETE(args[0],asSExprContext);
  5322. args.SetLength(0);
  5323. }
  5324. // Special case: If this is an object constructor and there are no arguments use the default constructor.
  5325. // If none has been registered, just allocate the variable and push it on the stack.
  5326. if( args.GetLength() == 0 )
  5327. {
  5328. asSTypeBehaviour *beh = tempObj.dataType.GetBehaviour();
  5329. if( beh && beh->construct == 0 && !(dt.GetObjectType()->flags & asOBJ_REF) )
  5330. {
  5331. // Call the default constructor
  5332. ctx->type = tempObj;
  5333. asASSERT(ctx->bc.GetLastInstr() == asBC_VAR);
  5334. ctx->bc.RemoveLastInstr();
  5335. CallDefaultConstructor(tempObj.dataType, tempObj.stackOffset, &ctx->bc, node);
  5336. // Push the reference on the stack
  5337. ctx->bc.InstrSHORT(asBC_PSF, tempObj.stackOffset);
  5338. return;
  5339. }
  5340. }
  5341. MatchFunctions(funcs, args, node, name.AddressOf(), NULL, false);
  5342. if( funcs.GetLength() != 1 )
  5343. {
  5344. // The error was reported by MatchFunctions()
  5345. // Dummy value
  5346. ctx->type.SetDummy();
  5347. }
  5348. else
  5349. {
  5350. asCByteCode objBC(engine);
  5351. PrepareFunctionCall(funcs[0], &ctx->bc, args);
  5352. MoveArgsToStack(funcs[0], &ctx->bc, args, false);
  5353. if( !(dt.GetObjectType()->flags & asOBJ_REF) )
  5354. {
  5355. int offset = 0;
  5356. asCScriptFunction *descr = builder->GetFunctionDescription(funcs[0]);
  5357. for( asUINT n = 0; n < args.GetLength(); n++ )
  5358. offset += descr->parameterTypes[n].GetSizeOnStackDWords();
  5359. ctx->bc.InstrWORD(asBC_GETREF, (asWORD)offset);
  5360. PerformFunctionCall(funcs[0], ctx, true, &args, tempObj.dataType.GetObjectType());
  5361. // The constructor doesn't return anything,
  5362. // so we have to manually inform the type of
  5363. // the return value
  5364. ctx->type = tempObj;
  5365. // Push the address of the object on the stack again
  5366. ctx->bc.InstrSHORT(asBC_PSF, tempObj.stackOffset);
  5367. }
  5368. else
  5369. {
  5370. // Call the factory to create the reference type
  5371. PerformFunctionCall(funcs[0], ctx, false, &args);
  5372. }
  5373. }
  5374. }
  5375. else
  5376. {
  5377. // Failed to compile the argument list, set the result to the dummy type
  5378. ctx->type.SetDummy();
  5379. }
  5380. // Cleanup
  5381. for( asUINT n = 0; n < args.GetLength(); n++ )
  5382. if( args[n] )
  5383. {
  5384. asDELETE(args[n],asSExprContext);
  5385. }
  5386. }
  5387. void asCCompiler::CompileFunctionCall(asCScriptNode *node, asSExprContext *ctx, asCObjectType *objectType, bool objIsConst, const asCString &scope)
  5388. {
  5389. asCString name;
  5390. asCTypeInfo tempObj;
  5391. asCArray<int> funcs;
  5392. asCScriptNode *nm = node->lastChild->prev;
  5393. name.Assign(&script->code[nm->tokenPos], nm->tokenLength);
  5394. if( objectType )
  5395. {
  5396. // If we're compiling a constructor and the name of the function is super then
  5397. // the constructor of the base class is being called.
  5398. // super cannot be prefixed with a scope operator
  5399. if( m_isConstructor && name == SUPER_TOKEN && nm->prev == 0 )
  5400. {
  5401. // If the class is not derived from anyone else, calling super should give an error
  5402. if( objectType->derivedFrom )
  5403. funcs = objectType->derivedFrom->beh.constructors;
  5404. }
  5405. else
  5406. builder->GetObjectMethodDescriptions(name.AddressOf(), objectType, funcs, objIsConst, scope);
  5407. }
  5408. else
  5409. builder->GetFunctionDescriptions(name.AddressOf(), funcs);
  5410. if( globalExpression )
  5411. {
  5412. Error(TXT_FUNCTION_IN_GLOBAL_EXPR, node);
  5413. // Output dummy code
  5414. ctx->type.SetDummy();
  5415. return;
  5416. }
  5417. // Compile the arguments
  5418. asCArray<asSExprContext *> args;
  5419. asCArray<asCTypeInfo> temporaryVariables;
  5420. if( CompileArgumentList(node->lastChild, args) >= 0 )
  5421. {
  5422. // Special case: Allow calling func(void) with a void expression.
  5423. if( args.GetLength() == 1 && args[0]->type.dataType == asCDataType::CreatePrimitive(ttVoid, false) )
  5424. {
  5425. // Evaluate the expression before the function call
  5426. MergeExprContexts(ctx, args[0]);
  5427. asDELETE(args[0],asSExprContext);
  5428. args.SetLength(0);
  5429. }
  5430. MatchFunctions(funcs, args, node, name.AddressOf(), objectType, objIsConst, false, true, scope);
  5431. if( funcs.GetLength() != 1 )
  5432. {
  5433. // The error was reported by MatchFunctions()
  5434. // Dummy value
  5435. ctx->type.SetDummy();
  5436. }
  5437. else
  5438. {
  5439. MakeFunctionCall(ctx, funcs[0], objectType, args, node);
  5440. }
  5441. }
  5442. else
  5443. {
  5444. // Failed to compile the argument list, set the dummy type and continue compilation
  5445. ctx->type.SetDummy();
  5446. }
  5447. // Cleanup
  5448. for( asUINT n = 0; n < args.GetLength(); n++ )
  5449. if( args[n] )
  5450. {
  5451. asDELETE(args[n],asSExprContext);
  5452. }
  5453. }
  5454. int asCCompiler::CompileExpressionPreOp(asCScriptNode *node, asSExprContext *ctx)
  5455. {
  5456. int op = node->tokenType;
  5457. IsVariableInitialized(&ctx->type, node);
  5458. if( op == ttHandle )
  5459. {
  5460. // Verify that the type allow its handle to be taken
  5461. if( ctx->type.isExplicitHandle || !ctx->type.dataType.IsObject() || !ctx->type.dataType.GetObjectType()->beh.addref || !ctx->type.dataType.GetObjectType()->beh.release )
  5462. {
  5463. Error(TXT_OBJECT_HANDLE_NOT_SUPPORTED, node);
  5464. return -1;
  5465. }
  5466. // Objects that are not local variables are not references
  5467. if( !ctx->type.dataType.IsReference() && !(ctx->type.dataType.IsObject() && !ctx->type.isVariable) )
  5468. {
  5469. Error(TXT_NOT_VALID_REFERENCE, node);
  5470. return -1;
  5471. }
  5472. // If this is really an object then the handle created is a const handle
  5473. bool makeConst = !ctx->type.dataType.IsObjectHandle();
  5474. // Mark the type as an object handle
  5475. ctx->type.dataType.MakeHandle(true);
  5476. ctx->type.isExplicitHandle = true;
  5477. if( makeConst )
  5478. ctx->type.dataType.MakeReadOnly(true);
  5479. }
  5480. else if( (op == ttMinus || op == ttBitNot) && ctx->type.dataType.IsObject() )
  5481. {
  5482. // Look for the opNeg or opCom methods
  5483. const char *opName = 0;
  5484. switch( op )
  5485. {
  5486. case ttMinus: opName = "opNeg"; break;
  5487. case ttBitNot: opName = "opCom"; break;
  5488. }
  5489. if( opName )
  5490. {
  5491. ProcessPropertyGetAccessor(ctx, node);
  5492. // Is it a const value?
  5493. bool isConst = false;
  5494. if( ctx->type.dataType.IsObjectHandle() )
  5495. isConst = ctx->type.dataType.IsHandleToConst();
  5496. else
  5497. isConst = ctx->type.dataType.IsReadOnly();
  5498. // TODO: If the value isn't const, then first try to find the non const method, and if not found try to find the const method
  5499. // Find the correct method
  5500. asCArray<int> funcs;
  5501. asCObjectType *ot = ctx->type.dataType.GetObjectType();
  5502. for( asUINT n = 0; n < ot->methods.GetLength(); n++ )
  5503. {
  5504. asCScriptFunction *func = engine->scriptFunctions[ot->methods[n]];
  5505. if( func->name == opName &&
  5506. func->parameterTypes.GetLength() == 0 &&
  5507. (!isConst || func->isReadOnly) )
  5508. {
  5509. funcs.PushLast(func->id);
  5510. }
  5511. }
  5512. // Did we find the method?
  5513. if( funcs.GetLength() == 1 )
  5514. {
  5515. asCTypeInfo objType = ctx->type;
  5516. asCArray<asSExprContext *> args;
  5517. MakeFunctionCall(ctx, funcs[0], objType.dataType.GetObjectType(), args, node);
  5518. ReleaseTemporaryVariable(objType, &ctx->bc);
  5519. return 0;
  5520. }
  5521. else if( funcs.GetLength() == 0 )
  5522. {
  5523. asCString str;
  5524. str = asCString(opName) + "()";
  5525. if( isConst )
  5526. str += " const";
  5527. str.Format(TXT_FUNCTION_s_NOT_FOUND, str.AddressOf());
  5528. Error(str.AddressOf(), node);
  5529. ctx->type.SetDummy();
  5530. return -1;
  5531. }
  5532. else if( funcs.GetLength() > 1 )
  5533. {
  5534. Error(TXT_MORE_THAN_ONE_MATCHING_OP, node);
  5535. PrintMatchingFuncs(funcs, node);
  5536. ctx->type.SetDummy();
  5537. return -1;
  5538. }
  5539. }
  5540. }
  5541. else if( op == ttPlus || op == ttMinus )
  5542. {
  5543. ProcessPropertyGetAccessor(ctx, node);
  5544. asCDataType to = ctx->type.dataType;
  5545. // TODO: The case -2147483648 gives an unecessary warning of changed sign for implicit conversion
  5546. if( ctx->type.dataType.IsUnsignedType() || ctx->type.dataType.IsEnumType() )
  5547. {
  5548. if( ctx->type.dataType.GetSizeInMemoryBytes() == 1 )
  5549. to = asCDataType::CreatePrimitive(ttInt8, false);
  5550. else if( ctx->type.dataType.GetSizeInMemoryBytes() == 2 )
  5551. to = asCDataType::CreatePrimitive(ttInt16, false);
  5552. else if( ctx->type.dataType.GetSizeInMemoryBytes() == 4 )
  5553. to = asCDataType::CreatePrimitive(ttInt, false);
  5554. else if( ctx->type.dataType.GetSizeInMemoryBytes() == 8 )
  5555. to = asCDataType::CreatePrimitive(ttInt64, false);
  5556. else
  5557. {
  5558. Error(TXT_INVALID_TYPE, node);
  5559. return -1;
  5560. }
  5561. }
  5562. if( ctx->type.dataType.IsReference() ) ConvertToVariable(ctx);
  5563. ImplicitConversion(ctx, to, node, asIC_IMPLICIT_CONV);
  5564. if( !ctx->type.isConstant )
  5565. {
  5566. ConvertToTempVariable(ctx);
  5567. if( op == ttMinus )
  5568. {
  5569. if( ctx->type.dataType.IsIntegerType() && ctx->type.dataType.GetSizeInMemoryDWords() == 1 )
  5570. ctx->bc.InstrSHORT(asBC_NEGi, ctx->type.stackOffset);
  5571. else if( ctx->type.dataType.IsIntegerType() && ctx->type.dataType.GetSizeInMemoryDWords() == 2 )
  5572. ctx->bc.InstrSHORT(asBC_NEGi64, ctx->type.stackOffset);
  5573. else if( ctx->type.dataType.IsFloatType() )
  5574. ctx->bc.InstrSHORT(asBC_NEGf, ctx->type.stackOffset);
  5575. else if( ctx->type.dataType.IsDoubleType() )
  5576. ctx->bc.InstrSHORT(asBC_NEGd, ctx->type.stackOffset);
  5577. else
  5578. {
  5579. Error(TXT_ILLEGAL_OPERATION, node);
  5580. return -1;
  5581. }
  5582. return 0;
  5583. }
  5584. }
  5585. else
  5586. {
  5587. if( op == ttMinus )
  5588. {
  5589. if( ctx->type.dataType.IsIntegerType() && ctx->type.dataType.GetSizeInMemoryDWords() == 1 )
  5590. ctx->type.intValue = -ctx->type.intValue;
  5591. else if( ctx->type.dataType.IsIntegerType() && ctx->type.dataType.GetSizeInMemoryDWords() == 2 )
  5592. ctx->type.qwordValue = -(asINT64)ctx->type.qwordValue;
  5593. else if( ctx->type.dataType.IsFloatType() )
  5594. ctx->type.floatValue = -ctx->type.floatValue;
  5595. else if( ctx->type.dataType.IsDoubleType() )
  5596. ctx->type.doubleValue = -ctx->type.doubleValue;
  5597. else
  5598. {
  5599. Error(TXT_ILLEGAL_OPERATION, node);
  5600. return -1;
  5601. }
  5602. return 0;
  5603. }
  5604. }
  5605. if( op == ttPlus )
  5606. {
  5607. if( !ctx->type.dataType.IsIntegerType() &&
  5608. !ctx->type.dataType.IsFloatType() &&
  5609. !ctx->type.dataType.IsDoubleType() )
  5610. {
  5611. Error(TXT_ILLEGAL_OPERATION, node);
  5612. return -1;
  5613. }
  5614. }
  5615. }
  5616. else if( op == ttNot )
  5617. {
  5618. if( ctx->type.dataType.IsEqualExceptRefAndConst(asCDataType::CreatePrimitive(ttBool, true)) )
  5619. {
  5620. if( ctx->type.isConstant )
  5621. {
  5622. ctx->type.dwordValue = (ctx->type.dwordValue == 0 ? VALUE_OF_BOOLEAN_TRUE : 0);
  5623. return 0;
  5624. }
  5625. ProcessPropertyGetAccessor(ctx, node);
  5626. ConvertToTempVariable(ctx);
  5627. ctx->bc.InstrSHORT(asBC_NOT, ctx->type.stackOffset);
  5628. }
  5629. else
  5630. {
  5631. Error(TXT_ILLEGAL_OPERATION, node);
  5632. return -1;
  5633. }
  5634. }
  5635. else if( op == ttBitNot )
  5636. {
  5637. ProcessPropertyGetAccessor(ctx, node);
  5638. asCDataType to = ctx->type.dataType;
  5639. if( ctx->type.dataType.IsIntegerType() || ctx->type.dataType.IsEnumType() )
  5640. {
  5641. if( ctx->type.dataType.GetSizeInMemoryBytes() == 1 )
  5642. to = asCDataType::CreatePrimitive(ttUInt8, false);
  5643. else if( ctx->type.dataType.GetSizeInMemoryBytes() == 2 )
  5644. to = asCDataType::CreatePrimitive(ttUInt16, false);
  5645. else if( ctx->type.dataType.GetSizeInMemoryBytes() == 4 )
  5646. to = asCDataType::CreatePrimitive(ttUInt, false);
  5647. else if( ctx->type.dataType.GetSizeInMemoryBytes() == 8 )
  5648. to = asCDataType::CreatePrimitive(ttUInt64, false);
  5649. else
  5650. {
  5651. Error(TXT_INVALID_TYPE, node);
  5652. return -1;
  5653. }
  5654. }
  5655. if( ctx->type.dataType.IsReference() ) ConvertToVariable(ctx);
  5656. ImplicitConversion(ctx, to, node, asIC_IMPLICIT_CONV);
  5657. if( ctx->type.dataType.IsUnsignedType() )
  5658. {
  5659. if( ctx->type.isConstant )
  5660. {
  5661. ctx->type.qwordValue = ~ctx->type.qwordValue;
  5662. return 0;
  5663. }
  5664. ConvertToTempVariable(ctx);
  5665. if( ctx->type.dataType.GetSizeInMemoryDWords() == 1 )
  5666. ctx->bc.InstrSHORT(asBC_BNOT, ctx->type.stackOffset);
  5667. else
  5668. ctx->bc.InstrSHORT(asBC_BNOT64, ctx->type.stackOffset);
  5669. }
  5670. else
  5671. {
  5672. Error(TXT_ILLEGAL_OPERATION, node);
  5673. return -1;
  5674. }
  5675. }
  5676. else if( op == ttInc || op == ttDec )
  5677. {
  5678. // Need a reference to the primitive that will be updated
  5679. // The result of this expression is the same reference as before
  5680. if( globalExpression )
  5681. {
  5682. Error(TXT_INC_OP_IN_GLOBAL_EXPR, node);
  5683. return -1;
  5684. }
  5685. // Make sure the reference isn't a temporary variable
  5686. if( ctx->type.isTemporary )
  5687. {
  5688. Error(TXT_REF_IS_TEMP, node);
  5689. return -1;
  5690. }
  5691. if( ctx->type.dataType.IsReadOnly() )
  5692. {
  5693. Error(TXT_REF_IS_READ_ONLY, node);
  5694. return -1;
  5695. }
  5696. if( ctx->type.isVariable )
  5697. ConvertToReference(ctx);
  5698. else if( !ctx->type.dataType.IsReference() )
  5699. {
  5700. Error(TXT_NOT_VALID_REFERENCE, node);
  5701. return -1;
  5702. }
  5703. if( ctx->type.dataType.IsEqualExceptRef(asCDataType::CreatePrimitive(ttInt64, false)) ||
  5704. ctx->type.dataType.IsEqualExceptRef(asCDataType::CreatePrimitive(ttUInt64, false)) )
  5705. {
  5706. if( op == ttInc )
  5707. ctx->bc.Instr(asBC_INCi64);
  5708. else
  5709. ctx->bc.Instr(asBC_DECi64);
  5710. }
  5711. else if( ctx->type.dataType.IsEqualExceptRef(asCDataType::CreatePrimitive(ttInt, false)) ||
  5712. ctx->type.dataType.IsEqualExceptRef(asCDataType::CreatePrimitive(ttUInt, false)) )
  5713. {
  5714. if( op == ttInc )
  5715. ctx->bc.Instr(asBC_INCi);
  5716. else
  5717. ctx->bc.Instr(asBC_DECi);
  5718. }
  5719. else if( ctx->type.dataType.IsEqualExceptRef(asCDataType::CreatePrimitive(ttInt16, false)) ||
  5720. ctx->type.dataType.IsEqualExceptRef(asCDataType::CreatePrimitive(ttUInt16, false)) )
  5721. {
  5722. if( op == ttInc )
  5723. ctx->bc.Instr(asBC_INCi16);
  5724. else
  5725. ctx->bc.Instr(asBC_DECi16);
  5726. }
  5727. else if( ctx->type.dataType.IsEqualExceptRef(asCDataType::CreatePrimitive(ttInt8, false)) ||
  5728. ctx->type.dataType.IsEqualExceptRef(asCDataType::CreatePrimitive(ttUInt8, false)) )
  5729. {
  5730. if( op == ttInc )
  5731. ctx->bc.Instr(asBC_INCi8);
  5732. else
  5733. ctx->bc.Instr(asBC_DECi8);
  5734. }
  5735. else if( ctx->type.dataType.IsEqualExceptRef(asCDataType::CreatePrimitive(ttFloat, false)) )
  5736. {
  5737. if( op == ttInc )
  5738. ctx->bc.Instr(asBC_INCf);
  5739. else
  5740. ctx->bc.Instr(asBC_DECf);
  5741. }
  5742. else if( ctx->type.dataType.IsEqualExceptRef(asCDataType::CreatePrimitive(ttDouble, false)) )
  5743. {
  5744. if( op == ttInc )
  5745. ctx->bc.Instr(asBC_INCd);
  5746. else
  5747. ctx->bc.Instr(asBC_DECd);
  5748. }
  5749. else
  5750. {
  5751. Error(TXT_ILLEGAL_OPERATION, node);
  5752. return -1;
  5753. }
  5754. }
  5755. else
  5756. {
  5757. // Unknown operator
  5758. asASSERT(false);
  5759. return -1;
  5760. }
  5761. return 0;
  5762. }
  5763. void asCCompiler::ConvertToReference(asSExprContext *ctx)
  5764. {
  5765. if( ctx->type.isVariable )
  5766. {
  5767. ctx->bc.InstrSHORT(asBC_LDV, ctx->type.stackOffset);
  5768. ctx->type.dataType.MakeReference(true);
  5769. ctx->type.Set(ctx->type.dataType);
  5770. }
  5771. }
  5772. int asCCompiler::FindPropertyAccessor(const asCString &name, asSExprContext *ctx, asCScriptNode *node)
  5773. {
  5774. if( !ctx->type.dataType.IsObject() )
  5775. return 0;
  5776. // Check if the object as any methods with the property name prefixed by get_ or set_
  5777. int getId = 0, setId = 0;
  5778. asCString getName = "get_" + name;
  5779. asCString setName = "set_" + name;
  5780. asCArray<int> multipleGetFuncs, multipleSetFuncs;
  5781. asCObjectType *ot = ctx->type.dataType.GetObjectType();
  5782. for( asUINT n = 0; n < ot->methods.GetLength(); n++ )
  5783. {
  5784. asCScriptFunction *f = engine->scriptFunctions[ot->methods[n]];
  5785. if( f->name == getName && f->parameterTypes.GetLength() == 0 )
  5786. {
  5787. if( getId == 0 )
  5788. getId = ot->methods[n];
  5789. else
  5790. {
  5791. if( multipleGetFuncs.GetLength() == 0 )
  5792. multipleGetFuncs.PushLast(getId);
  5793. multipleGetFuncs.PushLast(ot->methods[n]);
  5794. }
  5795. }
  5796. // TODO: getset: If the parameter is a reference, it must not be an out reference. Should we allow inout ref?
  5797. if( f->name == setName && f->parameterTypes.GetLength() == 1 )
  5798. {
  5799. if( setId == 0 )
  5800. setId = ot->methods[n];
  5801. else
  5802. {
  5803. if( multipleSetFuncs.GetLength() == 0 )
  5804. multipleSetFuncs.PushLast(setId);
  5805. multipleSetFuncs.PushLast(ot->methods[n]);
  5806. }
  5807. }
  5808. }
  5809. // Check for multiple matches
  5810. if( multipleGetFuncs.GetLength() > 0 )
  5811. {
  5812. asCString str;
  5813. str.Format(TXT_MULTIPLE_PROP_GET_ACCESSOR_FOR_s, name.AddressOf());
  5814. Error(str.AddressOf(), node);
  5815. PrintMatchingFuncs(multipleGetFuncs, node);
  5816. return -1;
  5817. }
  5818. if( multipleSetFuncs.GetLength() > 0 )
  5819. {
  5820. asCString str;
  5821. str.Format(TXT_MULTIPLE_PROP_SET_ACCESSOR_FOR_s, name.AddressOf());
  5822. Error(str.AddressOf(), node);
  5823. PrintMatchingFuncs(multipleSetFuncs, node);
  5824. return -1;
  5825. }
  5826. // Check for type compatibility between get and set accessor
  5827. if( getId && setId )
  5828. {
  5829. asCScriptFunction *getFunc = engine->scriptFunctions[getId];
  5830. asCScriptFunction *setFunc = engine->scriptFunctions[setId];
  5831. if( !getFunc->returnType.IsEqualExceptRefAndConst(setFunc->parameterTypes[0]) )
  5832. {
  5833. asCString str;
  5834. str.Format(TXT_GET_SET_ACCESSOR_TYPE_MISMATCH_FOR_s, name.AddressOf());
  5835. Error(str.AddressOf(), node);
  5836. asCArray<int> funcs;
  5837. funcs.PushLast(getId);
  5838. funcs.PushLast(setId);
  5839. PrintMatchingFuncs(funcs, node);
  5840. return -1;
  5841. }
  5842. }
  5843. if( getId || setId )
  5844. {
  5845. // Property accessors were found, but we don't know which is to be used yet, so
  5846. // we just prepare the bytecode for the method call, and then store the function ids
  5847. // so that the right one can be used when we get there.
  5848. ctx->property_get = getId;
  5849. ctx->property_set = setId;
  5850. // If the object is read-only then we need to remember
  5851. if( (!ctx->type.dataType.IsObjectHandle() && ctx->type.dataType.IsReadOnly()) ||
  5852. (ctx->type.dataType.IsObjectHandle() && ctx->type.dataType.IsHandleToConst()) )
  5853. ctx->property_const = true;
  5854. else
  5855. ctx->property_const = false;
  5856. // If the object is a handle then we need to remember that
  5857. ctx->property_handle = ctx->type.dataType.IsObjectHandle();
  5858. asCDataType dt;
  5859. if( getId )
  5860. dt = engine->scriptFunctions[getId]->returnType;
  5861. else
  5862. dt = engine->scriptFunctions[setId]->parameterTypes[0];
  5863. // Just change the type, the context must still maintain information
  5864. // about previous variable offset and the indicator of temporary variable.
  5865. int offset = ctx->type.stackOffset;
  5866. bool isTemp = ctx->type.isTemporary;
  5867. ctx->type.Set(dt);
  5868. ctx->type.stackOffset = offset;
  5869. ctx->type.isTemporary = isTemp;
  5870. return 1;
  5871. }
  5872. // No accessor was found
  5873. return 0;
  5874. }
  5875. int asCCompiler::ProcessPropertySetAccessor(asSExprContext *ctx, asSExprContext *arg, asCScriptNode *node)
  5876. {
  5877. // TODO: A lot of this code is similar to ProcessPropertyGetAccessor. Can we unify them?
  5878. if( !ctx->property_set )
  5879. {
  5880. Error(TXT_PROPERTY_HAS_NO_SET_ACCESSOR, node);
  5881. return -1;
  5882. }
  5883. // Setup the context with the original type so the method call gets built correctly
  5884. asCTypeInfo objType = ctx->type;
  5885. asCScriptFunction *func = engine->scriptFunctions[ctx->property_set];
  5886. ctx->type.dataType = asCDataType::CreateObject(func->objectType, ctx->property_const);
  5887. if( ctx->property_handle )
  5888. ctx->type.dataType.MakeHandle(true);
  5889. ctx->type.dataType.MakeReference(true);
  5890. // Don't allow the call if the object is read-only and the property accessor is not const
  5891. // TODO: This can probably be moved into MakeFunctionCall
  5892. if( ctx->property_const && !func->isReadOnly )
  5893. {
  5894. Error(TXT_NON_CONST_METHOD_ON_CONST_OBJ, node);
  5895. asCArray<int> funcs;
  5896. funcs.PushLast(ctx->property_set);
  5897. PrintMatchingFuncs(funcs, node);
  5898. }
  5899. // Call the accessor
  5900. asCArray<asSExprContext *> args;
  5901. args.PushLast(arg);
  5902. MakeFunctionCall(ctx, ctx->property_set, func->objectType, args, node);
  5903. // TODO: This is from CompileExpressionPostOp, can we unify the code?
  5904. if( objType.isTemporary &&
  5905. ctx->type.dataType.IsReference() &&
  5906. !ctx->type.isVariable ) // If the resulting type is a variable, then the reference is not a member
  5907. {
  5908. // Remember the original object's variable, so that it can be released
  5909. // later on when the reference to its member goes out of scope
  5910. ctx->type.isTemporary = true;
  5911. ctx->type.stackOffset = objType.stackOffset;
  5912. }
  5913. else
  5914. {
  5915. // As the method didn't return a reference to a member
  5916. // we can safely release the original object now
  5917. ReleaseTemporaryVariable(objType, &ctx->bc);
  5918. }
  5919. ctx->property_get = 0;
  5920. ctx->property_set = 0;
  5921. return 0;
  5922. }
  5923. void asCCompiler::ProcessPropertyGetAccessor(asSExprContext *ctx, asCScriptNode *node)
  5924. {
  5925. // If no property accessor has been prepared then don't do anything
  5926. if( !ctx->property_get && !ctx->property_set )
  5927. return;
  5928. if( !ctx->property_get )
  5929. {
  5930. // Raise error on missing accessor
  5931. Error(TXT_PROPERTY_HAS_NO_GET_ACCESSOR, node);
  5932. ctx->type.SetDummy();
  5933. return;
  5934. }
  5935. // Setup the context with the original type so the method call gets built correctly
  5936. asCTypeInfo objType = ctx->type;
  5937. asCScriptFunction *func = engine->scriptFunctions[ctx->property_get];
  5938. ctx->type.dataType = asCDataType::CreateObject(func->objectType, ctx->property_const);
  5939. if( ctx->property_handle ) ctx->type.dataType.MakeHandle(true);
  5940. ctx->type.dataType.MakeReference(true);
  5941. // Don't allow the call if the object is read-only and the property accessor is not const
  5942. if( ctx->property_const && !func->isReadOnly )
  5943. {
  5944. Error(TXT_NON_CONST_METHOD_ON_CONST_OBJ, node);
  5945. asCArray<int> funcs;
  5946. funcs.PushLast(ctx->property_get);
  5947. PrintMatchingFuncs(funcs, node);
  5948. }
  5949. // Call the accessor
  5950. asCArray<asSExprContext *> args;
  5951. MakeFunctionCall(ctx, ctx->property_get, func->objectType, args, node);
  5952. // TODO: This is from CompileExpressionPostOp, can we unify the code?
  5953. if( objType.isTemporary &&
  5954. ctx->type.dataType.IsReference() &&
  5955. !ctx->type.isVariable ) // If the resulting type is a variable, then the reference is not a member
  5956. {
  5957. // Remember the original object's variable, so that it can be released
  5958. // later on when the reference to its member goes out of scope
  5959. ctx->type.isTemporary = true;
  5960. ctx->type.stackOffset = objType.stackOffset;
  5961. }
  5962. else
  5963. {
  5964. // As the method didn't return a reference to a member
  5965. // we can safely release the original object now
  5966. ReleaseTemporaryVariable(objType, &ctx->bc);
  5967. }
  5968. ctx->property_get = 0;
  5969. ctx->property_set = 0;
  5970. }
  5971. int asCCompiler::CompileExpressionPostOp(asCScriptNode *node, asSExprContext *ctx)
  5972. {
  5973. int op = node->tokenType;
  5974. // Check if the variable is initialized (if it indeed is a variable)
  5975. IsVariableInitialized(&ctx->type, node);
  5976. if( op == ttInc || op == ttDec )
  5977. {
  5978. if( globalExpression )
  5979. {
  5980. Error(TXT_INC_OP_IN_GLOBAL_EXPR, node);
  5981. return -1;
  5982. }
  5983. // Make sure the reference isn't a temporary variable
  5984. if( ctx->type.isTemporary )
  5985. {
  5986. Error(TXT_REF_IS_TEMP, node);
  5987. return -1;
  5988. }
  5989. if( ctx->type.dataType.IsReadOnly() )
  5990. {
  5991. Error(TXT_REF_IS_READ_ONLY, node);
  5992. return -1;
  5993. }
  5994. if( ctx->type.isVariable )
  5995. ConvertToReference(ctx);
  5996. else if( !ctx->type.dataType.IsReference() )
  5997. {
  5998. Error(TXT_NOT_VALID_REFERENCE, node);
  5999. return -1;
  6000. }
  6001. // Copy the value to a temp before changing it
  6002. ConvertToTempVariable(ctx);
  6003. // Increment the value pointed to by the reference still in the register
  6004. asEBCInstr iInc = asBC_INCi, iDec = asBC_DECi;
  6005. if( ctx->type.dataType.IsDoubleType() )
  6006. {
  6007. iInc = asBC_INCd;
  6008. iDec = asBC_DECd;
  6009. }
  6010. else if( ctx->type.dataType.IsFloatType() )
  6011. {
  6012. iInc = asBC_INCf;
  6013. iDec = asBC_DECf;
  6014. }
  6015. else if( ctx->type.dataType.IsIntegerType() || ctx->type.dataType.IsUnsignedType() )
  6016. {
  6017. if( ctx->type.dataType.IsEqualExceptRef(asCDataType::CreatePrimitive(ttInt16, false)) ||
  6018. ctx->type.dataType.IsEqualExceptRef(asCDataType::CreatePrimitive(ttUInt16, false)) )
  6019. {
  6020. iInc = asBC_INCi16;
  6021. iDec = asBC_DECi16;
  6022. }
  6023. else if( ctx->type.dataType.IsEqualExceptRef(asCDataType::CreatePrimitive(ttInt8, false)) ||
  6024. ctx->type.dataType.IsEqualExceptRef(asCDataType::CreatePrimitive(ttUInt8, false)) )
  6025. {
  6026. iInc = asBC_INCi8;
  6027. iDec = asBC_DECi8;
  6028. }
  6029. else if( ctx->type.dataType.IsEqualExceptRef(asCDataType::CreatePrimitive(ttInt64, false)) ||
  6030. ctx->type.dataType.IsEqualExceptRef(asCDataType::CreatePrimitive(ttUInt64, false)) )
  6031. {
  6032. iInc = asBC_INCi64;
  6033. iDec = asBC_DECi64;
  6034. }
  6035. }
  6036. else
  6037. {
  6038. Error(TXT_ILLEGAL_OPERATION, node);
  6039. return -1;
  6040. }
  6041. if( op == ttInc ) ctx->bc.Instr(iInc); else ctx->bc.Instr(iDec);
  6042. }
  6043. else if( op == ttDot )
  6044. {
  6045. if( node->firstChild->nodeType == snIdentifier )
  6046. {
  6047. ProcessPropertyGetAccessor(ctx, node);
  6048. // Get the property name
  6049. asCString name(&script->code[node->firstChild->tokenPos], node->firstChild->tokenLength);
  6050. // We need to look for get/set property accessors.
  6051. // If found, the context stores information on the get/set accessors
  6052. // until it is known which is to be used.
  6053. int r = FindPropertyAccessor(name, ctx, node);
  6054. if( r != 0 )
  6055. return r;
  6056. if( !ctx->type.dataType.IsPrimitive() )
  6057. Dereference(ctx, true);
  6058. if( ctx->type.dataType.IsObjectHandle() )
  6059. {
  6060. // Convert the handle to a normal object
  6061. asCDataType dt = ctx->type.dataType;
  6062. dt.MakeHandle(false);
  6063. ImplicitConversion(ctx, dt, node, asIC_IMPLICIT_CONV);
  6064. }
  6065. // Find the property offset and type
  6066. if( ctx->type.dataType.IsObject() )
  6067. {
  6068. bool isConst = ctx->type.dataType.IsReadOnly();
  6069. asCObjectProperty *prop = builder->GetObjectProperty(ctx->type.dataType, name.AddressOf());
  6070. if( prop )
  6071. {
  6072. // Put the offset on the stack
  6073. ctx->bc.InstrINT(asBC_ADDSi, prop->byteOffset);
  6074. if( prop->type.IsReference() )
  6075. ctx->bc.Instr(asBC_RDSPTR);
  6076. // Reference to primitive must be stored in the temp register
  6077. if( prop->type.IsPrimitive() )
  6078. {
  6079. // The ADD offset command should store the reference in the register directly
  6080. ctx->bc.Instr(asBC_PopRPtr);
  6081. }
  6082. // Set the new type (keeping info about temp variable)
  6083. ctx->type.dataType = prop->type;
  6084. ctx->type.dataType.MakeReference(true);
  6085. ctx->type.isVariable = false;
  6086. if( ctx->type.dataType.IsObject() && !ctx->type.dataType.IsObjectHandle() )
  6087. {
  6088. // Objects that are members are not references
  6089. ctx->type.dataType.MakeReference(false);
  6090. }
  6091. ctx->type.dataType.MakeReadOnly(isConst ? true : prop->type.IsReadOnly());
  6092. }
  6093. else
  6094. {
  6095. asCString str;
  6096. str.Format(TXT_s_NOT_MEMBER_OF_s, name.AddressOf(), ctx->type.dataType.Format().AddressOf());
  6097. Error(str.AddressOf(), node);
  6098. return -1;
  6099. }
  6100. }
  6101. else
  6102. {
  6103. asCString str;
  6104. str.Format(TXT_s_NOT_MEMBER_OF_s, name.AddressOf(), ctx->type.dataType.Format().AddressOf());
  6105. Error(str.AddressOf(), node);
  6106. return -1;
  6107. }
  6108. }
  6109. else
  6110. {
  6111. if( globalExpression )
  6112. {
  6113. Error(TXT_METHOD_IN_GLOBAL_EXPR, node);
  6114. return -1;
  6115. }
  6116. // Make sure it is an object we are accessing
  6117. if( !ctx->type.dataType.IsObject() )
  6118. {
  6119. asCString str;
  6120. str.Format(TXT_ILLEGAL_OPERATION_ON_s, ctx->type.dataType.Format().AddressOf());
  6121. Error(str.AddressOf(), node);
  6122. return -1;
  6123. }
  6124. // Process the get property accessor
  6125. ProcessPropertyGetAccessor(ctx, node);
  6126. bool isConst = false;
  6127. if( ctx->type.dataType.IsObjectHandle() )
  6128. isConst = ctx->type.dataType.IsHandleToConst();
  6129. else
  6130. isConst = ctx->type.dataType.IsReadOnly();
  6131. asCObjectType *trueObj = ctx->type.dataType.GetObjectType();
  6132. asCTypeInfo objType = ctx->type;
  6133. // Compile function call
  6134. CompileFunctionCall(node->firstChild, ctx, trueObj, isConst);
  6135. // If the method returned a reference, then we can't release the original
  6136. // object yet, because the reference may be to a member of it
  6137. if( objType.isTemporary &&
  6138. (ctx->type.dataType.IsReference() || (ctx->type.dataType.IsObject() && !ctx->type.dataType.IsObjectHandle())) &&
  6139. !ctx->type.isVariable ) // If the resulting type is a variable, then the reference is not a member
  6140. {
  6141. // Remember the original object's variable, so that it can be released
  6142. // later on when the reference to its member goes out of scope
  6143. ctx->type.isTemporary = true;
  6144. ctx->type.stackOffset = objType.stackOffset;
  6145. }
  6146. else
  6147. {
  6148. // As the method didn't return a reference to a member
  6149. // we can safely release the original object now
  6150. ReleaseTemporaryVariable(objType, &ctx->bc);
  6151. }
  6152. }
  6153. }
  6154. else if( op == ttOpenBracket )
  6155. {
  6156. if( !ctx->type.dataType.IsObject() )
  6157. {
  6158. asCString str;
  6159. str.Format(TXT_OBJECT_DOESNT_SUPPORT_INDEX_OP, ctx->type.dataType.Format().AddressOf());
  6160. Error(str.AddressOf(), node);
  6161. return -1;
  6162. }
  6163. ProcessPropertyGetAccessor(ctx, node);
  6164. Dereference(ctx, true);
  6165. bool isConst = ctx->type.dataType.IsReadOnly();
  6166. if( ctx->type.dataType.IsObjectHandle() )
  6167. {
  6168. // Convert the handle to a normal object
  6169. asCDataType dt = ctx->type.dataType;
  6170. dt.MakeHandle(false);
  6171. ImplicitConversion(ctx, dt, node, asIC_IMPLICIT_CONV);
  6172. }
  6173. // Compile the expression
  6174. asSExprContext expr(engine);
  6175. CompileAssignment(node->firstChild, &expr);
  6176. asCTypeInfo objType = ctx->type;
  6177. asSTypeBehaviour *beh = ctx->type.dataType.GetBehaviour();
  6178. if( beh == 0 )
  6179. {
  6180. asCString str;
  6181. str.Format(TXT_OBJECT_DOESNT_SUPPORT_INDEX_OP, ctx->type.dataType.Format().AddressOf());
  6182. Error(str.AddressOf(), node);
  6183. return -1;
  6184. }
  6185. else
  6186. {
  6187. // Now find a matching function for the object type and indexing type
  6188. asCArray<int> ops;
  6189. asUINT n;
  6190. if( isConst )
  6191. {
  6192. // Only list const behaviours
  6193. for( n = 0; n < beh->operators.GetLength(); n += 2 )
  6194. {
  6195. if( asBEHAVE_INDEX == beh->operators[n] && engine->scriptFunctions[beh->operators[n+1]]->isReadOnly )
  6196. ops.PushLast(beh->operators[n+1]);
  6197. }
  6198. }
  6199. else
  6200. {
  6201. // TODO: Prefer non-const over const
  6202. for( n = 0; n < beh->operators.GetLength(); n += 2 )
  6203. {
  6204. if( asBEHAVE_INDEX == beh->operators[n] )
  6205. ops.PushLast(beh->operators[n+1]);
  6206. }
  6207. }
  6208. asCArray<int> ops1;
  6209. MatchArgument(ops, ops1, &expr.type, 0);
  6210. if( !isConst )
  6211. FilterConst(ops1);
  6212. // Did we find a suitable function?
  6213. if( ops1.GetLength() == 1 )
  6214. {
  6215. asCScriptFunction *descr = engine->scriptFunctions[ops1[0]];
  6216. // Store the code for the object
  6217. asCByteCode objBC(engine);
  6218. objBC.AddCode(&ctx->bc);
  6219. // Add code for arguments
  6220. PrepareArgument(&descr->parameterTypes[0], &expr, node->firstChild, true, descr->inOutFlags[0]);
  6221. MergeExprContexts(ctx, &expr);
  6222. if( descr->parameterTypes[0].IsReference() )
  6223. {
  6224. if( descr->parameterTypes[0].IsObject() && !descr->parameterTypes[0].IsObjectHandle() )
  6225. ctx->bc.InstrWORD(asBC_GETOBJREF, 0);
  6226. else
  6227. ctx->bc.InstrWORD(asBC_GETREF, 0);
  6228. }
  6229. else if( descr->parameterTypes[0].IsObject() )
  6230. {
  6231. ctx->bc.InstrWORD(asBC_GETOBJ, 0);
  6232. // The temporary variable must not be freed as it will no longer hold an object
  6233. DeallocateVariable(expr.type.stackOffset);
  6234. expr.type.isTemporary = false;
  6235. }
  6236. // Add the code for the object again
  6237. ctx->bc.AddCode(&objBC);
  6238. asCArray<asSExprContext*> args;
  6239. args.PushLast(&expr);
  6240. PerformFunctionCall(descr->id, ctx, false, &args);
  6241. }
  6242. else if( ops.GetLength() > 1 )
  6243. {
  6244. Error(TXT_MORE_THAN_ONE_MATCHING_OP, node);
  6245. PrintMatchingFuncs(ops, node);
  6246. return -1;
  6247. }
  6248. else
  6249. {
  6250. asCString str;
  6251. str.Format(TXT_NO_MATCHING_OP_FOUND_FOR_TYPE_s, expr.type.dataType.Format().AddressOf());
  6252. Error(str.AddressOf(), node);
  6253. return -1;
  6254. }
  6255. }
  6256. // If the method returned a reference, then we can't release the original
  6257. // object yet, because the reference may be to a member of it
  6258. if( objType.isTemporary &&
  6259. (ctx->type.dataType.IsReference() || (ctx->type.dataType.IsObject() && !ctx->type.dataType.IsObjectHandle())) &&
  6260. !ctx->type.isVariable ) // If the resulting type is a variable, then the reference is not to a member
  6261. {
  6262. // Remember the object's variable, so that it can be released
  6263. // later on when the reference to its member goes out of scope
  6264. ctx->type.isTemporary = true;
  6265. ctx->type.stackOffset = objType.stackOffset;
  6266. }
  6267. else
  6268. {
  6269. // As the index operator didn't return a reference to a
  6270. // member we can release the original object now
  6271. ReleaseTemporaryVariable(objType, &ctx->bc);
  6272. }
  6273. }
  6274. return 0;
  6275. }
  6276. int asCCompiler::GetPrecedence(asCScriptNode *op)
  6277. {
  6278. // x * y, x / y, x % y
  6279. // x + y, x - y
  6280. // x <= y, x < y, x >= y, x > y
  6281. // x = =y, x != y, x xor y, x is y, x !is y
  6282. // x and y
  6283. // x or y
  6284. // The following are not used in this function,
  6285. // but should have lower precedence than the above
  6286. // x ? y : z
  6287. // x = y
  6288. // The expression term have the highest precedence
  6289. if( op->nodeType == snExprTerm )
  6290. return 1;
  6291. // Evaluate operators by token
  6292. int tokenType = op->tokenType;
  6293. if( tokenType == ttStar || tokenType == ttSlash || tokenType == ttPercent )
  6294. return 0;
  6295. if( tokenType == ttPlus || tokenType == ttMinus )
  6296. return -1;
  6297. if( tokenType == ttBitShiftLeft ||
  6298. tokenType == ttBitShiftRight ||
  6299. tokenType == ttBitShiftRightArith )
  6300. return -2;
  6301. if( tokenType == ttAmp )
  6302. return -3;
  6303. if( tokenType == ttBitXor )
  6304. return -4;
  6305. if( tokenType == ttBitOr )
  6306. return -5;
  6307. if( tokenType == ttLessThanOrEqual ||
  6308. tokenType == ttLessThan ||
  6309. tokenType == ttGreaterThanOrEqual ||
  6310. tokenType == ttGreaterThan )
  6311. return -6;
  6312. if( tokenType == ttEqual || tokenType == ttNotEqual || tokenType == ttXor || tokenType == ttIs || tokenType == ttNotIs )
  6313. return -7;
  6314. if( tokenType == ttAnd )
  6315. return -8;
  6316. if( tokenType == ttOr )
  6317. return -9;
  6318. // Unknown operator
  6319. asASSERT(false);
  6320. return 0;
  6321. }
  6322. int asCCompiler::MatchArgument(asCArray<int> &funcs, asCArray<int> &matches, const asCTypeInfo *argType, int paramNum, bool allowObjectConstruct)
  6323. {
  6324. bool isExactMatch = false;
  6325. bool isMatchExceptConst = false;
  6326. bool isMatchWithBaseType = false;
  6327. bool isMatchExceptSign = false;
  6328. bool isMatchNotVarType = false;
  6329. asUINT n;
  6330. matches.SetLength(0);
  6331. for( n = 0; n < funcs.GetLength(); n++ )
  6332. {
  6333. asCScriptFunction *desc = builder->GetFunctionDescription(funcs[n]);
  6334. // Does the function have arguments enough?
  6335. if( (int)desc->parameterTypes.GetLength() <= paramNum )
  6336. continue;
  6337. // Can we make the match by implicit conversion?
  6338. asSExprContext ti(engine);
  6339. ti.type = *argType;
  6340. if( argType->dataType.IsPrimitive() ) ti.type.dataType.MakeReference(false);
  6341. ImplicitConversion(&ti, desc->parameterTypes[paramNum], 0, asIC_IMPLICIT_CONV, false, 0, allowObjectConstruct);
  6342. if( desc->parameterTypes[paramNum].IsEqualExceptRef(ti.type.dataType) )
  6343. {
  6344. // Is it an exact match?
  6345. if( argType->dataType.IsEqualExceptRef(ti.type.dataType) )
  6346. {
  6347. if( !isExactMatch ) matches.SetLength(0);
  6348. isExactMatch = true;
  6349. matches.PushLast(funcs[n]);
  6350. continue;
  6351. }
  6352. if( !isExactMatch )
  6353. {
  6354. // Is it a match except const?
  6355. if( argType->dataType.IsEqualExceptRefAndConst(ti.type.dataType) )
  6356. {
  6357. if( !isMatchExceptConst ) matches.SetLength(0);
  6358. isMatchExceptConst = true;
  6359. matches.PushLast(funcs[n]);
  6360. continue;
  6361. }
  6362. if( !isMatchExceptConst )
  6363. {
  6364. // Is it a size promotion, e.g. int8 -> int?
  6365. if( argType->dataType.IsSamePrimitiveBaseType(ti.type.dataType) )
  6366. {
  6367. if( !isMatchWithBaseType ) matches.SetLength(0);
  6368. isMatchWithBaseType = true;
  6369. matches.PushLast(funcs[n]);
  6370. continue;
  6371. }
  6372. if( !isMatchWithBaseType )
  6373. {
  6374. // Conversion between signed and unsigned integer is better than between integer and float
  6375. // Is it a match except for sign?
  6376. if( (argType->dataType.IsIntegerType() && ti.type.dataType.IsUnsignedType()) ||
  6377. (argType->dataType.IsUnsignedType() && ti.type.dataType.IsIntegerType()) )
  6378. {
  6379. if( !isMatchExceptSign ) matches.SetLength(0);
  6380. isMatchExceptSign = true;
  6381. matches.PushLast(funcs[n]);
  6382. continue;
  6383. }
  6384. if( !isMatchExceptSign )
  6385. {
  6386. // If there was any match without a var type it has higher priority
  6387. if( desc->parameterTypes[paramNum].GetTokenType() != ttQuestion )
  6388. {
  6389. if( !isMatchNotVarType ) matches.SetLength(0);
  6390. isMatchNotVarType = true;
  6391. matches.PushLast(funcs[n]);
  6392. continue;
  6393. }
  6394. // Implicit conversion to ?& has the smallest priority
  6395. if( !isMatchNotVarType )
  6396. matches.PushLast(funcs[n]);
  6397. }
  6398. }
  6399. }
  6400. }
  6401. }
  6402. }
  6403. return (int)matches.GetLength();
  6404. }
  6405. void asCCompiler::PrepareArgument2(asSExprContext *ctx, asSExprContext *arg, asCDataType *paramType, bool isFunction, int refType, asCArray<int> *reservedVars)
  6406. {
  6407. asSExprContext e(engine);
  6408. // Reference parameters whose value won't be used don't evaluate the expression
  6409. if( !paramType->IsReference() || (refType & 1) )
  6410. {
  6411. MergeExprContexts(&e, arg);
  6412. }
  6413. else
  6414. {
  6415. // Store the original bytecode so that it can be reused when processing the deferred output parameter
  6416. asSExprContext *orig = asNEW(asSExprContext)(engine);
  6417. MergeExprContexts(orig, arg);
  6418. orig->exprNode = arg->exprNode;
  6419. orig->type = arg->type;
  6420. orig->property_get = arg->property_get;
  6421. orig->property_set = arg->property_set;
  6422. orig->property_const = arg->property_const;
  6423. orig->property_handle = arg->property_handle;
  6424. arg->origExpr = orig;
  6425. }
  6426. e.type = arg->type;
  6427. e.property_get = arg->property_get;
  6428. e.property_set = arg->property_set;
  6429. e.property_const = arg->property_const;
  6430. e.property_handle = arg->property_handle;
  6431. PrepareArgument(paramType, &e, arg->exprNode, isFunction, refType, reservedVars);
  6432. arg->type = e.type;
  6433. ctx->bc.AddCode(&e.bc);
  6434. }
  6435. bool asCCompiler::CompileOverloadedDualOperator(asCScriptNode *node, asSExprContext *lctx, asSExprContext *rctx, asSExprContext *ctx)
  6436. {
  6437. // What type of operator is it?
  6438. int token = node->tokenType;
  6439. if( token == ttUnrecognizedToken )
  6440. {
  6441. // This happens when the compiler is inferring an assignment
  6442. // operation from another action, for example in preparing a value
  6443. // as a function argument
  6444. token = ttAssignment;
  6445. }
  6446. // boolean operators are not overloadable
  6447. if( token == ttAnd ||
  6448. token == ttOr ||
  6449. token == ttXor )
  6450. return false;
  6451. // Dual operators can also be implemented as class methods
  6452. if( token == ttEqual ||
  6453. token == ttNotEqual )
  6454. {
  6455. // TODO: Should evaluate which of the two have the best match. If both have equal match, the first version should be used
  6456. // Find the matching opEquals method
  6457. int r = CompileOverloadedDualOperator2(node, "opEquals", lctx, rctx, ctx, true, asCDataType::CreatePrimitive(ttBool, false));
  6458. if( r == 0 )
  6459. {
  6460. // Try again by switching the order of the operands
  6461. r = CompileOverloadedDualOperator2(node, "opEquals", rctx, lctx, ctx, true, asCDataType::CreatePrimitive(ttBool, false));
  6462. }
  6463. if( r == 1 )
  6464. {
  6465. if( token == ttNotEqual )
  6466. ctx->bc.InstrSHORT(asBC_NOT, ctx->type.stackOffset);
  6467. // Success, don't continue
  6468. return true;
  6469. }
  6470. else if( r < 0 )
  6471. {
  6472. // Compiler error, don't continue
  6473. ctx->type.SetConstantDW(asCDataType::CreatePrimitive(ttBool, true), true);
  6474. return true;
  6475. }
  6476. }
  6477. if( token == ttEqual ||
  6478. token == ttNotEqual ||
  6479. token == ttLessThan ||
  6480. token == ttLessThanOrEqual ||
  6481. token == ttGreaterThan ||
  6482. token == ttGreaterThanOrEqual )
  6483. {
  6484. bool swappedOrder = false;
  6485. // TODO: Should evaluate which of the two have the best match. If both have equal match, the first version should be used
  6486. // Find the matching opCmp method
  6487. int r = CompileOverloadedDualOperator2(node, "opCmp", lctx, rctx, ctx, true, asCDataType::CreatePrimitive(ttInt, false));
  6488. if( r == 0 )
  6489. {
  6490. // Try again by switching the order of the operands
  6491. swappedOrder = true;
  6492. r = CompileOverloadedDualOperator2(node, "opCmp", rctx, lctx, ctx, true, asCDataType::CreatePrimitive(ttInt, false));
  6493. }
  6494. if( r == 1 )
  6495. {
  6496. ReleaseTemporaryVariable(ctx->type, &ctx->bc);
  6497. int a = AllocateVariable(asCDataType::CreatePrimitive(ttBool, false), true);
  6498. ctx->bc.InstrW_DW(asBC_CMPIi, ctx->type.stackOffset, 0);
  6499. if( token == ttEqual )
  6500. ctx->bc.Instr(asBC_TZ);
  6501. else if( token == ttNotEqual )
  6502. ctx->bc.Instr(asBC_TNZ);
  6503. else if( (token == ttLessThan && !swappedOrder) ||
  6504. (token == ttGreaterThan && swappedOrder) )
  6505. ctx->bc.Instr(asBC_TS);
  6506. else if( (token == ttLessThanOrEqual && !swappedOrder) ||
  6507. (token == ttGreaterThanOrEqual && swappedOrder) )
  6508. ctx->bc.Instr(asBC_TNP);
  6509. else if( (token == ttGreaterThan && !swappedOrder) ||
  6510. (token == ttLessThan && swappedOrder) )
  6511. ctx->bc.Instr(asBC_TP);
  6512. else if( (token == ttGreaterThanOrEqual && !swappedOrder) ||
  6513. (token == ttLessThanOrEqual && swappedOrder) )
  6514. ctx->bc.Instr(asBC_TNS);
  6515. ctx->bc.InstrSHORT(asBC_CpyRtoV4, (short)a);
  6516. ctx->type.SetVariable(asCDataType::CreatePrimitive(ttBool, false), a, true);
  6517. // Success, don't continue
  6518. return true;
  6519. }
  6520. else if( r < 0 )
  6521. {
  6522. // Compiler error, don't continue
  6523. ctx->type.SetConstantDW(asCDataType::CreatePrimitive(ttBool, true), true);
  6524. return true;
  6525. }
  6526. }
  6527. // The rest of the operators are not commutative, and doesn't require specific return type
  6528. const char *op = 0, *op_r = 0;
  6529. switch( token )
  6530. {
  6531. case ttPlus: op = "opAdd"; op_r = "opAdd_r"; break;
  6532. case ttMinus: op = "opSub"; op_r = "opSub_r"; break;
  6533. case ttStar: op = "opMul"; op_r = "opMul_r"; break;
  6534. case ttSlash: op = "opDiv"; op_r = "opDiv_r"; break;
  6535. case ttPercent: op = "opMod"; op_r = "opMod_r"; break;
  6536. case ttBitOr: op = "opOr"; op_r = "opOr_r"; break;
  6537. case ttAmp: op = "opAnd"; op_r = "opAnd_r"; break;
  6538. case ttBitXor: op = "opXor"; op_r = "opXor_r"; break;
  6539. case ttBitShiftLeft: op = "opShl"; op_r = "opShl_r"; break;
  6540. case ttBitShiftRight: op = "opShr"; op_r = "opShr_r"; break;
  6541. case ttBitShiftRightArith: op = "opUShr"; op_r = "opUShr_r"; break;
  6542. }
  6543. // TODO: Might be interesting to support a concatenation operator, e.g. ~
  6544. if( op && op_r )
  6545. {
  6546. // TODO: Should evaluate which of the two have the best match. If both have equal match, the first version should be used
  6547. // Find the matching operator method
  6548. int r = CompileOverloadedDualOperator2(node, op, lctx, rctx, ctx);
  6549. if( r == 0 )
  6550. {
  6551. // Try again by switching the order of the operands, and using the reversed operator
  6552. r = CompileOverloadedDualOperator2(node, op_r, rctx, lctx, ctx);
  6553. }
  6554. if( r == 1 )
  6555. {
  6556. // Success, don't continue
  6557. return true;
  6558. }
  6559. else if( r < 0 )
  6560. {
  6561. // Compiler error, don't continue
  6562. ctx->type.SetDummy();
  6563. return true;
  6564. }
  6565. }
  6566. // Assignment operators
  6567. op = 0;
  6568. switch( token )
  6569. {
  6570. case ttAssignment: op = "opAssign"; break;
  6571. case ttAddAssign: op = "opAddAssign"; break;
  6572. case ttSubAssign: op = "opSubAssign"; break;
  6573. case ttMulAssign: op = "opMulAssign"; break;
  6574. case ttDivAssign: op = "opDivAssign"; break;
  6575. case ttModAssign: op = "opModAssign"; break;
  6576. case ttOrAssign: op = "opOrAssign"; break;
  6577. case ttAndAssign: op = "opAndAssign"; break;
  6578. case ttXorAssign: op = "opXorAssign"; break;
  6579. case ttShiftLeftAssign: op = "opShlAssign"; break;
  6580. case ttShiftRightLAssign: op = "opShrAssign"; break;
  6581. case ttShiftRightAAssign: op = "opUShrAssign"; break;
  6582. }
  6583. if( op )
  6584. {
  6585. // TODO: Shouldn't accept const lvalue with the assignment operators
  6586. // Find the matching operator method
  6587. int r = CompileOverloadedDualOperator2(node, op, lctx, rctx, ctx);
  6588. if( r == 1 )
  6589. {
  6590. // Success, don't continue
  6591. return true;
  6592. }
  6593. else if( r < 0 )
  6594. {
  6595. // Compiler error, don't continue
  6596. ctx->type.SetDummy();
  6597. return true;
  6598. }
  6599. }
  6600. // No suitable operator was found
  6601. return false;
  6602. }
  6603. // Returns negative on compile error
  6604. // zero on no matching operator
  6605. // one on matching operator
  6606. int asCCompiler::CompileOverloadedDualOperator2(asCScriptNode *node, const char *methodName, asSExprContext *lctx, asSExprContext *rctx, asSExprContext *ctx, bool specificReturn, const asCDataType &returnType)
  6607. {
  6608. // Find the matching method
  6609. if( lctx->type.dataType.IsObject() && !lctx->type.isExplicitHandle )
  6610. {
  6611. // Is the left value a const?
  6612. bool isConst = false;
  6613. if( lctx->type.dataType.IsObjectHandle() )
  6614. isConst = lctx->type.dataType.IsHandleToConst();
  6615. else
  6616. isConst = lctx->type.dataType.IsReadOnly();
  6617. asCArray<int> funcs;
  6618. asCObjectType *ot = lctx->type.dataType.GetObjectType();
  6619. for( asUINT n = 0; n < ot->methods.GetLength(); n++ )
  6620. {
  6621. asCScriptFunction *func = engine->scriptFunctions[ot->methods[n]];
  6622. if( func->name == methodName &&
  6623. (!specificReturn || func->returnType == returnType) &&
  6624. func->parameterTypes.GetLength() == 1 &&
  6625. (!isConst || func->isReadOnly) )
  6626. {
  6627. // Make sure the method is accessible by the module
  6628. asCConfigGroup *group = engine->FindConfigGroupForFunction(func->id);
  6629. if( !group || group->HasModuleAccess(builder->module->name.AddressOf()) )
  6630. funcs.PushLast(func->id);
  6631. }
  6632. }
  6633. // Which is the best matching function?
  6634. asCArray<int> ops;
  6635. MatchArgument(funcs, ops, &rctx->type, 0);
  6636. // Did we find an operator?
  6637. if( ops.GetLength() == 1 )
  6638. {
  6639. // Process the lctx expression as get accessor
  6640. ProcessPropertyGetAccessor(lctx, node);
  6641. // Merge the bytecode so that it forms lvalue.methodName(rvalue)
  6642. asCTypeInfo objType = lctx->type;
  6643. asCArray<asSExprContext *> args;
  6644. args.PushLast(rctx);
  6645. MergeExprContexts(ctx, lctx);
  6646. ctx->type = lctx->type;
  6647. MakeFunctionCall(ctx, ops[0], objType.dataType.GetObjectType(), args, node);
  6648. // TODO: Can we do this here?
  6649. ReleaseTemporaryVariable(objType, &ctx->bc);
  6650. // Found matching operator
  6651. return 1;
  6652. }
  6653. else if( ops.GetLength() > 1 )
  6654. {
  6655. Error(TXT_MORE_THAN_ONE_MATCHING_OP, node);
  6656. PrintMatchingFuncs(ops, node);
  6657. ctx->type.SetDummy();
  6658. // Compiler error
  6659. return -1;
  6660. }
  6661. }
  6662. // No matching operator
  6663. return 0;
  6664. }
  6665. void asCCompiler::MakeFunctionCall(asSExprContext *ctx, int funcId, asCObjectType *objectType, asCArray<asSExprContext*> &args, asCScriptNode *node, bool useVariable, int stackOffset)
  6666. {
  6667. if( objectType )
  6668. {
  6669. Dereference(ctx, true);
  6670. // Warn if the method is non-const and the object is temporary
  6671. // since the changes will be lost when the object is destroyed.
  6672. // If the object is accessed through a handle, then it is assumed
  6673. // the object is not temporary, even though the handle is.
  6674. if( ctx->type.isTemporary &&
  6675. !ctx->type.dataType.IsObjectHandle() &&
  6676. !engine->scriptFunctions[funcId]->isReadOnly )
  6677. {
  6678. Warning(TXT_CALLING_NONCONST_METHOD_ON_TEMP, node);
  6679. }
  6680. }
  6681. asCByteCode objBC(engine);
  6682. objBC.AddCode(&ctx->bc);
  6683. PrepareFunctionCall(funcId, &ctx->bc, args);
  6684. // Verify if any of the args variable offsets are used in the other code.
  6685. // If they are exchange the offset for a new one
  6686. asUINT n;
  6687. for( n = 0; n < args.GetLength(); n++ )
  6688. {
  6689. if( args[n]->type.isTemporary && objBC.IsVarUsed(args[n]->type.stackOffset) )
  6690. {
  6691. // Release the current temporary variable
  6692. ReleaseTemporaryVariable(args[n]->type, 0);
  6693. asCArray<int> usedVars;
  6694. objBC.GetVarsUsed(usedVars);
  6695. ctx->bc.GetVarsUsed(usedVars);
  6696. asCDataType dt = args[n]->type.dataType;
  6697. dt.MakeReference(false);
  6698. int newOffset = AllocateVariableNotIn(dt, true, &usedVars);
  6699. ctx->bc.ExchangeVar(args[n]->type.stackOffset, newOffset);
  6700. args[n]->type.stackOffset = (short)newOffset;
  6701. args[n]->type.isTemporary = true;
  6702. args[n]->type.isVariable = true;
  6703. }
  6704. }
  6705. ctx->bc.AddCode(&objBC);
  6706. MoveArgsToStack(funcId, &ctx->bc, args, objectType ? true : false);
  6707. PerformFunctionCall(funcId, ctx, false, &args, 0, useVariable, stackOffset);
  6708. }
  6709. int asCCompiler::CompileOperator(asCScriptNode *node, asSExprContext *lctx, asSExprContext *rctx, asSExprContext *ctx)
  6710. {
  6711. IsVariableInitialized(&lctx->type, node);
  6712. IsVariableInitialized(&rctx->type, node);
  6713. if( lctx->type.isExplicitHandle || rctx->type.isExplicitHandle ||
  6714. node->tokenType == ttIs || node->tokenType == ttNotIs )
  6715. {
  6716. CompileOperatorOnHandles(node, lctx, rctx, ctx);
  6717. return 0;
  6718. }
  6719. else
  6720. {
  6721. // Compile an overloaded operator for the two operands
  6722. if( CompileOverloadedDualOperator(node, lctx, rctx, ctx) )
  6723. return 0;
  6724. // If both operands are objects, then we shouldn't continue
  6725. if( lctx->type.dataType.IsObject() && rctx->type.dataType.IsObject() )
  6726. {
  6727. asCString str;
  6728. str.Format(TXT_NO_MATCHING_OP_FOUND_FOR_TYPES_s_AND_s, lctx->type.dataType.Format().AddressOf(), rctx->type.dataType.Format().AddressOf());
  6729. Error(str.AddressOf(), node);
  6730. ctx->type.SetDummy();
  6731. return -1;
  6732. }
  6733. // Make sure we have two variables or constants
  6734. if( lctx->type.dataType.IsReference() ) ConvertToVariableNotIn(lctx, rctx);
  6735. if( rctx->type.dataType.IsReference() ) ConvertToVariableNotIn(rctx, lctx);
  6736. // Process the property get accessors (if any)
  6737. ProcessPropertyGetAccessor(lctx, node);
  6738. ProcessPropertyGetAccessor(rctx, node);
  6739. // Make sure lctx doesn't end up with a variable used in rctx
  6740. if( lctx->type.isTemporary && rctx->bc.IsVarUsed(lctx->type.stackOffset) )
  6741. {
  6742. asCArray<int> vars;
  6743. rctx->bc.GetVarsUsed(vars);
  6744. int offset = AllocateVariable(lctx->type.dataType, true);
  6745. rctx->bc.ExchangeVar(lctx->type.stackOffset, offset);
  6746. ReleaseTemporaryVariable(offset, 0);
  6747. }
  6748. // Math operators
  6749. // + - * / % += -= *= /= %=
  6750. int op = node->tokenType;
  6751. if( op == ttPlus || op == ttAddAssign ||
  6752. op == ttMinus || op == ttSubAssign ||
  6753. op == ttStar || op == ttMulAssign ||
  6754. op == ttSlash || op == ttDivAssign ||
  6755. op == ttPercent || op == ttModAssign )
  6756. {
  6757. CompileMathOperator(node, lctx, rctx, ctx);
  6758. return 0;
  6759. }
  6760. // Bitwise operators
  6761. // << >> >>> & | ^ <<= >>= >>>= &= |= ^=
  6762. if( op == ttAmp || op == ttAndAssign ||
  6763. op == ttBitOr || op == ttOrAssign ||
  6764. op == ttBitXor || op == ttXorAssign ||
  6765. op == ttBitShiftLeft || op == ttShiftLeftAssign ||
  6766. op == ttBitShiftRight || op == ttShiftRightLAssign ||
  6767. op == ttBitShiftRightArith || op == ttShiftRightAAssign )
  6768. {
  6769. CompileBitwiseOperator(node, lctx, rctx, ctx);
  6770. return 0;
  6771. }
  6772. // Comparison operators
  6773. // == != < > <= >=
  6774. if( op == ttEqual || op == ttNotEqual ||
  6775. op == ttLessThan || op == ttLessThanOrEqual ||
  6776. op == ttGreaterThan || op == ttGreaterThanOrEqual )
  6777. {
  6778. CompileComparisonOperator(node, lctx, rctx, ctx);
  6779. return 0;
  6780. }
  6781. // Boolean operators
  6782. // && || ^^
  6783. if( op == ttAnd || op == ttOr || op == ttXor )
  6784. {
  6785. CompileBooleanOperator(node, lctx, rctx, ctx);
  6786. return 0;
  6787. }
  6788. }
  6789. asASSERT(false);
  6790. return -1;
  6791. }
  6792. void asCCompiler::ConvertToTempVariableNotIn(asSExprContext *ctx, asSExprContext *exclude)
  6793. {
  6794. asCArray<int> excludeVars;
  6795. if( exclude ) exclude->bc.GetVarsUsed(excludeVars);
  6796. ConvertToTempVariableNotIn(ctx, &excludeVars);
  6797. }
  6798. void asCCompiler::ConvertToTempVariableNotIn(asSExprContext *ctx, asCArray<int> *reservedVars)
  6799. {
  6800. ConvertToVariableNotIn(ctx, reservedVars);
  6801. if( !ctx->type.isTemporary )
  6802. {
  6803. if( ctx->type.dataType.IsPrimitive() )
  6804. {
  6805. int offset = AllocateVariableNotIn(ctx->type.dataType, true, reservedVars);
  6806. if( ctx->type.dataType.GetSizeInMemoryDWords() == 1 )
  6807. ctx->bc.InstrW_W(asBC_CpyVtoV4, offset, ctx->type.stackOffset);
  6808. else
  6809. ctx->bc.InstrW_W(asBC_CpyVtoV8, offset, ctx->type.stackOffset);
  6810. ctx->type.SetVariable(ctx->type.dataType, offset, true);
  6811. }
  6812. else if( ctx->type.dataType.IsObjectHandle() )
  6813. {
  6814. asASSERT(false);
  6815. }
  6816. else // ctx->type.dataType.IsObject()
  6817. {
  6818. // Make sure the variable is not used in the expression
  6819. asCArray<int> vars;
  6820. ctx->bc.GetVarsUsed(vars);
  6821. int offset = AllocateVariableNotIn(ctx->type.dataType, true, &vars);
  6822. // Allocate and construct the temporary object
  6823. asCByteCode tmpBC(engine);
  6824. CallDefaultConstructor(ctx->type.dataType, offset, &tmpBC, ctx->exprNode);
  6825. // Insert the code before the expression code
  6826. tmpBC.AddCode(&ctx->bc);
  6827. ctx->bc.AddCode(&tmpBC);
  6828. // Assign the evaluated expression to the temporary variable
  6829. PrepareForAssignment(&ctx->type.dataType, ctx, 0);
  6830. asCTypeInfo type;
  6831. type.SetVariable(ctx->type.dataType, offset, true);
  6832. ctx->bc.InstrSHORT(asBC_PSF, (short)offset);
  6833. PerformAssignment(&type, &ctx->type, &ctx->bc, 0);
  6834. ReleaseTemporaryVariable(ctx->type, &ctx->bc);
  6835. ctx->type = type;
  6836. }
  6837. }
  6838. }
  6839. void asCCompiler::ConvertToTempVariable(asSExprContext *ctx)
  6840. {
  6841. ConvertToVariable(ctx);
  6842. if( !ctx->type.isTemporary )
  6843. {
  6844. if( ctx->type.dataType.IsPrimitive() )
  6845. {
  6846. int offset = AllocateVariable(ctx->type.dataType, true);
  6847. if( ctx->type.dataType.GetSizeInMemoryDWords() == 1 )
  6848. ctx->bc.InstrW_W(asBC_CpyVtoV4, offset, ctx->type.stackOffset);
  6849. else
  6850. ctx->bc.InstrW_W(asBC_CpyVtoV8, offset, ctx->type.stackOffset);
  6851. ctx->type.SetVariable(ctx->type.dataType, offset, true);
  6852. }
  6853. else if( ctx->type.dataType.IsObjectHandle() )
  6854. {
  6855. asASSERT(false);
  6856. }
  6857. else // ctx->type.dataType.IsObject()
  6858. {
  6859. // Make sure the variable is not used in the expression
  6860. asCArray<int> vars;
  6861. ctx->bc.GetVarsUsed(vars);
  6862. int offset = AllocateVariableNotIn(ctx->type.dataType, true, &vars);
  6863. // Allocate and construct the temporary object
  6864. asCByteCode tmpBC(engine);
  6865. CallDefaultConstructor(ctx->type.dataType, offset, &tmpBC, ctx->exprNode);
  6866. // Insert the code before the expression code
  6867. tmpBC.AddCode(&ctx->bc);
  6868. ctx->bc.AddCode(&tmpBC);
  6869. // Assign the evaluated expression to the temporary variable
  6870. PrepareForAssignment(&ctx->type.dataType, ctx, 0);
  6871. asCTypeInfo type;
  6872. type.SetVariable(ctx->type.dataType, offset, true);
  6873. ctx->bc.InstrSHORT(asBC_PSF, (short)offset);
  6874. PerformAssignment(&type, &ctx->type, &ctx->bc, 0);
  6875. ReleaseTemporaryVariable(ctx->type, &ctx->bc);
  6876. ctx->type = type;
  6877. }
  6878. }
  6879. }
  6880. void asCCompiler::ConvertToVariable(asSExprContext *ctx)
  6881. {
  6882. ConvertToVariableNotIn(ctx, (asCArray<int>*)0);
  6883. }
  6884. void asCCompiler::ConvertToVariableNotIn(asSExprContext *ctx, asCArray<int> *reservedVars)
  6885. {
  6886. if( !ctx->type.isVariable )
  6887. {
  6888. asCArray<int> excludeVars;
  6889. if( reservedVars ) excludeVars.Concatenate(*reservedVars);
  6890. int offset;
  6891. if( ctx->type.dataType.IsObjectHandle() )
  6892. {
  6893. offset = AllocateVariableNotIn(ctx->type.dataType, true, &excludeVars);
  6894. if( ctx->type.IsNullConstant() )
  6895. {
  6896. // TODO: Adapt pointer size
  6897. ctx->bc.InstrSHORT_DW(asBC_SetV4, (short)offset, 0);
  6898. }
  6899. else
  6900. {
  6901. // Copy the object handle to a variable
  6902. ctx->bc.InstrSHORT(asBC_PSF, (short)offset);
  6903. ctx->bc.InstrPTR(asBC_REFCPY, ctx->type.dataType.GetObjectType());
  6904. ctx->bc.Pop(AS_PTR_SIZE);
  6905. }
  6906. ReleaseTemporaryVariable(ctx->type, &ctx->bc);
  6907. ctx->type.SetVariable(ctx->type.dataType, offset, true);
  6908. }
  6909. else if( ctx->type.dataType.IsPrimitive() )
  6910. {
  6911. if( ctx->type.isConstant )
  6912. {
  6913. offset = AllocateVariableNotIn(ctx->type.dataType, true, &excludeVars);
  6914. if( ctx->type.dataType.GetSizeInMemoryBytes() == 1 )
  6915. ctx->bc.InstrSHORT_B(asBC_SetV1, (short)offset, ctx->type.byteValue);
  6916. else if( ctx->type.dataType.GetSizeInMemoryBytes() == 2 )
  6917. ctx->bc.InstrSHORT_W(asBC_SetV2, (short)offset, ctx->type.wordValue);
  6918. else if( ctx->type.dataType.GetSizeInMemoryBytes() == 4 )
  6919. ctx->bc.InstrSHORT_DW(asBC_SetV4, (short)offset, ctx->type.dwordValue);
  6920. else
  6921. ctx->bc.InstrSHORT_QW(asBC_SetV8, (short)offset, ctx->type.qwordValue);
  6922. ctx->type.SetVariable(ctx->type.dataType, offset, true);
  6923. return;
  6924. }
  6925. else
  6926. {
  6927. asASSERT(ctx->type.dataType.IsPrimitive());
  6928. asASSERT(ctx->type.dataType.IsReference());
  6929. ctx->type.dataType.MakeReference(false);
  6930. offset = AllocateVariableNotIn(ctx->type.dataType, true, &excludeVars);
  6931. // Read the value from the address in the register directly into the variable
  6932. if( ctx->type.dataType.GetSizeInMemoryBytes() == 1 )
  6933. ctx->bc.InstrSHORT(asBC_RDR1, (short)offset);
  6934. else if( ctx->type.dataType.GetSizeInMemoryBytes() == 2 )
  6935. ctx->bc.InstrSHORT(asBC_RDR2, (short)offset);
  6936. else if( ctx->type.dataType.GetSizeInMemoryDWords() == 1 )
  6937. ctx->bc.InstrSHORT(asBC_RDR4, (short)offset);
  6938. else
  6939. ctx->bc.InstrSHORT(asBC_RDR8, (short)offset);
  6940. }
  6941. ReleaseTemporaryVariable(ctx->type, &ctx->bc);
  6942. ctx->type.SetVariable(ctx->type.dataType, offset, true);
  6943. }
  6944. }
  6945. }
  6946. void asCCompiler::ConvertToVariableNotIn(asSExprContext *ctx, asSExprContext *exclude)
  6947. {
  6948. asCArray<int> excludeVars;
  6949. if( exclude ) exclude->bc.GetVarsUsed(excludeVars);
  6950. ConvertToVariableNotIn(ctx, &excludeVars);
  6951. }
  6952. void asCCompiler::CompileMathOperator(asCScriptNode *node, asSExprContext *lctx, asSExprContext *rctx, asSExprContext *ctx)
  6953. {
  6954. // TODO: If a constant is only using 32bits, then a 32bit operation is preferred
  6955. // Implicitly convert the operands to a number type
  6956. asCDataType to;
  6957. if( lctx->type.dataType.IsDoubleType() || rctx->type.dataType.IsDoubleType() )
  6958. to.SetTokenType(ttDouble);
  6959. else if( lctx->type.dataType.IsFloatType() || rctx->type.dataType.IsFloatType() )
  6960. to.SetTokenType(ttFloat);
  6961. else if( lctx->type.dataType.GetSizeInMemoryDWords() == 2 || rctx->type.dataType.GetSizeInMemoryDWords() == 2 )
  6962. {
  6963. if( lctx->type.dataType.IsIntegerType() || rctx->type.dataType.IsIntegerType() )
  6964. to.SetTokenType(ttInt64);
  6965. else if( lctx->type.dataType.IsUnsignedType() || rctx->type.dataType.IsUnsignedType() )
  6966. to.SetTokenType(ttUInt64);
  6967. }
  6968. else
  6969. {
  6970. if( lctx->type.dataType.IsIntegerType() || rctx->type.dataType.IsIntegerType() ||
  6971. lctx->type.dataType.IsEnumType() || rctx->type.dataType.IsEnumType() )
  6972. to.SetTokenType(ttInt);
  6973. else if( lctx->type.dataType.IsUnsignedType() || rctx->type.dataType.IsUnsignedType() )
  6974. to.SetTokenType(ttUInt);
  6975. }
  6976. // If doing an operation with double constant and float variable, the constant should be converted to float
  6977. if( (lctx->type.isConstant && lctx->type.dataType.IsDoubleType() && !rctx->type.isConstant && rctx->type.dataType.IsFloatType()) ||
  6978. (rctx->type.isConstant && rctx->type.dataType.IsDoubleType() && !lctx->type.isConstant && lctx->type.dataType.IsFloatType()) )
  6979. to.SetTokenType(ttFloat);
  6980. // Do the actual conversion
  6981. asCArray<int> reservedVars;
  6982. rctx->bc.GetVarsUsed(reservedVars);
  6983. lctx->bc.GetVarsUsed(reservedVars);
  6984. ImplicitConversion(lctx, to, node, asIC_IMPLICIT_CONV, true, &reservedVars);
  6985. ImplicitConversion(rctx, to, node, asIC_IMPLICIT_CONV, true, &reservedVars);
  6986. // Verify that the conversion was successful
  6987. if( !lctx->type.dataType.IsIntegerType() &&
  6988. !lctx->type.dataType.IsUnsignedType() &&
  6989. !lctx->type.dataType.IsFloatType() &&
  6990. !lctx->type.dataType.IsDoubleType() )
  6991. {
  6992. asCString str;
  6993. str.Format(TXT_NO_CONVERSION_s_TO_MATH_TYPE, lctx->type.dataType.Format().AddressOf());
  6994. Error(str.AddressOf(), node);
  6995. ctx->type.SetDummy();
  6996. return;
  6997. }
  6998. if( !rctx->type.dataType.IsIntegerType() &&
  6999. !rctx->type.dataType.IsUnsignedType() &&
  7000. !rctx->type.dataType.IsFloatType() &&
  7001. !rctx->type.dataType.IsDoubleType() )
  7002. {
  7003. asCString str;
  7004. str.Format(TXT_NO_CONVERSION_s_TO_MATH_TYPE, rctx->type.dataType.Format().AddressOf());
  7005. Error(str.AddressOf(), node);
  7006. ctx->type.SetDummy();
  7007. return;
  7008. }
  7009. bool isConstant = lctx->type.isConstant && rctx->type.isConstant;
  7010. // Verify if we are dividing with a constant zero
  7011. int op = node->tokenType;
  7012. if( rctx->type.isConstant && rctx->type.qwordValue == 0 &&
  7013. (op == ttSlash || op == ttDivAssign ||
  7014. op == ttPercent || op == ttModAssign) )
  7015. {
  7016. Error(TXT_DIVIDE_BY_ZERO, node);
  7017. }
  7018. if( !isConstant )
  7019. {
  7020. ConvertToVariableNotIn(lctx, rctx);
  7021. ConvertToVariableNotIn(rctx, lctx);
  7022. ReleaseTemporaryVariable(lctx->type, &lctx->bc);
  7023. ReleaseTemporaryVariable(rctx->type, &rctx->bc);
  7024. if( op == ttAddAssign || op == ttSubAssign ||
  7025. op == ttMulAssign || op == ttDivAssign ||
  7026. op == ttModAssign )
  7027. {
  7028. // Merge the operands in the different order so that they are evaluated correctly
  7029. MergeExprContexts(ctx, rctx);
  7030. MergeExprContexts(ctx, lctx);
  7031. }
  7032. else
  7033. {
  7034. MergeExprContexts(ctx, lctx);
  7035. MergeExprContexts(ctx, rctx);
  7036. }
  7037. asEBCInstr instruction = asBC_ADDi;
  7038. if( lctx->type.dataType.IsIntegerType() ||
  7039. lctx->type.dataType.IsUnsignedType() )
  7040. {
  7041. if( lctx->type.dataType.GetSizeInMemoryDWords() == 1 )
  7042. {
  7043. if( op == ttPlus || op == ttAddAssign )
  7044. instruction = asBC_ADDi;
  7045. else if( op == ttMinus || op == ttSubAssign )
  7046. instruction = asBC_SUBi;
  7047. else if( op == ttStar || op == ttMulAssign )
  7048. instruction = asBC_MULi;
  7049. else if( op == ttSlash || op == ttDivAssign )
  7050. instruction = asBC_DIVi;
  7051. else if( op == ttPercent || op == ttModAssign )
  7052. instruction = asBC_MODi;
  7053. }
  7054. else
  7055. {
  7056. if( op == ttPlus || op == ttAddAssign )
  7057. instruction = asBC_ADDi64;
  7058. else if( op == ttMinus || op == ttSubAssign )
  7059. instruction = asBC_SUBi64;
  7060. else if( op == ttStar || op == ttMulAssign )
  7061. instruction = asBC_MULi64;
  7062. else if( op == ttSlash || op == ttDivAssign )
  7063. instruction = asBC_DIVi64;
  7064. else if( op == ttPercent || op == ttModAssign )
  7065. instruction = asBC_MODi64;
  7066. }
  7067. }
  7068. else if( lctx->type.dataType.IsFloatType() )
  7069. {
  7070. if( op == ttPlus || op == ttAddAssign )
  7071. instruction = asBC_ADDf;
  7072. else if( op == ttMinus || op == ttSubAssign )
  7073. instruction = asBC_SUBf;
  7074. else if( op == ttStar || op == ttMulAssign )
  7075. instruction = asBC_MULf;
  7076. else if( op == ttSlash || op == ttDivAssign )
  7077. instruction = asBC_DIVf;
  7078. else if( op == ttPercent || op == ttModAssign )
  7079. instruction = asBC_MODf;
  7080. }
  7081. else if( lctx->type.dataType.IsDoubleType() )
  7082. {
  7083. if( op == ttPlus || op == ttAddAssign )
  7084. instruction = asBC_ADDd;
  7085. else if( op == ttMinus || op == ttSubAssign )
  7086. instruction = asBC_SUBd;
  7087. else if( op == ttStar || op == ttMulAssign )
  7088. instruction = asBC_MULd;
  7089. else if( op == ttSlash || op == ttDivAssign )
  7090. instruction = asBC_DIVd;
  7091. else if( op == ttPercent || op == ttModAssign )
  7092. instruction = asBC_MODd;
  7093. }
  7094. else
  7095. {
  7096. // Shouldn't be possible
  7097. asASSERT(false);
  7098. }
  7099. // Do the operation
  7100. int a = AllocateVariable(lctx->type.dataType, true);
  7101. int b = lctx->type.stackOffset;
  7102. int c = rctx->type.stackOffset;
  7103. ctx->bc.InstrW_W_W(instruction, a, b, c);
  7104. ctx->type.SetVariable(lctx->type.dataType, a, true);
  7105. }
  7106. else
  7107. {
  7108. // Both values are constants
  7109. if( lctx->type.dataType.IsIntegerType() ||
  7110. lctx->type.dataType.IsUnsignedType() )
  7111. {
  7112. if( lctx->type.dataType.GetSizeInMemoryDWords() == 1 )
  7113. {
  7114. int v = 0;
  7115. if( op == ttPlus )
  7116. v = lctx->type.intValue + rctx->type.intValue;
  7117. else if( op == ttMinus )
  7118. v = lctx->type.intValue - rctx->type.intValue;
  7119. else if( op == ttStar )
  7120. v = lctx->type.intValue * rctx->type.intValue;
  7121. else if( op == ttSlash )
  7122. {
  7123. if( rctx->type.intValue == 0 )
  7124. v = 0;
  7125. else
  7126. v = lctx->type.intValue / rctx->type.intValue;
  7127. }
  7128. else if( op == ttPercent )
  7129. {
  7130. if( rctx->type.intValue == 0 )
  7131. v = 0;
  7132. else
  7133. v = lctx->type.intValue % rctx->type.intValue;
  7134. }
  7135. ctx->type.SetConstantDW(lctx->type.dataType, v);
  7136. // If the right value is greater than the left value in a minus operation, then we need to convert the type to int
  7137. if( lctx->type.dataType.GetTokenType() == ttUInt && op == ttMinus && lctx->type.intValue < rctx->type.intValue )
  7138. ctx->type.dataType.SetTokenType(ttInt);
  7139. }
  7140. else
  7141. {
  7142. asQWORD v = 0;
  7143. if( op == ttPlus )
  7144. v = lctx->type.qwordValue + rctx->type.qwordValue;
  7145. else if( op == ttMinus )
  7146. v = lctx->type.qwordValue - rctx->type.qwordValue;
  7147. else if( op == ttStar )
  7148. v = lctx->type.qwordValue * rctx->type.qwordValue;
  7149. else if( op == ttSlash )
  7150. {
  7151. if( rctx->type.qwordValue == 0 )
  7152. v = 0;
  7153. else
  7154. v = lctx->type.qwordValue / rctx->type.qwordValue;
  7155. }
  7156. else if( op == ttPercent )
  7157. {
  7158. if( rctx->type.qwordValue == 0 )
  7159. v = 0;
  7160. else
  7161. v = lctx->type.qwordValue % rctx->type.qwordValue;
  7162. }
  7163. ctx->type.SetConstantQW(lctx->type.dataType, v);
  7164. // If the right value is greater than the left value in a minus operation, then we need to convert the type to int
  7165. if( lctx->type.dataType.GetTokenType() == ttUInt64 && op == ttMinus && lctx->type.qwordValue < rctx->type.qwordValue )
  7166. ctx->type.dataType.SetTokenType(ttInt64);
  7167. }
  7168. }
  7169. else if( lctx->type.dataType.IsFloatType() )
  7170. {
  7171. float v = 0.0f;
  7172. if( op == ttPlus )
  7173. v = lctx->type.floatValue + rctx->type.floatValue;
  7174. else if( op == ttMinus )
  7175. v = lctx->type.floatValue - rctx->type.floatValue;
  7176. else if( op == ttStar )
  7177. v = lctx->type.floatValue * rctx->type.floatValue;
  7178. else if( op == ttSlash )
  7179. {
  7180. if( rctx->type.floatValue == 0 )
  7181. v = 0;
  7182. else
  7183. v = lctx->type.floatValue / rctx->type.floatValue;
  7184. }
  7185. else if( op == ttPercent )
  7186. {
  7187. if( rctx->type.floatValue == 0 )
  7188. v = 0;
  7189. else
  7190. v = fmodf(lctx->type.floatValue, rctx->type.floatValue);
  7191. }
  7192. ctx->type.SetConstantF(lctx->type.dataType, v);
  7193. }
  7194. else if( lctx->type.dataType.IsDoubleType() )
  7195. {
  7196. double v = 0.0;
  7197. if( op == ttPlus )
  7198. v = lctx->type.doubleValue + rctx->type.doubleValue;
  7199. else if( op == ttMinus )
  7200. v = lctx->type.doubleValue - rctx->type.doubleValue;
  7201. else if( op == ttStar )
  7202. v = lctx->type.doubleValue * rctx->type.doubleValue;
  7203. else if( op == ttSlash )
  7204. {
  7205. if( rctx->type.doubleValue == 0 )
  7206. v = 0;
  7207. else
  7208. v = lctx->type.doubleValue / rctx->type.doubleValue;
  7209. }
  7210. else if( op == ttPercent )
  7211. {
  7212. if( rctx->type.doubleValue == 0 )
  7213. v = 0;
  7214. else
  7215. v = fmod(lctx->type.doubleValue, rctx->type.doubleValue);
  7216. }
  7217. ctx->type.SetConstantD(lctx->type.dataType, v);
  7218. }
  7219. else
  7220. {
  7221. // Shouldn't be possible
  7222. asASSERT(false);
  7223. }
  7224. }
  7225. }
  7226. void asCCompiler::CompileBitwiseOperator(asCScriptNode *node, asSExprContext *lctx, asSExprContext *rctx, asSExprContext *ctx)
  7227. {
  7228. // TODO: If a constant is only using 32bits, then a 32bit operation is preferred
  7229. int op = node->tokenType;
  7230. if( op == ttAmp || op == ttAndAssign ||
  7231. op == ttBitOr || op == ttOrAssign ||
  7232. op == ttBitXor || op == ttXorAssign )
  7233. {
  7234. // Convert left hand operand to integer if it's not already one
  7235. asCDataType to;
  7236. if( lctx->type.dataType.GetSizeInMemoryDWords() == 2 ||
  7237. rctx->type.dataType.GetSizeInMemoryDWords() == 2 )
  7238. to.SetTokenType(ttUInt64);
  7239. else
  7240. to.SetTokenType(ttUInt);
  7241. // Do the actual conversion
  7242. asCArray<int> reservedVars;
  7243. rctx->bc.GetVarsUsed(reservedVars);
  7244. ImplicitConversion(lctx, to, node, asIC_IMPLICIT_CONV, true, &reservedVars);
  7245. // Verify that the conversion was successful
  7246. if( !lctx->type.dataType.IsUnsignedType() )
  7247. {
  7248. asCString str;
  7249. str.Format(TXT_NO_CONVERSION_s_TO_s, lctx->type.dataType.Format().AddressOf(), to.Format().AddressOf());
  7250. Error(str.AddressOf(), node);
  7251. }
  7252. // Convert right hand operand to same type as left hand operand
  7253. asCArray<int> vars;
  7254. lctx->bc.GetVarsUsed(vars);
  7255. ImplicitConversion(rctx, lctx->type.dataType, node, asIC_IMPLICIT_CONV, true, &vars);
  7256. if( !rctx->type.dataType.IsEqualExceptRef(lctx->type.dataType) )
  7257. {
  7258. asCString str;
  7259. str.Format(TXT_NO_CONVERSION_s_TO_s, rctx->type.dataType.Format().AddressOf(), lctx->type.dataType.Format().AddressOf());
  7260. Error(str.AddressOf(), node);
  7261. }
  7262. bool isConstant = lctx->type.isConstant && rctx->type.isConstant;
  7263. if( !isConstant )
  7264. {
  7265. ConvertToVariableNotIn(lctx, rctx);
  7266. ConvertToVariableNotIn(rctx, lctx);
  7267. ReleaseTemporaryVariable(lctx->type, &lctx->bc);
  7268. ReleaseTemporaryVariable(rctx->type, &rctx->bc);
  7269. if( op == ttAndAssign || op == ttOrAssign || op == ttXorAssign )
  7270. {
  7271. // Compound assignments execute the right hand value first
  7272. MergeExprContexts(ctx, rctx);
  7273. MergeExprContexts(ctx, lctx);
  7274. }
  7275. else
  7276. {
  7277. MergeExprContexts(ctx, lctx);
  7278. MergeExprContexts(ctx, rctx);
  7279. }
  7280. asEBCInstr instruction = asBC_BAND;
  7281. if( lctx->type.dataType.GetSizeInMemoryDWords() == 1 )
  7282. {
  7283. if( op == ttAmp || op == ttAndAssign )
  7284. instruction = asBC_BAND;
  7285. else if( op == ttBitOr || op == ttOrAssign )
  7286. instruction = asBC_BOR;
  7287. else if( op == ttBitXor || op == ttXorAssign )
  7288. instruction = asBC_BXOR;
  7289. }
  7290. else
  7291. {
  7292. if( op == ttAmp || op == ttAndAssign )
  7293. instruction = asBC_BAND64;
  7294. else if( op == ttBitOr || op == ttOrAssign )
  7295. instruction = asBC_BOR64;
  7296. else if( op == ttBitXor || op == ttXorAssign )
  7297. instruction = asBC_BXOR64;
  7298. }
  7299. // Do the operation
  7300. int a = AllocateVariable(lctx->type.dataType, true);
  7301. int b = lctx->type.stackOffset;
  7302. int c = rctx->type.stackOffset;
  7303. ctx->bc.InstrW_W_W(instruction, a, b, c);
  7304. ctx->type.SetVariable(lctx->type.dataType, a, true);
  7305. }
  7306. else
  7307. {
  7308. if( lctx->type.dataType.GetSizeInMemoryDWords() == 2 )
  7309. {
  7310. asQWORD v = 0;
  7311. if( op == ttAmp )
  7312. v = lctx->type.qwordValue & rctx->type.qwordValue;
  7313. else if( op == ttBitOr )
  7314. v = lctx->type.qwordValue | rctx->type.qwordValue;
  7315. else if( op == ttBitXor )
  7316. v = lctx->type.qwordValue ^ rctx->type.qwordValue;
  7317. // Remember the result
  7318. ctx->type.SetConstantQW(lctx->type.dataType, v);
  7319. }
  7320. else
  7321. {
  7322. asDWORD v = 0;
  7323. if( op == ttAmp )
  7324. v = lctx->type.dwordValue & rctx->type.dwordValue;
  7325. else if( op == ttBitOr )
  7326. v = lctx->type.dwordValue | rctx->type.dwordValue;
  7327. else if( op == ttBitXor )
  7328. v = lctx->type.dwordValue ^ rctx->type.dwordValue;
  7329. // Remember the result
  7330. ctx->type.SetConstantDW(lctx->type.dataType, v);
  7331. }
  7332. }
  7333. }
  7334. else if( op == ttBitShiftLeft || op == ttShiftLeftAssign ||
  7335. op == ttBitShiftRight || op == ttShiftRightLAssign ||
  7336. op == ttBitShiftRightArith || op == ttShiftRightAAssign )
  7337. {
  7338. // Don't permit object to primitive conversion, since we don't know which integer type is the correct one
  7339. if( lctx->type.dataType.IsObject() )
  7340. {
  7341. asCString str;
  7342. str.Format(TXT_ILLEGAL_OPERATION_ON_s, lctx->type.dataType.Format().AddressOf());
  7343. Error(str.AddressOf(), node);
  7344. // Set an integer value and allow the compiler to continue
  7345. ctx->type.SetConstantDW(asCDataType::CreatePrimitive(ttInt, true), 0);
  7346. return;
  7347. }
  7348. // Convert left hand operand to integer if it's not already one
  7349. asCDataType to = lctx->type.dataType;
  7350. if( lctx->type.dataType.IsUnsignedType() &&
  7351. lctx->type.dataType.GetSizeInMemoryBytes() < 4 )
  7352. {
  7353. to = asCDataType::CreatePrimitive(ttUInt, false);
  7354. }
  7355. else if( !lctx->type.dataType.IsUnsignedType() )
  7356. {
  7357. asCDataType to;
  7358. if( lctx->type.dataType.GetSizeInMemoryDWords() == 2 )
  7359. to.SetTokenType(ttInt64);
  7360. else
  7361. to.SetTokenType(ttInt);
  7362. }
  7363. // Do the actual conversion
  7364. asCArray<int> reservedVars;
  7365. rctx->bc.GetVarsUsed(reservedVars);
  7366. ImplicitConversion(lctx, to, node, asIC_IMPLICIT_CONV, true, &reservedVars);
  7367. // Verify that the conversion was successful
  7368. if( lctx->type.dataType != to )
  7369. {
  7370. asCString str;
  7371. str.Format(TXT_NO_CONVERSION_s_TO_s, lctx->type.dataType.Format().AddressOf(), to.Format().AddressOf());
  7372. Error(str.AddressOf(), node);
  7373. }
  7374. // Right operand must be 32bit uint
  7375. asCArray<int> vars;
  7376. lctx->bc.GetVarsUsed(vars);
  7377. ImplicitConversion(rctx, asCDataType::CreatePrimitive(ttUInt, true), node, asIC_IMPLICIT_CONV, true, &vars);
  7378. if( !rctx->type.dataType.IsUnsignedType() )
  7379. {
  7380. asCString str;
  7381. str.Format(TXT_NO_CONVERSION_s_TO_s, rctx->type.dataType.Format().AddressOf(), "uint");
  7382. Error(str.AddressOf(), node);
  7383. }
  7384. bool isConstant = lctx->type.isConstant && rctx->type.isConstant;
  7385. if( !isConstant )
  7386. {
  7387. ConvertToVariableNotIn(lctx, rctx);
  7388. ConvertToVariableNotIn(rctx, lctx);
  7389. ReleaseTemporaryVariable(lctx->type, &lctx->bc);
  7390. ReleaseTemporaryVariable(rctx->type, &rctx->bc);
  7391. if( op == ttShiftLeftAssign || op == ttShiftRightLAssign || op == ttShiftRightAAssign )
  7392. {
  7393. // Compound assignments execute the right hand value first
  7394. MergeExprContexts(ctx, rctx);
  7395. MergeExprContexts(ctx, lctx);
  7396. }
  7397. else
  7398. {
  7399. MergeExprContexts(ctx, lctx);
  7400. MergeExprContexts(ctx, rctx);
  7401. }
  7402. asEBCInstr instruction = asBC_BSLL;
  7403. if( lctx->type.dataType.GetSizeInMemoryDWords() == 1 )
  7404. {
  7405. if( op == ttBitShiftLeft || op == ttShiftLeftAssign )
  7406. instruction = asBC_BSLL;
  7407. else if( op == ttBitShiftRight || op == ttShiftRightLAssign )
  7408. instruction = asBC_BSRL;
  7409. else if( op == ttBitShiftRightArith || op == ttShiftRightAAssign )
  7410. instruction = asBC_BSRA;
  7411. }
  7412. else
  7413. {
  7414. if( op == ttBitShiftLeft || op == ttShiftLeftAssign )
  7415. instruction = asBC_BSLL64;
  7416. else if( op == ttBitShiftRight || op == ttShiftRightLAssign )
  7417. instruction = asBC_BSRL64;
  7418. else if( op == ttBitShiftRightArith || op == ttShiftRightAAssign )
  7419. instruction = asBC_BSRA64;
  7420. }
  7421. // Do the operation
  7422. int a = AllocateVariable(lctx->type.dataType, true);
  7423. int b = lctx->type.stackOffset;
  7424. int c = rctx->type.stackOffset;
  7425. ctx->bc.InstrW_W_W(instruction, a, b, c);
  7426. ctx->type.SetVariable(lctx->type.dataType, a, true);
  7427. }
  7428. else
  7429. {
  7430. if( lctx->type.dataType.GetSizeInMemoryDWords() == 1 )
  7431. {
  7432. asDWORD v = 0;
  7433. if( op == ttBitShiftLeft )
  7434. v = lctx->type.dwordValue << rctx->type.dwordValue;
  7435. else if( op == ttBitShiftRight )
  7436. v = lctx->type.dwordValue >> rctx->type.dwordValue;
  7437. else if( op == ttBitShiftRightArith )
  7438. v = lctx->type.intValue >> rctx->type.dwordValue;
  7439. ctx->type.SetConstantDW(lctx->type.dataType, v);
  7440. }
  7441. else
  7442. {
  7443. asQWORD v = 0;
  7444. if( op == ttBitShiftLeft )
  7445. v = lctx->type.qwordValue << rctx->type.dwordValue;
  7446. else if( op == ttBitShiftRight )
  7447. v = lctx->type.qwordValue >> rctx->type.dwordValue;
  7448. else if( op == ttBitShiftRightArith )
  7449. v = asINT64(lctx->type.qwordValue) >> rctx->type.dwordValue;
  7450. ctx->type.SetConstantQW(lctx->type.dataType, v);
  7451. }
  7452. }
  7453. }
  7454. }
  7455. void asCCompiler::CompileComparisonOperator(asCScriptNode *node, asSExprContext *lctx, asSExprContext *rctx, asSExprContext *ctx)
  7456. {
  7457. // Both operands must be of the same type
  7458. // Implicitly convert the operands to a number type
  7459. asCDataType to;
  7460. if( lctx->type.dataType.IsDoubleType() || rctx->type.dataType.IsDoubleType() )
  7461. to.SetTokenType(ttDouble);
  7462. else if( lctx->type.dataType.IsFloatType() || rctx->type.dataType.IsFloatType() )
  7463. to.SetTokenType(ttFloat);
  7464. else if( lctx->type.dataType.GetSizeInMemoryDWords() == 2 || rctx->type.dataType.GetSizeInMemoryDWords() == 2 )
  7465. {
  7466. if( lctx->type.dataType.IsIntegerType() || rctx->type.dataType.IsIntegerType() )
  7467. to.SetTokenType(ttInt64);
  7468. else if( lctx->type.dataType.IsUnsignedType() || rctx->type.dataType.IsUnsignedType() )
  7469. to.SetTokenType(ttUInt64);
  7470. }
  7471. else
  7472. {
  7473. if( lctx->type.dataType.IsIntegerType() || rctx->type.dataType.IsIntegerType() ||
  7474. lctx->type.dataType.IsEnumType() || rctx->type.dataType.IsEnumType() )
  7475. to.SetTokenType(ttInt);
  7476. else if( lctx->type.dataType.IsUnsignedType() || rctx->type.dataType.IsUnsignedType() )
  7477. to.SetTokenType(ttUInt);
  7478. else if( lctx->type.dataType.IsBooleanType() || rctx->type.dataType.IsBooleanType() )
  7479. to.SetTokenType(ttBool);
  7480. }
  7481. // If doing an operation with double constant and float variable, the constant should be converted to float
  7482. if( (lctx->type.isConstant && lctx->type.dataType.IsDoubleType() && !rctx->type.isConstant && rctx->type.dataType.IsFloatType()) ||
  7483. (rctx->type.isConstant && rctx->type.dataType.IsDoubleType() && !lctx->type.isConstant && lctx->type.dataType.IsFloatType()) )
  7484. to.SetTokenType(ttFloat);
  7485. // Is it an operation on signed values?
  7486. bool signMismatch = false;
  7487. if( !lctx->type.dataType.IsUnsignedType() || !rctx->type.dataType.IsUnsignedType() )
  7488. {
  7489. if( lctx->type.dataType.GetTokenType() == ttUInt64 )
  7490. {
  7491. if( !lctx->type.isConstant )
  7492. signMismatch = true;
  7493. else if( lctx->type.qwordValue & (I64(1)<<63) )
  7494. signMismatch = true;
  7495. }
  7496. if( lctx->type.dataType.GetTokenType() == ttUInt )
  7497. {
  7498. if( !lctx->type.isConstant )
  7499. signMismatch = true;
  7500. else if( lctx->type.dwordValue & (1<<31) )
  7501. signMismatch = true;
  7502. }
  7503. if( rctx->type.dataType.GetTokenType() == ttUInt64 )
  7504. {
  7505. if( !rctx->type.isConstant )
  7506. signMismatch = true;
  7507. else if( rctx->type.qwordValue & (I64(1)<<63) )
  7508. signMismatch = true;
  7509. }
  7510. if( rctx->type.dataType.GetTokenType() == ttUInt )
  7511. {
  7512. if( !rctx->type.isConstant )
  7513. signMismatch = true;
  7514. else if( rctx->type.dwordValue & (1<<31) )
  7515. signMismatch = true;
  7516. }
  7517. }
  7518. // Check for signed/unsigned mismatch
  7519. if( signMismatch )
  7520. Warning(TXT_SIGNED_UNSIGNED_MISMATCH, node);
  7521. // Do the actual conversion
  7522. asCArray<int> reservedVars;
  7523. rctx->bc.GetVarsUsed(reservedVars);
  7524. ImplicitConversion(lctx, to, node, asIC_IMPLICIT_CONV, true, &reservedVars);
  7525. ImplicitConversion(rctx, to, node, asIC_IMPLICIT_CONV);
  7526. // Verify that the conversion was successful
  7527. bool ok = true;
  7528. if( !lctx->type.dataType.IsEqualExceptConst(to) )
  7529. {
  7530. asCString str;
  7531. str.Format(TXT_NO_CONVERSION_s_TO_s, lctx->type.dataType.Format().AddressOf(), to.Format().AddressOf());
  7532. Error(str.AddressOf(), node);
  7533. ok = false;
  7534. }
  7535. if( !rctx->type.dataType.IsEqualExceptConst(to) )
  7536. {
  7537. asCString str;
  7538. str.Format(TXT_NO_CONVERSION_s_TO_s, rctx->type.dataType.Format().AddressOf(), to.Format().AddressOf());
  7539. Error(str.AddressOf(), node);
  7540. ok = false;
  7541. }
  7542. if( !ok )
  7543. {
  7544. // It wasn't possible to get two valid operands, so we just return
  7545. // a boolean result and let the compiler continue.
  7546. ctx->type.SetConstantDW(asCDataType::CreatePrimitive(ttBool, true), true);
  7547. return;
  7548. }
  7549. bool isConstant = lctx->type.isConstant && rctx->type.isConstant;
  7550. int op = node->tokenType;
  7551. if( !isConstant )
  7552. {
  7553. if( to.IsBooleanType() )
  7554. {
  7555. int op = node->tokenType;
  7556. if( op == ttEqual || op == ttNotEqual )
  7557. {
  7558. // Must convert to temporary variable, because we are changing the value before comparison
  7559. ConvertToTempVariableNotIn(lctx, rctx);
  7560. ConvertToTempVariableNotIn(rctx, lctx);
  7561. ReleaseTemporaryVariable(lctx->type, &lctx->bc);
  7562. ReleaseTemporaryVariable(rctx->type, &rctx->bc);
  7563. // Make sure they are equal if not false
  7564. lctx->bc.InstrWORD(asBC_NOT, lctx->type.stackOffset);
  7565. rctx->bc.InstrWORD(asBC_NOT, rctx->type.stackOffset);
  7566. MergeExprContexts(ctx, lctx);
  7567. MergeExprContexts(ctx, rctx);
  7568. int a = AllocateVariable(asCDataType::CreatePrimitive(ttBool, true), true);
  7569. int b = lctx->type.stackOffset;
  7570. int c = rctx->type.stackOffset;
  7571. if( op == ttEqual )
  7572. {
  7573. ctx->bc.InstrW_W(asBC_CMPi,b,c);
  7574. ctx->bc.Instr(asBC_TZ);
  7575. ctx->bc.InstrSHORT(asBC_CpyRtoV4, (short)a);
  7576. }
  7577. else if( op == ttNotEqual )
  7578. {
  7579. ctx->bc.InstrW_W(asBC_CMPi,b,c);
  7580. ctx->bc.Instr(asBC_TNZ);
  7581. ctx->bc.InstrSHORT(asBC_CpyRtoV4, (short)a);
  7582. }
  7583. ctx->type.SetVariable(asCDataType::CreatePrimitive(ttBool, true), a, true);
  7584. }
  7585. else
  7586. {
  7587. // TODO: Use TXT_ILLEGAL_OPERATION_ON
  7588. Error(TXT_ILLEGAL_OPERATION, node);
  7589. ctx->type.SetConstantDW(asCDataType::CreatePrimitive(ttBool, true), 0);
  7590. }
  7591. }
  7592. else
  7593. {
  7594. ConvertToVariableNotIn(lctx, rctx);
  7595. ConvertToVariableNotIn(rctx, lctx);
  7596. ReleaseTemporaryVariable(lctx->type, &lctx->bc);
  7597. ReleaseTemporaryVariable(rctx->type, &rctx->bc);
  7598. MergeExprContexts(ctx, lctx);
  7599. MergeExprContexts(ctx, rctx);
  7600. asEBCInstr iCmp = asBC_CMPi, iT = asBC_TZ;
  7601. if( lctx->type.dataType.IsIntegerType() && lctx->type.dataType.GetSizeInMemoryDWords() == 1 )
  7602. iCmp = asBC_CMPi;
  7603. else if( lctx->type.dataType.IsUnsignedType() && lctx->type.dataType.GetSizeInMemoryDWords() == 1 )
  7604. iCmp = asBC_CMPu;
  7605. else if( lctx->type.dataType.IsIntegerType() && lctx->type.dataType.GetSizeInMemoryDWords() == 2 )
  7606. iCmp = asBC_CMPi64;
  7607. else if( lctx->type.dataType.IsUnsignedType() && lctx->type.dataType.GetSizeInMemoryDWords() == 2 )
  7608. iCmp = asBC_CMPu64;
  7609. else if( lctx->type.dataType.IsFloatType() )
  7610. iCmp = asBC_CMPf;
  7611. else if( lctx->type.dataType.IsDoubleType() )
  7612. iCmp = asBC_CMPd;
  7613. else
  7614. asASSERT(false);
  7615. if( op == ttEqual )
  7616. iT = asBC_TZ;
  7617. else if( op == ttNotEqual )
  7618. iT = asBC_TNZ;
  7619. else if( op == ttLessThan )
  7620. iT = asBC_TS;
  7621. else if( op == ttLessThanOrEqual )
  7622. iT = asBC_TNP;
  7623. else if( op == ttGreaterThan )
  7624. iT = asBC_TP;
  7625. else if( op == ttGreaterThanOrEqual )
  7626. iT = asBC_TNS;
  7627. int a = AllocateVariable(asCDataType::CreatePrimitive(ttBool, true), true);
  7628. int b = lctx->type.stackOffset;
  7629. int c = rctx->type.stackOffset;
  7630. ctx->bc.InstrW_W(iCmp, b, c);
  7631. ctx->bc.Instr(iT);
  7632. ctx->bc.InstrSHORT(asBC_CpyRtoV4, (short)a);
  7633. ctx->type.SetVariable(asCDataType::CreatePrimitive(ttBool, true), a, true);
  7634. }
  7635. }
  7636. else
  7637. {
  7638. if( to.IsBooleanType() )
  7639. {
  7640. int op = node->tokenType;
  7641. if( op == ttEqual || op == ttNotEqual )
  7642. {
  7643. // Make sure they are equal if not false
  7644. if( lctx->type.dwordValue != 0 ) lctx->type.dwordValue = VALUE_OF_BOOLEAN_TRUE;
  7645. if( rctx->type.dwordValue != 0 ) rctx->type.dwordValue = VALUE_OF_BOOLEAN_TRUE;
  7646. asDWORD v = 0;
  7647. if( op == ttEqual )
  7648. {
  7649. v = lctx->type.intValue - rctx->type.intValue;
  7650. if( v == 0 ) v = VALUE_OF_BOOLEAN_TRUE; else v = 0;
  7651. }
  7652. else if( op == ttNotEqual )
  7653. {
  7654. v = lctx->type.intValue - rctx->type.intValue;
  7655. if( v != 0 ) v = VALUE_OF_BOOLEAN_TRUE; else v = 0;
  7656. }
  7657. ctx->type.SetConstantDW(asCDataType::CreatePrimitive(ttBool, true), v);
  7658. }
  7659. else
  7660. {
  7661. // TODO: Use TXT_ILLEGAL_OPERATION_ON
  7662. Error(TXT_ILLEGAL_OPERATION, node);
  7663. }
  7664. }
  7665. else
  7666. {
  7667. int i = 0;
  7668. if( lctx->type.dataType.IsIntegerType() && lctx->type.dataType.GetSizeInMemoryDWords() == 1 )
  7669. {
  7670. int v = lctx->type.intValue - rctx->type.intValue;
  7671. if( v < 0 ) i = -1;
  7672. if( v > 0 ) i = 1;
  7673. }
  7674. else if( lctx->type.dataType.IsUnsignedType() && lctx->type.dataType.GetSizeInMemoryDWords() == 1 )
  7675. {
  7676. asDWORD v1 = lctx->type.dwordValue;
  7677. asDWORD v2 = rctx->type.dwordValue;
  7678. if( v1 < v2 ) i = -1;
  7679. if( v1 > v2 ) i = 1;
  7680. }
  7681. else if( lctx->type.dataType.IsIntegerType() && lctx->type.dataType.GetSizeInMemoryDWords() == 2 )
  7682. {
  7683. asINT64 v = asINT64(lctx->type.qwordValue) - asINT64(rctx->type.qwordValue);
  7684. if( v < 0 ) i = -1;
  7685. if( v > 0 ) i = 1;
  7686. }
  7687. else if( lctx->type.dataType.IsUnsignedType() && lctx->type.dataType.GetSizeInMemoryDWords() == 2 )
  7688. {
  7689. asQWORD v1 = lctx->type.qwordValue;
  7690. asQWORD v2 = rctx->type.qwordValue;
  7691. if( v1 < v2 ) i = -1;
  7692. if( v1 > v2 ) i = 1;
  7693. }
  7694. else if( lctx->type.dataType.IsFloatType() )
  7695. {
  7696. float v = lctx->type.floatValue - rctx->type.floatValue;
  7697. if( v < 0 ) i = -1;
  7698. if( v > 0 ) i = 1;
  7699. }
  7700. else if( lctx->type.dataType.IsDoubleType() )
  7701. {
  7702. double v = lctx->type.doubleValue - rctx->type.doubleValue;
  7703. if( v < 0 ) i = -1;
  7704. if( v > 0 ) i = 1;
  7705. }
  7706. if( op == ttEqual )
  7707. i = (i == 0 ? VALUE_OF_BOOLEAN_TRUE : 0);
  7708. else if( op == ttNotEqual )
  7709. i = (i != 0 ? VALUE_OF_BOOLEAN_TRUE : 0);
  7710. else if( op == ttLessThan )
  7711. i = (i < 0 ? VALUE_OF_BOOLEAN_TRUE : 0);
  7712. else if( op == ttLessThanOrEqual )
  7713. i = (i <= 0 ? VALUE_OF_BOOLEAN_TRUE : 0);
  7714. else if( op == ttGreaterThan )
  7715. i = (i > 0 ? VALUE_OF_BOOLEAN_TRUE : 0);
  7716. else if( op == ttGreaterThanOrEqual )
  7717. i = (i >= 0 ? VALUE_OF_BOOLEAN_TRUE : 0);
  7718. ctx->type.SetConstantDW(asCDataType::CreatePrimitive(ttBool, true), i);
  7719. }
  7720. }
  7721. }
  7722. void asCCompiler::PushVariableOnStack(asSExprContext *ctx, bool asReference)
  7723. {
  7724. // Put the result on the stack
  7725. ctx->bc.InstrSHORT(asBC_PSF, ctx->type.stackOffset);
  7726. if( asReference )
  7727. ctx->type.dataType.MakeReference(true);
  7728. else
  7729. {
  7730. if( ctx->type.dataType.GetSizeInMemoryDWords() == 1 )
  7731. ctx->bc.Instr(asBC_RDS4);
  7732. else
  7733. ctx->bc.Instr(asBC_RDS8);
  7734. }
  7735. }
  7736. void asCCompiler::CompileBooleanOperator(asCScriptNode *node, asSExprContext *lctx, asSExprContext *rctx, asSExprContext *ctx)
  7737. {
  7738. // Both operands must be booleans
  7739. asCDataType to;
  7740. to.SetTokenType(ttBool);
  7741. // Do the actual conversion
  7742. asCArray<int> reservedVars;
  7743. rctx->bc.GetVarsUsed(reservedVars);
  7744. lctx->bc.GetVarsUsed(reservedVars);
  7745. ImplicitConversion(lctx, to, node, asIC_IMPLICIT_CONV, true, &reservedVars);
  7746. ImplicitConversion(rctx, to, node, asIC_IMPLICIT_CONV, true, &reservedVars);
  7747. // Verify that the conversion was successful
  7748. if( !lctx->type.dataType.IsBooleanType() )
  7749. {
  7750. asCString str;
  7751. str.Format(TXT_NO_CONVERSION_s_TO_s, lctx->type.dataType.Format().AddressOf(), "bool");
  7752. Error(str.AddressOf(), node);
  7753. // Force the conversion to allow compilation to proceed
  7754. lctx->type.SetConstantB(asCDataType::CreatePrimitive(ttBool, true), true);
  7755. }
  7756. if( !rctx->type.dataType.IsBooleanType() )
  7757. {
  7758. asCString str;
  7759. str.Format(TXT_NO_CONVERSION_s_TO_s, rctx->type.dataType.Format().AddressOf(), "bool");
  7760. Error(str.AddressOf(), node);
  7761. // Force the conversion to allow compilation to proceed
  7762. rctx->type.SetConstantB(asCDataType::CreatePrimitive(ttBool, true), true);
  7763. }
  7764. bool isConstant = lctx->type.isConstant && rctx->type.isConstant;
  7765. ctx->type.Set(asCDataType::CreatePrimitive(ttBool, true));
  7766. // What kind of operator is it?
  7767. int op = node->tokenType;
  7768. if( op == ttXor )
  7769. {
  7770. if( !isConstant )
  7771. {
  7772. // Must convert to temporary variable, because we are changing the value before comparison
  7773. ConvertToTempVariableNotIn(lctx, rctx);
  7774. ConvertToTempVariableNotIn(rctx, lctx);
  7775. ReleaseTemporaryVariable(lctx->type, &lctx->bc);
  7776. ReleaseTemporaryVariable(rctx->type, &rctx->bc);
  7777. // Make sure they are equal if not false
  7778. lctx->bc.InstrWORD(asBC_NOT, lctx->type.stackOffset);
  7779. rctx->bc.InstrWORD(asBC_NOT, rctx->type.stackOffset);
  7780. MergeExprContexts(ctx, lctx);
  7781. MergeExprContexts(ctx, rctx);
  7782. int a = AllocateVariable(ctx->type.dataType, true);
  7783. int b = lctx->type.stackOffset;
  7784. int c = rctx->type.stackOffset;
  7785. ctx->bc.InstrW_W_W(asBC_BXOR,a,b,c);
  7786. ctx->type.SetVariable(asCDataType::CreatePrimitive(ttBool, true), a, true);
  7787. }
  7788. else
  7789. {
  7790. // Make sure they are equal if not false
  7791. #if AS_SIZEOF_BOOL == 1
  7792. if( lctx->type.byteValue != 0 ) lctx->type.byteValue = VALUE_OF_BOOLEAN_TRUE;
  7793. if( rctx->type.byteValue != 0 ) rctx->type.byteValue = VALUE_OF_BOOLEAN_TRUE;
  7794. asBYTE v = 0;
  7795. v = lctx->type.byteValue - rctx->type.byteValue;
  7796. if( v != 0 ) v = VALUE_OF_BOOLEAN_TRUE; else v = 0;
  7797. ctx->type.isConstant = true;
  7798. ctx->type.byteValue = v;
  7799. #else
  7800. if( lctx->type.dwordValue != 0 ) lctx->type.dwordValue = VALUE_OF_BOOLEAN_TRUE;
  7801. if( rctx->type.dwordValue != 0 ) rctx->type.dwordValue = VALUE_OF_BOOLEAN_TRUE;
  7802. asDWORD v = 0;
  7803. v = lctx->type.intValue - rctx->type.intValue;
  7804. if( v != 0 ) v = VALUE_OF_BOOLEAN_TRUE; else v = 0;
  7805. ctx->type.isConstant = true;
  7806. ctx->type.dwordValue = v;
  7807. #endif
  7808. }
  7809. }
  7810. else if( op == ttAnd ||
  7811. op == ttOr )
  7812. {
  7813. if( !isConstant )
  7814. {
  7815. // If or-operator and first value is 1 the second value shouldn't be calculated
  7816. // if and-operator and first value is 0 the second value shouldn't be calculated
  7817. ConvertToVariable(lctx);
  7818. ReleaseTemporaryVariable(lctx->type, &lctx->bc);
  7819. MergeExprContexts(ctx, lctx);
  7820. int offset = AllocateVariable(asCDataType::CreatePrimitive(ttBool, false), true);
  7821. int label1 = nextLabel++;
  7822. int label2 = nextLabel++;
  7823. if( op == ttAnd )
  7824. {
  7825. ctx->bc.InstrSHORT(asBC_CpyVtoR4, lctx->type.stackOffset);
  7826. ctx->bc.Instr(asBC_ClrHi);
  7827. ctx->bc.InstrDWORD(asBC_JNZ, label1);
  7828. ctx->bc.InstrW_DW(asBC_SetV4, (asWORD)offset, 0);
  7829. ctx->bc.InstrINT(asBC_JMP, label2);
  7830. }
  7831. else if( op == ttOr )
  7832. {
  7833. ctx->bc.InstrSHORT(asBC_CpyVtoR4, lctx->type.stackOffset);
  7834. ctx->bc.Instr(asBC_ClrHi);
  7835. ctx->bc.InstrDWORD(asBC_JZ, label1);
  7836. #if AS_SIZEOF_BOOL == 1
  7837. ctx->bc.InstrSHORT_B(asBC_SetV1, (short)offset, VALUE_OF_BOOLEAN_TRUE);
  7838. #else
  7839. ctx->bc.InstrSHORT_DW(asBC_SetV4, (short)offset, VALUE_OF_BOOLEAN_TRUE);
  7840. #endif
  7841. ctx->bc.InstrINT(asBC_JMP, label2);
  7842. }
  7843. ctx->bc.Label((short)label1);
  7844. ConvertToVariable(rctx);
  7845. ReleaseTemporaryVariable(rctx->type, &rctx->bc);
  7846. rctx->bc.InstrW_W(asBC_CpyVtoV4, offset, rctx->type.stackOffset);
  7847. MergeExprContexts(ctx, rctx);
  7848. ctx->bc.Label((short)label2);
  7849. ctx->type.SetVariable(asCDataType::CreatePrimitive(ttBool, false), offset, true);
  7850. }
  7851. else
  7852. {
  7853. #if AS_SIZEOF_BOOL == 1
  7854. asBYTE v = 0;
  7855. if( op == ttAnd )
  7856. v = lctx->type.byteValue && rctx->type.byteValue;
  7857. else if( op == ttOr )
  7858. v = lctx->type.byteValue || rctx->type.byteValue;
  7859. // Remember the result
  7860. ctx->type.isConstant = true;
  7861. ctx->type.byteValue = v;
  7862. #else
  7863. asDWORD v = 0;
  7864. if( op == ttAnd )
  7865. v = lctx->type.dwordValue && rctx->type.dwordValue;
  7866. else if( op == ttOr )
  7867. v = lctx->type.dwordValue || rctx->type.dwordValue;
  7868. // Remember the result
  7869. ctx->type.isConstant = true;
  7870. ctx->type.dwordValue = v;
  7871. #endif
  7872. }
  7873. }
  7874. }
  7875. void asCCompiler::CompileOperatorOnHandles(asCScriptNode *node, asSExprContext *lctx, asSExprContext *rctx, asSExprContext *ctx)
  7876. {
  7877. // Process the property accessor as get
  7878. ProcessPropertyGetAccessor(lctx, node);
  7879. ProcessPropertyGetAccessor(rctx, node);
  7880. // Make sure lctx doesn't end up with a variable used in rctx
  7881. if( lctx->type.isTemporary && rctx->bc.IsVarUsed(lctx->type.stackOffset) )
  7882. {
  7883. asCArray<int> vars;
  7884. rctx->bc.GetVarsUsed(vars);
  7885. int offset = AllocateVariable(lctx->type.dataType, true);
  7886. rctx->bc.ExchangeVar(lctx->type.stackOffset, offset);
  7887. ReleaseTemporaryVariable(offset, 0);
  7888. }
  7889. // Warn if not both operands are explicit handles
  7890. if( (node->tokenType == ttEqual || node->tokenType == ttNotEqual) &&
  7891. ((!lctx->type.isExplicitHandle && !(lctx->type.dataType.GetObjectType() && (lctx->type.dataType.GetObjectType()->flags & asOBJ_IMPLICIT_HANDLE))) ||
  7892. (!rctx->type.isExplicitHandle && !(rctx->type.dataType.GetObjectType() && (rctx->type.dataType.GetObjectType()->flags & asOBJ_IMPLICIT_HANDLE)))) )
  7893. {
  7894. Warning(TXT_HANDLE_COMPARISON, node);
  7895. }
  7896. // Implicitly convert null to the other type
  7897. asCDataType to;
  7898. if( lctx->type.IsNullConstant() )
  7899. to = rctx->type.dataType;
  7900. else if( rctx->type.IsNullConstant() )
  7901. to = lctx->type.dataType;
  7902. else
  7903. {
  7904. // TODO: Use the common base type
  7905. to = lctx->type.dataType;
  7906. }
  7907. // Need to pop the value if it is a null constant
  7908. if( lctx->type.IsNullConstant() )
  7909. lctx->bc.Pop(AS_PTR_SIZE);
  7910. if( rctx->type.IsNullConstant() )
  7911. rctx->bc.Pop(AS_PTR_SIZE);
  7912. // Convert both sides to explicit handles
  7913. to.MakeHandle(true);
  7914. to.MakeReference(false);
  7915. // Do the conversion
  7916. ImplicitConversion(lctx, to, node, asIC_IMPLICIT_CONV);
  7917. ImplicitConversion(rctx, to, node, asIC_IMPLICIT_CONV);
  7918. // Both operands must be of the same type
  7919. // Verify that the conversion was successful
  7920. if( !lctx->type.dataType.IsEqualExceptConst(to) )
  7921. {
  7922. asCString str;
  7923. str.Format(TXT_NO_CONVERSION_s_TO_s, lctx->type.dataType.Format().AddressOf(), to.Format().AddressOf());
  7924. Error(str.AddressOf(), node);
  7925. }
  7926. if( !rctx->type.dataType.IsEqualExceptConst(to) )
  7927. {
  7928. asCString str;
  7929. str.Format(TXT_NO_CONVERSION_s_TO_s, rctx->type.dataType.Format().AddressOf(), to.Format().AddressOf());
  7930. Error(str.AddressOf(), node);
  7931. }
  7932. ctx->type.Set(asCDataType::CreatePrimitive(ttBool, true));
  7933. int op = node->tokenType;
  7934. if( op == ttEqual || op == ttNotEqual || op == ttIs || op == ttNotIs )
  7935. {
  7936. // If the object handle already is in a variable we must manually pop it from the stack
  7937. if( lctx->type.isVariable )
  7938. lctx->bc.Pop(AS_PTR_SIZE);
  7939. if( rctx->type.isVariable )
  7940. rctx->bc.Pop(AS_PTR_SIZE);
  7941. // TODO: optimize: Treat the object handles as two integers, i.e. don't do REFCPY
  7942. ConvertToVariableNotIn(lctx, rctx);
  7943. ConvertToVariable(rctx);
  7944. MergeExprContexts(ctx, lctx);
  7945. MergeExprContexts(ctx, rctx);
  7946. int a = AllocateVariable(ctx->type.dataType, true);
  7947. int b = lctx->type.stackOffset;
  7948. int c = rctx->type.stackOffset;
  7949. if( op == ttEqual || op == ttIs )
  7950. {
  7951. #ifdef AS_64BIT_PTR
  7952. // TODO: Optimize: Use a 64bit integer comparison instead of double
  7953. ctx->bc.InstrW_W(asBC_CMPd, b, c);
  7954. #else
  7955. ctx->bc.InstrW_W(asBC_CMPi, b, c);
  7956. #endif
  7957. ctx->bc.Instr(asBC_TZ);
  7958. ctx->bc.InstrSHORT(asBC_CpyRtoV4, (short)a);
  7959. }
  7960. else if( op == ttNotEqual || op == ttNotIs )
  7961. {
  7962. #ifdef AS_64BIT_PTR
  7963. // TODO: Optimize: Use a 64bit integer comparison instead of double
  7964. ctx->bc.InstrW_W(asBC_CMPd, b, c);
  7965. #else
  7966. ctx->bc.InstrW_W(asBC_CMPi, b, c);
  7967. #endif
  7968. ctx->bc.Instr(asBC_TNZ);
  7969. ctx->bc.InstrSHORT(asBC_CpyRtoV4, (short)a);
  7970. }
  7971. ctx->type.SetVariable(asCDataType::CreatePrimitive(ttBool, true), a, true);
  7972. ReleaseTemporaryVariable(lctx->type, &ctx->bc);
  7973. ReleaseTemporaryVariable(rctx->type, &ctx->bc);
  7974. }
  7975. else
  7976. {
  7977. // TODO: Use TXT_ILLEGAL_OPERATION_ON
  7978. Error(TXT_ILLEGAL_OPERATION, node);
  7979. }
  7980. }
  7981. void asCCompiler::PerformFunctionCall(int funcId, asSExprContext *ctx, bool isConstructor, asCArray<asSExprContext*> *args, asCObjectType *objType, bool useVariable, int varOffset)
  7982. {
  7983. asCScriptFunction *descr = builder->GetFunctionDescription(funcId);
  7984. int argSize = descr->GetSpaceNeededForArguments();
  7985. ctx->type.Set(descr->returnType);
  7986. if( isConstructor )
  7987. {
  7988. // TODO: When value types are allocated on the stack, this won't be needed anymore
  7989. // as the constructor will be called just like any other function
  7990. asASSERT(useVariable == false);
  7991. ctx->bc.Alloc(asBC_ALLOC, objType, descr->id, argSize+AS_PTR_SIZE);
  7992. // The instruction has already moved the returned object to the variable
  7993. ctx->type.Set(asCDataType::CreatePrimitive(ttVoid, false));
  7994. // Clean up arguments
  7995. if( args )
  7996. AfterFunctionCall(funcId, *args, ctx, false);
  7997. ProcessDeferredParams(ctx);
  7998. return;
  7999. }
  8000. else if( descr->funcType == asFUNC_IMPORTED )
  8001. ctx->bc.Call(asBC_CALLBND , descr->id, argSize + (descr->objectType ? AS_PTR_SIZE : 0));
  8002. // TODO: Maybe we need two different byte codes
  8003. else if( descr->funcType == asFUNC_INTERFACE || descr->funcType == asFUNC_VIRTUAL )
  8004. ctx->bc.Call(asBC_CALLINTF, descr->id, argSize + (descr->objectType ? AS_PTR_SIZE : 0));
  8005. else if( descr->funcType == asFUNC_SCRIPT )
  8006. ctx->bc.Call(asBC_CALL , descr->id, argSize + (descr->objectType ? AS_PTR_SIZE : 0));
  8007. else // if( descr->funcType == asFUNC_SYSTEM )
  8008. ctx->bc.Call(asBC_CALLSYS , descr->id, argSize + (descr->objectType ? AS_PTR_SIZE : 0));
  8009. if( ctx->type.dataType.IsObject() && !descr->returnType.IsReference() )
  8010. {
  8011. int returnOffset = 0;
  8012. if( useVariable )
  8013. {
  8014. // Use the given variable
  8015. returnOffset = varOffset;
  8016. ctx->type.SetVariable(descr->returnType, returnOffset, false);
  8017. }
  8018. else
  8019. {
  8020. // Allocate a temporary variable for the returned object
  8021. returnOffset = AllocateVariable(descr->returnType, true);
  8022. ctx->type.SetVariable(descr->returnType, returnOffset, true);
  8023. }
  8024. ctx->type.dataType.MakeReference(true);
  8025. // Move the pointer from the object register to the temporary variable
  8026. ctx->bc.InstrSHORT(asBC_STOREOBJ, (short)returnOffset);
  8027. // Clean up arguments
  8028. if( args )
  8029. AfterFunctionCall(funcId, *args, ctx, false);
  8030. ProcessDeferredParams(ctx);
  8031. ctx->bc.InstrSHORT(asBC_PSF, (short)returnOffset);
  8032. }
  8033. else if( descr->returnType.IsReference() )
  8034. {
  8035. asASSERT(useVariable == false);
  8036. // We cannot clean up the arguments yet, because the
  8037. // reference might be pointing to one of them.
  8038. // Clean up arguments
  8039. if( args )
  8040. AfterFunctionCall(funcId, *args, ctx, true);
  8041. // Do not process the output parameters yet, because it
  8042. // might invalidate the returned reference
  8043. if( descr->returnType.IsPrimitive() )
  8044. ctx->type.Set(descr->returnType);
  8045. else
  8046. {
  8047. ctx->bc.Instr(asBC_PshRPtr);
  8048. if( descr->returnType.IsObject() && !descr->returnType.IsObjectHandle() )
  8049. {
  8050. // We are getting the pointer to the object
  8051. // not a pointer to a object variable
  8052. ctx->type.dataType.MakeReference(false);
  8053. }
  8054. }
  8055. }
  8056. else
  8057. {
  8058. asASSERT(useVariable == false);
  8059. if( descr->returnType.GetSizeInMemoryBytes() )
  8060. {
  8061. int offset = AllocateVariable(descr->returnType, true);
  8062. ctx->type.SetVariable(descr->returnType, offset, true);
  8063. // Move the value from the return register to the variable
  8064. if( descr->returnType.GetSizeOnStackDWords() == 1 )
  8065. ctx->bc.InstrSHORT(asBC_CpyRtoV4, (short)offset);
  8066. else if( descr->returnType.GetSizeOnStackDWords() == 2 )
  8067. ctx->bc.InstrSHORT(asBC_CpyRtoV8, (short)offset);
  8068. }
  8069. else
  8070. ctx->type.Set(descr->returnType);
  8071. // Clean up arguments
  8072. if( args )
  8073. AfterFunctionCall(funcId, *args, ctx, false);
  8074. ProcessDeferredParams(ctx);
  8075. }
  8076. }
  8077. void asCCompiler::MergeExprContexts(asSExprContext *before, asSExprContext *after)
  8078. {
  8079. before->bc.AddCode(&after->bc);
  8080. for( asUINT n = 0; n < after->deferredParams.GetLength(); n++ )
  8081. before->deferredParams.PushLast(after->deferredParams[n]);
  8082. after->deferredParams.SetLength(0);
  8083. asASSERT( after->origExpr == 0 );
  8084. }
  8085. void asCCompiler::FilterConst(asCArray<int> &funcs)
  8086. {
  8087. if( funcs.GetLength() == 0 ) return;
  8088. // This is only done for object methods
  8089. asCScriptFunction *desc = builder->GetFunctionDescription(funcs[0]);
  8090. if( desc->objectType == 0 ) return;
  8091. // Check if there are any non-const matches
  8092. asUINT n;
  8093. bool foundNonConst = false;
  8094. for( n = 0; n < funcs.GetLength(); n++ )
  8095. {
  8096. desc = builder->GetFunctionDescription(funcs[n]);
  8097. if( !desc->isReadOnly )
  8098. {
  8099. foundNonConst = true;
  8100. break;
  8101. }
  8102. }
  8103. if( foundNonConst )
  8104. {
  8105. // Remove all const methods
  8106. for( n = 0; n < funcs.GetLength(); n++ )
  8107. {
  8108. desc = builder->GetFunctionDescription(funcs[n]);
  8109. if( desc->isReadOnly )
  8110. {
  8111. if( n == funcs.GetLength() - 1 )
  8112. funcs.PopLast();
  8113. else
  8114. funcs[n] = funcs.PopLast();
  8115. n--;
  8116. }
  8117. }
  8118. }
  8119. }
  8120. END_AS_NAMESPACE