/xbmc/visualizations/Vortex/angelscript/angelscript/source/as_compiler.cpp
C++ | 9536 lines | 6933 code | 1388 blank | 1215 comment | 2755 complexity | a92e1baea38a8a3db634ec40d054c6da MD5 | raw file
Possible License(s): GPL-3.0, CC-BY-SA-3.0, LGPL-2.0, 0BSD, Unlicense, GPL-2.0, AGPL-1.0, BSD-3-Clause, LGPL-2.1, LGPL-3.0
Large files files are truncated, but you can click here to view the full file
- /*
- AngelCode Scripting Library
- Copyright (c) 2003-2009 Andreas Jonsson
- This software is provided 'as-is', without any express or implied
- warranty. In no event will the authors be held liable for any
- damages arising from the use of this software.
- Permission is granted to anyone to use this software for any
- purpose, including commercial applications, and to alter it and
- redistribute it freely, subject to the following restrictions:
- 1. The origin of this software must not be misrepresented; you
- must not claim that you wrote the original software. If you use
- this software in a product, an acknowledgment in the product
- documentation would be appreciated but is not required.
- 2. Altered source versions must be plainly marked as such, and
- must not be misrepresented as being the original software.
- 3. This notice may not be removed or altered from any source
- distribution.
- The original version of this library can be located at:
- http://www.angelcode.com/angelscript/
- Andreas Jonsson
- andreas@angelcode.com
- */
- //
- // as_compiler.cpp
- //
- // The class that does the actual compilation of the functions
- //
- #include <math.h> // fmodf()
- #include "as_config.h"
- #include "as_compiler.h"
- #include "as_tokendef.h"
- #include "as_tokenizer.h"
- #include "as_string_util.h"
- #include "as_texts.h"
- #include "as_parser.h"
- BEGIN_AS_NAMESPACE
- asCCompiler::asCCompiler(asCScriptEngine *engine) : byteCode(engine)
- {
- builder = 0;
- script = 0;
- variables = 0;
- isProcessingDeferredParams = false;
- noCodeOutput = 0;
- }
- asCCompiler::~asCCompiler()
- {
- while( variables )
- {
- asCVariableScope *var = variables;
- variables = variables->parent;
- asDELETE(var,asCVariableScope);
- }
- }
- void asCCompiler::Reset(asCBuilder *builder, asCScriptCode *script, asCScriptFunction *outFunc)
- {
- this->builder = builder;
- this->engine = builder->engine;
- this->script = script;
- this->outFunc = outFunc;
- hasCompileErrors = false;
- m_isConstructor = false;
- m_isConstructorCalled = false;
- nextLabel = 0;
- breakLabels.SetLength(0);
- continueLabels.SetLength(0);
- byteCode.ClearAll();
- objVariableTypes.SetLength(0);
- objVariablePos.SetLength(0);
- globalExpression = false;
- }
- int asCCompiler::CompileDefaultConstructor(asCBuilder *builder, asCScriptCode *script, asCScriptFunction *outFunc)
- {
- Reset(builder, script, outFunc);
- // If the class is derived from another, then the base class' default constructor must be called
- if( outFunc->objectType->derivedFrom )
- {
- // Call the base class' default constructor
- byteCode.InstrSHORT(asBC_PSF, 0);
- byteCode.Instr(asBC_RDSPTR);
- byteCode.Call(asBC_CALL, outFunc->objectType->derivedFrom->beh.construct, AS_PTR_SIZE);
- }
- // Pop the object pointer from the stack
- byteCode.Ret(AS_PTR_SIZE);
- byteCode.Finalize();
- // Copy byte code to the function
- outFunc->byteCode.SetLength(byteCode.GetSize());
- byteCode.Output(outFunc->byteCode.AddressOf());
- outFunc->AddReferences();
- outFunc->stackNeeded = byteCode.largestStackUsed;
- outFunc->lineNumbers = byteCode.lineNumbers;
- outFunc->objVariablePos = objVariablePos;
- outFunc->objVariableTypes = objVariableTypes;
- #ifdef AS_DEBUG
- // DEBUG: output byte code
- byteCode.DebugOutput(("__" + outFunc->objectType->name + "_" + outFunc->name + "__dc.txt").AddressOf(), engine);
- #endif
- return 0;
- }
- int asCCompiler::CompileFactory(asCBuilder *builder, asCScriptCode *script, asCScriptFunction *outFunc)
- {
- Reset(builder, script, outFunc);
- unsigned int n;
- // Find the corresponding constructor
- asCDataType dt = asCDataType::CreateObject(outFunc->returnType.GetObjectType(), false);
- int constructor = 0;
- for( n = 0; n < dt.GetBehaviour()->factories.GetLength(); n++ )
- {
- if( dt.GetBehaviour()->factories[n] == outFunc->id )
- {
- constructor = dt.GetBehaviour()->constructors[n];
- break;
- }
- }
- // Allocate the class and instanciate it with the constructor
- int varOffset = AllocateVariable(dt, true);
- byteCode.Push(AS_PTR_SIZE);
- byteCode.InstrSHORT(asBC_PSF, (short)varOffset);
- // Copy all arguments to the top of the stack
- int argDwords = (int)outFunc->GetSpaceNeededForArguments();
- for( int a = argDwords-1; a >= 0; a-- )
- byteCode.InstrSHORT(asBC_PshV4, short(-a));
- byteCode.Alloc(asBC_ALLOC, dt.GetObjectType(), constructor, argDwords + AS_PTR_SIZE);
- // Return a handle to the newly created object
- byteCode.InstrSHORT(asBC_LOADOBJ, (short)varOffset);
- byteCode.Pop(AS_PTR_SIZE);
- byteCode.Ret(argDwords);
- byteCode.Finalize();
- // Store the instantiated object as variable so it will be cleaned up on exception
- objVariableTypes.PushLast(variableAllocations[0].GetObjectType());
- objVariablePos.PushLast(GetVariableOffset(0));
- // Copy byte code to the function
- outFunc->byteCode.SetLength(byteCode.GetSize());
- byteCode.Output(outFunc->byteCode.AddressOf());
- outFunc->AddReferences();
- outFunc->stackNeeded = byteCode.largestStackUsed;
- outFunc->lineNumbers = byteCode.lineNumbers;
- outFunc->objVariablePos = objVariablePos;
- outFunc->objVariableTypes = objVariableTypes;
- // Tell the virtual machine not to clean up parameters on exception
- outFunc->dontCleanUpOnException = true;
- /*
- #ifdef AS_DEBUG
- // DEBUG: output byte code
- asCString args;
- args.Format("%d", outFunc->parameterTypes.GetLength());
- byteCode.DebugOutput(("__" + outFunc->name + "__factory" + args + ".txt").AddressOf(), engine);
- #endif
- */
- return 0;
- }
- int asCCompiler::CompileTemplateFactoryStub(asCBuilder *builder, int trueFactoryId, asCObjectType *objType, asCScriptFunction *outFunc)
- {
- Reset(builder, 0, outFunc);
- asCScriptFunction *descr = builder->GetFunctionDescription(trueFactoryId);
- byteCode.InstrPTR(asBC_OBJTYPE, objType);
- byteCode.Call(asBC_CALLSYS, trueFactoryId, descr->GetSpaceNeededForArguments());
- byteCode.Ret(outFunc->GetSpaceNeededForArguments());
- byteCode.Finalize();
- // Copy byte code to the function
- outFunc->byteCode.SetLength(byteCode.GetSize());
- byteCode.Output(outFunc->byteCode.AddressOf());
- outFunc->AddReferences();
- outFunc->stackNeeded = byteCode.largestStackUsed;
- outFunc->lineNumbers = byteCode.lineNumbers;
- outFunc->objVariablePos = objVariablePos;
- outFunc->objVariableTypes = objVariableTypes;
- // Tell the virtual machine not to clean up the object on exception
- outFunc->dontCleanUpOnException = true;
- return 0;
- }
- int asCCompiler::CompileFunction(asCBuilder *builder, asCScriptCode *script, asCScriptNode *func, asCScriptFunction *outFunc)
- {
- Reset(builder, script, outFunc);
- int buildErrors = builder->numErrors;
- int stackPos = 0;
- if( outFunc->objectType )
- stackPos = -AS_PTR_SIZE; // The first parameter is the pointer to the object
- // Reserve a label for the cleanup code
- nextLabel++;
- // Add the first variable scope, which the parameters and
- // variables declared in the outermost statement block is
- // part of.
- AddVariableScope();
- //----------------------------------------------
- // Examine return type
- bool isDestructor = false;
- asCDataType returnType;
- if( func->firstChild->nodeType == snDataType )
- {
- returnType = builder->CreateDataTypeFromNode(func->firstChild, script);
- returnType = builder->ModifyDataTypeFromNode(returnType, func->firstChild->next, script, 0, 0);
- // Make sure the return type is instanciable or is void
- if( !returnType.CanBeInstanciated() &&
- returnType != asCDataType::CreatePrimitive(ttVoid, false) )
- {
- asCString str;
- str.Format(TXT_DATA_TYPE_CANT_BE_s, returnType.Format().AddressOf());
- Error(str.AddressOf(), func->firstChild);
- }
- // TODO: Add support for returning references
- // The script language doesn't support returning references yet
- if( returnType.IsReference() )
- {
- Error(TXT_SCRIPT_FUNCTIONS_DOESNT_SUPPORT_RETURN_REF, func->firstChild);
- }
- }
- else
- {
- returnType = asCDataType::CreatePrimitive(ttVoid, false);
- if( func->firstChild->tokenType == ttBitNot )
- isDestructor = true;
- else
- m_isConstructor = true;
- }
- //----------------------------------------------
- // Declare parameters
- // Find first parameter
- asCScriptNode *node = func->firstChild;
- while( node && node->nodeType != snParameterList )
- node = node->next;
- // Register parameters from last to first, otherwise they will be destroyed in the wrong order
- asCVariableScope vs(0);
- if( node ) node = node->firstChild;
- while( node )
- {
- // Get the parameter type
- asCDataType type = builder->CreateDataTypeFromNode(node, script);
- asETypeModifiers inoutFlag = asTM_NONE;
- type = builder->ModifyDataTypeFromNode(type, node->next, script, &inoutFlag, 0);
- // Is the data type allowed?
- if( (type.IsReference() && inoutFlag != asTM_INOUTREF && !type.CanBeInstanciated()) ||
- (!type.IsReference() && !type.CanBeInstanciated()) )
- {
- asCString str;
- str.Format(TXT_PARAMETER_CANT_BE_s, type.Format().AddressOf());
- Error(str.AddressOf(), node);
- }
- // If the parameter has a name then declare it as variable
- node = node->next->next;
- if( node && node->nodeType == snIdentifier )
- {
- asCString name(&script->code[node->tokenPos], node->tokenLength);
- if( vs.DeclareVariable(name.AddressOf(), type, stackPos) < 0 )
- Error(TXT_PARAMETER_ALREADY_DECLARED, node);
- outFunc->AddVariable(name, type, stackPos);
- node = node->next;
- }
- else
- vs.DeclareVariable("", type, stackPos);
- // Move to next parameter
- stackPos -= type.GetSizeOnStackDWords();
- }
- int n;
- for( n = (int)vs.variables.GetLength() - 1; n >= 0; n-- )
- {
- variables->DeclareVariable(vs.variables[n]->name.AddressOf(), vs.variables[n]->type, vs.variables[n]->stackOffset);
- }
- // Is the return type allowed?
- if( (returnType.GetSizeOnStackDWords() == 0 && returnType != asCDataType::CreatePrimitive(ttVoid, false)) ||
- (returnType.IsReference() && !returnType.CanBeInstanciated()) )
- {
- asCString str;
- str.Format(TXT_RETURN_CANT_BE_s, returnType.Format().AddressOf());
- Error(str.AddressOf(), node);
- }
- variables->DeclareVariable("return", returnType, stackPos);
- //--------------------------------------------
- // Compile the statement block
- // We need to parse the statement block now
- // TODO: memory: We can parse the statement block one statement at a time, thus save even more memory
- asCParser parser(builder);
- int r = parser.ParseStatementBlock(script, func->lastChild);
- if( r < 0 ) return -1;
- asCScriptNode *block = parser.GetScriptNode();
- bool hasReturn;
- asCByteCode bc(engine);
- LineInstr(&bc, func->lastChild->tokenPos);
- CompileStatementBlock(block, false, &hasReturn, &bc);
- LineInstr(&bc, func->lastChild->tokenPos + func->lastChild->tokenLength);
- // Make sure there is a return in all paths (if not return type is void)
- if( returnType != asCDataType::CreatePrimitive(ttVoid, false) )
- {
- if( hasReturn == false )
- Error(TXT_NOT_ALL_PATHS_RETURN, func->lastChild);
- }
- //------------------------------------------------
- // Concatenate the bytecode
- // Insert a JitEntry at the start of the function for JIT compilers
- byteCode.InstrWORD(asBC_JitEntry, 0);
- // Count total variable size
- int varSize = GetVariableOffset((int)variableAllocations.GetLength()) - 1;
- byteCode.Push(varSize);
- if( outFunc->objectType )
- {
- // Call the base class' default constructor unless called manually in the code
- if( m_isConstructor && !m_isConstructorCalled && outFunc->objectType->derivedFrom )
- {
- byteCode.InstrSHORT(asBC_PSF, 0);
- byteCode.Instr(asBC_RDSPTR);
- byteCode.Call(asBC_CALL, outFunc->objectType->derivedFrom->beh.construct, AS_PTR_SIZE);
- }
- // Increase the reference for the object pointer, so that it is guaranteed to live during the entire call
- // TODO: optimize: This is probably not necessary for constructors as no outside reference to the object is created yet
- byteCode.InstrSHORT(asBC_PSF, 0);
- byteCode.Instr(asBC_RDSPTR);
- byteCode.Call(asBC_CALLSYS, outFunc->objectType->beh.addref, AS_PTR_SIZE);
- }
- // Add the code for the statement block
- byteCode.AddCode(&bc);
- // Deallocate all local variables
- for( n = (int)variables->variables.GetLength() - 1; n >= 0; n-- )
- {
- sVariable *v = variables->variables[n];
- if( v->stackOffset > 0 )
- {
- // Call variables destructors
- if( v->name != "return" && v->name != "return address" )
- CallDestructor(v->type, v->stackOffset, &byteCode);
- DeallocateVariable(v->stackOffset);
- }
- }
- // This is the label that return statements jump to
- // in order to exit the function
- byteCode.Label(0);
- // Release the object pointer again
- if( outFunc->objectType )
- {
- byteCode.InstrSHORT(asBC_PSF, 0);
- byteCode.InstrPTR(asBC_FREE, outFunc->objectType);
- }
- // Call destructors for function parameters
- for( n = (int)variables->variables.GetLength() - 1; n >= 0; n-- )
- {
- sVariable *v = variables->variables[n];
- if( v->stackOffset <= 0 )
- {
- // Call variable destructors here, for variables not yet destroyed
- if( v->name != "return" && v->name != "return address" )
- CallDestructor(v->type, v->stackOffset, &byteCode);
- }
- // Do not deallocate parameters
- }
- // If there are compile errors, there is no reason to build the final code
- if( hasCompileErrors || builder->numErrors != buildErrors )
- {
- // Clear the accessed global properties, so they are not prematurely released
- outFunc->globalVarPointers.SetLength(0);
- return -1;
- }
- // At this point there should be no variables allocated
- asASSERT(variableAllocations.GetLength() == freeVariables.GetLength());
- // Remove the variable scope
- RemoveVariableScope();
- byteCode.Pop(varSize);
- byteCode.Ret(-stackPos);
- // Tell the bytecode which variables are temporary
- for( n = 0; n < (signed)variableIsTemporary.GetLength(); n++ )
- {
- if( variableIsTemporary[n] )
- byteCode.DefineTemporaryVariable(GetVariableOffset(n));
- }
- // Finalize the bytecode
- byteCode.Finalize();
- // Compile the list of object variables for the exception handler
- for( n = 0; n < (int)variableAllocations.GetLength(); n++ )
- {
- if( variableAllocations[n].IsObject() && !variableAllocations[n].IsReference() )
- {
- objVariableTypes.PushLast(variableAllocations[n].GetObjectType());
- objVariablePos.PushLast(GetVariableOffset(n));
- }
- }
- if( hasCompileErrors || builder->numErrors != buildErrors )
- {
- // Clear the accessed global properties, so they are not prematurely released
- outFunc->globalVarPointers.SetLength(0);
- return -1;
- }
- // Copy byte code to the function
- outFunc->byteCode.SetLength(byteCode.GetSize());
- byteCode.Output(outFunc->byteCode.AddressOf());
- outFunc->AddReferences();
- outFunc->stackNeeded = byteCode.largestStackUsed;
- outFunc->lineNumbers = byteCode.lineNumbers;
- outFunc->objVariablePos = objVariablePos;
- outFunc->objVariableTypes = objVariableTypes;
- #ifdef AS_DEBUG
- // // DEBUG: output byte code
- // if( outFunc->objectType )
- // byteCode.DebugOutput(("__" + outFunc->objectType->name + "_" + outFunc->name + ".txt").AddressOf(), engine);
- // else
- // byteCode.DebugOutput(("__" + outFunc->name + ".txt").AddressOf(), engine);
- #endif
- return 0;
- }
- int asCCompiler::CallDefaultConstructor(asCDataType &type, int offset, asCByteCode *bc, asCScriptNode *node, bool isGlobalVar)
- {
- // Call constructor for the data type
- if( type.IsObject() && !type.IsObjectHandle() )
- {
- if( type.GetObjectType()->flags & asOBJ_REF )
- {
- asSExprContext ctx(engine);
- int func = 0;
- asSTypeBehaviour *beh = type.GetBehaviour();
- if( beh ) func = beh->factory;
- if( func > 0 )
- {
- if( !isGlobalVar )
- {
- // Call factory and store the handle in the given variable
- PerformFunctionCall(func, &ctx, false, 0, type.GetObjectType(), true, offset);
- // Pop the reference left by the function call
- ctx.bc.Pop(AS_PTR_SIZE);
- }
- else
- {
- // Call factory
- PerformFunctionCall(func, &ctx, false, 0, type.GetObjectType());
- // Store the returned handle in the global variable
- ctx.bc.Instr(asBC_RDSPTR);
- // TODO: global: The global var address should be stored in the instruction directly
- ctx.bc.InstrWORD(asBC_PGA, (asWORD)outFunc->GetGlobalVarPtrIndex(offset));
- ctx.bc.InstrPTR(asBC_REFCPY, type.GetObjectType());
- ctx.bc.Pop(AS_PTR_SIZE);
- ReleaseTemporaryVariable(ctx.type.stackOffset, &ctx.bc);
- }
- bc->AddCode(&ctx.bc);
- }
- else
- {
- asCString str;
- str.Format(TXT_NO_DEFAULT_CONSTRUCTOR_FOR_s, type.GetObjectType()->GetName());
- Error(str.AddressOf(), node);
- //Class has no default constructor.
- return -1;
- }
- }
- else
- {
- if( isGlobalVar )
- // TODO: global: The global var address should be stored in the instruction directly
- bc->InstrWORD(asBC_PGA, (asWORD)outFunc->GetGlobalVarPtrIndex(offset));
- else
- bc->InstrSHORT(asBC_PSF, (short)offset);
- asSTypeBehaviour *beh = type.GetBehaviour();
- int func = 0;
- if( beh ) func = beh->construct;
- // TODO: Should give error if the value type doesn't have a default constructor and isn't a POD type
- bc->Alloc(asBC_ALLOC, type.GetObjectType(), func, AS_PTR_SIZE);
- }
- }
- return 0;
- }
- void asCCompiler::CallDestructor(asCDataType &type, int offset, asCByteCode *bc)
- {
- if( !type.IsReference() )
- {
- // Call destructor for the data type
- if( type.IsObject() )
- {
- // Free the memory
- bc->InstrSHORT(asBC_PSF, (short)offset);
- bc->InstrPTR(asBC_FREE, type.GetObjectType());
- }
- }
- }
- void asCCompiler::LineInstr(asCByteCode *bc, size_t pos)
- {
- int r, c;
- script->ConvertPosToRowCol(pos, &r, &c);
- bc->Line(r, c);
- }
- void asCCompiler::CompileStatementBlock(asCScriptNode *block, bool ownVariableScope, bool *hasReturn, asCByteCode *bc)
- {
- *hasReturn = false;
- bool isFinished = false;
- bool hasWarned = false;
- if( ownVariableScope )
- AddVariableScope();
- asCScriptNode *node = block->firstChild;
- while( node )
- {
- if( !hasWarned && (*hasReturn || isFinished) )
- {
- hasWarned = true;
- Warning(TXT_UNREACHABLE_CODE, node);
- }
- if( node->nodeType == snBreak || node->nodeType == snContinue )
- isFinished = true;
- asCByteCode statement(engine);
- if( node->nodeType == snDeclaration )
- CompileDeclaration(node, &statement);
- else
- CompileStatement(node, hasReturn, &statement);
- LineInstr(bc, node->tokenPos);
- bc->AddCode(&statement);
- if( !hasCompileErrors )
- asASSERT( tempVariables.GetLength() == 0 );
- node = node->next;
- }
- if( ownVariableScope )
- {
- // Deallocate variables in this block, in reverse order
- for( int n = (int)variables->variables.GetLength() - 1; n >= 0; n-- )
- {
- sVariable *v = variables->variables[n];
- // Call variable destructors here, for variables not yet destroyed
- // If the block is terminated with a break, continue, or
- // return the variables are already destroyed
- if( !isFinished && !*hasReturn )
- CallDestructor(v->type, v->stackOffset, bc);
- // Don't deallocate function parameters
- if( v->stackOffset > 0 )
- DeallocateVariable(v->stackOffset);
- }
- RemoveVariableScope();
- }
- }
- int asCCompiler::CompileGlobalVariable(asCBuilder *builder, asCScriptCode *script, asCScriptNode *node, sGlobalVariableDescription *gvar, asCScriptFunction *outFunc)
- {
- Reset(builder, script, outFunc);
- globalExpression = true;
- // Add a variable scope (even though variables can't be declared)
- AddVariableScope();
- asSExprContext ctx(engine);
- gvar->isPureConstant = false;
- // Parse the initialization nodes
- asCParser parser(builder);
- if( node )
- {
- int r = parser.ParseGlobalVarInit(script, node);
- if( r < 0 )
- return r;
- node = parser.GetScriptNode();
- }
- // Compile the expression
- if( node && node->nodeType == snArgList )
- {
- // Make sure that it is a registered type, and that it isn't a pointer
- if( gvar->datatype.GetObjectType() == 0 || gvar->datatype.IsObjectHandle() )
- {
- Error(TXT_MUST_BE_OBJECT, node);
- }
- else
- {
- // Compile the arguments
- asCArray<asSExprContext *> args;
- if( CompileArgumentList(node, args) >= 0 )
- {
- // Find all constructors
- asCArray<int> funcs;
- asSTypeBehaviour *beh = gvar->datatype.GetBehaviour();
- if( beh )
- {
- if( gvar->datatype.GetObjectType()->flags & asOBJ_REF )
- funcs = beh->factories;
- else
- funcs = beh->constructors;
- }
- asCString str = gvar->datatype.Format();
- MatchFunctions(funcs, args, node, str.AddressOf());
- if( funcs.GetLength() == 1 )
- {
- if( gvar->datatype.GetObjectType()->flags & asOBJ_REF )
- {
- MakeFunctionCall(&ctx, funcs[0], 0, args, node);
- // Store the returned handle in the global variable
- ctx.bc.Instr(asBC_RDSPTR);
- // TODO: global: The global var address should be stored in the instruction directly
- ctx.bc.InstrWORD(asBC_PGA, (asWORD)outFunc->GetGlobalVarPtrIndex(gvar->index));
- ctx.bc.InstrPTR(asBC_REFCPY, gvar->datatype.GetObjectType());
- ctx.bc.Pop(AS_PTR_SIZE);
- ReleaseTemporaryVariable(ctx.type.stackOffset, &ctx.bc);
- }
- else
- {
- // TODO: This reference is open while evaluating the arguments. We must fix this
- // TODO: global: The global var address should be stored in the instruction directly
- ctx.bc.InstrWORD(asBC_PGA, (asWORD)outFunc->GetGlobalVarPtrIndex(gvar->index));
- PrepareFunctionCall(funcs[0], &ctx.bc, args);
- MoveArgsToStack(funcs[0], &ctx.bc, args, false);
- PerformFunctionCall(funcs[0], &ctx, true, &args, gvar->datatype.GetObjectType());
- }
- }
- }
- // Cleanup
- for( asUINT n = 0; n < args.GetLength(); n++ )
- if( args[n] )
- {
- asDELETE(args[n],asSExprContext);
- }
- }
- }
- else if( node && node->nodeType == snInitList )
- {
- asCTypeInfo ti;
- ti.Set(gvar->datatype);
- ti.isVariable = false;
- ti.isTemporary = false;
- ti.stackOffset = (short)gvar->index;
- CompileInitList(&ti, node, &ctx.bc);
- node = node->next;
- }
- else
- {
- // Call constructor for all data types
- if( gvar->datatype.IsObject() && !gvar->datatype.IsObjectHandle() )
- {
- CallDefaultConstructor(gvar->datatype, gvar->index, &ctx.bc, gvar->idNode, true);
- }
- if( node )
- {
- asSExprContext expr(engine);
- int r = CompileAssignment(node, &expr); if( r < 0 ) return r;
- if( gvar->datatype.IsPrimitive() )
- {
- if( gvar->datatype.IsReadOnly() && expr.type.isConstant )
- {
- ImplicitConversion(&expr, gvar->datatype, node, asIC_IMPLICIT_CONV);
- gvar->isPureConstant = true;
- gvar->constantValue = expr.type.qwordValue;
- }
- asSExprContext lctx(engine);
- lctx.type.Set(gvar->datatype);
- lctx.type.dataType.MakeReference(true);
- lctx.type.dataType.MakeReadOnly(false);
- // If it is an enum value that is being compiled, then
- // we skip this, as the bytecode won't be used anyway
- // TODO: global: The global var address should be stored in the instruction directly
- if( !gvar->isEnumValue )
- lctx.bc.InstrWORD(asBC_LDG, (asWORD)outFunc->GetGlobalVarPtrIndex(gvar->index));
- DoAssignment(&ctx, &lctx, &expr, node, node, ttAssignment, node);
- }
- else
- {
- asSExprContext lexpr(engine);
- lexpr.type.Set(gvar->datatype);
- lexpr.type.dataType.MakeReference(true);
- lexpr.type.dataType.MakeReadOnly(false);
- lexpr.type.stackOffset = -1;
- if( gvar->datatype.IsObjectHandle() )
- lexpr.type.isExplicitHandle = true;
- // TODO: global: The global var address should be stored in the instruction directly
- lexpr.bc.InstrWORD(asBC_PGA, (asWORD)outFunc->GetGlobalVarPtrIndex(gvar->index));
- // If left expression resolves into a registered type
- // check if the assignment operator is overloaded, and check
- // the type of the right hand expression. If none is found
- // the default action is a direct copy if it is the same type
- // and a simple assignment.
- bool assigned = false;
- if( lexpr.type.dataType.IsObject() && !lexpr.type.isExplicitHandle )
- {
- assigned = CompileOverloadedDualOperator(node, &lexpr, &expr, &ctx);
- if( assigned )
- {
- // Pop the resulting value
- ctx.bc.Pop(ctx.type.dataType.GetSizeOnStackDWords());
- // Release the argument
- ProcessDeferredParams(&ctx);
- }
- }
- if( !assigned )
- {
- PrepareForAssignment(&lexpr.type.dataType, &expr, node);
- // If the expression is constant and the variable also is constant
- // then mark the variable as pure constant. This will allow the compiler
- // to optimize expressions with this variable.
- if( gvar->datatype.IsReadOnly() && expr.type.isConstant )
- {
- gvar->isPureConstant = true;
- gvar->constantValue = expr.type.qwordValue;
- }
- // Add expression code to bytecode
- MergeExprContexts(&ctx, &expr);
- // Add byte code for storing value of expression in variable
- // TODO: global: The global var address should be stored in the instruction directly
- ctx.bc.InstrWORD(asBC_PGA, (asWORD)outFunc->GetGlobalVarPtrIndex(gvar->index));
- PerformAssignment(&lexpr.type, &expr.type, &ctx.bc, node);
- // Release temporary variables used by expression
- ReleaseTemporaryVariable(expr.type, &ctx.bc);
- ctx.bc.Pop(expr.type.dataType.GetSizeOnStackDWords());
- }
- }
- }
- }
- // Concatenate the bytecode
- int varSize = GetVariableOffset((int)variableAllocations.GetLength()) - 1;
- // We need to push zeroes on the stack to guarantee
- // that temporary object handles are clear
- int n;
- for( n = 0; n < varSize; n++ )
- byteCode.InstrINT(asBC_PshC4, 0);
- byteCode.AddCode(&ctx.bc);
- // Deallocate variables in this block, in reverse order
- for( n = (int)variables->variables.GetLength() - 1; n >= 0; --n )
- {
- sVariable *v = variables->variables[n];
- // Call variable destructors here, for variables not yet destroyed
- CallDestructor(v->type, v->stackOffset, &byteCode);
- DeallocateVariable(v->stackOffset);
- }
- if( hasCompileErrors ) return -1;
- // At this point there should be no variables allocated
- asASSERT(variableAllocations.GetLength() == freeVariables.GetLength());
- // Remove the variable scope again
- RemoveVariableScope();
- if( varSize )
- byteCode.Pop(varSize);
- return 0;
- }
- void asCCompiler::PrepareArgument(asCDataType *paramType, asSExprContext *ctx, asCScriptNode *node, bool isFunction, int refType, asCArray<int> *reservedVars)
- {
- asCDataType param = *paramType;
- if( paramType->GetTokenType() == ttQuestion )
- {
- // Since the function is expecting a var type ?, then we don't want to convert the argument to anything else
- param = ctx->type.dataType;
- param.MakeHandle(ctx->type.isExplicitHandle);
- param.MakeReference(paramType->IsReference());
- param.MakeReadOnly(paramType->IsReadOnly());
- }
- else
- param = *paramType;
- asCDataType dt = param;
- // Need to protect arguments by reference
- if( isFunction && dt.IsReference() )
- {
- if( paramType->GetTokenType() == ttQuestion )
- {
- asCByteCode tmpBC(engine);
- // Place the type id on the stack as a hidden parameter
- tmpBC.InstrDWORD(asBC_TYPEID, engine->GetTypeIdFromDataType(param));
- // Insert the code before the expression code
- tmpBC.AddCode(&ctx->bc);
- ctx->bc.AddCode(&tmpBC);
- }
- // Allocate a temporary variable of the same type as the argument
- dt.MakeReference(false);
- dt.MakeReadOnly(false);
- int offset;
- if( refType == 1 ) // &in
- {
- ProcessPropertyGetAccessor(ctx, node);
- // If the reference is const, then it is not necessary to make a copy if the value already is a variable
- // Even if the same variable is passed in another argument as non-const then there is no problem
- if( dt.IsPrimitive() || dt.IsNullHandle() )
- {
- IsVariableInitialized(&ctx->type, node);
- if( ctx->type.dataType.IsReference() ) ConvertToVariable(ctx);
- ImplicitConversion(ctx, dt, node, asIC_IMPLICIT_CONV, true, reservedVars);
- if( !(param.IsReadOnly() && ctx->type.isVariable) )
- ConvertToTempVariable(ctx);
- PushVariableOnStack(ctx, true);
- ctx->type.dataType.MakeReadOnly(param.IsReadOnly());
- }
- else
- {
- IsVariableInitialized(&ctx->type, node);
- ImplicitConversion(ctx, param, node, asIC_IMPLICIT_CONV, true, reservedVars);
- if( !ctx->type.dataType.IsEqualExceptRef(param) )
- {
- asCString str;
- str.Format(TXT_CANT_IMPLICITLY_CONVERT_s_TO_s, ctx->type.dataType.Format().AddressOf(), param.Format().AddressOf());
- Error(str.AddressOf(), node);
- ctx->type.Set(param);
- }
- // If the argument already is a temporary
- // variable we don't need to allocate another
- // If the parameter is read-only and the object already is a local
- // variable then it is not necessary to make a copy either
- if( !ctx->type.isTemporary && !(param.IsReadOnly() && ctx->type.isVariable))
- {
- // Make sure the variable is not used in the expression
- asCArray<int> vars;
- ctx->bc.GetVarsUsed(vars);
- if( reservedVars ) vars.Concatenate(*reservedVars);
- offset = AllocateVariableNotIn(dt, true, &vars);
- // Allocate and construct the temporary object
- asCByteCode tmpBC(engine);
- CallDefaultConstructor(dt, offset, &tmpBC, node);
- // Insert the code before the expression code
- tmpBC.AddCode(&ctx->bc);
- ctx->bc.AddCode(&tmpBC);
- // Assign the evaluated expression to the temporary variable
- PrepareForAssignment(&dt, ctx, node);
- dt.MakeReference(true);
- asCTypeInfo type;
- type.Set(dt);
- type.isTemporary = true;
- type.stackOffset = (short)offset;
- if( dt.IsObjectHandle() )
- type.isExplicitHandle = true;
- ctx->bc.InstrSHORT(asBC_PSF, (short)offset);
- PerformAssignment(&type, &ctx->type, &ctx->bc, node);
- ctx->bc.Pop(ctx->type.dataType.GetSizeOnStackDWords());
- ReleaseTemporaryVariable(ctx->type, &ctx->bc);
- ctx->type = type;
- ctx->bc.InstrSHORT(asBC_PSF, (short)offset);
- if( dt.IsObject() && !dt.IsObjectHandle() )
- ctx->bc.Instr(asBC_RDSPTR);
- if( paramType->IsReadOnly() )
- ctx->type.dataType.MakeReadOnly(true);
- }
- }
- }
- else if( refType == 2 ) // &out
- {
- // Make sure the variable is not used in the expression
- asCArray<int> vars;
- ctx->bc.GetVarsUsed(vars);
- if( reservedVars ) vars.Concatenate(*reservedVars);
- offset = AllocateVariableNotIn(dt, true, &vars);
- if( dt.IsPrimitive() )
- {
- ctx->type.SetVariable(dt, offset, true);
- PushVariableOnStack(ctx, true);
- }
- else
- {
- // Allocate and construct the temporary object
- asCByteCode tmpBC(engine);
- CallDefaultConstructor(dt, offset, &tmpBC, node);
- // Insert the code before the expression code
- tmpBC.AddCode(&ctx->bc);
- ctx->bc.AddCode(&tmpBC);
- dt.MakeReference((!dt.IsObject() || dt.IsObjectHandle()));
- asCTypeInfo type;
- type.Set(dt);
- type.isTemporary = true;
- type.stackOffset = (short)offset;
- ctx->type = type;
- ctx->bc.InstrSHORT(asBC_PSF, (short)offset);
- if( dt.IsObject() && !dt.IsObjectHandle() )
- ctx->bc.Instr(asBC_RDSPTR);
- }
- // After the function returns the temporary variable will
- // be assigned to the expression, if it is a valid lvalue
- }
- else if( refType == asTM_INOUTREF )
- {
- // Literal constants cannot be passed to inout ref arguments
- if( !ctx->type.isVariable && ctx->type.isConstant )
- {
- Error(TXT_NOT_VALID_REFERENCE, node);
- }
- // Only objects that support object handles
- // can be guaranteed to be safe. Local variables are
- // already safe, so there is no need to add an extra
- // references
- if( !engine->ep.allowUnsafeReferences &&
- !ctx->type.isVariable &&
- ctx->type.dataType.IsObject() &&
- !ctx->type.dataType.IsObjectHandle() &&
- ctx->type.dataType.GetBehaviour()->addref &&
- ctx->type.dataType.GetBehaviour()->release )
- {
- // Store a handle to the object as local variable
- asSExprContext tmp(engine);
- asCDataType dt = ctx->type.dataType;
- dt.MakeHandle(true);
- dt.MakeReference(false);
- asCArray<int> vars;
- ctx->bc.GetVarsUsed(vars);
- if( reservedVars ) vars.Concatenate(*reservedVars);
- offset = AllocateVariableNotIn(dt, true, &vars);
- // Copy the handle
- if( !ctx->type.dataType.IsObjectHandle() && ctx->type.dataType.IsReference() )
- ctx->bc.Instr(asBC_RDSPTR);
- ctx->bc.InstrWORD(asBC_PSF, (asWORD)offset);
- ctx->bc.InstrPTR(asBC_REFCPY, ctx->type.dataType.GetObjectType());
- ctx->bc.Pop(AS_PTR_SIZE);
- ctx->bc.InstrWORD(asBC_PSF, (asWORD)offset);
- dt.MakeHandle(false);
- dt.MakeReference(true);
- // Release previous temporary variable stored in the context (if any)
- if( ctx->type.isTemporary )
- {
- ReleaseTemporaryVariable(ctx->type.stackOffset, &ctx->bc);
- }
- ctx->type.SetVariable(dt, offset, true);
- }
- // Make sure the reference to the value is on the stack
- if( ctx->type.dataType.IsObject() && ctx->type.dataType.IsReference() )
- Dereference(ctx, true);
- else if( ctx->type.isVariable )
- ctx->bc.InstrSHORT(asBC_PSF, ctx->type.stackOffset);
- else if( ctx->type.dataType.IsPrimitive() )
- ctx->bc.Instr(asBC_PshRPtr);
- }
- }
- else
- {
- ProcessPropertyGetAccessor(ctx, node);
- if( dt.IsPrimitive() )
- {
- IsVariableInitialized(&ctx->type, node);
- if( ctx->type.dataType.IsReference() ) ConvertToVariable(ctx);
- // Implicitly convert primitives to the parameter type
- ImplicitConversion(ctx, dt, node, asIC_IMPLICIT_CONV, true, reservedVars);
- if( ctx->type.isVariable )
- {
- PushVariableOnStack(ctx, dt.IsReference());
- }
- else if( ctx->type.isConstant )
- {
- ConvertToVariable(ctx);
- PushVariableOnStack(ctx, dt.IsReference());
- }
- }
- else
- {
- IsVariableInitialized(&ctx->type, node);
- // Implicitly convert primitives to the parameter type
- ImplicitConversion(ctx, dt, node, asIC_IMPLICIT_CONV, true, reservedVars);
- // Was the conversion successful?
- if( !ctx->type.dataType.IsEqualExceptRef(dt) )
- {
- asCString str;
- str.Format(TXT_CANT_IMPLICITLY_CONVERT_s_TO_s, ctx->type.dataType.Format().AddressOf(), dt.Format().AddressOf());
- Error(str.AddressOf(), node);
- ctx->type.Set(dt);
- }
- if( dt.IsObjectHandle() )
- ctx->type.isExplicitHandle = true;
- if( dt.IsObject() )
- {
- if( !dt.IsReference() )
- {
- // Objects passed by value must be placed in temporary variables
- // so that they are guaranteed to not be referenced anywhere else
- PrepareTemporaryObject(node, ctx, reservedVars);
- // The implicit conversion shouldn't convert the object to
- // non-reference yet. It will be dereferenced just before the call.
- // Otherwise the object might be missed by the exception handler.
- dt.MakeReference(true);
- }
- else
- {
- // An object passed by reference should place the pointer to
- // the object on the stack.
- dt.MakeReference(false);
- }
- }
- }
- }
- // Don't put any pointer on the stack yet
- if( param.IsReference() || param.IsObject() )
- {
- // &inout parameter may leave the reference on the stack already
- if( refType != 3 )
- {
- ctx->bc.Pop(AS_PTR_SIZE);
- ctx->bc.InstrSHORT(asBC_VAR, ctx->type.stackOffset);
- }
- ProcessDeferredParams(ctx);
- }
- }
- void asCCompiler::PrepareFunctionCall(int funcID, asCByteCode *bc, asCArray<asSExprContext *> &args)
- {
- // When a match has been found, compile the final byte code using correct parameter types
- asCScriptFunction *descr = builder->GetFunctionDescription(funcID);
- // Add code for arguments
- asSExprContext e(engine);
- int n;
- for( n = (int)args.GetLength()-1; n >= 0; n-- )
- {
- // Make sure PrepareArgument doesn't use any variable that is already
- // being used by any of the following argument expressions
- asCArray<int> reservedVars;
- for( int m = n-1; m >= 0; m-- )
- args[m]->bc.GetVarsUsed(reservedVars);
- PrepareArgument2(&e, args[n], &descr->parameterTypes[n], true, descr->inOutFlags[n], &reservedVars);
- }
- bc->AddCode(&e.bc);
- }
- void asCCompiler::MoveArgsToStack(int funcID, asCByteCode *bc, asCArray<asSExprContext *> &args, bool addOneToOffset)
- {
- asCScriptFunction *descr = builder->GetFunctionDescription(funcID);
- int offset = 0;
- if( addOneToOffset )
- offset += AS_PTR_SIZE;
- // Move the objects that are sent by value to the stack just before the call
- for( asUINT n = 0; n < descr->parameterTypes.GetLength(); n++ )
- {
- if( descr->parameterTypes[n].IsReference() )
- {
- if( descr->parameterTypes[n].IsObject() && !descr->parameterTypes[n].IsObjectHandle() )
- {
- if( descr->inOutFlags[n] != asTM_INOUTREF )
- bc->InstrWORD(asBC_GETOBJREF, (asWORD)offset);
- if( args[n]->type.dataType.IsObjectHandle() )
- bc->InstrWORD(asBC_ChkNullS, (asWORD)offset);
- }
- else if( descr->inOutFlags[n] != asTM_INOUTREF )
- {
- if( descr->parameterTypes[n].GetTokenType() == ttQuestion &&
- args[n]->type.dataType.IsObject() && !args[n]->type.dataType.IsObjectHandle() )
- {
- // Send the object as a reference to the object,
- // and not to the variable holding the object
- bc->InstrWORD(asBC_GETOBJREF, (asWORD)offset);
- }
- else
- bc->InstrWORD(asBC_GETREF, (asWORD)offset);
- }
- }
- else if( descr->parameterTypes[n].IsObject() )
- {
- bc->InstrWORD(asBC_GETOBJ, (asWORD)offset);
- // The temporary variable must not be freed as it will no longer hold an object
- DeallocateVariable(args[n]->type.stackOffset);
- args[n]->type.isTemporary = false;
- }
- offset += descr->parameterTypes[n].GetSizeOnStackDWords();
- }
- }
- int asCCompiler::CompileArgumentList(asCScriptNode *node, asCArray<asSExprContext*> &args)
- {
- asASSERT(node->nodeType == snArgList);
- // Count arguments
- asCScriptNode *arg = node->firstChild;
- int argCount = 0;
- while( arg )
- {
- argCount++;
- arg = arg->next;
- }
- // Prepare the arrays
- args.SetLength(argCount);
- int n;
- for( n = 0; n < argCount; n++ )
- args[n] = 0;
- n = argCount-1;
- // Compile the arguments in reverse order (as they will be pushed on the stack)
- bool anyErrors = false;
- arg = node->lastChild;
- while( arg )
- {
- asSExprContext expr(engine);
- int r = CompileAssignment(arg, &expr);
- if( r < 0 ) anyErrors = true;
- args[n] = asNEW(asSExprContext)(engine);
- MergeExprContexts(args[n], &expr);
- args[n]->type = expr.type;
- args[n]->property_get = expr.property_get;
- args[n]->property_set = expr.property_set;
- args[n]->property_const = expr.property_const;
- args[n]->property_handle = expr.property_handle;
- args[n]->exprNode = arg;
- n--;
- arg = arg->prev;
- }
- return anyErrors ? -1 : 0;
- }
- 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)
- {
- asCArray<int> origFuncs = funcs; // Keep the original list for error message
- asUINT n;
- if( funcs.GetLength() > 0 )
- {
- // Check the number of parameters in the found functions
- for( n = 0; n < funcs.GetLength(); ++n )
- {
- asCScriptFunction *desc = builder->GetFunctionDescription(funcs[n]);
- if( desc->parameterTypes.GetLength() != args.GetLength() )
- {
- // remove it from the list
- if( n == funcs.GetLength()-1 )
- funcs.PopLast();
- else
- funcs[n] = funcs.PopLast();
- n--;
- }
- }
- // Match functions with the parameters, and discard those that do not match
- asCArray<int> matchingFuncs = funcs;
- for( n = 0; n < args.GetLength(); ++n )
- {
- asCArray<int> tempFuncs;
- MatchArgument(funcs, tempFuncs, &args[n]->type, n, allowObjectConstruct);
- // Intersect the found functions with the list of matching functions
- for( asUINT f = 0; f < matchingFuncs.GetLength(); f++ )
- {
- asUINT c;
- for( c = 0; c < tempFuncs.GetLength(); c++ )
- {
- if( matchingFuncs[f] == tempFuncs[c] )
- break;
- }
- // Was the function a match?
- if( c == tempFuncs.GetLength() )
- {
- // No, remove it from the list
- if( f == matchingFuncs.GetLength()-1 )
- matchingFuncs.PopLast();
- else
- matchingFuncs[f] = matchingFuncs.PopLast();
- f--;
- }
- }
- }
- funcs = matchingFuncs;
- }
- if( !isConstMethod )
- FilterConst(funcs);
- if( funcs.GetLength() != 1 && !silent )
- {
- // Build a readable string of the function with parameter types
- asCString str;
- if( scope != "" )
- {
- if( scope == "::" )
- str = scope;
- else
- str = scope + "::";
- }
- str += name;
- str += "(";
- if( args.GetLength() )
- str += args[0]->type.dataType.Format();
- for( n = 1; n < args.GetLength(); n++ )
- str += ", " + args[n]->type.dataType.Format();
- str += ")";
- if( isConstMethod )
- str += " const";
- if( objectType && scope == "" )
- str = objectType->name + "::" + str;
- if( funcs.GetLength() == 0 )
- {
- str.Format(TXT_NO_MATCHING_SIGNATURES_TO_s, str.AddressOf());
- Error(str.AddressOf(), node);
- // Print the list of candidates
- if( origFuncs.GetLength() > 0 )
- {
- int r, c;
- script->ConvertPosToRowCol(node->tokenPos, &r, &c);
- builder->WriteInfo(script->name.AddressOf(), TXT_CANDIDATES_ARE, r, c, false);
- PrintMatchingFuncs(origFuncs, node);
- }
- }
- else
- {
- str.Format(TXT_MULTIPLE_MATCHING_SIGNATURES_TO_s, str.AddressOf());
- Error(str.AddressOf(), node);
-
- PrintMatchingFuncs(funcs, node);
- }
- }
- }
- void asCCompiler::CompileDeclaration(asCScriptNode *decl, asCByteCode *bc)
- {
- // Get the data type
- asCDataType type = builder->CreateDataTypeFromNode(decl->firstChild, script);
- // Declare all variables in this declaration
- asCScriptNode *node = decl->firstChild->next;
- while( node )
- {
- // Is the type allowed?
- if( !type.CanBeInstanciated() )
- {
- asCString str;
- // TODO: Change to "'type' cannot be declared as variable"
- str.Format(TXT_DATA_TYPE_CANT_BE_s, type.Format().AddressOf());
- Error(str.AddressOf(), node);
- // Use int instead to avoid further problems
- type = asCDataType::CreatePrimitive(ttInt, false);
- }
- // Get the name of the identifier
- asCString name(&script->code[node->tokenPos], node->tokenLength);
- // Verify that the name isn't used by a dynamic data type
- if( engine->GetObjectType(name.AddressOf()) != 0 )
- {
- asCString str;
- str.Format(TXT_ILLEGAL_VARIABLE_NAME_s, name.AddressOf());
- Error(str.AddressOf(), node);
- }
- int offset = AllocateVariable(type, false);
- if( variables->DeclareVariable(name.AddressOf(), type, offset) < 0 )
- {
- asCString str;
- str.Format(TXT_s_ALREADY_DECLARED, name.AddressOf());
- Error(str.AddressOf(), node);
- }
- outFunc->AddVariable(name, type, offset);
- // Keep the node for the variable decl
- asCScriptNode *varNode = node;
- node = node->next;
- if( node && node->nodeType == snArgList )
- {
- // Make sure that it is a registered type, and that is isn't a pointer
- if( type.GetObjectType() == 0 || type.IsObjectHandle() )
- {
- Error(TXT_MUST_BE_OBJECT, node);
- }
- else
- {
- // Compile the arguments
- asCArray<asSExprContext *> args;
- if( CompileArgumentList(node, args) >= 0 )
- {
- // Find all constructors
- asCArray<int> funcs;
- asSTypeBehaviour *beh = type.GetBehaviour();
- if( beh )
- {
- if( type.GetObjectType()->flags & asOBJ_REF )
- funcs = beh->factories;
- else
- funcs = beh->constructors;
- }
- asCString str = type.Format();
- MatchFunctions(funcs, args, node, str.AddressOf());
- if( funcs.GetLength() == 1 )
- {
- sVariable *v = variables->GetVariable(name.AddressOf());
- asSExprContext ctx(engine);
- if( v->type.GetObjectType()->flags & asOBJ_REF )
- {
- MakeFunctionCall(&ctx, funcs[0], 0, args, node, true, v->stackOffset);
- // Pop the reference left by the function call
- ctx.bc.Pop(AS_PTR_SIZE);
- }
- else
- {
- ctx.bc.InstrSHORT(asBC_VAR, (short)v->stackOffset);
- PrepareFunctionCall(funcs[0], &ctx.bc, args);
- MoveArgsToStack(funcs[0], &ctx.bc, args, false);
- int offset = 0;
- asCScriptFunction *descr = builder->GetFunctionDescription(funcs[0]);
- for( asUINT n = 0; n < args.GetLength(); n++ )
- offset += descr->parameterTypes[n].GetSizeOnStackDWords();
- ctx.bc.InstrWORD(asBC_GETREF, (asWORD)offset);
- PerformFunctionCall(funcs[0], &ctx, true, &args, type.GetObjectType());
- }
- bc->AddCode(&ctx.bc);
- }
- }
- // Cleanup
- for( asUINT n = 0; n < args.GetLength(); n++ )
- if( args[n] )
- {
- asDELETE(args[n],asSExprContext);
- }
- }
- node = node->next;
- }
- else if( node && node->nodeType == snInitList )
- {
- sVariable *v = variables->GetVariable(name.AddressOf());
- asCTypeInfo ti;
- ti.Set(type);
- ti.isVariable = true;
- ti.isTemporary = false;
- ti.stackOffset = (short)v->stackOffset;
- CompileInitList(&ti, node, bc);
- node = node->next;
- }
- else
- {
- asSExprContext ctx(engine);
- // Call the default constructor here
- CallDefaultConstructor(type, offset, &ctx.bc, varNode);
- // Is the variable initialized?
- if( node && node->nodeType == snAssignment )
- {
- // Compile the expression
- asSExprContext expr(engine);
- int r = CompileAssignment(node, &expr);
- if( r >= 0 )
- {
- if( type.IsPrimitive() )
- {
- if( type.IsReadOnly() && expr.type.isConstant )
- {
- ImplicitConversion(&expr, type, node, asIC_IMPLICIT_CONV);
- sVariable *v = variables->GetVariable(name.AddressOf());
- v->isPureConstant = true;
- v->constantValue = expr.type.qwordValue;
- }
- asSExprContext lctx(engine);
- lctx.type.SetVariable(type, offset, false);
- lctx.type.dataType.MakeReadOnly(false);
- DoAssignment(&ctx, &lctx, &expr, node, node, ttAssignment, node);
- }
- else
- {
- // TODO: We can use a copy constructor here
- asSExprContext lexpr(engine);
- lexpr.type.Set(type);
- lexpr.type.dataType.MakeReference(true);
- // Allow initialization of constant variables
- lexpr.type.dataType.MakeReadOnly(false);
- if( type.IsObjectHandle() )
- lexpr.type.isExplicitHandle = true;
- sVariable *v = variables->GetVariable(name.AddressOf());
- lexpr.bc.InstrSHORT(asBC_PSF, (short)v->stackOffset);
- lexpr.type.stackOffset = (short)v->stackOffset;
- // If left expression resolves into a registered type
- // check if the assignment operator is overloaded, and check
- // the type of the right hand expression. If none is found
- // the default action is a direct copy if it is the same type
- // and a simple assignment.
- bool assigned = false;
- if( lexpr.type.dataType.IsObject() && !lexpr.type.isExplicitHandle )
- {
- assigned = CompileOverloadedDualOperator(node, &lexpr, &expr, &ctx);
- if( assigned )
- {
- // Pop the resulting value
- ctx.bc.Pop(ctx.type.dataType.GetSizeOnStackDWords());
- // Release the argument
- ProcessDeferredParams(&ctx);
- }
- }
- if( !assigned )
- {
- PrepareForAssignment(&lexpr.type.dataType, &expr, node);
- // If the expression is constant and the variable also is constant
- // then mark the variable as pure constant. This will allow the compiler
- // to optimize expressions with this variable.
- if( v->type.IsReadOnly() && expr.type.isConstant )
- {
- v->isPureConstant = true;
- v->constantValue = expr.type.qwordValue;
- }
- // Add expression code to bytecode
- MergeExprContexts(&ctx, &expr);
- // Add byte code for storing value of expression in variable
- ctx.bc.AddCode(&lexpr.bc);
- lexpr.type.stackOffset = (short)v->stackOffset;
- PerformAssignment(&lexpr.type, &expr.type, &ctx.bc, node->prev);
- // Release temporary variables used by expression
- ReleaseTemporaryVariable(expr.type, &ctx.bc);
- ctx.bc.Pop(expr.type.dataType.GetSizeOnStackDWords());
- ProcessDeferredParams(&ctx);
- }
- }
- }
- node = node->next;
- }
- bc->AddCode(&ctx.bc);
- // TODO: Can't this leave deferred output params without being compiled?
- }
- }
- }
- void asCCompiler::CompileInitList(asCTypeInfo *var, asCScriptNode *node, asCByteCode *bc)
- {
- if( var->dataType.IsArrayType() && !var->dataType.IsObjectHandle() )
- {
- // Count the number of elements and initialize the array with the correct size
- int countElements = 0;
- asCScriptNode *el = node->firstChild;
- while( el )
- {
- countElements++;
- el = el->next;
- }
- // Construct the array with the size elements
- // Find the constructor that takes an uint
- asCArray<int> funcs;
- if( var->dataType.GetObjectType()->flags & asOBJ_REF )
- funcs = var->dataType.GetBehaviour()->factories;
- else
- funcs = var->dataType.GetBehaviour()->constructors;
- asCArray<asSExprContext *> args;
- asSExprContext arg1(engine);
- arg1.bc.InstrDWORD(asBC_PshC4, countElements);
- arg1.type.Set(asCDataType::CreatePrimitive(ttUInt, false));
- args.PushLast(&arg1);
- asCString str = var->dataType.Format();
- MatchFunctions(funcs, args, node, str.AddressOf());
- if( funcs.GetLength() == 1 )
- {
- asSExprContext ctx(engine);
- if( var->dataType.GetObjectType()->flags & asOBJ_REF )
- {
- PrepareFunctionCall(funcs[0], &ctx.bc, args);
- MoveArgsToStack(funcs[0], &ctx.bc, args, false);
- if( var->isVariable )
- {
- // Call factory and store the handle in the given variable
- PerformFunctionCall(funcs[0], &ctx, false, &args, 0, true, var->stackOffset);
- ctx.bc.Pop(AS_PTR_SIZE);
- }
- else
- {
- PerformFunctionCall(funcs[0], &ctx, false, &args);
- // Store the returned handle in the global variable
- ctx.bc.Instr(asBC_RDSPTR);
- // TODO: global: The global var address should be stored in the instruction directly
- ctx.bc.InstrWORD(asBC_PGA, (asWORD)outFunc->GetGlobalVarPtrIndex(var->stackOffset));
- ctx.bc.InstrPTR(asBC_REFCPY, var->dataType.GetObjectType());
- ctx.bc.Pop(AS_PTR_SIZE);
- ReleaseTemporaryVariable(ctx.type.stackOffset, &ctx.bc);
- }
- }
- else
- {
- if( var->isVariable )
- ctx.bc.InstrSHORT(asBC_PSF, var->stackOffset);
- else
- // TODO: global: The global var address should be stored in the instruction directly
- ctx.bc.InstrWORD(asBC_PGA, (asWORD)outF…
Large files files are truncated, but you can click here to view the full file