65443f8f23
1c55f0bf23 Squash angelscript.master-modified-for-urho3d. REVERT: 76c92e1a7e Another attempt to merge upstream changes with local ones. REVERT: 4407e00f81 Adapt to AngelScript 2.33.0 WIP. REVERT: 2d1dcc5ace Travis CI: bump copyright to 2018. [cache clear] REVERT: 632c3f505a Add macro for checking the compiler's C++11 type trait templates. Modify angelscript.h header file to use our custom compiler define based on the check result to cater for pre-standard Clang compiler toolchain. REVERT: a8c35e00c4 Remove "execute" permission in the source files. [ci skip] REVERT: faa58bc4f1 Allow possibility of building Urho3D without C++ exceptions feature. Disallow Android build to turn off C++ RTTI feature. REVERT: 380964ffd8 Unconditionally disable the x86 syscall prologs / epilogs on GCC, since the assembler error related to cfi instructions could also happen on newer versions. REVERT: 4944524d01 Old GCC version check in as_callfunc_x86.cpp for CI. REVERT: 73aac66775 Restore Urho3D tweaks to as_config.h and earlier ifdef check for C++11 features. REVERT: d0dd6f54dc Restore earlier ifdef for AS type traits due to some Android CI builds failing. REVERT: 53bf0c68b4 Clang internal error workaround to as_callfunc_x86.cpp REVERT: 882e776ad1 Travis CI: bump copyright to 2017. [ccache clear] REVERT: b41f490557 Initial work to update to AngelScript 2.31.2. REVERT: 4619fb1240 Move the bits into places. REVERT: 3550aa1f60 - fixed a bug with symbol lookup introduced with the refactoring REVERT: 2e3a756923 - fixed a bug with symbol lookup introduced with the refactoring REVERT: f86ebb43fc - Continued the refactoring of symbol lookup so both function calls and variable access use the same logic - Virtual property accessors must not have the same name as real property or function in the same scope REVERT: 7265999643 - Refactored how he compiler does symbol lookups to better handle namespace hierarchies and child types. - The array's and string's length property accessor has been renamed to 'size' to avoid conflict with the length() method REVERT: 270856ff08 The copy constructor of script classes are now used properly by the compiler REVERT: af2c56e2ad - Fixed problem with use of unsafe references and global variables REVERT: c9e4bb6040 - When unsafe references is allowed the stream operator (<<) can now be implemented on value types with correct behaviour for chained expressions - The asBC_REFCPY and asBC_RefCpyV instructions can now be used with value types too REVERT: 0aa30f092b Fixed compiler error in as_callfunc_x86.cpp for gnuc/mingw when compiling with optimizations REVERT: 7707e9a22c CScriptAny now supports forwarding gc enum callbacks to value types with gc behaviour REVERT: c598c98ad0 CScriptGrid now support forwarding gc enum callbacks to value types with gc behaviour REVERT: d0a44858e4 Included SetCircularRefDetectedCallback to allow application developers to easier identify the origin of circular references REVERT: 2fea402a22 Fixed a bug when loading bytecode having identical shared functions in different namespaces REVERT: bf9480602b Fixed problem in compiler that didn't release a temporary variable when passing anonymous object to function expecting ?&in REVERT: 0a32333cf9 - AssignScriptObject will now set a script exception for ref types if asEP_DISALLOW_VALUE_ASSIGN_FOR_REF_TYPE is used - Initialization lists will implicitly assume objects by handle if asEP_DISALLOW_VALUE_ASSIGN_FOR_REF_TYPE is used REVERT: 1ef192e4cc Passing a string literal to function expecting &out will now give compiler error REVERT: 5c154f1ccb Fixed bug introduced in previous revision REVERT: 71d557fd6d Fixed assert failure on call to opCast(?&out) with a non-variable expression REVERT: fbfc3cbc5c Bytecode with external shared classes with virtual methods failed to load from bytecode REVERT: 5b97a695c8 The bytecode for external shared entities is no longer saved in the module when inherited from (additional fix when the same function was used in another class too) REVERT: 3b7448cd61 The bytecode for external shared entities is no longer saved in the module when inherited from REVERT: 21e4c65fb7 Fixed loading byte code that uses external shared classes in namespace REVERT: 19cb3e303f Reduced the size of the saved byte code, especially when there are lots of functions without parameters REVERT: ce6fe5b80b CScriptDictionary now supports forwarding gc enum callbacks to value types with gc behaviour REVERT: c102cf24a3 Added exception handling to the MSVC8 project REVERT: 3af7bab9d5 Initializing an ASHANDLE type with overloaded opHndlAssign taking a var type as a handle wasn't done correctly REVERT: 11600f0d96 - Added the engine methods ForwardGCEnumReferences and ForwardGCReleaseAllReferences - CScriptArray now supports forwarding gc enum callbacks to value types with gc behaviour REVERT: d787280628 - Registered value types can now register GC behaviours to solve circular references involving these types - CScriptHandle registers the ENUMREFS and RELEASEREFS gc behaviours REVERT: db6a199d60 CScriptBuilder now supports having multiple meta data blocks for each entity REVERT: e6da96550a auto declarations now works correctly with implicit handle types REVERT: 45194c3cf6 Fixed bug when loading bytecode containing shared interfaces with inheritance REVERT: 6c6f887d26 meta data is now correctly extracted for class methods with decorators REVERT: f982e0dc2a The correct resolution is now done for types implementing both opCast and opImplConv and the const overloads. The same for opConv and opImplConv REVERT: 7716d5479f Added MSVC 2017 projects REVERT: e7368f62a8 Fixed compiler error when both opConv and opImplConv are implemented REVERT: 6fafba3022 opImplCast with variable type won't be used for non-ref types REVERT: f8a9eaed89 Fixed memory build-up due to delayed cleanup when discarding modules with shared entities REVERT: 1649f60370 Further fixes related to template types and namespaces REVERT: 14b56414db Template types are now properly identified in separate namespaces REVERT: f0ed5e519f Fixed an error when compiling a class method call as post-op and the name matches a type name REVERT: e3ca22c65a Saving bytecode for a never returning function would cause assert failure REVERT: c8095307f3 Class constructors can now be declared with private and protected too REVERT: 64ab05ba0c Fixed error when saving bytecode for scripts containing interfaces that derives from interfaces REVERT: 8bd5d40097 weakref now properly identifies different object instance when the address is the same as the previously freed object REVERT: cbaa500fb3 Fixed compiler error on Linux REVERT: d0e37c201b Implemented a callback to allow application to translate C++ exceptions thrown by registered functions REVERT: 8037ae9a51 By default asEP_GENERIC_CALL_MODE is set to use the new behaviour REVERT: ef7d9a44ad Implemented engine property asEP_GENERIC_CALL_MODE to make the generic calling convention treat handles the same way that the native calling convention do REVERT: f15dcfcc56 Compiler would crash on compiling ternary operator with anonymous list in one of the conditions REVERT: aa7d45b60e Template types can now be registered with methods taking parameters as &inout REVERT: 12c7ab4d6c - Loading bytecode that included use of template value types with a template type as argument crashed - All methods for template instances now get unique functions to show the template instance as the object type REVERT: 32c4b235ba documentation REVERT: 18678f1da9 The compiler will now give an error on bitwise operator with floats instead of silently converting to integer REVERT: 400c54c299 Fixed bug with anonymous initialization lists when used to initialize value type passed by value to function REVERT: c168f89b4b - Fixed crash in debugger add-on when printing call stack in template function - Documented the script grammar in BNF - Added support for specifying parameter types to lambda functions REVERT: 80adf56b06 Minor refactoring to reduce some duplicated code for verifying if proceeding token sequence is a type REVERT: d8e324cad1 Engine error messages now include the symbol name for the error code for easier interpretation REVERT: c6803b2a0a - Implemented a #pragma callback in the builder add-on - asrun can now turn on debugging with #pragma debug (works the same as -d command arg) REVERT: 42c35611d6 Fixed incorrect use of setlocale in parseFloat REVERT: 18fc360ef2 Changed the compiler to do less copies of objects and especially string constants REVERT: ba12d89ea0 cmake changes REVERT: 9c93ca0c69 Fixed crash when compiling is null comparison with ASHANDLE object without any opEquals operator REVERT: 66e4471016 Changed the clean-up of the string factory in the stdstring add-on to avoid crash if the script engine is stored in global singleton REVERT: 1eb058cf24 - filesystem's changeCurrentPath doesn't modify anything if the new path is invalid - Implemented the methods makeDir, removeDir, deleteFile, copyFile, and move on the filesystem add-on REVERT: 76aeadd7f3 Updated the cmake project for better usability REVERT: bfd447ddcb The asrun sample now waits for user input before closing on any error when executed from file explorer on Windows REVERT: 153b385a1f Preparing the tests for new optimizations REVERT: 4dd0bd304a Added methods setDate and setTime to datetime add-on and operators to add seconds, compare, and determine difference in seconds REVERT: d50e15345c Added the methods IsLink and GetSize to filesystem add-on REVERT: 315252e5d1 Releasing 2.32.0 REVERT: 117d6a49cd Updating the config according to the new way of registering the string factory REVERT: 833178fcaf RefCastObject was allowing explicit cast from base class to derived class even though the true type was of the base class REVERT: d582d62bf4 Linux with ARM doesn't support catching c++ exceptions yet REVERT: 4232d7c0c8 Fixed problem on platforms where char is unsigned by default REVERT: c13e697abd Removed unnecessary include REVERT: 0270b00838 Increased initial buffer size in asCString::Format to work around problem on Linux REVERT: 3f5163be6c Fixed compiler warning REVERT: 827c153abe Fixed buffer overflow when formatting error message with very long tokens REVERT: 028f4cf94e Testing long tokens REVERT: 8b279427dd Corrections for Linux/GNUC REVERT: 5257e9234e Fixed an incorrect default arg REVERT: 38fb52292f - Implemented generic calling convention for the CScriptArray::Sort method with callback - Fixed compiler error with AS_NO_COMPILER - Fixed tests for AS_MAX_PORTABILITY REVERT: 8a5f493719 Documentation REVERT: 680bde46d5 - Documentation - Removed the option to turn off the new string factory REVERT: 11f1654449 Documentation REVERT: a49a9b59ba Documentation REVERT: 12d2dcae4d Documentation REVERT: cdc47e3c1f Documentation REVERT: 00b102d455 - Object properties can also be registered for composite objects - WriteConfigToStream and ConfigEngineFromStream properly handles composite properties - Debugger is capable of inspecting value of composite properties REVERT: d0896a561c Added support for registering class methods for composite types REVERT: 737a4dc5c0 Removed deprecated code from previous versions REVERT: c503c760b9 Changed how string literals are handled by the script engine. Now they are evaluated at compile time and a pointer to the application native string object is stored in the byte code. REVERT: 6f3df65557 Cleaning up REVERT: fc9738dc20 Compiler was allowing to pass a const object by reference to a parameter expecting a non-const when using unsafe references REVERT: 18cc16c9fd Saved byte code with scripts that used child funcdefs couldn't be loaded REVERT: 699cd4806f Value type without opAssign but with a copy constructor can now be implicitly copied in type conversions REVERT: af1cd92622 Explicitly creating a copy of a const object to get a non-const handle would give a compiler error REVERT: 6672b26d13 - Objects that rely on asGetActiveContext during clean-up can now do so even during exception handling - Dictionary storing a const handle now allows retrieving by value REVERT: c5ad0228ab cleaning up the test code a bit REVERT: 03d1e39c1e more cleanup in test code REVERT: 8b44cde9b4 - Both Read and Write in asIBinaryStream can now return an error code to signal failure - fixed compiler warnings in test applications REVERT: 49f44eaffa Fixed compilation error when the project path included space characters REVERT: 026b87b5b6 Code clean-up REVERT: 99d5a7d155 Using bit fields for function traits REVERT: 4f6b4f38f0 Fixed the asbuild sample config and script files REVERT: aa74157748 Minor code cleanup REVERT: 923f709dd3 Fixed assert failure when compiling expression with anonymous initialization list REVERT: f61dc2abc3 Fixed assert failure while compiling unary minus operator on constant integers smaller than 32 bits. REVERT: 570966664a Fixed crash in compiler when compiling type conversion with invalid use of named arguments REVERT: 393403156b Fixed assert failure when compiling comparison expressions with primitives smaller than 32bit REVERT: f610f7ed14 Fixed compiler error in deprecated IsHandleCompatibleWithObject when using AS_DEPRECATED REVERT: 3d0c058e3c Handles received by reference is now properly treated when passed to function expecting variant type REVERT: cb4ddab368 Improved compiler error message when identifier before parenthesis doesn't evaluate to a function REVERT: 8e58862e53 Fixed bug in compiler that would in complex expressions overwrite a temporary variable before it was used REVERT: 25ba4116f4 Fixed memory leak in RemoveGlobalProperty as the property wasn't uninitialized before it was removed REVERT: 87acdae676 Fixed compiler error with address sanitizer on g++ 5 REVERT: 4661e59bb9 Fixed rtti in test_refcast.cpp on gnuc REVERT: 679528d396 - minor tweaks to datetime.cpp add-on - additional regression tests for garbage collections and native calling convention with multiple inheritance REVERT: 0c46ee6ae6 Fixed bug where compiler didn't allow default arguments to be name of global functions REVERT: d36df794c9 The VM now raises an exception when application performs too many nested calls REVERT: 5c5602222a RegisterFuncdef, RegisterInterface, RegisterTypedef, RegisterEnum now returns the type id on success REVERT: be39c3c299 Implemented sort in CScriptArray that takes as input a script function to compare two elements REVERT: bbbcbad25e Template instances now correctly replaces child funcdefs REVERT: ba3a9d0fe2 Child funcdefs of template instances are now released together with the template instance type rather than at the shutdown of the engine REVERT: 70e6b2d506 Fixed assert failure when compiling ref cast and multiple matching cast operators are found REVERT: af747c88ca Improved compiler error messages with anonymous initialization lists REVERT: 44aaca6cf7 Fixed bug in parser that would produce misleading error message on invalid code REVERT: 9e22deffe6 Implemented support for typeless anonymous initialization lists REVERT: 5fc02ac206 WriteConfigToStream now resets the namespace for the string factory REVERT: 0e892a7395 Saving the noDebugInfo flag in a platform independent way REVERT: f695abcad2 Fixed a couple of bugs on platforms with 4 byte booleans REVERT: 07a5b91412 Documentation REVERT: 1f75a0e039 - Fixed compiler warning in engine - Added proper error messages if external shared entities are not found while loading bytecode REVERT: c8551eff54 Saved bytecode now takes advantage of shared funcdefs and functions declared as external and includes less bytes REVERT: 3c31e25425 Saved bytecode now takes advantage of shared enums, classes, and interfaces declared as external and includes less bytes REVERT: 1d730f3bc7 - ScriptBuilder add-on now support metadata for enums too - ScriptBuilder add-on properly handles 'external' and 'shared' keywords when parsing metadata REVERT: 33462b453c - funcdefs can now be explicitly declared as shared - shared funcdefs can also be declared as external REVERT: 404c5739e0 shared functions can also be declared as external REVERT: 445ebcb958 shared classes can also be declared as external REVERT: 3cdec2d340 shared interfaces can also be declared as external REVERT: 9d3c51b1fa Can now declared shared enums as external to explicitly inform that the enum should be included from previously compiled module REVERT: 47df85f289 Fixed crash when compiling expression using opIndex property accessor without arguments REVERT: d4475e8b4c Fixed macro redefinition warning REVERT: b50c53fa2a Fixed problem with calling function expecting @&in where the argument was itself a & argument REVERT: eb753e5b0a Fixed problem that could happen when saving bytecode that used shared classes REVERT: bdadb13885 Fixed problem that could happen when saving bytecode that used shared functions REVERT: d0498a4c96 Fixed problem with SetDefaultNamespace and nested namespaces REVERT: 3c9642bc3a Fixed compiler error in asFUNCTIONPR on MSVC2017 REVERT: 1195178d0a Handles explicitly initialized with null in declarations no longer produce any bytecode REVERT: bdb4bb7fbc Fixed the symbol search order so class name with same name as type is properly handled REVERT: d255499c16 Fixed a problem where the use of a get accessor could incorrectly reuse temporary variables REVERT: 8a8f0f1e00 documentation REVERT: 567a01d8e2 Fixed bug in CScriptArray::RemoveRange REVERT: 7c53d1fd64 Fixed compiler error on MSVC2008 due to missing stdint.h REVERT: 9216d9b990 Registered funcdefs can now be used by shared script functions REVERT: 3f06f8244f Releasing 2.31.2 REVERT: f4792b6e28 Fixed problem with temporary variables being overwritten when invoking overloaded dual operators with left-to-right evaluation. REVERT: 18a798da6b Fixing last remaining CastToObjectType REVERT: f712d97c3a Fixed crash on GCC 6 due to CastToObjectType accepting this == null REVERT: 4a735d5f6e Fixed bug in script builder for meta data on functions with default arguments REVERT: 07ca5fc71c Corrected as_config.h so Linux for 64bit ARM doesn't try to compile as_callfunc_x64_gcc.cpp REVERT: 07268f51e3 Fixed compiler warnings on Linux with gnuc REVERT: bf360f5aea Documentation REVERT: e8ad42295b Changes to meson project files REVERT: fc6d89e29b opCast(?&out) on null handle now works without raising null pointer exceptions REVERT: 93df53f93f Fixed assert failure on implicit conversion from 8/16 bit uint constant to integer REVERT: cfad5f2fe2 Non-assignment dual operators, e.g. +, now evaluates left to right even when using overloaded operators REVERT: 166f988247 Reduced the number of calls to addref and release that the compiler includes in the bytecode in some cases REVERT: 4060eda8b9 Fixed problem with registering types using the asOBJ_IMPLICIT_HANDLE flag REVERT: b695c4ae56 Fixed bug in bytecode serialization when adjusting pointer sizes for calls using asBC_ALLOC REVERT: 6a0489669d Fixed assert failure when compiling comparison operator on boolean constant REVERT: d86baba90e Fixed compiler error on Linux with non-x86 64bit platforms, e.g. arm64, mips, and s390x architectures REVERT: ce8bc5ffc0 Non-pod value types are now allowed to be registered without default constructor, as long as at least one constructor is registered REVERT: b952c63d6c - Compiler emits warnings if 64bit integer constants cannot fit when implicitly converted to smaller types - Compiler didn't support implicitly converting constant uint64 values to uint32 values - Introduced a new keyword 'if_handle_then_const' that is used to tell template that a handle should be to const object - Compiler now emits an error if an integer constant is larger than 2^64-1 REVERT: 2fe55ac217 Fixed incorrect compiler warning about too large value when assigning negative value to const int8/int16 REVERT: e8d1be8ba8 Fixed assert failure when compiling boolean not operator on constant REVERT: ac47ea3f53 Fixed bug when compiling get property accessor returning a registered type that is then implicitly converted to another REVERT: cfe4ccf1c9 Fixed bug in compiler when passing a @& argument to a function REVERT: 336e95c22c Fixed assert failure in compiler when compiling invalid switch case REVERT: c7d3977db1 Fixed bug with saved byte code not being portable between 32bit and 64bit platforms when including ChkNullS instruction REVERT: 802ad4aadf Fixed big endian errors in the compiler related to implicit conversions of constants REVERT: 671c53885f Introduced a new keyword 'if_handle_then_const' that is used to tell template that a handle should be to const object REVERT: 3833ce1f8d Fixed crash when discarding modules containing shared funcdefs REVERT: 4aac321d14 Documentation REVERT: 992c5de0e8 Registered the opAssign method for weakref to allow value assignments for this type REVERT: cb1f7925ea CreateScriptObject crashed when called for template value types REVERT: 83055f97ba Fixed a big endian error in the test framework REVERT: 871307605c Fixed big endian errors in the compiler related to enum values REVERT: 7eace17781 Fixed a bug in asrun example where the context was returned to the pool before being unprepared REVERT: 88f8e7a959 - Fixed asGetTypeTraits on clang 3.8 - Fixed a minor bug in ExecuteString that could cause problem in rare cases REVERT: 8855bb2afc Fixed compiler warning about missing GNU-stack on gentoo Linux REVERT: ecfa22dba6 Releasing version 2.31.1. REVERT: a64afbdbff Fixed some gnuc compiler errors on Linux REVERT: f5d9c1b19f Fixed compiler error in datetime add-on on gnuc REVERT: 44900ddd97 Fixed bug when retrieving a bool from a dictionary value stored with an int. REVERT: 33cda50795 Fixed some big endian compatibility issues REVERT: 60d282c5d1 Function pointers wasn't properly handled in asIScriptGeneric GetArg and SetReturn methods REVERT: 32a796e10f Function pointers wasn't properly handled in context's SetArg and GetReturn methods REVERT: 17f3412a0c Added asEP_HEREDOC_TRIM_MODE to allow application to decide how/if heredoc strings should be trimmed REVERT: d979819717 Anonymous objects initialized with initialization lists are now parsed as expression terms, rather than expressions by themselves REVERT: 364a5782de opCast can now be used on types registered with asOBJ_NOHANDLE too without crashing REVERT: 36ee5711fc Fixed crash with 'auto var = null;' as global variable REVERT: 30617a4366 Function pointers in initialization lists would crash the application REVERT: a48749ae3d Returning a function pointer from a registered function wasn't working properly REVERT: 97f113da6f Incorrect constructor could be used when declaring variable of object type and the initialization expression was of the same type but type had no copy constructor REVERT: 0fb532d05f Fixed the script array when using AS_USE_STLNAMES REVERT: 6647cf776a Implemented insertAt for the array add-on that inserts all the elements from another array REVERT: 0ccaa66fe2 Enum to int32 conversion is now preferred over enum to int of other sizes in function overload resolution REVERT: ba54cb3cf2 Fixed crash in compiler when compiling 'null();' REVERT: 64f1710a02 Fixed assert failure in compiler with AS_DEBUG when compiling anonymous function from class method REVERT: b652b22831 Offset for bytecode ClrVPtr wasn't adjusted correctly when saving/loading bytecode when the referred to variable was a null pointer REVERT: 9fb010c6c2 Implemented removeRange for the array add-on REVERT: b1abf6c829 Implemented findFirstOf, findLastOf, findFirstNotOf, findLastNotOf, insert, and erase for the string add-on REVERT: cd1c9635fc Implemented parseUInt for the string add-on REVERT: 8f4ccd57f8 Fixed error when discarding a module if a context was still holding a reference to global var REVERT: 3208626ade Compiler wasn't giving an error on 'void &' REVERT: bb6d2cdbcd auto var = null; no longer causes a crash in the compiler REVERT: b14d84bcf1 Expressions like (expr ? null : null) is now compiled correctly REVERT: 430580a6cf Fixed null pointer access crash in compiler when compiling invalid script with value assign of function pointer REVERT: b36173c237 The generic calling convention wasn't properly handling funcdefs in arguments or return type REVERT: 89cd10929b Fixed null pointer access crash in asBC_Thiscall1 when calling opIndex on a null pointer REVERT: 025552c4de The auxiliary pointer wasn't stored with object methods registered with the asCALL_GENERIC calling convention REVERT: 45f8d6d50c Fixed compiler bug when passing funcdef by reference to a function REVERT: ded27ec5fb Fixed assert failure in compiler when attempting to implicitly convert primitive to function pointer REVERT: be5c1c4028 Releasing 2.31.0 REVERT: 8b09cb6883 Documentation REVERT: 8fcda61d36 Compiler properly resolves namespace when calling base classes method and the base class is declared in a different namespace REVERT: 32e64f7604 CScriptBuilder incorrectly replaced /./ in include paths with nothing REVERT: f32f202ec0 CScriptAny released asITypeInfo that it didn't own when a null handle was stored in it REVERT: 098eee16c5 Fixed bug in compiler when passing a handle from function argument to copy constructor REVERT: 401b3ce3a1 Documentation REVERT: 217cfaa10e Documentation REVERT: d3eebc9d22 Implemented getInput in the asrun sample to allow it to get user input from standard input REVERT: 96bf7a4459 Implemented CDateTime as a basic way of getting the time in scripts REVERT: 550003b0d1 Documentation REVERT: df4c869dcb Documentation REVERT: aab1949ccb Documentation REVERT: 4d0e08e718 Documentation REVERT: 5916c4f56c Fixed memory leak when passing funcdef to application registered function REVERT: 5fc10cdb88 Fixed the problems on Linux 32bit when compiling with optimizations REVERT: 2bfef733a6 Fixed crash on Linux 32bit when application threw C++ exception and library had been compiled with optimizations REVERT: 1ffa571cac - documentation - Implemented CScriptDictionary::find - Changed CScriptDictionary::Delete to return bool REVERT: 82216d4a5e Documentation REVERT: 0b41281657 - Fixed some compiler warnings in the test code - Added a clang configuration for MSVC (though not fully working yet) REVERT: 424d40e737 Clang is now recognized in angelscript.h as supporting C++11 features REVERT: c04e6f42ee Modules now keep a reference to funcdefs that are indirectly created by taking the address of function pointers. This way these are saved as declared funcdefs together with the bytecode, eliminating the need to store the function signature together with each use of the type info. REVERT: ae4e15c440 Finished the refactoring of the internal representation of types REVERT: 48ff982d27 Refactoring REVERT: 3d37442ee2 Fixed a crash when calling CompileGlobalVar and the compilation failed. REVERT: 4d7a725d64 - Implemented GetParentType in asITypeInfo - Deprecated GetParentType in asIScriptFunction REVERT: 3d3ae06c8c - Fixed compiler warnings about hidden variables - GetChildFuncdef now returns an asITypeInfo instead of asIScriptFunction - Added GetFuncdefSignature to asITypeInfo - Deprecated GetFuncdefFromTypeId REVERT: 519c19b96a - asOBJ_SCRIPT_FUNCTION has been removed - RefCastObject can now be used for function pointers too - ScriptHandle and Dictionary no longer need special code for function pointers REVERT: 6f145057ff Fixes for big endian CPUs REVERT: 0147c13af9 Fixed compiler error in as_memory.cpp on OpenBSD REVERT: 5e2845abb2 Fixed compilation error without AS_DEBUG defined REVERT: 2ee4179af7 - Array is now probably disallowing funcdefs by value as subtypes - The compiler is properly handling passing reference to a handle to a funcdef as function argument REVERT: 9302c4cdf5 Fixed a bug in compiler with calling function with const reference and the function expected non-const reference REVERT: ddbee98e92 Functions with generic calling conventions now catch application exceptions REVERT: ed3047385e Funcdefs are now handled as asCFuncdefType instead of asCScriptFunction internally thus making it more uniform with other types REVERT: 2cd4774c40 Refactoring REVERT: c51331e505 funcdefs are now stored internally as asCFuncdefType derived from asCTypeInfo REVERT: f226c6e558 Changed GetTypedefByIndex to return an asITypeInfo and removed all the output parameters REVERT: 9a7eb83199 Changed GetEnumByIndex to return an asITypeInfo and removed all the output parameters REVERT: 3652d0b7e6 - Implemented asITypeInfo::GetEnumValueCount and GetEnumValueByIndex - Deprecated GetObjectTypeByName/Decl, GetEnumValueCount/ByIndex, GetObjectTypeById, SetObjectTypeUserDataCleanupCallback in engine - Deprecated GetObjectTypeByName/Decl, GetEnumValueCount/ByIndex in module - Deprecated asIObjectType REVERT: 87ad59d04f Implemented GetTypeInfoByDecl, GetTypeInfoByName, GetTypeInfoById, and SetTypeInfoUserDataCleanupCallback REVERT: 3e81962d87 Partially specializing the scope as the enum type when referring to enums in namespaces is now possible REVERT: d550b0b686 Access mask in the engine now defaults to all bits set to make sure built-in functions are available to all modules REVERT: 43d5e4d768 Linux with PPC64 Little Endian is now working with AS_MAX_PORTABILITY REVERT: 8f98c60f1c Renamed asIObjectType to asITypeInfo REVERT: cc0f302e03 Continuing the refactoring REVERT: e86c3f9d9a Refactoring the code in preparation for the new asITypeInfo interface REVERT: d49694f095 Added configuration in as_config.h to identify GNUC+Linux+PPC64 REVERT: 6686a7317f Refactoring REVERT: 0beecd15e9 Fixed problem with named args and registered behaviours REVERT: 344c6585a5 Implemented asEP_ALLOW_UNICODE_IDENTIFIERS REVERT: 96ae96a92b Tweaking the gnuc makefile REVERT: fe410fb75b Corrected the name of the shared object filename in the gnuc makefile REVERT: 815fbb762e Fixed cmake project files REVERT: dae5b89167 Ternary condition can now return function pointers REVERT: 5befb8f6ab Fixed problem in CScriptAny with constructing any object from primitive REVERT: 54a8f4a8ee Added GetParentType to asIScriptFunction to allow querying the parent type for child funcdefs REVERT: 3abef81c47 Improving the validations for error scenarios with child funcdefs REVERT: ccd7a6a491 Child funcdefs in templates works properly in the template instances REVERT: edc36aba1a Working on the support for child funcdefs in template types REVERT: 24e739a313 Fixed bug with namespace and class member initialization REVERT: 7d7cdd846c It is possible to register child funcdefs in template types too REVERT: 0b9b7e2b51 WriteConfigToStream and ConfigEngineFromStream is properly handling child funcdefs REVERT: 6b024152f2 It is now possible to register child funcdefs with RegisterFuncdef too REVERT: 0a846b7014 - Added GetChildFuncdefCount and GetChildFuncdef to asIObjectType - Renamed GetFuncDefFromTypeId to GetFuncdefFromTypeId for consistency REVERT: cbbb047d31 Fixed compiler errors in add-ons when using AS_USE_NAMESPACE REVERT: 3e2d8b798f funcdefs declared as child of class can now use other child funcdefs in the same class in the signature REVERT: ff1cfa873f Child funcdefs are now visible in derived classes too REVERT: d86c14881e Fixed bug with template value types and loading bytecode REVERT: 038df03ce4 child funcdefs in shared classes is now working properly REVERT: ad9710bede Saving and loading bytecode with child funcdefs is now working REVERT: 7194d5a8a1 Name conflicts between child funcdefs and other class members are now properly detected REVERT: b6949e8cc3 The basics for child funcdefs is now working REVERT: 08e72ea853 Working on member funcdefs REVERT: ff249212ba Compiler will no longer match funcdefs for datatype if the namespace is not the correct one REVERT: 67464a35ac Parser now supports declaring funcdef as child of class REVERT: 8499398404 Improved parser error message when finding unexpected token to inform if the token is reserved or not REVERT: ff4690a39c Compiler no longer crashes when assigning anonymous function to variable declared with auto type REVERT: 80ef835773 Added support for registering and retrieving the auxiliary object pointer with asCALL_GENERIC REVERT: 15bc0c5a08 Fixed compiler error on non-MSVC compilers with AS_DEBUG defined. REVERT: f68d27beef - Upgraded project files to MSVC 2015 - Fixed compiler errors on MSVC 2015 REVERT: 8ccf4d8720 Releasing 2.30.2 REVERT: a278d917dc Fixed a bug with SaveByteCode on 64bit platforms when the script called constructor with primitive arg before object arg REVERT: 517162d870 Documentation REVERT: e187097c95 Documentation REVERT: 96a9b9ce74 Documentation REVERT: 9a8de9bf16 Documentation REVERT: 491e19f64c Initialization lists can be used to initialize variables declared as handles too REVERT: 63eb215025 ANGELSCRIPT_VERSION is now the default argument to asCreateScriptEngine REVERT: 738045d945 - CScriptDictionary now uses a typedef for the key type to make it easier for applications that uses custom string types - Changed to use std::unordered_map in CScriptDictionary when C++11 is used - The CScriptDictionary now caches its object type to avoid looking it up in the engine for each instance REVERT: 2b6813bd23 Fixed compiler bug with nested namespaces, when the outer namespace has not been explicitly registered REVERT: 3fa061c89a Improved compiler to create less temporary objects when using the opConv(?&out) type conversion operator REVERT: 35f4b84673 - Optimized some byte code sequences generated by the compiler - On C++11 enabled compilers the string pool is now using the unordered_map container for faster look-up REVERT: 43843f584a - cleaned up old code comments - Improved the compiler to generate less temporary copies of objects - The options argument in formatInt, UInt, and Float now has a default argument of an empty string REVERT: d928c8e71a Minor improvements REVERT: 8355f7ba9a - Functions can be called with anonymous functions as arguments - asIScriptModule::CompileFunction can also declare anonymous functions - anonymous functions can also be used within shared functions - Fixed compiler error in scriptbuilder.h - anonymous functions can be used in asIScriptModule::CompileGlobalVar REVERT: 968d4a7098 Working on the anonymous functions REVERT: 5b4a451c77 Fixed crash with asCALL_THISCALL_ASGLOBAL and objects passed by value in argument REVERT: bba7e771f9 Parser now understands the syntax for anonymous functions REVERT: 9158e0da21 Added the reserved keyword 'function' that will be used to declare lambdas REVERT: b1edac721c Fixed compiler error REVERT: 0d9ff0bdc2 Throwing exception from application code would cause a crash if the library had been built without optimizations on 64bit GNUC REVERT: 15676bbf39 Improved performance for saving bytecode that uses lots of global properties REVERT: 9a6184a2e6 Improved thread safety to support multiple threads querying typeIds simultaneously REVERT: e9c3bcf1f1 Optimized GetTypeIdFromDataType REVERT: 0c93911923 Changed how strings are saved to byte stream to reduce the size of the stream REVERT: 94c9697ea7 - Built-in function names and object types are now prefixed with $ to make it quicker to identify them while loading bytecode - Optimized logic for finding registered function/method in LoadByteCode - ConfigEngineFromStream appropriately treats names of behaviour functions before registering them since they are now prefixed with $ in WriteConfigToStream REVERT: 5d94458567 Preparing tests for optimizing LoadByteCode REVERT: 9c4a4a3965 Releasing 2.30.1 REVERT: 602ef9f655 Defined AS_NO_EXCEPTIONS for Android ARM, as catching C++ exceptions is currently not working REVERT: 64dd02148e Documentation REVERT: 27a1944019 Documentation REVERT: cfdcbdc264 Fixed compile error in scriptbuilder.cpp REVERT: 14eb7bc90f Improved performance for loading saved byte code REVERT: f12c372e19 CScriptBuilder now does case insensitive compares for duplicate include files on Windows REVERT: 24d17cc165 Fixed compiler bug that allowed class method handle to be converted to primitive REVERT: b21ed4b607 Fixed compiler bug when using construct call for a reftype on an object with opConv(?&out) REVERT: f0dfbfb192 for-loops now support multiple increment expressions separated by , REVERT: 15b8bb1e4d - Implemented GetAddressOfValue in the dictionary value and iterator to simplify inspecting the content with the debugger - Added a Set/GetEngine to the CDebugger to hold an engine pointer that can be used by the callbacks - The debugger's ToString method and Callbacks now take a expand members recursively - Implemented DictionaryToString debugger callback in asrun sample REVERT: a13051ef2a Fixed some compiler errors on Linux with ARM REVERT: 4455d829d0 Fixed a possible null pointer access while releasing a script function REVERT: ebfc38c7f2 Fixed a compiler problem in asrun when C++11 support is not available REVERT: d212634bc5 Fixed compiler warnings in asGetTypeTraits on gnuc 5.1 REVERT: 2176ce3332 Fixed a null pointer access failure that could happen in asGetActiveContext REVERT: c6a7a000bc - Improved Android project files - Added configuration to build AngelScript for Android with MIPS REVERT: 9938f3d678 - Added meson project file - Changed a couple of tests to not fail if regression test suite is executed from the wrong path REVERT: 64ea1694ea Added an android project for test_feature REVERT: 2d3f8c0c03 XBox 360 now supports 64bit integer arguments in native functions REVERT: 83ab984007 Fixed compiler error with AS_MAX_PORTABILITY REVERT: 1e06c7f0eb Added #if !defined(AS_MAX_PORTABILITY) condition in the GNUC assembler files to avoid compilation errors when AS_MAX_PORTABILITY is defined REVERT: f9a5aafa9f Added supported for native calling conventions on Linux with MIPS REVERT: d6312f6d73 Native calling conventions on Linux with MIPS is now fully working REVERT: 754023866e Working on native calling conventions for Linux with MIPS REVERT: e3ad5cfebf Fixed a bug with native calling conventions on Win32 when using Clang with MinGW 4.7+ REVERT: 63c9c072a5 Fixed crash on Win64 when calling native function that takes object by value that requires clean-up after call and the function also return an object by value REVERT: 9eecf27d2c Working on support for native calling convention on Linux with MIPS REVERT: 7aac1390da Fixed assert failure in compiler when creating delegate from temporary object REVERT: 81eb0833fa Working on support for native calling conventions on Linux with MIPS REVERT: c36b608be8 Working on support for native calling conventions on Linux with MIPS REVERT: c899ba1332 Working on support for native calling conventions on Linux with MIPS REVERT: a3fa86cc67 Preparing for Linux MIPS REVERT: 92fe6b8d40 Saving bytecode that used templates with multiple subtypes is now working properly REVERT: b51b4072bb Fixed bug with opIndex when global variable had same name as datatype in a different namespace REVERT: fb79903ce6 Script array is now constructed even if the current script context has a script exception set REVERT: a43411c013 Included asserts in CScriptAny to detect improper usage from application REVERT: efb0a831cd Fixed problem with ternary conditions returning references to global variables REVERT: 38f9bf2ea1 Parser properly handles template type and non-template type of same name in different namespaces REVERT: d4c789236c Added formatUInt for strings REVERT: 7b652687dd Added assert in CScriptArray to verify that the informed object type is indeed an array type REVERT: 9ad99f428b Implemented asBC_Thiscall1 for faster calls to class methods that take an int argument and return a reference REVERT: 68c2f844e8 cmake now reads the version from the angelscript.h file REVERT: 17583ec9dd Implemented an experimental byte code instruction for quicker access to array elements. It reduces the overhead of the opIndex call to about 1/3rd. REVERT: d0db10a7f2 - Added GetBuffer to the CScriptArray object - Added GetRef to the CScriptHandle REVERT: 1ea8dc3e64 Fixed bug with compiler accepting null handle in initialization lists that didn't support it REVERT: 2ba7ab93b5 It is now possible to register member properties as references if the member should be dereferenced before access REVERT: 8ad8dae1dc Serializer attempted to backup non-pod types that had no user type registered with bitwise copy potentially causing errors REVERT: a4ffb5f59b Fixed bugs in serializer add-on REVERT: a34206d682 Compiler no longer gives error when access global variable in global namespace from class in namespace REVERT: bc7f3697b8 Documentation REVERT: 7e403bfc93 Fixed bug in compiler when using compound assignment on virtual property on temporary variable REVERT: c79bf717b7 Releasing version 2.30.0 REVERT: b929ab4c65 Documentation REVERT: acfd1b4916 Documentation REVERT: cf80801f81 Fixed incorrect compiler warning about converting to handle after get accessor REVERT: bef2781977 Fixed crash on iOS/arm64 with thiscall callback functions REVERT: cd4adcab09 Fixed bug in asBC_u64TOd and asBC_u64TOf on non-MSVC compilers REVERT: 817988ec6e Documentation REVERT: ed11f51869 Documentation REVERT: 23e6b93129 Fixed a couple of compiler warnings on XCode REVERT: 0fd3fc8f83 - Template instances have the clean-up instructions in the functions re-prepared - Prepared CContextMgr for use with AS_MAX_PORTABILITY - Fixed bug in dictionary's opIndex when generic calling convention was used - Fixed bug in CScriptHandle when AS_MAX_PORTABILITY was used - Fixed bug in CScriptWeakRef when AS_MAX_PORTABILITY was used REVERT: 0e394473fc Documentation REVERT: aa59c4e078 Added engine property asEP_PRIVATE_PROP_AS_PROTECTED REVERT: 28a96b5624 Documentation REVERT: 441b1ebcba Mapping the document changes that are needed REVERT: fd13ec11d2 More cmake enhancements REVERT: e5cfebf1af Documentation REVERT: 352a5d9573 Recursively search parent namespaces in methods that look for functions, global variables, and object types REVERT: 638ebd212f Improved cmake project to support building the library as shared object REVERT: 769ca172ee Fixed problem with compiler not detecting incorrect type when passing class method as argument to constructor REVERT: e494b21916 bug fix: Methods declared in mixins in namespaces weren't compiled with the correct namespace REVERT: ff574d8f75 Directly calling a funcdef retrieved from member property accessor would get wrong arguments REVERT: 14ead22a69 Compiler didn't properly check the access masks for registered class methods REVERT: 44343a783b Fixed compilation error on platforms with SPLIT_OBJS_BY_MEMBER_TYPES config (e.g. gnuc) REVERT: b8f09654ab Improved WriteConfigToStream so that template types are fully registered before other types REVERT: ce6d59a27c Included check to avoid modifying template types registration after a template instance has already been generated REVERT: 4a44d597c9 Fixed problem with nested namespace in ConfigEngineFromStream REVERT: 582e1cba77 Fixed bug in compiler where default argument could use temp var used in previous arguments REVERT: dd927755c7 Fixed ScriptArrayIsEmpty_Generic that didn't set the return value REVERT: fa90832a29 Fixed an issue with the global namespace in ConfigEngineFromStream REVERT: efa5c626ef - When formatting type declarations for messages the namespace will always be included if different than the current namespace - Fixed WriteConfigToStream to keep the valid object type flags REVERT: ed4f2d57f0 Fixed a couple of invalid memory accesses detected with valgrind REVERT: b1412ea68e Fixed compiler warnings with gnuc REVERT: 490d8cc0cd Fixed access to previously deleted memory when discarding modules REVERT: 4523ce7abb Fixed for multiple overloads for opCast and opImplCast REVERT: ec6c22c5f8 Deprecated asBEHAVE_REF_CAST and asBEHAVE_IMPLICIT_REF_CAST REVERT: edf43b9a5a Script classes can now implement opCast and opImplCast to provide custom ref cast behaviours REVERT: 19452c4370 Added support for protected class members REVERT: 70c0aa4b66 Private class properties are not accessible from derived classes REVERT: 6620c4fed8 - script engine now only keeps reference to shared script types - removed compiler warning on empty script sections REVERT: 70ff76edb7 Changed delete to asDELETE REVERT: 18945fd0ca Fixed bug in compiler for compound assignments with virtual properties REVERT: bd8173c7ac - Completely re-factored the way the memory management for the internal script code is done - Discarding modules is much faster, and doesn't burden the garbage collector - No more memory build-up when the script has circular references between classes and templates REVERT: 3ba650dc66 Fixed a problem with the conditional operator returning a reference to objects in parameters REVERT: 5b34473f1a Better error messages to explain why compound assignments are not always supported for property accessors REVERT: 2ed370fb8a Implemented a test to verify performance of rebuilding a module repeatedly REVERT: a878e42615 Compound assignment operators can now be used with virtual properties REVERT: d3281b396d Fixed GetModule() thread safety for real REVERT: 51e915475a Fixed thread safety issue in GetModule REVERT: 61a7fd79ae Planning a redesign for module clean-up for improved performance during shutdown and recompilation of modules. REVERT: ea3345fb0b Added support for compound assignments for global virtual properties REVERT: c00ed1c0ac Documentation REVERT: 0ced9c01ce imported functions can now be placed in namespaces too REVERT: 894eeabca4 Tests and documentation REVERT: ab78103964 It is possible to register an enum with the same in a namespace after it has been registered in the global namespace REVERT: 4812cd99d0 CScriptBuilder expands relative paths to absolute paths before verifying if the include file is duplicate REVERT: ff192e1d93 Debugger now accepts namespace for printing variables REVERT: bf2537042f opImplConv can now be used to allow implicit conversion to bool for registered value types REVERT: d662b11976 The ternary condition operator can now be used as lvalue if both options are lvalues of the same type REVERT: fec451a013 Changed SetLineCallback so that it is safe to call it from a second thread while the context is running REVERT: 4f264b13a4 Reduced overhead in CallSystemFunction REVERT: 5a857c3175 Completed documenting the syntax in BNF REVERT: d3af3139f1 Tweaks and code clean-up REVERT: c29db17fef Minor tweaks and optimizations REVERT: 8716f462fe Minor tweaks and optimizations REVERT: e25147b5e9 Updated the manual with the maximum and mininum value for double REVERT: 7f4a23ee55 - Implemented support for user data in script object instances - The asCArray now use asUINT to hold the size instead of size_t REVERT: b84f429291 dictionaryValue has better support for boolean values REVERT: 4f37b2b656 Documenting the syntax in BNF REVERT: 22eab20828 Added ShutDownAndRelease to explicitly tell when the engine should be shut down, compared to simply releasing the reference REVERT: 07b95ab0fe Documenting the syntax in BNF REVERT: 4ba7604879 Documenting the syntax in BNF REVERT: 40e366f410 Simplified the CFileSystem a bit (no longer does it do pattern matching when enumerating files/directories) REVERT: 5c0c2c0406 Implementing Linux support for the file system add-on REVERT: 151e39b4f6 Fix to the gnuc makefile REVERT: e3af360d8f Minor portability fixes for Linux REVERT: 02818a9358 Updated gnuc makefile to automatically retrieve version number from angelscript.h for the shared library file name REVERT: 618faf5b9a Fixed some compiler warnings on gnuc in the add-ons REVERT: a5db6461bf Implemented SetArgVarType to the context for calling functions with variable argument types REVERT: eee744cfeb Continuing to document the language syntax in extended BNF REVERT: 2b8750cfd8 Starting to document the language syntax in extended BNF REVERT: edc9ac4928 - Fixed bug in CScriptFile - Improved compiler error messages when constructor/destructor is given the wrong name REVERT: 88ef135865 Changed CScriptFile::ReadLine and ReadString to return the string by value instead of as an output parameter REVERT: 81d660cf16 Class method can refer to base class' method with fully specified namespace REVERT: 73d7a15e84 Application can now register types with asOBJ_IMPLICIT_HANDLE if this option is turned on REVERT: e6e9c032fb - The dictionary now supports function pointers in the ref cast behaviour again - RefCastObject returns an error if used for a function pointer REVERT: ab7c741385 Implemented asEP_DISALLOW_EMPTY_LIST_ELEMENTS to allow application to disable empty elements in initialization lists REVERT: 9272a923be When compiling a default argument expression the namespace of the called function is used as default REVERT: cf51a38e67 - CScriptFileSystem properly supports Unicode file names - asrun can write Unicode text to console and it is automatically converted to current locale REVERT: c51b955654 - Changed ContextMgr::AddContext to allow application to tell manager that it want to keep the context after execution - Added ContextMgr::DoneWithContext to return the context after the application has retrieve what it wants from the - Fixed the asrun sample so now returns the status from the script again REVERT: 736f875de4 Created a filesystem script add-on REVERT: 638b55695f - The compiler was producing wrong bytecode when calling asBEHAVE_IMPLICIT_REF_CAST on value type - Fixed crash when script class contained value template type as member - The weakref is now a value template type - The syntax for using weakref in script is much more like a normal handles REVERT: d144b5eb63 - GetFunctionByDecl wasn't able to find the global function if the name was prefixed with :: - Returned declarations with namespaces no longer prefix symbols with :: when declared in the global namespace REVERT: d2066231f6 The constructor stubs for value template types didn't pop the object pointer from the stack REVERT: f1964f7cfd - Renamed CastObject to RefCastObject - Deprecated asIScriptEngine::IsHandleCompatibleWithObject - CScriptHandle, CScriptAny, and CScriptDictionary use RefCastObject to properly cast the contained handle REVERT: eb67007487 GetModule and DiscardModule are now thread-safe REVERT: 97a0581bf6 - Fixed an assert failure in the compiler when compiling an assignment with non-temporary deferred arguments - The dictionaryValue was missing a copy opAssign method so it wasn't possible to directly assign one dictionaryValue to another REVERT: 94243f0968 - Fixed bug with asOBJ_APP_ARRAY in arm and ppc64 calling conventions - Fixed bug in Linux arm hardfloat ABI - Added support for native calling conventions on PS Vita REVERT: f5b97db9cd - Template specialization failed to remove generated template instances that has been created as a consequence - Engine detects if application attempts to register method or property for generated template instances REVERT: 5876d72bca - Implemented asIScriptEngine::CastObject - The CScriptHandle uses the new CastObject to properly cast to all supported types of the contained handle REVERT: cee64c7c56 Added asIScriptObject::GetWeakRefFlag REVERT: afb16b497f - asIScriptEngine::AssignScriptObject now returns an int with the success or error code - The tokenLength arg for ParseToken is changed to asUINT REVERT: 1a6c8f60ae Template specialization failed to remove generated template instances that has been created as a consequence REVERT: 209a11fa6a Fixed an incorrect error code when registering a template specialization and the template type instance already existed REVERT: 1f6820261a Improved the algorithm for verifying if script classes require garbage collection REVERT: 2d56ebec05 Delayed the callback to template instances further so the template can know if the script class will be garbage collected or not. REVERT: f4476236c7 Funcdefs can now have template types with script classes as subtypes REVERT: 5b2a2dc236 The script array and grid add-ons avoid flagging themselves as garbage collected for handle subtypes that are known not to be garbage collected REVERT: 892ed82632 Compiler will now give error if there is an overflow when evaluating power-of in constant expression REVERT: 5e3ef4c3c0 Deprecated asBEHAVE_VALUE_CAST and asBEHAVE_IMPLICIT_VALUE_CAST REVERT: bea2bb2246 Fixed bug when comparing two handles and one is a handle to a read-only object REVERT: c0b8591b7f Preparing to deprecate asBEHAVE_IMPLICIT_VALUE_CAST and asBEHAVE_VALUE_CAST REVERT: 7c4bb6cae8 Improving the test case for aligned scoped reference REVERT: edac97e629 Fixed bug with use of scoped reference types declared in global variables REVERT: 29be95f106 Updating the version string in angelscript.h REVERT: 02b6519670 Releasing version 2.29.2 REVERT: 4fcbc16c05 Documentation REVERT: 846c77ae7d Documentation REVERT: ede21ae639 Fixed bug in compiler with use of implicit value conversion and temporary variables REVERT: 3a2e261263 Updated asBC_CALLBND so that imported functions can now be bound to application registered functions too REVERT: 1e57d8255a asGetTypeTraits compiles on gnuc 4.8+ again REVERT: a031a31527 Adding missing test_propintegerdivision.cpp REVERT: 0ea883fbd6 Compiler automatically resolves ambiguous enum values on comparisons REVERT: cadcb29d39 Fixed bug in CScriptFile when reading ints for least significant byte first REVERT: 08badc7c5a CScriptBuilder will report the correct path to the include files that fails to load REVERT: ebb643ef7a CScriptBuilder::AddSectionFromMemory now has an optional lineOffset argument REVERT: 36e755d349 The template types can now refer to each other using different template subtypes REVERT: 079d220842 Template types can now use other template types as method arguments where the sub-types are shared REVERT: 34701b24c1 Compiler warns when overloading inherited class method and default args in the methods cause conflicts REVERT: 673c6bc408 Compiler warns when variable in inner scope hides variable of same name in outer scope REVERT: ac91c57dfe Store the owning module pointer in interfaces, enums, and typedefs REVERT: 3f34f038e6 Improved performance of shutting down the engine REVERT: 67513235c8 Added asEP_DISABLE_INTEGER_DIVISION that makes all divisions with integer values result in doubles REVERT: dccea150dc Template instances now receive the member properties that were registered for the template REVERT: 33ad23c246 Improved compiler compatibility of asGetTypeTraits to support Xcode/Clang REVERT: c6760055eb Application no longer crashes if an asIObjectType is released after the engine has been released REVERT: 1e9f0ecbee Script classes can now inherit opConv and opImplConv from base class and add its own for other types REVERT: 916782a782 Passing a non-lvalue expression to a &out argument will now yield compiler error instead of warning REVERT: 923c10443c dictionary::get now supports retrieving stored handle by value if type is compatible REVERT: 9dd9633b91 Script classes can now implement opConv and opImplConv to support type conversions REVERT: 96b90c049c Concatenation with integers and string now properly supports 64bit types REVERT: 5fe60dd116 Identity comparison on handle received as parameter by reference wasn't working REVERT: 1e8eaca610 Fixed crash in compiler when compiling initialization lists with 'null' as one of the elements REVERT: 455010fec2 Taking the address of an imported function now works as it should REVERT: c13c516b32 Compiler was incorrectly accepting syntax as object@(expr) REVERT: e96445fc3a Fixed problem with storing/retrieving enum values from the dictionary REVERT: 0fd3b473e4 Fixed incorrect warning on literal -9223372036854775808 REVERT: a363068020 Merged patch with partial implementation to support 16byte aligned native types, e.g. __m128. The implementation will be turned off by default until it is completed. Turn it on by defining WIP_16BYTE_ALIGN. REVERT: 1538e828b3 Fixed bug when global variable expression had casts between script classes REVERT: fa33516901 - Empty initialization lists are now properly working where a repeat pattern is expected - The array and grid add-ons no longer set a script exception if the initialization list is empty REVERT: aef636c563 Fixed bug related to opCall overload REVERT: a064a09e65 Releasing 2.29.1 REVERT: 6543366dc6 Documentation REVERT: 2660b4d911 Fixed errors with saved bytecode when using template types declared in namespace REVERT: 66e913e777 Fixed bug in closeTo() math add-on function REVERT: 34a6e5a8e2 Fixed parser bug when local variable had the same name as a type in different namespace REVERT: 246ebd7008 Fixed compiler warnings REVERT: 215790ad79 asOBJ_TEMPLATE can now be used with value types too REVERT: 6f5b93aa20 Improved error messages REVERT: 2aab9ae56d Added support for declaring classes as abstract to prevent instantiation REVERT: 61d3b72075 Added asEP_ALTER_SYNTAX_NAMED_ARGS to optionally support the previous syntax for named arguments REVERT: b29d088eaa Changed syntax of named arguments to func(arg: expr) REVERT: 96eaffc094 - The module's ResetGlobalVars uses the context callbacks to support debugging initialization of global variables without informing a pre-created context - The context callbacks are disabled before destroying the engine to avoid creation of new contexts while cleaning up objects - The asrun sample is now using the ContextMgr add-on to support co-routines - The ContextMgr uses the RequestContext instead of CreateContext to take advantage of the context callbacks REVERT: 3fe8a21560 Replaced the helper function PrintException with GetExceptionInfo that returns the information in a string REVERT: 0f3ecfa65b Promoted asGetTypeTraits<T>() from the helper add-on to an official interface function REVERT: cd88cd1c0f Improved compiler error messages REVERT: b9e837aa11 Improved compiler error messages REVERT: b3c65534a2 Fixed buffer overflow problem when Prepare() with virtual script function near the end of the stack memory REVERT: e187dfb287 Fixed null pointer access bug in grid add-on REVERT: a95034ddab Fixed problem with enumerating entities after removing a config group REVERT: 169cee8353 Fixed compiler assert failure with unsafe references and deferred arguments REVERT: 236810f63a Array elements are initialized to zero when resizing the array too REVERT: 4882c42f44 Array elements are initialized to zero by default REVERT: 96391a0873 Minor clean-up in the CDebugger REVERT: f146c90538 Releasing 2.29.0 REVERT: eff8892933 Documentation REVERT: 3ee5340f8c Changed to use a single SetContextCallbacks method instead of two separate methods REVERT: aaf88c43e5 Documentation REVERT: fad247ef66 Documentation REVERT: 7e77a16479 Documentation REVERT: 5f9affeb31 - Updating the coroutine samples with the changes to the contextmgr add-on - Documentation REVERT: 52a06edfe0 Compiler now gives an error when script declares handle to handle REVERT: 1db9a4d87a Documentation REVERT: 5f09f636f9 Resolved conflict between named arguments and initialization of anonymous objects as argument REVERT: f3e92d10ba Code clean-up REVERT: 42e6912de8 Mixin classes in parent namespace can now be properly included in a class REVERT: 0ef2438822 asCALL_THISCALL_OBJFIRST and asCALL_THISCALL_OBJLAST works on Linux ARM with softfp too REVERT: 063442dd1b Added support for registering functor objects as class methods with asCALL_THISCALL_OBJLAST and asCALL_THISCALL_OBJFIRST REVERT: 54b30fdad4 Fixed compilation error in asrun REVERT: 5cb7f2b53e Engine no longer silently accept registering template methods where the template subtype is passed by value REVERT: 54ff0c8bf0 Fixed bug in compiler that allowed expression with name of function without doing anything with it REVERT: 6d33c13393 Stack unwinding now works on Linux 64bit with native calling conventions REVERT: 63e331129c Improved runtime performance by avoiding increasing/decreasing refcount of script object in class methods REVERT: 875fa0c7c3 - asbuild now uses the ConfigEngineFromStream helper function - code clean-up REVERT: 1212396197 GarbageCollect(asGC_FULL_CYCLE | asGC_DESTROY_GARBAGE) will now properly destroy the garbage in the new generation too REVERT: b6c004b6d4 WriteConfigToFile wasn't saving the return type for the string factory correctly REVERT: b4855d58fa - Fixed compiler warning on CLang - Updated xcode project to support both 32bit and 64bit targets REVERT: 675933d310 Fixed problem with the compilation of asBEHAVE_VALUE_CAST with ?&out and primitive REVERT: cc362e52ad Added the argument numIterations to GarbageCollect to support multiple iterations without having to call the method multiple times REVERT: d08bac1b14 Fixed AS_NO_COMPILER REVERT: fe9fec344e Improve the touch scroll in the documentation REVERT: e55b08c19d Working on the context manager add-on REVERT: 903b962a83 - GetSubType now works for registered template specializations too - Loading bytecode with funcdefs declared from script wasn't working identical to building from source REVERT: 4abb2f9367 Fixed the scrolling in the documentation on Android devices, without hiding the nav-tree REVERT: d2f7931722 - Added RequestContext, ReturnContext, SetRequestContextCallback, SetReturnContextCallback to engine - Removed the custom header for the html documentation since it was broken REVERT: 7cf4265ba3 GetSubTypeId now works for registered template specializations too REVERT: b8a48b9165 Fixed bug in GetDeclaration when returning the list pattern REVERT: c2be2a8756 Dictionary uses asAllocMem and asFreeMem, and has factory functions to create instances REVERT: 59618bfe4a Reduced the size of the top logo a bit REVERT: bb6dccb2a2 - Updated doxygen files to add support for Android touch devices - Added a small logo on top of each page REVERT: fe44710459 Dictionary now has the opIndex that can be used to directly assign and retrieve values without using the set/get methods REVERT: b58b774692 Implemented C++ operator[] for the CScriptDictionary add-on REVERT: c0cff2bcbd Fixed the missing check for declaration of duplicate global variables REVERT: 485263ce01 The value cast behaviour with var type works for conversions to primitives too REVERT: 0346865a34 asBEHAVE_VALUE_CAST now supports the generic form 'void f(?&out)' REVERT: 6f7fb2b7a7 Defined the MULTI_BASE_OFFSET macro for the Sun CC compiler REVERT: 9f0d401f34 CreateScriptObjectCopy now uses the copy constructor for value types if available REVERT: 020e847d3c Compiler choses opHndlAssign or opAssign on initialization based on how the object was declared REVERT: 4d8e582e2e Improving the opHndlAssign usage REVERT: 17d70d9f75 Types registered with asOBJ_ASHANDLE can also register the opAssign method to support value assignments REVERT: cebd1b0311 opHndlAssign should be used to overload the handle assignment operator in registered ASHANDLE types REVERT: 39ade9bcbb Added the global functions asAllocMem and asFreeMem REVERT: 824a0a5e9b Added support for multiple user data in module, context, and function REVERT: 96619fe1ea Improved error message when trying to pass class method as argument REVERT: 8ce6959f45 Documentation REVERT: bc9cdbd674 Compiling the library with Sun CC for SPARC is now possible, though without native calling conventions REVERT: c2e521d339 Documentation REVERT: 14faebfb46 Preparing the test_feature for iOS and Android REVERT: 54eab28c39 Minor issues detected by PVS REVERT: b486b63287 Improvements for AS_MAX_PORTABILITY REVERT: ea74037913 Project updates REVERT: 0cc92e7ba8 Fixed compilation error in scriptdictionary on xcode 5.1 REVERT: 080727c8f1 Fixed some compiler warnings REVERT: bd90821cd7 The template callback can now write error messages to facilitate the understanding of why a template cannot be instantiated REVERT: 89b66c746e Removed old deprecated methods REVERT: ad3dd514ca - CreateScriptObject will return null if the object constructor throws C++ exception - Array and grid properly handle exceptions while constructing elements REVERT: cca0a8595c Improved C++ exception handling to avoid deleting object that was never returned REVERT: 90ba91cb03 Fixed assert failure when compiling auto declaration without initialization expression REVERT: 6faa1a453e Fixed bug with explicit construct call and reference types REVERT: 963413d704 Clean-up REVERT: 6f659d9ff5 Incorporated the patch for 'auto' REVERT: 067c8dfee0 Added GetObjectTypeByDecl REVERT: a9c90cf289 - The parameter names are stored with the saved bytecode, unless debug information is stripped - Added asIScriptFunction::GetParam that can optionally return both the name and default argument in addition to the type id - Deprecated asIScriptFunction::GetParamTypeId REVERT: 6c176e1816 Fixed bug in tokenizer for one line comments on the last line of a script REVERT: 1dab5aee29 Fixed bug in compiler where temporary variable could be overwritten when calling overloaded operators REVERT: 9dd7c2360d Added default factory and resize method to the grid REVERT: 61b775eaf6 Fixed crash when calling opCall on a property accessor on a global variable REVERT: 4d55f7fa22 Fixed opCall on global variable REVERT: 9604669f5a Fixed assert failure as the compiler tried to release the same temporary variable twice when compiling an implicit cast for a value received from a get accessor REVERT: 216eadf007 Added support for named arguments in function calls REVERT: 21de525201 Working on the named arguments support REVERT: 04323de214 Parameter names are now stored in the script function object REVERT: 1e24c2043c Fixed RWX section in as_callfunc_arm_gcc.S REVERT: c7b4ee6a3f Fixed minor issue when compiling with AS_DEBUG REVERT: 4f43892139 - Function handles were incorrectly treated as handles in value assignment - Added the flag asOBJ_APP_ARRAY to support registering types that are really C arrays - The GetTypeTraits<T>() helper function automatically detects when to use asOBJ_APP_ARRAY REVERT: 0846005bc0 Releasing 2.28.2 REVERT: f42c8b919c Documentation REVERT: 1a6386773f Documentation REVERT: d70f74c6fc Fixed bug when passing 'null' or 'void' to var type argument REVERT: b5e5cd3f20 Fixed compiler warnings on gnuc REVERT: c308b35a2f Fixed problem with passing ref type to const ?&in parameter REVERT: ef606baca5 Documentation REVERT: ccf0b638e2 Documentation REVERT: 4a90e5f789 Fixed bug with missing implicit ref cast when passing object to parameter as inout reference REVERT: c16bf786c0 - Finished the grid add-on - Improved parser error messages REVERT: 4f5c92257c Starting the work on the new grid add-on REVERT: e64986e99c Optimization to avoid copy when passing ref type to const &in parameter REVERT: 256a0347e7 - Cleaning up some code in the compiler - Improving compiler error messages REVERT: a2c80cb7c1 Optimization to use the copy constructor instead of the assignment when preparing temp object for func argument REVERT: 4d278e48f1 - Added GetTypeId to the dictionary to retrieve the type id of the stored value - The dictionary now has an iterator on the C++ side that supports C++ range-for loops REVERT: be1ae7dd2d Improved implicit cast of object to primitive in math expressions REVERT: a21bb53cf0 When unsafe references is turned on the compiler allows passing literal constants to const references REVERT: 2a610f1140 Fixed memory leak with registered cast behaviours on template types REVERT: 5c72920e84 Fixed assert failure in compiler when using opAssign that returns a value instead of a reference REVERT: 85effdc3c4 Fixed crash when returning primitive value implicitly cast from global handle REVERT: c531b98141 The script array should be allocated with factory functions instead of directly with new REVERT: b7044eba66 Fixed some compiler warnings REVERT: ee85730ad5 Added custom memory functions to CScriptArray REVERT: 2a1fa4b147 Code clean up REVERT: 32c1c6ed7c opCall works for objects returned by property accessor too REVERT: 747429d6d4 Implemented support for opCall REVERT: fc408296d7 Compiler now casts to common base class when comparing two handles REVERT: 404ddb6ae9 GetObjectTypeById crashed if the typeId represented a template instance type that had already been released REVERT: 04fce3aebe Cleaning up the code REVERT: 23923eb083 Completed the implementation of multiple args for opIndex REVERT: 3e0ff93f26 Fixed the pre-compiler condition to avoid use of InitializeCriticalSectionEx on Windows XP REVERT: 32fb54f073 Fixed saving initialization lists for arrays of int8 or any type smaller than 4 bytes REVERT: d3790b0db7 opIndex with multiple args is now working REVERT: 9c92ca69dc Working on adding support for opIndex with multiple args REVERT: 0088e23641 - Moved define of AS_CAN_USE_CPP11 from scripthelper.h to angelscript.h - Implemented C++11 move operators in asCString REVERT: cbe1e5ca4b Minor improvements for the garbage collector REVERT: 074da2467d GarbageCollect() can now be executed with full cycle in parallel with script executions in other threads REVERT: 5dc6679eaf Fixed a problem with the implicit cast of the right hand operand with bitwise operators REVERT: f062e30770 Improved error handling in the asCReader class REVERT: e48ac8d868 Reduced size of saved bytecode REVERT: e5bdf73481 Anonymous objects can now be initialized in expressions with lists REVERT: 6041570dc7 Tokenizer was splitting identifiers that started with keywords that ended with numbers, e.g. int8 (Thanks loboWu) REVERT: 03aa840375 Compiler no longer warns on sign change for bitwise operator with negative enum value REVERT: c124bf7621 Fixed bug with saved bytecode and initialization lists that skip values REVERT: db0f977f46 Fixed crash after loading bytecode with initialization lists holding ref types by value REVERT: 912fef2453 - Implemented closeTo in math add-on - GetTypeTraits<T>() no longer incorrectly identifies arrays as asOBJ_APP_CLASS REVERT: 501dddc0f8 Added a warning if JIT compiler is used without asEP_INCLUDE_JIT_INSTRUCTIONS turned on REVERT: 936325887c It's now possible to use repeat_same in list pattern to tell compiler that all lines must be of the same length REVERT: f0b745162f The Library can now be compiled for WinXP with multithread support again REVERT: f130cd0ffe Releasing 2.28.1 REVERT: 6917ad3479 Fixed crash when registering two or more methods for an object type using template instance types in parameters or return value REVERT: ec936b9e14 Changed the set() methods on the dictionary to take the reference as const REVERT: 89753000ba Fixed crash when attempting to assign a script object to another of another type REVERT: c47f6d738e CScriptHandle will hold a reference to engine to avoid crash if engine is released before the contained reference REVERT: 7ec322ac0e Fixed a mistake in the manual regarding default arguments REVERT: 5ae63f164d Fixed intermittent crash with registered object properties of template instance types REVERT: f5dd9018aa Fixed compilation errors on MinGW REVERT: ff9bf0c1cf - WriteConfigToFile escapes quote characters in default arg expressions - asbuild sample updated to treat the escaped quote characters properly REVERT: 754fb316f5 Fixed memory leak on iOS when passing complex object by value to registered function REVERT: e60390d093 Improved messages when loading invalid bytecode REVERT: 2cd071c73f Slightly reduced size of saved bytecode REVERT: dab8aceddb Compiler was incorrectly converting both results to const in ?: if one of the expressions was null REVERT: 79e8344812 documentation REVERT: e9b2751585 Fixed incorrect bytecode sequence when compiling initialization list for dictionary containing values from global string variables REVERT: d3e4ad4ab0 Implicit cast didn't work in initialization of global vars REVERT: 66add3c907 Fixed crash with THISCALL_ASGLOBAL and virtual methods REVERT: 2fd805500d Fixed compiler error on gcc due to missing abs() REVERT: e193aea59e Fixed compiler error with abs() on VS2005 REVERT: da79c44e89 Test cases for the exponent operator REVERT: b3d9879144 Implemented the ** operator to perform exponent math operations natively in the script REVERT: 499208adae Corrected the use of WINAPI_PARTITION_DESKTOP define REVERT: dc4488b8d2 Multithreading is now supported on Windows Store Apps too REVERT: eeb1b36545 Working on support for multithreading on Windows Phone REVERT: ce1dfe0c07 Fixed linker error when compiling for Windows 8 Store REVERT: fa978db54b Ternary condition operator now converts either case to const @ if the other is const @ REVERT: 2db21bf45f Fixed assert failure in CScriptWeakRef REVERT: bbd426f243 Documentation REVERT: 191aa53171 POD value types are now allocated inline when members of script classes REVERT: b2b4cee717 Improved the to-string callback feature in the debugger to better support template types REVERT: 4108d80480 It's now possible to register to-string callbacks with the debugger to automatically convert objects to readable strings REVERT: a6b3d89f32 Fixed some compiler warnings REVERT: 021ad7fceb Reduced overhead in calls to interface methods REVERT: c372509d6b - The compiler will give an error if the script didn't produce any output. - SaveByteCode will return an error if called on an empty module. REVERT: 84b06064af Compiler wasn't searching parent namespaces when resolving the symbol for class inheritance REVERT: cf4624cce3 Compiler is capable of using constructors with default args as default constructor REVERT: 1938a65407 Linux ARM now supports soft-float ABI too REVERT: 74202997a1 Added verification against attempt to compile AngelScript with -mfloat-abi=soft on Linux/ARM as it doesn't currently work REVERT: 034ddcfa56 Fixed the position reported for errors while compiling global variables REVERT: 709e4911ae Slightly reduced memory consumption as virtual functions no longer store the default args REVERT: 48d854fd95 The compiler now uses the default args declared in the derived method instead of the base method REVERT: 657538936b Added a verification against use of -ffast-math on Linux with ARM as it breaks the native calling convention REVERT: a465d8e1b3 Implemented FindByRef in the script array add-on REVERT: 2e9e945d52 Compiler is capable of resolving ambiguous enum value by looking at destination REVERT: 7034c5979b Improved error message from garbage collector when application holds on to a delegate while releasing the engine REVERT: 0515923942 Added GetTypeId() to CScriptHandle to make it easier to retrieve function pointers REVERT: 3f67b427a8 - Fixed assert failure in Execute when forgetting to set the object on a class method call REVERT: af007781ca Added check against attempts to register multiple list factories REVERT: 3f4ac0ff61 Optimized compiler performance REVERT: 2b29228415 Optimized the lookup of registered types REVERT: 7b66d53235 - Added as_namespace.h - Optimized the symboltable key by using the namespace pointer directly instead of the name of the namespace REVERT: 381bb6e997 registeredGlobalFuncs is now a symbol table for faster lookups REVERT: c44ff55faa - Minor refactoring - Compiler will not use enum value from registered enum types if the module doesn't have access to it REVERT: 47ba2dd67e Optimized IsTemplateType() as used by the parser REVERT: c4403f1665 Minor optimizations and refactoring REVERT: c7d069dfc4 Removed some unused code in AS_NO_COMPILER mode REVERT: 0b6523aa3f Added verification to avoid use of context from a different engine to call functions REVERT: c4abf385c2 Defined default arg for asSFuncPtr constructor to allow trivial instantiation REVERT: 38b5d2a99f - WriteConfigToFile now writes the engine properties too - asbuild sample updated to set the engine properties as informed in config file REVERT: 128640fa94 Another change to fix the warning of used types when removing config group REVERT: ec73595c63 Fixed memory leak on registering opAssign twice for the same object type REVERT: 1135f094c0 Fixed improper warning of used types when releasing engine and having circular references between registered types REVERT: 2b617d6348 Releasing 2.28.0 REVERT: cc0f3a8163 Fixed assert failure in compiler on error in script REVERT: 356ed928db - Updated xcode project - Updated config to detect iOS with 64bit arm REVERT: a34b95ceb6 The engine no longer writes parser errors to message callback in the GetByDecl methods REVERT: cebef64cf8 Fixed assert failure in compiler REVERT: a69c60ae0b Added support for atomics and posix threads with Android NDK 9+ REVERT: 9182d06e77 Fixed compiler errors with Borland REVERT: a4536c50d7 Fixed compiler error and warnings on g++ based compilers REVERT: b002bf3596 Documentation REVERT: 480b6ab2ad Documentation REVERT: 13926bb73b Documentation REVERT: d99ad0c620 Documentation REVERT: 09059b0442 The function declaration for constructors and destructors now show the object type name instead of _beh_x_ REVERT: 11a8f65ac5 GetVarDecl and GetPropertyDeclaration now take an optional includeNamespace argument REVERT: 91e23b4ce1 Fixed crash with chained handle assignment REVERT: 88d5433af0 Fixed GetTypeDeclaration for registered template specializations REVERT: 4f4508e9d1 Added code to detect and warn instead of crashing if application attempts to resurrect script object during its destruction REVERT: 128ae815f0 Added tests for value types with initialization lists as class members and within other initialization lists REVERT: fdc53ac46b Value type in global variable works with initialization lists too REVERT: b6e2bd56d9 Implemented asBEHAVE_LIST_CONSTRUCT REVERT: 4f2cfe0ca1 FindNextLineWithCode() works properly for class constructors where class has non-primitive members too REVERT: 1509f64cb3 Script functions store information on where they are declared to make FindNextLineWithCode more useful REVERT: 310f936497 Added GetModuleCount and GetModuleByIndex for enumerating existing modules REVERT: a421d9c701 Minor compiler performance improvements REVERT: 460076baf3 The object type for script classes that formed circular reference with base class wasn't resolved by gc REVERT: 34e9a54a12 Rebuilding a module without discarding it no longer accumulates memory in the engine REVERT: 9d35b3da38 Improved validations of some arguments in the script engine's methods REVERT: 0f5708a500 List elements are now properly aligned on 4 byte boundaries in the list buffer REVERT: 048ce9a73d - Fixed problem with conditional operator ?: and implicit casts of primitive types - Added warning on implicit cast from float to integer REVERT: b24cb5608c When no expression is given for a list element the compiler will try to use the default constructor. REVERT: 03019d915a Fixed crash in compiler when resolving function pointers with namespace REVERT: a6c47a1c01 - Fixed bug in IsNested when called with parameter - Fixed compiler error in CDebugger add-on when using the AngelScript namespace REVERT: ebf6750136 Fixed bug with implicit casts of large integer constants and uint64 REVERT: 9dff175aa9 Working on the implementation of list factory for the dictionary add-on REVERT: 87d63c8ab3 Working on the implementation of list factory for the dictionary add-on REVERT: cb78f667f1 Working on the implementation of list factory for the dictionary add-on REVERT: ee083233af Working on the implementation of list factory for the dictionary add-on REVERT: 3c542fa2e8 opEquals with funcdef works again REVERT: 43aff7e446 Fixed bug with passing handle to ?& arg with unsafe references REVERT: ff4fc84bc4 Fixed bug in script array resize() method that could cause a crash if reserving too much memory REVERT: 8849d51045 Fixed crash on registering template specialization twice REVERT: a4b2487987 Fixed crash on registering function with autohandles for unknown type REVERT: 30a159398a Implemented asBC_PshListElmnt with a 32bit offset to handle large initialization lists REVERT: 20376008cd Optimized tokenizer with jump tables REVERT: f72d0b3bef Fixed AS_NO_COMPILER REVERT: e9812b7b9e Fixed memory leaks with default args and shared functions REVERT: 66fdc1e031 Continued work on the initialization lists REVERT: 64dee4ba3d The engine now fully uses the declared list pattern to determine the layout of the list buffer dynamically REVERT: 489f3c29ec The bytecode writer now uses the dynamically declared list pattern to adjust the offsets in the list buffers REVERT: 29cc9b43a2 Unified the assembler code on Linux + ARM to support both normal and thumb mode REVERT: 05ae1009f3 Implemented bytecode instruction asBC_SetListSize REVERT: 53557fdf65 The compiler is now using the dynamically declared list pattern to compile the initialization list REVERT: 1e40ff3642 Working on the compilation of initialization lists REVERT: 418019879c The runtime cleanup of the list buffer is now using the declared list pattern REVERT: 41bb1a94c0 Fixed compiler error with asBC_DWORDARG on gnuc REVERT: 78d381352b Fixed null pointer access when calling GetMethodByDecl on template instance type REVERT: 38ede20930 Fixed null pointer access during compilation if JIT compiler is used REVERT: f10d6e278d Fixed stack alignment problem with native calling convention for Linux and ARM REVERT: 70660a47fc When registering the list factory behaviour the desired pattern must now be informed REVERT: 003c8a8b63 - The GET_WEAKREF_FLAG behaviour is now registered for the script types in AS_MAX_PORTABILITY too - Engine now properly supports use of autohandles in template factories - The weakref add-on now works properly in AS_MAX_PORTABILITY too REVERT: b9e9cc9d88 - The initialization list buffer now contains all values regardless of the type REVERT: 53a975d2db - The initialization list buffer now contains the values for arrays of ref types too REVERT: 07dda0a075 - The initialization list buffer now contains the values for arrays of handles too - The exception handler is capable of cleaning up halfway initialized buffers - Removed the new FreeMem bytecode and instead adapted the existing FREE bytecode to handle the list buffer too - The bytecode serialization code adjusts the offsets into initialization buffer to keep them platform independent REVERT: 80faa7239b - The list factory behaviour now receives a pointer to a buffer instead of just the expected length - The list factory buffer contains all the element values for primitive types - Implemented the bytecodes AllocMem and FreeMem for handling buffers for initialization lists REVERT: 7fb7f455d9 Releasing 2.27.1 REVERT: e6502b39f3 Implemented a different ExecuteString that can return a value of variable type REVERT: 9e8608d016 Documentation REVERT: 65eb54a379 >Fixed bug in CScriptBuilder with loading an empty file REVERT: e0b6feced4 Identified problem with doing memcpy to move objects to stack when they are passed by value to application functions REVERT: 1ab07d67b7 void is a legal expression and can be used to ignore output parameters REVERT: 8023214abe Default arguments can be used with anonymous parameters REVERT: 943cb9c4c5 For consistency @null is now a valid expression REVERT: ac05face4b Compiler was incorrectly making a copy of the object in some cases for explicit ref casts REVERT: 048bc213a2 Compiler was incorrectly reusing temporary variables in some expressions with ternary condition operator REVERT: 1471bb9e1b Fixed bug in the compiler when dealing with opAssign that returns anything else than a reference REVERT: 410842f912 Fixed 64bit bug in asBC_SwapPtr REVERT: dfc97c08a2 Implicit conversion to ASHANDLE type will not copy the value any longer REVERT: 0c0ab8a92b Engine no longer accepts registering template factory with autohandles REVERT: 625b1a58dd Reduced memory footprint for registered functions REVERT: f721681007 Fixed mispelling in compiler error message REVERT: 092a6d2af4 Releasing 2.27.0 REVERT: 163ff91031 Documentation REVERT: 469c949cee Documentation REVERT: f69b4e5c62 Documentation REVERT: 1a8c676fb0 Documentation REVERT: e2254db45c Documentation REVERT: 6b5fc10326 Deprecated AddRefScriptObject and ReleaseScriptObject that takes type id REVERT: 5d0b63ccd0 AssignScriptObject now takes asIObjectType REVERT: d75eef1fdc CreateScriptObjectCopy now takes and asIObjectType REVERT: b4fe421ae9 CreateScriptObject now takes an asIObjectType REVERT: f610e98b78 Changed the parameter type of CreateUninitializedScriptObject to asIObjectType REVERT: 24ac34c074 Made the weakref solution threadsafe REVERT: 6d8b93052c - Template arguments declared as const T@ will keep the handle-to-const attribute in template instances - Game sample now uses the weakref add-on REVERT: 09bfb84a63 - Registered the CScriptWeakRef as const_weakref too - Game sample now uses the const_weakref - Fixed bug in templates where 'const T@' would become just 'T@' in the template instance REVERT: ba44b55c68 Testing application registered type with weak reference REVERT: f5e2dd6324 - Added asCreateSharedBool for application classes to create instances of shared booleans - Implemented validations when registering the behaviour GET_WEAKREF_FLAG to check for invalid types and declaration REVERT: f2999368ba Implemented support for weak references REVERT: 2633bd95f7 Updated the trolltech qt makefile REVERT: c8e791cbbd Class methods with default args using parenthesis failed to parse REVERT: e0f6a3604c Fixed compilation error with has_trivial_destructor on g++ 4.8 REVERT: 395cf20287 Derived classes can no longer override base methods with different return types REVERT: 5e88d296dc - Registered value types that didn't have size evenly divisable of 4 weren't allocated with enough space - Template subtypes can no longer be declared as read-only REVERT: 3cda67d03b Function pointers for global functions in namespaces wasn't resolving properly REVERT: f29b1e99ca Fixed memory leak in compiler with shared classes and virtual properties REVERT: d06495eb81 Literal float and double constants accept the format 1e5 REVERT: 4ae4b8ba63 Engine no longer silently accepts modifications to built-in types by the application REVERT: 1ce5a0c7a5 Slightly reduced the amount of work performed by the GC REVERT: 08eb0d6343 - NotifyGarbageCollectorOfNewObject will return the sequence number attributed to the object - Added GetObjectInGC to allow inspecting objects that are currently known by the GC REVERT: 445a811818 GC will call script class destructor before breaking circular references REVERT: 68c17ea33a - GC now gives an id for each object from a sequence and reports this when it cannot destroy an object - Fixed memory leak in thread manager REVERT: 845d582e3e Improved performance of thread manager and with that reduced the overhead of script calls REVERT: fa2b8d2e29 Created the MSVC2012 project for the performance tests REVERT: cf7e05eed6 Fixed bug with max portability and delegates REVERT: ab4d5cbbb6 Nested calls in context could overwrite value in register REVERT: 0d0f783a73 Script array does a better search for matching opCmp and opEquals REVERT: 99d175a2a5 - Added type modifier asTM_CONST to indicate parameters and return types that are const references - GetArgTypeId and GetReturnTypeId now have optional parameter to retrieve the type modifier flags - Added GetFuncDefFromTypeId to allow getting the asIScriptFunction for a function definition REVERT: 58de77b4bc - Compiler didn't detect ambigious enum values when compiling initialization of global variables - RegisterObjectBehaviour and RegisterStringFactory now accepts asCALL_THISCALL_ASGLOBAL REVERT: 4e54cd9821 Added CreateDelegate to asIScriptEngine REVERT: 08eef73b57 Added GetDelegateObject and GetDelegateFunction to asIScriptFunction REVERT: b4844a6b60 Added Discard to asIScriptModule REVERT: cfaf12acf7 Added GetModule method to the asIScriptFunction and asIObjectType interfaces REVERT: 378e463a47 Script array add-on can now sort arrays of handles by calling the opCmp on the objects REVERT: 7de8114c27 Fixed compiler error on FreeBSD REVERT: e71573e4f9 GetExceptionLineNumber would crash if the exception had been raised in a generated function REVERT: 935ac2c30e A compound assignment to a class member could be compiled incorrectly if the lvalue had deferred parameters in expression REVERT: a98222620c Removed previously deprecated methods REVERT: fc33f3d72c Compiler would call a script class' opAssign method as if it was a system function in a specific situation causing a crash REVERT: 6e6bee9033 Fixed presentation issue in manual REVERT: 704e0a26ae Releasing 2.26.3 REVERT: 26fbabcfd8 Fixed some compiler errors and warnings with MinGW REVERT: 90b93bbd58 Added fpToIEEE and fpFromIEEE to the math addon. These are meant to allow platform agnostic conversion between floating point and IEEE 754 representation REVERT: 920ecb07d3 Added Marmalade makefiles REVERT: e03840dfa8 Documentation REVERT: bb66ce1e0b Multi-template types where both subtypes are of the same script class no longer crash the GC REVERT: 0e97c10d01 The context could in some situations attempt to destroy objects on the stack that had never been initialized upon cleanup REVERT: 3826e9b0e2 Documentation REVERT: 41602ce148 Fixed null pointer access error when compiling enums after discarding a module with global variables REVERT: 35d85789c1 Parent namespaces are now searched recursively for matching global functions REVERT: b6bc0fa31d Parent namespaces are now searched recursively for matching global variables REVERT: 0072e93cc5 Removed accidental use of C++11 feature REVERT: 678539b011 Parent namespaces are now searched recursively for matching types REVERT: f3d4090a92 Created MSVC 2012 project files for the samples REVERT: 76406e04c8 - Function handles for overloaded functions are now implicitly resolved by the compiler by deferring the choice of the function until it's used - Added methods GetSectionCount() and GetSectionName() to builder for enumerating the included script sections REVERT: 5a09599c66 - Script array add-on will not allow subtypes of reference types unless they are handles if asEP_DISALLOW_VALUE_ASSIGN_FOR_REF_TYPE is turned on - AssignScriptObject will no longer do anything for ref types if asEP_DISALLOW_VALUE_ASSIGN_FOR_REF_TYPE is turned on - Compiler will implicitly pass handles to ? parameters when asEP_DISALLOW_VALUE_ASSIGN_FOR_REF_TYPE is turned on REVERT: cc1adc22f3 The engine property asEP_DISALLOW_VALUE_ASSIGN_FOR_REF_TYPE disables value assignments for reference types REVERT: 127dc3f474 Fixed bug in compiler where the class constructor could become invalid if two classes with the same name was declared in different namespaces REVERT: aa840554fa RegisterObjectProperty() will now return an error if the offset is larger than a signed 16bit value which is what the VM supports REVERT: 7ef148b8da Fixed a null pointer access exception that could occur with delegates and overloaded methods. REVERT: 49777a87c0 Delegates are now fully functional REVERT: 163d5da7d6 Working on the implementation of support for delegates REVERT: 111198ae31 Working on the implementation of support for delegates REVERT: 88dc1379fa It's now possible to create and call delegates for script classes within the scripts REVERT: 46bc5ff610 Working on the implementation of support for delegates REVERT: 2145a7e4e4 Working on the implementation of support for delegates REVERT: 248af7b8e8 >Compiler was generating invalid bytecode when compiling var declaration with initialization using void opAssign REVERT: eb79d0e4dd Working on the implementation of support for delegates REVERT: bfba4cda26 Working on the implementation of support for delegates REVERT: bbb1f17d6a Fixed the config for gnuc with -std=c++11 REVERT: 92526c9384 Releasing version 2.26.2 REVERT: 82a417e16c Testing class without its own destructor, but a member that has a destructor REVERT: b05d6ed0bc Integer constants are now considered signed by default REVERT: 352e4a9665 Increased maximum limit of number of functions in the engine from 65535 to approximately 10^9 REVERT: 84fb69d44d Implemented helper function asGetTypeTraits<T> to aid in the registration of value types REVERT: 4baf19bdc1 Changed the code back to emit a warning instead of an error on unreachable code as it was often used by developers to temporarily skip parts of the code during testing REVERT: 58f7114344 Added engine property asEP_COMPILER_WARNINGS to allow applications to turn off warnings REVERT: 4e16f53084 Updated trolltech makefile to allow it to compile as_callfunc_x64_msvc_asm.asm on 64bit Windows REVERT: ae2a613513 Updated gnuc makefile to make it easier for Linux package maintainers REVERT: 6917fe4375 - Fixed memory leak with MinGW64 due to wrong configuration in as_config.h - Fixed some compiler warnings on 32bit x86 with gnuc REVERT: afcd6ce51a Fixed native calling convention on MinGW64 4.7 REVERT: 28c6a88a92 Initializing class member in declaration with a non-default constructor produced invalid bytecode REVERT: beae1ee289 Evaluating -2147483648 / -1 is now treated properly with a script exception REVERT: 66ff811314 The compiler wasn't giving error when default arg expression didn't match the parameter type REVERT: fb3077e084 - Fixed crash with native calling conventions on Linux 64bit when compiled with optimizations - Changed Android make file to make sure thumb mode is turned off REVERT: 29baba179d Fixed a problem with shared global functions being compiled in every module thus causing memory leaks. REVERT: b679d29a75 Fixed problem with native calling conventions for Android REVERT: c71c886e15 Fixed crash that could happen on releasing the engine if some garbage collected object was still kept alive by application REVERT: 6d57a66d41 - Improved error messages when GC can't destroy an object - Fixed bug in compiler related to implicit casts of reference types in global variables REVERT: ff4efc0def Releasing 2.26.1 REVERT: 65df60452c Documentation REVERT: d067b771c6 Fixed memory leak that could happen upon script exception REVERT: 565a27131a Fixed some compiler warnings on gnuc REVERT: bbd2f37050 Fixed mixup of script section ids in loaded bytecode REVERT: de71c820e0 Heredoc strings keep the last linebreak REVERT: 244c8b93ec Added test to catch memory leak on exception REVERT: 087b1620ea Compiler generated invalid bytecode when compiling expression that called function pointer returned from virtual property accessor REVERT: 10591f62cb Fixed sharing of funcdefs when loading from bytecode REVERT: 7fb8d31918 - Compiler no longer emits warnings on implementing the same interface multiple times - Compiler will report error if virtual property in shared interface doesn't match first declaration of the interface - Funcdefs are now automatically shared between modules if they are identical REVERT: 40c0e2d078 Fixed crash in compiler when compiling call to imported function with default arguments REVERT: a8d7810c48 Added support for octal (0o777) and binary (0b1111) numbers. REVERT: e84120228d Changed the constructor of the complex math add-on to support implicit conversion from primitive to the complex type REVERT: 62cb909f5e Added a couple of extra tests for validating native calling conventions REVERT: 651da8563b Primitives can now be implicitly cast to value types via a call to the constructors REVERT: cd027e8944 Added support for native calling conventions on Linux with ARM and gnuc compiler REVERT: 75d05e493d Fixed null pointer access in compiler when compiling default constructor for a class with invalid member initialization expressions REVERT: 6500bddb0a Fixed assert failure in compiler with nested access to virtual property accessors REVERT: f877d7da7f Fixed assert failure in compiler with use of copy constructor for value type with unsafe reference REVERT: 13800535dd Interfaces that inherited other interfaces didn't expose the inherited methods REVERT: 4d849e316f The compiler wasn't considering the correct namespace when parsing methods of classes and interfaces within namespaces REVERT: e63d3bf737 Add-ons now use asAtomicInc/Dec to provide thread safe reference counting REVERT: 6306190287 Templates now support multiple subtypes REVERT: a3e4622396 Working on the support for multiple template subtypes REVERT: 5e8f34aa92 Fixed bug with loading bytecode that used registered value types that could cause unexpected behaviour REVERT: afa104d75b Working on the support for multiple template subtypes REVERT: 14912a7233 Fixed more bugs with the allocation of function ids REVERT: dc50cdb79e Fixed bug with the allocation of function ids, which could cause random behaviour for very large scripts REVERT: e9ef0269d1 Improved error message when encountering non-terminated string literals REVERT: 96f706c82e Fixed the line numbers for for-loops REVERT: 41c3192ef1 Parser doesn't complain with empty declaration in classes REVERT: b14f0270bc Working on support for templates with multiple subtypes REVERT: 83ad5f31b6 Fixed bug in compiler with complex expressions in default args REVERT: 64c6af957b AssignScriptObject and CopyFrom are now correctly calling the script class' opAssign method REVERT: 68a3f0106d Improved error message from CreateScriptObject when called for type that doesn't have default constructor REVERT: 2aeac5bcc9 Fixed segfault in GetGlobalVarIndexByDecl on passing invalid declaration REVERT: 1bccaf6209 Fixed compiler error on Mac OS X in with the atomic functions REVERT: e203a72248 - Documentation - Releasing 2.26.0 REVERT: 64e702b93e Added the global functions asAtomicInc and asAtomicDec to aid the development of portable and threadsafe add-ons REVERT: ad2c2b8218 Fixed bug where a script class could be incorrectly marked as non-garbage collected when including a garbage collected member by value REVERT: 54b827a2af Script arrays could be falsely marked as non-garbage collected as the asOBJ_GC flag wasn't set when the template callback was invoked REVERT: cad1f0fe53 Improved the criteria for marking script classes as garbage collected by taking advantage of 'final' REVERT: a3ecbb4c1a Fixed uninitialized value in CompileGlobalVar REVERT: dac8b665ba Prepared asIObjectType for future implementation of templates with multiple subtypes REVERT: 1e8e0706c0 Fixed crash that could happen discarding a module and the garbage collector held a reference to an object declared in the module REVERT: c18cc41674 Fixed unnecessary 'unreachable code' on empty statement REVERT: 8f357ab291 Added GetDefaultNamespace REVERT: b46b77d923 Slight reduction on overhead of calling system functions REVERT: fe73bec0f0 Fixed null pointer access with GetLineNumber() for script function without debug info. REVERT: d28efa756f Fixed a couple of compiler warnings REVERT: 5733e8a612 - asPrepareMultithread now takes an optional argument for using a shared thread manager in multiple dlls - asGetThreadManager and asIThreadManager for retrieving an existing thread manager REVERT: f6d01bef2e Fixed parser bug with default args REVERT: d923facfb9 Fixed some compiler warnings on MSVC 64bit REVERT: 45065e4061 Documentation REVERT: 39259401a2 Reduced size of executable by avoiding code inlining in templated functions for taking function pointers REVERT: d70e048945 Constructor with const &inout is now recognized as copy constructor with asEP_ALLOW_UNSAFE_REFERENCES REVERT: 345c8d9fe4 - Loading bytecode always failed if global variables had been disabled, even if the saved bytecode didn't have any global variables - Fixed crash in compiler when compiling default constructor if class inherited from base class without default constructor - CScriptBuilder::AddSectionFromMemory() now requires a name and an optional length for the buffer in case it is not null terminated - Switch case where default case was not last now gives proper error message and not a parser error - Documentation REVERT: 822faf15dc Compiler wasn't considering the correct implicit namespace when looking up datatypes REVERT: 04096c6519 - Minor bug fix - Documentation REVERT: e6f2db3219 Fixed bug in compiler with passing temp handle to function taking inout ref to object REVERT: b774ce9007 Added AS_NO_MEMBER_INIT to provide backwards compatibility for those who encounter null pointer exceptions due to the new order of member initialization. REVERT: f7ce48895d Including mixin classes with member initialization from other script sections now works REVERT: 2c49ea7b07 Minor adjustments REVERT: 2a8244ad2c Added more tests REVERT: 0f5d2c6531 The derived class' members should be initialized before the base class' constructor even when the base class' constructor is implicitly called. REVERT: 563b013c7f Changed order of initializations so that members without explicit expression are initialized first before the rest of the constructor is executed. REVERT: 27b1419c14 Fixed memory invasion that could occur during the initialization of class members in explicitly declared constructors REVERT: 3e4a2340b8 Cleaned up the code REVERT: 712677f4f0 Adding additional test cases for initialization of class members in declaration REVERT: 060d91f04b Saving and loading bytecode with scoped types in global variables or as class members is working again REVERT: bf4ff99910 - Removed use of AS_NEW to make the initialization of members feature official - Cleaned up the code a bit - Class constructor will now have correct line numbers for debugging the initialization REVERT: 8a7acd5e8d Fixed shared types getting the implemented interfaces duplicated if compiled in multiple modules REVERT: b85b78db03 - Fixed compiler errors on Wii U - Fixed bug in script array when comparing two arrays for equality and only opCmp had been implemented in the element type REVERT: ff14d6f878 Fixed bug in XBox 360 native calling convention code when returning simple objects by value REVERT: 052691855e It is now possible to initialize all types of class members in the declaration REVERT: b84593d557 - Fixed global variables of scoped reference types - Members of all types are now properly initialized by the bytecode in the constructor REVERT: 136a940b24 Working on the class member initialization REVERT: b4d086acc8 Fixed problem with saving/loading bytecode that passed function pointers in a function parameter REVERT: d92fd842ec Fixed crash that occurred when using asCALL_GENERIC with asBEHAVE_TEMPLATE_CALLBACK REVERT: 5d2b2dad0c It is now possible to initialize handles directly in the class declaration REVERT: 0fa73f11b1 Added a new test for bytecode optimization REVERT: 02ce3e21fd It is now possible to initialize primitives directly in the class declaration REVERT: 00aa33cce3 Unified the code for compiling initialization of local and global variables. The same code will also be used to compile initialization of class members. REVERT: e10fd77ffc Prepared the tests to work with AS_MAX_PORTABILITY REVERT: ad648fc9e4 - Implemented the generic calling convention wrappers for the script handle add-on - Implemented the generic calling convention wrappers for the missing methods in the std::string add-on REVERT: d9d2fdebd2 Implemented the generic calling convention wrappers for the missing methods in the script array REVERT: 9837e48d5b Working on the class member initialization REVERT: d27c0956fd Planning REVERT: b2d2bcafbe Fixed further compiler issues with Marmalade REVERT: eaeca2b9b6 Namespaces are now properly considered when compiler searches for property accessors REVERT: 148e376241 Working on the class member initialization REVERT: e06750d82a as_config.h now identifies Marmalade without the need to manually define anything in the project settings REVERT: b6a9f3cc50 Added MSVC2012 (aka MSVC11) project files REVERT: 6fd558fd24 Parser now accepts initialization expression in class member declaration REVERT: ff011f1740 Planning for the support for initialization of class members directly in the declaration REVERT: bf75518670 Added support for registering class methods as global functions REVERT: b0dd05ca8c Fixed compiler warnings when compiling with AS_NO_THREADS REVERT: 51ffa6e375 Fixed incorrect static_cast on MSVC when attempting to register method for class with virtual inheritance REVERT: c1008c660b Fixed rare crash after an object type had been deleted prematurely by the engine REVERT: 3126ea32fd - Updated the msvc10 project files - Changed the unreachable code message to be an error instead of a warning - Removed misleading error about not all paths returning values after unreachable code error REVERT: 1b8adc203b Releasing version 2.25.2 REVERT: e68a28049b Fixed incorrect error messages for mismatching property accessors when this had been disabled by application REVERT: e2f8003365 MSVC++ 2010 project settings REVERT: 1ade7d6f97 Documentation REVERT: 41ccde3089 Fixed compiler warnings with Marmalade REVERT: 83794823b1 Documentation REVERT: 041ae2ef34 Changes to improve compatibility with Marmalade REVERT: f7c7434b77 - Fixed some compilation errors with MSVC6 - Fixed some minor memory issues detected by valgrind REVERT: 1730c80246 Garbage collector now uses a memory pool to avoid dynamic memory allocations while processing REVERT: a49e3ae9ef Factory stubs are no longer added to the GC REVERT: 491badfd66 Object types are no longer added to the GC while the module still references them REVERT: e8727832d2 Global properties and their initialization functions are only added to the GC when the owning module releases them REVERT: 265cc763b3 Cleaning up the code a little REVERT: ebc7b8e7c5 Cleaning up the code a little REVERT: 81f030fd0c Cleaning up the code a little REVERT: 0148125eb5 Cleaning up the code a little REVERT: bcaa375ab2 Cleaning up some comments REVERT: edb7dac6b6 Script functions are now only added to the GC when the owning module releases them REVERT: 68763f9697 Optimized bytecode sequence for storing a handle returned by a function into a local handle REVERT: 7faccc0a28 import functions can now be declared with default arguments without crashing REVERT: 996bc96536 ScriptBuilder now supports metadata for entities in namespaces REVERT: 018077a02b Removed some unnecessary ref copies when assigning handles REVERT: ee1cc34c3a Functions that return a handle from a local variable now avoid an unnecessary ref copy to a temp variable REVERT: 0d833c4ad4 Mixin classes can now implement interfaces REVERT: da620583d2 Added a couple of tests that validates the optimized bytecode sequence. REVERT: 3b44880a3d Reversed iteration over bytecode in OptimizeLocally REVERT: 8a22a4e5f1 Fixed some compilation problems with gnuc in the sample projects REVERT: 2fcd03bba9 Removed the temporary bytecode DiscardVar as it is no longer necessary with the localized bytecode optimizations REVERT: 7ac59a2128 Changed to localized bytecode optimizations for greatly enhanced compilation speeds in large complex functions REVERT: 6af06d828b Problem with template types when non-default factory was registered before the default factory REVERT: 0084e841ff Releasing 2.25.1 REVERT: 4b6187ac9a Added validation against duplicate registration of global functions or object methods. REVERT: 4b6ad47e9d Improved the way function overloads are matched against arguments so least conversion cost for all arguments wins REVERT: cb63b06a13 - The context's line callback is now invoked one extra time just before leaving the Execute() method to allow listeners to catch state changes - Made some improvements towards compatibility with Marmalade - Implicit conversion from interface to another implemented interface is now working - Compiler now detects circular inheritance between interfaces too REVERT: 3aac556ebf Shared classes will now validate that declared members exist in the original declaration during compilation REVERT: 7a1a51ec36 - Shared enums in pre-compiled bytecode were not treated as shared when loading - Improved error messages when loading bytecode with shared types that doesn't match existing shared types REVERT: 96cc61c21d Interface can now inherit from other interfaces REVERT: 304a73c50f Compiler wasn't showing error for virtual properties without any accessor REVERT: 69ce06b8c8 Fixed compiler bug when compiling function that takes out reference of a type where opAssign returned a value REVERT: 2e22fd58bd - Fixed bug in compiler when compiling property get expression that returned a reference to an object, but the reference wasn't used - Fixed bug with virtual methods returning objects in memory on ARM platforms REVERT: 815ca5f83d Compiler would incorrectly find global functions when compiling function calls as post ops REVERT: c147c81cc3 GetLineNumber() could crash when called for nested context calls REVERT: 13f07a64ad - Fixed comparison with wrong enum type - Added -fno-strict-aliasing to code::blocks project REVERT: 6bc568a45c Fixed crash with GNUC 64bit targets, caused by previous changes for MinGW 4.7 REVERT: 0c55095996 MinGW32 4.7 is now fully working REVERT: 46bee9cce8 The message callback is now working on MinGW 4.7 REVERT: 750ed116ba Working on support for MinGW 4.7 REVERT: 82b4678fe2 test_feature can now be compiled with mingw 4.7 REVERT: 4655ff9c5b - Attempt to reproduce reported problem - Updated code::blocks project for test_feature REVERT: 5ec126d774 Fixed problem with exception handling after loading pre-compiled bytecode when class constructor takes object in argument REVERT: f9423e413f Trying to reproduce a reported bug REVERT: 45f8acf36d Fixed native thiscall on MinGW 4.7 REVERT: e8f0d75b70 Fixed bug in script builder add-on with parsing metadata for invalid script REVERT: f7244e108b Fixed bug with NOCOUNT object when copying script class members REVERT: 4df8978e1a Fixed compiler assert on unary negate operator on void expression REVERT: 6c64194b16 Registering template specializations for templates without default constructor is now working REVERT: 841c53caa1 Fixed including mixin from a different script section REVERT: baed08b3c3 Working on adding support for interface to inherit other interfaces REVERT: 915054c550 The profiler now do all the summarization at runtime instead of using a temporary file and then summarize it on exit. REVERT: 4e06a3e41e Implemented an inline profiler REVERT: 4405a648af - minor optimization to the string pool in the string add-on - script class properties can now be declared in the same statement separated by comma REVERT: aec13a673b Releasing 2.25.0 REVERT: cbb5dc25d5 - Documentation REVERT: 1dcc900474 Compiler gave error when compiling non-const methods that returned const references to members REVERT: 7235b27677 - Fixed problem with namespaces and construct calls - Fixed comparison with infinity for float and doubles - Fixed serializing bytecode with calls to functions with variable argument REVERT: 0df32c02b3 - cmake project change to link with pthread on posix systems - Documentation REVERT: 1feecbdf8b - Added an optional argument to asIScriptModule::SaveByteCode to strip the debug info - Added an optional argument to asIScriptModule::LoadByteCode to determine if the bytecode contains debug info or not - Fixed bug in CSerializer related to extra objects REVERT: 8e42b4f058 Added wrapper for std::string::operator== since it may be implemented as a member function or global function on different platforms REVERT: 2e14dfb588 Local function pointers variables now properly take precedence over class methods of the same name REVERT: f04a384076 Fixed null pointer access when empty mixin class included mixin class REVERT: 0486d2aacb - Completed the mixin implementation - Added check for duplicate declaration of class methods REVERT: 8418fa5d3a - Mixin classes with methods can now be included in a class to automatically receive the methods - Renamed cByteInstruction to asCByteInstruction for consistency REVERT: 4348cfe6ab Mixin classes with properties can now be included in a class to automatically receive the properties REVERT: a658f86674 Improved build performance when optimizing bytecode with a large amount of conditions REVERT: d320f45d00 - Improved error message when parser encounters unexpected end of file - Added check for too many labels in a function to avoid crash when the code is too large to handle - Error messages when application does something wrong now include the return code for easier interpretation - Added performance test for compiling a complex function with many levels of if/else and switch cases REVERT: 976af0891e - asOBJ_APP_PRIMITIVE and asOBJ_APP_FLOAT types can now be passed by value in native calling conventions on 64bit Linux and Mac - Fixed a problem where a script object would be copied incorrectly to a temporary variable during a value assignment - Working on mixin classes REVERT: 99b98670a3 - fixed problem in CScriptArray::SetValue - working on mixin classes REVERT: c9f2b55d70 - Added GetTypeId() for asIScriptFunction - Parsing of mixin classes is working REVERT: 209bb1f71b - Added asOBJ_SCRIPT_FUNCTION flag to identify the object type that represents script functions - Added asIScriptFunction::IsCompatibleWithTypeId() - CScriptHandle::Cast() properly handles function pointers REVERT: 5771fda337 - Added copy constructor to CScriptArray - Added GetType to CScriptHandle - Fixed problem with parsing default argument expressions - Improved error message when parsing invalid default argument expressions REVERT: a98da50c9a - Fixed bug with temporary variables being reused too early in compiler - Fixed crash in garbage collector when automatic collection is turned on REVERT: 8604c70f91 Replaced the std::multimap with asCMap REVERT: aaa77e3289 The module's global functions are now also stored in a symbol table for faster lookups. REVERT: f28a830bae - Added asOBJ_APP_CLASS_ALIGN8 used to inform AngelScript that a type may need to be aligned on 8byte boundaries - The template callback behaviour now take a second argument to allow the application to tell the engine if an instance shouldn't be garbage collected REVERT: f648fafe02 - Fixed memory leak with virtual properties in script interfaces - Fixed compile problem with gnuc - Added support for 64bit types in Android native calling conventions REVERT: 6380a62713 - Fixed crash on callstack cleanup after script exception - Registered properties are now also stored in a symbol table - Added mapping from address of global property to the asCGlobalProperty for quicker walk through of the bytecode REVERT: 6f475e4df2 Added symbol table for quicker searches for global properties when compiling scripts and loading bytecode. REVERT: c65e1ab3ae Fixed bug with returning handle in a function declared to return the object by value. REVERT: 11eaec95e1 - Improved performance of tokenizer for large initialization lists - Improved performance of parser for scripts with a lot of statements REVERT: 0e6bb72b26 Loading large bytecodes will be much faster REVERT: 167b8e166d - GetEnumByIndex and GetTypedefByIndex wasn't returning a valid typeId when the type was not in the default namespace - Inheriting from a class or interface in a different namespace didn't work - Functions and class methods didn't accept the scope operator in the return type - The VM will catch C++ exceptions thrown by the registered functions and set a script exception. This can be turned off by AS_NO_EXCEPTIONS if exception handling is not supported REVERT: 6cfcb5e60a Releasing 2.24.1 REVERT: fd7b732e45 Documentation REVERT: d025b61900 Fixed crash in script array when the array was created with a very large initial size REVERT: 98c13e007c - documentation - fixed compiler warnings on MSVC 64bit - fixed 64bit bug with saving bytecode REVERT: f222db5243 - Fixed cmake project for MSVC 64bit - Fixed compile error with registering class methods from classes with multiple inheritance on MSVC 64bit REVERT: b9a4d3b455 - Fixed bug with registering global property from a different namespace - Documentation REVERT: 52d7332335 Documentation REVERT: 413a92c960 - Fixed memory invasion when script allocated script class that had other script classes as members - Added JitEntry instructions in the factory stub functions and generated default constructor REVERT: 75c4b78578 Fixed linker error in scriptstdstring.cpp on XCode 4.3 with CLang++ 3.1 REVERT: a9548a7c2b Fixed bug when registering objects in namespaces and the used types are registered in the same namespace REVERT: f993bb9aa3 - Fixed some warnings reported by PVS Studio - Corrected a bug with reporting the line numbers in the callstack REVERT: 59c73feba6 Reduced recursiveness in compiler to allow it to compile extremely long expressions without stack overflow REVERT: d816801024 Fixed bug in bytecode writer and reader related to 64bit platforms REVERT: 9d6b257f07 Fixed bug in bytecode reader REVERT: a33a820aa8 - Fixed a problem with saving bytecode on 64bit platforms - corrected the autowrapper for gnuc REVERT: 5c3fa327d8 - Compatibility improvements for Android - Added gnuc project for game sample - Made game sample work on Linux 64bit - Added tests to catch problem with misaligned 64bit arguments in native calling conventions REVERT: e8fff8d571 - minor fixes - added cmake projects for test_features and test_performance - upgraded test_performance to work with latest release - made game sample work for OSX REVERT: b2ce9920f3 - AS_64BIT_PTR is now defined in angelscript.h instead of in as_config.h - When the engine receives a request to abort a script in a nested call it will now forward the request to the outer call, after aborting the inner call - The script array will forward requests to abort a script to the outer call when performing comparisons, which uses nested calls REVERT: ec0e33bfb5 - Function pointers can now be invoked from arbitrary expressions without first assigning them to a variable - Bytecode optimizer exchanges some more sequences with shorter ones for better performance REVERT: d74ddf5bf8 Reduced the size of saved bytecode by not storing the namespace of class methods REVERT: e42d662c19 Aborting a nested script call could cause a crash in the VM REVERT: 20e937b89c The garbage collector could enter an infinite recursive loop if invoked while calling a class destructor REVERT: c112fa2c63 Fixed compilation error in scriptbuilder.cpp on XCode 4.3 with CLang++ 3.1 REVERT: 300730597d IsVarInScope() and GetAddressOfVar() now works properly even before Execute() REVERT: 082e1f137e - Fixed incorrect division operator for Complex add-on - Scope operator can now be used in inheritance list for classes REVERT: 363cc8209b Fixed bug in GetFunctionByDecl() REVERT: e104a9abf0 Fixed problem with loading bytecode that contained arrays of function pointers REVERT: fc795e7e76 Created an internal namespace object to manage the namespaces rather than keeping the name of the namespace in each entity. REVERT: 94fa955e10 - Removed functionality where interfaces would be automatically shared if the declaration was identical. This was already deprecated in version 2.23.0 - Improved internal handling of namespaces REVERT: e6ef334602 Stack pointer wasn't restored correctly when reusing context, which lead to memory invasions REVERT: 17c1609244 Parser can now parse templates where the subtype is in a different namespace REVERT: 9befec6611 Parser now recognizes declarations where the type is declared in a different scope REVERT: e5d334f3a7 Releasing 2.24.0 REVERT: b31d48ff3b Documentation REVERT: ddf75c3282 Documentation REVERT: ece25f2509 - Fixed a bug in compiler with ASHANDLE - PrintException helper is now able to understand the nested contexts REVERT: 82cc1c11b1 - Fixed bug in IsHandleCompatibleWithObject - Script array now uses nested contexts REVERT: 65a8aae38e - Improvements to the CScriptHandle - Added optional output argument to IsNested() that returns the nesting count REVERT: 0051c58ecc Finished the nested context feature REVERT: 66eadbe90d Working on the nested context feature REVERT: 02fe301794 Fixed bug with virtual property access with index and variable type argument REVERT: 2abb4768af Working on the nested context feature REVERT: 2ce48c066c Renaming context members to add prefix m_ REVERT: 16ad83fe8d Working on the nested context feature REVERT: 6d0ea2afc4 Fixed problem with returning float/double on XBox 360 with C++ compiler optimizations turned on REVERT: e5514db417 - Fixed bug on XBox 360 when calling registered function with more than 8 arguments - Outlined what needs to be implemented for the nested use of the context REVERT: 35316d19ba - Updated change list - Update xcode projects to remove unwanted file references REVERT: 3530f61b1e Fixed SegV when calling message callback as virtual method REVERT: 76686dff16 Added test for a bug report REVERT: 99e9f3a445 Documentation REVERT: 19e8d5550d Added test for a bug report REVERT: cf1bbc500f Updated the samples to work with the interface changes REVERT: b6ca4d8fcf - Templates with const subtypes are now properly handled - Cleaned up the code for native calling conventions on XBox 360 REVERT: 6ddcd90065 Improved compatibility with native calling conventions on iOS REVERT: 4ec87b9c33 Renamed CopyScriptObject to AssignScriptObject REVERT: a66a8e581f Code cleanup REVERT: fb2a64ff95 Added CSerializer::AddExtraObjectToStore() REVERT: 9430cde8bd Disallow register of multiple ref casts to the same type REVERT: 49919e6d72 - Added method CreateUninitializedScriptObject - Changed CSerializer add-on to use it REVERT: 7dc188db9d The string pool now supports multiple engine instances. REVERT: 4b0dde9482 The engine can now hold multiple different user data REVERT: 1c85549200 Improving stability when loading invalid bytecode, e.g. do not set the length of the arrays if there is a chance not all elements will be correctly populated, and also avoid allocating large amount of in arrays after an error occurred. REVERT: b97b1f7185 Fixed crash with releasing a suspended context REVERT: 300587df3b Loading faulty bytecode is now much safer REVERT: ecf66b537f SaveByteCode() was crashing when the script contained assignment of POD types without explicit opAssign method REVERT: 2036d39da0 - Added asAcquireSharedLock and asReleaseSharedLock - Made the string pool thread safe REVERT: 71a61e73cb Properly handling out of memory when allocating from memory pool REVERT: 56f091e871 Added verification for out-of-memory conditions to all places where memory is allocated in order to improve stability REVERT: 5b247b7dad - Changed BindImportedFunction to take a asIScriptFunction pointer - Deprecated context::Prepare(int id) - Deprecated generic::GetFunctionId() and GetFunctionUserData() - Deprecated objecttype::GetFactoryId...() and GetMethodId...() - Changed objecttype::GetBehaviourByIndex() to return asIScriptFunction* REVERT: 00c1eccb32 Changed GetAddressOfVar to return address to value even for reference parameters REVERT: c4f6c81527 - Improvements to support for native calling conventions with Android, ARM, gcc - Fixes for namespace with registered types REVERT: 272f78a3be Implemented the read/write lock object using critical sections and semaphores to get a fair non-starving implementation. REVERT: e7d46967aa The SRWLOCK synchronization object on Windows is only available from Vista onwards, so to maintain compatibility with previous versions I'll write my own ReadWriteLock object. REVERT: 4afd186c09 Deprecated some of the methods that work with the function id instead of the function object REVERT: b3a32f9a63 - Added asAcquireExclusiveLock() and asReleaseExclusiveLock() - Array is now creating the shared cache in a thread safe manner REVERT: cc8d9ef91a Implemented read/write thread synchronization to avoid bottlenecks with critical sections for retrieving user data REVERT: cfeda85e84 Fixed problem on Android with native calling convention and class methods REVERT: 45200732f2 The array add-on takes advantage of the multiple user data types to share its cache between arrays of the same type REVERT: 1d5c49ac19 The object type can now hold multiple different user data REVERT: 0c7ea2fa55 Fixed compilation error on platforms with posix critical section REVERT: eaa23c4b25 Fixed crash on Mac OS X 64bit REVERT: b4d50d018a Fixed error in compiler when compiling function call with @& parameter and asEP_ALLOW_UNSAFE_REFERENCES REVERT: 53de7c1205 Fixed problem in parsing enum declarations where unexpected tokens might get ignored REVERT: 00f06c784d Fixed error in compiler when compiling function call with @& parameter and asEP_ALLOW_UNSAFE_REFERENCES REVERT: b1b599f418 Fixed error in compiler with index operator on a value returned by a property index accessor REVERT: b1bb87406b SetValue is now a public method of CScriptArray REVERT: 0ae24bfb8f Updated project REVERT: 0f49de547c Fixed the example for how to create a script array from C++ REVERT: 313287c542 Fixed 'string subscript out of range' REVERT: 204e938da4 Fixed assert failure on compiling unary plus operator on non-primitive REVERT: 20f2185950 Fixed accessing entities in global scope from within a function in a namespace REVERT: d87c453d44 Created asPrepareMultithread() and asUnprepareMultithread() REVERT: 9dc98b674b Fixed the crash when releasing the last engine if it happens after the global critical section had already been released REVERT: 9d5dc33d73 Fixed potential crash when a context is prepared but never executed REVERT: 84adddc22e Attempting to reproduce reported bugs REVERT: a5800a26b6 Re-enabled language extensions to avoid problem when compiling together with Microsoft's Platform SDK version 6.0a. REVERT: 64dcf1b629 Releasing 2.23.1 REVERT: 6f282e045c Documentation REVERT: 806f445ef2 documentation REVERT: 0680ca0fe7 conditional use of the template keyword as the code didn't work on gnuc REVERT: b20006988b - Fixed assert failure - Fixed compile error with autowrapper on Android - Fixed configuration for Android REVERT: e2f4c98d1d Fixed gnuc x64 inline assembler REVERT: 0e81a9505a Fixed bug in VM where it might do an undetected null pointer access. REVERT: dbe6e6ef37 AS_USE_STLNAMES=1 makes the add-ons register their methods with the same names as corresponding method in STL REVERT: 040b54c020 Attempting to fix the problem with native calling conventions on 64bit Mac 10.7 REVERT: f9f6c76065 Disabled use of string pooling by default as it was not threadsafe REVERT: 264530635e Fixed crash with compiling private virtual property accessors REVERT: 8a89107b06 Updated the stdcall and thiscall implementations to copy eax:edx to a local variable that will then be returned REVERT: 36d13dae42 Updated all cdecl implementations to copy eax:edx to a local variable that will then be returned REVERT: f98569a2b8 Fixed the inline assembler on gnuc REVERT: 6c3b764020 Fixed missing LEAVECRITICALSECTION when the thread manager has already been removed. REVERT: 0d34c260a7 Trying out a theory for fixing the problems on 32bit Mac REVERT: 9d3d439335 Fixed compilation error with AS_NO_THREADS REVERT: 68bf4c8354 Fixed problem with opAssign on CScriptArray when the destination had a reserved buffer larger than the source array REVERT: 4b86fcf5d5 - Improved thread safety of the thread manager when multiple threads created separate script engines. - Added #ifndef ANGELSCRIPT_H check before #include <angelscript.h> in add-on headers REVERT: d84b13e07c Fixed crash on 32bit Mac OS 10.7 with XCode 4.3 REVERT: d4c6211be2 Documentation REVERT: 366417cae7 Fixed the StringFactory to copy the full string length and not just up to the null char. REVERT: a9bbb56070 Fixed crash when loading bytecode with shared classes in specific scenario REVERT: a9112fff50 Fixed the for(;;) statement not executing REVERT: a1c03f50f8 Changed the way the context knows if a local variable is allocated on the heap or the stack, thus made it quicker to set up the call frame when calling script functions. REVERT: bce455c2da Fixed problems with global property accessors REVERT: aa72a1672e Fixed shared global functions REVERT: cdabc43e95 Write error message to callback if a call to context's Prepare or Execute is invalid REVERT: 9cdb3c1e39 Optimized how call state stack is allocated to reduce amount of memory copied for each new call. REVERT: 777e9e8282 Compiler no longer accepts non-handles with the identity operator 'is' REVERT: e13dfb2270 Testing REVERT: 62b1beefa3 code cleanup REVERT: 28fb0dc069 Added a function for resizing internal arrays without copying each individual element REVERT: d0f2febad1 Implemented asBC_PshGPtr REVERT: 99bda6944e - Added a SUSPEND in each function, even if asEP_BUILD_WITHOUT_LINE_CUES is set - Rearranged bytecode for for loop to avoid JMP instruction - Added JLowZ and JLowNZ to replace ClrHi + JZ and ClrHi + JNZ REVERT: 3bb9817dc6 Fixed bug in asCScriptFunction REVERT: 00c637dbeb - Added isEmpty to string add-on - Fixed crash when compiling constructor and base class doesn't have default constructor REVERT: 795ac999eb Changed the performance test app to measure the CPU time instead of the system time, to get less interference from other processes running in parallel. REVERT: 9f86ea5162 - Added RefCpyV - additional bytecode optimizations - changed config to enable multithreading on Mac OS X 64bit REVERT: 6c5b3f5533 Removed unnecessary ADDREF to object pointer in some class methods REVERT: 0645332b1e Renamed POP to PopPtr REVERT: 2c96f34e51 - Changed RDSPtr to check for null-pointer - Removing CHKREF before RDSPtr REVERT: 9752258ceb Changed ADDSi to check for null pointer Optimize away CHKREF when used together with ADDSi REVERT: c0ea1a0880 Added Reserve() to the script array add-on REVERT: a23665c085 Missing CScriptArray::IsEmpty() implementation REVERT: 197f4991a7 Added a few utility methods for dictionary and array add-ons REVERT: cbf43adc7c Preparing the performance test for new optimizations REVERT: cce6ae7201 Added proper support for const overloads in property accessors REVERT: a36d373deb - fixed bug in asCReader::CalculateStackNeeded - changed the way the statistics is gathered so it can now be turned on all the time with AS_DEBUG, i.e. no need for AS_DEBUG_STATS REVERT: 263006481d Releasing 2.23.0 REVERT: be94d53c59 Fixed improper cast on 64bit platforms REVERT: 13fccd482b Fixed problem with saving bytecode when a function was calling a function pointer from a class member REVERT: 3299ca867b Updated the autowrappers to include wrappers for class constructors and destructors as well REVERT: 51a84548e7 Documentation REVERT: 18c088ba80 Documentation REVERT: dffad75bd7 Documentation REVERT: fdb0e96256 Documentation REVERT: 35e2b55258 Documentation REVERT: f1d846bdce Changed the stats to collect data over the entire execution of the application REVERT: 13893ce8e4 Documentation REVERT: ae2374d4db Documentation REVERT: d79cbc9e4d Making the samples compatible with the new interface REVERT: 116f0ef28f Fixed crash on Mac OS X 32bit with GCC 4.2 when compiling the library with optimizations REVERT: 2e9f5db864 Fixed the adjustment of GET instructions so it only comes after the CallPtr instruction has had it variable offset updated REVERT: baa978fe61 offsets in the GETOBJ, GETREF, and GETOBJREF are now stored in a platform independent way REVERT: d678e08c82 Fixed Find() for arrays of handles REVERT: cb1d600654 Added test for function pointers in saved bytecode. REVERT: 5c432681de The information on local variables is now saved with the bytecode REVERT: 2f71fe24c7 Fixed the script arrays so it can accepts opEquals and opCmp that take the other value by handle REVERT: 5caec55e61 Fixed script arrays with enums REVERT: 7032cc6b71 Fixing a bug in the compilation of factory stubs when the constructor takes enums in the arguments REVERT: 203542844d Testing a reported bug REVERT: a998d7c492 POP is now also stored in a platform independent way REVERT: cb691c0ef4 The RET instruction is now also stored in a platform independent way REVERT: 0938ed4dfb - Fixed warnings and a bug in CReader - Added checksum verification for the saved bytecode in the tests to better validate platform independence REVERT: fbc88101c9 The stackNeeded member is no longer saved with the bytecode REVERT: 9d80a472fe The stackNeeded member is now calculated after loading the bytecode REVERT: 219bd01a8a - Added opEquals to ScriptArray add-on - Added getKeys to ScriptDictionary add-on REVERT: 94172b6513 Reduced the size of the saved bytecode a bit REVERT: b4ca0a5838 Encoded 64bit values in the bytecode too REVERT: 984bcb7242 Fixed a bug in CWriter REVERT: 6e7f470afc Working on the saved bytecode adjustments REVERT: 5cf5749915 Working on the saved bytecode adjustments REVERT: 713914d255 Changed bytecode in global variable initialization functions to permit them to be stored in a platform independent way REVERT: 62fddf275f Changed the bytecode for script class factories so it can be stored in a platform independent way REVERT: a66400fe0b The GET instructions are now also adjusted to be platform independent. REVERT: 03d0c64b17 Working on the saved bytecode adjustments REVERT: d1293c3add Added test with byte-for-byte validation on the saved bytecode REVERT: a497cb190a Added configuration flag AS_DEBUG_STATS to output bytecode statistics REVERT: a5e603875e Offsets to function parameters are now stored in a platform independent way REVERT: 3488695209 The program position for line numbers and variables is now stored in a platform independent way REVERT: 8ec28b2f5b The length of the function bytecode is now stored in a platform independent way REVERT: 7c529bc8a3 Fixed bug on 64bit REVERT: 5296dadb93 Planning further code changes for platform independence in the bytecode REVERT: 8eea4d79d4 The saved byte code now has offsets to local variables that are platform independent. REVERT: 94e87089b8 The jump offsets in the bytecode is now stored in a platform independent way when saved to file REVERT: cbf69366e6 More planning REVERT: 0b8421a2d8 Fixing the generator too REVERT: 696f96f3f4 Fixing the new autowrappers on gnuc REVERT: 725ba72db5 Planning for platform independent bytecode REVERT: 0cc3342304 Implemented new autowrappers REVERT: f1b58674ac Fixed use of wrong enum constant REVERT: da0e87bfa7 Minor improvements to add-ons REVERT: 77658cfa4b Enums can now be shared across modules too REVERT: dbfa720386 Further reduced the size of the parser when the compiler is excluded from the engine REVERT: 094c9b151a Implemented a memory pool for string literals REVERT: 65bad37fdf Removed the C lib as standard add-on REVERT: a12c8af922 The condition operator didn't accept when the two results differed in type with the only difference being const REVERT: 560d156b14 Updated asbuild to support access masks and namespaces REVERT: 273cdb2b33 Added includeNamespace to asIScriptModule::GetGlobalVarDeclaration(), and asIScriptEngine::GetTypeDeclaration() REVERT: 7ce90d7396 The string factory can now be registered to return a reference to the string object REVERT: f9c80abfae - Added namespace argument to GetGlobalPropertyByIndex(), GetEnumByIndex(), GetTypedefByIndex(), and GetGlobalVar() - Updated CSerializer to support namespaces REVERT: ee73fa93e9 - Fixed last compiler warning related to use of long long - Fixed compilation error in CScriptBuilder on XBox360 due to missing getcwd() REVERT: 9aff0ac40c Fixed problem with uintptr_t not being available on MSVC6 REVERT: a8dafa679e Cleaned up the code REVERT: 945ba44f4d - Fixed problem with global variables and asOBJ_NOCOUNT - Fixed problem with objects passed by value on systems where these objects are implicitly passed by reference REVERT: 393c8919e6 Fixed compiler warnings on gnuc 64bit with asFUNCTION(0) REVERT: 3da4a0641e Substituting size_t when used to hold a pointer for asPWORD which is typedefed as uintptr_t. REVERT: e0b10b4b4a Fixed a couple of errors with script classes holding members to asOBJ_NOCOUNT objects. REVERT: ceb27c96da Added GetNamespace to asIScriptFunction and asIObjectType REVERT: 35fb3128d9 - Added asIScriptModule::RemoveFunction() that takes a pointer to asIScriptFunction - Added asIScriptModule::GetObjectTypeByName() - Added IsFinal(), IsOverride(), IsShared() to asIScriptFunction REVERT: e44782db31 Added interface to allow retrieving the access mask for the registered entities REVERT: 1dfa50d2c0 Added object type flag asOBJ_NOCOUNT REVERT: 3f3dfb1470 Fixed compiler warnings. REVERT: b6152199d8 Fixed compiler warnings. REVERT: 265c632cc1 Fixed compiler error on 64bit Linux due to missing uintptr_t. REVERT: 6802c1f32b Fixed compiler warnings. REVERT: 288faf44f6 Fixed compiler warnings REVERT: a717404908 stdcall is not used for Android on arm CPUs. REVERT: eb8cf859e6 Fixed compiler warnings REVERT: ff8394d044 Renamed as_callfunc_arm_xcode.s to as_callfunc_arm_xcode.S to fix compilation problem with scons on Mac REVERT: 01ec19f342 Renaming to .S to fix compile problem with scons on Mac REVERT: 46ed1cd3e4 Fixed some compiler warnings REVERT: a877c2f63a Fixed the add-ons to work on Marmalade too. REVERT: 0155c4b40c Added asIScriptModule::SetDefaultNamespace REVERT: 3a55be828c Added configuration for Marmalade with MSVC REVERT: 84a4a8f1d1 Changed NotifyGarbageCollectorOfNewObject to take asIObjectType instead of type id REVERT: 0f94176711 GetExceptionFunction now returns an asIScriptFunction* REVERT: 93e3933ca1 Added GetGlobalPropertyIndexByName and GetGlobalPropertyIndexByDecl REVERT: bd1c0d14c8 Removed old deprecated code REVERT: 3ac41d0cf6 Working on namespace for application interface REVERT: e96721f856 Added method asIScriptEngine::SetDefaultNamespace REVERT: dbf7012f90 Improved a compiler message REVERT: 5073a68705 - Updated gnuc makefile for iOS 5 SDK - Updated xcode project for iOS 5 SDK REVERT: 574af1b7fb Added asIScriptContext::GetSystemFunction() REVERT: 8528da9d0f The compiler will no longer share interface type ids unless the interfaces are explicitly marked as shared. Previous behaviour is still temporarily available with AS_DEPRECATED REVERT: 617ce74b7f AS_NO_COMPILER removes all code related to script compilation REVERT: 6519d42956 Fixed bug in as_callfunc_x64_gcc.cpp affecting Mac OSX x64 REVERT: 497fd833e4 As much as possible is removed from asCParser when AS_NO_COMPILER is defined REVERT: 47d22e8aab The entire asCCompiler class is now removed with AS_NO_COMPILER REVERT: eb97ac1946 Document fix REVERT: 773815de79 Releasing version 2.22.2 REVERT: 91eac79204 Fixed problem with releasing engine while a script function is kept alive by application REVERT: 05bc10df53 Planning for version 2.23.0 REVERT: 6bb9683a49 Code cleanup REVERT: 4e498223ee Documentation REVERT: 4d7c6bc687 valgrind has documented inaccuracies with operations on double REVERT: 644dba401a valgrind seems to have trouble with division of doubles REVERT: 333eb4653a Fixing a problem detected by valgrind REVERT: c405f3ef8f It's now Working on the support for namespaces. Saving and loading bytecode with namespaces is now working too REVERT: f1ee0b4888 Working on the support for namespaces REVERT: 84afaae60b Working on the support for namespaces REVERT: 7fd29b0918 - Working on the support for namespaces - Exchanged strtod() for my own implementation REVERT: 8495bf3860 Working on the support for namespaces REVERT: c340f754fe Working on the support for namespaces REVERT: 3b1dc7a853 Working on the support for namespaces REVERT: f96eb52627 Working on the support for namespaces REVERT: 9d7c514992 Working on the support for namespaces REVERT: 18006602d3 The builder is now recursively registering the entities from namespaces. REVERT: 4da8939967 Fixed bug in context in CallPtr where it wasn't prepared to call system functions REVERT: b4f9357361 Added keyword 'namespace' and parsing of the namespace block. The compiler still don't understand the namespace though. REVERT: 1a2616f970 Fixed bug in GetGlobalFunctionByDecl REVERT: caf0a7b7df More code is now removed when defining AS_NO_COMPILER REVERT: b624b1f869 CScriptBuilder now properly extract metadata for class methods and virtual properties REVERT: 6f87b83412 Made the script array more robust in out of memory situations REVERT: 4d93418c19 The pre-compiled bytecode now support shared global functions REVERT: fac7d23751 Global functions can now be marked as 'shared' too REVERT: 9f4fd8b9a6 AS_NO_COMPILER can be defined to remove some code that is only used by the script compiler. This is just a start, there is a lot more that can be removed REVERT: 73bc80e211 Fixed bug with orphaned shared classes REVERT: e81046d3f9 Updating the performance test REVERT: 429f25a606 Separated the bytecode loader and saver in two different classes REVERT: bee6220451 Confirmed the fix for the segfault with -O2 on gnuc 4.6.1 REVERT: 2b84b54d3b Added push %ebx REVERT: e9e7a81dd9 Missed a % REVERT: 84ef3c7136 Another attempt to fix the problem with gnuc on 4.6.1. REVERT: 374edf9662 Attempt to fix the problem with gnuc 4.6.1 REVERT: f4b0f3ba66 Didn't work REVERT: f303008068 Attempting to fix problem on gnuc 4.6.1 REVERT: 9e12cab997 Fixed a minor problem where script section name wasn't loaded properly if it was blank REVERT: 3de382fe46 Fixed bug in compiler with regards to doing value assignment of class members REVERT: 3f08b8507c Code cleanup REVERT: b26b92d0dd - Added new bytecode instructions for working with pointers - Fixed bug where RegisterGlobalProperty with null pointer wasn't detected REVERT: 04b2248420 Attempt to fix the problem optimised code on latest version of gnuc. REVERT: c3c66d23bc Attempt to fix the problem optimised code on latest version of gnuc. REVERT: 279db0e809 Fixed bug where classes that contained shared classes as members would fail in the second module that declared the shared class REVERT: f0caa7786f - Fixed bug with interfaces and templates - Fixed bug with shared code and templates REVERT: 2edfc75ad8 Fixed compiler warnings in MSVC9 REVERT: eccb18f91a Releasing version 2.22.1 REVERT: b62ce3214e Documentation REVERT: 4f1e868b37 Documentation REVERT: 9cfc8396ad Documentation REVERT: 6516e823f6 Documentation REVERT: 0f25c7fed0 Fixed error in documentation REVERT: 3cbf3d548e Fixed problem where the compiler wasn't detecting that a class property didn't have a default constructor REVERT: eb163d9488 Fixed the #include path REVERT: f59ac57b21 - Fixed bug in CScriptBuilder related to metadata for class properties - Renamed test_metadata.cpp to test_addon_scriptbuilder.cpp REVERT: dc7ccb9352 Fixed memory leak when implicitly converting to ASHANDLE that will be sent by reference to a function REVERT: c3322ddcf7 Cleaned up the code a bit now that ASHANDLE is never marked as handle REVERT: e85b4110a0 Cleaned up the code a bit now that ASHANDLE is never marked as handle REVERT: a4c64c8539 - The ASHANDLE type can be declared with or without @ - The game sample uses the CScriptHandle to pass data between game objects REVERT: a522c11f46 Fixed bug in compiler when passing ASHANDLE type by value to function REVERT: 8251b9690c Added implicit conversion to ASHANDLE types REVERT: 89a4ed3016 Fixed problem with script array accepting script classes without default constructor REVERT: 53cfca0564 Fixed bug with GetTypeIdByDecl REVERT: 1ac2774223 Cleaning up the code a bit REVERT: 518a53809f Added support for passing objects by value on 64bit gnuc when used with asOBJ_APP_CLASS_ALLINTS or asOBJ_APP_CLASS_ALLFLOATS REVERT: e3b85e23a4 Corrected the expected output REVERT: dcc464f90d Fixed compile error on 64bit gnuc REVERT: af528360b5 Fixed crash in native calling conventions on 64bit Mac OS X REVERT: 78307b3619 Removed the CGameObjLink object and registered CGameObj directly with the engine REVERT: 2a6b3772e4 Providing more verbose error messages when application does something wrong during the interface registration REVERT: 96755c0b7d More #includes were missing REVERT: 4cc052599f Fixed compiler error due to missing #include on gnuc REVERT: 6215da65f1 - Adding some functions to the script std string add-on - compiler won't warn when passing 0 to output parameter, this is the signal to ignore the returned value REVERT: 5fa1e05e6d Minor change to fix a potential issue. REVERT: 0ae118b3bf Avoid creating temporary copies when calling opAssign or copy constructor REVERT: a5a45c556b Improved error message when it is not possible to create temporary copy of an object REVERT: 5c1be405de - Fixed the saving of precompiled bytecode when there is no default constructor in the script classes - Turned off default constructors by default REVERT: 0b6fd5a05e Default constructor and opAssign is no longer automatically implemented for script classes that have a non-default constructor. REVERT: 81df55b031 Implemented formatFloat REVERT: 49193c79d1 Fixed bug in the asCTokenizer::IsKeyWord REVERT: 08ebd77556 Optimized the tokenizer REVERT: 147904bf37 Fixed missing #includes on gnuc REVERT: 7f412f58d2 Implemented formatInt as string util add-on REVERT: a7dc6379c3 Fixed compiler warnings in scriptfile.cpp REVERT: 66ad541c93 Improved compiler performance when scripts have a lot of literal string contants REVERT: 3533e55a38 Moved as_scriptmath3d.cpp from add_on to test_feature REVERT: ed9958193d Fixed bug in CScriptBuilder when turning off metadata REVERT: 49cfa9f17e Fixed bug in compiler where a temporary was improperly reused under certain circumstances REVERT: f78f9e6a89 Added a test case for reproducing a reported bug REVERT: c1cc6928ef Fixed the gnuc and mingw makefiles for test_feature REVERT: 183ffb277d It's now possible to disable the alternative property accessors with the engine property asEP_PROPERTY_ACCESSOR_MODE REVERT: f952fa06ed Implemented the alternative syntax for declaring virtual properties: int prop { get; set; } REVERT: d502d432ca Changed the tokens 'final' and 'override' to not be reserved keywords REVERT: 5769f91a36 Implemented the complex number add-on that will substitute the vector3 add-on as the value type reference. REVERT: ff55afa58e Fixed assert failure on compilation error REVERT: 637a9e7d70 - Added tests for the final and override keywords - Fixed a few problems with the final and override keywords REVERT: 3c8be0b0f8 Added 'final' and 'override' keywords to the script language for better control over inheritance REVERT: da3946d8b6 - Added property accessors for length on CScriptArray - Added GetMetadataStringForTypeMethod on CScriptBuilder REVERT: a14776e439 Fixed bug in compiler REVERT: 2a155b0c23 Implemented a test case that reproduces a bug in the compiler REVERT: c982ef4b6f Fixed bug with @&inout and unsafe refernences REVERT: 90fdb7248c Made the logo transparent REVERT: 798d5bc8ef Releasing 2.22.0 REVERT: 86021c0564 Documentation REVERT: 196d5cf91e Documentation REVERT: a507f759f1 Documentation REVERT: ea5a87451d Documented the asOFFSET macro REVERT: 9577bdbed1 Created the macro asOFFSET REVERT: 8ca70ff74d - Fixed problem with shared classes not being able to create other shared classes due to factory not being shared - Fixed crash with imported global property accessors REVERT: bde9be0254 Fixed a problem where an implicit value cast wouldn't be used in an assignment operator. REVERT: 63ba76a481 Improved the error message when there is no copy operator available. REVERT: f11e6070ed Fixed compile error in test_features REVERT: 8144f785e9 Improved the description of script classes in the language section of the manual. REVERT: fa87324342 - Upgraded to Doxygen 1.7.5.1 - Documented the use of function pointers instead of function ids - Minor corrections and improvements to the documentation REVERT: bb774fbf63 Fixed bug with value cast operator that returned an object by value REVERT: 66206f7ebd Reorganized the script language documentation for script classes REVERT: ec179e8ff3 Documented the shared script entities feature REVERT: 8dc917193b Documented the JIT compilation changes REVERT: 6f409fa7e4 Fixed null pointer exception in compiler REVERT: 17d77dbe7d Updated the interface documentation REVERT: 8ead90212e - Added some new methods to the public interface - Updated the samples with the new public interface REVERT: 8c85d3c9e5 - Improved the game sample to take advantage of the shared classes for messaging - Improved the game sample to provide better error handling for script errors REVERT: 1029e6c3db Made some improvements for JIT compilers REVERT: 01806d5424 Code cleanup and optimizations REVERT: 22d6e1fa00 Code cleanup and optimizations REVERT: 13fae921f0 Minor code cleanups and updated the change log REVERT: f3cc59eaad Fixed alignment and stack cleanup in the 64bit GNUC native calling convention REVERT: 4712cebd64 Fixed problem with returning 128bit pod with floats when compiling on Linux 64bit with optimizations REVERT: ba159363c5 Added a verification in the game sample to make sure the game is executed with the correct working directory, otherwise it would just run without any scripts. REVERT: 09cd08c346 Fixed bug where default args for registered functions were not recognized REVERT: 1a7d7870da Completed the implementation for shared classes REVERT: d717fd384f Fixed bug in asCModule::GetFunctionByDecl REVERT: b640a1a2ff Working on the shared objects. REVERT: c9f2c18e89 Working on the shared objects. REVERT: 827d5dc548 Fixed bug with indexed property accessor REVERT: 021b763792 - Implemented the shared class feature - Fixed bug in CSerializer add-on REVERT: 9757d5c5f6 Planning for the 'shared' feature REVERT: 9015956ee3 Added asEP_DISALLOW_GLOBAL_VARS REVERT: 64ba21dd78 Improved GC to not crash in case the engine already forcibly released the object type methods, which can happen if the application registered types that can form circular references with script objects but didn't register the GC behaviours. REVERT: c504eaa236 Updated codeblocks project REVERT: 6defe178ef Fixed bug in compiler where temporary variables were reused inappropriately while evaluating function arguments. REVERT: b56a2d8057 Fixed bug in GetFunctionByName REVERT: 9e49ba5646 A module's access to registered entity is now controlled through binary access masks that can be defined individually for each entity REVERT: 4d76864ef3 - Added GetGlobalFunctionByIndex and Decl to asIScriptEngine - Deprecated GetFunctionDescriptorById in asIScriptEngine and asIScriptModule and replaced it with asIScriptEngine::GetFunctionById - Added GetFunctionByName and Decl to asIScriptModule - Deprecated asIScriptModule::GetFunctionDescriptorByIndex and replaced it with GetFunctionByIndex - Deprecated asIScriptGeneric::GetFunctionDescriptor and replaced it with GetFunction - Added GetFactoryByIndex and Decl to asIObjectType - Added GetMethodByName and Decl to asIObjectType - Deprecated asIObjectType::GetMethodDescriptorByIndex and replaced it with GetMethodByIndex REVERT: 1b32cb0810 Planning for the new access mask functionality REVERT: f3ad6c5302 Added user data to asIScriptModule REVERT: 1552868fc5 Improved the game sample to take advantage of the object type user data REVERT: a1f0af8d57 - Added user data to asIObjectType - Added Prepare(asIScriptFunction *) to asIScriptContext REVERT: 8dc3c44684 Releasing 2.21.2 REVERT: 5c98d35580 Undoing the change to disable optimizations because it caused a segmentation fault. REVERT: 99973b4c47 Disable optimization in functions that use inline assembly to avoid problems with the GCC optimizer REVERT: 77d5e04342 Documented the new ALLINTS and ALLFLOATS flags REVERT: 39f02ac61c Structs with 3 or 4 floats should now be working as well. REVERT: 14bb626af0 Added asOBJ_APP_CLASS_ALLFLOATS REVERT: 451b13400b One test failed because it registered a function that is not supported yet REVERT: c7531e7bd1 Re-included the tests that should work with asOBJ_APP_CLASS_ALLINTS REVERT: 97c30dd3d2 Added the flag asOBJ_APP_CLASS_ALLINTS REVERT: 09393cd057 All levels of optimizations cause segfault on Linux 64bit REVERT: d546cdf3d0 O1 also caused segfault on Linux 64bit. Trying O. REVERT: 2573aa9def O2 also caused segfault on Linux 64bit. Trying O1. REVERT: 85f020c5da O3 caused a segmentation fault for some reason. Trying O2 instead. REVERT: 94ee2a5524 Added optimization -O3 to gnuc makefile to validate that this works REVERT: 29b9be0919 Fixed a test case for Linux 64bit REVERT: 1e4f05b6c0 Fixed a test case for Linux 64bit REVERT: ddd703ebb0 Fixed a test case for Linux 64bit REVERT: 530d8364a2 Fixed a test case for Linux 64bit REVERT: 8693967154 Fixed a test case for Linux 64bit REVERT: 398e33bd65 - Possible fix for problem with floats and doubles not being returned correctly on gcc 64bit with optimizations (Linux, Max). - Disabled some tests that are not supported on Linux 64bit REVERT: ad68190900 Disabled some tests that are not supported on 64bit Linux REVERT: a667c01bfe Compiler error on Linux 64bit. REVERT: 2ac5c6b01e The library is no longer allowing the return by value of simple types on Linux 64bit, as it is not possible to predict which registers will be used. REVERT: 99e7a39e1d Fixed compiler warning in script builder REVERT: 53c2cda395 The tests were incorrect on 64bit Linux, because a long is really a 64bit type. REVERT: d7270de8c8 Added test for returning a struct with an array of 3 floats. REVERT: abe94ffb3f Fixed the new code in the script builder to support metadata for class members. REVERT: a287988b9f Documentation REVERT: 16a7f95b41 Documented the sample game REVERT: ae635a6a68 Created a sample game that shows one way of integrating the script engine for game control REVERT: 18a9a3c20e Added support for metadata on class properties REVERT: 7096419777 Completed the changes to return by value on the stack rather than on the heap. REVERT: 2f82f2d764 Working on the changes to return by value on the stack rather than on the heap. REVERT: 0c0ab9a7ef Working on the changes to return by value on the stack rather than on the heap. REVERT: faaf3f8bb7 Working on the changes to return by value on the stack rather than on the heap. REVERT: b61c86e7ca WoWorking on the changes to return by value on the stack rather than on the heap. REVERT: 45bc3a577d Working on the changes to return by value on the stack rather than on the heap. REVERT: bed1563825 Code fix for CLang compiler REVERT: 3ad1def2a2 Fixed bug with function overloading and default args REVERT: 43a5efdcef Testing MinGW with full optimizations. REVERT: 4b3f7165c6 Fixed bug with property accessors and &inout parameters REVERT: 0eb364e2ea documentation REVERT: 0e1842221e - Documenting the CSerializer add-on - Releasing 2.21.1 REVERT: bf7d6a1398 Added the CSerializer add-on REVERT: 99ca19bb13 Added support for native calling conventions on Win64 with MinGW64 REVERT: 6643bf7ef2 Fixed bug with handle assignment and set_opIndex REVERT: a354f26155 - Updated the clib add-on to the latest interface - Minor tweaks REVERT: 08565d824f Testing REVERT: 107f4fdb16 - Updated code::blocks project files - Fixed bug in debugger add-on - Fixed compiler warning in as_compiler.cpp REVERT: 29e889fcdb Documentation REVERT: 1c86e279c8 Documentation REVERT: f0f0890915 Renamed the type name used to register the generic handle add-on as. REVERT: 58f9f52e58 Fixed bug with instanciating templates where the subtype has the same name as the template subtype. REVERT: 6f0031bd74 Fixed bug with ternary condition operator REVERT: b883ba7f32 Fixed the library gnuc makefile REVERT: 152af33d86 The CScriptHandle add-on is now working REVERT: b3a38dc08f The ASHANDLE type is properly working when returning a reference to it from a function REVERT: 3412611d0b - ASHANDLE now works with global variables too - Started implementation of the standard ASHANDLE add-on REVERT: d6d0d95ebe Fixed bug in compiler when passing null to output parameter REVERT: 426a0704ad - ASHANDLE types must now be declared as handles in variables, parameters, etc - ASHANDLE types no longer accept value assignments REVERT: b3dbd6b8fd - Removed unnecessary deallocation of stack memory in Unprepare() - fixed bug in std::string add-on REVERT: 435286a18e Implemented asBEHAVE_REF_CAST with signature 'void f(?&out)' for the ASHANDLE type. REVERT: ae0bd8a51b Added 64bit target to the MSVC10 project files REVERT: 5dd25c7804 Don't warn for loss of precision when passing a double constant to a float arg REVERT: db46c8fb5a Removed warning in compiler when passing null to an output handle argument REVERT: e632649088 Working on the ASHANDLE type REVERT: 9bbfda422a Added flag asOBJ_ASHANDLE REVERT: 89051e1e6d Compiler will now give error when assigning to property of an object that is returned by value from a function REVERT: 1212343617 Updating the performance test REVERT: 381ca1c1a7 Fixed crash when doing a value assignment to a handle obtained from a property accessor REVERT: ec84021c03 Releasing 2.21.0 REVERT: e137ab3505 Documentation REVERT: 12b9758eca Fixed include scriptarray.h REVERT: 5ccd8894e5 Deleting CScriptString add-on as it has been moved to test_feature REVERT: 9808264743 Removed CScriptString as standard add-on REVERT: d196587f7f Documentation REVERT: d14fa0fa0b Added FindNextLineWithCode that a debugger can use to find a line with code to set breakpoints at REVERT: 6e1b1653d9 Improved error message in compiler when typing the wrong name for the data type in a variable declaration REVERT: 4c5324257a Implementation of interface methods can now be inherited from base class REVERT: 366ea841ab Fixed crash if context is unprepared or released after SetObject but without calling Execute REVERT: a9c4274d98 It's now possible to use property accessor of another object of the same type from within a member property accessor REVERT: 65983a9b50 Improved error message when local variable overrides global function REVERT: cb41f74fa0 Fixed native calling convention on Win64 with MSVC REVERT: 9ec8f50c29 Renamed as_callfunc_x64_msvc.asm to as_callfunc_x64_msvc_asm.asm to avoid conflict with as_callfunc_x64_msvc.cpp REVERT: 7737c94147 Fixed bug with passing value type allocated on stack to var argument REVERT: 9c11deda5c Fixed implicit conversion of negative float constant to uint REVERT: 251f395df8 Fixed bug with importedFunctions array only increasing with new builds, eventually crashing. REVERT: 65a3863a15 - Cleaned up the interface by using asUINT where the value can't be negative - Working on the debugger - GetProperty now informs if the property is a reference or not REVERT: b3ed56e790 Working on the debugger REVERT: 8e7e87594b Added support for native calling conventions on Illumos OS. REVERT: ba41abde63 Added gnuc makefile for asbuild sample REVERT: 6afd43814b The debugger will now find the name of enum values REVERT: 060b5ed829 Improved the GC REVERT: 9bd8024a50 Improving the GC REVERT: 9ffbd25f84 Updated documentation REVERT: 87270dc022 Planning for future changes REVERT: 837e42257d Automatized the garbage collection so it is done during the execution of scripts, even if they are running for long times. REVERT: 5a565f941a - Fixed pthread linkage error on gnuc - Fixed cmake configuration - Fixed asrun compilation on msvc6 - Worked on debugger REVERT: 2e727e0697 - Planning for automatizing garbage collection - Added TryEnter on the critical section object REVERT: 2383fe8983 - Cleaned up the interface a bit - Working on the debugger REVERT: 494ba36de9 Added ability to determine if a variable is within scope or not REVERT: f521fdb244 Planning to automate garbage collection REVERT: d8eaacb45f Planning to add information about variable visibility for debugger. REVERT: ab89f354d8 Fixed crash on MSVC when basic behaviour methods was implemented with multiple inheritance REVERT: 014f502e9c Cleaned up the code in the script array a bit REVERT: ca76448061 - Removed the restriction to call functions in global variable initializations - Added the possibility to debug initialization of global variables REVERT: 0fed71794a - Added ability to set deferred breakpoints at function names - Prepared the debugger class to override the standard output by putting the code that prints in a single virtual method REVERT: 14763f34a1 Added printing of variable values REVERT: ce155022d2 Implemented ability to list global variables REVERT: 13f346a160 - Fixed bug in GetCallstackSize - Added ability to list local variables, print call stack, and to abort an execution in the debugger REVERT: e8cfedec29 Added ability to list break points and to remove them REVERT: 137755bde8 Fixed the test case REVERT: 8e9364ea9e Fixed a bug in debugger when setting a break point incorrectly REVERT: 53424da0f2 Changed join to be a global function which is more natural for what it does REVERT: 6e51dc6e63 - Added ability to set break points in the debugger - Fixed bug in the join function - Added msvc6 project for the asrun sample - Added function for getting command line args in the asrun sample REVERT: c2f5cadb5f - Fixed crash in compiler when script tries to call a function that has been declared incorrectly with default arguments - Added the CDebugger add-on REVERT: 4a7bb097b8 Testing and some adjustments REVERT: 7b51ee01a9 Implemented split and join for std::string add-on REVERT: 61f0b931dd Added substr, findFirst, and findLast to the std::string registration REVERT: 56401f5b8c Created the basics of the asrun sample REVERT: a2fdfd1227 - Removed deprecated code - fixed compiler warnings REVERT: a68975b119 Fixed problem with native calling conventions on PS3 and XBox 360. REVERT: 89672f31ee - Added msvc10 project files - updated change file REVERT: 8677914058 - Fixed bug with write-only property accessors. - Added critical section around the creation of the global threadmanager. REVERT: 9142ec03dd Some fixes detected while testing on MSVC6. REVERT: 16c3bd1c96 Validating property name conflicts against methods REVERT: f5c8b3a5ae Fixed compile error when attempting to assign to a member of a handle returned from another function REVERT: dca1d17725 Fixed bugs related to enums REVERT: 2e071b51ce Fixed bug with identifying shared interfaces in multiple modules REVERT: 44fdce8a79 Completed the implementation for default arguments REVERT: b38c8ea04a - Fixed default args that used variables already existent in subsequent arguments - Removed compiler warning when calling non-const method on temporary object REVERT: 717c609a1b - Fixed default args for class methods - Fixed saving/loading bytecode with default args REVERT: 0435a437fc Fixed default args for constructors/factories REVERT: 22ca1a447a Fixed bug with private method where compiler was incorrectly invoking base class constructor REVERT: 4043d2b724 The default arg string is now cleaned up for better storage. REVERT: 825e1a33ce Removed duplicate functions as they can now use default args instead REVERT: de146363ab Fixed crash when compiling invalid script REVERT: 85aefa41ff Default args no longer have access to local variables. REVERT: 1add655df9 Validating that the default values for parameters are correctly declared REVERT: 6e31cacb8d Registered functions can now also use default args. REVERT: b1cbd3993b Initial support for functions with default arguments. REVERT: 3043d549a3 Starting the support for default arguments. REVERT: 046b890001 Changing the complex mask for MinGW to include the copy constructor. REVERT: bf83c0319b Fixed bug with asEP_ALLOW_UNSAFE_REFERENCES REVERT: 8a11ff40d1 Planning for the default arg implementation REVERT: cae45ff2c4 Fix compile error on GNUC REVERT: 03d47a5e0b Fix compile error on GNUC REVERT: ceea2c1bbd Fix compile error on GNUC REVERT: b475e8f8f0 Fixed bug with native class methods on iPhone REVERT: da4027117e Properly handling loading bytecode that attempts to instanciate invalid template type. REVERT: 5030b43a81 Fixed bug in compiler when a property that takes a reference in set accessor was used in a dual operator expression REVERT: 43a4f39225 Added LoadRObjR and LoadVObjR and corresponding bytecode optimizations REVERT: ceef4b22c2 Defined the macro CLEAR_FPU_STACK to determine the use of fninit or emms to clear the stack. REVERT: 45f2f3cab3 Fixed bug in array add-on when trying to instanciate array<void> REVERT: 439c9f8d33 Added reverse and find to the script array add-on REVERT: ec40b67c69 Fixed the PS3 code when a native function returns a pointer. REVERT: e23982c888 Created a new test for get properties returning references to objects REVERT: 572c93fb29 There are now less copies done when performing an assignment operation. REVERT: ec16136555 Preparing some optimizations. REVERT: da6d0aea1a - Removed the last fninit instructions. - Removed improper assert when AS_USE_DOUBLE_AS_FLOAT is used. REVERT: db0f08d924 Updated the documentation with the new asOBJ_APP_CLASS_COPY_CONSTRUCTOR REVERT: 9d5f6d54a0 Fixed COMPLEX_MASK for 32bit Mac REVERT: 898faa5b2f Using asASSERT instead of assert REVERT: a8a1644d6d Added asOBJ_APP_CLASS_COPY_CONSTRUCTOR REVERT: cad0f7ca35 Fixed compiler warning about implicit cast from asQWORD to asUINT REVERT: 5ff73c6096 Fixed gnuc makefiles REVERT: 5be66d6411 Fixed the calling conventions on 64bit mac for older gnuc versions (4.2.1) REVERT: 9f5d38af43 Added methods sortAsc and sortDesc to script array add-on REVERT: e1aa607fb9 Fixed compiler warning on MinGW REVERT: 2edb5713d8 Releasing 2.20.2 REVERT: 5d2dac1dff Removed the compiler attribute to allow optimization REVERT: bffbe04022 Fixed the assembler syntax in the macros. REVERT: 108322c32c Added clobber list to inline assembler to avoid the optimizer mix up the registers. REVERT: b21e04088f Disabled optimization for functions that uses inline assembler in as_callfunc_x64_gcc to allow optimization of the rest of the library REVERT: 355fe627a9 Added support for declaring function pointers before the declaration of the types used in its parameters or return type. REVERT: 7dc9951545 Fixed bug when invoking function pointer directly from class member REVERT: f135349ad5 Added CMakeLists.txt REVERT: 4d66ea1a0e Fixed bug with unsafe references and value types REVERT: cbd6853aa1 Fixed bug in parser when parsing the index operator REVERT: e9594830de Adding test for compiling complex expressions REVERT: c521040dcc Fixed bugs with function pointers REVERT: f83bd0dfe8 Moved the CallSystemFunction into as_callfunc.cpp where it is shared between all platforms. REVERT: 81c32bc967 Unified code for CallSystemFunction for x86 and sh4 REVERT: ebc22711e1 Unified code for CallSystemFunction for x86 and mips REVERT: eb4a3a13bc Unified code for CallSystemFunction for x86 and arm REVERT: 42d379cd34 Unified code for CallSystemFunction for x86 and ppc_64 REVERT: 046a1c6668 Unified code for CallSystemFunction for x86 and x64_msvc REVERT: c4705dd8b7 Fixed some compiler warnings of unused variables REVERT: 64f79ccc16 Unified code for CallSystemFunction for x86 and x64_gcc REVERT: fdb76faabf Improved error handling on AddScriptSection REVERT: a6974f9243 Minor changes to unify the assembler code on x64_gcc REVERT: 1628c58273 Unified code for CallSystemFunction for x86 and xbox360 REVERT: c86e026dc5 Creating a new test for a possible bug on Xbox 360 REVERT: 459093ac6e Unified code for CallSystemFunction for x86 and ppc REVERT: 380c963697 Fixed code for native calling conventions on ppc REVERT: 76df607c71 Preparing to separate native code from common code in as_callfunc REVERT: d5d8f0e2c0 Preparing to separate native code from common code in as_callfunc REVERT: eafe192332 Fixed bug with compiling explicit ref cast of temporary handle that produced invalid bytecode REVERT: 8d80d4b54b Added swizzle operators xyz, yzx, zxy, zyx, yxz, and xzy to the vector3 add-on REVERT: 2cb443d2b7 Fixed a bug in the std::string add operator implementations where the input string was incorrectly modified REVERT: 7a79c63599 Value types allocated on the stack didn't work correctly with the condition operator REVERT: cc41332bb1 Value cast of an object retrieved from a get accessor wasn't working properly REVERT: b37862d805 - Cleaned up the code a bit - Added copy constructor to std::string add-on REVERT: e93dc2e65f When copying an object to remove constness the compiler wasn't handling objects allocated on the stack correctly REVERT: bf5745ca78 Set property accessor that takes the value by ref was incorrectly accepted for increment/decrement operators REVERT: 31e7302a40 Using emms instead of fninit to preserve FPU configuration on x86 REVERT: 3ce6ffdb5e Added configurations for Cygwin and Linux/ARM REVERT: e0dd95d812 Fixed the gnuc makefile for the console sample REVERT: 5f3de443a6 Fixed bug when calling copy constructor for value types allocated on the stack REVERT: 8f3a1f5398 Turning off the allocation of value types on the stack until the detected bugs can be fixed. REVERT: ff73004950 Corrected unsigned integer division and modulo when most significant bit is set. REVERT: 6c6dd93c38 Corrected integer division and modulo when most significant bit is set. Still need to correct unsigned division and modulo. REVERT: 1366fd9c7e Releasing 2.20.1 REVERT: 2527eef69b Missing test REVERT: a4be2c3f63 Fixed bug in 32bit ppc calling convention when passing value type by value REVERT: ba1b1e4021 Fixed makefile REVERT: 9479243d07 Testing the performance with and without allocating value types on the stack. REVERT: 5fcd7df5ac Adding automatic calls to garbage collector REVERT: f6c61f71a0 Completed the code to adjust bytecode according to the size of value types on the target platform. REVERT: 3ad44a62cd Working on the adjustment of pre-compiled bytecode when the value type has a different size than originally compiled for. REVERT: 47f4f7ed47 Saving the size of the value types with the pre-compiled bytecode so it can be validated upon restore REVERT: 260f6b0eaf Fixed a problem during clean up of configuration groups in the engine destructor REVERT: f482f3ab11 A little cleanup REVERT: f2d13dbd3d GetAddressOfVar now returns null if the value object is not initialized yet. REVERT: 7f42c31e27 Saving/loading the object variable info for the exception handler REVERT: 22ed96124c Fixed bug with inspecting the callstack REVERT: 48a24cf364 Fixed memory invasion with value types on 64bit platforms REVERT: dd82c22a92 Fixed a problem with returning values when there are value types on the stack. REVERT: 0ebd8f3e4a Fixed a test on gnuc REVERT: 04aa73b4af Completed the allocation of value types on the stack REVERT: 2c055db3aa Working on the allocation of value type on the stack REVERT: 685c444ed9 RegisterObjectType now returns the type id of the registered type REVERT: 4dc11dd8a9 Working on the allocation of value type on the stack REVERT: e0027f84da Working on the allocation of value type on the stack REVERT: 3012bd9a60 Working on the allocation of value type on the stack REVERT: a98b200ec9 Working on the allocation of value type on the stack REVERT: ac1cdc7d82 Working on the allocation of value type on the stack REVERT: b5ae6500a2 Preparing for the implementation of allocation of value types on the stack. REVERT: ba2cdf529d Added methods for reading/writing binary files to CScriptFile add-on. REVERT: 37edf4993c Improving the tests with better error messages for the buildbots REVERT: c340713305 Minor improvements to add-ons REVERT: 9401d3efd8 Releasing 2.20.0 REVERT: ff5a8a8fcf Writing the article on concurrent scripts REVERT: 123ab5f15e Fixed bug on Android with asEFuncType and asFUNC_DUMMY REVERT: 99b38d52c1 Adjusted the methods for debug information in the asIScriptContext REVERT: 9e98d21a73 Documented the indexed property accessors REVERT: c6c78dc2b3 Property accessors can now accept an index argument to emulate arrays REVERT: c2a88ed400 Added PrintException as a standard add-on REVERT: 4720dfb307 - Fixed crash if script class destructor accessed global variables that had already been deleted. - Improved tests with better error messages for the buildbot. REVERT: f15d5bfb93 Fixed the preprocessor error REVERT: 5bd66c6949 Fixed configuration for Linux and non-x86 based processors. REVERT: 27779f2053 Changes to allow cross-compiling with GNUC REVERT: 65090edc0a Added support for get_opIndex and set_opIndex. REVERT: 23bb9df433 Configuration for more BSD systems. REVERT: 0f4d90ee6f Documented the new need to register the dynamic array type. REVERT: cd42c713b7 __attribute__ ((init_priority (30000) )) doesn't work for non-class types. REVERT: f46121ed4f Fixed compile error on GNUC REVERT: cb5afde4c5 Fixed initialization order of globals REVERT: 4bce77c849 Fixed memory access after delete REVERT: 75717000b9 Fixing some uninitialized variables REVERT: 1d1ce72623 Fixed more compiler warnings in test project REVERT: 443d0ab32e Improving the tests for the build bot. REVERT: 73caf25241 Removed compiler warnings REVERT: 0b2d64c93f Removed compiler warnings on gnuc REVERT: 1ce2a7a43b Added ability to set callback functions for cleaning up user data when the objects are destroyed. REVERT: 02ac3a2b34 Corrected change list REVERT: 07d8c223da Improved compiler error message when trying to declare variable in switch case REVERT: ffcd13fd94 Fixed compiler warnings. REVERT: 776fb2b8aa Some minor changes to automatize the tests with buildbot. REVERT: 0333752587 Fixed bug in parser for functions returning handle to template types. REVERT: d4699b6323 Added #include <stdio.h> to compile on Linux REVERT: 25faeb7a2b - Added GetProperty and GetPropertyDeclaration to asIObjectType - Deprecated GetPropertyName, GetPropertyTypeId, IsPropertyPrivate, and GetPropertyOffset in asIObjectType REVERT: e0b733b04f - Added GetGlobalVar to asIScriptModule - Deprecated GetGlobalVarName and GetGlobalVarTypeId in asIScriptModule REVERT: 71470cd74e Updated change list REVERT: 8fcaab2ad5 Added the return of the script section name to the methods that previously returned on the line number and column. REVERT: 1b5451b8db Improvements to the auto wrapper to support the asCDECL_OBJLAST and asCDECL_OBJFIRST calling conventions. REVERT: d134d07dbf - Added methods to asIScriptFunction to enumerate local variables - Deprecated IsClassMethod and IsInterfaceMethod, use the new GetFuncType instead - Added a parameter to the GetMethod methods to allow retrieving the real method instead of the virtual method REVERT: c2c70c7d5b Added support for native calling conventions on PS3 with SNC compiler. REVERT: 86f490f2db Test calling script class constructor with argument. REVERT: 0231b7ef56 Improvements to gnuc makefile REVERT: 66bbb33307 - Added support for attaching user data to a registered function. - Fixed compiler warnings in aswrappedcall.h. REVERT: b25d4553da Removed the deprecated ExecuteString REVERT: 221f37de05 Improved compatibility in add-ons with different versions of MSVC REVERT: ffd55905b6 Implemented InsertAt, InsertLast, RemoveAt, and RemoveLast for the array add-on REVERT: c0739ee54b Improving GNUC makefile to allow defining the compiler from environment variable. REVERT: c9cf1244d1 Added support for templates and default array type to asbuild REVERT: f604ebb51f Improving the manual REVERT: 10522e2c59 Removed the built-in array type REVERT: c1f93d89eb Improved MinGW makefile to allow defining compiler and linker commands from environment variables. REVERT: 781066eb4b Added methods to query whether a class member is private or not. REVERT: e5cd492756 Improved const correctness of interface methods REVERT: 5c1ca553ee Added opPreInc, opPreDec, opPostInc, and opPostDec REVERT: 9c174dfd7d Deprecated asBEHAVE_INDEX REVERT: c682a853fc Releasing 2.19.2 REVERT: da4295c417 Implemented WriteConfigToFile REVERT: 601c9d0f15 Enum types prioritizes int when matching functions REVERT: 172c954bb0 Fixed assert failure on unreleased temporary variables with return statement. REVERT: 280e2889e0 Fixed implicit conversion from enum to double. REVERT: e755087ce1 Documented the asbuild sample REVERT: e14f55c49d The size of script classes and the property offsets are now determined dynamically while loading bytecode instead of reading it from the file itself. REVERT: f25edd9641 Working on the offline compiler REVERT: 7058c705bb Starting to implement a generic offline compiler REVERT: 654f7453e9 Float numbers can now be written without the leading 0 REVERT: 1c22acb56c Fixed problem with comparison of value returned by reference from property accessor. REVERT: d11fe28970 Fixed bug with loading bytecode with shared interfaces REVERT: 1edb75de7c Fixed bug in restoring bytecode where some script arrays weren't flagged as garbage collected. REVERT: c01e549193 Added configurations for native calling conventions on Haiku REVERT: ff175e1601 Fixed bug in array object that prevented GC from breaking circular references. REVERT: 76fc76f072 Improving bytecode debug output with print out of variable declarations. REVERT: c157b2453e Changed the index behaviours to opIndex methods for the string add-ons REVERT: 8249f3f3e8 Documenting how a script class can be passed to an application function. REVERT: 1bf5eec5a2 Added instruction PshV8. REVERT: 79863cb20e Added FreeBSD x64 configurations for native calling conventions REVERT: 923e6f590a Minor changes REVERT: dc1842ab6d Detecting class methods with virtual inheritance again REVERT: 58b5f70a8c Improved compiler message REVERT: 0107760e79 It's no longer possible to register global function with const modifier. REVERT: 11563dcb87 Improved validations of the bytecode when loading it to avoid crashes REVERT: 91d30dff8a Documenting private class members REVERT: 0676f1ad6a Class methods can now be declared as private REVERT: e8a186b456 Parser now supports the private keyword in preceding class methods. REVERT: 726b03407b Fixed assert failure when compiling switch statement with deferred parameters. REVERT: c6966870d2 Added conversion of bool to string to the string add-ons REVERT: 3d662a6239 - Get accessor can now return handle, while set accessor takes value or reference. - Made addref/release methods on add-ons const REVERT: 6119c420b1 Skipping test cases that are not compatible with MAX_PORTABILITY REVERT: 3d188d356e Adding support for returning 'this' pointer REVERT: 60ef9178dd Fixed crash on empty heredoc strings REVERT: 488e8528c4 Minor compatibility issue with MSVC++ 7.0 REVERT: 447d6bd304 Improved the messages when failing to initialize global variables. REVERT: 0ba2755cd4 Releasing 2.19.1 REVERT: 2093b66c1c Updated some of documentation REVERT: 5bfe3017a7 Fixed bug in compiler with switch cases and non-local variables. REVERT: f83a700ec4 Fixed error where temporary variable was overwritten by deferred parameters. REVERT: dcbf4ee17c Fixed assert failure with registered function definitions REVERT: 267f13969f Fixed bug when copying script class with handle member on 64bit processors. REVERT: 7bebe3fbcf Removed unnecessary temporary copy when using default copy behaviour. REVERT: 6c0c05531f Added compatibility with Mac OS X for X64 CPUs. REVERT: fb55f2c817 Fixed compiler error when AS_DOUBLEBYTE_CHARSET is defined. REVERT: 2400f634cc Fixed asIScriptModule::GetFunctionIdByIndex REVERT: 5197a5b10f Added support for declaring private class members REVERT: cec3ab114c Implemented the opAssign method for the dictionary add-on. REVERT: 40a3a488aa Fixed bug with conversion from reference to primitive REVERT: fd8b34bea4 Fixed changelist HTML error REVERT: 04f907b664 Storing the section names with the saved bytecode REVERT: b7288d7c69 - The saved bytecode is now independent of object property offsets - Added the instruction LoadThisR to optimize the access to class members REVERT: a9be290c42 Preparing for the deprecation of asBEHAVE_INDEX REVERT: dd56d30628 It is no longer possible to declare a class method with the name of the class REVERT: dfacfff624 Allowing 'void' parameter lists REVERT: 8829ce53bb Some fixes for GNUC REVERT: 59f3d5a78b Added support for the opIndex operator overload method REVERT: 1b57e53f17 Fixed crash on compiler error with duplicate declaration of variables. REVERT: 3d4cf4c229 Documented the restrictions for returning references from script functions. REVERT: 7f608ffdd6 Completed the support for returning references. REVERT: c2abcc8ef4 Worked on the support for returning references for script functions REVERT: 0bc8747cad Worked on the support for returning references for script functions REVERT: 41c4125526 Fixed error message in parser when forgetting the ; after the function definition. REVERT: e11643e762 Fixed bug with implicit conversion from 'const obj' to 'obj' REVERT: 54cafd813e Fixed memory leak on compile error when returning ref with deferred parameters REVERT: ca52e5f69e Working on returning references from script functions. REVERT: 6637c46246 Updating some project files REVERT: 98fda5a5cb Planning the necessary changes to support returning references REVERT: 154fbbd06c Cleaned up the code a bit in as_compiler.cpp REVERT: 48c330be49 BC_Cast was not saved properly in as_restore.cpp REVERT: 9c753d1f07 Added some validations to the set property accessors. REVERT: 1b1916e9cf Fixed precompiled bytecode with enums and arrays REVERT: 538408f9cd Fixed null pointer exception on a specific compiler error REVERT: 9eac203005 Releasing 2.19.0 REVERT: 7811496c0c - Added the new factory to the built-in array type as well. - Updated the documentation. REVERT: 41002bd54b Fixed bug in compiler REVERT: 8c5ec5c956 Fixed memory leak when a template type had a method that used the template subtype in the parameter list. When the template type then had a circular reference with a script class that instanciated the template, the memory leak occurred. REVERT: 3d6164e781 - Fixed the template callback for the add-on so that it doesn't fail for script classes if array template is instantiated before the class has been fully defined. - Added the generic calling convention for the new script array factory with default value. REVERT: 7d18ae35c9 Added factory to template array add-on to allow initializing elements with default value REVERT: 9399eaf8d9 - Fixed assert failure on compile error - Added GetFuncdefCount and GetFuncdefByIndex REVERT: 986a02b61a Fixed saving bytecode with function pointers REVERT: b4ef14867c Added RegisterFuncdef REVERT: a05d15ef2f Fixed a memory leak REVERT: 16ede0f99f Improved support for Borland C++ Builder REVERT: 3e64714d3d Fixed bug with restoring bytecode with template types that used classes declared later in script. REVERT: e2f606cc0a Fixed bug with templates in script classes REVERT: 6740c60edf Fixed a stack overflow in as_callfunc_x64_gcc.cpp REVERT: 1430ee78e9 Fixed problems on big endian CPUs. REVERT: f1df249aa9 Applying fixes to XBox 360 code. REVERT: 41d78b415e Fixed bug with implicit ref casts for global variables. REVERT: cbf5c006ee Fixed assert failure on compilation with undeclared variable REVERT: e3e2bc863e Fixed bug with property accessors in type conversions REVERT: 5e8fc27486 Fixed a bug in the saving of bytecode that caused problem with template classes. REVERT: b2b7d4e458 Added engine property to permit disabling property accessors REVERT: eed0f5fd68 The saved bytecode is now independent of the CPU byteorder REVERT: cc04aae9d3 - Strings are now only saved once in the pre-compiled bytecode - Fixed a bug with incremental garbage collection REVERT: 470a5c0b5a Further decreased the size of saved bytecode REVERT: ea5c52eb70 Fixed bug with property accessors in switch expression REVERT: 52f09244af No more recursiveness for property accessors in classes, when there is a true property of the same name. REVERT: 50f3ee68cd Further decreased the size of saved bytecode REVERT: b1273f4552 Property accessors won't go into infinite recursiveness when attempting to access true property of the same name. REVERT: cabe3431e2 Compiler no longer hangs on compiling enums with name conflicts. REVERT: f8df6c1967 Improving the size of the saved bytecode. REVERT: 297242c8dc Improving the size of the saved bytecode. REVERT: ad826849ab Fixed calling property accessor surrounded by parenthesis REVERT: 7379848c3d Bug fixes REVERT: 130230de5f Fixed size of enum types on 64 bit platforms REVERT: 0098551c05 Releasing 2.18.2 REVERT: ef25204531 Fixing a bug when growing the stack while calling a class method. REVERT: c8ddc8a266 Fixed bug with posix threads on 64bit platforms REVERT: 458acff58e Fixed bug with property accessor in do-while condition REVERT: 41f717fd8b Fixed bug on 64bit Linux native calling convention REVERT: aa2c732d1e Added Borland C++ Builder project and configuration REVERT: f939742075 Testing REVERT: 38956aaa02 Fixed bug with property accessors from within class method REVERT: a0e9968b4b Implicit conversion from handle to reference no longer gives compiler error. REVERT: 55f0f3a968 Fixed bug in virtual machine when growing the stack REVERT: d8db93498e Fixed bug in compiler REVERT: 531f2a1b72 Fixed bug when calling local function pointer with arguments. REVERT: 9706bac1f4 Fixed bug when parsing local function pointer variables. REVERT: 4c2a1bdc05 Completed the function pointer feature for 2.18.2. More improvements will be added in future versions. REVERT: fccc2a9be3 Fixed bug in compiler REVERT: 59c0da773c Added the bytecode instruction FuncPtr for pushing a function pointer on the stack. REVERT: 6871b8d4f3 It is now possible to call a function through a pointer that is member of a class. REVERT: 6600355c6c Fixed a bug with property accessors for objects in an array REVERT: c65943cc7d Fixed bug with property accessor in while condition REVERT: 83cdc25ff0 Fixed a bug in the compiler REVERT: 6ace36ddd2 Minor adjustments to the function pointer REVERT: 92e1239c55 It is now possible to call a function through a function pointer. REVERT: 84e30891de Working on the function pointers REVERT: edace57e43 Planning the implementation of the function call through function pointer. REVERT: 268ae5d979 Working on the function pointers. The address of a function can now be stored in a variable with matching signature for the funcdef. REVERT: ed9b44922b Working on the function pointers. REVERT: 9c1fb31eac Implemented parsing for the funcdef declaration. REVERT: 6a3b8df47c Updating change history to reflect release of 2.18.1 REVERT: 732b2a2c24 Releasing 2.18.1 REVERT: 562c185d07 Optimizations REVERT: 7435aa18fa Fixing the fix REVERT: 5bbf18ee7a Fixed bug where ref cast for null handle would raise a null pointer exception. REVERT: 58fbd8e599 Optimizing the generated bytecode. REVERT: 1575e5a5d2 Adding some comments on ideas for optimizations REVERT: d11ff3efc3 Fixed the assembler code for native calling conventions on 64bit Windows to work nicely with full compiler optimizations, i.e. make sure the stack is 16byte aligned and also has room for temporary stack space. REVERT: 8386bccd84 Fixed bug with saving bytecode and script constants. REVERT: bb17373635 - Fixed memory leak when global property forms a cyclic reference an initialization function that calls a function that references the property. - Optimized the byte code. Especially scripts with lots of comparisons will see a great improvement. REVERT: a79103b677 Property accessors are now available for global properties as well. REVERT: acb1d577e6 Native calling conventions on Win64 is now fully working with MSVC compiler. REVERT: 4d4e4d3e9a Characters above 127 were incorrectly converted to UTF8 when scanner was set to ASCII REVERT: 13e0a8dda4 Only one test is still failing for native calling conventions on 64bit Windows. REVERT: f89b753486 Working on the native calling conventions for 64bit Windows REVERT: eb7cd0b712 Fixed a bug with asCContext::Abort that only suspended the execution. REVERT: 999ddb15da Native calling conventions on 64bit Windows is now mostly working. Only a few tests still fail. REVERT: 592a179d13 The cdecl calling convention is now working natively on 64bit Windows. REVERT: 69bbb0bd9a Working on support for native calling conventions on 64bit Windows. REVERT: 8862d43995 Fixed some 64bit compatibility issues. REVERT: 3ffd4ef4d1 Added support for asBEHAVE_LIST_FACTORY REVERT: 8d2856dbb4 Working on the new factory behaviour used with initialization lists REVERT: cb6852625e Using the copy constructor to initialize temporary copies of objects. REVERT: 81008415bf Changed the bytecode instructions to hold the address of the global variables directly in the arguments instead of an index into an array. REVERT: cb6f0574ac Releasing 2.18.0 REVERT: c5ad6ebe43 documentation REVERT: a14cd36d88 Updated the console sample to add the ability to dynamically declare user functions and variables. REVERT: 5b4bc5d1fa A week of work REVERT: df6eb2f40d Added full garbage collection to script functions and script class types so that these objects can be properly managed. REVERT: 24b66b1c81 - Added asIScriptModule::RemoveFunction - Started work on garbage collection for script functions REVERT: 7cf7aadc0e Working on CompileFunction REVERT: 2f196dff8e Added asIScriptModule::CompileFunction for dynamically compiling individual functions. REVERT: 02eee03179 The global properties are now reference counted as well. REVERT: 514b3eaabf Cleaning up the code a bit REVERT: 8713216f14 Changed the memory management of script functions to be reference counted. REVERT: 43f0fb9728 - Moved information about imported functions to the engine - The context no longer references the module REVERT: 1c3260a179 String constants are now shared between modules. REVERT: 7aa98eeccd - Added support for native calling conventions on iPhone. - Each script function now has its own list of global variables, instead of relying on the module for that. REVERT: 1b43dd42da Global script variables now have individual initialization functions which will permit variables to be added and removed separately from the module. REVERT: 49de2c7a86 Fixed some problems with AS_MAX_PORTABILITY mode. REVERT: 640e5bc3ef Minor fixes REVERT: 988514631d Fixed UTF16 encoding on big endian processors REVERT: 712365dfef - Improvements to the code - Documentation - Updating MinGW makefile REVERT: 73ce8c8ffb Fixed improper re-use of temporary variable by property get accessor when compiling binary operators REVERT: b0e679e57b Added support for UTF-16 encoded string literals REVERT: a08935cc4a Fixed crash with too large array sizes for built-in array object and array template add-on. REVERT: 3d7d0295b0 Fixed the samples makefiles for Linux GNUC REVERT: 0dc1105033 Fixed a bug with get and set property accessors on temporary variables. REVERT: efbd5317c1 Releasing 2.17.2. REVERT: 5d847c2808 Documentation REVERT: 7298cd6368 Minor changes REVERT: 5e46275367 Fixed bug in the template factory stub generation that made the stubs not clean up the arguments from the stack after returning. REVERT: 2dd78616f2 Added asBEHAVE_TEMPLATE_CALLBACK REVERT: 19c860e40a Fixed registration of class methods & behaviours when the type was registered in a previous config group. REVERT: 2e65106b85 Fixed bug in CScriptBuilder. Updated the include sample to use the new CScriptBuilder. REVERT: 596e3de86e Changed compiler to permit instantiation of ref types without default factory if a non-default factory is explicitly called. REVERT: 9ea3e562e8 Fixed bug in compiler that freed objects too early when a post operator returned a reference to a member. REVERT: 9ed3ec73f0 Fixed assert failure after compiler error in construct call. REVERT: e15fbc8018 Releasing 2.17.1 REVERT: 27121c69f2 Added const correctness for property accessors. REVERT: a39e37fbd6 Fixed the compilation of pre and post operators after a get property access. REVERT: a9ddef7042 - Officially added the support for get and set property accessors - Documented the property get and set property accessors REVERT: da40563d00 Some more work on getters and setters REVERT: c1ac7a1d04 Improved the CContextMgr a bit while fixing a memory access violation bug in it. REVERT: a627fee8c8 Getters and setters is now working for object types too. REVERT: 96412a9a2c - Renamed as_callfunc_armasm.s to as_callfunc_armasm.S as it seems it makes a difference to the GNUC compiler for Android. - Added makefile to compile AngelScript for Android. REVERT: 60be135cc1 Property accessors now work when used with function arguments as well. REVERT: 06ae260549 Fixed crash when loading pre-compiled bytecode that had classes with enums as members REVERT: 45e7c00b94 - Added support for the Android OS. - Adapted the assembler routines for ARM processors to GNUC. REVERT: 93ce4c58fd Property accessors can now be used from within class methods without prefixing them with this. REVERT: 74c2958b2c Fixed bug with &inout and constants for invalid script REVERT: 53920d95d1 Fixed an error in the parser that prevented the declaration of templates with a handle as subtype. REVERT: 6fb8db2d75 Improved the CScriptBuilder to allow custom include directive processing via a callback. REVERT: d8435560f3 Working on the get/set accessors REVERT: 516a01fc7e Added initial support for get/set accessors for properties. REVERT: c65cec9254 Fixing a bug in the loading of pre-compiled bytecode and the built-in array type. REVERT: 3e40e188e2 - Added reference counter to global variables. - Pre-compiled bytecode is now agnostic of the order the global properties were registered. REVERT: 4d354c88ee Changing the way global properties are stored internally. REVERT: ca49a747bd Added support for template types as subtypes of a template type. REVERT: 741fdd7120 Releasing 2.17.0 REVERT: d00e1f575e - Updated the C interface with the latest changes - Added CContextMgr as add-on with support for co-routines and concurrent scripts REVERT: 03fc8bbc0f - Adjusting the JIT compiler interface - Fixed the name space declaration in as_criticalsection.h REVERT: 53e9339907 Documentation REVERT: fb4c0beb21 - Added validation for duplicate switch cases in script compiler - Added AddRef, Release, and IsReadOnly methods to asIScriptFunction. - Deprecated asIScriptObject's GetPropertyPointer, which has been replaced with GetAddressOfProperty. - Deprecated asIScriptContext's GetArgPointer, which has been replaced with GetAddressOfArg. - Renamed as_scriptstruct files to as_scriptobject REVERT: 3c967092e2 - Fixed a bug with ref types as members of script classes. - The variable parameter type now receives references to objects, instead of references to pointers to objects. - Added IsReadOnly to asIScriptFunction. REVERT: 5243d9dd55 - Added CScriptArray add-on. - Updated documentation. - Changed asBC_SET8 to asBC_PshC8. REVERT: 65a4d1472c Fixed crash in compiler on invalid script. REVERT: a9d7aa7741 - Added asEP_INCLUDE_JIT_INSTRUCTIONS to add the extra instructions needed for JIT compilation. - Documentation REVERT: c8903f803b - documentation - fixed a bug with MSVC6 and scriptmath.cpp REVERT: 629a9bb847 Added CompareRelation and CompareEquality helper functions as add-ons REVERT: 672863f67c Added the atan2 function to the math add-on REVERT: 13203be527 Deprecated the asBEHAVE_ASSIGNMENT operator REVERT: d93c8fd586 - Change asBEHAVE_REF_CAST and asBEHAVE_IMPLICIT_REF_CAST to object behaviours. - Deprecated RegisterGlobalBehaviour, GetGlobalBehaviourCount, and GetGlobalBehaviourByIndex - Deprecated CompareScriptObjects REVERT: 4f2e74b840 Fixed some minor issues with x64 platform and AS_MAX_PORTABILITY REVERT: 703aa65f96 It's no longer required to define the application type flags when registering a value type, unless the type is going to be returned by value or passed by value to a function with native calling convention. REVERT: 279718c21c Minor adjustments to the JIT compiler plug-in interface REVERT: dedb84fc63 Completed the interface for external JIT compiler plugins REVERT: 404e77d211 More code cleanup for the JIT compiler interface REVERT: c2005802fa Added asIJITCompiler interface REVERT: fd233928ff - Cleaning up the code in preparation for public JIT compiler interface - Unified parts of the native calling convention code for better maintainability REVERT: 74c7eaf7e3 Fixed an error with AS_MAX_PORTABILITY. REVERT: b55e7072b4 Fixed some compiler warning on GCC 4 with full warnings turned on REVERT: 1672a7afcb - Added GetTypeId, GetSubTypeId, AddRef, and Release to asIObjectType - The built-in array type is now completely agnostic of the internals of the library, it sees only the public interface REVERT: 16f3f79a7f - Fixed compiler error in as_callfunc_xenon.cpp and some warnings in other files. - Removed all old deprecated functionality. REVERT: b6305b78e8 Releasing 2.16.3 REVERT: 8becba047e - Completed the script class operator overloads. - Documentation. REVERT: f88fe5d4bf All dual operators can now be implemented by the script classes. REVERT: 136c24a30f Script classes can now implement opCmp to support all relational comparison operators. REVERT: c2f8ec1bd4 Fixed a bug with addition and subtraction. REVERT: 4bae144908 - Compiler can now automatically swap operands for equality tests, if the left operand doesn't have a matching opEquals method, but the right operand does. - If conditions followed by an empty statement now give a compiler error. REVERT: 355b0acf29 Implemented == and != operator overloads with opEquals class method. REVERT: e0dadc9f80 Started working on implementing operator overloads for script classes. REVERT: 37e99e2598 Fixed bug in compiler with output references and constants REVERT: 284f6eb2b5 Documentation REVERT: 3e2a87b500 Added support for native calling conventions on ARM processors with MSVC compiler. REVERT: d2721db7be Releasing 2.16.2 REVERT: 5867602607 Fixed alignment bug for script class properties. REVERT: 7c4f29789d Fixed a rare bug with MinGW/GNUC with fully optimized application that receive float args in functions. REVERT: 758daaeb04 Updating gnuc makefiles REVERT: 35e77ba002 Added support for escape sequences \u and \U. Added asEP_SCRIPT_SCANNER property. With this full support for UTF8 encoded scripts is available. REVERT: 360383970d Enabled registering the template types for applications. It's working, but not quite useful yet as the application needs to be able to obtain more information from the script engine. REVERT: 51b130d399 Template work REVERT: 5869e147bf Template work. Code cleanup. REVERT: 7d121afc2a Fixed value of asTYPEID_SCRIPTOBJECT. REVERT: e3189d973c Working on the template type. REVERT: 6b76930782 Fixed crash in compiler upon error REVERT: 63e4442578 Changed the asFUNCTIONPR and asMETHODPR macros to use static_cast instead of C-style cast. REVERT: 69f466aeaa Global variables of primitive types are now always initialized before variables of non-primitive types so that constructors can safely access primitive global variables. REVERT: ab16c7dfa3 Fixed some compatibility issues with MSVC and 64bit REVERT: 5dc0cb66ff Fixed bug with multiple levels of inheritance. REVERT: dd47d7e885 Fixed crash on compiling invalid script. REVERT: 53351b0117 Fixed application crash after DiscardModule with objects still in garbage collector. REVERT: f374f251bd Progress on the template type support REVERT: a713590eb2 Releasing 2.16.1 REVERT: 0ff32fc4a8 Bug fixes and documentation REVERT: dd315d6384 Documentation REVERT: cca6790a34 Call stack is now only cleaned up when context is reused or released. This permits examining the call stack after the Execute method returns asEXECUTION_EXCEPTION. REVERT: 37d054e186 Fixed bug when compiling script functions that return 'const string'. REVERT: 1b060a0f78 - Fixed multithread support on FreeBSD. - Improved include directive support in CScriptBuilder to support relative paths. REVERT: c70204fe6c Fixed explicit value cast to object type via implicit value cast behaviour instead of constructor. REVERT: b6fc9ff964 Fixed bug with implicit value cast to another object type. REVERT: cb9097c840 Added support for specifying enum value with scope operator. REVERT: 82000927f4 - Compiler is now giving a proper error when script function returns reference (Thanks marvi) - Fixed crash when a script constructor that receives object handle throws an exception (Thanks cvet) - Removed compiler warnings on MSVC9 REVERT: 40da0a31d7 Bug fixes REVERT: 00ea0dabd9 Improved support for scripts containing international characters. REVERT: e3b590ce88 Fixed crash when assigning array to itself. REVERT: 8782f8e521 Fixed incorrect conversion of null to primitive REVERT: f3b0ade046 Fixed problem with float values and locale. REVERT: 38387724b6 - The built-in array object is now implemented as template object - Fixed a bug with implicit conversion from 'const obj@' to 'obj&' REVERT: 0f45f51896 Releasing 2.16.0 REVERT: ec3aabd223 Adjusting the autowrapper. REVERT: dddf9f3c4c Fixed assert failure after compiler error. REVERT: 8a4fcf48e0 Fixed bug with removing dynamic config groups that had registered a script class interface. REVERT: 21a06a07b1 Fixed some compiler warnings on GNUC. REVERT: efafc699b5 Documentation. REVERT: 4311ff5e96 Various bug fixes. REVERT: b0b65a3df8 Added out param to GetParamTypeId that permits querying reference type of function parameters. Added documentation for using script classes. REVERT: a4b67d246f Fixed crash when declaring a script class that inherits from a class declared below it REVERT: 62778dfeee Implemented GetAddressOfReturnLocation. Added resize method to the string add-ons. Added option for double precision to the math add-on. REVERT: fe436465ae - Completed interface changes for enumerating registered interface. - A few bug fixes. - Added the autowrapper add-on. - Removed all length output parameters where functions returned strings. REVERT: 6a01b75253 Added more methods for enumerating the registered interface. REVERT: d387dd0893 - Added enumeration of registered global properties. - Added engine property to turn off initialization of global variables, for when the compiler is separate from the actual application. - Changed CScriptBuilder to make metadata processing optional through conditional compilation. - Changed CScriptFile to be compatible with both CScriptString and std::string. REVERT: f6408d0c44 Added complete support for enumerating the code entities in the modules. REVERT: 2fc6f4b7bf Improved enumeration of object type info. Moved RegisterStdString to the add-on folder. REVERT: 5a597e4f29 Added enumeration of factory functions, and GetBaseType to asIObjectType. REVERT: 4890c869fd Changing asIScriptStruct to asIScriptObject. Type id for primitive types are now hardcoded to avoid need to look them up. Added asEP_BUILD_WITHOUT_LINE_CUES. REVERT: a6739ff4ad Fixing samples coroutine and events on Linux REVERT: e84b037ed7 Fixed bug in compiler. Fixed concurrent sample on Linux. REVERT: b9d8eb8a2d Releasing 2.15.2 REVERT: d5d754706a Fixed conditional compilation in CScriptBuilder. REVERT: 2428396adc Documentation. Added conditional compilation support to CScriptBuilder. REVERT: 082983dc1b Removing debug flags in gnuc makefile REVERT: 95eddef2fe Fixed auto handles in as_callfunc_x64_gcc REVERT: 914342240e Fixed some errors reported by valgrind REVERT: 824fcd3cea Generic functions are now prepared with a common function, independently of the native calling convention. REVERT: d576049534 GNUC/LINUX/64Bit doesn't support passing objects by value to application functions REVERT: 8b11b71ef7 Scope operator now works for variables, to permit accessing global variables when local variable has the same name. Fixed dangling pointer with removal of config groups. REVERT: 798b06b82b Fixed some 64bit compatibility issues REVERT: bf6daffd8d Adjusting 64bit support for Linux. Improved error messages for unmatching functions with scope operator. REVERT: a76e8ac4a2 The scope operator now permits calling global functions when the class implements the same, or calling methods from base class when the method is overloaded in the derived class. REVERT: 2ed26470f4 Added support for the scope resolution operator in the parser. The compiler still ignores it though. REVERT: 175d048362 Fixing more compatibility issues with 64bit REVERT: ef76e04c4c Adding -fPIC to GCC makefile REVERT: de5c8b6d82 Completed the support for native calling conventions on Linux 64bit. REVERT: 47678d47b7 Fixed as_config.h REVERT: 950afcd338 Fixed some 64bit compatibility issues REVERT: 930fbb737a Fixed as_atomic.cpp for older versions of GCC REVERT: c6ebd325fc Added support for native calling conventions on 64bit Linux. Still missing support for virtual methods. REVERT: 469ad5d949 Calling super class' constructor is now working REVERT: a929a99135 Added support for writing to CScriptString. REVERT: ec4629b3ea Fixed bug with pre-compiled bytecode REVERT: db0891f45a Fixed implicit conversion to base class when passing object to function expecting base class by reference. REVERT: 1285819da8 Fixed explicit cast for script classes. REVERT: ba57a1acf9 Fixed bug with script class factories with multiple arguments. REVERT: 98d3085efc Added support for inheritance to script classes. REVERT: e7cb44c6f9 Releasing 2.15.1 REVERT: 442dfd319e Several fixes for MinGW/Win32 REVERT: d246002801 Adjusting the library for Mac OS X REVERT: 840696e7be Changed as_atomic.cpp for Linux to use GNUC built-in functions instead of functions meant to be exclusive to the kernel. REVERT: 36b1607061 Minor adjustments REVERT: 1077463800 Documentation and more tests REVERT: 3cd02d5cd4 Memory for global variables is now allocated individually. REVERT: 64fba0fab2 Created factory stubs for creating the array object so that the compiler doesn't have to supply the object type as a hidden parameter. REVERT: e15e46f0c7 Fixed bug with temporary variables and index operator REVERT: 78b85d67cd Added factory functions for script classes. Compiler now call the factory functions to allocate reference objects instead of using the ALLOC bytecode. REVERT: f88c7a85a0 Fixed bug with bitwise operators and integers smaller than 32 bits REVERT: f70be5f358 Fixed a bug in CScriptBuilder add-on REVERT: 302fadbfdd Releasing 2.15.0 REVERT: 47b627b128 Documentation and improvements to the add-ons. REVERT: 04f3a7c903 Added the asIScriptModule interface. Moved all module methods from the engine interface to the module interface. REVERT: 95a3c9470b Planning ahead REVERT: 30c7f733d2 Fixed AS_MAX_PORTABILITY REVERT: 49147e1a27 Improving the CScriptBuilder REVERT: d42ddd4dff Removing old deprecated functions. Cleaning up the code. REVERT: 91553b441e FFixed assert failure. Added ScriptBuilder add-on REVERT: 8e67597758 Fixed a multithread issue with simultaneous access to the script node memory pool. REVERT: e85788ef5d Adding ParseToken REVERT: 3929f7507a Changing asIScriptContext::GetReturnPointer to GetAddressOfReturnValue. Changing asIScriptGeneric::GetArgPointer to GetAddressOfArg. REVERT: fe43acf82e Fixed bug with index operator and temporary objects. Fixed bug with tokenizer and !is token. Changed GetVarPointer to GetAddressOfVar. REVERT: e89d870add Fixed bug with saving bytecode with enums REVERT: 778da764e0 Various minor updates REVERT: d97782e8e6 Adding more control to the application about how garbage collection is done. REVERT: 0ab6a36bc4 Fixed a bug with assignment using temporary object REVERT: a32ac0ebd8 Releasing 2.14.1 REVERT: 6707564ad1 Fixed infinite loop while parsing script that declare const return value for class methods. Fixed some assert failures that happened after compilation errors. REVERT: 7c81103323 Added multithread support for MacOS X REVERT: 315b56eee6 Fixed a crash upon application exit when the script engine is released by global variable upon destruction. REVERT: 02f3c1a1e7 Implemented atomic reference counters for thread safety REVERT: 35192dfe4a Fixed the namespace for the scriptnode modules REVERT: 993ab9da55 Improving thread safety of the gc. REVERT: 05f33b2457 Preparing the GC for improvements REVERT: 9a1ecd081d Added library logo REVERT: 237f5baea8 Implemented the 'is' operator REVERT: 036e89844e New add-on: vector3 REVERT: 9005f1538e Added support for const script class methods REVERT: e7faa3a990 Updated change list REVERT: 8eb42dbc09 Added support for optional implicit handle types. Improved object handles in the script handle. REVERT: f5880b99bd Updated change list to reflect 2.14.0 release REVERT: 074d17e540 Releasing 2.14.0 REVERT: 359e5d65c4 Improvements to enumeration of global variables, type casts, etc. REVERT: 003fb72f82 Completed the feature 'shared interface type id for identical interfaces' REVERT: 2f1ae70257 Identical interfaces in two modules share the same type id. REVERT: 62f53d146f C interface and bug fixes REVERT: 05253811b7 Fixed some bugs with SCOPED reference types. REVERT: 01667cd462 Updating the documentation REVERT: f09563820a Writing documentation REVERT: f040eb85b4 Releasing 2.13.1 REVERT: a193eba93c Fixed bug with compiler optimization of constant expressions REVERT: 31ca05af5b Moving the documentation to doxygen format REVERT: c888353d68 Moving the documentation to doxygen format REVERT: 37682a4a6e Moving the documentation to doxygen format REVERT: 29f1e237e3 Added support for implicit ref casts REVERT: 29e358cf7e Implementing the implicit ref cast behaviour REVERT: d3ff1ba2fb Preparing the code for asBEHAVE_IMPLICIT_REF_CAST REVERT: 338992d884 Fixed bug with enums not ended with comma REVERT: 98eab84beb Implementing the asIModule interface REVERT: cb6dea7d41 Fixed bug with temporary variables not being released REVERT: 7c4417646a Reproduzing a bug REVERT: 74507685a3 Working on the asIScriptModule interface REVERT: 16c8874787 Extra validations in Register functions REVERT: fb72bf9235 Fixed bug with temporary variables REVERT: 9a1eeff48d Added some test cases to confirm reported bugs REVERT: 70257fc47b Added support for posix threads. Support for multithreading is now turned on by default. REVERT: 456f280338 Fixed CRITICAL_SECTION on 64bit Windows REVERT: afb5d67c59 Added asEP_ALLOW_MULTILINE_STRINGS REVERT: d397c9f483 Started on a separate C header file for the C interface REVERT: d6cd8f23d3 Implemented support for single quoted strings. REVERT: 64b609ba96 Fixed bug with ref casts and script class members. Added asCScriptFile add-on REVERT: 5fcf2543e1 Fixed the samples before the release of 2.13.0 REVERT: e387964d29 Releasing 2.13.0 REVERT: c083b480e2 Fixed a bug where global arrays of handles where initialized with handles of other global variables REVERT: 56c7262984 Fixed a bug in compiler that made the script engine fail to clear some object variables when entering functions. REVERT: a85c72ba40 Fixed cast with missing argument bug REVERT: b5e31b1fab Updated change history REVERT: 766a1c6ad0 Added initial support for native calling conventions on 64bit platforms REVERT: a80d0b6f49 Added support asBEHAVE_REF_CAST REVERT: 9f1920daf0 More interface improvements REVERT: f9ea6a2951 Moved some methods from asIScriptEngine to asIScriptFunction REVERT: 560980dc3b Added methods for enumerating parameter types to asIScriptFunction REVERT: f9be410dab Moved enumeration of methods to the asIObjectType interface REVERT: 6116f2ee37 Fixed enum values on big endian CPUs REVERT: bfe49d25f7 Fixed a bug with bool[] on PPC REVERT: c09a594096 Fixed compile error on GNUC/Linux REVERT: ba74f30e6c Added asEP_MAX_STACK_SIZE. Deprecated SetDefaultContextStackSize. REVERT: 6f5d6b5295 Fixed bug with shift operators and byte operands that should have been converted to dword REVERT: 0e45e16595 Global variables declared with enum types crashed the engine while compiling the script. REVERT: c6d1cda717 Moved enum declarations to top of header to fix compiler error on GNUC. REVERT: 7f7d9dd281 Completed the interface documentation. REVERT: 9994aee554 Added more function return values to the documentation. REVERT: f9681a3015 Fixed the C interface REVERT: d05ede7fa1 Fixed error on non-standards compliant compilers, e.g. MSVC6 REVERT: f7835a9a12 Updated change list according to version 2.12.0 REVERT: 2ace22bafa Releasing 2.12.0 REVERT: 80f3695925 Completed the API reference manual REVERT: ea8b28798c Setting eol-style REVERT: 9630d496f0 Corrected the behaviour values REVERT: 0bc52a6510 Completed some more functions for the doxygen reference manual. REVERT: a89f0e349e Completed some more functions for the doxygen reference manual. REVERT: 43e0eb74c7 Completed some more functions for the doxygen reference manual. REVERT: 8f1033d9d9 Added asIScriptFunction interface REVERT: e9aa58a764 Improved detection of calling conventions, and began Doxygen documentation. REVERT: 9eef474709 Added asIObjectType interface REVERT: ff06a986ac Improved the asIScriptGeneric interface REVERT: 89045a6e71 Made some tests with bstr REVERT: 9f94588b43 Added RegisterTypedef and documented the typedef feature REVERT: 6bbbe233fd Completed the enum feature and documented it. REVERT: f1fb097388 More tests and improvements of the enum feature REVERT: 0e7916b993 Implemented explicit casts with enum. REVERT: 7e4c597560 Implemented implicit conversions with enums. REVERT: 3bdc8ebb65 Improved enums and performed more tests. REVERT: c2b505b830 Added support for enum REVERT: ff1e26a42b Added support for typedefs REVERT: 50a76f7817 Fixed potential heap corruption REVERT: 4c67836a4d Fixed assert failure. Exchanged all asserts for asASSERT REVERT: db1c37b792 Fixed assert failure REVERT: 9ccb9c76b4 Releasing 2.11.2 REVERT: b170d2bdff Added asBEHAVE_VALUE_CAST REVERT: 71dc413555 Bug fix and improved error message. REVERT: a5ed37b077 Setting eol-style REVERT: 14291c9716 Fixed bug where compiler didn't warn about uninitialized variable when using post increment operator. Fixed bug with switch case and uint8 expressions. REVERT: 429160f80d Fixed compiler errors with CallFree and ArrayObjectFactory2 REVERT: f6ccb00847 Minor changes REVERT: 9d42dbc87b ExecuteString no longer locks dynamic configuration groups REVERT: 4de1bca1da Releasing 2.11.1 REVERT: 05e6c56948 Added scoped reference type. Fixed a couple of bugs. REVERT: dddf7e0b49 Releasing 2.11.0 REVERT: ed6b4bbcee Fixed compiler error in test project REVERT: 423daafe44 Fixed uint64 to double conversion REVERT: 6dfcbe9005 Preparing for 2.11.0 release. REVERT: 128d508386 Fixed some null pointer accesses. REVERT: 61580bd9ea bug fixes REVERT: 89a93b27b0 Fixed bug with gc releasing type definitions too early. REVERT: d9667fd02e Multithreading support in as_thread.cpp was broken REVERT: c5fc6fd1b9 Fixed a bug with bytecode optimization. REVERT: a82d77f154 Added support for native calling conventions on XBox 360. Added asOBJ_NOHANDLE flag to support singleton properties. REVERT: 4a8038153f Setting eol-style REVERT: d81dadccdd Implemented the factory behaviour. Added the pod type flag. Removed the alloc and free behaviours. REVERT: 3d75deb56c Improving RegisterObjectType and validation of behaviours and registered functions. REVERT: 122046e714 Released 2.10.0 REVERT: be9311afdd Bitwise operations now keep the signed/unsigned type of the left operand rather than converting the result to uint. Several bug fixes are also being checked in. REVERT: 3bb05582af Fixing compiler warnings REVERT: 1f412e2415 Adding CompareScriptObjects. Improving ScriptAny. Updating projects. REVERT: b8979767c2 Finishing the gc feature REVERT: e8226aea62 A couple of bug fixes REVERT: 1274e7be48 Fixed compiler warning in script dictionary REVERT: ba609796fb Fixed CallObjectMethod so it can call virtual methods REVERT: c2bb8caa20 Improving GC REVERT: 997fca65d3 bug fixes REVERT: 3310a6cb4d Improving GC REVERT: 5fa1580f4d Releasing 2.9.1 REVERT: 16cbc76667 Fixed a bug in ReleaseScriptObject REVERT: 3b82b37e09 Implementing destructors for script classes REVERT: ee0fb4757d Releasing 2.9.0 REVERT: 1c21852e2a Fixed bug on PPC REVERT: 2af1a6a4f1 Setting eol-style for new files REVERT: d47d4baaa3 Fixed a bug with &inout references and script class members REVERT: 35921168c3 Finished the dictionary REVERT: 941c1654dd Moving on with the ScriptDictionary implementation REVERT: 0b0211dfa7 New methods to support development of generic containers REVERT: 91dbd9457a Added engine property asEP_COPY_SCRIPT_SECTIONS REVERT: 72bb86c81d The beginning of a generic dictionary type REVERT: 21b8bd9298 Adding support for var type REVERT: a14b55db06 Making GetTypeIdByDecl understand const REVERT: ee4728e29d Added scriptmath as add-on REVERT: 569b84a2c2 Added GetThisPointer and GetThisTypeId REVERT: a55636618c Fixing GetGlobalVarPointer, Adding SetArgByte, SetArgWord, etc REVERT: 4e22c6a4da Updating project files REVERT: 10401f9df4 Removed redundant xcode project files REVERT: 8c85417bf5 Released 2.8.1 REVERT: b713faaec0 Compiler didn't always add references for used object types REVERT: 0c53f51c8c Updated concurrent sample for Mac REVERT: 3b59c50d69 Fixed explicit handles on array elements and class members REVERT: b697b06bf5 Updating MSVC7 projects REVERT: 6417867b82 Array now calls default constructor for script classes REVERT: 42a5a524b9 Fixed bug with save/load feature REVERT: 8511f86a37 Made a mistake when merging main.cpp in test_features REVERT: a17f0719b4 Fixed boolean operations with trash in upper bytes of word REVERT: 7f1d8d33c2 Fixed an assert failure after compiler error REVERT: b26d90319d Corrected ChkNullS REVERT: 5edbe4b6a2 Fixed problem with the use of CHKREF and parameters REVERT: b45bae039e Fixed bug with loading bytecode with script classes declared out of order REVERT: 9f9d99aa88 Removed obselete scr_handles.html REVERT: ee4faa4bc5 Updates to documentation and PS3 fixes for int64 problems REVERT: d891d1950f Fixed int64 parameters on 32bit PPC REVERT: a1d3f259d3 Fixed bug with loading bytecodes and registered array types REVERT: c2474c3ab4 Removed restriction of object type size REVERT: 7ca60c596f Fixed bug with initialization lists and arrays of handles REVERT: e332bef1e1 More tests for PPC. Corrections to make PS3 work again REVERT: 1f2d6ed45f Fixed bug in GetGlobalVarID methods REVERT: 7b89eb061a Minor bug fixes REVERT: a1a2fe5760 Moved PS3 code to as_callfunc_ppc_64.ppc REVERT: c85acc120b Mac OS PPC native calling conventions are now fully working REVERT: 9bbf231519 Mac OS X / PPC Native calling conventions is almost fully working now REVERT: 91f2d32378 Getting closer o supporting PPC on Mac OS X REVERT: 9f2a2b162a Changes in preparation for PPC / Mac OS X support REVERT: 7e14fcce22 More testing on PPC REVERT: 8563604165 Minor changes REVERT: 1b194b4ab1 Fixed incorrect name conflict between script class methods and global functions REVERT: 95eaf5b8f1 Fixed intermittent bug in byte code optimization on big endian processors REVERT: dac89e7bab Minor changes for PPC REVERT: 32479466af Fixed bug where properties were not validated correctly for name conflicts REVERT: 914f055448 Fixed assertion failure in compiler REVERT: e596164b9d Tiny fixes in test_feature project REVERT: 1f871e79c8 Applying corrections for PS3 code base which got broken with the last bug fixes REVERT: 997657b3c1 Fixed incorrect registration of functions with void parameters REVERT: ebbecb4f62 Fixed problem with const references and parameters REVERT: 4c92af8bc1 Fixed bug with booleans returned from functions that weren't always evaluated as false when expected REVERT: 65e57b347b Added support for native calling conventions on Mac OS X with Intel CPU REVERT: db0043e89d Tiny bug fix REVERT: 9f17576a5c Adding XCode project for test_feature REVERT: d84d132fbb Preparation for Mac OS X port REVERT: 4394a030d9 Minor changes REVERT: af0c90a446 Added support for PS3 native calling conventions REVERT: b5ec4a5c61 Fixed problem where C++ optimizations could change the result of operations REVERT: 850e84cf9f Fixed some compiler warnings REVERT: 098b740650 Reproduced problem with void parameter REVERT: 0da2eda8c9 More tests on bool types REVERT: 7c018a7e82 Minor bug fix. Added tests that reproduce some reported problems REVERT: a208282d80 Parser is now aware of declared types REVERT: 1e28880fe5 Minor changes. REVERT: 2de7071122 Improved memory usage from as_array REVERT: c78c30d6d2 Memory improvements REVERT: 59fe93d530 More fixes for AS_USE_NAMESPACE REVERT: c21bd1dc8e Fixed behaviour of the AS_USE_NAMESPACE macro REVERT: 7a18af83d1 Improvement on memory usage in compilation REVERT: 832e0b97aa Released 2.8.0a REVERT: 4ba3dad2f5 Minor fixes for Linux in test_feature REVERT: 99bb45b957 Fixed Linux compatibility issues with test_features. Converted samples to Linux. REVERT: 6a982f3aea Fixed the DELETEARRAY macro, removed the DELETEOBJARRAY macro REVERT: daf3edb8e1 Grammar fix in the change list. REVERT: 0e1ec407f9 Added AS_NO_USER_ALLOC macro to aid users having problem with the user defined memory functions. REVERT: 4c8168f0f7 Minor bug fix and better memory tracking REVERT: 4327f7ab9c Updated project files REVERT: 4a148de944 Fixed problem with exception handler in script class methods. Released version 2.8.0. REVERT: dc3e2d0e68 Blocked implicit conversion with multiple chained object constructions REVERT: e9288aa861 Added support for implicit conversions using class constructors REVERT: d8fcbc8626 More tuning of memory management REVERT: deacadf12d Tiny changes and added statistics for memory allocations. REVERT: b8f085afc2 Improved asCString with regards to memory management REVERT: ce482cccc4 Added SetUserData and GetUserData to context REVERT: 915f5d602b Added support for user defined memory manager. REVERT: eae8c90b05 Updated copyright message. REVERT: 6dd6d1c048 Fixed the bug with incorrect use of temporary variables during evaluation of function arguments. REVERT: d0ad656fdd Added tests to reproduce problem of incorrect use of temporary variables during expression evaluations. Fix will be available soon. REVERT: 672074f71c Fixed numerous compiler warnings REVERT: 0c732e089c Added GetArgPointer and GetReturnPointer. REVERT: e939515b66 Testing string parameters REVERT: 73b2bc1955 Use of the this keyword is not optional when accessing class members from within a class method. REVERT: c876c79d73 Merged 2.7.1b with trunk REVERT: 14bbd94200 Cleaned up the code a bit REVERT: 913934babf Fixed save/load bug REVERT: ae41ccd7af Fixed MSVC6 compile errors. Reproduced Save/Load bytecode bug. REVERT: 1914cd7d1c int64 and uint64 are now fully supported in expressions. REVERT: bfba0495c5 Added the int64 and uint64 types. Still need to make expressions use the full 64 bits though. REVERT: 5e156a5237 Deprecated the bits type, which is substituted by uint. REVERT: 6865276cbd Added Set/GetEngineProperty(). ALLOW_UNSAFE_REFERENCES is now an engine property. Removed deprecated functions. REVERT: 4e68f4b418 Fixed a bug with constant bits8 and bits16. REVERT: 59d6c4a9ff Testing bits8 REVERT: 9320982348 Releasing version 2.7.1 REVERT: e320938e80 More tests. REVERT: 16878b1014 Fixed a bug where temporary variables where incorrectly reused. REVERT: 1a43d7b331 Completed the cast operator. Several bug fixes. REVERT: 29c51e7026 VM now throws null exception if value is used after invalid dynamic cast REVERT: f8f585c283 Progress on the cast operator REVERT: 2d650dd575 Added the actual sample files REVERT: 5915f8f960 Added a sample showing how to implement #include REVERT: ee21970855 Fixed the last known problems with PPC in max portability mode. REVERT: 5a22a07d31 Added proper treatment of literal constants of 8bit and 16bit sizes REVERT: f80cd06659 Further changes towards PPC compatibility. Now only literal constants remain incompatible. REVERT: 1545fc2477 Fixed compiler differences for 64bit int literals REVERT: 7f854f8f66 Implemented iTOb and iTOw with compiler changes for support for big-endian CPUs REVERT: cfbc0db68e More fixes for PPC. Everything should be working now, except for when datatypes of different sizes are mixed in expressions. REVERT: 308a7465aa More fixes for PPC. REVERT: 4a994345a3 Setting eol-style for all source files REVERT: 615c6029f0 Planning ahead for improved handling of 8bit and 16bit variables in the virtual machine. This is necessary to support CPUs that use big endian memory layout. REVERT: a2a6fda1fc Minor changes REVERT: 3ead0d05d9 Restructured as_config.h to make it easier to maintain. REVERT: d487bd925b Fixed some portability issues for PPC. More is needed though. REVERT: 60dc3f6098 Setting eol-style to native REVERT: 705b678960 Unification of line endings REVERT: eef3a8c0e8 The new cast operator now works for primitive types. REVERT: afe315fc75 Fixed a bug with RegisterGlobalProperty. REVERT: dd3f77d127 Adding parsing of the cast<type>(expr) operator REVERT: 9c4f84eeb7 Minor bug fixes REVERT: be51b57547 Released 2.7.0 REVERT: 6af9533ede Added check to verify that script class members have a size greater than zero. REVERT: 7921131491 Added as_callfunc_ppc.cpp prepared by Pecan Heber and adjusted by Kunitoki REVERT: aaeb979c81 Fixed a compile error in as_bytecode.cpp REVERT: 1c6c0ee476 Added test_feature makefile for gnuc. REVERT: a1392b6c1b Fixed some bugs on 64bit processors REVERT: 04ce3e4758 Updated the tutorial sample to support the generic interface when native calling conventions are not supported. REVERT: 27adb83aac Several bug fixes REVERT: 6c4f24ffbf Updated test cases for better coverage with AS_MAX_PORTABILITY. REVERT: c29793ab37 - Added asGetLibraryOptions(). - Fixed a couple of bugs with AS_MAX_PORTABILITY. REVERT: c0748d0b0b Fixed a couple of compiler warnings. REVERT: 3dc591ffa7 Disabled assembler code on PPC by default, since the code in as_callfunc_ppc.cpp is incorrect. REVERT: f6eca932d6 - Fixed bug with Discard(). - Updated manual and samples with the new message callback. REVERT: 092289c40c Replaced SetCommonMessageStream with SetMessageCallback REVERT: c76118ea03 Fixed assert hit for some compiler errors REVERT: 7f70b2e33a Improved performance of Prepare() when calling the same script function repeatedly REVERT: edb957e08d Improved the speed for calling interface methods. REVERT: 4509657a16 Added xcode project files. Updated manual for GetVarPointer(). REVERT: 5a4ca9670c Added asIScriptContext::GetVarTypeId() REVERT: ba98e590a0 Deprecated the 'struct' keyword in the script language REVERT: 2d39a375be Unified the way script and system functions are stored by the engine REVERT: 843227c9ff Added support for the '\t' char escape sequence in string literals. REVERT: 0abafe4c47 - Added GetFunctionModule(), deprecated GetModuleIndex() and GetModuleNameFromIndex() - Saving and loading of bytecode now works with interfaces and class methods as well REVERT: 8240a131c8 - Improvements to MIPS code. - Preliminary native support for PPC processors. REVERT: a0ac57e827 Added methods RegisterInterface and RegisterInterfaceMethod to the engine REVERT: 9f3fe47940 Modified engine's GetMethod methods to take a typeId instead of module and type names. REVERT: e136a338d7 Added the interfaces to the script language so that polymorphism can be used. REVERT: 3845fe1093 Fixed a couple of bugs with the parser REVERT: 910e2fdf55 - More bug fixes - Added MIPS support REVERT: 4c3c6a6d8f Fixed bug where the engine failed to find the script class methods if the class was not defined in the default module. REVERT: 4b32359c15 Applied code changes to make the library work better with 64bit systems. REVERT: 53d79c9a09 Released 2.6.0 REVERT: cc6f71b1e9 Fixed a bug in Prepare() that caused the stackFramePointer to be calculated incorrectly, causing buffer overflow. REVERT: 9250cbf070 Reorganized the documentation. REVERT: 669a23db9e Released 2.6.0 WIP 4 REVERT: 5f8ee7adf6 Compiling for 64bit targets now automatically defines the AS_64BIT_PTR flag REVERT: 549313bb29 Added methods to the script engine to obtain the function id for script declared class methods. REVERT: 960a850b48 Updated the documentation with the latest changes. REVERT: 4594343d83 - Type conversions may now be used in initializations of global variables. - A line break is now added to the string passed to ExecuteString() allowing the use of one line comments. - When trying to register dual operators as object behaviours a text message explaining the error is sent to the message stream. REVERT: ffab304774 git-svn-id: https://svn.code.sf.net/p/angelscript/code/trunk@12 404ce1b2-830e-0410-a2e2-b09542c77caf REVERT: 587fa98c56 git-svn-id: https://svn.code.sf.net/p/angelscript/code/trunk@9 404ce1b2-830e-0410-a2e2-b09542c77caf REVERT: 018658d367 git-svn-id: https://svn.code.sf.net/p/angelscript/code/trunk@7 404ce1b2-830e-0410-a2e2-b09542c77caf REVERT: fdcd552f0e Added 2.1.0c REVERT: 445c9f1710 git-svn-id: https://svn.code.sf.net/p/angelscript/code/trunk@2 404ce1b2-830e-0410-a2e2-b09542c77caf REVERT: 44362156b0 Created the repository git-subtree-dir: Source/ThirdParty/AngelScript git-subtree-split: 1c55f0bf23af1b9a68d263a94270f3f69066839c
16333 lines
510 KiB
C++
16333 lines
510 KiB
C++
/*
|
|
AngelCode Scripting Library
|
|
Copyright (c) 2003-2019 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
|
|
*/
|
|
|
|
// Modified by Lasse Oorni for Urho3D
|
|
|
|
//
|
|
// as_compiler.cpp
|
|
//
|
|
// The class that does the actual compilation of the functions
|
|
//
|
|
|
|
#include <math.h> // fmodf() pow()
|
|
|
|
#include "as_config.h"
|
|
|
|
#ifndef AS_NO_COMPILER
|
|
|
|
#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"
|
|
#include "as_debug.h"
|
|
#include "as_context.h" // as_powi()
|
|
|
|
BEGIN_AS_NAMESPACE
|
|
|
|
//
|
|
// The calling convention rules for script functions:
|
|
// - If a class method returns a reference, the caller must guarantee the object pointer stays alive until the function returns, and the reference is no longer going to be used
|
|
// - If a class method doesn't return a reference, it must guarantee by itself that the this pointer stays alive during the function call. If no outside access is made, then the function is guaranteed to stay alive and nothing needs to be done
|
|
// - The object pointer is always passed as the first argument, position 0
|
|
// - If the function returns a value type the caller must reserve the memory for this and pass the pointer as the first argument after the object pointer
|
|
//
|
|
|
|
|
|
|
|
|
|
|
|
// TODO: I must correct the interpretation of a reference to objects in the compiler.
|
|
// A reference should mean that a pointer to the object is on the stack.
|
|
// No expression should end up as non-references to objects, as the actual object is
|
|
// never put on the stack.
|
|
// Local variables are declared as non-references, but the expression should be a reference to the variable.
|
|
// Function parameters of called functions can also be non-references, but in that case it means the
|
|
// object will be passed by value (currently on the heap, which will be moved to the application stack).
|
|
//
|
|
// The compiler shouldn't use the asCDataType::IsReference. The datatype should always be stored as non-references.
|
|
// Instead the compiler should keep track of references in TypeInfo, where it should also state how the reference
|
|
// is currently stored, i.e. in variable, in register, on stack, etc.
|
|
|
|
asCCompiler::asCCompiler(asCScriptEngine *engine) : byteCode(engine)
|
|
{
|
|
builder = 0;
|
|
script = 0;
|
|
|
|
variables = 0;
|
|
isProcessingDeferredParams = false;
|
|
isCompilingDefaultArg = false;
|
|
noCodeOutput = 0;
|
|
}
|
|
|
|
asCCompiler::~asCCompiler()
|
|
{
|
|
while( variables )
|
|
{
|
|
asCVariableScope *var = variables;
|
|
variables = variables->parent;
|
|
|
|
asDELETE(var,asCVariableScope);
|
|
}
|
|
|
|
// Clean up all the string constants that were allocated. By now the script
|
|
// functions that were compiled successfully already holds their own references
|
|
for (asUINT n = 0; n < usedStringConstants.GetLength(); n++)
|
|
engine->stringFactory->ReleaseStringConstant(usedStringConstants[n]);
|
|
usedStringConstants.SetLength(0);
|
|
}
|
|
|
|
void asCCompiler::Reset(asCBuilder *in_builder, asCScriptCode *in_script, asCScriptFunction *in_outFunc)
|
|
{
|
|
this->builder = in_builder;
|
|
this->engine = in_builder->engine;
|
|
this->script = in_script;
|
|
this->outFunc = in_outFunc;
|
|
|
|
hasCompileErrors = false;
|
|
|
|
m_isConstructor = false;
|
|
m_isConstructorCalled = false;
|
|
m_classDecl = 0;
|
|
m_globalVar = 0;
|
|
|
|
nextLabel = 0;
|
|
breakLabels.SetLength(0);
|
|
continueLabels.SetLength(0);
|
|
|
|
numLambdas = 0;
|
|
|
|
byteCode.ClearAll();
|
|
}
|
|
|
|
int asCCompiler::CompileDefaultConstructor(asCBuilder *in_builder, asCScriptCode *in_script, asCScriptNode *in_node, asCScriptFunction *in_outFunc, sClassDeclaration *in_classDecl)
|
|
{
|
|
Reset(in_builder, in_script, in_outFunc);
|
|
|
|
m_classDecl = in_classDecl;
|
|
|
|
// Insert a JitEntry at the start of the function for JIT compilers
|
|
byteCode.InstrPTR(asBC_JitEntry, 0);
|
|
|
|
// Add a variable scope that might be needed to declare dummy variables
|
|
// in case the member initialization refers to undefined symbols.
|
|
AddVariableScope();
|
|
|
|
// Initialize the class members that have no explicit expression first. This will allow the
|
|
// base class' constructor to access these members without worry they will be uninitialized.
|
|
// This can happen if the base class' constructor calls a method that is overridden by the derived class
|
|
CompileMemberInitialization(&byteCode, true);
|
|
|
|
// If the class is derived from another, then the base class' default constructor must be called
|
|
if( outFunc->objectType->derivedFrom )
|
|
{
|
|
// Make sure the base class really has a default constructor
|
|
if( outFunc->objectType->derivedFrom->beh.construct == 0 )
|
|
Error(TEXT_BASE_DOESNT_HAVE_DEF_CONSTR, in_node);
|
|
|
|
// 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);
|
|
}
|
|
|
|
// Initialize the class members that explicit expressions afterwards. This allow the expressions
|
|
// to access the base class members without worry they will be uninitialized
|
|
CompileMemberInitialization(&byteCode, false);
|
|
byteCode.OptimizeLocally(tempVariableOffsets);
|
|
|
|
// If there are compile errors, there is no reason to build the final code
|
|
if( hasCompileErrors )
|
|
return -1;
|
|
|
|
// Pop the object pointer from the stack
|
|
byteCode.Ret(AS_PTR_SIZE);
|
|
|
|
// Count total variable size
|
|
int varSize = GetVariableOffset((int)variableAllocations.GetLength()) - 1;
|
|
outFunc->scriptData->variableSpace = varSize;
|
|
|
|
FinalizeFunction();
|
|
|
|
#ifdef AS_DEBUG
|
|
// DEBUG: output byte code
|
|
byteCode.DebugOutput(("__" + outFunc->objectType->name + "_" + outFunc->name + "__defconstr.txt").AddressOf(), in_outFunc);
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
int asCCompiler::CompileFactory(asCBuilder *in_builder, asCScriptCode *in_script, asCScriptFunction *in_outFunc)
|
|
{
|
|
Reset(in_builder, in_script, in_outFunc);
|
|
|
|
// Insert a JitEntry at the start of the function for JIT compilers
|
|
byteCode.InstrPTR(asBC_JitEntry, 0);
|
|
|
|
// Find the corresponding constructor
|
|
asCDataType dt = asCDataType::CreateType(outFunc->returnType.GetTypeInfo(), false);
|
|
int constructor = 0;
|
|
for( unsigned int 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 instantiate it with the constructor
|
|
int varOffset = AllocateVariable(dt, true);
|
|
|
|
outFunc->scriptData->variableSpace = AS_PTR_SIZE;
|
|
byteCode.InstrSHORT(asBC_PSF, (short)varOffset);
|
|
|
|
// Copy all arguments to the top of the stack
|
|
// TODO: runtime optimize: Might be interesting to have a specific instruction for copying all arguments
|
|
int offset = (int)outFunc->GetSpaceNeededForArguments();
|
|
for( int a = int(outFunc->parameterTypes.GetLength()) - 1; a >= 0; a-- )
|
|
{
|
|
if( !outFunc->parameterTypes[a].IsPrimitive() ||
|
|
outFunc->parameterTypes[a].IsReference() )
|
|
{
|
|
offset -= AS_PTR_SIZE;
|
|
byteCode.InstrSHORT(asBC_PshVPtr, short(-offset));
|
|
}
|
|
else
|
|
{
|
|
if( outFunc->parameterTypes[a].GetSizeOnStackDWords() == 2 )
|
|
{
|
|
offset -= 2;
|
|
byteCode.InstrSHORT(asBC_PshV8, short(-offset));
|
|
}
|
|
else
|
|
{
|
|
offset -= 1;
|
|
byteCode.InstrSHORT(asBC_PshV4, short(-offset));
|
|
}
|
|
}
|
|
}
|
|
|
|
int argDwords = (int)outFunc->GetSpaceNeededForArguments();
|
|
byteCode.Alloc(asBC_ALLOC, dt.GetTypeInfo(), constructor, argDwords + AS_PTR_SIZE);
|
|
|
|
// Return a handle to the newly created object
|
|
byteCode.InstrSHORT(asBC_LOADOBJ, (short)varOffset);
|
|
|
|
byteCode.Ret(argDwords);
|
|
|
|
FinalizeFunction();
|
|
|
|
// 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;
|
|
}
|
|
|
|
void asCCompiler::FinalizeFunction()
|
|
{
|
|
TimeIt("asCCompiler::FinalizeFunction");
|
|
|
|
asASSERT( outFunc->scriptData );
|
|
asUINT n;
|
|
|
|
// Finalize the bytecode
|
|
byteCode.Finalize(tempVariableOffsets);
|
|
|
|
// extract the try/catch info before object variable info, as
|
|
// some variable info is not needed if there are no try/catch blocks
|
|
byteCode.ExtractTryCatchInfo(outFunc);
|
|
|
|
byteCode.ExtractObjectVariableInfo(outFunc);
|
|
|
|
// Compile the list of object variables for the exception handler
|
|
// Start with the variables allocated on the heap, and then the ones allocated on the stack
|
|
for( n = 0; n < variableAllocations.GetLength(); n++ )
|
|
{
|
|
if( (variableAllocations[n].IsObject() || variableAllocations[n].IsFuncdef()) && !variableAllocations[n].IsReference() )
|
|
{
|
|
if( variableIsOnHeap[n] )
|
|
{
|
|
outFunc->scriptData->objVariableTypes.PushLast(variableAllocations[n].GetTypeInfo());
|
|
outFunc->scriptData->objVariablePos.PushLast(GetVariableOffset(n));
|
|
}
|
|
}
|
|
}
|
|
outFunc->scriptData->objVariablesOnHeap = asUINT(outFunc->scriptData->objVariablePos.GetLength());
|
|
for( n = 0; n < variableAllocations.GetLength(); n++ )
|
|
{
|
|
if( (variableAllocations[n].IsObject() || variableAllocations[n].IsFuncdef()) && !variableAllocations[n].IsReference() )
|
|
{
|
|
if( !variableIsOnHeap[n] )
|
|
{
|
|
outFunc->scriptData->objVariableTypes.PushLast(variableAllocations[n].GetTypeInfo());
|
|
outFunc->scriptData->objVariablePos.PushLast(GetVariableOffset(n));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Copy byte code to the function
|
|
asASSERT( outFunc->scriptData->byteCode.GetLength() == 0 );
|
|
outFunc->scriptData->byteCode.SetLength(byteCode.GetSize());
|
|
byteCode.Output(outFunc->scriptData->byteCode.AddressOf());
|
|
outFunc->AddReferences();
|
|
outFunc->scriptData->stackNeeded = byteCode.largestStackUsed + outFunc->scriptData->variableSpace;
|
|
outFunc->scriptData->lineNumbers = byteCode.lineNumbers;
|
|
|
|
// Extract the script section indexes too if there are any entries that are different from the function's script section
|
|
int lastIdx = outFunc->scriptData->scriptSectionIdx;
|
|
for( n = 0; n < byteCode.sectionIdxs.GetLength(); n++ )
|
|
{
|
|
if( byteCode.sectionIdxs[n] != lastIdx )
|
|
{
|
|
lastIdx = byteCode.sectionIdxs[n];
|
|
outFunc->scriptData->sectionIdxs.PushLast(byteCode.lineNumbers[n*2]);
|
|
outFunc->scriptData->sectionIdxs.PushLast(lastIdx);
|
|
}
|
|
}
|
|
}
|
|
|
|
// internal
|
|
int asCCompiler::SetupParametersAndReturnVariable(asCArray<asCString> ¶meterNames, asCScriptNode *func)
|
|
{
|
|
int stackPos = 0;
|
|
|
|
if( outFunc->objectType )
|
|
stackPos = -AS_PTR_SIZE; // The first parameter is the pointer to the object
|
|
|
|
// Add the first variable scope, which the parameters and
|
|
// variables declared in the outermost statement block is
|
|
// part of.
|
|
AddVariableScope();
|
|
|
|
bool isDestructor = false;
|
|
asCDataType returnType;
|
|
|
|
// Examine return type
|
|
returnType = outFunc->returnType;
|
|
|
|
// Check if this is a constructor or destructor
|
|
if( returnType.GetTokenType() == ttVoid && outFunc->objectType )
|
|
{
|
|
if( outFunc->name[0] == '~' )
|
|
isDestructor = true;
|
|
else if( outFunc->objectType->name == outFunc->name )
|
|
m_isConstructor = true;
|
|
}
|
|
|
|
// Is the return type allowed?
|
|
if( returnType != asCDataType::CreatePrimitive(ttVoid, false) &&
|
|
!returnType.CanBeInstantiated() )
|
|
{
|
|
// TODO: Hasn't this been validated by the builder already?
|
|
asCString str;
|
|
str.Format(TXT_RETURN_CANT_BE_s, returnType.Format(outFunc->nameSpace).AddressOf());
|
|
Error(str, func);
|
|
}
|
|
|
|
// If the return type is a value type returned by value the address of the
|
|
// location where the value will be stored is pushed on the stack before
|
|
// the arguments
|
|
if( !(isDestructor || m_isConstructor) && outFunc->DoesReturnOnStack() )
|
|
stackPos -= AS_PTR_SIZE;
|
|
|
|
asCVariableScope vs(0);
|
|
|
|
// Declare parameters
|
|
asUINT n;
|
|
for( n = 0; n < parameterNames.GetLength(); n++ )
|
|
{
|
|
// Get the parameter type
|
|
asCDataType &type = outFunc->parameterTypes[n];
|
|
asETypeModifiers inoutFlag = n < outFunc->inOutFlags.GetLength() ? outFunc->inOutFlags[n] : asTM_NONE;
|
|
|
|
// Is the data type allowed?
|
|
// TODO: Hasn't this been validated by the builder already?
|
|
if( (type.IsReference() && inoutFlag != asTM_INOUTREF && !type.CanBeInstantiated()) ||
|
|
(!type.IsReference() && !type.CanBeInstantiated()) )
|
|
{
|
|
asCString parm = type.Format(outFunc->nameSpace);
|
|
if( inoutFlag == asTM_INREF )
|
|
parm += "in";
|
|
else if( inoutFlag == asTM_OUTREF )
|
|
parm += "out";
|
|
|
|
asCString str;
|
|
str.Format(TXT_PARAMETER_CANT_BE_s, parm.AddressOf());
|
|
Error(str, func);
|
|
}
|
|
|
|
// If the parameter has a name then declare it as variable
|
|
if( parameterNames[n] != "" )
|
|
{
|
|
asCString &name = parameterNames[n];
|
|
if( vs.DeclareVariable(name.AddressOf(), type, stackPos, true) < 0 )
|
|
{
|
|
// TODO: It might be an out-of-memory too
|
|
Error(TXT_PARAMETER_ALREADY_DECLARED, func);
|
|
}
|
|
|
|
// Add marker for variable declaration
|
|
byteCode.VarDecl((int)outFunc->scriptData->variables.GetLength());
|
|
outFunc->AddVariable(name, type, stackPos);
|
|
}
|
|
else
|
|
vs.DeclareVariable("", type, stackPos, true);
|
|
|
|
// Move to next parameter
|
|
stackPos -= type.GetSizeOnStackDWords();
|
|
}
|
|
|
|
for( n = asUINT(vs.variables.GetLength()); n-- > 0; )
|
|
variables->DeclareVariable(vs.variables[n]->name.AddressOf(), vs.variables[n]->type, vs.variables[n]->stackOffset, vs.variables[n]->onHeap);
|
|
|
|
variables->DeclareVariable("return", returnType, stackPos, true);
|
|
|
|
return stackPos;
|
|
}
|
|
|
|
void asCCompiler::CompileMemberInitialization(asCByteCode *bc, bool onlyDefaults)
|
|
{
|
|
asASSERT( m_classDecl );
|
|
|
|
// Initialize each member in the order they were declared
|
|
for( asUINT n = 0; n < outFunc->objectType->properties.GetLength(); n++ )
|
|
{
|
|
asCObjectProperty *prop = outFunc->objectType->properties[n];
|
|
|
|
// Check if the property has an initialization expression
|
|
asCScriptNode *declNode = 0;
|
|
asCScriptNode *initNode = 0;
|
|
asCScriptCode *initScript = 0;
|
|
for( asUINT m = 0; m < m_classDecl->propInits.GetLength(); m++ )
|
|
{
|
|
if( m_classDecl->propInits[m].name == prop->name )
|
|
{
|
|
declNode = m_classDecl->propInits[m].declNode;
|
|
initNode = m_classDecl->propInits[m].initNode;
|
|
initScript = m_classDecl->propInits[m].file;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// If declNode is null, the property was inherited in which case
|
|
// it was already initialized by the base class' constructor
|
|
if( declNode )
|
|
{
|
|
if( initNode )
|
|
{
|
|
if( onlyDefaults )
|
|
continue;
|
|
|
|
#ifdef AS_NO_MEMBER_INIT
|
|
// Give an error as the initialization in the declaration has been disabled
|
|
asCScriptCode *origScript = script;
|
|
script = initScript;
|
|
Error("Initialization of members in declaration is not supported", initNode);
|
|
script = origScript;
|
|
|
|
// Clear the initialization node
|
|
initNode = 0;
|
|
initScript = script;
|
|
#else
|
|
// Re-parse the initialization expression as the parser now knows the types, which it didn't earlier
|
|
asCParser parser(builder);
|
|
int r = parser.ParseVarInit(initScript, initNode);
|
|
if( r < 0 )
|
|
continue;
|
|
|
|
initNode = parser.GetScriptNode();
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
if( !onlyDefaults )
|
|
continue;
|
|
}
|
|
|
|
#ifdef AS_NO_MEMBER_INIT
|
|
// The initialization will be done in the asCScriptObject constructor, so
|
|
// here we should just validate that the member has a default constructor
|
|
if( prop->type.IsObject() &&
|
|
!prop->type.IsObjectHandle() &&
|
|
(((prop->type.GetTypeInfo()->flags & asOBJ_REF) &&
|
|
prop->type.GetBehaviour()->factory == 0) ||
|
|
((prop->type.GetTypeInfo()->flags & asOBJ_VALUE) &&
|
|
prop->type.GetBehaviour()->construct == 0 &&
|
|
!(prop->type.GetTypeInfo()->flags & asOBJ_POD))) )
|
|
{
|
|
// Class has no default factory/constructor.
|
|
asCString str;
|
|
// TODO: funcdef: asCDataType should have a GetTypeName()
|
|
if( prop->type.GetFuncDef() )
|
|
str.Format(TXT_NO_DEFAULT_CONSTRUCTOR_FOR_s, prop->type.GetFuncDef()->GetName());
|
|
else
|
|
str.Format(TXT_NO_DEFAULT_CONSTRUCTOR_FOR_s, prop->type.GetTypeInfo()->GetName());
|
|
Error(str, declNode);
|
|
}
|
|
#else
|
|
// Temporarily set the script that is being compiled to where the member initialization is declared.
|
|
// The script can be different when including mixin classes from a different script section
|
|
asCScriptCode *origScript = script;
|
|
script = initScript;
|
|
|
|
// Add a line instruction with the position of the declaration
|
|
LineInstr(bc, declNode->tokenPos);
|
|
|
|
// Compile the initialization
|
|
asQWORD constantValue;
|
|
asCByteCode bcInit(engine);
|
|
CompileInitialization(initNode, &bcInit, prop->type, declNode, prop->byteOffset, &constantValue, 2);
|
|
bcInit.OptimizeLocally(tempVariableOffsets);
|
|
bc->AddCode(&bcInit);
|
|
|
|
script = origScript;
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
// Entry
|
|
int asCCompiler::CompileFunction(asCBuilder *in_builder, asCScriptCode *in_script, asCArray<asCString> &in_parameterNames, asCScriptNode *in_func, asCScriptFunction *in_outFunc, sClassDeclaration *in_classDecl)
|
|
{
|
|
TimeIt("asCCompiler::CompileFunction");
|
|
|
|
Reset(in_builder, in_script, in_outFunc);
|
|
int buildErrors = builder->numErrors;
|
|
|
|
int stackPos = SetupParametersAndReturnVariable(in_parameterNames, in_func);
|
|
|
|
//--------------------------------------------
|
|
// Compile the statement block
|
|
|
|
if( m_isConstructor )
|
|
m_classDecl = in_classDecl;
|
|
|
|
// We need to parse the statement block now
|
|
asCScriptNode *blockBegin;
|
|
|
|
// If the function signature was implicit, e.g. virtual property accessor or
|
|
// lambda function, then the received node already is the statement block
|
|
if( in_func->nodeType != snStatementBlock )
|
|
blockBegin = in_func->lastChild;
|
|
else
|
|
blockBegin = in_func;
|
|
|
|
// TODO: memory: We can parse the statement block one statement at a time, thus save even more memory
|
|
// TODO: optimize: For large functions, the parsing of the statement block can take a long time. Presumably because a lot of memory needs to be allocated
|
|
asCParser parser(builder);
|
|
int r = parser.ParseStatementBlock(script, blockBegin);
|
|
if( r < 0 ) return -1;
|
|
asCScriptNode *block = parser.GetScriptNode();
|
|
|
|
// Reserve a label for the cleanup code
|
|
nextLabel++;
|
|
|
|
bool hasReturn;
|
|
asCByteCode bc(engine);
|
|
LineInstr(&bc, blockBegin->tokenPos);
|
|
CompileStatementBlock(block, false, &hasReturn, &bc);
|
|
LineInstr(&bc, blockBegin->tokenPos + blockBegin->tokenLength);
|
|
|
|
// Make sure there is a return in all paths (if not return type is void)
|
|
// Don't bother with this check if there are compiler errors, e.g. Unreachable code
|
|
if( !hasCompileErrors && outFunc->returnType != asCDataType::CreatePrimitive(ttVoid, false) )
|
|
{
|
|
if( hasReturn == false )
|
|
Error(TXT_NOT_ALL_PATHS_RETURN, blockBegin);
|
|
}
|
|
|
|
//------------------------------------------------
|
|
// Concatenate the bytecode
|
|
|
|
// Insert a JitEntry at the start of the function for JIT compilers
|
|
byteCode.InstrPTR(asBC_JitEntry, 0);
|
|
|
|
if( outFunc->objectType )
|
|
{
|
|
if( m_isConstructor )
|
|
{
|
|
if( outFunc->objectType->derivedFrom )
|
|
{
|
|
// Call the base class' default constructor unless called manually in the code
|
|
if( !m_isConstructorCalled )
|
|
{
|
|
if( outFunc->objectType->derivedFrom->beh.construct )
|
|
{
|
|
// Initialize members without explicit expression first
|
|
CompileMemberInitialization(&byteCode, true);
|
|
|
|
// Call base class' constructor
|
|
asCByteCode tmpBC(engine);
|
|
tmpBC.InstrSHORT(asBC_PSF, 0);
|
|
tmpBC.Instr(asBC_RDSPtr);
|
|
tmpBC.Call(asBC_CALL, outFunc->objectType->derivedFrom->beh.construct, AS_PTR_SIZE);
|
|
tmpBC.OptimizeLocally(tempVariableOffsets);
|
|
byteCode.AddCode(&tmpBC);
|
|
|
|
// Add the initialization of the members with explicit expressions
|
|
CompileMemberInitialization(&byteCode, false);
|
|
}
|
|
else
|
|
Error(TEXT_BASE_DOESNT_HAVE_DEF_CONSTR, blockBegin);
|
|
}
|
|
else
|
|
{
|
|
// Only initialize members that don't have an explicit expression
|
|
// The members that are explicitly initialized will be initialized after the call to base class' constructor
|
|
CompileMemberInitialization(&byteCode, true);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Add the initialization of the members
|
|
CompileMemberInitialization(&byteCode, true);
|
|
CompileMemberInitialization(&byteCode, false);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Add the code for the statement block
|
|
byteCode.AddCode(&bc);
|
|
|
|
// Count total variable size
|
|
int varSize = GetVariableOffset((int)variableAllocations.GetLength()) - 1;
|
|
outFunc->scriptData->variableSpace = varSize;
|
|
|
|
// Deallocate all local variables
|
|
int n;
|
|
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, v->onHeap, &byteCode);
|
|
|
|
DeallocateVariable(v->stackOffset);
|
|
}
|
|
}
|
|
|
|
// This is the label that return statements jump to
|
|
// in order to exit the function
|
|
byteCode.Label(0);
|
|
|
|
// 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, v->onHeap, &byteCode);
|
|
}
|
|
|
|
// Do not deallocate parameters
|
|
}
|
|
|
|
// Check if the number of labels in the functions isn't too many to be handled
|
|
if( nextLabel >= (1<<15) )
|
|
Error(TXT_TOO_MANY_JUMP_LABELS, in_func);
|
|
|
|
// If there are compile errors, there is no reason to build the final code
|
|
if( hasCompileErrors || builder->numErrors != buildErrors )
|
|
return -1;
|
|
|
|
// At this point there should be no variables allocated
|
|
asASSERT(variableAllocations.GetLength() == freeVariables.GetLength());
|
|
|
|
// Remove the variable scope
|
|
RemoveVariableScope();
|
|
|
|
byteCode.Ret(-stackPos);
|
|
|
|
FinalizeFunction();
|
|
|
|
#ifdef AS_DEBUG
|
|
// DEBUG: output byte code
|
|
if( outFunc->objectType )
|
|
byteCode.DebugOutput(("__" + outFunc->objectType->name + "_" + outFunc->name + ".txt").AddressOf(), in_outFunc);
|
|
else
|
|
byteCode.DebugOutput(("__" + outFunc->name + ".txt").AddressOf(), in_outFunc);
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
int asCCompiler::CallCopyConstructor(asCDataType &type, int offset, bool isObjectOnHeap, asCByteCode *bc, asCExprContext *arg, asCScriptNode *node, bool isGlobalVar, bool derefDest)
|
|
{
|
|
if( !type.IsObject() )
|
|
return 0;
|
|
|
|
// CallCopyConstructor should not be called for object handles.
|
|
asASSERT( !type.IsObjectHandle() );
|
|
|
|
asCArray<asCExprContext*> args;
|
|
args.PushLast(arg);
|
|
|
|
// The reference parameter must be pushed on the stack
|
|
asASSERT( arg->type.dataType.GetTypeInfo() == type.GetTypeInfo() );
|
|
|
|
// Since we're calling the copy constructor, we have to trust the function to not do
|
|
// anything stupid otherwise we will just enter a loop, as we try to make temporary
|
|
// copies of the argument in order to guarantee safety.
|
|
|
|
|
|
if( type.GetTypeInfo()->flags & asOBJ_REF )
|
|
{
|
|
asCExprContext ctx(engine);
|
|
|
|
int func = 0;
|
|
asSTypeBehaviour *beh = type.GetBehaviour();
|
|
if( beh ) func = beh->copyfactory;
|
|
|
|
if( func > 0 )
|
|
{
|
|
if( !isGlobalVar )
|
|
{
|
|
// Call factory and store the handle in the given variable
|
|
PerformFunctionCall(func, &ctx, false, &args, CastToObjectType(type.GetTypeInfo()), true, offset);
|
|
|
|
// Pop the reference left by the function call
|
|
ctx.bc.Instr(asBC_PopPtr);
|
|
}
|
|
else
|
|
{
|
|
// Call factory
|
|
PerformFunctionCall(func, &ctx, false, &args, CastToObjectType(type.GetTypeInfo()));
|
|
|
|
// Store the returned handle in the global variable
|
|
ctx.bc.Instr(asBC_RDSPtr);
|
|
ctx.bc.InstrPTR(asBC_PGA, engine->globalProperties[offset]->GetAddressOfValue());
|
|
ctx.bc.InstrPTR(asBC_REFCPY, type.GetTypeInfo());
|
|
ctx.bc.Instr(asBC_PopPtr);
|
|
ReleaseTemporaryVariable(ctx.type.stackOffset, &ctx.bc);
|
|
}
|
|
|
|
bc->AddCode(&ctx.bc);
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
asSTypeBehaviour *beh = type.GetBehaviour();
|
|
int func = beh ? beh->copyconstruct : 0;
|
|
if( func > 0 )
|
|
{
|
|
// Push the address where the object will be stored on the stack, before the argument
|
|
// TODO: When the context is serializable this probably has to be changed, since this
|
|
// pointer can remain on the stack while the context is suspended. There is no
|
|
// risk the pointer becomes invalid though, there is just no easy way to serialize it.
|
|
asCByteCode tmp(engine);
|
|
if( isGlobalVar )
|
|
tmp.InstrPTR(asBC_PGA, engine->globalProperties[offset]->GetAddressOfValue());
|
|
else if( isObjectOnHeap )
|
|
tmp.InstrSHORT(asBC_PSF, (short)offset);
|
|
tmp.AddCode(bc);
|
|
bc->AddCode(&tmp);
|
|
|
|
// When the object is allocated on the stack the object pointer
|
|
// must be pushed on the stack after the arguments
|
|
if( !isObjectOnHeap )
|
|
{
|
|
asASSERT( !isGlobalVar );
|
|
bc->InstrSHORT(asBC_PSF, (short)offset);
|
|
if( derefDest )
|
|
{
|
|
// The variable is a reference to the real location, so we need to dereference it
|
|
bc->Instr(asBC_RDSPtr);
|
|
}
|
|
}
|
|
|
|
asCExprContext ctx(engine);
|
|
PerformFunctionCall(func, &ctx, isObjectOnHeap, &args, CastToObjectType(type.GetTypeInfo()));
|
|
|
|
bc->AddCode(&ctx.bc);
|
|
|
|
// TODO: value on stack: This probably needs to be done in PerformFunctionCall
|
|
// Mark the object as initialized
|
|
if( !isObjectOnHeap )
|
|
bc->ObjInfo(offset, asOBJ_INIT);
|
|
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
// Class has no copy constructor/factory.
|
|
asCString str;
|
|
str.Format(TXT_NO_COPY_CONSTRUCTOR_FOR_s, type.GetTypeInfo()->GetName());
|
|
Error(str, node);
|
|
|
|
return -1;
|
|
}
|
|
|
|
int asCCompiler::CallDefaultConstructor(const asCDataType &type, int offset, bool isObjectOnHeap, asCByteCode *bc, asCScriptNode *node, int isVarGlobOrMem, bool derefDest)
|
|
{
|
|
if( !type.IsObject() || type.IsObjectHandle() )
|
|
return 0;
|
|
|
|
if( type.GetTypeInfo()->flags & asOBJ_REF )
|
|
{
|
|
asCExprContext ctx(engine);
|
|
ctx.exprNode = node;
|
|
|
|
int func = 0;
|
|
asSTypeBehaviour *beh = type.GetBehaviour();
|
|
if( beh )
|
|
{
|
|
func = beh->factory;
|
|
|
|
// If no trivial default factory is found, look for a factory where all params have default args
|
|
if( func == 0 )
|
|
{
|
|
for( asUINT n = 0; n < beh->factories.GetLength(); n++ )
|
|
{
|
|
asCScriptFunction *f = engine->scriptFunctions[beh->factories[n]];
|
|
if( f->defaultArgs.GetLength() == f->parameterTypes.GetLength() &&
|
|
f->defaultArgs[0] != 0 )
|
|
{
|
|
func = beh->factories[n];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if( func > 0 )
|
|
{
|
|
asCArray<asCExprContext *> args;
|
|
asCScriptFunction *f = engine->scriptFunctions[func];
|
|
if( f->parameterTypes.GetLength() )
|
|
{
|
|
// Add the default values for arguments not explicitly supplied
|
|
CompileDefaultAndNamedArgs(node, args, func, CastToObjectType(type.GetTypeInfo()));
|
|
|
|
PrepareFunctionCall(func, &ctx.bc, args);
|
|
|
|
MoveArgsToStack(func, &ctx.bc, args, false);
|
|
}
|
|
|
|
if( isVarGlobOrMem == 0 )
|
|
{
|
|
// Call factory and store the handle in the given variable
|
|
PerformFunctionCall(func, &ctx, false, &args, CastToObjectType(type.GetTypeInfo()), true, offset);
|
|
|
|
// Pop the reference left by the function call
|
|
ctx.bc.Instr(asBC_PopPtr);
|
|
}
|
|
else
|
|
{
|
|
// Call factory
|
|
PerformFunctionCall(func, &ctx, false, &args, CastToObjectType(type.GetTypeInfo()));
|
|
|
|
// TODO: runtime optimize: Should have a way of storing the object pointer directly to the destination
|
|
// instead of first storing it in a local variable and then copying it to the
|
|
// destination.
|
|
|
|
if( !(type.GetTypeInfo()->flags & asOBJ_SCOPED) )
|
|
{
|
|
// Only dereference the variable if not a scoped type
|
|
ctx.bc.Instr(asBC_RDSPtr);
|
|
}
|
|
|
|
if( isVarGlobOrMem == 1 )
|
|
{
|
|
// Store the returned handle in the global variable
|
|
ctx.bc.InstrPTR(asBC_PGA, engine->globalProperties[offset]->GetAddressOfValue());
|
|
}
|
|
else
|
|
{
|
|
// Store the returned handle in the class member
|
|
ctx.bc.InstrSHORT(asBC_PSF, 0);
|
|
ctx.bc.Instr(asBC_RDSPtr);
|
|
ctx.bc.InstrSHORT_DW(asBC_ADDSi, (short)offset, engine->GetTypeIdFromDataType(asCDataType::CreateType(outFunc->objectType, false)));
|
|
}
|
|
|
|
if( type.GetTypeInfo()->flags & asOBJ_SCOPED )
|
|
{
|
|
// For scoped typed we must move the reference from the local
|
|
// variable rather than copy it as there is no AddRef behaviour
|
|
ctx.bc.InstrSHORT_DW(asBC_COPY, AS_PTR_SIZE, asTYPEID_OBJHANDLE | engine->GetTypeIdFromDataType(type));
|
|
|
|
// Clear the local variable so the reference isn't released
|
|
ctx.bc.InstrSHORT(asBC_ClrVPtr, ctx.type.stackOffset);
|
|
}
|
|
else
|
|
{
|
|
if( type.IsFuncdef() )
|
|
ctx.bc.InstrPTR(asBC_REFCPY, &engine->functionBehaviours);
|
|
else
|
|
ctx.bc.InstrPTR(asBC_REFCPY, type.GetTypeInfo());
|
|
}
|
|
ctx.bc.Instr(asBC_PopPtr);
|
|
ReleaseTemporaryVariable(ctx.type.stackOffset, &ctx.bc);
|
|
}
|
|
|
|
bc->AddCode(&ctx.bc);
|
|
|
|
// Cleanup
|
|
for( asUINT n = 0; n < args.GetLength(); n++ )
|
|
if( args[n] )
|
|
{
|
|
asDELETE(args[n], asCExprContext);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
asCExprContext ctx(engine);
|
|
ctx.exprNode = node;
|
|
|
|
asSTypeBehaviour *beh = type.GetBehaviour();
|
|
|
|
int func = 0;
|
|
if( beh )
|
|
{
|
|
func = beh->construct;
|
|
|
|
// If no trivial default constructor is found, look for a constructor where all params have default args
|
|
if( func == 0 )
|
|
{
|
|
for( asUINT n = 0; n < beh->constructors.GetLength(); n++ )
|
|
{
|
|
asCScriptFunction *f = engine->scriptFunctions[beh->constructors[n]];
|
|
if( f->defaultArgs.GetLength() == f->parameterTypes.GetLength() &&
|
|
f->defaultArgs[0] != 0 )
|
|
{
|
|
func = beh->constructors[n];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Allocate and initialize with the default constructor
|
|
if( func != 0 || (type.GetTypeInfo()->flags & asOBJ_POD) )
|
|
{
|
|
asCArray<asCExprContext *> args;
|
|
asCScriptFunction *f = engine->scriptFunctions[func];
|
|
if( f && f->parameterTypes.GetLength() )
|
|
{
|
|
// Add the default values for arguments not explicitly supplied
|
|
CompileDefaultAndNamedArgs(node, args, func, CastToObjectType(type.GetTypeInfo()));
|
|
|
|
PrepareFunctionCall(func, &ctx.bc, args);
|
|
|
|
MoveArgsToStack(func, &ctx.bc, args, false);
|
|
}
|
|
|
|
if( !isObjectOnHeap )
|
|
{
|
|
if( isVarGlobOrMem == 0 )
|
|
{
|
|
// There is nothing to do if there is no function,
|
|
// as the memory is already allocated on the stack
|
|
if( func )
|
|
{
|
|
// Call the constructor as a normal function
|
|
bc->InstrSHORT(asBC_PSF, (short)offset);
|
|
if( derefDest )
|
|
bc->Instr(asBC_RDSPtr);
|
|
|
|
asCExprContext ctxCall(engine);
|
|
PerformFunctionCall(func, &ctxCall, false, 0, CastToObjectType(type.GetTypeInfo()));
|
|
bc->AddCode(&ctxCall.bc);
|
|
|
|
// TODO: value on stack: This probably needs to be done in PerformFunctionCall
|
|
// Mark the object as initialized
|
|
bc->ObjInfo(offset, asOBJ_INIT);
|
|
}
|
|
}
|
|
else if( isVarGlobOrMem == 2 )
|
|
{
|
|
// Only POD types can be allocated inline in script classes
|
|
asASSERT( type.GetTypeInfo()->flags & asOBJ_POD );
|
|
|
|
if( func )
|
|
{
|
|
// Call the constructor as a normal function
|
|
bc->InstrSHORT(asBC_PSF, 0);
|
|
bc->Instr(asBC_RDSPtr);
|
|
bc->InstrSHORT_DW(asBC_ADDSi, (short)offset, engine->GetTypeIdFromDataType(asCDataType::CreateType(outFunc->objectType, false)));
|
|
|
|
asCExprContext ctxCall(engine);
|
|
PerformFunctionCall(func, &ctxCall, false, 0, CastToObjectType(type.GetTypeInfo()));
|
|
bc->AddCode(&ctxCall.bc);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
asASSERT( false );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( isVarGlobOrMem == 0 )
|
|
bc->InstrSHORT(asBC_PSF, (short)offset);
|
|
else if( isVarGlobOrMem == 1 )
|
|
bc->InstrPTR(asBC_PGA, engine->globalProperties[offset]->GetAddressOfValue());
|
|
else
|
|
{
|
|
bc->InstrSHORT(asBC_PSF, 0);
|
|
bc->Instr(asBC_RDSPtr);
|
|
bc->InstrSHORT_DW(asBC_ADDSi, (short)offset, engine->GetTypeIdFromDataType(asCDataType::CreateType(outFunc->objectType, false)));
|
|
}
|
|
|
|
if( (type.GetTypeInfo()->flags & asOBJ_TEMPLATE) )
|
|
{
|
|
asCScriptFunction *descr = engine->scriptFunctions[func];
|
|
asASSERT( descr->funcType == asFUNC_SCRIPT );
|
|
|
|
// Find the id of the real constructor and not the generated stub
|
|
asUINT id = 0;
|
|
asDWORD *funcBc = descr->scriptData->byteCode.AddressOf();
|
|
while( funcBc )
|
|
{
|
|
if( (*(asBYTE*)funcBc) == asBC_CALLSYS )
|
|
{
|
|
id = asBC_INTARG(funcBc);
|
|
break;
|
|
}
|
|
funcBc += asBCTypeSize[asBCInfo[*(asBYTE*)funcBc].type];
|
|
}
|
|
|
|
asASSERT( id );
|
|
|
|
bc->InstrPTR(asBC_OBJTYPE, type.GetTypeInfo());
|
|
bc->Alloc(asBC_ALLOC, type.GetTypeInfo(), id, AS_PTR_SIZE + AS_PTR_SIZE);
|
|
}
|
|
else
|
|
bc->Alloc(asBC_ALLOC, type.GetTypeInfo(), func, AS_PTR_SIZE);
|
|
}
|
|
|
|
// Cleanup
|
|
for( asUINT n = 0; n < args.GetLength(); n++ )
|
|
if( args[n] )
|
|
{
|
|
asDELETE(args[n], asCExprContext);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
// Class has no default factory/constructor.
|
|
asCString str;
|
|
str.Format(TXT_NO_DEFAULT_CONSTRUCTOR_FOR_s, type.GetTypeInfo()->GetName());
|
|
Error(str, node);
|
|
|
|
return -1;
|
|
}
|
|
|
|
void asCCompiler::CallDestructor(asCDataType &type, int offset, bool isObjectOnHeap, asCByteCode *bc)
|
|
{
|
|
if( !type.IsReference() )
|
|
{
|
|
// Call destructor for the data type
|
|
if( type.IsObject() || type.IsFuncdef() )
|
|
{
|
|
// The null pointer doesn't need to be destroyed
|
|
if( type.IsNullHandle() )
|
|
return;
|
|
|
|
// Nothing is done for list pattern types, as this is taken care of by the CompileInitList method
|
|
if( type.GetTypeInfo()->flags & asOBJ_LIST_PATTERN )
|
|
return;
|
|
|
|
if( isObjectOnHeap || type.IsObjectHandle() )
|
|
{
|
|
// Free the memory
|
|
if (type.IsFuncdef())
|
|
bc->InstrW_PTR(asBC_FREE, (short)offset, &engine->functionBehaviours);
|
|
else
|
|
bc->InstrW_PTR(asBC_FREE, (short)offset, type.GetTypeInfo());
|
|
}
|
|
else
|
|
{
|
|
asASSERT( type.GetTypeInfo()->GetFlags() & asOBJ_VALUE );
|
|
|
|
if( type.GetBehaviour()->destruct )
|
|
{
|
|
// Call the destructor as a regular function
|
|
asCExprContext ctx(engine);
|
|
ctx.bc.InstrSHORT(asBC_PSF, (short)offset);
|
|
PerformFunctionCall(type.GetBehaviour()->destruct, &ctx);
|
|
ctx.bc.OptimizeLocally(tempVariableOffsets);
|
|
bc->AddCode(&ctx.bc);
|
|
}
|
|
|
|
// TODO: Value on stack: This probably needs to be done in PerformFunctionCall
|
|
// Mark the object as destroyed
|
|
bc->ObjInfo(offset, asOBJ_UNINIT);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void asCCompiler::LineInstr(asCByteCode *bc, size_t pos)
|
|
{
|
|
int r, c;
|
|
script->ConvertPosToRowCol(pos, &r, &c);
|
|
bc->Line(r, c, script->idx);
|
|
}
|
|
|
|
void asCCompiler::CompileStatementBlock(asCScriptNode *block, bool ownVariableScope, bool *hasReturn, asCByteCode *bc)
|
|
{
|
|
*hasReturn = false;
|
|
bool isFinished = false;
|
|
bool hasUnreachableCode = false;
|
|
bool hasReturnBefore = false;
|
|
|
|
if( ownVariableScope )
|
|
{
|
|
bc->Block(true);
|
|
AddVariableScope();
|
|
}
|
|
|
|
asCScriptNode *node = block->firstChild;
|
|
while( node )
|
|
{
|
|
#ifdef AS_DEBUG
|
|
// Keep the current line in a variable so it will be easier
|
|
// to determine where in a script an assert is occurring.
|
|
int currentLine = 0;
|
|
script->ConvertPosToRowCol(node->tokenPos, ¤tLine, 0);
|
|
#endif
|
|
|
|
if( !hasUnreachableCode && (*hasReturn || isFinished) )
|
|
{
|
|
// Empty statements don't count
|
|
if( node->nodeType != snExpressionStatement || node->firstChild )
|
|
{
|
|
hasUnreachableCode = true;
|
|
Warning(TXT_UNREACHABLE_CODE, node);
|
|
}
|
|
|
|
if( *hasReturn )
|
|
hasReturnBefore = true;
|
|
}
|
|
|
|
if( node->nodeType == snBreak || node->nodeType == snContinue )
|
|
isFinished = true;
|
|
|
|
asCByteCode statement(engine);
|
|
if( node->nodeType == snDeclaration )
|
|
CompileDeclaration(node, &statement);
|
|
else
|
|
CompileStatement(node, hasReturn, &statement);
|
|
|
|
// Ignore missing returns in unreachable code paths
|
|
if( !(*hasReturn) && hasReturnBefore )
|
|
*hasReturn = true;
|
|
|
|
LineInstr(bc, node->tokenPos);
|
|
bc->AddCode(&statement);
|
|
|
|
if( !hasCompileErrors )
|
|
{
|
|
asASSERT( tempVariables.GetLength() == 0 );
|
|
asASSERT( reservedVariables.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, v->onHeap, bc);
|
|
|
|
// Don't deallocate function parameters
|
|
if( v->stackOffset > 0 )
|
|
DeallocateVariable(v->stackOffset);
|
|
}
|
|
|
|
RemoveVariableScope();
|
|
bc->Block(false);
|
|
}
|
|
}
|
|
|
|
// Entry
|
|
int asCCompiler::CompileGlobalVariable(asCBuilder *in_builder, asCScriptCode *in_script, asCScriptNode *in_node, sGlobalVariableDescription *in_gvar, asCScriptFunction *in_outFunc)
|
|
{
|
|
Reset(in_builder, in_script, in_outFunc);
|
|
m_globalVar = in_gvar;
|
|
|
|
// Add a variable scope (even though variables can't be declared)
|
|
AddVariableScope();
|
|
|
|
in_gvar->isPureConstant = false;
|
|
|
|
// Parse the initialization nodes
|
|
asCParser parser(builder);
|
|
if (in_node)
|
|
{
|
|
int r = parser.ParseVarInit(in_script, in_node);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
in_node = parser.GetScriptNode();
|
|
}
|
|
|
|
asCExprContext compiledCtx(engine);
|
|
bool preCompiled = false;
|
|
if (in_gvar->datatype.IsAuto())
|
|
{
|
|
preCompiled = CompileAutoType(in_gvar->datatype, compiledCtx, in_node, in_gvar->declaredAtNode);
|
|
if (!preCompiled)
|
|
{
|
|
// If it wasn't possible to determine the type from the expression then there
|
|
// is no need to continue with the initialization. The error was already reported
|
|
// in CompileAutoType.
|
|
return -1;
|
|
}
|
|
}
|
|
if( in_gvar->property == 0 )
|
|
{
|
|
in_gvar->property = builder->module->AllocateGlobalProperty(in_gvar->name.AddressOf(), in_gvar->datatype, in_gvar->ns);
|
|
in_gvar->index = in_gvar->property->id;
|
|
}
|
|
|
|
// Compile the expression
|
|
asCExprContext ctx(engine);
|
|
asQWORD constantValue = 0;
|
|
if( CompileInitialization(in_node, &ctx.bc, in_gvar->datatype, in_gvar->declaredAtNode, in_gvar->index, &constantValue, 1, preCompiled ? &compiledCtx : 0) )
|
|
{
|
|
// Should the variable be marked as pure constant?
|
|
if( in_gvar->datatype.IsPrimitive() && in_gvar->datatype.IsReadOnly() )
|
|
{
|
|
in_gvar->isPureConstant = true;
|
|
in_gvar->constantValue = constantValue;
|
|
}
|
|
}
|
|
|
|
// Concatenate the bytecode
|
|
int varSize = GetVariableOffset((int)variableAllocations.GetLength()) - 1;
|
|
|
|
// Add information on the line number for the global variable
|
|
size_t pos = 0;
|
|
if( in_gvar->declaredAtNode )
|
|
pos = in_gvar->declaredAtNode->tokenPos;
|
|
else if( in_gvar->initializationNode )
|
|
pos = in_gvar->initializationNode->tokenPos;
|
|
LineInstr(&byteCode, pos);
|
|
|
|
// Reserve space for all local variables
|
|
outFunc->scriptData->variableSpace = varSize;
|
|
|
|
ctx.bc.OptimizeLocally(tempVariableOffsets);
|
|
|
|
byteCode.AddCode(&ctx.bc);
|
|
|
|
// 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
|
|
CallDestructor(v->type, v->stackOffset, v->onHeap, &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();
|
|
|
|
byteCode.Ret(0);
|
|
|
|
FinalizeFunction();
|
|
|
|
#ifdef AS_DEBUG
|
|
// DEBUG: output byte code
|
|
byteCode.DebugOutput(("___init_" + in_gvar->name + ".txt").AddressOf(), outFunc);
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
void asCCompiler::DetermineSingleFunc(asCExprContext *ctx, asCScriptNode *node)
|
|
{
|
|
// Don't do anything if this is not a deferred global function
|
|
if( !ctx->IsGlobalFunc() )
|
|
return;
|
|
|
|
// Determine the namespace
|
|
asSNameSpace *ns = 0;
|
|
asCString name = "";
|
|
int pos = ctx->methodName.FindLast("::");
|
|
if( pos >= 0 )
|
|
{
|
|
asCString nsName = ctx->methodName.SubString(0, pos+2);
|
|
|
|
// Cut off the ::
|
|
if( nsName.GetLength() > 2 )
|
|
nsName.SetLength(nsName.GetLength()-2);
|
|
|
|
ns = DetermineNameSpace(nsName);
|
|
name = ctx->methodName.SubString(pos+2);
|
|
}
|
|
else
|
|
{
|
|
DetermineNameSpace("");
|
|
name = ctx->methodName;
|
|
}
|
|
|
|
asCArray<int> funcs;
|
|
if( ns )
|
|
builder->GetFunctionDescriptions(name.AddressOf(), funcs, ns);
|
|
|
|
// CompileVariableAccess should guarantee that at least one function is exists
|
|
asASSERT( funcs.GetLength() > 0 );
|
|
|
|
if( funcs.GetLength() > 1 )
|
|
{
|
|
asCString str;
|
|
str.Format(TXT_MULTIPLE_MATCHING_SIGNATURES_TO_s, ctx->methodName.AddressOf());
|
|
Error(str, node);
|
|
|
|
// Fall through so the compiler can continue as if only one function was matching
|
|
}
|
|
|
|
// A shared object may not access global functions unless they too are shared (e.g. registered functions)
|
|
if( !builder->GetFunctionDescription(funcs[0])->IsShared() &&
|
|
outFunc->IsShared() )
|
|
{
|
|
asCString msg;
|
|
msg.Format(TXT_SHARED_CANNOT_CALL_NON_SHARED_FUNC_s, builder->GetFunctionDescription(funcs[0])->GetDeclaration());
|
|
Error(msg, node);
|
|
|
|
// Fall through so the compiler can continue anyway
|
|
}
|
|
|
|
// Push the function pointer on the stack
|
|
ctx->bc.InstrPTR(asBC_FuncPtr, builder->GetFunctionDescription(funcs[0]));
|
|
ctx->type.Set(asCDataType::CreateType(engine->FindMatchingFuncdef(builder->GetFunctionDescription(funcs[0]), builder->module), false));
|
|
ctx->type.dataType.MakeHandle(true);
|
|
ctx->type.isExplicitHandle = true;
|
|
ctx->methodName = "";
|
|
}
|
|
|
|
void asCCompiler::CompileInitAsCopy(asCDataType &dt, int offset, asCByteCode *bc, asCExprContext *arg, asCScriptNode *node, bool derefDestination)
|
|
{
|
|
bool isObjectOnHeap = derefDestination ? false : IsVariableOnHeap(offset);
|
|
|
|
// Use copy constructor if available.
|
|
asCObjectType *ot = CastToObjectType(dt.GetTypeInfo());
|
|
if(!dt.IsObjectHandle() && ot && (ot->beh.copyconstruct || ot->beh.copyfactory))
|
|
{
|
|
PrepareForAssignment(&dt, arg, node, true);
|
|
int r = CallCopyConstructor(dt, offset, isObjectOnHeap, bc, arg, node, 0, derefDestination);
|
|
if( r < 0 && tempVariables.Exists(offset) )
|
|
Error(TXT_FAILED_TO_CREATE_TEMP_OBJ, node);
|
|
}
|
|
else
|
|
{
|
|
// TODO: Need to reserve variables, as the default constructor may need
|
|
// to allocate temporary variables to compute default args
|
|
|
|
// Allocate and construct the temporary object before whatever is already in the bytecode
|
|
asCByteCode tmpBC(engine);
|
|
int r = CallDefaultConstructor(dt, offset, isObjectOnHeap, &tmpBC, node, 0, derefDestination);
|
|
if( r < 0 )
|
|
{
|
|
if( tempVariables.Exists(offset) )
|
|
Error(TXT_FAILED_TO_CREATE_TEMP_OBJ, node);
|
|
return;
|
|
}
|
|
|
|
tmpBC.AddCode(bc);
|
|
bc->AddCode(&tmpBC);
|
|
|
|
// Assign the evaluated expression to the temporary variable
|
|
PrepareForAssignment(&dt, arg, node, true);
|
|
bc->AddCode(&arg->bc);
|
|
|
|
// Call the opAssign method to assign the value to the temporary object
|
|
dt.MakeReference(isObjectOnHeap);
|
|
asCExprValue type;
|
|
type.Set(dt);
|
|
type.isTemporary = true;
|
|
type.stackOffset = (short)offset;
|
|
|
|
if( dt.IsObjectHandle() )
|
|
type.isExplicitHandle = true;
|
|
|
|
bc->InstrSHORT(asBC_PSF, (short)offset);
|
|
if( derefDestination )
|
|
bc->Instr(asBC_RDSPtr);
|
|
|
|
r = PerformAssignment(&type, &arg->type, bc, node);
|
|
if( r < 0 )
|
|
{
|
|
if( tempVariables.Exists(offset) )
|
|
Error(TXT_FAILED_TO_CREATE_TEMP_OBJ, node);
|
|
return;
|
|
}
|
|
|
|
// Pop the reference that was pushed on the stack if the result is an object
|
|
if( type.dataType.IsObject() || type.dataType.IsFuncdef() )
|
|
bc->Instr(asBC_PopPtr);
|
|
|
|
// If the assignment operator returned an object by value it will
|
|
// be in a temporary variable which we need to destroy now
|
|
if( type.isTemporary && type.stackOffset != (short)offset )
|
|
ReleaseTemporaryVariable(type.stackOffset, bc);
|
|
|
|
// Release the original value too in case it is a temporary
|
|
ReleaseTemporaryVariable(arg->type, bc);
|
|
}
|
|
}
|
|
|
|
int asCCompiler::PrepareArgument(asCDataType *paramType, asCExprContext *ctx, asCScriptNode *node, bool isFunction, int refType, bool isMakingCopy)
|
|
{
|
|
asCDataType param = *paramType;
|
|
if( paramType->GetTokenType() == ttQuestion )
|
|
{
|
|
// The function is expecting a var type. If the argument is a function name, we must now decide which function it is
|
|
DetermineSingleFunc(ctx, node);
|
|
|
|
// 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 || ctx->type.IsNullConstant());
|
|
|
|
// Treat the void expression like a null handle when working with var types
|
|
if( ctx->IsVoidExpression() )
|
|
param = asCDataType::CreateNullHandle();
|
|
|
|
// If value assign is disabled for reference types, then make
|
|
// sure to always pass the handle to ? parameters
|
|
if( builder->engine->ep.disallowValueAssignForRefType &&
|
|
ctx->type.dataType.GetTypeInfo() && (ctx->type.dataType.GetTypeInfo()->flags & asOBJ_REF) && !(ctx->type.dataType.GetTypeInfo()->flags & asOBJ_SCOPED) )
|
|
{
|
|
param.MakeHandle(true);
|
|
}
|
|
|
|
param.MakeReference(paramType->IsReference());
|
|
param.MakeReadOnly(paramType->IsReadOnly());
|
|
}
|
|
else
|
|
param = *paramType;
|
|
|
|
asCDataType dt = param;
|
|
|
|
// Need to protect arguments by reference
|
|
if( isFunction && dt.IsReference() )
|
|
{
|
|
// Allocate a temporary variable of the same type as the argument
|
|
dt.MakeReference(false);
|
|
|
|
int offset;
|
|
if( refType == asTM_INREF )
|
|
{
|
|
ProcessPropertyGetAccessor(ctx, node);
|
|
|
|
// Add the type id as hidden arg if the parameter is a ? type
|
|
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);
|
|
}
|
|
|
|
if( dt.IsPrimitive() )
|
|
{
|
|
// 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
|
|
IsVariableInitialized(&ctx->type, node);
|
|
|
|
if( ctx->type.dataType.IsReference() ) ConvertToVariable(ctx);
|
|
ImplicitConversion(ctx, dt, node, asIC_IMPLICIT_CONV, true);
|
|
|
|
if( !(param.IsReadOnly() && ctx->type.isVariable) )
|
|
ConvertToTempVariable(ctx);
|
|
|
|
PushVariableOnStack(ctx, true);
|
|
ctx->type.dataType.MakeReadOnly(param.IsReadOnly());
|
|
}
|
|
else if( ctx->type.dataType.IsNullHandle() )
|
|
{
|
|
// Make sure the argument type can support handles (or is itself a handle)
|
|
if( !dt.SupportHandles() && !dt.IsObjectHandle() )
|
|
{
|
|
asCString str;
|
|
str.Format(TXT_CANT_IMPLICITLY_CONVERT_s_TO_s, ctx->type.dataType.Format(outFunc->nameSpace).AddressOf(), param.Format(outFunc->nameSpace).AddressOf());
|
|
Error(str, node);
|
|
|
|
ctx->type.Set(param);
|
|
return -1;
|
|
}
|
|
|
|
// Need to initialize a local temporary variable to
|
|
// represent the null handle when passed as reference
|
|
asASSERT( ctx->bc.GetLastInstr() == asBC_PshNull );
|
|
ctx->bc.Instr(asBC_PopPtr);
|
|
|
|
dt.MakeHandle(true);
|
|
dt.MakeReadOnly(false);
|
|
offset = AllocateVariableNotIn(dt, true, false, ctx);
|
|
|
|
// Push the reference to the variable on the stack
|
|
ctx->bc.InstrWORD(asBC_PSF, (short)offset);
|
|
|
|
ctx->type.SetVariable(dt, offset, true);
|
|
ctx->type.isExplicitHandle = true;
|
|
}
|
|
else
|
|
{
|
|
IsVariableInitialized(&ctx->type, node);
|
|
|
|
if( !isMakingCopy )
|
|
{
|
|
// For parameters expecting a reference to a handle we need to make sure the argument
|
|
// is really a handle, and not just a reference to the object. Do this check before the
|
|
// implicit conversion so it can be treated correctly.
|
|
if (dt.IsObjectHandle() && !ctx->type.dataType.IsObjectHandle())
|
|
{
|
|
// Make a refCopy into a local handle variable
|
|
// Allocate a handle variable
|
|
dt.MakeHandle(true);
|
|
dt.MakeReadOnly(false);
|
|
offset = AllocateVariableNotIn(dt, true, false, ctx);
|
|
|
|
// Copy the handle
|
|
Dereference(ctx, true);
|
|
ctx->bc.InstrWORD(asBC_PSF, (asWORD)offset);
|
|
if (ctx->type.dataType.IsFuncdef())
|
|
ctx->bc.InstrPTR(asBC_REFCPY, &engine->functionBehaviours);
|
|
else
|
|
ctx->bc.InstrPTR(asBC_REFCPY, ctx->type.dataType.GetTypeInfo());
|
|
ctx->bc.Instr(asBC_PopPtr);
|
|
ctx->bc.InstrWORD(asBC_PSF, (asWORD)offset);
|
|
|
|
// Release the original temporary variable
|
|
if( ctx->type.isTemporary )
|
|
ReleaseTemporaryVariable(ctx->type.stackOffset, &ctx->bc);
|
|
|
|
ctx->type.SetVariable(dt, offset, true);
|
|
}
|
|
|
|
// Even though the parameter expects a reference, it is only meant to be
|
|
// used as input value and doesn't have to refer to the actual object, so it
|
|
// is OK to do an implicit conversion.
|
|
ImplicitConversion(ctx, dt, node, asIC_IMPLICIT_CONV, true);
|
|
if( !ctx->type.dataType.IsEqualExceptRefAndConst(param) )
|
|
{
|
|
asCString str;
|
|
str.Format(TXT_CANT_IMPLICITLY_CONVERT_s_TO_s, ctx->type.dataType.Format(outFunc->nameSpace).AddressOf(), param.Format(outFunc->nameSpace).AddressOf());
|
|
Error(str, node);
|
|
|
|
ctx->type.Set(param);
|
|
return -1;
|
|
}
|
|
|
|
// The compiler must guarantee that the object stays alive during the execution
|
|
// of the function, and it must also guarantee that the value isn't modified by
|
|
// the function.
|
|
|
|
// If the argument is a temporary local variable then it is safe to be passed to
|
|
// the function as it is, since the local variable will stay alive, and since it
|
|
// is temporary there is no side effect if the function modifies it.
|
|
|
|
// If the parameter is read-only and therefore guaranteed not to be modified by the
|
|
// function, then it is enough that the variable is local to guarantee the lifetime.
|
|
if( !ctx->type.isTemporary && !(param.IsReadOnly() && (ctx->type.isVariable || ctx->type.isRefSafe)) )
|
|
{
|
|
if( ctx->type.dataType.IsFuncdef() || ((ctx->type.dataType.GetTypeInfo()->flags & asOBJ_REF) && param.IsReadOnly() && !(ctx->type.dataType.GetTypeInfo()->flags & asOBJ_SCOPED)) )
|
|
{
|
|
// Funcdefs only need an extra handle to guarantee the lifetime.
|
|
|
|
// If the object is a reference type (except scoped reference types), and the
|
|
// parameter is a const reference, then it is not necessary to make a copy of the
|
|
// object. The compiler just needs to hold a handle to guarantee the lifetime.
|
|
|
|
// Allocate a handle variable
|
|
dt.MakeHandle(true);
|
|
dt.MakeReadOnly(false);
|
|
offset = AllocateVariableNotIn(dt, true, false, ctx);
|
|
|
|
// Copy the handle
|
|
Dereference(ctx, true);
|
|
ctx->bc.InstrWORD(asBC_PSF, (asWORD)offset);
|
|
if (ctx->type.dataType.IsFuncdef())
|
|
ctx->bc.InstrPTR(asBC_REFCPY, &engine->functionBehaviours);
|
|
else
|
|
ctx->bc.InstrPTR(asBC_REFCPY, ctx->type.dataType.GetTypeInfo());
|
|
ctx->bc.Instr(asBC_PopPtr);
|
|
ctx->bc.InstrWORD(asBC_PSF, (asWORD)offset);
|
|
|
|
// The type should be set to the param type instead of dt to guarantee
|
|
// that the expression keeps the correct type for variable ? args. Otherwise
|
|
// MoveArgsToStack will use the wrong bytecode to move the arg to the stack
|
|
bool isExplicitHandle = ctx->type.isExplicitHandle;
|
|
ctx->type.SetVariable(param, offset, true);
|
|
ctx->type.dataType.MakeHandle(true);
|
|
ctx->type.isExplicitHandle = isExplicitHandle;
|
|
}
|
|
else
|
|
{
|
|
// Make a copy of the object to guarantee that the original isn't modified
|
|
asASSERT(!dt.IsFuncdef());
|
|
|
|
// Allocate and initialize a temporary local object
|
|
dt.MakeReadOnly(false);
|
|
offset = AllocateVariableNotIn(dt, true, false, ctx);
|
|
CompileInitAsCopy(dt, offset, &ctx->bc, ctx, node, false);
|
|
|
|
// Push the object pointer on the stack
|
|
ctx->bc.InstrSHORT(asBC_PSF, (short)offset);
|
|
if( dt.IsObject() && !dt.IsObjectHandle() )
|
|
ctx->bc.Instr(asBC_RDSPtr);
|
|
|
|
// Set the resulting type
|
|
ctx->type.Set(dt);
|
|
ctx->type.isTemporary = true;
|
|
ctx->type.stackOffset = short(offset);
|
|
if( dt.IsObjectHandle() )
|
|
ctx->type.isExplicitHandle = true;
|
|
ctx->type.dataType.MakeReference(false);
|
|
if( paramType->IsReadOnly() )
|
|
ctx->type.dataType.MakeReadOnly(true);
|
|
}
|
|
}
|
|
|
|
// When calling a function expecting a var arg with a parameter received as reference to handle
|
|
// then it is necessary to copy the handle to a local variable, otherwise MoveArgsToStack will
|
|
// not be able to do the correct double dereference to put the reference to the object on the stack.
|
|
if (paramType->GetTokenType() == ttQuestion && !param.IsObjectHandle() && ctx->type.isVariable)
|
|
{
|
|
sVariable *var = variables->GetVariableByOffset(ctx->type.stackOffset);
|
|
if (var && var->type.IsReference() && var->type.IsObjectHandle())
|
|
{
|
|
// Copy the handle to local variable
|
|
|
|
// Allocate a handle variable
|
|
dt.MakeHandle(true);
|
|
dt.MakeReadOnly(false);
|
|
offset = AllocateVariableNotIn(dt, true, false, ctx);
|
|
|
|
// Copy the handle
|
|
Dereference(ctx, true);
|
|
ctx->bc.InstrWORD(asBC_PSF, (asWORD)offset);
|
|
if (ctx->type.dataType.IsFuncdef())
|
|
ctx->bc.InstrPTR(asBC_REFCPY, &engine->functionBehaviours);
|
|
else
|
|
ctx->bc.InstrPTR(asBC_REFCPY, ctx->type.dataType.GetTypeInfo());
|
|
ctx->bc.Instr(asBC_PopPtr);
|
|
ctx->bc.InstrWORD(asBC_PSF, (asWORD)offset);
|
|
|
|
// The type should be set to the param type instead of dt to guarantee
|
|
// that the expression keeps the correct type for variable ? args. Otherwise
|
|
// MoveArgsToStack will use the wrong bytecode to move the arg to the stack
|
|
ctx->type.SetVariable(param, offset, true);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// We must guarantee that the address to the value is on the stack
|
|
if( (ctx->type.dataType.IsObject() || ctx->type.dataType.IsFuncdef()) &&
|
|
!ctx->type.dataType.IsObjectHandle() &&
|
|
ctx->type.dataType.IsReference() )
|
|
Dereference(ctx, true);
|
|
}
|
|
}
|
|
}
|
|
else if( refType == asTM_OUTREF )
|
|
{
|
|
// Add the type id as hidden arg if the parameter is a ? type
|
|
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);
|
|
}
|
|
|
|
// If the expression is marked as clean, then it can be used directly
|
|
// without the need to allocate another temporary value as it is known
|
|
// that the argument has no other value than the default
|
|
if( ctx->isCleanArg )
|
|
{
|
|
// Must be a local variable
|
|
asASSERT( ctx->type.isVariable );
|
|
}
|
|
else
|
|
{
|
|
// Null handles and void expressions must be marked as explicit
|
|
// handles for correct treatement in MoveArgsToStack
|
|
if (dt.IsNullHandle())
|
|
ctx->type.isExplicitHandle = true;
|
|
|
|
// Make sure the variable is not used in the expression
|
|
dt.MakeReadOnly(false);
|
|
offset = AllocateVariableNotIn(dt, true, false, ctx);
|
|
|
|
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, IsVariableOnHeap(offset), &tmpBC, node);
|
|
|
|
// Insert the code before the expression code
|
|
tmpBC.AddCode(&ctx->bc);
|
|
ctx->bc.AddCode(&tmpBC);
|
|
|
|
dt.MakeReference(!(dt.IsObject() || dt.IsFuncdef()) || dt.IsObjectHandle());
|
|
asCExprValue type;
|
|
type.Set(dt);
|
|
type.isTemporary = true;
|
|
type.stackOffset = (short)offset;
|
|
|
|
type.isExplicitHandle = ctx->type.isExplicitHandle;
|
|
ctx->type = type;
|
|
|
|
ctx->bc.InstrSHORT(asBC_PSF, (short)offset);
|
|
if( (dt.IsObject() || dt.IsFuncdef()) && !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 )
|
|
{
|
|
ProcessPropertyGetAccessor(ctx, node);
|
|
|
|
// Add the type id as hidden arg if the parameter is a ? type
|
|
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);
|
|
}
|
|
|
|
// Literal constants cannot be passed to inout ref arguments
|
|
if( !ctx->type.isVariable &&
|
|
ctx->type.isConstant &&
|
|
!ctx->type.dataType.IsEqualExceptRefAndConst(engine->stringType) )
|
|
{
|
|
// Unless unsafe references are turned on and the reference is const
|
|
if( param.IsReadOnly() && engine->ep.allowUnsafeReferences )
|
|
{
|
|
// Since the parameter is a const & make a copy.
|
|
ConvertToTempVariable(ctx);
|
|
ctx->type.dataType.MakeReadOnly(true);
|
|
}
|
|
else
|
|
{
|
|
Error(TXT_NOT_VALID_REFERENCE, node);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
// Perform implicit ref cast if necessary, but don't allow the implicit conversion to create new objects
|
|
if( (ctx->type.dataType.IsObject() || ctx->type.dataType.IsFuncdef()) && ctx->type.dataType.GetTypeInfo() != dt.GetTypeInfo() )
|
|
ImplicitConversion(ctx, dt, node, asIC_IMPLICIT_CONV, true, false);
|
|
|
|
// 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.IsFuncdef()) &&
|
|
!ctx->type.dataType.IsObjectHandle() &&
|
|
((ctx->type.dataType.GetBehaviour()->addref &&
|
|
ctx->type.dataType.GetBehaviour()->release) ||
|
|
(ctx->type.dataType.GetTypeInfo()->flags & asOBJ_NOCOUNT) ||
|
|
ctx->type.dataType.IsFuncdef()) )
|
|
{
|
|
// Store a handle to the object as local variable
|
|
asCExprContext tmp(engine);
|
|
dt = ctx->type.dataType;
|
|
dt.MakeHandle(true);
|
|
dt.MakeReference(false);
|
|
dt.MakeReadOnly(false);
|
|
|
|
offset = AllocateVariableNotIn(dt, true, false, ctx);
|
|
|
|
// Copy the handle
|
|
if( !ctx->type.dataType.IsObjectHandle() && ctx->type.dataType.IsReference() )
|
|
ctx->bc.Instr(asBC_RDSPtr);
|
|
ctx->bc.InstrWORD(asBC_PSF, (asWORD)offset);
|
|
if( ctx->type.dataType.IsFuncdef() )
|
|
ctx->bc.InstrPTR(asBC_REFCPY, &engine->functionBehaviours);
|
|
else
|
|
ctx->bc.InstrPTR(asBC_REFCPY, ctx->type.dataType.GetTypeInfo());
|
|
ctx->bc.Instr(asBC_PopPtr);
|
|
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
|
|
// For objects, the reference needs to be dereferenced so the pointer on the stack is to the actual object
|
|
// For handles, the reference shouldn't be changed because the pointer on the stack should be to the handle
|
|
if( (ctx->type.dataType.IsObject() || ctx->type.dataType.IsFuncdef()) && ctx->type.dataType.IsReference() && !param.IsObjectHandle() )
|
|
Dereference(ctx, true);
|
|
else if( ctx->type.isVariable && !(ctx->type.dataType.IsObject() || ctx->type.dataType.IsFuncdef()) )
|
|
ctx->bc.InstrSHORT(asBC_PSF, ctx->type.stackOffset);
|
|
else if( ctx->type.dataType.IsPrimitive() )
|
|
ctx->bc.Instr(asBC_PshRPtr);
|
|
else if( ctx->type.dataType.IsObjectHandle() && !ctx->type.dataType.IsReference() )
|
|
ImplicitConversion(ctx, param, node, asIC_IMPLICIT_CONV, true, false);
|
|
}
|
|
}
|
|
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);
|
|
|
|
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);
|
|
|
|
// 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(outFunc->nameSpace).AddressOf(), dt.Format(outFunc->nameSpace).AddressOf());
|
|
Error(str, node);
|
|
|
|
ctx->type.Set(dt);
|
|
return -1;
|
|
}
|
|
|
|
if( dt.IsObjectHandle() )
|
|
ctx->type.isExplicitHandle = true;
|
|
|
|
if( (dt.IsObject() || dt.IsFuncdef()) && !dt.IsNullHandle() && !dt.IsReference() )
|
|
{
|
|
// Objects passed by value must be placed in temporary variables
|
|
// so that they are guaranteed to not be referenced anywhere else.
|
|
// The object must also be allocated on the heap, as the memory will
|
|
// be deleted by the called function.
|
|
|
|
// Handles passed by value must also be placed in a temporary variable
|
|
// to guarantee that the object referred to isn't freed too early.
|
|
|
|
// TODO: value on stack: How can we avoid this unnecessary allocation?
|
|
|
|
// Don't make temporary copies of handles if it is going to be used
|
|
// for handle assignment anyway, i.e. REFCPY.
|
|
if( !(!isFunction && isMakingCopy && ctx->type.dataType.IsObjectHandle() && ctx->type.isVariable) )
|
|
PrepareTemporaryVariable(node, ctx, true);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Don't put any pointer on the stack yet
|
|
if( param.IsReference() || ((param.IsObject() || param.IsFuncdef()) && !param.IsNullHandle()) )
|
|
{
|
|
// &inout parameter may leave the reference on the stack already
|
|
// references considered safe too, i.e. when the life time is known
|
|
if( refType != asTM_INOUTREF && !ctx->type.isRefSafe )
|
|
{
|
|
asASSERT( ctx->type.isVariable || ctx->type.isRefSafe || ctx->type.isTemporary || isMakingCopy );
|
|
|
|
if( ctx->type.isVariable || ctx->type.isTemporary )
|
|
{
|
|
ctx->bc.Instr(asBC_PopPtr);
|
|
ctx->bc.InstrSHORT(asBC_VAR, ctx->type.stackOffset);
|
|
|
|
ProcessDeferredParams(ctx);
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int asCCompiler::PrepareFunctionCall(int funcId, asCByteCode *bc, asCArray<asCExprContext *> &args)
|
|
{
|
|
// When a match has been found, compile the final byte code using correct parameter types
|
|
asCScriptFunction *descr = builder->GetFunctionDescription(funcId);
|
|
|
|
asASSERT( descr->parameterTypes.GetLength() == args.GetLength() );
|
|
|
|
// If the function being called is the opAssign or copy constructor for the same type
|
|
// as the argument, then we should avoid making temporary copy of the argument
|
|
bool makingCopy = false;
|
|
if( descr->parameterTypes.GetLength() == 1 &&
|
|
descr->parameterTypes[0].IsEqualExceptRefAndConst(args[0]->type.dataType) &&
|
|
(((descr->name == "opAssign" || descr->name == "$beh0") && descr->objectType && descr->objectType == args[0]->type.dataType.GetTypeInfo()) ||
|
|
(descr->objectType == 0 && args[0]->type.dataType.GetTypeInfo() && descr->name == args[0]->type.dataType.GetTypeInfo()->name)) )
|
|
makingCopy = true;
|
|
|
|
// Add code for arguments
|
|
asCExprContext e(engine);
|
|
for( int n = (int)args.GetLength()-1; n >= 0; n-- )
|
|
{
|
|
// Make sure PrepareArgument doesn't use any variable that is already
|
|
// being used by the argument or any of the following argument expressions
|
|
int l = int(reservedVariables.GetLength());
|
|
for( int m = n; m >= 0; m-- )
|
|
args[m]->bc.GetVarsUsed(reservedVariables);
|
|
|
|
int r = PrepareArgument2(&e, args[n], &descr->parameterTypes[n], true, descr->inOutFlags[n], makingCopy);
|
|
reservedVariables.SetLength(l);
|
|
|
|
if (r < 0)
|
|
return r;
|
|
}
|
|
|
|
bc->AddCode(&e.bc);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void asCCompiler::MoveArgsToStack(int funcId, asCByteCode *bc, asCArray<asCExprContext *> &args, bool addOneToOffset)
|
|
{
|
|
asCScriptFunction *descr = builder->GetFunctionDescription(funcId);
|
|
|
|
int offset = 0;
|
|
if( addOneToOffset )
|
|
offset += AS_PTR_SIZE;
|
|
|
|
// The address of where the return value should be stored is push on top of the arguments
|
|
if( descr->DoesReturnOnStack() )
|
|
offset += AS_PTR_SIZE;
|
|
|
|
#ifdef AS_DEBUG
|
|
// If the function being called is the opAssign or copy constructor for the same type
|
|
// as the argument, then we should avoid making temporary copy of the argument
|
|
bool makingCopy = false;
|
|
if( descr->parameterTypes.GetLength() == 1 &&
|
|
descr->parameterTypes[0].IsEqualExceptRefAndConst(args[0]->type.dataType) &&
|
|
(((descr->name == "opAssign" || descr->name == "$beh0") && descr->objectType && descr->objectType == args[0]->type.dataType.GetTypeInfo()) ||
|
|
(descr->objectType == 0 && args[0]->type.dataType.GetTypeInfo() && descr->name == args[0]->type.dataType.GetTypeInfo()->name)) )
|
|
makingCopy = true;
|
|
#endif
|
|
|
|
// 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].IsFuncdef()) && !descr->parameterTypes[n].IsObjectHandle() )
|
|
{
|
|
if( descr->inOutFlags[n] != asTM_INOUTREF && !args[n]->type.isRefSafe )
|
|
{
|
|
#ifdef AS_DEBUG
|
|
// This assert is inside AS_DEBUG because of the variable makingCopy which is only defined in debug mode
|
|
asASSERT( args[n]->type.isVariable || args[n]->type.isTemporary || makingCopy );
|
|
#endif
|
|
|
|
if( (args[n]->type.isVariable || args[n]->type.isTemporary) )
|
|
{
|
|
if( !IsVariableOnHeap(args[n]->type.stackOffset) )
|
|
// TODO: runtime optimize: Actually the reference can be pushed on the stack directly
|
|
// as the value allocated on the stack is guaranteed to be safe
|
|
bc->InstrWORD(asBC_GETREF, (asWORD)offset);
|
|
else
|
|
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 the argument is already known to be safe, i.e. has a guaranteed lifetime,
|
|
// then the address on the stack is already pointing to the correct object so no
|
|
// need to do anything else
|
|
if (!args[n]->type.isRefSafe)
|
|
{
|
|
if (descr->parameterTypes[n].GetTokenType() == ttQuestion &&
|
|
(args[n]->type.dataType.IsObject() || args[n]->type.dataType.IsFuncdef()) &&
|
|
!args[n]->type.dataType.IsObjectHandle())
|
|
{
|
|
// Send the object as a reference to the object,
|
|
// and not to the variable holding the object
|
|
if (!IsVariableOnHeap(args[n]->type.stackOffset))
|
|
// TODO: runtime optimize: Actually the reference can be pushed on the stack directly
|
|
// as the value allocated on the stack is guaranteed to be safe
|
|
bc->InstrWORD(asBC_GETREF, (asWORD)offset);
|
|
else
|
|
bc->InstrWORD(asBC_GETOBJREF, (asWORD)offset);
|
|
}
|
|
else if (descr->parameterTypes[n].GetTokenType() == ttQuestion &&
|
|
args[n]->type.dataType.IsObjectHandle() && !args[n]->type.isExplicitHandle)
|
|
{
|
|
// The object handle is being passed as an object, so dereference it before
|
|
// the call so the reference will be to the object rather than to the handle
|
|
if (engine->ep.disallowValueAssignForRefType)
|
|
{
|
|
// With disallow value assign all ref type objects are always passed by handle
|
|
bc->InstrWORD(asBC_GETREF, (asWORD)offset);
|
|
}
|
|
else
|
|
bc->InstrWORD(asBC_GETOBJREF, (asWORD)offset);
|
|
}
|
|
else
|
|
{
|
|
// If the variable is really an argument of @& type, then it is necessary
|
|
// to use asBC_GETOBJREF so the pointer is correctly dereferenced.
|
|
sVariable *var = variables->GetVariableByOffset(args[n]->type.stackOffset);
|
|
if (var == 0 || !var->type.IsReference() || !var->type.IsObjectHandle())
|
|
bc->InstrWORD(asBC_GETREF, (asWORD)offset);
|
|
else
|
|
bc->InstrWORD(asBC_GETOBJREF, (asWORD)offset);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if( descr->parameterTypes[n].IsObject() || descr->parameterTypes[n].IsFuncdef() )
|
|
{
|
|
asASSERT(!args[n]->type.isRefSafe);
|
|
|
|
// TODO: value on stack: What can we do to avoid this unnecessary allocation?
|
|
// The object must be allocated on the heap, because this memory will be deleted in as_callfunc_xxx
|
|
asASSERT(IsVariableOnHeap(args[n]->type.stackOffset));
|
|
|
|
// The pointer in the variable will be moved to the stack
|
|
bc->InstrWORD(asBC_GETOBJ, (asWORD)offset);
|
|
|
|
// Deallocate the variable slot so it can be reused, but do not attempt to
|
|
// free the content of the variable since it was moved to the stack for the call
|
|
DeallocateVariable(args[n]->type.stackOffset);
|
|
args[n]->type.isTemporary = false;
|
|
}
|
|
|
|
offset += descr->parameterTypes[n].GetSizeOnStackDWords();
|
|
}
|
|
}
|
|
|
|
int asCCompiler::CompileArgumentList(asCScriptNode *node, asCArray<asCExprContext*> &args, asCArray<asSNamedArgument> &namedArgs)
|
|
{
|
|
asASSERT(node->nodeType == snArgList);
|
|
|
|
// Count arguments
|
|
asCScriptNode *arg = node->firstChild;
|
|
int argCount = 0;
|
|
while( arg )
|
|
{
|
|
if( arg->nodeType != snNamedArgument )
|
|
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, inPositionalArguments = false;
|
|
arg = node->lastChild;
|
|
while( arg )
|
|
{
|
|
asCScriptNode *asgNode = arg, *namedNode = 0;
|
|
if( asgNode->nodeType == snNamedArgument )
|
|
{
|
|
if( inPositionalArguments )
|
|
{
|
|
Error(TXT_POS_ARG_AFTER_NAMED_ARG, node);
|
|
return -1;
|
|
}
|
|
|
|
asgNode = arg->firstChild->next;
|
|
namedNode = arg->firstChild;
|
|
|
|
asASSERT( namedNode->nodeType == snIdentifier );
|
|
}
|
|
else
|
|
inPositionalArguments = true;
|
|
|
|
asCExprContext expr(engine);
|
|
int r = CompileAssignment(asgNode, &expr);
|
|
if( r < 0 ) anyErrors = true;
|
|
|
|
asCExprContext *ctx = asNEW(asCExprContext)(engine);
|
|
if( ctx == 0 )
|
|
{
|
|
// Out of memory
|
|
return -1;
|
|
}
|
|
MergeExprBytecodeAndType(ctx, &expr);
|
|
|
|
if( inPositionalArguments )
|
|
{
|
|
args[n] = ctx;
|
|
n--;
|
|
}
|
|
else
|
|
{
|
|
asSNamedArgument namedArg;
|
|
namedArg.name = asCString(&script->code[namedNode->tokenPos], namedNode->tokenLength);
|
|
namedArg.ctx = ctx;
|
|
|
|
// Error out when multiple arguments with the same name are passed
|
|
for( asUINT a = 0; a < namedArgs.GetLength(); ++a )
|
|
{
|
|
if( namedArgs[a].name == namedArg.name )
|
|
{
|
|
Error(TXT_DUPLICATE_NAMED_ARG, asgNode);
|
|
anyErrors = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
namedArgs.PushLast(namedArg);
|
|
}
|
|
|
|
arg = arg->prev;
|
|
}
|
|
|
|
return anyErrors ? -1 : 0;
|
|
}
|
|
|
|
int asCCompiler::CompileDefaultAndNamedArgs(asCScriptNode *node, asCArray<asCExprContext*> &args, int funcId, asCObjectType *objectType, asCArray<asSNamedArgument> *namedArgs)
|
|
{
|
|
asCScriptFunction *func = builder->GetFunctionDescription(funcId);
|
|
if( func == 0 || args.GetLength() >= (asUINT)func->GetParamCount() )
|
|
return 0;
|
|
|
|
// Make sure to use the real function for virtual functions
|
|
if( func->funcType == asFUNC_VIRTUAL )
|
|
{
|
|
asASSERT( objectType );
|
|
func = objectType->virtualFunctionTable[func->vfTableIdx];
|
|
}
|
|
|
|
// Make sure none of the variables used in the previous arguments are reused in the default arguments
|
|
bool anyErrors = false;
|
|
int prevReservedVars = reservedVariables.GetLength();
|
|
|
|
int explicitArgs = (int)args.GetLength();
|
|
|
|
for( int p = 0; p < explicitArgs; p++ )
|
|
args[p]->bc.GetVarsUsed(reservedVariables);
|
|
|
|
// Make space for all the new arguments
|
|
args.SetLength(func->parameterTypes.GetLength());
|
|
for( asUINT c = explicitArgs; c < args.GetLength(); c++ )
|
|
args[c] = 0;
|
|
|
|
// Add the named arguments to the argument list in the right position
|
|
if( namedArgs )
|
|
{
|
|
for( asUINT n = 0; n < namedArgs->GetLength(); ++n )
|
|
{
|
|
asSNamedArgument &named = (*namedArgs)[n];
|
|
named.ctx->bc.GetVarsUsed(reservedVariables);
|
|
|
|
// Find the right spot to put it in
|
|
asUINT index = asUINT(-1);
|
|
for( asUINT j = 0; j < func->parameterTypes.GetLength(); ++j )
|
|
{
|
|
if( func->parameterNames[j] == (*namedArgs)[n].name )
|
|
{
|
|
index = j;
|
|
break;
|
|
}
|
|
}
|
|
|
|
asASSERT( index < args.GetLength() );
|
|
args[index] = named.ctx;
|
|
named.ctx = 0;
|
|
}
|
|
}
|
|
|
|
// Compile the arguments in reverse order (as they will be pushed on the stack)
|
|
for( int n = (int)func->parameterTypes.GetLength() - 1; n >= explicitArgs; n-- )
|
|
{
|
|
if( args[n] != 0 ) continue;
|
|
if( func->defaultArgs[n] == 0 ) { anyErrors = true; continue; }
|
|
|
|
// Parse the default arg string
|
|
asCParser parser(builder);
|
|
asCScriptCode *code = builder->FindOrAddCode("default arg", func->defaultArgs[n]->AddressOf(), func->defaultArgs[n]->GetLength());
|
|
int r = parser.ParseExpression(code);
|
|
if( r < 0 )
|
|
{
|
|
asCString msg;
|
|
msg.Format(TXT_FAILED_TO_COMPILE_DEF_ARG_d_IN_FUNC_s, n, func->GetDeclaration());
|
|
Error(msg, node);
|
|
anyErrors = true;
|
|
continue;
|
|
}
|
|
|
|
asCScriptNode *arg = parser.GetScriptNode();
|
|
|
|
// Temporarily set the script code to the default arg expression
|
|
asCScriptCode *origScript = script;
|
|
script = code;
|
|
|
|
// Don't allow the expression to access local variables
|
|
isCompilingDefaultArg = true;
|
|
|
|
// Temporarily set the namespace in the output function to the namespace of the called
|
|
// function so that the default arguments are evaluated in the correct namespace
|
|
asSNameSpace *origNameSpace = outFunc->nameSpace;
|
|
outFunc->nameSpace = func->nameSpace;
|
|
|
|
asCExprContext expr(engine);
|
|
r = CompileExpression(arg, &expr);
|
|
|
|
// Restore the namespace
|
|
outFunc->nameSpace = origNameSpace;
|
|
|
|
// Don't allow address of class method
|
|
if( expr.IsClassMethod() )
|
|
{
|
|
// TODO: Improve error message
|
|
Error(TXT_DEF_ARG_TYPE_DOESNT_MATCH, arg);
|
|
r = -1;
|
|
}
|
|
|
|
// Make sure the expression can be implicitly converted to the parameter type
|
|
if( r >= 0 )
|
|
{
|
|
asCArray<int> funcs;
|
|
funcs.PushLast(func->id);
|
|
asCArray<asSOverloadCandidate> matches;
|
|
if( MatchArgument(funcs, matches, &expr, n) == 0 )
|
|
{
|
|
Error(TXT_DEF_ARG_TYPE_DOESNT_MATCH, arg);
|
|
r = -1;
|
|
}
|
|
}
|
|
|
|
isCompilingDefaultArg = false;
|
|
|
|
script = origScript;
|
|
|
|
if( r < 0 )
|
|
{
|
|
asCString msg;
|
|
msg.Format(TXT_FAILED_TO_COMPILE_DEF_ARG_d_IN_FUNC_s, n, func->GetDeclaration());
|
|
Error(msg, node);
|
|
anyErrors = true;
|
|
continue;
|
|
}
|
|
|
|
args[n] = asNEW(asCExprContext)(engine);
|
|
if( args[n] == 0 )
|
|
{
|
|
// Out of memory
|
|
reservedVariables.SetLength(prevReservedVars);
|
|
return -1;
|
|
}
|
|
|
|
MergeExprBytecodeAndType(args[n], &expr);
|
|
}
|
|
|
|
reservedVariables.SetLength(prevReservedVars);
|
|
return anyErrors ? -1 : 0;
|
|
}
|
|
|
|
asUINT asCCompiler::MatchFunctions(asCArray<int> &funcs, asCArray<asCExprContext*> &args, asCScriptNode *node, const char *name, asCArray<asSNamedArgument> *namedArgs, asCObjectType *objectType, bool isConstMethod, bool silent, bool allowObjectConstruct, const asCString &scope)
|
|
{
|
|
asCArray<int> origFuncs = funcs; // Keep the original list for error message
|
|
asUINT cost = 0;
|
|
asUINT n;
|
|
|
|
if( funcs.GetLength() > 0 )
|
|
{
|
|
// Check the number of parameters in the found functions
|
|
asUINT totalArgs = (asUINT)args.GetLength();
|
|
if( namedArgs != 0 )
|
|
totalArgs += (asUINT)namedArgs->GetLength();
|
|
|
|
for( n = 0; n < funcs.GetLength(); ++n )
|
|
{
|
|
asCScriptFunction *desc = builder->GetFunctionDescription(funcs[n]);
|
|
|
|
if( desc->parameterTypes.GetLength() != totalArgs )
|
|
{
|
|
bool noMatch = true;
|
|
if( totalArgs < desc->parameterTypes.GetLength() )
|
|
{
|
|
// For virtual functions, the default args are defined in the real function of the object
|
|
if( desc->funcType == asFUNC_VIRTUAL )
|
|
desc = objectType->virtualFunctionTable[desc->vfTableIdx];
|
|
|
|
// Count the number of default args
|
|
asUINT defaultArgs = 0;
|
|
for( asUINT d = 0; d < desc->defaultArgs.GetLength(); d++ )
|
|
if( desc->defaultArgs[d] )
|
|
defaultArgs++;
|
|
|
|
if( totalArgs >= desc->parameterTypes.GetLength() - defaultArgs )
|
|
noMatch = false;
|
|
}
|
|
|
|
if( noMatch )
|
|
{
|
|
// 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<asSOverloadCandidate> matchingFuncs;
|
|
matchingFuncs.SetLengthNoConstruct( funcs.GetLength() );
|
|
for ( n = 0; n < funcs.GetLength(); ++n )
|
|
{
|
|
matchingFuncs[n].funcId = funcs[n];
|
|
matchingFuncs[n].cost = 0;
|
|
}
|
|
|
|
// Match positionally passed arguments
|
|
for( n = 0; n < args.GetLength(); ++n )
|
|
{
|
|
asCArray<asSOverloadCandidate> tempFuncs;
|
|
MatchArgument(funcs, tempFuncs, args[n], 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].funcId == tempFuncs[c].funcId )
|
|
{
|
|
// Sum argument cost
|
|
matchingFuncs[f].cost += tempFuncs[c].cost;
|
|
break;
|
|
|
|
} // End if match
|
|
}
|
|
|
|
// 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--;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Match named arguments
|
|
if( namedArgs != 0 )
|
|
{
|
|
for( asUINT i = 0; i < matchingFuncs.GetLength(); ++i )
|
|
{
|
|
asCScriptFunction *desc = builder->GetFunctionDescription(matchingFuncs[i].funcId);
|
|
if( desc->funcType == asFUNC_VIRTUAL )
|
|
desc = objectType->virtualFunctionTable[desc->vfTableIdx];
|
|
|
|
// Match every named argument to an argument in the function
|
|
for( n = 0; n < namedArgs->GetLength(); ++n )
|
|
(*namedArgs)[n].match = asUINT(-1);
|
|
|
|
bool matchedAll = true;
|
|
for( asUINT j = 0; j < desc->parameterTypes.GetLength(); ++j )
|
|
{
|
|
asUINT match = asUINT(-1);
|
|
for( n = 0; n < namedArgs->GetLength(); ++n )
|
|
{
|
|
asSNamedArgument &namedArg = (*namedArgs)[n];
|
|
if( desc->parameterNames[j] == namedArg.name )
|
|
{
|
|
namedArg.match = j;
|
|
match = n;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Check that every position is filled somehow
|
|
if( j >= args.GetLength() )
|
|
{
|
|
if( match == asUINT(-1) && !desc->defaultArgs[j] )
|
|
{
|
|
// No argument was found for this, and there is no
|
|
// default, so it doesn't work.
|
|
matchedAll = false;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( match != asUINT(-1) )
|
|
{
|
|
// Can't name an argument that was already passed
|
|
matchedAll = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check that every named argument was matched
|
|
if( matchedAll )
|
|
{
|
|
for( n = 0; n < namedArgs->GetLength(); ++n )
|
|
{
|
|
asSNamedArgument &named = (*namedArgs)[n];
|
|
|
|
if( named.match == asUINT(-1) )
|
|
{
|
|
matchedAll = false;
|
|
break;
|
|
}
|
|
|
|
// Add to the cost
|
|
cost = MatchArgument(desc, named.ctx, named.match, allowObjectConstruct);
|
|
if( cost == asUINT(-1) )
|
|
{
|
|
matchedAll = false;
|
|
break;
|
|
}
|
|
|
|
matchingFuncs[i].cost += cost;
|
|
}
|
|
}
|
|
|
|
if( !matchedAll )
|
|
{
|
|
// Remove the function, we didn't match all the arguments.
|
|
if( i == matchingFuncs.GetLength()-1 )
|
|
matchingFuncs.PopLast();
|
|
else
|
|
matchingFuncs[i] = matchingFuncs.PopLast();
|
|
i--;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Select the overload(s) with the lowest overall cost
|
|
funcs.SetLength(0);
|
|
asUINT bestCost = asUINT(-1);
|
|
for( n = 0; n < matchingFuncs.GetLength(); ++n )
|
|
{
|
|
cost = matchingFuncs[n].cost;
|
|
if( cost < bestCost )
|
|
{
|
|
funcs.SetLength(0);
|
|
bestCost = cost;
|
|
}
|
|
if( cost == bestCost )
|
|
funcs.PushLast( matchingFuncs[n].funcId );
|
|
}
|
|
|
|
// Cost returned is equivalent to the best cost discovered
|
|
cost = bestCost;
|
|
}
|
|
|
|
if( !isConstMethod )
|
|
FilterConst(funcs);
|
|
|
|
if( funcs.GetLength() != 1 && !silent )
|
|
{
|
|
// Build a readable string of the function with parameter types
|
|
bool attemptsPassingClassMethod = false;
|
|
asCString str;
|
|
if( scope != "" && scope != "::" )
|
|
str = scope + "::";
|
|
str += name;
|
|
str += "(";
|
|
for( n = 0; n < args.GetLength(); n++ )
|
|
{
|
|
if( n > 0 )
|
|
str += ", ";
|
|
if( args[n]->methodName != "" )
|
|
{
|
|
if( args[n]->IsClassMethod() )
|
|
{
|
|
attemptsPassingClassMethod = true;
|
|
str += args[n]->type.dataType.GetTypeInfo()->GetName();
|
|
str += "::";
|
|
}
|
|
str += args[n]->methodName;
|
|
}
|
|
else if (args[n]->IsAnonymousInitList())
|
|
{
|
|
str += "{...}";
|
|
}
|
|
else
|
|
str += args[n]->type.dataType.Format(outFunc->nameSpace);
|
|
}
|
|
if( namedArgs != 0 )
|
|
{
|
|
for( n = 0; n < namedArgs->GetLength(); n++ )
|
|
{
|
|
if( n > 0 || args.GetLength() )
|
|
str += ", ";
|
|
|
|
asSNamedArgument &named = (*namedArgs)[n];
|
|
str += named.name;
|
|
str += ": ";
|
|
if( named.ctx->methodName != "" )
|
|
str += named.ctx->methodName;
|
|
else
|
|
str += named.ctx->type.dataType.Format(outFunc->nameSpace);
|
|
}
|
|
}
|
|
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, node);
|
|
|
|
if( attemptsPassingClassMethod )
|
|
{
|
|
// Class methods must use delegate objects
|
|
Error(TXT_CANNOT_PASS_CLASS_METHOD_AS_ARG, node);
|
|
}
|
|
else
|
|
{
|
|
// Print the list of candidates
|
|
if( origFuncs.GetLength() > 0 )
|
|
{
|
|
int r = 0, c = 0;
|
|
asASSERT( node );
|
|
if( node ) script->ConvertPosToRowCol(node->tokenPos, &r, &c);
|
|
builder->WriteInfo(script->name.AddressOf(), TXT_CANDIDATES_ARE, r, c, false);
|
|
PrintMatchingFuncs(origFuncs, node, objectType);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
asASSERT( attemptsPassingClassMethod == false );
|
|
|
|
str.Format(TXT_MULTIPLE_MATCHING_SIGNATURES_TO_s, str.AddressOf());
|
|
Error(str, node);
|
|
|
|
PrintMatchingFuncs(funcs, node, objectType);
|
|
}
|
|
}
|
|
|
|
return cost;
|
|
}
|
|
|
|
bool asCCompiler::CompileAutoType(asCDataType &type, asCExprContext &compiledCtx, asCScriptNode *node, asCScriptNode *errNode)
|
|
{
|
|
if( node && node->nodeType == snAssignment )
|
|
{
|
|
int r = CompileAssignment(node, &compiledCtx);
|
|
if( r >= 0 )
|
|
{
|
|
// Must not have unused ambiguous names
|
|
if (compiledCtx.IsClassMethod() || compiledCtx.IsGlobalFunc())
|
|
{
|
|
// TODO: Should mention that the problem is the ambiguous name
|
|
Error(TXT_CANNOT_RESOLVE_AUTO, errNode);
|
|
return false;
|
|
}
|
|
|
|
// Must not have unused anonymous functions
|
|
if (compiledCtx.IsLambda())
|
|
{
|
|
// TODO: Should mention that the problem is the anonymous function
|
|
Error(TXT_CANNOT_RESOLVE_AUTO, errNode);
|
|
return false;
|
|
}
|
|
|
|
// Must not be a null handle
|
|
if (compiledCtx.type.dataType.IsNullHandle())
|
|
{
|
|
// TODO: Should mention that the problem is the null pointer
|
|
Error(TXT_CANNOT_RESOLVE_AUTO, errNode);
|
|
return false;
|
|
}
|
|
|
|
asCDataType newType = compiledCtx.type.dataType;
|
|
|
|
// Handle const qualifier on auto
|
|
if (type.IsReadOnly())
|
|
newType.MakeReadOnly(true);
|
|
else if (newType.IsPrimitive())
|
|
newType.MakeReadOnly(false);
|
|
|
|
// Handle reference/value stuff
|
|
newType.MakeReference(false);
|
|
if (!newType.IsObjectHandle())
|
|
{
|
|
// We got a value object or an object reference.
|
|
// Turn the variable into a handle if specified
|
|
// as auto@, otherwise make it a 'value'.
|
|
if (type.IsHandleToAuto())
|
|
{
|
|
if (newType.MakeHandle(true) < 0)
|
|
{
|
|
Error(TXT_OBJECT_HANDLE_NOT_SUPPORTED, errNode);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Implicit handle types should always be handles
|
|
if (newType.GetTypeInfo() &&
|
|
(newType.GetTypeInfo()->flags & asOBJ_IMPLICIT_HANDLE))
|
|
newType.MakeHandle(true);
|
|
|
|
// For types that support handles auto should prefer handle
|
|
// as it is more efficient than making a copy
|
|
// TODO: 'auto a = ...;' and 'auto @a = ...;' works the same in this case. Is this what we want?
|
|
if( newType.SupportHandles() )
|
|
newType.MakeHandle(true);
|
|
|
|
type = newType;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
Error(TXT_CANNOT_RESOLVE_AUTO, errNode);
|
|
type = asCDataType::CreatePrimitive(ttInt, false);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void asCCompiler::CompileDeclaration(asCScriptNode *decl, asCByteCode *bc)
|
|
{
|
|
// Get the data type
|
|
asCDataType type = builder->CreateDataTypeFromNode(decl->firstChild, script, outFunc->nameSpace, false, outFunc->objectType);
|
|
|
|
// Declare all variables in this declaration
|
|
asCScriptNode *node = decl->firstChild->next;
|
|
while( node )
|
|
{
|
|
// If this is an auto type, we have to compile the assignment now to figure out the type
|
|
asCExprContext compiledCtx(engine);
|
|
bool preCompiled = false;
|
|
if (type.IsAuto())
|
|
{
|
|
preCompiled = CompileAutoType(type, compiledCtx, node->next, node);
|
|
if (!preCompiled)
|
|
{
|
|
// If it wasn't possible to determine the type from the expression then there
|
|
// is no need to continue with the initialization. The error was already reported
|
|
// in CompileAutoType.
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Is the type allowed?
|
|
if( !type.CanBeInstantiated() )
|
|
{
|
|
asCString str;
|
|
if( type.IsAbstractClass() )
|
|
str.Format(TXT_ABSTRACT_CLASS_s_CANNOT_BE_INSTANTIATED, type.Format(outFunc->nameSpace).AddressOf());
|
|
else if( type.IsInterface() )
|
|
str.Format(TXT_INTERFACE_s_CANNOT_BE_INSTANTIATED, type.Format(outFunc->nameSpace).AddressOf());
|
|
else
|
|
// TODO: Improve error message to explain why
|
|
str.Format(TXT_DATA_TYPE_CANT_BE_s, type.Format(outFunc->nameSpace).AddressOf());
|
|
Error(str, node);
|
|
|
|
// Don't continue, as it will most likely lead to further
|
|
// errors that may just mislead the script writer
|
|
return;
|
|
}
|
|
|
|
// A shared object may not declare variables of non-shared types
|
|
if( outFunc->IsShared() )
|
|
{
|
|
asCTypeInfo *ot = type.GetTypeInfo();
|
|
if( ot && !ot->IsShared() )
|
|
{
|
|
asCString msg;
|
|
msg.Format(TXT_SHARED_CANNOT_USE_NON_SHARED_TYPE_s, ot->name.AddressOf());
|
|
Error(msg, decl);
|
|
}
|
|
}
|
|
|
|
// 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
|
|
// TODO: Must check against registered funcdefs too
|
|
if( engine->GetRegisteredType(name.AddressOf(), outFunc->nameSpace) != 0 )
|
|
{
|
|
asCString str;
|
|
str.Format(TXT_ILLEGAL_VARIABLE_NAME_s, name.AddressOf());
|
|
Error(str, node);
|
|
}
|
|
|
|
int offset = AllocateVariable(type, false);
|
|
if( variables->DeclareVariable(name.AddressOf(), type, offset, IsVariableOnHeap(offset)) < 0 )
|
|
{
|
|
// TODO: It might be an out-of-memory too
|
|
|
|
asCString str;
|
|
str.Format(TXT_s_ALREADY_DECLARED, name.AddressOf());
|
|
Error(str, node);
|
|
|
|
// Don't continue after this error, as it will just
|
|
// lead to more errors that are likely false
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
// Warn if this variable hides another variable in a higher scope
|
|
if( variables->parent && variables->parent->GetVariable(name.AddressOf()) )
|
|
{
|
|
asCString str;
|
|
str.Format(TXT_s_HIDES_VAR_IN_OUTER_SCOPE, name.AddressOf());
|
|
Warning(str, node);
|
|
}
|
|
}
|
|
|
|
// Add marker that the variable has been declared
|
|
bc->VarDecl((int)outFunc->scriptData->variables.GetLength());
|
|
outFunc->AddVariable(name, type, offset);
|
|
|
|
// Keep the node for the variable decl
|
|
asCScriptNode *varNode = node;
|
|
|
|
node = node->next;
|
|
|
|
if( node == 0 || node->nodeType == snIdentifier )
|
|
{
|
|
// Initialize with default constructor
|
|
CompileInitialization(0, bc, type, varNode, offset, 0, 0);
|
|
}
|
|
else
|
|
{
|
|
// Compile the initialization expression
|
|
asQWORD constantValue = 0;
|
|
if( CompileInitialization(node, bc, type, varNode, offset, &constantValue, 0, preCompiled ? &compiledCtx : 0) )
|
|
{
|
|
// Check if the variable should be marked as pure constant
|
|
if( type.IsPrimitive() && type.IsReadOnly() )
|
|
{
|
|
sVariable *v = variables->GetVariable(name.AddressOf());
|
|
v->isPureConstant = true;
|
|
v->constantValue = constantValue;
|
|
}
|
|
}
|
|
node = node->next;
|
|
}
|
|
}
|
|
|
|
bc->OptimizeLocally(tempVariableOffsets);
|
|
}
|
|
|
|
// Returns true if the initialization expression is a constant expression
|
|
bool asCCompiler::CompileInitialization(asCScriptNode *node, asCByteCode *bc, const asCDataType &type, asCScriptNode *errNode, int offset, asQWORD *constantValue, int isVarGlobOrMem, asCExprContext *preCompiled)
|
|
{
|
|
bool isConstantExpression = false;
|
|
if( node && node->nodeType == snArgList )
|
|
{
|
|
// Make sure it is an object and not a handle
|
|
if( type.GetTypeInfo() == 0 || type.IsObjectHandle() )
|
|
{
|
|
Error(TXT_MUST_BE_OBJECT, node);
|
|
}
|
|
else
|
|
{
|
|
// Compile the arguments
|
|
asCArray<asCExprContext *> args;
|
|
asCArray<asSNamedArgument> namedArgs;
|
|
if( CompileArgumentList(node, args, namedArgs) >= 0 )
|
|
{
|
|
// Find all constructors
|
|
asCArray<int> funcs;
|
|
asSTypeBehaviour *beh = type.GetBehaviour();
|
|
if( beh )
|
|
{
|
|
if( type.GetTypeInfo()->flags & asOBJ_REF )
|
|
funcs = beh->factories;
|
|
else
|
|
funcs = beh->constructors;
|
|
}
|
|
|
|
asCString str = type.Format(outFunc->nameSpace);
|
|
MatchFunctions(funcs, args, node, str.AddressOf(), &namedArgs);
|
|
|
|
if( funcs.GetLength() == 1 )
|
|
{
|
|
// Add the default values for arguments not explicitly supplied
|
|
int r = CompileDefaultAndNamedArgs(node, args, funcs[0], CastToObjectType(type.GetTypeInfo()), &namedArgs);
|
|
|
|
if( r == asSUCCESS )
|
|
{
|
|
asCExprContext ctx(engine);
|
|
if( type.GetTypeInfo() && (type.GetTypeInfo()->flags & asOBJ_REF) )
|
|
{
|
|
if( isVarGlobOrMem == 0 )
|
|
MakeFunctionCall(&ctx, funcs[0], 0, args, node, true, offset);
|
|
else
|
|
{
|
|
MakeFunctionCall(&ctx, funcs[0], 0, args, node);
|
|
ctx.bc.Instr(asBC_RDSPtr);
|
|
if( isVarGlobOrMem == 1 )
|
|
{
|
|
// Store the returned handle in the global variable
|
|
ctx.bc.InstrPTR(asBC_PGA, engine->globalProperties[offset]->GetAddressOfValue());
|
|
}
|
|
else
|
|
{
|
|
// Store the returned handle in the member
|
|
ctx.bc.InstrSHORT(asBC_PSF, 0);
|
|
ctx.bc.Instr(asBC_RDSPtr);
|
|
ctx.bc.InstrSHORT_DW(asBC_ADDSi, (short)offset, engine->GetTypeIdFromDataType(asCDataType::CreateType(outFunc->objectType, false)));
|
|
}
|
|
if( type.IsFuncdef())
|
|
ctx.bc.InstrPTR(asBC_REFCPY, &engine->functionBehaviours);
|
|
else
|
|
ctx.bc.InstrPTR(asBC_REFCPY, type.GetTypeInfo());
|
|
ReleaseTemporaryVariable(ctx.type.stackOffset, &ctx.bc);
|
|
}
|
|
|
|
// Pop the reference left by the function call
|
|
ctx.bc.Instr(asBC_PopPtr);
|
|
}
|
|
else
|
|
{
|
|
bool onHeap = false;
|
|
|
|
if( isVarGlobOrMem == 0 )
|
|
{
|
|
// When the object is allocated on the heap, the address where the
|
|
// reference will be stored must be pushed on the stack before the
|
|
// arguments. This reference on the stack is safe, even if the script
|
|
// is suspended during the evaluation of the arguments.
|
|
onHeap = IsVariableOnHeap(offset);
|
|
if( onHeap )
|
|
ctx.bc.InstrSHORT(asBC_PSF, (short)offset);
|
|
}
|
|
else if( isVarGlobOrMem == 1 )
|
|
{
|
|
// Push the address of the location where the variable will be stored on the stack.
|
|
// This reference is safe, because the addresses of the global variables cannot change.
|
|
onHeap = true;
|
|
ctx.bc.InstrPTR(asBC_PGA, engine->globalProperties[offset]->GetAddressOfValue());
|
|
}
|
|
else
|
|
{
|
|
// Value types may be allocated inline if they are POD types
|
|
onHeap = !(type.IsObject() || type.IsFuncdef()) || type.IsReference() || (type.GetTypeInfo()->flags & asOBJ_REF);
|
|
if( onHeap )
|
|
{
|
|
ctx.bc.InstrSHORT(asBC_PSF, 0);
|
|
ctx.bc.Instr(asBC_RDSPtr);
|
|
ctx.bc.InstrSHORT_DW(asBC_ADDSi, (short)offset, engine->GetTypeIdFromDataType(asCDataType::CreateType(outFunc->objectType, false)));
|
|
}
|
|
}
|
|
|
|
PrepareFunctionCall(funcs[0], &ctx.bc, args);
|
|
MoveArgsToStack(funcs[0], &ctx.bc, args, false);
|
|
|
|
// When the object is allocated on the stack, the address to the
|
|
// object is pushed on the stack after the arguments as the object pointer
|
|
if( !onHeap )
|
|
{
|
|
if( isVarGlobOrMem == 2 )
|
|
{
|
|
ctx.bc.InstrSHORT(asBC_PSF, 0);
|
|
ctx.bc.Instr(asBC_RDSPtr);
|
|
ctx.bc.InstrSHORT_DW(asBC_ADDSi, (short)offset, engine->GetTypeIdFromDataType(asCDataType::CreateType(outFunc->objectType, false)));
|
|
}
|
|
else
|
|
{
|
|
ctx.bc.InstrSHORT(asBC_PSF, (short)offset);
|
|
}
|
|
}
|
|
|
|
PerformFunctionCall(funcs[0], &ctx, onHeap, &args, CastToObjectType(type.GetTypeInfo()));
|
|
|
|
if( isVarGlobOrMem == 0 )
|
|
{
|
|
// Mark the object in the local variable as initialized
|
|
ctx.bc.ObjInfo(offset, asOBJ_INIT);
|
|
}
|
|
}
|
|
bc->AddCode(&ctx.bc);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Cleanup
|
|
for( asUINT n = 0; n < args.GetLength(); n++ )
|
|
if( args[n] )
|
|
{
|
|
asDELETE(args[n], asCExprContext);
|
|
}
|
|
for( asUINT n = 0; n < namedArgs.GetLength(); n++ )
|
|
if( namedArgs[n].ctx )
|
|
{
|
|
asDELETE(namedArgs[n].ctx, asCExprContext);
|
|
}
|
|
}
|
|
}
|
|
else if( node && node->nodeType == snInitList )
|
|
{
|
|
asCExprValue ti;
|
|
ti.Set(type);
|
|
ti.isVariable = (isVarGlobOrMem == 0);
|
|
ti.isTemporary = false;
|
|
ti.stackOffset = (short)offset;
|
|
ti.isLValue = true;
|
|
|
|
CompileInitList(&ti, node, bc, isVarGlobOrMem);
|
|
}
|
|
else if( node && node->nodeType == snAssignment )
|
|
{
|
|
// Compile the expression
|
|
asCExprContext newExpr(engine);
|
|
asCExprContext* expr;
|
|
int r = 0;
|
|
|
|
if( preCompiled )
|
|
{
|
|
expr = preCompiled;
|
|
}
|
|
else
|
|
{
|
|
expr = &newExpr;
|
|
r = CompileAssignment(node, expr);
|
|
}
|
|
|
|
// handles initialized with null doesn't need any bytecode
|
|
// since handles will be initialized to null by default anyway
|
|
if (type.IsObjectHandle() && expr->type.IsNullConstant() && expr->bc.IsSimpleExpression() )
|
|
return false;
|
|
|
|
// Look for appropriate constructor
|
|
asCArray<int> funcs;
|
|
asCArray<asCExprContext *> args;
|
|
|
|
// Handles must use the handle assignment operation.
|
|
// Types that are ASHANDLE must not allow the use of the constructor in this case,
|
|
// because it is ambiguous whether a value assignment or handle assignment will be done.
|
|
// Only do this if the expression is of the same type, as the expression is an assignment
|
|
// and an initialization constructor may not have the same meaning.
|
|
// TODO: Should allow initialization constructor if it is declared as allowed for implicit conversions.
|
|
if( !type.IsObjectHandle() && !expr->type.isExplicitHandle &&
|
|
!(type.GetTypeInfo() && (type.GetTypeInfo()->GetFlags() & asOBJ_ASHANDLE)) &&
|
|
type.IsEqualExceptRefAndConst(expr->type.dataType) )
|
|
{
|
|
asSTypeBehaviour *beh = type.GetBehaviour();
|
|
if( beh )
|
|
{
|
|
if( type.GetTypeInfo()->flags & asOBJ_REF )
|
|
funcs = beh->factories;
|
|
else
|
|
funcs = beh->constructors;
|
|
}
|
|
|
|
asCString str = type.Format(outFunc->nameSpace);
|
|
args.PushLast(expr);
|
|
MatchFunctions(funcs, args, node, str.AddressOf(), 0, 0, 0, true);
|
|
|
|
// Make sure the argument is of the right type (and not just compatible with the expression)
|
|
if (funcs.GetLength() == 1)
|
|
{
|
|
asCScriptFunction *f = engine->scriptFunctions[funcs[0]];
|
|
if (!f->parameterTypes[0].IsEqualExceptRefAndConst(expr->type.dataType))
|
|
funcs.PopLast();
|
|
}
|
|
}
|
|
|
|
if( funcs.GetLength() == 1 )
|
|
{
|
|
// Use the constructor
|
|
|
|
// TODO: clean-up: A large part of this is identical to the initalization with argList above
|
|
|
|
// Add the default values for arguments not explicitly supplied
|
|
r = CompileDefaultAndNamedArgs(node, args, funcs[0], CastToObjectType(type.GetTypeInfo()));
|
|
|
|
if( r == asSUCCESS )
|
|
{
|
|
asCExprContext ctx(engine);
|
|
if( type.GetTypeInfo() && (type.GetTypeInfo()->flags & asOBJ_REF) )
|
|
{
|
|
if( isVarGlobOrMem == 0 )
|
|
MakeFunctionCall(&ctx, funcs[0], 0, args, node, true, offset);
|
|
else
|
|
{
|
|
MakeFunctionCall(&ctx, funcs[0], 0, args, node);
|
|
ctx.bc.Instr(asBC_RDSPtr);
|
|
if( isVarGlobOrMem == 1 )
|
|
{
|
|
// Store the returned handle in the global variable
|
|
ctx.bc.InstrPTR(asBC_PGA, engine->globalProperties[offset]->GetAddressOfValue());
|
|
}
|
|
else
|
|
{
|
|
// Store the returned handle in the member
|
|
ctx.bc.InstrSHORT(asBC_PSF, 0);
|
|
ctx.bc.Instr(asBC_RDSPtr);
|
|
ctx.bc.InstrSHORT_DW(asBC_ADDSi, (short)offset, engine->GetTypeIdFromDataType(asCDataType::CreateType(outFunc->objectType, false)));
|
|
}
|
|
if( type.IsFuncdef() )
|
|
ctx.bc.InstrPTR(asBC_REFCPY, &engine->functionBehaviours);
|
|
else
|
|
ctx.bc.InstrPTR(asBC_REFCPY, type.GetTypeInfo());
|
|
ReleaseTemporaryVariable(ctx.type.stackOffset, &ctx.bc);
|
|
}
|
|
|
|
// Pop the reference left by the function call
|
|
ctx.bc.Instr(asBC_PopPtr);
|
|
}
|
|
else
|
|
{
|
|
bool onHeap = false;
|
|
|
|
if( isVarGlobOrMem == 0 )
|
|
{
|
|
// When the object is allocated on the heap, the address where the
|
|
// reference will be stored must be pushed on the stack before the
|
|
// arguments. This reference on the stack is safe, even if the script
|
|
// is suspended during the evaluation of the arguments.
|
|
onHeap = IsVariableOnHeap(offset);
|
|
if( onHeap )
|
|
ctx.bc.InstrSHORT(asBC_PSF, (short)offset);
|
|
}
|
|
else if( isVarGlobOrMem == 1 )
|
|
{
|
|
// Push the address of the location where the variable will be stored on the stack.
|
|
// This reference is safe, because the addresses of the global variables cannot change.
|
|
onHeap = true;
|
|
ctx.bc.InstrPTR(asBC_PGA, engine->globalProperties[offset]->GetAddressOfValue());
|
|
}
|
|
else
|
|
{
|
|
// Value types may be allocated inline if they are POD types
|
|
onHeap = !(type.IsObject() || type.IsFuncdef()) || type.IsReference() || (type.GetTypeInfo()->flags & asOBJ_REF);
|
|
if( onHeap )
|
|
{
|
|
ctx.bc.InstrSHORT(asBC_PSF, 0);
|
|
ctx.bc.Instr(asBC_RDSPtr);
|
|
ctx.bc.InstrSHORT_DW(asBC_ADDSi, (short)offset, engine->GetTypeIdFromDataType(asCDataType::CreateType(outFunc->objectType, false)));
|
|
}
|
|
}
|
|
|
|
PrepareFunctionCall(funcs[0], &ctx.bc, args);
|
|
MoveArgsToStack(funcs[0], &ctx.bc, args, false);
|
|
|
|
// When the object is allocated on the stack, the address to the
|
|
// object is pushed on the stack after the arguments as the object pointer
|
|
if( !onHeap )
|
|
{
|
|
if( isVarGlobOrMem == 2 )
|
|
{
|
|
ctx.bc.InstrSHORT(asBC_PSF, 0);
|
|
ctx.bc.Instr(asBC_RDSPtr);
|
|
ctx.bc.InstrSHORT_DW(asBC_ADDSi, (short)offset, engine->GetTypeIdFromDataType(asCDataType::CreateType(outFunc->objectType, false)));
|
|
}
|
|
else
|
|
{
|
|
ctx.bc.InstrSHORT(asBC_PSF, (short)offset);
|
|
}
|
|
}
|
|
|
|
PerformFunctionCall(funcs[0], &ctx, onHeap, &args, CastToObjectType(type.GetTypeInfo()));
|
|
|
|
if( isVarGlobOrMem == 0 )
|
|
{
|
|
// Mark the object in the local variable as initialized
|
|
ctx.bc.ObjInfo(offset, asOBJ_INIT);
|
|
}
|
|
}
|
|
bc->AddCode(&ctx.bc);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Call the default constructur, then call the assignment operator
|
|
asCExprContext ctx(engine);
|
|
|
|
// Call the default constructor here
|
|
if( isVarGlobOrMem == 0 )
|
|
CallDefaultConstructor(type, offset, IsVariableOnHeap(offset), &ctx.bc, errNode);
|
|
else if( isVarGlobOrMem == 1 )
|
|
CallDefaultConstructor(type, offset, true, &ctx.bc, errNode, isVarGlobOrMem);
|
|
else if( isVarGlobOrMem == 2 )
|
|
CallDefaultConstructor(type, offset, type.IsReference(), &ctx.bc, errNode, isVarGlobOrMem);
|
|
|
|
if( r >= 0 )
|
|
{
|
|
if( type.IsPrimitive() )
|
|
{
|
|
if( type.IsReadOnly() && expr->type.isConstant )
|
|
{
|
|
ImplicitConversion(expr, type, node, asIC_IMPLICIT_CONV);
|
|
|
|
// Tell caller that the expression is a constant so it can mark the variable as pure constant
|
|
isConstantExpression = true;
|
|
*constantValue = expr->type.GetConstantData();
|
|
}
|
|
|
|
asCExprContext lctx(engine);
|
|
if( isVarGlobOrMem == 0 )
|
|
lctx.type.SetVariable(type, offset, false);
|
|
else if( isVarGlobOrMem == 1 )
|
|
{
|
|
lctx.type.Set(type);
|
|
lctx.type.dataType.MakeReference(true);
|
|
|
|
// If it is an enum value, i.e. offset is negative, that is being compiled then
|
|
// we skip this as the bytecode won't be used anyway, only the constant value
|
|
if( offset >= 0 )
|
|
lctx.bc.InstrPTR(asBC_LDG, engine->globalProperties[offset]->GetAddressOfValue());
|
|
}
|
|
else
|
|
{
|
|
asASSERT( isVarGlobOrMem == 2 );
|
|
lctx.type.Set(type);
|
|
lctx.type.dataType.MakeReference(true);
|
|
|
|
// Load the reference of the primitive member into the register
|
|
lctx.bc.InstrSHORT(asBC_PSF, 0);
|
|
lctx.bc.Instr(asBC_RDSPtr);
|
|
lctx.bc.InstrSHORT_DW(asBC_ADDSi, (short)offset, engine->GetTypeIdFromDataType(asCDataType::CreateType(outFunc->objectType, false)));
|
|
lctx.bc.Instr(asBC_PopRPtr);
|
|
}
|
|
lctx.type.dataType.MakeReadOnly(false);
|
|
lctx.type.isLValue = true;
|
|
|
|
DoAssignment(&ctx, &lctx, expr, node, node, ttAssignment, node);
|
|
ProcessDeferredParams(&ctx);
|
|
}
|
|
else
|
|
{
|
|
// TODO: runtime optimize: Here we should look for the best matching constructor, instead of
|
|
// just the copy constructor. Only if no appropriate constructor is
|
|
// available should the assignment operator be used.
|
|
|
|
asCExprContext lexpr(engine);
|
|
lexpr.type.Set(type);
|
|
if( isVarGlobOrMem == 0 )
|
|
lexpr.type.dataType.MakeReference(IsVariableOnHeap(offset));
|
|
else if( isVarGlobOrMem == 1 )
|
|
lexpr.type.dataType.MakeReference(true);
|
|
else if( isVarGlobOrMem == 2 )
|
|
{
|
|
if( !lexpr.type.dataType.IsObject() || lexpr.type.dataType.IsFuncdef() || (lexpr.type.dataType.GetTypeInfo()->flags & asOBJ_REF) )
|
|
lexpr.type.dataType.MakeReference(true);
|
|
}
|
|
|
|
// Allow initialization of constant variables
|
|
lexpr.type.dataType.MakeReadOnly(false);
|
|
|
|
if( type.IsObjectHandle() )
|
|
lexpr.type.isExplicitHandle = true;
|
|
|
|
if( isVarGlobOrMem == 0 )
|
|
{
|
|
lexpr.bc.InstrSHORT(asBC_PSF, (short)offset);
|
|
lexpr.type.stackOffset = (short)offset;
|
|
lexpr.type.isVariable = true;
|
|
}
|
|
else if( isVarGlobOrMem == 1 )
|
|
{
|
|
lexpr.bc.InstrPTR(asBC_PGA, engine->globalProperties[offset]->GetAddressOfValue());
|
|
}
|
|
else
|
|
{
|
|
lexpr.bc.InstrSHORT(asBC_PSF, 0);
|
|
lexpr.bc.Instr(asBC_RDSPtr);
|
|
lexpr.bc.InstrSHORT_DW(asBC_ADDSi, (short)offset, engine->GetTypeIdFromDataType(asCDataType::CreateType(outFunc->objectType, false)));
|
|
lexpr.type.stackOffset = -1;
|
|
}
|
|
lexpr.type.isLValue = true;
|
|
|
|
|
|
// 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;
|
|
// Even though an ASHANDLE can be an explicit handle the overloaded operator needs to be called
|
|
if( (lexpr.type.dataType.IsObject() || lexpr.type.dataType.IsFuncdef()) && (!lexpr.type.isExplicitHandle || (lexpr.type.dataType.GetTypeInfo() && (lexpr.type.dataType.GetTypeInfo()->flags & asOBJ_ASHANDLE))) )
|
|
{
|
|
bool useHndlAssign = false;
|
|
if (lexpr.type.dataType.IsHandleToAsHandleType())
|
|
{
|
|
useHndlAssign = true;
|
|
|
|
// Make sure the right hand expression is treated as a handle
|
|
if (!expr->type.isExplicitHandle && !expr->type.IsNullConstant() )
|
|
{
|
|
// TODO: Clean-up: This code is from CompileExpressionPreOp. Create a reusable function
|
|
// Convert the expression to a handle
|
|
if (!expr->type.dataType.IsObjectHandle() && expr->type.dataType.GetTypeInfo() && !(expr->type.dataType.GetTypeInfo()->flags & asOBJ_ASHANDLE))
|
|
{
|
|
asCDataType to = expr->type.dataType;
|
|
to.MakeHandle(true);
|
|
to.MakeReference(true);
|
|
to.MakeHandleToConst(expr->type.dataType.IsReadOnly());
|
|
ImplicitConversion(expr, to, node, asIC_IMPLICIT_CONV, true, false);
|
|
|
|
asASSERT(expr->type.dataType.IsObjectHandle());
|
|
}
|
|
else if (expr->type.dataType.GetTypeInfo() && expr->type.dataType.GetTypeInfo()->flags & asOBJ_ASHANDLE)
|
|
{
|
|
// For the ASHANDLE type we'll simply set the expression as a handle
|
|
expr->type.dataType.MakeHandle(true);
|
|
}
|
|
|
|
if( !expr->type.dataType.IsObjectHandle() && !expr->type.dataType.SupportHandles())
|
|
{
|
|
Error(TXT_OBJECT_HANDLE_NOT_SUPPORTED, node);
|
|
}
|
|
expr->type.isExplicitHandle = true;
|
|
}
|
|
}
|
|
assigned = CompileOverloadedDualOperator(node, &lexpr, expr, false, &ctx, useHndlAssign);
|
|
if( assigned )
|
|
{
|
|
// Pop the resulting value
|
|
if( !ctx.type.dataType.IsPrimitive() )
|
|
ctx.bc.Instr(asBC_PopPtr);
|
|
|
|
// Release the argument
|
|
ProcessDeferredParams(&ctx);
|
|
|
|
// Release temporary variable that may be allocated by the overloaded operator
|
|
ReleaseTemporaryVariable(ctx.type, &ctx.bc);
|
|
}
|
|
}
|
|
|
|
if( !assigned )
|
|
{
|
|
PrepareForAssignment(&lexpr.type.dataType, expr, node, false);
|
|
|
|
// 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( type.IsReadOnly() && expr->type.isConstant )
|
|
{
|
|
isConstantExpression = true;
|
|
*constantValue = expr->type.GetConstantQW();
|
|
}
|
|
|
|
// Add expression code to bytecode
|
|
MergeExprBytecode(&ctx, expr);
|
|
|
|
// Add byte code for storing value of expression in variable
|
|
ctx.bc.AddCode(&lexpr.bc);
|
|
|
|
PerformAssignment(&lexpr.type, &expr->type, &ctx.bc, errNode);
|
|
|
|
// Release temporary variables used by expression
|
|
ReleaseTemporaryVariable(expr->type, &ctx.bc);
|
|
|
|
ctx.bc.Instr(asBC_PopPtr);
|
|
|
|
ProcessDeferredParams(&ctx);
|
|
}
|
|
}
|
|
}
|
|
|
|
bc->AddCode(&ctx.bc);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
asASSERT( node == 0 );
|
|
|
|
// Call the default constructor here, as no explicit initialization is done
|
|
if( isVarGlobOrMem == 0 )
|
|
CallDefaultConstructor(type, offset, IsVariableOnHeap(offset), bc, errNode);
|
|
else if( isVarGlobOrMem == 1 )
|
|
CallDefaultConstructor(type, offset, true, bc, errNode, isVarGlobOrMem);
|
|
else if( isVarGlobOrMem == 2 )
|
|
{
|
|
if( !(type.IsObject() || type.IsFuncdef()) || type.IsReference() || (type.GetTypeInfo()->flags & asOBJ_REF) )
|
|
CallDefaultConstructor(type, offset, true, bc, errNode, isVarGlobOrMem);
|
|
else
|
|
CallDefaultConstructor(type, offset, false, bc, errNode, isVarGlobOrMem);
|
|
}
|
|
}
|
|
|
|
return isConstantExpression;
|
|
}
|
|
|
|
void asCCompiler::CompileInitList(asCExprValue *var, asCScriptNode *node, asCByteCode *bc, int isVarGlobOrMem)
|
|
{
|
|
// Check if the type supports initialization lists
|
|
if( var->dataType.GetTypeInfo() == 0 ||
|
|
var->dataType.GetBehaviour()->listFactory == 0 )
|
|
{
|
|
asCString str;
|
|
str.Format(TXT_INIT_LIST_CANNOT_BE_USED_WITH_s, var->dataType.Format(outFunc->nameSpace).AddressOf());
|
|
Error(str, node);
|
|
return;
|
|
}
|
|
|
|
// Construct the buffer with the elements
|
|
|
|
// Find the list factory
|
|
int funcId = var->dataType.GetBehaviour()->listFactory;
|
|
asASSERT( engine->scriptFunctions[funcId]->listPattern );
|
|
|
|
// TODO: runtime optimize: A future optimization should be to use the stack space directly
|
|
// for small buffers so that the dynamic allocation is skipped
|
|
|
|
// Create a new special object type for the lists. Both asCRestore and the
|
|
// context exception handler will need this to know how to parse the buffer.
|
|
asCObjectType *listPatternType = engine->GetListPatternType(funcId);
|
|
|
|
// Allocate a temporary variable to hold the pointer to the buffer
|
|
int bufferVar = AllocateVariable(asCDataType::CreateType(listPatternType, false), true);
|
|
asUINT bufferSize = 0;
|
|
|
|
// Evaluate all elements of the list
|
|
asCExprContext valueExpr(engine);
|
|
asCScriptNode *el = node;
|
|
asSListPatternNode *patternNode = engine->scriptFunctions[listPatternType->templateSubTypes[0].GetBehaviour()->listFactory]->listPattern;
|
|
int elementsInSubList = -1;
|
|
int r = CompileInitListElement(patternNode, el, engine->GetTypeIdFromDataType(asCDataType::CreateType(listPatternType, false)), short(bufferVar), bufferSize, valueExpr.bc, elementsInSubList);
|
|
asASSERT( r || patternNode == 0 );
|
|
if (r < 0)
|
|
{
|
|
asCString msg;
|
|
msg.Format(TXT_PREV_ERROR_WHILE_COMP_LIST_FOR_TYPE_s, var->dataType.Format(outFunc->nameSpace).AddressOf());
|
|
Error(msg, node);
|
|
}
|
|
|
|
// After all values have been evaluated we know the final size of the buffer
|
|
asCExprContext allocExpr(engine);
|
|
allocExpr.bc.InstrSHORT_DW(asBC_AllocMem, short(bufferVar), bufferSize);
|
|
|
|
// Merge the bytecode into the final sequence
|
|
bc->AddCode(&allocExpr.bc);
|
|
bc->AddCode(&valueExpr.bc);
|
|
|
|
// The object itself is the last to be created and will receive the pointer to the buffer
|
|
asCArray<asCExprContext *> args;
|
|
asCExprContext arg1(engine);
|
|
arg1.type.Set(asCDataType::CreatePrimitive(ttUInt, false));
|
|
arg1.type.dataType.MakeReference(true);
|
|
arg1.bc.InstrSHORT(asBC_PshVPtr, short(bufferVar));
|
|
args.PushLast(&arg1);
|
|
|
|
asCExprContext ctx(engine);
|
|
|
|
if( var->isVariable )
|
|
{
|
|
asASSERT( isVarGlobOrMem == 0 );
|
|
|
|
if( var->dataType.GetTypeInfo()->GetFlags() & asOBJ_REF )
|
|
{
|
|
ctx.bc.AddCode(&arg1.bc);
|
|
|
|
// Call factory and store the handle in the given variable
|
|
PerformFunctionCall(funcId, &ctx, false, &args, 0, true, var->stackOffset);
|
|
ctx.bc.Instr(asBC_PopPtr);
|
|
}
|
|
else
|
|
{
|
|
// Call the constructor
|
|
|
|
// When the object is allocated on the heap, the address where the
|
|
// reference will be stored must be pushed on the stack before the
|
|
// arguments. This reference on the stack is safe, even if the script
|
|
// is suspended during the evaluation of the arguments.
|
|
bool onHeap = IsVariableOnHeap(var->stackOffset);
|
|
if( onHeap )
|
|
ctx.bc.InstrSHORT(asBC_PSF, var->stackOffset);
|
|
|
|
ctx.bc.AddCode(&arg1.bc);
|
|
|
|
// When the object is allocated on the stack, the address to the
|
|
// object is pushed on the stack after the arguments as the object pointer
|
|
if( !onHeap )
|
|
ctx.bc.InstrSHORT(asBC_PSF, var->stackOffset);
|
|
|
|
PerformFunctionCall(funcId, &ctx, onHeap, &args, CastToObjectType(var->dataType.GetTypeInfo()));
|
|
|
|
// Mark the object in the local variable as initialized
|
|
ctx.bc.ObjInfo(var->stackOffset, asOBJ_INIT);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( var->dataType.GetTypeInfo()->GetFlags() & asOBJ_REF )
|
|
{
|
|
ctx.bc.AddCode(&arg1.bc);
|
|
|
|
PerformFunctionCall(funcId, &ctx, false, &args);
|
|
|
|
ctx.bc.Instr(asBC_RDSPtr);
|
|
if( isVarGlobOrMem == 1 )
|
|
{
|
|
// Store the returned handle in the global variable
|
|
ctx.bc.InstrPTR(asBC_PGA, engine->globalProperties[var->stackOffset]->GetAddressOfValue());
|
|
}
|
|
else
|
|
{
|
|
// Store the returned handle in the member
|
|
ctx.bc.InstrSHORT(asBC_PSF, 0);
|
|
ctx.bc.Instr(asBC_RDSPtr);
|
|
ctx.bc.InstrSHORT_DW(asBC_ADDSi, (short)var->stackOffset, engine->GetTypeIdFromDataType(asCDataType::CreateType(outFunc->objectType, false)));
|
|
}
|
|
if (var->dataType.IsFuncdef())
|
|
ctx.bc.InstrPTR(asBC_REFCPY, &engine->functionBehaviours);
|
|
else
|
|
ctx.bc.InstrPTR(asBC_REFCPY, var->dataType.GetTypeInfo());
|
|
ctx.bc.Instr(asBC_PopPtr);
|
|
ReleaseTemporaryVariable(ctx.type.stackOffset, &ctx.bc);
|
|
}
|
|
else
|
|
{
|
|
bool onHeap = true;
|
|
|
|
// Put the address where the object pointer will be placed on the stack
|
|
if( isVarGlobOrMem == 1 )
|
|
ctx.bc.InstrPTR(asBC_PGA, engine->globalProperties[var->stackOffset]->GetAddressOfValue());
|
|
else
|
|
{
|
|
onHeap = !(var->dataType.IsObject() || var->dataType.IsFuncdef()) || var->dataType.IsReference() || (var->dataType.GetTypeInfo()->flags & asOBJ_REF);
|
|
if( onHeap )
|
|
{
|
|
ctx.bc.InstrSHORT(asBC_PSF, 0);
|
|
ctx.bc.Instr(asBC_RDSPtr);
|
|
ctx.bc.InstrSHORT_DW(asBC_ADDSi, (short)var->stackOffset, engine->GetTypeIdFromDataType(asCDataType::CreateType(outFunc->objectType, false)));
|
|
}
|
|
}
|
|
|
|
// Add the address of the list buffer as the argument
|
|
ctx.bc.AddCode(&arg1.bc);
|
|
|
|
if( !onHeap )
|
|
{
|
|
ctx.bc.InstrSHORT(asBC_PSF, 0);
|
|
ctx.bc.Instr(asBC_RDSPtr);
|
|
ctx.bc.InstrSHORT_DW(asBC_ADDSi, (short)var->stackOffset, engine->GetTypeIdFromDataType(asCDataType::CreateType(outFunc->objectType, false)));
|
|
}
|
|
|
|
// Call the ALLOC instruction to allocate memory and invoke constructor
|
|
PerformFunctionCall(funcId, &ctx, onHeap, &args, CastToObjectType(var->dataType.GetTypeInfo()));
|
|
}
|
|
}
|
|
|
|
bc->AddCode(&ctx.bc);
|
|
|
|
// Free the temporary buffer. The FREE instruction will make sure to destroy
|
|
// each element in the buffer so there is no need to do this manually
|
|
bc->InstrW_PTR(asBC_FREE, short(bufferVar), listPatternType);
|
|
ReleaseTemporaryVariable(bufferVar, bc);
|
|
}
|
|
|
|
int asCCompiler::CompileInitListElement(asSListPatternNode *&patternNode, asCScriptNode *&valueNode, int bufferTypeId, short bufferVar, asUINT &bufferSize, asCByteCode &bcInit, int &elementsInSubList)
|
|
{
|
|
if( patternNode->type == asLPT_START )
|
|
{
|
|
if( valueNode == 0 || valueNode->nodeType != snInitList )
|
|
{
|
|
Error(TXT_EXPECTED_LIST, valueNode);
|
|
return -1;
|
|
}
|
|
|
|
// Compile all values until asLPT_END
|
|
patternNode = patternNode->next;
|
|
asCScriptNode *node = valueNode->firstChild;
|
|
while( patternNode->type != asLPT_END )
|
|
{
|
|
// Check for missing value here, else the error reporting will not have a source position to report the error for
|
|
if( node == 0 && patternNode->type == asLPT_TYPE )
|
|
{
|
|
Error(TXT_NOT_ENOUGH_VALUES_FOR_LIST, valueNode);
|
|
return -1;
|
|
}
|
|
|
|
asCScriptNode *errNode = node;
|
|
int r = CompileInitListElement(patternNode, node, bufferTypeId, bufferVar, bufferSize, bcInit, elementsInSubList);
|
|
if( r < 0 ) return r;
|
|
|
|
if( r == 1 )
|
|
{
|
|
asASSERT( engine->ep.disallowEmptyListElements );
|
|
// Empty elements in the middle are not allowed
|
|
Error(TXT_EMPTY_LIST_ELEMENT_IS_NOT_ALLOWED, errNode);
|
|
}
|
|
|
|
asASSERT( patternNode );
|
|
}
|
|
|
|
if( node )
|
|
{
|
|
Error(TXT_TOO_MANY_VALUES_FOR_LIST, valueNode);
|
|
return -1;
|
|
}
|
|
|
|
// Move to the next node
|
|
valueNode = valueNode->next;
|
|
patternNode = patternNode->next;
|
|
}
|
|
else if( patternNode->type == asLPT_REPEAT || patternNode->type == asLPT_REPEAT_SAME )
|
|
{
|
|
// TODO: list: repeat_inner should make sure the list has the same size as the inner list, i.e. square area
|
|
// TODO: list: repeat_prev should make sure the list is the same size as the previous
|
|
|
|
asEListPatternNodeType repeatType = patternNode->type;
|
|
asCScriptNode *firstValue = valueNode;
|
|
|
|
// The following values will be repeated N times
|
|
patternNode = patternNode->next;
|
|
|
|
// Keep track of the patternNode so it can be reset
|
|
asSListPatternNode *nextNode = patternNode;
|
|
|
|
// Align the buffer size to 4 bytes in case previous value was smaller than 4 bytes
|
|
if( bufferSize & 0x3 )
|
|
bufferSize += 4 - (bufferSize & 0x3);
|
|
|
|
// The first dword will hold the number of elements in the list
|
|
asDWORD currSize = bufferSize;
|
|
bufferSize += 4;
|
|
asUINT countElements = 0;
|
|
|
|
int elementsInSubSubList = -1;
|
|
|
|
asCExprContext ctx(engine);
|
|
while( valueNode )
|
|
{
|
|
patternNode = nextNode;
|
|
asCScriptNode *errNode = valueNode;
|
|
int r = CompileInitListElement(patternNode, valueNode, bufferTypeId, bufferVar, bufferSize, ctx.bc, elementsInSubSubList);
|
|
if( r < 0 ) return r;
|
|
|
|
if( r == 0 )
|
|
countElements++;
|
|
else
|
|
{
|
|
asASSERT( r == 1 && engine->ep.disallowEmptyListElements );
|
|
if( valueNode )
|
|
{
|
|
// Empty elements in the middle are not allowed
|
|
Error(TXT_EMPTY_LIST_ELEMENT_IS_NOT_ALLOWED, errNode);
|
|
}
|
|
}
|
|
}
|
|
|
|
if( countElements == 0 )
|
|
{
|
|
// Skip the sub pattern that was expected to be repeated, otherwise the caller will try to match these when we return
|
|
patternNode = nextNode;
|
|
if( patternNode->type == asLPT_TYPE )
|
|
patternNode = patternNode->next;
|
|
else if( patternNode->type == asLPT_START )
|
|
{
|
|
int subCount = 1;
|
|
do
|
|
{
|
|
patternNode = patternNode->next;
|
|
if( patternNode->type == asLPT_START )
|
|
subCount++;
|
|
else if( patternNode->type == asLPT_END )
|
|
subCount--;
|
|
} while( subCount > 0 );
|
|
patternNode = patternNode->next;
|
|
}
|
|
}
|
|
|
|
// For repeat_same each repeated sublist must have the same size to form a rectangular array
|
|
if( repeatType == asLPT_REPEAT_SAME && elementsInSubList != -1 && asUINT(elementsInSubList) != countElements )
|
|
{
|
|
if( countElements < asUINT(elementsInSubList) )
|
|
Error(TXT_NOT_ENOUGH_VALUES_FOR_LIST, firstValue);
|
|
else
|
|
Error(TXT_TOO_MANY_VALUES_FOR_LIST, firstValue);
|
|
|
|
return -1;
|
|
}
|
|
else
|
|
{
|
|
// Return to caller the amount of elments in this sublist
|
|
elementsInSubList = countElements;
|
|
}
|
|
|
|
// The first dword in the buffer will hold the number of elements
|
|
bcInit.InstrSHORT_DW_DW(asBC_SetListSize, bufferVar, currSize, countElements);
|
|
|
|
// Add the values
|
|
bcInit.AddCode(&ctx.bc);
|
|
}
|
|
else if( patternNode->type == asLPT_TYPE )
|
|
{
|
|
bool isEmpty = false;
|
|
|
|
// Determine the size of the element
|
|
asUINT size = 0;
|
|
|
|
asCDataType dt = reinterpret_cast<asSListPatternDataTypeNode*>(patternNode)->dataType;
|
|
|
|
if( valueNode->nodeType == snAssignment || valueNode->nodeType == snInitList )
|
|
{
|
|
asCExprContext lctx(engine);
|
|
asCExprContext rctx(engine);
|
|
|
|
if( valueNode->nodeType == snAssignment )
|
|
{
|
|
// Compile the assignment expression
|
|
CompileAssignment(valueNode, &rctx);
|
|
|
|
if( dt.GetTokenType() == ttQuestion )
|
|
{
|
|
// Make sure the type is not ambiguous
|
|
DetermineSingleFunc(&rctx, valueNode);
|
|
|
|
// We now know the type
|
|
dt = rctx.type.dataType;
|
|
dt.MakeReadOnly(false);
|
|
dt.MakeReference(false);
|
|
|
|
// Values on the list must be aligned to 32bit boundaries, except if the type is smaller than 32bit.
|
|
if( bufferSize & 0x3 )
|
|
bufferSize += 4 - (bufferSize & 0x3);
|
|
|
|
// When value assignment for reference types us disabled, make sure all ref types are passed in as handles
|
|
if (engine->ep.disallowValueAssignForRefType && dt.SupportHandles())
|
|
dt.MakeHandle(true);
|
|
|
|
// Place the type id in the buffer
|
|
bcInit.InstrSHORT_DW_DW(asBC_SetListType, bufferVar, bufferSize, engine->GetTypeIdFromDataType(dt));
|
|
bufferSize += 4;
|
|
}
|
|
}
|
|
else if( valueNode->nodeType == snInitList )
|
|
{
|
|
if( dt.GetTokenType() == ttQuestion )
|
|
{
|
|
// Can't use init lists with var type as it is not possible to determine what type should be allocated
|
|
asCString str;
|
|
str.Format(TXT_INIT_LIST_CANNOT_BE_USED_WITH_s, "?");
|
|
Error(str.AddressOf(), valueNode);
|
|
rctx.type.SetDummy();
|
|
dt = rctx.type.dataType;
|
|
}
|
|
else
|
|
{
|
|
// Allocate a temporary variable that will be initialized with the list
|
|
int offset = AllocateVariable(dt, true);
|
|
|
|
rctx.type.Set(dt);
|
|
rctx.type.isVariable = true;
|
|
rctx.type.isTemporary = true;
|
|
rctx.type.stackOffset = (short)offset;
|
|
|
|
CompileInitList(&rctx.type, valueNode, &rctx.bc, 0);
|
|
|
|
// Put the object on the stack
|
|
rctx.bc.InstrSHORT(asBC_PSF, rctx.type.stackOffset);
|
|
|
|
// It is a reference that we place on the stack
|
|
rctx.type.dataType.MakeReference(true);
|
|
}
|
|
}
|
|
|
|
// Determine size of the element
|
|
if( dt.IsPrimitive() || (!dt.IsNullHandle() && (dt.GetTypeInfo()->flags & asOBJ_VALUE)) )
|
|
size = dt.GetSizeInMemoryBytes();
|
|
else
|
|
size = AS_PTR_SIZE*4;
|
|
|
|
// Values on the list must be aligned to 32bit boundaries, except if the type is smaller than 32bit.
|
|
if( size >= 4 && (bufferSize & 0x3) )
|
|
bufferSize += 4 - (bufferSize & 0x3);
|
|
|
|
// Compile the lvalue
|
|
lctx.bc.InstrSHORT_DW(asBC_PshListElmnt, bufferVar, bufferSize);
|
|
lctx.type.Set(dt);
|
|
lctx.type.isLValue = true;
|
|
if( dt.IsPrimitive() )
|
|
{
|
|
lctx.bc.Instr(asBC_PopRPtr);
|
|
lctx.type.dataType.MakeReference(true);
|
|
}
|
|
else if( dt.IsObjectHandle() ||
|
|
dt.GetTypeInfo()->flags & asOBJ_REF )
|
|
{
|
|
lctx.type.isExplicitHandle = true;
|
|
lctx.type.dataType.MakeReference(true);
|
|
}
|
|
else
|
|
{
|
|
asASSERT( dt.GetTypeInfo()->flags & asOBJ_VALUE );
|
|
|
|
// Make sure the object has been constructed before the assignment
|
|
// TODO: runtime optimize: Use copy constructor instead of assignment to initialize the objects
|
|
asSTypeBehaviour *beh = dt.GetBehaviour();
|
|
int func = 0;
|
|
if( beh ) func = beh->construct;
|
|
if( func == 0 && (dt.GetTypeInfo()->flags & asOBJ_POD) == 0 )
|
|
{
|
|
asCString str;
|
|
str.Format(TXT_NO_DEFAULT_CONSTRUCTOR_FOR_s, dt.GetTypeInfo()->GetName());
|
|
Error(str, valueNode);
|
|
}
|
|
else if( func )
|
|
{
|
|
// Call the constructor as a normal function
|
|
bcInit.InstrSHORT_DW(asBC_PshListElmnt, bufferVar, bufferSize);
|
|
|
|
asCExprContext ctx(engine);
|
|
PerformFunctionCall(func, &ctx, false, 0, CastToObjectType(dt.GetTypeInfo()));
|
|
bcInit.AddCode(&ctx.bc);
|
|
}
|
|
}
|
|
|
|
if( lctx.type.dataType.IsNullHandle() )
|
|
{
|
|
// Don't add any code to assign a null handle. RefCpy doesn't work without a known type.
|
|
// The buffer is already initialized to zero in asBC_AllocMem anyway.
|
|
asASSERT( rctx.bc.GetLastInstr() == asBC_PshNull );
|
|
asASSERT( reinterpret_cast<asSListPatternDataTypeNode*>(patternNode)->dataType.GetTokenType() == ttQuestion );
|
|
}
|
|
else
|
|
{
|
|
asCExprContext ctx(engine);
|
|
DoAssignment(&ctx, &lctx, &rctx, valueNode, valueNode, ttAssignment, valueNode);
|
|
|
|
if( !lctx.type.dataType.IsPrimitive() )
|
|
ctx.bc.Instr(asBC_PopPtr);
|
|
|
|
// Release temporary variables used by expression
|
|
ReleaseTemporaryVariable(ctx.type, &ctx.bc);
|
|
|
|
ProcessDeferredParams(&ctx);
|
|
|
|
bcInit.AddCode(&ctx.bc);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( builder->engine->ep.disallowEmptyListElements )
|
|
{
|
|
// Empty elements are not allowed, except if it is the last in the list
|
|
isEmpty = true;
|
|
}
|
|
else
|
|
{
|
|
// There is no specific value so we need to fill it with a default value
|
|
if( dt.GetTokenType() == ttQuestion )
|
|
{
|
|
// Values on the list must be aligned to 32bit boundaries, except if the type is smaller than 32bit.
|
|
if( bufferSize & 0x3 )
|
|
bufferSize += 4 - (bufferSize & 0x3);
|
|
|
|
// Place the type id for a null handle in the buffer
|
|
bcInit.InstrSHORT_DW_DW(asBC_SetListType, bufferVar, bufferSize, 0);
|
|
bufferSize += 4;
|
|
|
|
dt = asCDataType::CreateNullHandle();
|
|
|
|
// No need to initialize the handle as the buffer is already initialized with zeroes
|
|
}
|
|
else if( dt.GetTypeInfo() && dt.GetTypeInfo()->flags & asOBJ_VALUE )
|
|
{
|
|
// For value types with default constructor we need to call the constructor
|
|
asSTypeBehaviour *beh = dt.GetBehaviour();
|
|
int func = 0;
|
|
if( beh ) func = beh->construct;
|
|
if( func == 0 && (dt.GetTypeInfo()->flags & asOBJ_POD) == 0 )
|
|
{
|
|
asCString str;
|
|
str.Format(TXT_NO_DEFAULT_CONSTRUCTOR_FOR_s, dt.GetTypeInfo()->GetName());
|
|
Error(str, valueNode);
|
|
}
|
|
else if( func )
|
|
{
|
|
// Values on the list must be aligned to 32bit boundaries, except if the type is smaller than 32bit.
|
|
if( bufferSize & 0x3 )
|
|
bufferSize += 4 - (bufferSize & 0x3);
|
|
|
|
// Call the constructor as a normal function
|
|
bcInit.InstrSHORT_DW(asBC_PshListElmnt, bufferVar, bufferSize);
|
|
|
|
asCExprContext ctx(engine);
|
|
PerformFunctionCall(func, &ctx, false, 0, CastToObjectType(dt.GetTypeInfo()));
|
|
bcInit.AddCode(&ctx.bc);
|
|
}
|
|
}
|
|
else if( !dt.IsObjectHandle() && dt.GetTypeInfo() && dt.GetTypeInfo()->flags & asOBJ_REF )
|
|
{
|
|
// For ref types (not handles) we need to call the default factory
|
|
asSTypeBehaviour *beh = dt.GetBehaviour();
|
|
int func = 0;
|
|
if( beh ) func = beh->factory;
|
|
if( func == 0 )
|
|
{
|
|
asCString str;
|
|
str.Format(TXT_NO_DEFAULT_CONSTRUCTOR_FOR_s, dt.GetTypeInfo()->GetName());
|
|
Error(str, valueNode);
|
|
}
|
|
else if( func )
|
|
{
|
|
asCExprContext rctx(engine);
|
|
PerformFunctionCall(func, &rctx, false, 0, CastToObjectType(dt.GetTypeInfo()));
|
|
|
|
// Values on the list must be aligned to 32bit boundaries, except if the type is smaller than 32bit.
|
|
if( bufferSize & 0x3 )
|
|
bufferSize += 4 - (bufferSize & 0x3);
|
|
|
|
asCExprContext lctx(engine);
|
|
lctx.bc.InstrSHORT_DW(asBC_PshListElmnt, bufferVar, bufferSize);
|
|
lctx.type.Set(dt);
|
|
lctx.type.isLValue = true;
|
|
lctx.type.isExplicitHandle = true;
|
|
lctx.type.dataType.MakeReference(true);
|
|
|
|
asCExprContext ctx(engine);
|
|
DoAssignment(&ctx, &lctx, &rctx, valueNode, valueNode, ttAssignment, valueNode);
|
|
|
|
if( !lctx.type.dataType.IsPrimitive() )
|
|
ctx.bc.Instr(asBC_PopPtr);
|
|
|
|
// Release temporary variables used by expression
|
|
ReleaseTemporaryVariable(ctx.type, &ctx.bc);
|
|
|
|
ProcessDeferredParams(&ctx);
|
|
|
|
bcInit.AddCode(&ctx.bc);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if( !isEmpty )
|
|
{
|
|
// Determine size of the element
|
|
if( dt.IsPrimitive() || (!dt.IsNullHandle() && (dt.GetTypeInfo()->flags & asOBJ_VALUE)) )
|
|
size = dt.GetSizeInMemoryBytes();
|
|
else
|
|
size = AS_PTR_SIZE*4;
|
|
asASSERT( size <= 4 || (size & 0x3) == 0 );
|
|
|
|
bufferSize += size;
|
|
}
|
|
|
|
// Move to the next element
|
|
patternNode = patternNode->next;
|
|
valueNode = valueNode->next;
|
|
|
|
if( isEmpty )
|
|
{
|
|
// The caller will determine if the empty element should be ignored or not
|
|
return 1;
|
|
}
|
|
}
|
|
else
|
|
asASSERT( false );
|
|
|
|
return 0;
|
|
}
|
|
|
|
void asCCompiler::CompileStatement(asCScriptNode *statement, bool *hasReturn, asCByteCode *bc)
|
|
{
|
|
// Don't clear the hasReturn flag if this is an empty statement
|
|
// to avoid false errors of 'not all paths return'
|
|
if( statement->nodeType != snExpressionStatement || statement->firstChild )
|
|
*hasReturn = false;
|
|
|
|
if (statement->nodeType == snStatementBlock)
|
|
CompileStatementBlock(statement, true, hasReturn, bc);
|
|
else if (statement->nodeType == snIf)
|
|
CompileIfStatement(statement, hasReturn, bc);
|
|
else if (statement->nodeType == snFor)
|
|
CompileForStatement(statement, bc);
|
|
else if (statement->nodeType == snWhile)
|
|
CompileWhileStatement(statement, bc);
|
|
else if (statement->nodeType == snDoWhile)
|
|
CompileDoWhileStatement(statement, bc);
|
|
else if (statement->nodeType == snExpressionStatement)
|
|
CompileExpressionStatement(statement, bc);
|
|
else if (statement->nodeType == snBreak)
|
|
CompileBreakStatement(statement, bc);
|
|
else if (statement->nodeType == snContinue)
|
|
CompileContinueStatement(statement, bc);
|
|
else if (statement->nodeType == snSwitch)
|
|
CompileSwitchStatement(statement, hasReturn, bc);
|
|
else if (statement->nodeType == snTryCatch)
|
|
CompileTryCatch(statement, hasReturn, bc);
|
|
else if (statement->nodeType == snReturn)
|
|
{
|
|
CompileReturnStatement(statement, bc);
|
|
*hasReturn = true;
|
|
}
|
|
else
|
|
asASSERT(false);
|
|
}
|
|
|
|
void asCCompiler::CompileSwitchStatement(asCScriptNode *snode, bool *, asCByteCode *bc)
|
|
{
|
|
// TODO: inheritance: Must guarantee that all options in the switch case call a constructor, or that none call it.
|
|
|
|
// Reserve label for break statements
|
|
int breakLabel = nextLabel++;
|
|
breakLabels.PushLast(breakLabel);
|
|
|
|
// Add a variable scope that will be used by CompileBreak
|
|
// to know where to stop deallocating variables
|
|
AddVariableScope(true, false);
|
|
|
|
//---------------------------
|
|
// Compile the switch expression
|
|
//-------------------------------
|
|
|
|
// Compile the switch expression
|
|
asCExprContext expr(engine);
|
|
CompileAssignment(snode->firstChild, &expr);
|
|
|
|
// Verify that the expression is a primitive type
|
|
if( !expr.type.dataType.IsIntegerType() && !expr.type.dataType.IsUnsignedType() )
|
|
{
|
|
Error(TXT_SWITCH_MUST_BE_INTEGRAL, snode->firstChild);
|
|
return;
|
|
}
|
|
|
|
ProcessPropertyGetAccessor(&expr, snode);
|
|
|
|
// TODO: Need to support 64bit integers
|
|
// Convert the expression to a 32bit variable
|
|
asCDataType to;
|
|
if( expr.type.dataType.IsIntegerType() )
|
|
to.SetTokenType(ttInt);
|
|
else if( expr.type.dataType.IsUnsignedType() )
|
|
to.SetTokenType(ttUInt);
|
|
|
|
// Make sure the value is in a variable
|
|
if( expr.type.dataType.IsReference() )
|
|
ConvertToVariable(&expr);
|
|
|
|
ImplicitConversion(&expr, to, snode->firstChild, asIC_IMPLICIT_CONV, true);
|
|
|
|
ConvertToVariable(&expr);
|
|
int offset = expr.type.stackOffset;
|
|
|
|
ProcessDeferredParams(&expr);
|
|
|
|
//-------------------------------
|
|
// Determine case values and labels
|
|
//--------------------------------
|
|
|
|
// Remember the first label so that we can later pass the
|
|
// correct label to each CompileCase()
|
|
int firstCaseLabel = nextLabel;
|
|
int defaultLabel = 0;
|
|
|
|
asCArray<int> caseValues;
|
|
asCArray<int> caseLabels;
|
|
|
|
// Compile all case comparisons and make them jump to the right label
|
|
asCScriptNode *cnode = snode->firstChild->next;
|
|
while( cnode )
|
|
{
|
|
// Each case should have a constant expression
|
|
if( cnode->firstChild && cnode->firstChild->nodeType == snExpression )
|
|
{
|
|
// Compile expression
|
|
asCExprContext c(engine);
|
|
CompileExpression(cnode->firstChild, &c);
|
|
|
|
// Verify that the result is a constant
|
|
if( !c.type.isConstant )
|
|
Error(TXT_SWITCH_CASE_MUST_BE_CONSTANT, cnode->firstChild);
|
|
|
|
// Verify that the result is an integral number
|
|
if (!c.type.dataType.IsIntegerType() && !c.type.dataType.IsUnsignedType())
|
|
Error(TXT_SWITCH_MUST_BE_INTEGRAL, cnode->firstChild);
|
|
else
|
|
{
|
|
ImplicitConversion(&c, to, cnode->firstChild, asIC_IMPLICIT_CONV, true);
|
|
|
|
// Has this case been declared already?
|
|
if (caseValues.IndexOf(c.type.GetConstantDW()) >= 0)
|
|
Error(TXT_DUPLICATE_SWITCH_CASE, cnode->firstChild);
|
|
|
|
// TODO: Optimize: We can insert the numbers sorted already
|
|
|
|
// Store constant for later use
|
|
caseValues.PushLast(c.type.GetConstantDW());
|
|
|
|
// Reserve label for this case
|
|
caseLabels.PushLast(nextLabel++);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// TODO: It shouldn't be necessary for the default case to be the last one.
|
|
// Is default the last case?
|
|
if( cnode->next )
|
|
{
|
|
Error(TXT_DEFAULT_MUST_BE_LAST, cnode);
|
|
break;
|
|
}
|
|
|
|
// Reserve label for this case
|
|
defaultLabel = nextLabel++;
|
|
}
|
|
|
|
cnode = cnode->next;
|
|
}
|
|
|
|
// check for empty switch
|
|
if (caseValues.GetLength() == 0)
|
|
{
|
|
Error(TXT_EMPTY_SWITCH, snode);
|
|
return;
|
|
}
|
|
|
|
if( defaultLabel == 0 )
|
|
defaultLabel = breakLabel;
|
|
|
|
//---------------------------------
|
|
// Output the optimized case comparisons
|
|
// with jumps to the case code
|
|
//------------------------------------
|
|
|
|
// Sort the case values by increasing value. Do the sort together with the labels
|
|
// A simple bubble sort is sufficient since we don't expect a huge number of values
|
|
for( asUINT fwd = 1; fwd < caseValues.GetLength(); fwd++ )
|
|
{
|
|
for( int bck = fwd - 1; bck >= 0; bck-- )
|
|
{
|
|
int bckp = bck + 1;
|
|
if( caseValues[bck] > caseValues[bckp] )
|
|
{
|
|
// Swap the values in both arrays
|
|
int swap = caseValues[bckp];
|
|
caseValues[bckp] = caseValues[bck];
|
|
caseValues[bck] = swap;
|
|
|
|
swap = caseLabels[bckp];
|
|
caseLabels[bckp] = caseLabels[bck];
|
|
caseLabels[bck] = swap;
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Find ranges of consecutive numbers
|
|
asCArray<int> ranges;
|
|
ranges.PushLast(0);
|
|
asUINT n;
|
|
for( n = 1; n < caseValues.GetLength(); ++n )
|
|
{
|
|
// We can join numbers that are less than 5 numbers
|
|
// apart since the output code will still be smaller
|
|
if( caseValues[n] > caseValues[n-1] + 5 )
|
|
ranges.PushLast(n);
|
|
}
|
|
|
|
// If the value is larger than the largest case value, jump to default
|
|
int tmpOffset = AllocateVariable(asCDataType::CreatePrimitive(ttInt, false), true);
|
|
expr.bc.InstrSHORT_DW(asBC_SetV4, (short)tmpOffset, caseValues[caseValues.GetLength()-1]);
|
|
expr.bc.InstrW_W(asBC_CMPi, offset, tmpOffset);
|
|
expr.bc.InstrDWORD(asBC_JP, defaultLabel);
|
|
ReleaseTemporaryVariable(tmpOffset, &expr.bc);
|
|
|
|
// TODO: runtime optimize: We could possibly optimize this even more by doing a
|
|
// binary search instead of a linear search through the ranges
|
|
|
|
// For each range
|
|
int range;
|
|
for( range = 0; range < (int)ranges.GetLength(); range++ )
|
|
{
|
|
// Find the largest value in this range
|
|
int maxRange = caseValues[ranges[range]];
|
|
int index = ranges[range];
|
|
for( ; (index < (int)caseValues.GetLength()) && (caseValues[index] <= maxRange + 5); index++ )
|
|
maxRange = caseValues[index];
|
|
|
|
// If there are only 2 numbers then it is better to compare them directly
|
|
if( index - ranges[range] > 2 )
|
|
{
|
|
// If the value is smaller than the smallest case value in the range, jump to default
|
|
tmpOffset = AllocateVariable(asCDataType::CreatePrimitive(ttInt, false), true);
|
|
expr.bc.InstrSHORT_DW(asBC_SetV4, (short)tmpOffset, caseValues[ranges[range]]);
|
|
expr.bc.InstrW_W(asBC_CMPi, offset, tmpOffset);
|
|
expr.bc.InstrDWORD(asBC_JS, defaultLabel);
|
|
ReleaseTemporaryVariable(tmpOffset, &expr.bc);
|
|
|
|
int nextRangeLabel = nextLabel++;
|
|
// If this is the last range we don't have to make this test
|
|
if( range < (int)ranges.GetLength() - 1 )
|
|
{
|
|
// If the value is larger than the largest case value in the range, jump to the next range
|
|
tmpOffset = AllocateVariable(asCDataType::CreatePrimitive(ttInt, false), true);
|
|
expr.bc.InstrSHORT_DW(asBC_SetV4, (short)tmpOffset, maxRange);
|
|
expr.bc.InstrW_W(asBC_CMPi, offset, tmpOffset);
|
|
expr.bc.InstrDWORD(asBC_JP, nextRangeLabel);
|
|
ReleaseTemporaryVariable(tmpOffset, &expr.bc);
|
|
}
|
|
|
|
// Jump forward according to the value
|
|
tmpOffset = AllocateVariable(asCDataType::CreatePrimitive(ttInt, false), true);
|
|
expr.bc.InstrSHORT_DW(asBC_SetV4, (short)tmpOffset, caseValues[ranges[range]]);
|
|
expr.bc.InstrW_W_W(asBC_SUBi, tmpOffset, offset, tmpOffset);
|
|
ReleaseTemporaryVariable(tmpOffset, &expr.bc);
|
|
expr.bc.JmpP(tmpOffset, maxRange - caseValues[ranges[range]]);
|
|
|
|
// Add the list of jumps to the correct labels (any holes, jump to default)
|
|
index = ranges[range];
|
|
for( int i = caseValues[index]; i <= maxRange; i++ )
|
|
{
|
|
if( caseValues[index] == i )
|
|
expr.bc.InstrINT(asBC_JMP, caseLabels[index++]);
|
|
else
|
|
expr.bc.InstrINT(asBC_JMP, defaultLabel);
|
|
}
|
|
|
|
expr.bc.Label((short)nextRangeLabel);
|
|
}
|
|
else
|
|
{
|
|
// Simply make a comparison with each value
|
|
for( int i = ranges[range]; i < index; ++i )
|
|
{
|
|
tmpOffset = AllocateVariable(asCDataType::CreatePrimitive(ttInt, false), true);
|
|
expr.bc.InstrSHORT_DW(asBC_SetV4, (short)tmpOffset, caseValues[i]);
|
|
expr.bc.InstrW_W(asBC_CMPi, offset, tmpOffset);
|
|
expr.bc.InstrDWORD(asBC_JZ, caseLabels[i]);
|
|
ReleaseTemporaryVariable(tmpOffset, &expr.bc);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Catch any value that falls trough
|
|
expr.bc.InstrINT(asBC_JMP, defaultLabel);
|
|
|
|
// Release the temporary variable previously stored
|
|
ReleaseTemporaryVariable(expr.type, &expr.bc);
|
|
|
|
// TODO: optimize: Should optimize each piece individually
|
|
expr.bc.OptimizeLocally(tempVariableOffsets);
|
|
|
|
//----------------------------------
|
|
// Output case implementations
|
|
//----------------------------------
|
|
|
|
// Compile case implementations, each one with the label before it
|
|
cnode = snode->firstChild->next;
|
|
while( cnode )
|
|
{
|
|
// Each case should have a constant expression
|
|
if( cnode->firstChild && cnode->firstChild->nodeType == snExpression )
|
|
{
|
|
expr.bc.Label((short)firstCaseLabel++);
|
|
|
|
CompileCase(cnode->firstChild->next, &expr.bc);
|
|
}
|
|
else
|
|
{
|
|
expr.bc.Label((short)defaultLabel);
|
|
|
|
// Is default the last case?
|
|
if( cnode->next )
|
|
{
|
|
// We've already reported this error
|
|
break;
|
|
}
|
|
|
|
CompileCase(cnode->firstChild, &expr.bc);
|
|
}
|
|
|
|
cnode = cnode->next;
|
|
}
|
|
|
|
//--------------------------------
|
|
|
|
bc->AddCode(&expr.bc);
|
|
|
|
// Add break label
|
|
bc->Label((short)breakLabel);
|
|
|
|
breakLabels.PopLast();
|
|
RemoveVariableScope();
|
|
}
|
|
|
|
void asCCompiler::CompileCase(asCScriptNode *node, asCByteCode *bc)
|
|
{
|
|
bool isFinished = false;
|
|
bool hasReturn = false;
|
|
bool hasUnreachableCode = false;
|
|
while( node )
|
|
{
|
|
if( !hasUnreachableCode && (hasReturn || isFinished) )
|
|
{
|
|
hasUnreachableCode = true;
|
|
Warning(TXT_UNREACHABLE_CODE, node);
|
|
break;
|
|
}
|
|
|
|
if( node->nodeType == snBreak || node->nodeType == snContinue )
|
|
isFinished = true;
|
|
|
|
asCByteCode statement(engine);
|
|
if( node->nodeType == snDeclaration )
|
|
{
|
|
Error(TXT_DECL_IN_SWITCH, node);
|
|
|
|
// Compile it anyway to avoid further compiler errors
|
|
CompileDeclaration(node, &statement);
|
|
}
|
|
else
|
|
CompileStatement(node, &hasReturn, &statement);
|
|
|
|
LineInstr(bc, node->tokenPos);
|
|
bc->AddCode(&statement);
|
|
|
|
if( !hasCompileErrors )
|
|
asASSERT( tempVariables.GetLength() == 0 );
|
|
|
|
node = node->next;
|
|
}
|
|
}
|
|
|
|
void asCCompiler::CompileTryCatch(asCScriptNode *node, bool *hasReturn, asCByteCode *bc)
|
|
{
|
|
// We will use one label before and another after the catch statement
|
|
int beforeCatchLabel = nextLabel++;
|
|
int afterCatchLabel = nextLabel++;
|
|
|
|
// Compile the try block
|
|
bool hasReturnTry;
|
|
asCByteCode tryBC(engine);
|
|
CompileStatement(node->firstChild, &hasReturnTry, &tryBC);
|
|
|
|
// Add marker to unwind exception until here, then jump to catch block
|
|
bc->TryBlock((short)beforeCatchLabel);
|
|
|
|
// Add the byte code
|
|
LineInstr(bc, node->firstChild->tokenPos);
|
|
bc->AddCode(&tryBC);
|
|
|
|
// Add jump to after catch
|
|
bc->InstrINT(asBC_JMP, afterCatchLabel);
|
|
|
|
// Compile the catch block
|
|
bool hasReturnCatch;
|
|
asCByteCode catchBC(engine);
|
|
CompileStatement(node->firstChild->next, &hasReturnCatch, &catchBC);
|
|
|
|
// Add marker to tell bytecode optimizer that this is a catch
|
|
// block so the code is not removed as unreachable code
|
|
bc->Label((short)beforeCatchLabel);
|
|
|
|
// Add the byte code
|
|
LineInstr(bc, node->firstChild->next->tokenPos);
|
|
bc->AddCode(&catchBC);
|
|
|
|
// Add the label after catch
|
|
bc->Label((short)afterCatchLabel);
|
|
|
|
// The try/catch statement only has return (i.e. no code after
|
|
// the try/catch block will be executed) if both blocks have
|
|
*hasReturn = hasReturnTry && hasReturnCatch;
|
|
}
|
|
|
|
void asCCompiler::CompileIfStatement(asCScriptNode *inode, bool *hasReturn, asCByteCode *bc)
|
|
{
|
|
// We will use one label for the if statement
|
|
// and possibly another for the else statement
|
|
int afterLabel = nextLabel++;
|
|
|
|
// Compile the expression
|
|
asCExprContext expr(engine);
|
|
int r = CompileAssignment(inode->firstChild, &expr);
|
|
if( r == 0 )
|
|
{
|
|
// Allow value types to be converted to bool using 'bool opImplConv()'
|
|
if( expr.type.dataType.GetTypeInfo() && (expr.type.dataType.GetTypeInfo()->GetFlags() & asOBJ_VALUE) )
|
|
ImplicitConversion(&expr, asCDataType::CreatePrimitive(ttBool, false), inode, asIC_IMPLICIT_CONV);
|
|
|
|
if( !expr.type.dataType.IsEqualExceptRefAndConst(asCDataType::CreatePrimitive(ttBool, true)) )
|
|
Error(TXT_EXPR_MUST_BE_BOOL, inode->firstChild);
|
|
else
|
|
{
|
|
if( !expr.type.isConstant )
|
|
{
|
|
ProcessPropertyGetAccessor(&expr, inode);
|
|
ConvertToVariable(&expr);
|
|
ProcessDeferredParams(&expr);
|
|
|
|
// Add a test
|
|
expr.bc.InstrSHORT(asBC_CpyVtoR4, expr.type.stackOffset);
|
|
expr.bc.Instr(asBC_ClrHi);
|
|
expr.bc.InstrDWORD(asBC_JZ, afterLabel);
|
|
ReleaseTemporaryVariable(expr.type, &expr.bc);
|
|
|
|
expr.bc.OptimizeLocally(tempVariableOffsets);
|
|
bc->AddCode(&expr.bc);
|
|
}
|
|
#if AS_SIZEOF_BOOL == 1
|
|
else if( expr.type.GetConstantB() == 0 )
|
|
#else
|
|
else if (expr.type.GetConstantDW() == 0)
|
|
#endif
|
|
{
|
|
// Jump to the else case
|
|
bc->InstrINT(asBC_JMP, afterLabel);
|
|
|
|
// TODO: Should we warn that the expression will always go to the else?
|
|
}
|
|
}
|
|
}
|
|
|
|
// Compile the if statement
|
|
bool origIsConstructorCalled = m_isConstructorCalled;
|
|
|
|
bool hasReturn1;
|
|
asCByteCode ifBC(engine);
|
|
CompileStatement(inode->firstChild->next, &hasReturn1, &ifBC);
|
|
|
|
// Add the byte code
|
|
LineInstr(bc, inode->firstChild->next->tokenPos);
|
|
bc->AddCode(&ifBC);
|
|
|
|
if( inode->firstChild->next->nodeType == snExpressionStatement && inode->firstChild->next->firstChild == 0 )
|
|
{
|
|
// Don't allow if( expr );
|
|
Error(TXT_IF_WITH_EMPTY_STATEMENT, inode->firstChild->next);
|
|
}
|
|
|
|
// If one of the statements call the constructor, the other must as well
|
|
// otherwise it is possible the constructor is never called
|
|
bool constructorCall1 = false;
|
|
bool constructorCall2 = false;
|
|
if( !origIsConstructorCalled && m_isConstructorCalled )
|
|
constructorCall1 = true;
|
|
|
|
// Do we have an else statement?
|
|
if( inode->firstChild->next != inode->lastChild )
|
|
{
|
|
// Reset the constructor called flag so the else statement can call the constructor too
|
|
m_isConstructorCalled = origIsConstructorCalled;
|
|
|
|
int afterElse = 0;
|
|
if( !hasReturn1 )
|
|
{
|
|
afterElse = nextLabel++;
|
|
|
|
// Add jump to after the else statement
|
|
bc->InstrINT(asBC_JMP, afterElse);
|
|
}
|
|
|
|
// Add label for the else statement
|
|
bc->Label((short)afterLabel);
|
|
|
|
bool hasReturn2;
|
|
asCByteCode elseBC(engine);
|
|
CompileStatement(inode->lastChild, &hasReturn2, &elseBC);
|
|
|
|
// Add byte code for the else statement
|
|
LineInstr(bc, inode->lastChild->tokenPos);
|
|
bc->AddCode(&elseBC);
|
|
|
|
if( inode->lastChild->nodeType == snExpressionStatement && inode->lastChild->firstChild == 0 )
|
|
{
|
|
// Don't allow if( expr ) {} else;
|
|
Error(TXT_ELSE_WITH_EMPTY_STATEMENT, inode->lastChild);
|
|
}
|
|
|
|
if( !hasReturn1 )
|
|
{
|
|
// Add label for the end of else statement
|
|
bc->Label((short)afterElse);
|
|
}
|
|
|
|
// The if statement only has return if both alternatives have
|
|
*hasReturn = hasReturn1 && hasReturn2;
|
|
|
|
if( !origIsConstructorCalled && m_isConstructorCalled )
|
|
constructorCall2 = true;
|
|
}
|
|
else
|
|
{
|
|
// Add label for the end of if statement
|
|
bc->Label((short)afterLabel);
|
|
*hasReturn = false;
|
|
}
|
|
|
|
// Make sure both or neither conditions call a constructor
|
|
if( (constructorCall1 && !constructorCall2) ||
|
|
(constructorCall2 && !constructorCall1) )
|
|
{
|
|
Error(TXT_BOTH_CONDITIONS_MUST_CALL_CONSTRUCTOR, inode);
|
|
}
|
|
|
|
m_isConstructorCalled = origIsConstructorCalled || constructorCall1 || constructorCall2;
|
|
}
|
|
|
|
void asCCompiler::CompileForStatement(asCScriptNode *fnode, asCByteCode *bc)
|
|
{
|
|
// Add a variable scope that will be used by CompileBreak/Continue to know where to stop deallocating variables
|
|
AddVariableScope(true, true);
|
|
|
|
// We will use three labels for the for loop
|
|
int conditionLabel = nextLabel++;
|
|
int afterLabel = nextLabel++;
|
|
int continueLabel = nextLabel++;
|
|
int insideLabel = nextLabel++;
|
|
|
|
continueLabels.PushLast(continueLabel);
|
|
breakLabels.PushLast(afterLabel);
|
|
|
|
//---------------------------------------
|
|
// Compile the initialization statement
|
|
asCByteCode initBC(engine);
|
|
LineInstr(&initBC, fnode->firstChild->tokenPos);
|
|
if( fnode->firstChild->nodeType == snDeclaration )
|
|
CompileDeclaration(fnode->firstChild, &initBC);
|
|
else
|
|
CompileExpressionStatement(fnode->firstChild, &initBC);
|
|
|
|
//-----------------------------------
|
|
// Compile the condition statement
|
|
asCExprContext expr(engine);
|
|
asCScriptNode *second = fnode->firstChild->next;
|
|
if( second->firstChild )
|
|
{
|
|
int r = CompileAssignment(second->firstChild, &expr);
|
|
if( r >= 0 )
|
|
{
|
|
// Allow value types to be converted to bool using 'bool opImplConv()'
|
|
if( expr.type.dataType.GetTypeInfo() && (expr.type.dataType.GetTypeInfo()->GetFlags() & asOBJ_VALUE) )
|
|
ImplicitConversion(&expr, asCDataType::CreatePrimitive(ttBool, false), second->firstChild, asIC_IMPLICIT_CONV);
|
|
|
|
if( !expr.type.dataType.IsEqualExceptRefAndConst(asCDataType::CreatePrimitive(ttBool, true)) )
|
|
Error(TXT_EXPR_MUST_BE_BOOL, second);
|
|
else
|
|
{
|
|
ProcessPropertyGetAccessor(&expr, second);
|
|
ConvertToVariable(&expr);
|
|
ProcessDeferredParams(&expr);
|
|
|
|
// If expression is false exit the loop
|
|
expr.bc.InstrSHORT(asBC_CpyVtoR4, expr.type.stackOffset);
|
|
expr.bc.Instr(asBC_ClrHi);
|
|
expr.bc.InstrDWORD(asBC_JNZ, insideLabel);
|
|
ReleaseTemporaryVariable(expr.type, &expr.bc);
|
|
|
|
expr.bc.OptimizeLocally(tempVariableOffsets);
|
|
|
|
// Prepend the line instruction for the condition
|
|
asCByteCode tmp(engine);
|
|
LineInstr(&tmp, second->firstChild->tokenPos);
|
|
tmp.AddCode(&expr.bc);
|
|
expr.bc.AddCode(&tmp);
|
|
}
|
|
}
|
|
}
|
|
|
|
//---------------------------
|
|
// Compile the increment statement(s)
|
|
asCByteCode nextBC(engine);
|
|
asCScriptNode *cnode = second->next;
|
|
while( cnode && cnode->nodeType == snExpressionStatement && cnode != fnode->lastChild )
|
|
{
|
|
LineInstr(&nextBC, cnode->tokenPos);
|
|
CompileExpressionStatement(cnode, &nextBC);
|
|
cnode = cnode->next;
|
|
}
|
|
|
|
//------------------------------
|
|
// Compile loop statement
|
|
bool hasReturn;
|
|
asCByteCode forBC(engine);
|
|
CompileStatement(fnode->lastChild, &hasReturn, &forBC);
|
|
|
|
//-------------------------------
|
|
// Join the code pieces
|
|
bc->AddCode(&initBC);
|
|
bc->InstrDWORD(asBC_JMP, conditionLabel);
|
|
|
|
bc->Label((short)insideLabel);
|
|
|
|
// Add a suspend bytecode inside the loop to guarantee
|
|
// that the application can suspend the execution
|
|
bc->Instr(asBC_SUSPEND);
|
|
bc->InstrPTR(asBC_JitEntry, 0);
|
|
|
|
LineInstr(bc, fnode->lastChild->tokenPos);
|
|
bc->AddCode(&forBC);
|
|
|
|
bc->Label((short)continueLabel);
|
|
bc->AddCode(&nextBC);
|
|
|
|
bc->Label((short)conditionLabel);
|
|
if( expr.bc.GetLastInstr() == -1 )
|
|
// There is no condition, so we just always jump
|
|
bc->InstrDWORD(asBC_JMP, insideLabel);
|
|
else
|
|
bc->AddCode(&expr.bc);
|
|
|
|
bc->Label((short)afterLabel);
|
|
|
|
continueLabels.PopLast();
|
|
breakLabels.PopLast();
|
|
|
|
// 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
|
|
CallDestructor(v->type, v->stackOffset, v->onHeap, bc);
|
|
|
|
// Don't deallocate function parameters
|
|
if( v->stackOffset > 0 )
|
|
DeallocateVariable(v->stackOffset);
|
|
}
|
|
|
|
RemoveVariableScope();
|
|
}
|
|
|
|
void asCCompiler::CompileWhileStatement(asCScriptNode *wnode, asCByteCode *bc)
|
|
{
|
|
// Add a variable scope that will be used by CompileBreak/Continue to know where to stop deallocating variables
|
|
AddVariableScope(true, true);
|
|
|
|
// We will use two labels for the while loop
|
|
int beforeLabel = nextLabel++;
|
|
int afterLabel = nextLabel++;
|
|
|
|
continueLabels.PushLast(beforeLabel);
|
|
breakLabels.PushLast(afterLabel);
|
|
|
|
// Add label before the expression
|
|
bc->Label((short)beforeLabel);
|
|
|
|
// Compile expression
|
|
asCExprContext expr(engine);
|
|
int r = CompileAssignment(wnode->firstChild, &expr);
|
|
if( r == 0 )
|
|
{
|
|
// Allow value types to be converted to bool using 'bool opImplConv()'
|
|
if( expr.type.dataType.GetTypeInfo() && (expr.type.dataType.GetTypeInfo()->GetFlags() & asOBJ_VALUE) )
|
|
ImplicitConversion(&expr, asCDataType::CreatePrimitive(ttBool, false), wnode->firstChild, asIC_IMPLICIT_CONV);
|
|
|
|
if( !expr.type.dataType.IsEqualExceptRefAndConst(asCDataType::CreatePrimitive(ttBool, true)) )
|
|
Error(TXT_EXPR_MUST_BE_BOOL, wnode->firstChild);
|
|
else
|
|
{
|
|
ProcessPropertyGetAccessor(&expr, wnode);
|
|
ConvertToVariable(&expr);
|
|
ProcessDeferredParams(&expr);
|
|
|
|
// Jump to end of statement if expression is false
|
|
expr.bc.InstrSHORT(asBC_CpyVtoR4, expr.type.stackOffset);
|
|
expr.bc.Instr(asBC_ClrHi);
|
|
expr.bc.InstrDWORD(asBC_JZ, afterLabel);
|
|
ReleaseTemporaryVariable(expr.type, &expr.bc);
|
|
|
|
expr.bc.OptimizeLocally(tempVariableOffsets);
|
|
bc->AddCode(&expr.bc);
|
|
}
|
|
}
|
|
|
|
// Add a suspend bytecode inside the loop to guarantee
|
|
// that the application can suspend the execution
|
|
bc->Instr(asBC_SUSPEND);
|
|
bc->InstrPTR(asBC_JitEntry, 0);
|
|
|
|
// Compile statement
|
|
bool hasReturn;
|
|
asCByteCode whileBC(engine);
|
|
CompileStatement(wnode->lastChild, &hasReturn, &whileBC);
|
|
|
|
// Add byte code for the statement
|
|
LineInstr(bc, wnode->lastChild->tokenPos);
|
|
bc->AddCode(&whileBC);
|
|
|
|
// Jump to the expression
|
|
bc->InstrINT(asBC_JMP, beforeLabel);
|
|
|
|
// Add label after the statement
|
|
bc->Label((short)afterLabel);
|
|
|
|
continueLabels.PopLast();
|
|
breakLabels.PopLast();
|
|
|
|
RemoveVariableScope();
|
|
}
|
|
|
|
void asCCompiler::CompileDoWhileStatement(asCScriptNode *wnode, asCByteCode *bc)
|
|
{
|
|
// Add a variable scope that will be used by CompileBreak/Continue to know where to stop deallocating variables
|
|
AddVariableScope(true, true);
|
|
|
|
// We will use two labels for the while loop
|
|
int beforeLabel = nextLabel++;
|
|
int beforeTest = nextLabel++;
|
|
int afterLabel = nextLabel++;
|
|
|
|
continueLabels.PushLast(beforeTest);
|
|
breakLabels.PushLast(afterLabel);
|
|
|
|
// Add label before the statement
|
|
bc->Label((short)beforeLabel);
|
|
|
|
// Compile statement
|
|
bool hasReturn;
|
|
asCByteCode whileBC(engine);
|
|
CompileStatement(wnode->firstChild, &hasReturn, &whileBC);
|
|
|
|
// Add byte code for the statement
|
|
LineInstr(bc, wnode->firstChild->tokenPos);
|
|
bc->AddCode(&whileBC);
|
|
|
|
// Add label before the expression
|
|
bc->Label((short)beforeTest);
|
|
|
|
// Add a suspend bytecode inside the loop to guarantee
|
|
// that the application can suspend the execution
|
|
bc->Instr(asBC_SUSPEND);
|
|
bc->InstrPTR(asBC_JitEntry, 0);
|
|
|
|
// Add a line instruction
|
|
LineInstr(bc, wnode->lastChild->tokenPos);
|
|
|
|
// Compile expression
|
|
asCExprContext expr(engine);
|
|
CompileAssignment(wnode->lastChild, &expr);
|
|
|
|
// Allow value types to be converted to bool using 'bool opImplConv()'
|
|
if( expr.type.dataType.GetTypeInfo() && (expr.type.dataType.GetTypeInfo()->GetFlags() & asOBJ_VALUE) )
|
|
ImplicitConversion(&expr, asCDataType::CreatePrimitive(ttBool, false), wnode->lastChild, asIC_IMPLICIT_CONV);
|
|
|
|
if( !expr.type.dataType.IsEqualExceptRefAndConst(asCDataType::CreatePrimitive(ttBool, true)) )
|
|
Error(TXT_EXPR_MUST_BE_BOOL, wnode->firstChild);
|
|
else
|
|
{
|
|
ProcessPropertyGetAccessor(&expr, wnode);
|
|
ConvertToVariable(&expr);
|
|
ProcessDeferredParams(&expr);
|
|
|
|
// Jump to next iteration if expression is true
|
|
expr.bc.InstrSHORT(asBC_CpyVtoR4, expr.type.stackOffset);
|
|
expr.bc.Instr(asBC_ClrHi);
|
|
expr.bc.InstrDWORD(asBC_JNZ, beforeLabel);
|
|
ReleaseTemporaryVariable(expr.type, &expr.bc);
|
|
|
|
expr.bc.OptimizeLocally(tempVariableOffsets);
|
|
bc->AddCode(&expr.bc);
|
|
}
|
|
|
|
// Add label after the statement
|
|
bc->Label((short)afterLabel);
|
|
|
|
continueLabels.PopLast();
|
|
breakLabels.PopLast();
|
|
|
|
RemoveVariableScope();
|
|
}
|
|
|
|
void asCCompiler::CompileBreakStatement(asCScriptNode *node, asCByteCode *bc)
|
|
{
|
|
if( breakLabels.GetLength() == 0 )
|
|
{
|
|
Error(TXT_INVALID_BREAK, node);
|
|
return;
|
|
}
|
|
|
|
// Add destructor calls for all variables that will go out of scope
|
|
// Put this clean up in a block to allow exception handler to understand them
|
|
bc->Block(true);
|
|
asCVariableScope *vs = variables;
|
|
while( !vs->isBreakScope )
|
|
{
|
|
for( int n = (int)vs->variables.GetLength() - 1; n >= 0; n-- )
|
|
CallDestructor(vs->variables[n]->type, vs->variables[n]->stackOffset, vs->variables[n]->onHeap, bc);
|
|
|
|
vs = vs->parent;
|
|
}
|
|
bc->Block(false);
|
|
|
|
bc->InstrINT(asBC_JMP, breakLabels[breakLabels.GetLength()-1]);
|
|
}
|
|
|
|
void asCCompiler::CompileContinueStatement(asCScriptNode *node, asCByteCode *bc)
|
|
{
|
|
if( continueLabels.GetLength() == 0 )
|
|
{
|
|
Error(TXT_INVALID_CONTINUE, node);
|
|
return;
|
|
}
|
|
|
|
// Add destructor calls for all variables that will go out of scope
|
|
// Put this clean up in a block to allow exception handler to understand them
|
|
bc->Block(true);
|
|
asCVariableScope *vs = variables;
|
|
while( !vs->isContinueScope )
|
|
{
|
|
for( int n = (int)vs->variables.GetLength() - 1; n >= 0; n-- )
|
|
CallDestructor(vs->variables[n]->type, vs->variables[n]->stackOffset, vs->variables[n]->onHeap, bc);
|
|
|
|
vs = vs->parent;
|
|
}
|
|
bc->Block(false);
|
|
|
|
bc->InstrINT(asBC_JMP, continueLabels[continueLabels.GetLength()-1]);
|
|
}
|
|
|
|
void asCCompiler::CompileExpressionStatement(asCScriptNode *enode, asCByteCode *bc)
|
|
{
|
|
if( enode->firstChild )
|
|
{
|
|
// Compile the expression
|
|
asCExprContext expr(engine);
|
|
CompileAssignment(enode->firstChild, &expr);
|
|
|
|
// Must not have unused ambiguous names
|
|
if( expr.IsClassMethod() || expr.IsGlobalFunc() )
|
|
Error(TXT_INVALID_EXPRESSION_AMBIGUOUS_NAME, enode);
|
|
|
|
// Must not have unused anonymous functions
|
|
if( expr.IsLambda() )
|
|
Error(TXT_INVALID_EXPRESSION_LAMBDA, enode);
|
|
|
|
// If we get here and there is still an unprocessed property
|
|
// accessor, then process it as a get access. Don't call if there is
|
|
// already a compile error, or we might report an error that is not valid
|
|
if( !hasCompileErrors )
|
|
ProcessPropertyGetAccessor(&expr, enode);
|
|
|
|
// Pop the value from the stack
|
|
if( !expr.type.dataType.IsPrimitive() )
|
|
expr.bc.Instr(asBC_PopPtr);
|
|
|
|
// Release temporary variables used by expression
|
|
ReleaseTemporaryVariable(expr.type, &expr.bc);
|
|
|
|
ProcessDeferredParams(&expr);
|
|
|
|
expr.bc.OptimizeLocally(tempVariableOffsets);
|
|
bc->AddCode(&expr.bc);
|
|
}
|
|
}
|
|
|
|
void asCCompiler::PrepareTemporaryVariable(asCScriptNode *node, asCExprContext *ctx, bool forceOnHeap)
|
|
{
|
|
// The input can be either an object or funcdef, either as handle or reference
|
|
asASSERT(ctx->type.dataType.IsObject() || ctx->type.dataType.IsFuncdef());
|
|
|
|
// If the object already is stored in temporary variable then nothing needs to be done
|
|
// Note, a type can be temporary without being a variable, in which case it is holding off
|
|
// on releasing a previously used object.
|
|
if( ctx->type.isTemporary && ctx->type.isVariable &&
|
|
!(forceOnHeap && !IsVariableOnHeap(ctx->type.stackOffset)) )
|
|
{
|
|
// If the temporary object is currently not a reference
|
|
// the expression needs to be reevaluated to a reference
|
|
if( !ctx->type.dataType.IsReference() )
|
|
{
|
|
ctx->bc.Instr(asBC_PopPtr);
|
|
ctx->bc.InstrSHORT(asBC_PSF, ctx->type.stackOffset);
|
|
ctx->type.dataType.MakeReference(true);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
// Allocate temporary variable
|
|
asCDataType dt = ctx->type.dataType;
|
|
dt.MakeReference(false);
|
|
dt.MakeReadOnly(false);
|
|
|
|
int offset = AllocateVariable(dt, true, forceOnHeap);
|
|
|
|
// Objects stored on the stack are not considered references
|
|
dt.MakeReference(IsVariableOnHeap(offset));
|
|
|
|
asCExprValue lvalue;
|
|
lvalue.Set(dt);
|
|
lvalue.isExplicitHandle = ctx->type.isExplicitHandle;
|
|
bool isExplicitHandle = ctx->type.isExplicitHandle;
|
|
|
|
bool prevIsTemp = ctx->type.isTemporary;
|
|
int prevStackOffset = ctx->type.stackOffset;
|
|
|
|
CompileInitAsCopy(dt, offset, &ctx->bc, ctx, node, false);
|
|
|
|
// Release the previous temporary variable if it hasn't already been released
|
|
if( prevIsTemp && tempVariables.Exists(prevStackOffset) )
|
|
ReleaseTemporaryVariable(prevStackOffset, &ctx->bc);
|
|
|
|
// Push the reference to the temporary variable on the stack
|
|
ctx->bc.InstrSHORT(asBC_PSF, (short)offset);
|
|
|
|
ctx->type.Set(dt);
|
|
ctx->type.isTemporary = true;
|
|
ctx->type.stackOffset = (short)offset;
|
|
ctx->type.isVariable = true;
|
|
ctx->type.isExplicitHandle = isExplicitHandle;
|
|
ctx->type.dataType.MakeReference(IsVariableOnHeap(offset));
|
|
}
|
|
|
|
void asCCompiler::CompileReturnStatement(asCScriptNode *rnode, asCByteCode *bc)
|
|
{
|
|
// Get return type and location
|
|
sVariable *v = variables->GetVariable("return");
|
|
|
|
// Basic validations
|
|
if( v->type.GetSizeOnStackDWords() > 0 && !rnode->firstChild )
|
|
{
|
|
Error(TXT_MUST_RETURN_VALUE, rnode);
|
|
return;
|
|
}
|
|
else if( v->type.GetSizeOnStackDWords() == 0 && rnode->firstChild )
|
|
{
|
|
Error(TXT_CANT_RETURN_VALUE, rnode);
|
|
return;
|
|
}
|
|
|
|
// Compile the expression
|
|
if( rnode->firstChild )
|
|
{
|
|
// Compile the expression
|
|
asCExprContext expr(engine);
|
|
int r = CompileAssignment(rnode->firstChild, &expr);
|
|
if( r < 0 ) return;
|
|
|
|
if( v->type.IsReference() )
|
|
{
|
|
// The expression that gives the reference must not use any of the
|
|
// variables that must be destroyed upon exit, because then it means
|
|
// reference will stay alive while the clean-up is done, which could
|
|
// potentially mean that the reference is invalidated by the clean-up.
|
|
//
|
|
// When the function is returning a reference, the clean-up of the
|
|
// variables must be done before the evaluation of the expression.
|
|
//
|
|
// A reference to a global variable, or a class member for class methods
|
|
// should be allowed to be returned.
|
|
|
|
if( !(expr.type.dataType.IsReference() ||
|
|
(expr.type.dataType.IsObject() && !expr.type.dataType.IsObjectHandle())) )
|
|
{
|
|
// Clean up the potential deferred parameters
|
|
ProcessDeferredParams(&expr);
|
|
Error(TXT_NOT_VALID_REFERENCE, rnode);
|
|
return;
|
|
}
|
|
|
|
// No references to local variables, temporary variables, or parameters
|
|
// are allowed to be returned, since they go out of scope when the function
|
|
// returns. Even reference parameters are disallowed, since it is not possible
|
|
// to know the scope of them. The exception is the 'this' pointer, which
|
|
// is treated by the compiler as a local variable, but isn't really so.
|
|
if( (expr.type.isVariable && !(expr.type.stackOffset == 0 && outFunc->objectType)) || expr.type.isTemporary )
|
|
{
|
|
// Clean up the potential deferred parameters
|
|
ProcessDeferredParams(&expr);
|
|
Error(TXT_CANNOT_RETURN_REF_TO_LOCAL, rnode);
|
|
return;
|
|
}
|
|
|
|
// The type must match exactly as we cannot convert
|
|
// the reference without loosing the original value
|
|
if( !(v->type.IsEqualExceptConst(expr.type.dataType) ||
|
|
((expr.type.dataType.IsObject() || expr.type.dataType.IsFuncdef()) &&
|
|
!expr.type.dataType.IsObjectHandle() &&
|
|
v->type.IsEqualExceptRefAndConst(expr.type.dataType))) ||
|
|
(!v->type.IsReadOnly() && expr.type.dataType.IsReadOnly()) )
|
|
{
|
|
// Clean up the potential deferred parameters
|
|
ProcessDeferredParams(&expr);
|
|
asCString str;
|
|
str.Format(TXT_CANT_IMPLICITLY_CONVERT_s_TO_s, expr.type.dataType.Format(outFunc->nameSpace).AddressOf(), v->type.Format(outFunc->nameSpace).AddressOf());
|
|
Error(str, rnode);
|
|
return;
|
|
}
|
|
|
|
// The expression must not have any deferred expressions, because the evaluation
|
|
// of these cannot be done without keeping the reference which is not safe
|
|
if( expr.deferredParams.GetLength() )
|
|
{
|
|
// Clean up the potential deferred parameters
|
|
ProcessDeferredParams(&expr);
|
|
Error(TXT_REF_CANT_BE_RETURNED_DEFERRED_PARAM, rnode);
|
|
return;
|
|
}
|
|
|
|
// Make sure the expression isn't using any local variables that
|
|
// will need to be cleaned up before the function completes
|
|
asCArray<int> usedVars;
|
|
expr.bc.GetVarsUsed(usedVars);
|
|
for( asUINT n = 0; n < usedVars.GetLength(); n++ )
|
|
{
|
|
int var = GetVariableSlot(usedVars[n]);
|
|
if( var != -1 )
|
|
{
|
|
asCDataType dt = variableAllocations[var];
|
|
if( dt.IsObject() )
|
|
{
|
|
ProcessDeferredParams(&expr);
|
|
Error(TXT_REF_CANT_BE_RETURNED_LOCAL_VARS, rnode);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Can't return the reference if could point to a local variable
|
|
if( expr.type.isRefToLocal )
|
|
{
|
|
ProcessDeferredParams(&expr);
|
|
Error(TXT_REF_CANT_BE_TO_LOCAL_VAR, rnode);
|
|
return;
|
|
}
|
|
|
|
// All objects in the function must be cleaned up before the expression
|
|
// is evaluated, otherwise there is a possibility that the cleanup will
|
|
// invalidate the reference.
|
|
|
|
// Destroy the local variables before loading
|
|
// the reference into the register. This will
|
|
// be done before the expression is evaluated.
|
|
DestroyVariables(bc);
|
|
|
|
|
|
// For primitives the reference is already in the register,
|
|
// but for non-primitives the reference is on the stack so we
|
|
// need to load it into the register
|
|
if( !expr.type.dataType.IsPrimitive() )
|
|
{
|
|
if( !expr.type.dataType.IsObjectHandle() &&
|
|
expr.type.dataType.IsReference() )
|
|
expr.bc.Instr(asBC_RDSPtr);
|
|
|
|
expr.bc.Instr(asBC_PopRPtr);
|
|
}
|
|
|
|
// There are no temporaries to release so we're done
|
|
}
|
|
else // if( !v->type.IsReference() )
|
|
{
|
|
ProcessPropertyGetAccessor(&expr, rnode);
|
|
|
|
// Prepare the value for assignment
|
|
IsVariableInitialized(&expr.type, rnode->firstChild);
|
|
|
|
if( v->type.IsPrimitive() )
|
|
{
|
|
if( expr.type.dataType.IsReference() ) ConvertToVariable(&expr);
|
|
|
|
// Implicitly convert the value to the return type
|
|
ImplicitConversion(&expr, v->type, rnode->firstChild, asIC_IMPLICIT_CONV);
|
|
|
|
// Verify that the conversion was successful
|
|
if( expr.type.dataType != v->type )
|
|
{
|
|
asCString str;
|
|
str.Format(TXT_NO_CONVERSION_s_TO_s, expr.type.dataType.Format(outFunc->nameSpace).AddressOf(), v->type.Format(outFunc->nameSpace).AddressOf());
|
|
Error(str, rnode);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
ConvertToVariable(&expr);
|
|
|
|
// Clean up the local variables and process deferred parameters
|
|
DestroyVariables(&expr.bc);
|
|
ProcessDeferredParams(&expr);
|
|
|
|
ReleaseTemporaryVariable(expr.type, &expr.bc);
|
|
|
|
// Load the variable in the register
|
|
if( v->type.GetSizeOnStackDWords() == 1 )
|
|
expr.bc.InstrSHORT(asBC_CpyVtoR4, expr.type.stackOffset);
|
|
else
|
|
expr.bc.InstrSHORT(asBC_CpyVtoR8, expr.type.stackOffset);
|
|
}
|
|
}
|
|
else if( v->type.IsObject() || v->type.IsFuncdef() )
|
|
{
|
|
// Value types are returned on the stack, in a location
|
|
// that has been reserved by the calling function.
|
|
if( outFunc->DoesReturnOnStack() )
|
|
{
|
|
// TODO: runtime optimize: If the return type has a constructor that takes the type of the expression,
|
|
// it should be called directly instead of first converting the expression and
|
|
// then copy the value.
|
|
if( !v->type.IsEqualExceptRefAndConst(expr.type.dataType) )
|
|
{
|
|
ImplicitConversion(&expr, v->type, rnode->firstChild, asIC_IMPLICIT_CONV);
|
|
if( !v->type.IsEqualExceptRefAndConst(expr.type.dataType) )
|
|
{
|
|
asCString str;
|
|
str.Format(TXT_CANT_IMPLICITLY_CONVERT_s_TO_s, expr.type.dataType.Format(outFunc->nameSpace).AddressOf(), v->type.Format(outFunc->nameSpace).AddressOf());
|
|
Error(str, rnode->firstChild);
|
|
return;
|
|
}
|
|
}
|
|
|
|
int offset = outFunc->objectType ? -AS_PTR_SIZE : 0;
|
|
CompileInitAsCopy(v->type, offset, &expr.bc, &expr, rnode->firstChild, true);
|
|
|
|
// Clean up the local variables and process deferred parameters
|
|
DestroyVariables(&expr.bc);
|
|
ProcessDeferredParams(&expr);
|
|
}
|
|
else
|
|
{
|
|
asASSERT( (v->type.GetTypeInfo()->flags & asOBJ_REF) || v->type.IsFuncdef() );
|
|
|
|
// Prepare the expression to be loaded into the object
|
|
// register. This will place the reference in local variable
|
|
PrepareArgument(&v->type, &expr, rnode->firstChild, false, 0);
|
|
|
|
// Pop the reference to the temporary variable
|
|
expr.bc.Instr(asBC_PopPtr);
|
|
|
|
// Clean up the local variables and process deferred parameters
|
|
DestroyVariables(&expr.bc);
|
|
ProcessDeferredParams(&expr);
|
|
|
|
// Load the object pointer into the object register
|
|
// LOADOBJ also clears the address in the variable
|
|
expr.bc.InstrSHORT(asBC_LOADOBJ, expr.type.stackOffset);
|
|
|
|
// LOADOBJ cleared the address in the variable so the object will not be freed
|
|
// here, but the temporary variable must still be freed so the slot can be reused
|
|
// By releasing without the bytecode we do just that.
|
|
ReleaseTemporaryVariable(expr.type, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
expr.bc.OptimizeLocally(tempVariableOffsets);
|
|
bc->AddCode(&expr.bc);
|
|
}
|
|
else
|
|
{
|
|
// For functions that don't return anything
|
|
// we just detroy the local variables
|
|
DestroyVariables(bc);
|
|
}
|
|
|
|
// Jump to the end of the function
|
|
bc->InstrINT(asBC_JMP, 0);
|
|
}
|
|
|
|
void asCCompiler::DestroyVariables(asCByteCode *bc)
|
|
{
|
|
// Call destructor on all variables except for the function parameters
|
|
// Put the clean-up in a block to allow exception handler to understand this
|
|
bc->Block(true);
|
|
asCVariableScope *vs = variables;
|
|
while( vs )
|
|
{
|
|
for( int n = (int)vs->variables.GetLength() - 1; n >= 0; n-- )
|
|
if( vs->variables[n]->stackOffset > 0 )
|
|
CallDestructor(vs->variables[n]->type, vs->variables[n]->stackOffset, vs->variables[n]->onHeap, bc);
|
|
|
|
vs = vs->parent;
|
|
}
|
|
bc->Block(false);
|
|
}
|
|
|
|
void asCCompiler::AddVariableScope(bool isBreakScope, bool isContinueScope)
|
|
{
|
|
variables = asNEW(asCVariableScope)(variables);
|
|
if( variables == 0 )
|
|
{
|
|
// Out of memory
|
|
return;
|
|
}
|
|
variables->isBreakScope = isBreakScope;
|
|
variables->isContinueScope = isContinueScope;
|
|
}
|
|
|
|
void asCCompiler::RemoveVariableScope()
|
|
{
|
|
if( variables )
|
|
{
|
|
asCVariableScope *var = variables;
|
|
variables = variables->parent;
|
|
asDELETE(var,asCVariableScope);
|
|
}
|
|
}
|
|
|
|
void asCCompiler::Error(const asCString &msg, asCScriptNode *node)
|
|
{
|
|
asCString str;
|
|
|
|
int r = 0, c = 0;
|
|
asASSERT( node );
|
|
if( node ) script->ConvertPosToRowCol(node->tokenPos, &r, &c);
|
|
|
|
builder->WriteError(script->name, msg, r, c);
|
|
|
|
hasCompileErrors = true;
|
|
}
|
|
|
|
void asCCompiler::Warning(const asCString &msg, asCScriptNode *node)
|
|
{
|
|
asCString str;
|
|
|
|
int r = 0, c = 0;
|
|
asASSERT( node );
|
|
if( node ) script->ConvertPosToRowCol(node->tokenPos, &r, &c);
|
|
|
|
builder->WriteWarning(script->name, msg, r, c);
|
|
}
|
|
|
|
void asCCompiler::Information(const asCString &msg, asCScriptNode *node)
|
|
{
|
|
asCString str;
|
|
|
|
int r = 0, c = 0;
|
|
asASSERT( node );
|
|
if( node ) script->ConvertPosToRowCol(node->tokenPos, &r, &c);
|
|
|
|
builder->WriteInfo(script->name, msg, r, c, false);
|
|
}
|
|
|
|
void asCCompiler::PrintMatchingFuncs(asCArray<int> &funcs, asCScriptNode *node, asCObjectType *inType)
|
|
{
|
|
int r = 0, c = 0;
|
|
asASSERT( node );
|
|
if( node ) script->ConvertPosToRowCol(node->tokenPos, &r, &c);
|
|
|
|
for( unsigned int n = 0; n < funcs.GetLength(); n++ )
|
|
{
|
|
asCScriptFunction *func = builder->GetFunctionDescription(funcs[n]);
|
|
if( inType && func->funcType == asFUNC_VIRTUAL )
|
|
func = inType->virtualFunctionTable[func->vfTableIdx];
|
|
|
|
builder->WriteInfo(script->name, func->GetDeclaration(true, false, true), r, c, false);
|
|
|
|
if (func->objectType && (func->objectType->flags & asOBJ_TEMPLATE))
|
|
{
|
|
// Check for funcdefs in the arguments that may have been generated by the template instance, so these can be shown to user
|
|
for (unsigned int p = 0; p < func->GetParamCount(); p++)
|
|
{
|
|
int typeId = 0;
|
|
func->GetParam(p, &typeId);
|
|
asITypeInfo *ti = engine->GetTypeInfoById(typeId);
|
|
if (ti && (ti->GetFlags() & asOBJ_FUNCDEF))
|
|
{
|
|
asCString msg;
|
|
msg.Format(TXT_WHERE_s_IS_s, ti->GetName(), ti->GetFuncdefSignature()->GetDeclaration());
|
|
builder->WriteInfo(script->name, msg.AddressOf(), r, c, false);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int asCCompiler::AllocateVariableNotIn(const asCDataType &type, bool isTemporary, bool forceOnHeap, asCExprContext *ctx)
|
|
{
|
|
int l = int(reservedVariables.GetLength());
|
|
ctx->bc.GetVarsUsed(reservedVariables);
|
|
int var = AllocateVariable(type, isTemporary, forceOnHeap);
|
|
reservedVariables.SetLength(l);
|
|
return var;
|
|
}
|
|
|
|
int asCCompiler::AllocateVariable(const asCDataType &type, bool isTemporary, bool forceOnHeap, bool asReference)
|
|
{
|
|
asCDataType t(type);
|
|
t.MakeReference(asReference);
|
|
|
|
if( t.IsPrimitive() && t.GetSizeOnStackDWords() == 1 )
|
|
t.SetTokenType(ttInt);
|
|
|
|
if( t.IsPrimitive() && t.GetSizeOnStackDWords() == 2 )
|
|
t.SetTokenType(ttDouble);
|
|
|
|
// Only null handles have the token type unrecognized token
|
|
asASSERT( t.IsObjectHandle() || t.GetTokenType() != ttUnrecognizedToken );
|
|
|
|
bool isOnHeap = true;
|
|
if( t.IsPrimitive() ||
|
|
(t.GetTypeInfo() && (t.GetTypeInfo()->GetFlags() & asOBJ_VALUE) && !forceOnHeap) )
|
|
{
|
|
// Primitives and value types (unless overridden) are allocated on the stack
|
|
isOnHeap = false;
|
|
}
|
|
|
|
// Find a free location with the same type
|
|
for( asUINT n = 0; n < freeVariables.GetLength(); n++ )
|
|
{
|
|
int slot = freeVariables[n];
|
|
|
|
if( variableAllocations[slot].IsEqualExceptConst(t) &&
|
|
variableIsTemporary[slot] == isTemporary &&
|
|
variableIsOnHeap[slot] == isOnHeap )
|
|
{
|
|
// We can't return by slot, must count variable sizes
|
|
int offset = GetVariableOffset(slot);
|
|
|
|
// Verify that it is not in the list of reserved variables
|
|
bool isUsed = false;
|
|
if( reservedVariables.GetLength() )
|
|
isUsed = reservedVariables.Exists(offset);
|
|
|
|
if( !isUsed )
|
|
{
|
|
if( n != freeVariables.GetLength() - 1 )
|
|
freeVariables[n] = freeVariables.PopLast();
|
|
else
|
|
freeVariables.PopLast();
|
|
|
|
if( isTemporary )
|
|
tempVariables.PushLast(offset);
|
|
|
|
return offset;
|
|
}
|
|
}
|
|
}
|
|
|
|
variableAllocations.PushLast(t);
|
|
variableIsTemporary.PushLast(isTemporary);
|
|
variableIsOnHeap.PushLast(isOnHeap);
|
|
|
|
int offset = GetVariableOffset((int)variableAllocations.GetLength()-1);
|
|
|
|
if( isTemporary )
|
|
{
|
|
// Add offset to the currently allocated temporary variables
|
|
tempVariables.PushLast(offset);
|
|
|
|
// Add offset to all known offsets to temporary variables, whether allocated or not
|
|
tempVariableOffsets.PushLast(offset);
|
|
}
|
|
|
|
return offset;
|
|
}
|
|
|
|
int asCCompiler::GetVariableOffset(int varIndex)
|
|
{
|
|
// Return offset to the last dword on the stack
|
|
|
|
// Start at 1 as offset 0 is reserved for the this pointer (or first argument for global functions)
|
|
int varOffset = 1;
|
|
|
|
// Skip lower variables
|
|
for( int n = 0; n < varIndex; n++ )
|
|
{
|
|
if( !variableIsOnHeap[n] && variableAllocations[n].IsObject() )
|
|
varOffset += variableAllocations[n].GetSizeInMemoryDWords();
|
|
else
|
|
varOffset += variableAllocations[n].GetSizeOnStackDWords();
|
|
}
|
|
|
|
if( varIndex < (int)variableAllocations.GetLength() )
|
|
{
|
|
// For variables larger than 1 dword the returned offset should be to the last dword
|
|
int size;
|
|
if( !variableIsOnHeap[varIndex] && variableAllocations[varIndex].IsObject() )
|
|
size = variableAllocations[varIndex].GetSizeInMemoryDWords();
|
|
else
|
|
size = variableAllocations[varIndex].GetSizeOnStackDWords();
|
|
if( size > 1 )
|
|
varOffset += size-1;
|
|
}
|
|
|
|
return varOffset;
|
|
}
|
|
|
|
|
|
int asCCompiler::GetVariableSlot(int offset)
|
|
{
|
|
int varOffset = 1;
|
|
for( asUINT n = 0; n < variableAllocations.GetLength(); n++ )
|
|
{
|
|
if( !variableIsOnHeap[n] && variableAllocations[n].IsObject() )
|
|
varOffset += -1 + variableAllocations[n].GetSizeInMemoryDWords();
|
|
else
|
|
varOffset += -1 + variableAllocations[n].GetSizeOnStackDWords();
|
|
|
|
if( varOffset == offset )
|
|
return n;
|
|
|
|
varOffset++;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
bool asCCompiler::IsVariableOnHeap(int offset)
|
|
{
|
|
int varSlot = GetVariableSlot(offset);
|
|
if( varSlot < 0 )
|
|
{
|
|
// This happens for function arguments that are considered as on the heap
|
|
return true;
|
|
}
|
|
|
|
return variableIsOnHeap[varSlot];
|
|
}
|
|
|
|
void asCCompiler::DeallocateVariable(int offset)
|
|
{
|
|
// Remove temporary variable
|
|
int n;
|
|
for( n = 0; n < (int)tempVariables.GetLength(); n++ )
|
|
{
|
|
if( offset == tempVariables[n] )
|
|
{
|
|
if( n == (int)tempVariables.GetLength()-1 )
|
|
tempVariables.PopLast();
|
|
else
|
|
tempVariables[n] = tempVariables.PopLast();
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Mark the variable slot available for new allocations
|
|
n = GetVariableSlot(offset);
|
|
if( n != -1 )
|
|
{
|
|
freeVariables.PushLast(n);
|
|
return;
|
|
}
|
|
|
|
// We might get here if the variable was implicitly declared
|
|
// because it was used before a formal declaration, in this case
|
|
// the offset is 0x7FFF
|
|
|
|
asASSERT(offset == 0x7FFF);
|
|
}
|
|
|
|
void asCCompiler::ReleaseTemporaryVariable(asCExprValue &t, asCByteCode *bc)
|
|
{
|
|
if( t.isTemporary )
|
|
{
|
|
ReleaseTemporaryVariable(t.stackOffset, bc);
|
|
t.isTemporary = false;
|
|
}
|
|
}
|
|
|
|
void asCCompiler::ReleaseTemporaryVariable(int offset, asCByteCode *bc)
|
|
{
|
|
asASSERT( tempVariables.Exists(offset) );
|
|
|
|
if( bc )
|
|
{
|
|
// We need to call the destructor on the true variable type
|
|
int n = GetVariableSlot(offset);
|
|
asASSERT( n >= 0 );
|
|
if( n >= 0 )
|
|
{
|
|
asCDataType dt = variableAllocations[n];
|
|
bool isOnHeap = variableIsOnHeap[n];
|
|
|
|
// Call destructor
|
|
CallDestructor(dt, offset, isOnHeap, bc);
|
|
}
|
|
}
|
|
|
|
DeallocateVariable(offset);
|
|
}
|
|
|
|
void asCCompiler::Dereference(asCExprContext *ctx, bool generateCode)
|
|
{
|
|
if( ctx->type.dataType.IsReference() )
|
|
{
|
|
if( ctx->type.dataType.IsObject() || ctx->type.dataType.IsFuncdef() )
|
|
{
|
|
ctx->type.dataType.MakeReference(false);
|
|
if( generateCode )
|
|
ctx->bc.Instr(asBC_RDSPtr);
|
|
}
|
|
else
|
|
{
|
|
// This should never happen as primitives are treated differently
|
|
asASSERT(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool asCCompiler::IsVariableInitialized(asCExprValue *type, asCScriptNode *node)
|
|
{
|
|
// No need to check if there is no variable scope
|
|
if( variables == 0 ) return true;
|
|
|
|
// Temporary variables are assumed to be initialized
|
|
if( type->isTemporary ) return true;
|
|
|
|
// Verify that it is a variable
|
|
if( !type->isVariable ) return true;
|
|
|
|
// Find the variable
|
|
sVariable *v = variables->GetVariableByOffset(type->stackOffset);
|
|
|
|
// The variable isn't found if it is a constant, in which case it is guaranteed to be initialized
|
|
if( v == 0 ) return true;
|
|
|
|
if( v->isInitialized ) return true;
|
|
|
|
// Complex types don't need this test
|
|
if( v->type.IsObject() || v->type.IsFuncdef() ) return true;
|
|
|
|
// Mark as initialized so that the user will not be bothered again
|
|
v->isInitialized = true;
|
|
|
|
// Write warning
|
|
asCString str;
|
|
str.Format(TXT_s_NOT_INITIALIZED, (const char *)v->name.AddressOf());
|
|
Warning(str, node);
|
|
|
|
return false;
|
|
}
|
|
|
|
void asCCompiler::PrepareOperand(asCExprContext *ctx, asCScriptNode *node)
|
|
{
|
|
// Check if the variable is initialized (if it indeed is a variable)
|
|
IsVariableInitialized(&ctx->type, node);
|
|
|
|
asCDataType to = ctx->type.dataType;
|
|
to.MakeReference(false);
|
|
|
|
ImplicitConversion(ctx, to, node, asIC_IMPLICIT_CONV);
|
|
|
|
ProcessDeferredParams(ctx);
|
|
}
|
|
|
|
void asCCompiler::PrepareForAssignment(asCDataType *lvalue, asCExprContext *rctx, asCScriptNode *node, bool toTemporary, asCExprContext *lvalueExpr)
|
|
{
|
|
// Reserve the temporary variables used in the lvalue expression so they won't end up being used by the rvalue too
|
|
int l = int(reservedVariables.GetLength());
|
|
if( lvalueExpr ) lvalueExpr->bc.GetVarsUsed(reservedVariables);
|
|
|
|
|
|
ProcessPropertyGetAccessor(rctx, node);
|
|
|
|
// Make sure the rvalue is initialized if it is a variable
|
|
IsVariableInitialized(&rctx->type, node);
|
|
|
|
if( lvalue->IsPrimitive() )
|
|
{
|
|
if( rctx->type.dataType.IsPrimitive() )
|
|
{
|
|
if( rctx->type.dataType.IsReference() )
|
|
{
|
|
// Cannot do implicit conversion of references so we first convert the reference to a variable
|
|
ConvertToVariableNotIn(rctx, lvalueExpr);
|
|
}
|
|
}
|
|
|
|
// Implicitly convert the value to the right type
|
|
ImplicitConversion(rctx, *lvalue, node, asIC_IMPLICIT_CONV);
|
|
|
|
// Check data type
|
|
if( !lvalue->IsEqualExceptRefAndConst(rctx->type.dataType) )
|
|
{
|
|
asCString str;
|
|
str.Format(TXT_CANT_IMPLICITLY_CONVERT_s_TO_s, rctx->type.dataType.Format(outFunc->nameSpace).AddressOf(), lvalue->Format(outFunc->nameSpace).AddressOf());
|
|
Error(str, node);
|
|
|
|
rctx->type.SetDummy();
|
|
}
|
|
|
|
// Make sure the rvalue is a variable
|
|
if( !rctx->type.isVariable )
|
|
ConvertToVariableNotIn(rctx, lvalueExpr);
|
|
}
|
|
else
|
|
{
|
|
asCDataType to = *lvalue;
|
|
to.MakeReference(false);
|
|
|
|
// TODO: ImplicitConversion should know to do this by itself
|
|
// First convert to a handle which will do a reference cast
|
|
if( !lvalue->IsObjectHandle() &&
|
|
(lvalue->GetTypeInfo()->flags & asOBJ_SCRIPT_OBJECT) )
|
|
to.MakeHandle(true);
|
|
|
|
// Don't allow the implicit conversion to create an object
|
|
ImplicitConversion(rctx, to, node, asIC_IMPLICIT_CONV, true, !toTemporary);
|
|
|
|
if( !lvalue->IsObjectHandle() &&
|
|
(lvalue->GetTypeInfo()->flags & asOBJ_SCRIPT_OBJECT) )
|
|
{
|
|
// Then convert to a reference, which will validate the handle
|
|
to.MakeHandle(false);
|
|
ImplicitConversion(rctx, to, node, asIC_IMPLICIT_CONV, true, !toTemporary);
|
|
}
|
|
|
|
// Check data type
|
|
if( !lvalue->IsEqualExceptRefAndConst(rctx->type.dataType) )
|
|
{
|
|
asCString str;
|
|
str.Format(TXT_CANT_IMPLICITLY_CONVERT_s_TO_s, rctx->type.dataType.Format(outFunc->nameSpace).AddressOf(), lvalue->Format(outFunc->nameSpace).AddressOf());
|
|
Error(str, node);
|
|
}
|
|
else
|
|
{
|
|
// If the assignment will be made with the copy behaviour then the rvalue must not be a reference
|
|
asASSERT(!lvalue->IsObject() || !rctx->type.dataType.IsReference());
|
|
}
|
|
}
|
|
|
|
// Unreserve variables
|
|
reservedVariables.SetLength(l);
|
|
}
|
|
|
|
bool asCCompiler::IsLValue(asCExprValue &type)
|
|
{
|
|
if( !type.isLValue ) return false;
|
|
if( type.dataType.IsReadOnly() ) return false;
|
|
if( !type.dataType.IsObject() && !type.isVariable && !type.dataType.IsReference() ) return false;
|
|
return true;
|
|
}
|
|
|
|
int asCCompiler::PerformAssignment(asCExprValue *lvalue, asCExprValue *rvalue, asCByteCode *bc, asCScriptNode *node)
|
|
{
|
|
if( lvalue->dataType.IsReadOnly() )
|
|
{
|
|
Error(TXT_REF_IS_READ_ONLY, node);
|
|
return -1;
|
|
}
|
|
|
|
if( lvalue->dataType.IsPrimitive() )
|
|
{
|
|
if( lvalue->isVariable )
|
|
{
|
|
// Copy the value between the variables directly
|
|
if( lvalue->dataType.GetSizeInMemoryDWords() == 1 )
|
|
bc->InstrW_W(asBC_CpyVtoV4, lvalue->stackOffset, rvalue->stackOffset);
|
|
else
|
|
bc->InstrW_W(asBC_CpyVtoV8, lvalue->stackOffset, rvalue->stackOffset);
|
|
|
|
// Mark variable as initialized
|
|
sVariable *v = variables->GetVariableByOffset(lvalue->stackOffset);
|
|
if( v ) v->isInitialized = true;
|
|
}
|
|
else if( lvalue->dataType.IsReference() )
|
|
{
|
|
// Copy the value of the variable to the reference in the register
|
|
int s = lvalue->dataType.GetSizeInMemoryBytes();
|
|
if( s == 1 )
|
|
bc->InstrSHORT(asBC_WRTV1, rvalue->stackOffset);
|
|
else if( s == 2 )
|
|
bc->InstrSHORT(asBC_WRTV2, rvalue->stackOffset);
|
|
else if( s == 4 )
|
|
bc->InstrSHORT(asBC_WRTV4, rvalue->stackOffset);
|
|
else if( s == 8 )
|
|
bc->InstrSHORT(asBC_WRTV8, rvalue->stackOffset);
|
|
}
|
|
else
|
|
{
|
|
Error(TXT_NOT_VALID_LVALUE, node);
|
|
return -1;
|
|
}
|
|
}
|
|
else if( !lvalue->isExplicitHandle )
|
|
{
|
|
asCExprContext ctx(engine);
|
|
ctx.type = *lvalue;
|
|
Dereference(&ctx, true);
|
|
*lvalue = ctx.type;
|
|
bc->AddCode(&ctx.bc);
|
|
|
|
asSTypeBehaviour *beh = lvalue->dataType.GetBehaviour();
|
|
if( beh && beh->copy && beh->copy != engine->scriptTypeBehaviours.beh.copy )
|
|
{
|
|
asCExprContext res(engine);
|
|
PerformFunctionCall(beh->copy, &res, false, 0, CastToObjectType(lvalue->dataType.GetTypeInfo()));
|
|
|
|
bc->AddCode(&res.bc);
|
|
*lvalue = res.type;
|
|
}
|
|
else if( beh && beh->copy == engine->scriptTypeBehaviours.beh.copy )
|
|
{
|
|
// Call the default copy operator for script classes
|
|
// This is done differently because the default copy operator
|
|
// is registered as returning int&, but in reality it returns
|
|
// a reference to the object.
|
|
// TODO: Avoid this special case by implementing a copystub for
|
|
// script classes that uses the default copy operator
|
|
bc->Call(asBC_CALLSYS, beh->copy, 2*AS_PTR_SIZE);
|
|
bc->Instr(asBC_PshRPtr);
|
|
}
|
|
else
|
|
{
|
|
// Default copy operator
|
|
if( lvalue->dataType.GetSizeInMemoryDWords() == 0 ||
|
|
!(lvalue->dataType.GetTypeInfo()->flags & asOBJ_POD) )
|
|
{
|
|
asCString msg;
|
|
msg.Format(TXT_NO_DEFAULT_COPY_OP_FOR_s, lvalue->dataType.GetTypeInfo()->name.AddressOf());
|
|
Error(msg, node);
|
|
return -1;
|
|
}
|
|
|
|
// Copy larger data types from a reference
|
|
// TODO: runtime optimize: COPY should pop both arguments and store the reference in the register.
|
|
bc->InstrSHORT_DW(asBC_COPY, (short)lvalue->dataType.GetSizeInMemoryDWords(), engine->GetTypeIdFromDataType(lvalue->dataType));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// TODO: The object handle can be stored in a variable as well
|
|
if( !lvalue->dataType.IsReference() )
|
|
{
|
|
Error(TXT_NOT_VALID_REFERENCE, node);
|
|
return -1;
|
|
}
|
|
|
|
if( lvalue->dataType.IsFuncdef() )
|
|
bc->InstrPTR(asBC_REFCPY, &engine->functionBehaviours);
|
|
else
|
|
bc->InstrPTR(asBC_REFCPY, lvalue->dataType.GetTypeInfo());
|
|
|
|
// Mark variable as initialized
|
|
if( variables )
|
|
{
|
|
sVariable *v = variables->GetVariableByOffset(lvalue->stackOffset);
|
|
if( v ) v->isInitialized = true;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
bool asCCompiler::CompileRefCast(asCExprContext *ctx, const asCDataType &to, bool isExplicit, asCScriptNode *node, bool generateCode)
|
|
{
|
|
bool conversionDone = false;
|
|
|
|
asCArray<int> ops;
|
|
|
|
// A ref cast must not remove the constness
|
|
bool isConst = ctx->type.dataType.IsObjectConst();
|
|
|
|
// Find a suitable opCast or opImplCast method
|
|
asCObjectType *ot = CastToObjectType(ctx->type.dataType.GetTypeInfo());
|
|
for( asUINT n = 0; ot && n < ot->methods.GetLength(); n++ )
|
|
{
|
|
asCScriptFunction *func = engine->scriptFunctions[ot->methods[n]];
|
|
if( (isExplicit && func->name == "opCast") ||
|
|
func->name == "opImplCast" )
|
|
{
|
|
// Is the operator for the output type?
|
|
if( func->returnType.GetTypeInfo() != to.GetTypeInfo() )
|
|
continue;
|
|
|
|
// Can't call a non-const function on a const object
|
|
if( isConst && !func->IsReadOnly() )
|
|
continue;
|
|
|
|
ops.PushLast(func->id);
|
|
}
|
|
}
|
|
|
|
// Filter the list by constness to remove const methods if there are matching non-const methods
|
|
FilterConst(ops, !isConst);
|
|
|
|
// If there is multiple matches, then pick the most appropriate one
|
|
if (ops.GetLength() > 1)
|
|
{
|
|
// This should only happen if an explicit cast is compiled
|
|
// and the type has both the opCast and opImplCast
|
|
asASSERT(isExplicit);
|
|
asASSERT(ops.GetLength() == 2);
|
|
|
|
for (asUINT n = 0; n < ops.GetLength(); n++)
|
|
{
|
|
asCScriptFunction *func = engine->scriptFunctions[ops[n]];
|
|
if (func->name == "opImplCast")
|
|
{
|
|
ops.RemoveIndex(n);
|
|
n--;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Should only have one behaviour for each output type
|
|
if( ops.GetLength() == 1 )
|
|
{
|
|
conversionDone = true;
|
|
if( generateCode )
|
|
{
|
|
// TODO: runtime optimize: Instead of producing bytecode for checking if the handle is
|
|
// null, we can create a special CALLSYS instruction that checks
|
|
// if the object pointer is null and if so sets the object register
|
|
// to null directly without executing the function.
|
|
//
|
|
// Alternatively I could force the ref cast behaviours be global
|
|
// functions with 1 parameter, even though they should still be
|
|
// registered with RegisterObjectBehaviour()
|
|
|
|
if( (ctx->type.dataType.GetTypeInfo()->flags & asOBJ_REF) && !(ctx->type.dataType.GetTypeInfo()->flags & asOBJ_NOHANDLE))
|
|
{
|
|
// Add code to avoid calling the cast behaviour if the handle is already null,
|
|
// because that will raise a null pointer exception due to the cast behaviour
|
|
// being a class method, and the this pointer cannot be null.
|
|
|
|
if (!ctx->type.isVariable)
|
|
{
|
|
Dereference(ctx, true);
|
|
ConvertToVariable(ctx);
|
|
}
|
|
|
|
// The reference on the stack will not be used
|
|
ctx->bc.Instr(asBC_PopPtr);
|
|
|
|
// TODO: runtime optimize: should have immediate comparison for null pointer
|
|
int offset = AllocateVariable(asCDataType::CreateNullHandle(), true);
|
|
// TODO: runtime optimize: ClrVPtr is not necessary, because the VM should initialize the variable to null anyway (it is currently not done for null pointers though)
|
|
ctx->bc.InstrSHORT(asBC_ClrVPtr, (asWORD)offset);
|
|
ctx->bc.InstrW_W(asBC_CmpPtr, ctx->type.stackOffset, offset);
|
|
DeallocateVariable(offset);
|
|
|
|
int afterLabel = nextLabel++;
|
|
ctx->bc.InstrDWORD(asBC_JZ, afterLabel);
|
|
|
|
// Call the cast operator
|
|
ctx->bc.InstrSHORT(asBC_PSF, ctx->type.stackOffset);
|
|
ctx->bc.Instr(asBC_RDSPtr);
|
|
ctx->type.dataType.MakeReference(false);
|
|
|
|
asCArray<asCExprContext *> args;
|
|
MakeFunctionCall(ctx, ops[0], CastToObjectType(ctx->type.dataType.GetTypeInfo()), args, node);
|
|
ctx->bc.Instr(asBC_PopPtr);
|
|
|
|
int endLabel = nextLabel++;
|
|
|
|
ctx->bc.InstrINT(asBC_JMP, endLabel);
|
|
ctx->bc.Label((short)afterLabel);
|
|
|
|
// Make a NULL pointer
|
|
ctx->bc.InstrSHORT(asBC_ClrVPtr, ctx->type.stackOffset);
|
|
ctx->bc.Label((short)endLabel);
|
|
|
|
// Push the reference to the handle on the stack
|
|
ctx->bc.InstrSHORT(asBC_PSF, ctx->type.stackOffset);
|
|
}
|
|
else
|
|
{
|
|
// Value types cannot be null, so there is no need to check for this.
|
|
|
|
// Likewise for reference types that are registered with asOBJ_NOHANDLE
|
|
// as those are only expected as registered global properties that cannot
|
|
// be modified anyway.
|
|
|
|
// Call the cast operator
|
|
asCArray<asCExprContext *> args;
|
|
MakeFunctionCall(ctx, ops[0], CastToObjectType(ctx->type.dataType.GetTypeInfo()), args, node);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
asCScriptFunction *func = engine->scriptFunctions[ops[0]];
|
|
ctx->type.Set(func->returnType);
|
|
}
|
|
}
|
|
else if( ops.GetLength() == 0 && !(ctx->type.dataType.GetTypeInfo()->flags & asOBJ_SCRIPT_OBJECT) && to.IsObjectHandle() )
|
|
{
|
|
// Check for the generic ref cast method: void opCast(?&out)
|
|
// This option only works if the expected type is a handle
|
|
for( asUINT n = 0; ot && n < ot->methods.GetLength(); n++ )
|
|
{
|
|
asCScriptFunction *func = engine->scriptFunctions[ot->methods[n]];
|
|
if( (isExplicit && func->name == "opCast") ||
|
|
func->name == "opImplCast" )
|
|
{
|
|
// Does the operator take the ?&out parameter?
|
|
if( func->returnType.GetTokenType() != ttVoid ||
|
|
func->parameterTypes.GetLength() != 1 ||
|
|
func->parameterTypes[0].GetTokenType() != ttQuestion ||
|
|
func->inOutFlags[0] != asTM_OUTREF )
|
|
continue;
|
|
|
|
ops.PushLast(func->id);
|
|
}
|
|
}
|
|
|
|
// Filter the list by constness to remove const methods if there are matching non-const methods
|
|
FilterConst(ops, !isConst);
|
|
|
|
// If there is multiple matches, then pick the most appropriate one
|
|
if (ops.GetLength() > 1)
|
|
{
|
|
// This should only happen if an explicit cast is compiled
|
|
// and the type has both the opCast and opImplCast
|
|
asASSERT(isExplicit);
|
|
asASSERT(ops.GetLength() == 2);
|
|
|
|
for (asUINT n = 0; n < ops.GetLength(); n++)
|
|
{
|
|
asCScriptFunction *func = engine->scriptFunctions[ops[n]];
|
|
if (func->name == "opImplCast")
|
|
{
|
|
ops.RemoveIndex(n);
|
|
n--;
|
|
}
|
|
}
|
|
}
|
|
|
|
if( ops.GetLength() == 1 )
|
|
{
|
|
conversionDone = true;
|
|
if( generateCode )
|
|
{
|
|
int afterLabel = 0;
|
|
bool doNullCheck = false;
|
|
bool releaseTempVariable = false;
|
|
asCExprContext tmp(engine);
|
|
if ((ctx->type.dataType.GetTypeInfo()->flags & asOBJ_REF) && !(ctx->type.dataType.GetTypeInfo()->flags & asOBJ_NOHANDLE))
|
|
{
|
|
tmp.bc.AddCode(&ctx->bc);
|
|
tmp.Merge(ctx);
|
|
|
|
// Add code to avoid calling the cast behaviour if the handle is already null,
|
|
// because that will raise a null pointer exception due to the cast behaviour
|
|
// being a class method, and the this pointer cannot be null.
|
|
doNullCheck = true;
|
|
if (!ctx->type.isVariable)
|
|
{
|
|
Dereference(&tmp, true);
|
|
ConvertToVariable(&tmp);
|
|
releaseTempVariable = true;
|
|
}
|
|
|
|
// The reference on the stack will not be used
|
|
tmp.bc.Instr(asBC_PopPtr);
|
|
|
|
// TODO: runtime optimize: should have immediate comparison for null pointer
|
|
int offset = AllocateVariable(asCDataType::CreateNullHandle(), true);
|
|
// TODO: runtime optimize: ClrVPtr is not necessary, because the VM should initialize the variable to null anyway (it is currently not done for null pointers though)
|
|
tmp.bc.InstrSHORT(asBC_ClrVPtr, (asWORD)offset);
|
|
tmp.bc.InstrW_W(asBC_CmpPtr, tmp.type.stackOffset, offset);
|
|
DeallocateVariable(offset);
|
|
|
|
afterLabel = nextLabel++;
|
|
tmp.bc.InstrDWORD(asBC_JZ, afterLabel);
|
|
|
|
// Place the object pointer on the stack
|
|
ctx->bc.InstrSHORT(asBC_PSF, (short)tmp.type.stackOffset);
|
|
}
|
|
|
|
// Allocate a temporary variable of the requested handle type
|
|
int stackOffset = AllocateVariableNotIn(to, true, false, ctx);
|
|
|
|
// Pass the reference of that variable to the function as output parameter
|
|
asCDataType toRef(to);
|
|
toRef.MakeReference(true);
|
|
asCArray<asCExprContext *> args;
|
|
asCExprContext arg(engine);
|
|
arg.bc.InstrSHORT(asBC_PSF, (short)stackOffset);
|
|
// Don't mark the variable as temporary, so it won't be freed too early
|
|
arg.type.SetVariable(toRef, stackOffset, false);
|
|
arg.type.isLValue = true;
|
|
arg.type.isExplicitHandle = true;
|
|
args.PushLast(&arg);
|
|
|
|
// Call the behaviour method
|
|
MakeFunctionCall(ctx, ops[0], CastToObjectType(ctx->type.dataType.GetTypeInfo()), args, node);
|
|
|
|
if (doNullCheck)
|
|
{
|
|
// Add the call after the null check
|
|
tmp.bc.AddCode(&ctx->bc);
|
|
ctx->bc.AddCode(&tmp.bc);
|
|
|
|
int endLabel = nextLabel++;
|
|
|
|
ctx->bc.InstrINT(asBC_JMP, endLabel);
|
|
ctx->bc.Label((short)afterLabel);
|
|
|
|
// Make a NULL pointer
|
|
ctx->bc.InstrSHORT(asBC_ClrVPtr, (short)stackOffset);
|
|
ctx->bc.Label((short)endLabel);
|
|
}
|
|
|
|
// If a temporary variable was allocated in the tmp to convert
|
|
// the input expression to a variable, it must be released here
|
|
if (releaseTempVariable && tmp.type.isTemporary)
|
|
ReleaseTemporaryVariable(tmp.type.stackOffset, &ctx->bc);
|
|
|
|
// Use the reference to the variable as the result of the expression
|
|
// Now we can mark the variable as temporary
|
|
ctx->type.SetVariable(toRef, stackOffset, true);
|
|
ctx->bc.InstrSHORT(asBC_PSF, (short)stackOffset);
|
|
}
|
|
else
|
|
{
|
|
// All casts are legal
|
|
ctx->type.Set(to);
|
|
}
|
|
}
|
|
}
|
|
|
|
// If the script object didn't implement a matching opCast or opImplCast
|
|
// then check if the desired type is part of the hierarchy
|
|
if( !conversionDone && (ctx->type.dataType.GetTypeInfo()->flags & asOBJ_SCRIPT_OBJECT) )
|
|
{
|
|
// We need it to be a reference
|
|
if( !ctx->type.dataType.IsReference() )
|
|
{
|
|
asCDataType toRef = ctx->type.dataType;
|
|
toRef.MakeReference(true);
|
|
ImplicitConversion(ctx, toRef, 0, isExplicit ? asIC_EXPLICIT_REF_CAST : asIC_IMPLICIT_CONV, generateCode);
|
|
}
|
|
|
|
if( isExplicit )
|
|
{
|
|
// Allow dynamic cast between object handles (only for script objects).
|
|
// At run time this may result in a null handle,
|
|
// which when used will throw an exception
|
|
conversionDone = true;
|
|
if( generateCode )
|
|
{
|
|
ctx->bc.InstrDWORD(asBC_Cast, engine->GetTypeIdFromDataType(to));
|
|
|
|
// Allocate a temporary variable for the returned object
|
|
int returnOffset = AllocateVariable(to, true);
|
|
|
|
// Move the pointer from the object register to the temporary variable
|
|
ctx->bc.InstrSHORT(asBC_STOREOBJ, (short)returnOffset);
|
|
|
|
ctx->bc.InstrSHORT(asBC_PSF, (short)returnOffset);
|
|
|
|
ReleaseTemporaryVariable(ctx->type, &ctx->bc);
|
|
|
|
ctx->type.SetVariable(to, returnOffset, true);
|
|
ctx->type.dataType.MakeReference(true);
|
|
}
|
|
else
|
|
{
|
|
ctx->type.dataType = to;
|
|
ctx->type.dataType.MakeReference(true);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( CastToObjectType(ctx->type.dataType.GetTypeInfo())->DerivesFrom(to.GetTypeInfo()) )
|
|
{
|
|
conversionDone = true;
|
|
ctx->type.dataType.SetTypeInfo(to.GetTypeInfo());
|
|
}
|
|
}
|
|
|
|
// A ref cast must not remove the constness
|
|
if( isConst )
|
|
ctx->type.dataType.MakeHandleToConst(true);
|
|
}
|
|
|
|
return conversionDone;
|
|
}
|
|
|
|
asUINT asCCompiler::ImplicitConvPrimitiveToPrimitive(asCExprContext *ctx, const asCDataType &toOrig, asCScriptNode *node, EImplicitConv convType, bool generateCode)
|
|
{
|
|
asCDataType to = toOrig;
|
|
to.MakeReference(false);
|
|
asASSERT( !ctx->type.dataType.IsReference() );
|
|
|
|
// Maybe no conversion is needed
|
|
if( to.IsEqualExceptConst(ctx->type.dataType) )
|
|
{
|
|
// A primitive is const or not
|
|
ctx->type.dataType.MakeReadOnly(to.IsReadOnly());
|
|
return asCC_NO_CONV;
|
|
}
|
|
|
|
// Is the conversion an ambiguous enum value?
|
|
if( ctx->enumValue != "" )
|
|
{
|
|
if( to.IsEnumType() )
|
|
{
|
|
// Attempt to resolve an ambiguous enum value
|
|
asCDataType out;
|
|
asDWORD value;
|
|
if( builder->GetEnumValueFromType(CastToEnumType(to.GetTypeInfo()), ctx->enumValue.AddressOf(), out, value) )
|
|
{
|
|
ctx->type.SetConstantDW(out, value);
|
|
ctx->type.dataType.MakeReadOnly(to.IsReadOnly());
|
|
|
|
// Reset the enum value since we no longer need it
|
|
ctx->enumValue = "";
|
|
|
|
// It wasn't really a conversion. The compiler just resolved the ambiguity (or not)
|
|
return asCC_NO_CONV;
|
|
}
|
|
}
|
|
|
|
// The enum value is ambiguous
|
|
if( node && generateCode )
|
|
Error(TXT_FOUND_MULTIPLE_ENUM_VALUES, node);
|
|
|
|
// Set a dummy to allow the compiler to try to continue the conversion
|
|
ctx->type.SetDummy();
|
|
}
|
|
|
|
// Determine the cost of this conversion
|
|
asUINT cost = asCC_NO_CONV;
|
|
if( (to.IsIntegerType() || to.IsUnsignedType()) && (ctx->type.dataType.IsFloatType() || ctx->type.dataType.IsDoubleType()) )
|
|
cost = asCC_INT_FLOAT_CONV;
|
|
else if ((to.IsFloatType() || to.IsDoubleType()) && (ctx->type.dataType.IsIntegerType() || ctx->type.dataType.IsUnsignedType()))
|
|
cost = asCC_INT_FLOAT_CONV;
|
|
else if (ctx->type.dataType.IsEnumType() && to.IsIntegerType() && to.GetSizeInMemoryBytes() == ctx->type.dataType.GetSizeInMemoryBytes() )
|
|
cost = asCC_ENUM_SAME_SIZE_CONV;
|
|
else if (ctx->type.dataType.IsEnumType() && to.IsIntegerType() && to.GetSizeInMemoryBytes() != ctx->type.dataType.GetSizeInMemoryBytes())
|
|
cost = asCC_ENUM_DIFF_SIZE_CONV;
|
|
else if( to.IsUnsignedType() && ctx->type.dataType.IsIntegerType() )
|
|
cost = asCC_SIGNED_CONV;
|
|
else if( to.IsIntegerType() && ctx->type.dataType.IsUnsignedType() )
|
|
cost = asCC_SIGNED_CONV;
|
|
else if( to.GetSizeInMemoryBytes() != ctx->type.dataType.GetSizeInMemoryBytes() )
|
|
cost = asCC_PRIMITIVE_SIZE_CONV;
|
|
|
|
// Start by implicitly converting constant values
|
|
if( ctx->type.isConstant )
|
|
{
|
|
ImplicitConversionConstant(ctx, to, node, convType);
|
|
ctx->type.dataType.MakeReadOnly(to.IsReadOnly());
|
|
return cost;
|
|
}
|
|
|
|
// Allow implicit conversion between numbers
|
|
if( generateCode )
|
|
{
|
|
// When generating the code the decision has already been made, so we don't bother determining the cost
|
|
|
|
// Convert smaller types to 32bit first
|
|
int s = ctx->type.dataType.GetSizeInMemoryBytes();
|
|
if( s < 4 )
|
|
{
|
|
ConvertToTempVariable(ctx);
|
|
if( ctx->type.dataType.IsIntegerType() )
|
|
{
|
|
if( s == 1 )
|
|
ctx->bc.InstrSHORT(asBC_sbTOi, ctx->type.stackOffset);
|
|
else if( s == 2 )
|
|
ctx->bc.InstrSHORT(asBC_swTOi, ctx->type.stackOffset);
|
|
ctx->type.dataType.SetTokenType(ttInt);
|
|
}
|
|
else if( ctx->type.dataType.IsUnsignedType() )
|
|
{
|
|
if( s == 1 )
|
|
ctx->bc.InstrSHORT(asBC_ubTOi, ctx->type.stackOffset);
|
|
else if( s == 2 )
|
|
ctx->bc.InstrSHORT(asBC_uwTOi, ctx->type.stackOffset);
|
|
ctx->type.dataType.SetTokenType(ttUInt);
|
|
}
|
|
}
|
|
|
|
if( (to.IsIntegerType() && to.GetSizeInMemoryDWords() == 1 && !to.IsEnumType()) ||
|
|
(to.IsEnumType() && convType == asIC_EXPLICIT_VAL_CAST) )
|
|
{
|
|
if( ctx->type.dataType.IsIntegerType() ||
|
|
ctx->type.dataType.IsUnsignedType() )
|
|
{
|
|
if( ctx->type.dataType.GetSizeInMemoryDWords() == 1 )
|
|
{
|
|
ctx->type.dataType.SetTokenType(to.GetTokenType());
|
|
ctx->type.dataType.SetTypeInfo(to.GetTypeInfo());
|
|
}
|
|
else
|
|
{
|
|
ConvertToTempVariable(ctx);
|
|
ReleaseTemporaryVariable(ctx->type, &ctx->bc);
|
|
int offset = AllocateVariable(to, true);
|
|
ctx->bc.InstrW_W(asBC_i64TOi, offset, ctx->type.stackOffset);
|
|
ctx->type.SetVariable(to, offset, true);
|
|
}
|
|
}
|
|
else if( ctx->type.dataType.IsFloatType() )
|
|
{
|
|
ConvertToTempVariable(ctx);
|
|
ctx->bc.InstrSHORT(asBC_fTOi, ctx->type.stackOffset);
|
|
ctx->type.dataType.SetTokenType(to.GetTokenType());
|
|
ctx->type.dataType.SetTypeInfo(to.GetTypeInfo());
|
|
|
|
if( convType != asIC_EXPLICIT_VAL_CAST )
|
|
Warning(TXT_FLOAT_CONV_TO_INT_CAUSE_TRUNC, node);
|
|
}
|
|
else if( ctx->type.dataType.IsDoubleType() )
|
|
{
|
|
ConvertToTempVariable(ctx);
|
|
ReleaseTemporaryVariable(ctx->type, &ctx->bc);
|
|
int offset = AllocateVariable(to, true);
|
|
ctx->bc.InstrW_W(asBC_dTOi, offset, ctx->type.stackOffset);
|
|
ctx->type.SetVariable(to, offset, true);
|
|
|
|
if( convType != asIC_EXPLICIT_VAL_CAST )
|
|
Warning(TXT_FLOAT_CONV_TO_INT_CAUSE_TRUNC, node);
|
|
}
|
|
|
|
// Convert to smaller integer if necessary
|
|
s = to.GetSizeInMemoryBytes();
|
|
if( s < 4 )
|
|
{
|
|
ConvertToTempVariable(ctx);
|
|
if( s == 1 )
|
|
ctx->bc.InstrSHORT(asBC_iTOb, ctx->type.stackOffset);
|
|
else if( s == 2 )
|
|
ctx->bc.InstrSHORT(asBC_iTOw, ctx->type.stackOffset);
|
|
}
|
|
}
|
|
else if( to.IsIntegerType() && to.GetSizeInMemoryDWords() == 2 )
|
|
{
|
|
if( ctx->type.dataType.IsIntegerType() ||
|
|
ctx->type.dataType.IsUnsignedType() )
|
|
{
|
|
if( ctx->type.dataType.GetSizeInMemoryDWords() == 2 )
|
|
{
|
|
ctx->type.dataType.SetTokenType(to.GetTokenType());
|
|
ctx->type.dataType.SetTypeInfo(to.GetTypeInfo());
|
|
}
|
|
else
|
|
{
|
|
ConvertToTempVariable(ctx);
|
|
ReleaseTemporaryVariable(ctx->type, &ctx->bc);
|
|
int offset = AllocateVariable(to, true);
|
|
if( ctx->type.dataType.IsUnsignedType() )
|
|
ctx->bc.InstrW_W(asBC_uTOi64, offset, ctx->type.stackOffset);
|
|
else
|
|
ctx->bc.InstrW_W(asBC_iTOi64, offset, ctx->type.stackOffset);
|
|
ctx->type.SetVariable(to, offset, true);
|
|
}
|
|
}
|
|
else if( ctx->type.dataType.IsFloatType() )
|
|
{
|
|
ConvertToTempVariable(ctx);
|
|
ReleaseTemporaryVariable(ctx->type, &ctx->bc);
|
|
int offset = AllocateVariable(to, true);
|
|
ctx->bc.InstrW_W(asBC_fTOi64, offset, ctx->type.stackOffset);
|
|
ctx->type.SetVariable(to, offset, true);
|
|
|
|
if( convType != asIC_EXPLICIT_VAL_CAST )
|
|
Warning(TXT_FLOAT_CONV_TO_INT_CAUSE_TRUNC, node);
|
|
}
|
|
else if( ctx->type.dataType.IsDoubleType() )
|
|
{
|
|
ConvertToTempVariable(ctx);
|
|
ctx->bc.InstrSHORT(asBC_dTOi64, ctx->type.stackOffset);
|
|
ctx->type.dataType.SetTokenType(to.GetTokenType());
|
|
ctx->type.dataType.SetTypeInfo(to.GetTypeInfo());
|
|
|
|
if( convType != asIC_EXPLICIT_VAL_CAST )
|
|
Warning(TXT_FLOAT_CONV_TO_INT_CAUSE_TRUNC, node);
|
|
}
|
|
}
|
|
else if( to.IsUnsignedType() && to.GetSizeInMemoryDWords() == 1 )
|
|
{
|
|
if( ctx->type.dataType.IsIntegerType() ||
|
|
ctx->type.dataType.IsUnsignedType() )
|
|
{
|
|
if( ctx->type.dataType.GetSizeInMemoryDWords() == 1 )
|
|
{
|
|
ctx->type.dataType.SetTokenType(to.GetTokenType());
|
|
ctx->type.dataType.SetTypeInfo(to.GetTypeInfo());
|
|
}
|
|
else
|
|
{
|
|
ConvertToTempVariable(ctx);
|
|
ReleaseTemporaryVariable(ctx->type, &ctx->bc);
|
|
int offset = AllocateVariable(to, true);
|
|
ctx->bc.InstrW_W(asBC_i64TOi, offset, ctx->type.stackOffset);
|
|
ctx->type.SetVariable(to, offset, true);
|
|
}
|
|
}
|
|
else if( ctx->type.dataType.IsFloatType() )
|
|
{
|
|
ConvertToTempVariable(ctx);
|
|
ctx->bc.InstrSHORT(asBC_fTOu, ctx->type.stackOffset);
|
|
ctx->type.dataType.SetTokenType(to.GetTokenType());
|
|
ctx->type.dataType.SetTypeInfo(to.GetTypeInfo());
|
|
|
|
if( convType != asIC_EXPLICIT_VAL_CAST )
|
|
Warning(TXT_FLOAT_CONV_TO_INT_CAUSE_TRUNC, node);
|
|
}
|
|
else if( ctx->type.dataType.IsDoubleType() )
|
|
{
|
|
ConvertToTempVariable(ctx);
|
|
ReleaseTemporaryVariable(ctx->type, &ctx->bc);
|
|
int offset = AllocateVariable(to, true);
|
|
ctx->bc.InstrW_W(asBC_dTOu, offset, ctx->type.stackOffset);
|
|
ctx->type.SetVariable(to, offset, true);
|
|
|
|
if( convType != asIC_EXPLICIT_VAL_CAST )
|
|
Warning(TXT_FLOAT_CONV_TO_INT_CAUSE_TRUNC, node);
|
|
}
|
|
|
|
// Convert to smaller integer if necessary
|
|
s = to.GetSizeInMemoryBytes();
|
|
if( s < 4 )
|
|
{
|
|
ConvertToTempVariable(ctx);
|
|
if( s == 1 )
|
|
ctx->bc.InstrSHORT(asBC_iTOb, ctx->type.stackOffset);
|
|
else if( s == 2 )
|
|
ctx->bc.InstrSHORT(asBC_iTOw, ctx->type.stackOffset);
|
|
}
|
|
}
|
|
else if( to.IsUnsignedType() && to.GetSizeInMemoryDWords() == 2 )
|
|
{
|
|
if( ctx->type.dataType.IsIntegerType() ||
|
|
ctx->type.dataType.IsUnsignedType() )
|
|
{
|
|
if( ctx->type.dataType.GetSizeInMemoryDWords() == 2 )
|
|
{
|
|
ctx->type.dataType.SetTokenType(to.GetTokenType());
|
|
ctx->type.dataType.SetTypeInfo(to.GetTypeInfo());
|
|
}
|
|
else
|
|
{
|
|
ConvertToTempVariable(ctx);
|
|
ReleaseTemporaryVariable(ctx->type, &ctx->bc);
|
|
int offset = AllocateVariable(to, true);
|
|
if( ctx->type.dataType.IsUnsignedType() )
|
|
ctx->bc.InstrW_W(asBC_uTOi64, offset, ctx->type.stackOffset);
|
|
else
|
|
ctx->bc.InstrW_W(asBC_iTOi64, offset, ctx->type.stackOffset);
|
|
ctx->type.SetVariable(to, offset, true);
|
|
}
|
|
}
|
|
else if( ctx->type.dataType.IsFloatType() )
|
|
{
|
|
ConvertToTempVariable(ctx);
|
|
ReleaseTemporaryVariable(ctx->type, &ctx->bc);
|
|
int offset = AllocateVariable(to, true);
|
|
ctx->bc.InstrW_W(asBC_fTOu64, offset, ctx->type.stackOffset);
|
|
ctx->type.SetVariable(to, offset, true);
|
|
|
|
if( convType != asIC_EXPLICIT_VAL_CAST )
|
|
Warning(TXT_FLOAT_CONV_TO_INT_CAUSE_TRUNC, node);
|
|
}
|
|
else if( ctx->type.dataType.IsDoubleType() )
|
|
{
|
|
ConvertToTempVariable(ctx);
|
|
ctx->bc.InstrSHORT(asBC_dTOu64, ctx->type.stackOffset);
|
|
ctx->type.dataType.SetTokenType(to.GetTokenType());
|
|
ctx->type.dataType.SetTypeInfo(to.GetTypeInfo());
|
|
|
|
if( convType != asIC_EXPLICIT_VAL_CAST )
|
|
Warning(TXT_FLOAT_CONV_TO_INT_CAUSE_TRUNC, node);
|
|
}
|
|
}
|
|
else if( to.IsFloatType() )
|
|
{
|
|
if( ctx->type.dataType.IsIntegerType() && ctx->type.dataType.GetSizeInMemoryDWords() == 1 )
|
|
{
|
|
ConvertToTempVariable(ctx);
|
|
ctx->bc.InstrSHORT(asBC_iTOf, ctx->type.stackOffset);
|
|
ctx->type.dataType.SetTokenType(to.GetTokenType());
|
|
ctx->type.dataType.SetTypeInfo(to.GetTypeInfo());
|
|
}
|
|
else if( ctx->type.dataType.IsIntegerType() && ctx->type.dataType.GetSizeInMemoryDWords() == 2 )
|
|
{
|
|
ConvertToTempVariable(ctx);
|
|
ReleaseTemporaryVariable(ctx->type, &ctx->bc);
|
|
int offset = AllocateVariable(to, true);
|
|
ctx->bc.InstrW_W(asBC_i64TOf, offset, ctx->type.stackOffset);
|
|
ctx->type.SetVariable(to, offset, true);
|
|
}
|
|
else if( ctx->type.dataType.IsUnsignedType() && ctx->type.dataType.GetSizeInMemoryDWords() == 1 )
|
|
{
|
|
ConvertToTempVariable(ctx);
|
|
ctx->bc.InstrSHORT(asBC_uTOf, ctx->type.stackOffset);
|
|
ctx->type.dataType.SetTokenType(to.GetTokenType());
|
|
ctx->type.dataType.SetTypeInfo(to.GetTypeInfo());
|
|
}
|
|
else if( ctx->type.dataType.IsUnsignedType() && ctx->type.dataType.GetSizeInMemoryDWords() == 2 )
|
|
{
|
|
ConvertToTempVariable(ctx);
|
|
ReleaseTemporaryVariable(ctx->type, &ctx->bc);
|
|
int offset = AllocateVariable(to, true);
|
|
ctx->bc.InstrW_W(asBC_u64TOf, offset, ctx->type.stackOffset);
|
|
ctx->type.SetVariable(to, offset, true);
|
|
}
|
|
else if( ctx->type.dataType.IsDoubleType() )
|
|
{
|
|
ConvertToTempVariable(ctx);
|
|
ReleaseTemporaryVariable(ctx->type, &ctx->bc);
|
|
int offset = AllocateVariable(to, true);
|
|
ctx->bc.InstrW_W(asBC_dTOf, offset, ctx->type.stackOffset);
|
|
ctx->type.SetVariable(to, offset, true);
|
|
}
|
|
}
|
|
else if( to.IsDoubleType() )
|
|
{
|
|
if( ctx->type.dataType.IsIntegerType() && ctx->type.dataType.GetSizeInMemoryDWords() == 1 )
|
|
{
|
|
ConvertToTempVariable(ctx);
|
|
ReleaseTemporaryVariable(ctx->type, &ctx->bc);
|
|
int offset = AllocateVariable(to, true);
|
|
ctx->bc.InstrW_W(asBC_iTOd, offset, ctx->type.stackOffset);
|
|
ctx->type.SetVariable(to, offset, true);
|
|
}
|
|
else if( ctx->type.dataType.IsIntegerType() && ctx->type.dataType.GetSizeInMemoryDWords() == 2 )
|
|
{
|
|
ConvertToTempVariable(ctx);
|
|
ctx->bc.InstrSHORT(asBC_i64TOd, ctx->type.stackOffset);
|
|
ctx->type.dataType.SetTokenType(to.GetTokenType());
|
|
ctx->type.dataType.SetTypeInfo(to.GetTypeInfo());
|
|
}
|
|
else if( ctx->type.dataType.IsUnsignedType() && ctx->type.dataType.GetSizeInMemoryDWords() == 1 )
|
|
{
|
|
ConvertToTempVariable(ctx);
|
|
ReleaseTemporaryVariable(ctx->type, &ctx->bc);
|
|
int offset = AllocateVariable(to, true);
|
|
ctx->bc.InstrW_W(asBC_uTOd, offset, ctx->type.stackOffset);
|
|
ctx->type.SetVariable(to, offset, true);
|
|
}
|
|
else if( ctx->type.dataType.IsUnsignedType() && ctx->type.dataType.GetSizeInMemoryDWords() == 2 )
|
|
{
|
|
ConvertToTempVariable(ctx);
|
|
ctx->bc.InstrSHORT(asBC_u64TOd, ctx->type.stackOffset);
|
|
ctx->type.dataType.SetTokenType(to.GetTokenType());
|
|
ctx->type.dataType.SetTypeInfo(to.GetTypeInfo());
|
|
}
|
|
else if( ctx->type.dataType.IsFloatType() )
|
|
{
|
|
ConvertToTempVariable(ctx);
|
|
ReleaseTemporaryVariable(ctx->type, &ctx->bc);
|
|
int offset = AllocateVariable(to, true);
|
|
ctx->bc.InstrW_W(asBC_fTOd, offset, ctx->type.stackOffset);
|
|
ctx->type.SetVariable(to, offset, true);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( ((to.IsIntegerType() && !to.IsEnumType()) || to.IsUnsignedType() ||
|
|
to.IsFloatType() || to.IsDoubleType() ||
|
|
(to.IsEnumType() && convType == asIC_EXPLICIT_VAL_CAST)) &&
|
|
(ctx->type.dataType.IsIntegerType() || ctx->type.dataType.IsUnsignedType() ||
|
|
ctx->type.dataType.IsFloatType() || ctx->type.dataType.IsDoubleType()) )
|
|
{
|
|
ctx->type.dataType.SetTokenType(to.GetTokenType());
|
|
ctx->type.dataType.SetTypeInfo(to.GetTypeInfo());
|
|
}
|
|
}
|
|
|
|
// Primitive types on the stack, can be const or non-const
|
|
ctx->type.dataType.MakeReadOnly(to.IsReadOnly());
|
|
return cost;
|
|
}
|
|
|
|
asUINT asCCompiler::ImplicitConvLambdaToFunc(asCExprContext *ctx, const asCDataType &to, asCScriptNode * /*node*/, EImplicitConv /*convType*/, bool generateCode)
|
|
{
|
|
asASSERT( to.IsFuncdef() && ctx->IsLambda() );
|
|
|
|
asCScriptFunction *funcDef = CastToFuncdefType(to.GetTypeInfo())->funcdef;
|
|
|
|
// Check that the lambda has the correct amount of arguments
|
|
asUINT count = 0;
|
|
asCScriptNode *argNode = ctx->exprNode->firstChild;
|
|
while( argNode->nodeType != snStatementBlock )
|
|
{
|
|
// Check if the specified parameter types match the funcdef
|
|
if (argNode->nodeType == snDataType)
|
|
{
|
|
asCDataType dt = builder->CreateDataTypeFromNode(argNode, script, outFunc->nameSpace, false, outFunc->objectType);
|
|
asETypeModifiers inOutFlag;
|
|
dt = builder->ModifyDataTypeFromNode(dt, argNode->next, script, &inOutFlag, 0);
|
|
|
|
if (count >= funcDef->parameterTypes.GetLength() ||
|
|
funcDef->parameterTypes[count] != dt ||
|
|
funcDef->inOutFlags[count] != inOutFlag)
|
|
return asCC_NO_CONV;
|
|
|
|
argNode = argNode->next;
|
|
}
|
|
|
|
if( argNode->nodeType == snIdentifier )
|
|
count++;
|
|
argNode = argNode->next;
|
|
}
|
|
|
|
if (funcDef->parameterTypes.GetLength() != count)
|
|
return asCC_NO_CONV;
|
|
|
|
asASSERT(argNode->nodeType == snStatementBlock);
|
|
|
|
// The Lambda can be used as this funcdef
|
|
ctx->type.dataType = to;
|
|
|
|
if( generateCode )
|
|
{
|
|
// Build a unique name for the anonymous function
|
|
asCString name;
|
|
if( m_globalVar )
|
|
name.Format("$%s$%d", m_globalVar->name.AddressOf(), numLambdas++);
|
|
else
|
|
name.Format("$%s$%d", outFunc->GetDeclaration(), numLambdas++);
|
|
|
|
// Register the lambda with the builder for later compilation
|
|
asCScriptFunction *func = builder->RegisterLambda(ctx->exprNode, script, funcDef, name, outFunc->nameSpace);
|
|
asASSERT( func == 0 || funcDef->IsSignatureExceptNameEqual(func) );
|
|
ctx->bc.InstrPTR(asBC_FuncPtr, func);
|
|
|
|
// Clear the expression node as it is no longer valid
|
|
ctx->exprNode = 0;
|
|
}
|
|
|
|
return asCC_CONST_CONV;
|
|
}
|
|
|
|
asUINT asCCompiler::ImplicitConversion(asCExprContext *ctx, const asCDataType &to, asCScriptNode *node, EImplicitConv convType, bool generateCode, bool allowObjectConstruct)
|
|
{
|
|
asASSERT( ctx->type.dataType.GetTokenType() != ttUnrecognizedToken ||
|
|
ctx->type.dataType.IsNullHandle() ||
|
|
ctx->IsAnonymousInitList() );
|
|
|
|
if( to.IsFuncdef() && ctx->IsLambda() )
|
|
return ImplicitConvLambdaToFunc(ctx, to, node, convType, generateCode);
|
|
|
|
if (ctx->IsAnonymousInitList())
|
|
{
|
|
if (to.GetBehaviour() && to.GetBehaviour()->listFactory)
|
|
{
|
|
if (generateCode)
|
|
CompileAnonymousInitList(ctx->exprNode, ctx, to);
|
|
else
|
|
ctx->type.dataType = to;
|
|
}
|
|
return asCC_NO_CONV;
|
|
}
|
|
|
|
// No conversion from void to any other type
|
|
if( ctx->type.dataType.GetTokenType() == ttVoid )
|
|
return asCC_NO_CONV;
|
|
|
|
// No conversion from class method to any type (it requires delegate)
|
|
if( ctx->IsClassMethod() )
|
|
return asCC_NO_CONV;
|
|
|
|
// Do we want a var type?
|
|
if( to.GetTokenType() == ttQuestion )
|
|
{
|
|
// Any type can be converted to a var type, but only when not generating code
|
|
asASSERT( !generateCode );
|
|
|
|
ctx->type.dataType = to;
|
|
|
|
return asCC_VARIABLE_CONV;
|
|
}
|
|
// Do we want a primitive?
|
|
else if( to.IsPrimitive() )
|
|
{
|
|
if( !ctx->type.dataType.IsPrimitive() )
|
|
return ImplicitConvObjectToPrimitive(ctx, to, node, convType, generateCode);
|
|
else
|
|
return ImplicitConvPrimitiveToPrimitive(ctx, to, node, convType, generateCode);
|
|
}
|
|
else // The target is a complex type
|
|
{
|
|
if( ctx->type.dataType.IsPrimitive() )
|
|
return ImplicitConvPrimitiveToObject(ctx, to, node, convType, generateCode, allowObjectConstruct);
|
|
else if( ctx->type.IsNullConstant() || ctx->type.dataType.GetTypeInfo() )
|
|
return ImplicitConvObjectToObject(ctx, to, node, convType, generateCode, allowObjectConstruct);
|
|
}
|
|
|
|
return asCC_NO_CONV;
|
|
}
|
|
|
|
asUINT asCCompiler::ImplicitConvObjectToPrimitive(asCExprContext *ctx, const asCDataType &to, asCScriptNode *node, EImplicitConv convType, bool generateCode)
|
|
{
|
|
if( ctx->type.isExplicitHandle )
|
|
{
|
|
// An explicit handle cannot be converted to a primitive
|
|
if( convType != asIC_IMPLICIT_CONV && node )
|
|
{
|
|
asCString str;
|
|
str.Format(TXT_CANT_IMPLICITLY_CONVERT_s_TO_s, ctx->type.dataType.Format(outFunc->nameSpace).AddressOf(), to.Format(outFunc->nameSpace).AddressOf());
|
|
Error(str, node);
|
|
}
|
|
return asCC_NO_CONV;
|
|
}
|
|
|
|
// Find matching value cast behaviours
|
|
// Here we're only interested in those that convert the type to a primitive type
|
|
asCArray<int> funcs;
|
|
asCObjectType *ot = CastToObjectType(ctx->type.dataType.GetTypeInfo());
|
|
if( ot == 0 )
|
|
{
|
|
if( convType != asIC_IMPLICIT_CONV && node )
|
|
{
|
|
asCString str;
|
|
str.Format(TXT_CANT_IMPLICITLY_CONVERT_s_TO_s, ctx->type.dataType.Format(outFunc->nameSpace).AddressOf(), to.Format(outFunc->nameSpace).AddressOf());
|
|
Error(str, node);
|
|
}
|
|
return asCC_NO_CONV;
|
|
}
|
|
|
|
|
|
if( convType == asIC_EXPLICIT_VAL_CAST )
|
|
{
|
|
for( unsigned int n = 0; n < ot->methods.GetLength(); n++ )
|
|
{
|
|
// accept both implicit and explicit cast
|
|
asCScriptFunction *mthd = engine->scriptFunctions[ot->methods[n]];
|
|
if( (mthd->name == "opConv" || mthd->name == "opImplConv") &&
|
|
mthd->parameterTypes.GetLength() == 0 &&
|
|
mthd->returnType.IsPrimitive() )
|
|
funcs.PushLast(ot->methods[n]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for( unsigned int n = 0; n < ot->methods.GetLength(); n++ )
|
|
{
|
|
// accept only implicit cast
|
|
asCScriptFunction *mthd = engine->scriptFunctions[ot->methods[n]];
|
|
if( mthd->name == "opImplConv" &&
|
|
mthd->parameterTypes.GetLength() == 0 &&
|
|
mthd->returnType.IsPrimitive() )
|
|
funcs.PushLast(ot->methods[n]);
|
|
}
|
|
}
|
|
|
|
FilterConst(funcs, !ctx->type.dataType.IsReadOnly());
|
|
|
|
int funcId = 0;
|
|
if( to.IsMathType() )
|
|
{
|
|
// This matrix describes the priorities of the types to search for, for each target type
|
|
// The first column is the target type, the priorities goes from left to right
|
|
eTokenType matchMtx[10][10] =
|
|
{
|
|
{ttDouble, ttFloat, ttInt64, ttUInt64, ttInt, ttUInt, ttInt16, ttUInt16, ttInt8, ttUInt8},
|
|
{ttFloat, ttDouble, ttInt64, ttUInt64, ttInt, ttUInt, ttInt16, ttUInt16, ttInt8, ttUInt8},
|
|
{ttInt64, ttUInt64, ttInt, ttUInt, ttInt16, ttUInt16, ttInt8, ttUInt8, ttDouble, ttFloat},
|
|
{ttUInt64, ttInt64, ttUInt, ttInt, ttUInt16, ttInt16, ttUInt8, ttInt8, ttDouble, ttFloat},
|
|
{ttInt, ttUInt, ttInt64, ttUInt64, ttInt16, ttUInt16, ttInt8, ttUInt8, ttDouble, ttFloat},
|
|
{ttUInt, ttInt, ttUInt64, ttInt64, ttUInt16, ttInt16, ttUInt8, ttInt8, ttDouble, ttFloat},
|
|
{ttInt16, ttUInt16, ttInt, ttUInt, ttInt64, ttUInt64, ttInt8, ttUInt8, ttDouble, ttFloat},
|
|
{ttUInt16, ttInt16, ttUInt, ttInt, ttUInt64, ttInt64, ttUInt8, ttInt8, ttDouble, ttFloat},
|
|
{ttInt8, ttUInt8, ttInt16, ttUInt16, ttInt, ttUInt, ttInt64, ttUInt64, ttDouble, ttFloat},
|
|
{ttUInt8, ttInt8, ttUInt16, ttInt16, ttUInt, ttInt, ttUInt64, ttInt64, ttDouble, ttFloat},
|
|
};
|
|
|
|
// Which row to use?
|
|
eTokenType *row = 0;
|
|
for( unsigned int type = 0; type < 10; type++ )
|
|
{
|
|
if( to.GetTokenType() == matchMtx[type][0] )
|
|
{
|
|
row = &matchMtx[type][0];
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Find the best matching cast operator
|
|
if( row )
|
|
{
|
|
asCDataType target(to);
|
|
|
|
// Priority goes from left to right in the matrix
|
|
for( unsigned int attempt = 0; attempt < 10 && funcId == 0; attempt++ )
|
|
{
|
|
target.SetTokenType(row[attempt]);
|
|
for( unsigned int n = 0; n < funcs.GetLength(); n++ )
|
|
{
|
|
asCScriptFunction *descr = builder->GetFunctionDescription(funcs[n]);
|
|
if( descr->returnType.IsEqualExceptRefAndConst(target) )
|
|
{
|
|
funcId = funcs[n];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Only accept the exact conversion for non-math types
|
|
|
|
// Find the matching cast operator
|
|
for( unsigned int n = 0; n < funcs.GetLength(); n++ )
|
|
{
|
|
asCScriptFunction *descr = builder->GetFunctionDescription(funcs[n]);
|
|
if( descr->returnType.IsEqualExceptRefAndConst(to) )
|
|
{
|
|
funcId = funcs[n];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Did we find a suitable function?
|
|
if( funcId != 0 )
|
|
{
|
|
asCScriptFunction *descr = builder->GetFunctionDescription(funcId);
|
|
if( generateCode )
|
|
{
|
|
Dereference(ctx, true);
|
|
PerformFunctionCall(funcId, ctx);
|
|
}
|
|
else
|
|
ctx->type.Set(descr->returnType);
|
|
|
|
// Allow one more implicit conversion to another primitive type
|
|
return asCC_OBJ_TO_PRIMITIVE_CONV + ImplicitConversion(ctx, to, node, convType, generateCode, false);
|
|
}
|
|
|
|
// TODO: clean-up: This part is similar to what is in ImplicitConvObjectValue
|
|
// If no direct conversion is found we should look for the generic form 'void opConv(?&out)'
|
|
funcs.SetLength(0);
|
|
for( asUINT n = 0; n < ot->methods.GetLength(); n++ )
|
|
{
|
|
asCScriptFunction *func = engine->scriptFunctions[ot->methods[n]];
|
|
if( ((convType == asIC_EXPLICIT_VAL_CAST) && func->name == "opConv") ||
|
|
func->name == "opImplConv" )
|
|
{
|
|
// Does the operator take the ?&out parameter?
|
|
if( func->returnType != asCDataType::CreatePrimitive(ttVoid, false) ||
|
|
func->parameterTypes.GetLength() != 1 ||
|
|
func->parameterTypes[0].GetTokenType() != ttQuestion ||
|
|
func->inOutFlags[0] != asTM_OUTREF )
|
|
continue;
|
|
|
|
funcs.PushLast(ot->methods[n]);
|
|
}
|
|
}
|
|
|
|
FilterConst(funcs, !ctx->type.dataType.IsReadOnly());
|
|
|
|
// If there are multiple valid value casts, then we must choose the most appropriate one
|
|
if (funcs.GetLength() > 1)
|
|
{
|
|
// This should only happen in case of explicit value cast and
|
|
// the application has registered both opImplConv and opConv
|
|
asASSERT(convType == asIC_EXPLICIT_VAL_CAST);
|
|
asASSERT(funcs.GetLength() == 2);
|
|
|
|
for (asUINT n = 0; n < funcs.GetLength(); n++)
|
|
{
|
|
asCScriptFunction *func = engine->scriptFunctions[funcs[n]];
|
|
if (func->name == "opImplConv")
|
|
{
|
|
funcs.RemoveIndex(n);
|
|
n--;
|
|
}
|
|
}
|
|
}
|
|
|
|
if( funcs.GetLength() == 1 )
|
|
{
|
|
if( generateCode )
|
|
{
|
|
// Allocate a temporary variable of the requested type
|
|
int stackOffset = AllocateVariableNotIn(to, true, false, ctx);
|
|
CallDefaultConstructor(to, stackOffset, IsVariableOnHeap(stackOffset), &ctx->bc, node);
|
|
|
|
// Pass the reference of that variable to the function as output parameter
|
|
asCDataType toRef(to);
|
|
toRef.MakeReference(true);
|
|
toRef.MakeReadOnly(false);
|
|
asCArray<asCExprContext *> args;
|
|
asCExprContext arg(engine);
|
|
// Don't mark the variable as temporary, so it won't be freed too early
|
|
arg.type.SetVariable(toRef, stackOffset, false);
|
|
arg.type.isLValue = true;
|
|
arg.exprNode = node;
|
|
args.PushLast(&arg);
|
|
|
|
// Call the behaviour method
|
|
MakeFunctionCall(ctx, funcs[0], CastToObjectType(ctx->type.dataType.GetTypeInfo()), args, node);
|
|
|
|
// Use the reference to the variable as the result of the expression
|
|
// Now we can mark the variable as temporary
|
|
toRef.MakeReference(false);
|
|
ctx->type.SetVariable(toRef, stackOffset, true);
|
|
}
|
|
else
|
|
ctx->type.Set(to);
|
|
|
|
return asCC_OBJ_TO_PRIMITIVE_CONV;
|
|
}
|
|
|
|
if( convType != asIC_IMPLICIT_CONV && node )
|
|
{
|
|
asCString str;
|
|
str.Format(TXT_CANT_IMPLICITLY_CONVERT_s_TO_s, ctx->type.dataType.Format(outFunc->nameSpace).AddressOf(), to.Format(outFunc->nameSpace).AddressOf());
|
|
Error(str, node);
|
|
}
|
|
|
|
return asCC_NO_CONV;
|
|
}
|
|
|
|
|
|
asUINT asCCompiler::ImplicitConvObjectRef(asCExprContext *ctx, const asCDataType &to, asCScriptNode *node, EImplicitConv convType, bool generateCode)
|
|
{
|
|
// Convert null to any object type handle, but not to a non-handle type
|
|
if( ctx->type.IsNullConstant() && ctx->methodName == "" )
|
|
{
|
|
if( to.IsObjectHandle() )
|
|
{
|
|
ctx->type.dataType = to;
|
|
return asCC_REF_CONV;
|
|
}
|
|
return asCC_NO_CONV;
|
|
}
|
|
|
|
asASSERT(ctx->type.dataType.GetTypeInfo() || ctx->methodName != "");
|
|
|
|
// First attempt to convert the base type without instantiating another instance
|
|
if( to.GetTypeInfo() != ctx->type.dataType.GetTypeInfo() && ctx->methodName == "" )
|
|
{
|
|
// If the to type is an interface and the from type implements it, then we can convert it immediately
|
|
if( ctx->type.dataType.GetTypeInfo()->Implements(to.GetTypeInfo()) )
|
|
{
|
|
ctx->type.dataType.SetTypeInfo(to.GetTypeInfo());
|
|
return asCC_REF_CONV;
|
|
}
|
|
// If the to type is a class and the from type derives from it, then we can convert it immediately
|
|
else if( ctx->type.dataType.GetTypeInfo()->DerivesFrom(to.GetTypeInfo()) )
|
|
{
|
|
ctx->type.dataType.SetTypeInfo(to.GetTypeInfo());
|
|
return asCC_REF_CONV;
|
|
}
|
|
// If the types are not equal yet, then we may still be able to find a reference cast
|
|
else if( ctx->type.dataType.GetTypeInfo() != to.GetTypeInfo() )
|
|
{
|
|
// We may still be able to find an implicit ref cast behaviour
|
|
CompileRefCast(ctx, to, convType == asIC_EXPLICIT_REF_CAST, node, generateCode);
|
|
|
|
// Was the conversion done?
|
|
if( ctx->type.dataType.GetTypeInfo() == to.GetTypeInfo() )
|
|
return asCC_REF_CONV;
|
|
}
|
|
}
|
|
|
|
// Convert matching function types
|
|
if( to.IsFuncdef() )
|
|
{
|
|
// If the input expression is already a funcdef, check if it can be converted
|
|
if( ctx->type.dataType.IsFuncdef() &&
|
|
to.GetTypeInfo() != ctx->type.dataType.GetTypeInfo() )
|
|
{
|
|
asCScriptFunction *toFunc = CastToFuncdefType(to.GetTypeInfo())->funcdef;
|
|
asCScriptFunction *fromFunc = CastToFuncdefType(ctx->type.dataType.GetTypeInfo())->funcdef;
|
|
if( toFunc->IsSignatureExceptNameEqual(fromFunc) )
|
|
{
|
|
ctx->type.dataType.SetTypeInfo(to.GetTypeInfo());
|
|
return asCC_REF_CONV;
|
|
}
|
|
}
|
|
|
|
// If the input expression is a deferred function ref, check if there is a matching func
|
|
if( ctx->methodName != "" )
|
|
{
|
|
// Determine the namespace
|
|
asSNameSpace *ns = 0;
|
|
asCString name = "";
|
|
int pos = ctx->methodName.FindLast("::");
|
|
if( pos >= 0 )
|
|
{
|
|
asCString nsName = ctx->methodName.SubString(0, pos+2);
|
|
// Trim off the last ::
|
|
if( nsName.GetLength() > 2 )
|
|
nsName.SetLength(nsName.GetLength()-2);
|
|
ns = DetermineNameSpace(nsName);
|
|
name = ctx->methodName.SubString(pos+2);
|
|
}
|
|
else
|
|
{
|
|
DetermineNameSpace("");
|
|
name = ctx->methodName;
|
|
}
|
|
|
|
asCArray<int> funcs;
|
|
if( ns )
|
|
builder->GetFunctionDescriptions(name.AddressOf(), funcs, ns);
|
|
|
|
// Check if any of the functions have perfect match
|
|
asCScriptFunction *toFunc = CastToFuncdefType(to.GetTypeInfo())->funcdef;
|
|
for( asUINT n = 0; n < funcs.GetLength(); n++ )
|
|
{
|
|
asCScriptFunction *func = builder->GetFunctionDescription(funcs[n]);
|
|
if( toFunc->IsSignatureExceptNameEqual(func) )
|
|
{
|
|
if( generateCode )
|
|
{
|
|
ctx->bc.InstrPTR(asBC_FuncPtr, func);
|
|
|
|
// Make sure the identified function is shared if we're compiling a shared function
|
|
if( !func->IsShared() && outFunc->IsShared() )
|
|
{
|
|
asCString msg;
|
|
msg.Format(TXT_SHARED_CANNOT_CALL_NON_SHARED_FUNC_s, func->GetDeclaration());
|
|
Error(msg, node);
|
|
}
|
|
}
|
|
|
|
ctx->type.dataType = asCDataType::CreateType(to.GetTypeInfo(), false);
|
|
return asCC_REF_CONV;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return asCC_NO_CONV;
|
|
}
|
|
|
|
asUINT asCCompiler::ImplicitConvObjectValue(asCExprContext *ctx, const asCDataType &to, asCScriptNode *node, EImplicitConv convType, bool generateCode)
|
|
{
|
|
asUINT cost = asCC_NO_CONV;
|
|
|
|
// If the base type is still different, and we are allowed to instance
|
|
// another object then we can try an implicit value cast
|
|
if( to.GetTypeInfo() != ctx->type.dataType.GetTypeInfo() )
|
|
{
|
|
// TODO: Implement support for implicit constructor/factory
|
|
asCObjectType *ot = CastToObjectType(ctx->type.dataType.GetTypeInfo());
|
|
if( ot == 0 )
|
|
return cost;
|
|
|
|
asCArray<int> funcs;
|
|
if( convType == asIC_EXPLICIT_VAL_CAST )
|
|
{
|
|
for( unsigned int n = 0; n < ot->methods.GetLength(); n++ )
|
|
{
|
|
asCScriptFunction *func = engine->scriptFunctions[ot->methods[n]];
|
|
|
|
// accept both implicit and explicit cast
|
|
if( (func->name == "opConv" ||
|
|
func->name == "opImplConv") &&
|
|
func->returnType.GetTypeInfo() == to.GetTypeInfo() &&
|
|
func->parameterTypes.GetLength() == 0 )
|
|
funcs.PushLast(ot->methods[n]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for( unsigned int n = 0; n < ot->methods.GetLength(); n++ )
|
|
{
|
|
asCScriptFunction *func = engine->scriptFunctions[ot->methods[n]];
|
|
|
|
// accept only implicit cast
|
|
if( func->name == "opImplConv" &&
|
|
func->returnType.GetTypeInfo() == to.GetTypeInfo() &&
|
|
func->parameterTypes.GetLength() == 0 )
|
|
funcs.PushLast(ot->methods[n]);
|
|
}
|
|
}
|
|
|
|
FilterConst(funcs, !ctx->type.dataType.IsReadOnly());
|
|
|
|
// If there are multiple valid value casts, then we must choose the most appropriate one
|
|
if (funcs.GetLength() > 1)
|
|
{
|
|
// This should only happen in case of explicit value cast and
|
|
// the application has registered both opImplConv and opConv
|
|
asASSERT(convType == asIC_EXPLICIT_VAL_CAST);
|
|
asASSERT(funcs.GetLength() == 2);
|
|
|
|
for (asUINT n = 0; n < funcs.GetLength(); n++)
|
|
{
|
|
asCScriptFunction *func = engine->scriptFunctions[funcs[n]];
|
|
if (func->name == "opImplConv")
|
|
{
|
|
funcs.RemoveIndex(n);
|
|
n--;
|
|
}
|
|
}
|
|
}
|
|
|
|
if( funcs.GetLength() == 1 )
|
|
{
|
|
asCScriptFunction *f = builder->GetFunctionDescription(funcs[0]);
|
|
if( generateCode )
|
|
{
|
|
Dereference(ctx, true);
|
|
|
|
bool useVariable = false;
|
|
int stackOffset = 0;
|
|
|
|
if( f->DoesReturnOnStack() )
|
|
{
|
|
useVariable = true;
|
|
stackOffset = AllocateVariable(f->returnType, true);
|
|
|
|
// Push the pointer to the pre-allocated space for the return value
|
|
ctx->bc.InstrSHORT(asBC_PSF, short(stackOffset));
|
|
|
|
// The object pointer is already on the stack, but should be the top
|
|
// one, so we need to swap the pointers in order to get the correct
|
|
ctx->bc.Instr(asBC_SwapPtr);
|
|
}
|
|
|
|
PerformFunctionCall(funcs[0], ctx, false, 0, 0, useVariable, stackOffset);
|
|
}
|
|
else
|
|
ctx->type.Set(f->returnType);
|
|
|
|
cost = asCC_TO_OBJECT_CONV;
|
|
}
|
|
else
|
|
{
|
|
// TODO: cleanup: This part is similar to the second half of ImplicitConvObjectToPrimitive
|
|
// Look for a value cast with variable type
|
|
for( asUINT n = 0; n < ot->methods.GetLength(); n++ )
|
|
{
|
|
asCScriptFunction *func = engine->scriptFunctions[ot->methods[n]];
|
|
if( ((convType == asIC_EXPLICIT_VAL_CAST) && func->name == "opConv") ||
|
|
func->name == "opImplConv" )
|
|
{
|
|
// Does the operator take the ?&out parameter?
|
|
if( func->returnType != asCDataType::CreatePrimitive(ttVoid, false) ||
|
|
func->parameterTypes.GetLength() != 1 ||
|
|
func->parameterTypes[0].GetTokenType() != ttQuestion ||
|
|
func->inOutFlags[0] != asTM_OUTREF )
|
|
continue;
|
|
|
|
funcs.PushLast(ot->methods[n]);
|
|
}
|
|
}
|
|
|
|
FilterConst(funcs, !ctx->type.dataType.IsReadOnly());
|
|
|
|
// If there are multiple valid value casts, then we must choose the most appropriate one
|
|
if (funcs.GetLength() > 1)
|
|
{
|
|
// This should only happen in case of explicit value cast and
|
|
// the application has registered both opImplConv and opConv
|
|
asASSERT(convType == asIC_EXPLICIT_VAL_CAST);
|
|
asASSERT(funcs.GetLength() == 2);
|
|
|
|
for (asUINT n = 0; n < funcs.GetLength(); n++)
|
|
{
|
|
asCScriptFunction *func = engine->scriptFunctions[funcs[n]];
|
|
if (func->name == "opImplConv")
|
|
{
|
|
funcs.RemoveIndex(n);
|
|
n--;
|
|
}
|
|
}
|
|
}
|
|
|
|
if( funcs.GetLength() == 1 )
|
|
{
|
|
cost = asCC_TO_OBJECT_CONV;
|
|
if( generateCode )
|
|
{
|
|
// Allocate a temporary variable of the requested type
|
|
int stackOffset = AllocateVariableNotIn(to, true, false, ctx);
|
|
CallDefaultConstructor(to, stackOffset, IsVariableOnHeap(stackOffset), &ctx->bc, node);
|
|
|
|
// Pass the reference of that variable to the function as output parameter
|
|
asCDataType toRef(to);
|
|
toRef.MakeReference(false);
|
|
asCExprContext arg(engine);
|
|
arg.bc.InstrSHORT(asBC_PSF, (short)stackOffset);
|
|
|
|
// If this an object on the heap, the pointer must be dereferenced
|
|
if( IsVariableOnHeap(stackOffset) )
|
|
arg.bc.Instr(asBC_RDSPtr);
|
|
|
|
// Don't mark the variable as temporary, so it won't be freed too early
|
|
arg.type.SetVariable(toRef, stackOffset, false);
|
|
arg.type.isLValue = true;
|
|
arg.exprNode = node;
|
|
|
|
// Mark the argument as clean, so that MakeFunctionCall knows it
|
|
// doesn't have to make a copy of it in order to protect the value
|
|
arg.isCleanArg = true;
|
|
|
|
// Call the behaviour method
|
|
asCArray<asCExprContext *> args;
|
|
args.PushLast(&arg);
|
|
MakeFunctionCall(ctx, funcs[0], CastToObjectType(ctx->type.dataType.GetTypeInfo()), args, node);
|
|
|
|
// Use the reference to the variable as the result of the expression
|
|
// Now we can mark the variable as temporary
|
|
ctx->type.SetVariable(toRef, stackOffset, true);
|
|
ctx->bc.InstrSHORT(asBC_PSF, (short)stackOffset);
|
|
}
|
|
else
|
|
{
|
|
// All casts are legal
|
|
ctx->type.Set(to);
|
|
}
|
|
}
|
|
else if( CastToObjectType(to.GetTypeInfo()) )
|
|
{
|
|
// If no opConv/opImplConv methods were found on the object, then try to find a conversion constructor on the target type
|
|
if( to.GetTypeInfo()->flags & asOBJ_REF )
|
|
funcs = CastToObjectType(to.GetTypeInfo())->beh.factories;
|
|
else
|
|
funcs = CastToObjectType(to.GetTypeInfo())->beh.constructors;
|
|
|
|
// If not explicit cast, remove any explicit conversion constructors
|
|
for (asUINT n = 0; n < funcs.GetLength(); n++)
|
|
{
|
|
asCScriptFunction *f = engine->scriptFunctions[funcs[n]];
|
|
if( f == 0 || f->parameterTypes.GetLength() != 1 || (convType != asIC_EXPLICIT_VAL_CAST && f->IsExplicit()) )
|
|
funcs.RemoveIndex(n--);
|
|
}
|
|
|
|
asCArray<asCExprContext *> args;
|
|
args.PushLast(ctx);
|
|
|
|
cost = asCC_TO_OBJECT_CONV + MatchFunctions(funcs, args, node, 0, 0, 0, false, true, false);
|
|
|
|
// Did we find a matching constructor?
|
|
if (funcs.GetLength() == 1)
|
|
{
|
|
if (generateCode)
|
|
{
|
|
// TODO: This should really reuse the code from CompileConstructCall
|
|
|
|
// Allocate the new object
|
|
asCExprValue tempObj;
|
|
asCExprContext e(engine);
|
|
bool onHeap = false;
|
|
if (to.GetTypeInfo()->flags & asOBJ_VALUE)
|
|
{
|
|
tempObj.dataType = to;
|
|
tempObj.dataType.MakeReference(false);
|
|
tempObj.stackOffset = (short)AllocateVariable(tempObj.dataType, true);
|
|
tempObj.dataType.MakeReference(true);
|
|
tempObj.isTemporary = true;
|
|
tempObj.isVariable = true;
|
|
|
|
onHeap = IsVariableOnHeap(tempObj.stackOffset);
|
|
|
|
// Push the address of the object on the stack
|
|
if (onHeap)
|
|
e.bc.InstrSHORT(asBC_VAR, tempObj.stackOffset);
|
|
}
|
|
|
|
PrepareFunctionCall(funcs[0], &e.bc, args);
|
|
MoveArgsToStack(funcs[0], &e.bc, args, false);
|
|
|
|
if (to.GetTypeInfo()->flags & asOBJ_VALUE)
|
|
{
|
|
// If the object is allocated on the stack, then call the constructor as a normal function
|
|
if (onHeap)
|
|
{
|
|
int offset = 0;
|
|
asCScriptFunction *descr = builder->GetFunctionDescription(funcs[0]);
|
|
offset = descr->parameterTypes[0].GetSizeOnStackDWords();
|
|
|
|
e.bc.InstrWORD(asBC_GETREF, (asWORD)offset);
|
|
}
|
|
else
|
|
e.bc.InstrSHORT(asBC_PSF, tempObj.stackOffset);
|
|
}
|
|
|
|
PerformFunctionCall(funcs[0], &e, onHeap, &args, CastToObjectType(tempObj.dataType.GetTypeInfo()));
|
|
|
|
if (to.GetTypeInfo()->flags & asOBJ_VALUE)
|
|
{
|
|
// Add tag that the object has been initialized
|
|
e.bc.ObjInfo(tempObj.stackOffset, asOBJ_INIT);
|
|
|
|
// The constructor doesn't return anything,
|
|
// so we have to manually inform the type of
|
|
// the return value
|
|
e.type = tempObj;
|
|
if (!onHeap)
|
|
e.type.dataType.MakeReference(false);
|
|
|
|
// Push the address of the object on the stack again
|
|
e.bc.InstrSHORT(asBC_PSF, tempObj.stackOffset);
|
|
}
|
|
|
|
MergeExprBytecodeAndType(ctx, &e);
|
|
}
|
|
else
|
|
{
|
|
ctx->type.Set(asCDataType::CreateType(to.GetTypeInfo(), false));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return cost;
|
|
}
|
|
|
|
asUINT asCCompiler::ImplicitConvObjectToObject(asCExprContext *ctx, const asCDataType &to, asCScriptNode *node, EImplicitConv convType, bool generateCode, bool allowObjectConstruct)
|
|
{
|
|
// First try a ref cast
|
|
asUINT cost = ImplicitConvObjectRef(ctx, to, node, convType, generateCode);
|
|
|
|
// If the desired type is an asOBJ_ASHANDLE then we'll assume it is allowed to implicitly
|
|
// construct the object through any of the available constructors
|
|
if( to.GetTypeInfo() && (to.GetTypeInfo()->flags & asOBJ_ASHANDLE) && to.GetTypeInfo() != ctx->type.dataType.GetTypeInfo() && allowObjectConstruct )
|
|
{
|
|
asCArray<int> funcs;
|
|
funcs = CastToObjectType(to.GetTypeInfo())->beh.constructors;
|
|
|
|
asCArray<asCExprContext *> args;
|
|
args.PushLast(ctx);
|
|
|
|
cost = asCC_TO_OBJECT_CONV + MatchFunctions(funcs, args, node, 0, 0, 0, false, true, false);
|
|
|
|
// Did we find a matching constructor?
|
|
if( funcs.GetLength() == 1 )
|
|
{
|
|
if( generateCode )
|
|
{
|
|
// If the ASHANDLE receives a variable type parameter, then we need to
|
|
// make sure the expression is treated as a handle and not as a value
|
|
asCScriptFunction *func = engine->scriptFunctions[funcs[0]];
|
|
if( func->parameterTypes[0].GetTokenType() == ttQuestion )
|
|
{
|
|
if( !ctx->type.isExplicitHandle )
|
|
{
|
|
asCDataType toHandle = ctx->type.dataType;
|
|
toHandle.MakeHandle(true);
|
|
toHandle.MakeReference(true);
|
|
toHandle.MakeHandleToConst(ctx->type.dataType.IsReadOnly());
|
|
ImplicitConversion(ctx, toHandle, node, asIC_IMPLICIT_CONV, true, false);
|
|
|
|
asASSERT( ctx->type.dataType.IsObjectHandle() );
|
|
}
|
|
ctx->type.isExplicitHandle = true;
|
|
}
|
|
|
|
// TODO: This should really reuse the code from CompileConstructCall
|
|
|
|
// Allocate the new object
|
|
asCExprValue tempObj;
|
|
tempObj.dataType = to;
|
|
tempObj.dataType.MakeReference(false);
|
|
tempObj.stackOffset = (short)AllocateVariable(tempObj.dataType, true);
|
|
tempObj.dataType.MakeReference(true);
|
|
tempObj.isTemporary = true;
|
|
tempObj.isVariable = true;
|
|
|
|
bool onHeap = IsVariableOnHeap(tempObj.stackOffset);
|
|
|
|
// Push the address of the object on the stack
|
|
asCExprContext e(engine);
|
|
if( onHeap )
|
|
e.bc.InstrSHORT(asBC_VAR, tempObj.stackOffset);
|
|
|
|
PrepareFunctionCall(funcs[0], &e.bc, args);
|
|
MoveArgsToStack(funcs[0], &e.bc, args, false);
|
|
|
|
// If the object is allocated on the stack, then call the constructor as a normal function
|
|
if( onHeap )
|
|
{
|
|
int offset = 0;
|
|
asCScriptFunction *descr = builder->GetFunctionDescription(funcs[0]);
|
|
offset = descr->parameterTypes[0].GetSizeOnStackDWords();
|
|
|
|
e.bc.InstrWORD(asBC_GETREF, (asWORD)offset);
|
|
}
|
|
else
|
|
e.bc.InstrSHORT(asBC_PSF, tempObj.stackOffset);
|
|
|
|
PerformFunctionCall(funcs[0], &e, onHeap, &args, CastToObjectType(tempObj.dataType.GetTypeInfo()));
|
|
|
|
// Add tag that the object has been initialized
|
|
e.bc.ObjInfo(tempObj.stackOffset, asOBJ_INIT);
|
|
|
|
// The constructor doesn't return anything,
|
|
// so we have to manually inform the type of
|
|
// the return value
|
|
e.type = tempObj;
|
|
if( !onHeap )
|
|
e.type.dataType.MakeReference(false);
|
|
|
|
// Push the address of the object on the stack again
|
|
e.bc.InstrSHORT(asBC_PSF, tempObj.stackOffset);
|
|
|
|
MergeExprBytecodeAndType(ctx, &e);
|
|
}
|
|
else
|
|
{
|
|
ctx->type.Set(asCDataType::CreateType(to.GetTypeInfo(), false));
|
|
}
|
|
}
|
|
}
|
|
|
|
// If the base type is still different, and we are allowed to instance
|
|
// another object then we can try an implicit value cast
|
|
if( to.GetTypeInfo() != ctx->type.dataType.GetTypeInfo() && allowObjectConstruct )
|
|
{
|
|
// Attempt implicit value cast
|
|
cost = ImplicitConvObjectValue(ctx, to, node, convType, generateCode);
|
|
}
|
|
|
|
// If we still haven't converted the base type to the correct type, then there is
|
|
// no need to continue as it is not possible to do the conversion
|
|
if( to.GetTypeInfo() != ctx->type.dataType.GetTypeInfo() )
|
|
return asCC_NO_CONV;
|
|
|
|
|
|
if( to.IsObjectHandle() )
|
|
{
|
|
// There is no extra cost in converting to a handle
|
|
|
|
// reference to handle -> handle
|
|
// reference -> handle
|
|
// object -> handle
|
|
// handle -> reference to handle
|
|
// reference -> reference to handle
|
|
// object -> reference to handle
|
|
|
|
if( (!ctx->type.dataType.IsObjectHandle() && ctx->type.dataType.IsReadOnly() && !to.IsHandleToConst()) ||
|
|
(ctx->type.dataType.IsObjectHandle() && ctx->type.dataType.IsHandleToConst() && !to.IsHandleToConst()) )
|
|
{
|
|
// String literals can be implicitly converted to temporary local variables in order to pass them to functions expecting non-const
|
|
// TODO: NEWSTRING: Should have an engine property to warn or error on this
|
|
if (ctx->type.isConstant && ctx->type.dataType.IsEqualExceptRefAndConst(engine->stringType))
|
|
{
|
|
if (generateCode)
|
|
PrepareTemporaryVariable(node, ctx);
|
|
else
|
|
{
|
|
ctx->type.dataType.MakeReadOnly(false);
|
|
ctx->type.isConstant = false;
|
|
}
|
|
|
|
// Add the cost for the copy
|
|
cost += asCC_TO_OBJECT_CONV;
|
|
}
|
|
else if( convType != asIC_IMPLICIT_CONV )
|
|
{
|
|
asASSERT(node);
|
|
asCString str;
|
|
str.Format(TXT_CANT_IMPLICITLY_CONVERT_s_TO_s, ctx->type.dataType.Format(outFunc->nameSpace).AddressOf(), to.Format(outFunc->nameSpace).AddressOf());
|
|
Error(str, node);
|
|
}
|
|
}
|
|
|
|
if( !ctx->type.dataType.IsObjectHandle() )
|
|
{
|
|
// An object type can be directly converted to a handle of the
|
|
// same type by doing a ref copy to a new variable
|
|
if( ctx->type.dataType.SupportHandles() )
|
|
{
|
|
asCDataType dt = ctx->type.dataType;
|
|
dt.MakeHandle(true);
|
|
dt.MakeReference(false);
|
|
|
|
if( generateCode )
|
|
{
|
|
// If the expression is already a local variable, then it is not
|
|
// necessary to do a ref copy, as the ref objects on the stack are
|
|
// really handles, only the handles cannot be modified.
|
|
if( ctx->type.isVariable )
|
|
{
|
|
bool isHandleToConst = ctx->type.dataType.IsReadOnly();
|
|
ctx->type.dataType.MakeReadOnly(false);
|
|
ctx->type.dataType.MakeHandle(true);
|
|
ctx->type.dataType.MakeReadOnly(true);
|
|
ctx->type.dataType.MakeHandleToConst(isHandleToConst);
|
|
|
|
if( to.IsReference() && !ctx->type.dataType.IsReference() )
|
|
{
|
|
ctx->bc.Instr(asBC_PopPtr);
|
|
ctx->bc.InstrSHORT(asBC_PSF, ctx->type.stackOffset);
|
|
ctx->type.dataType.MakeReference(true);
|
|
}
|
|
else if( ctx->type.dataType.IsReference() )
|
|
{
|
|
ctx->bc.Instr(asBC_RDSPtr);
|
|
ctx->type.dataType.MakeReference(false);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int offset = AllocateVariable(dt, true);
|
|
|
|
if( ctx->type.dataType.IsReference() )
|
|
ctx->bc.Instr(asBC_RDSPtr);
|
|
ctx->bc.InstrSHORT(asBC_PSF, (short)offset);
|
|
if (dt.IsFuncdef())
|
|
ctx->bc.InstrPTR(asBC_REFCPY, &engine->functionBehaviours);
|
|
else
|
|
ctx->bc.InstrPTR(asBC_REFCPY, dt.GetTypeInfo());
|
|
ctx->bc.Instr(asBC_PopPtr);
|
|
ctx->bc.InstrSHORT(asBC_PSF, (short)offset);
|
|
|
|
ReleaseTemporaryVariable(ctx->type, &ctx->bc);
|
|
|
|
if( to.IsReference() )
|
|
dt.MakeReference(true);
|
|
else
|
|
ctx->bc.Instr(asBC_RDSPtr);
|
|
|
|
ctx->type.SetVariable(dt, offset, true);
|
|
}
|
|
}
|
|
else
|
|
ctx->type.dataType = dt;
|
|
|
|
// When this conversion is done the expression is no longer an lvalue
|
|
ctx->type.isLValue = false;
|
|
}
|
|
}
|
|
|
|
if( ctx->type.dataType.IsObjectHandle() )
|
|
{
|
|
// A handle to non-const can be converted to a
|
|
// handle to const, but not the other way
|
|
if( to.IsHandleToConst() )
|
|
ctx->type.dataType.MakeHandleToConst(true);
|
|
|
|
// A const handle can be converted to a non-const
|
|
// handle and vice versa as the handle is just a value
|
|
ctx->type.dataType.MakeReadOnly(to.IsReadOnly());
|
|
}
|
|
|
|
if( to.IsReference() && !ctx->type.dataType.IsReference() )
|
|
{
|
|
if( generateCode )
|
|
{
|
|
asASSERT( ctx->type.dataType.IsObjectHandle() );
|
|
|
|
// If the input type is a handle, then a simple ref copy is enough
|
|
bool isExplicitHandle = ctx->type.isExplicitHandle;
|
|
ctx->type.isExplicitHandle = ctx->type.dataType.IsObjectHandle();
|
|
|
|
// If the input type is read-only we'll need to temporarily
|
|
// remove this constness, otherwise the assignment will fail
|
|
bool typeIsReadOnly = ctx->type.dataType.IsReadOnly();
|
|
ctx->type.dataType.MakeReadOnly(false);
|
|
|
|
// If the object already is a temporary variable, then the copy
|
|
// doesn't have to be made as it is already a unique object
|
|
PrepareTemporaryVariable(node, ctx);
|
|
|
|
ctx->type.dataType.MakeReadOnly(typeIsReadOnly);
|
|
ctx->type.isExplicitHandle = isExplicitHandle;
|
|
}
|
|
|
|
// A non-reference can be converted to a reference,
|
|
// by putting the value in a temporary variable
|
|
ctx->type.dataType.MakeReference(true);
|
|
|
|
// Since it is a new temporary variable it doesn't have to be const
|
|
ctx->type.dataType.MakeReadOnly(to.IsReadOnly());
|
|
}
|
|
else if( !to.IsReference() && ctx->type.dataType.IsReference() )
|
|
{
|
|
Dereference(ctx, generateCode);
|
|
}
|
|
}
|
|
else // if( !to.IsObjectHandle() )
|
|
{
|
|
if( !to.IsReference() )
|
|
{
|
|
// reference to handle -> object
|
|
// handle -> object
|
|
// reference -> object
|
|
|
|
// An implicit handle can be converted to an object by adding a check for null pointer
|
|
if( ctx->type.dataType.IsObjectHandle() && !ctx->type.isExplicitHandle )
|
|
{
|
|
if( generateCode )
|
|
{
|
|
if( ctx->type.dataType.IsReference() )
|
|
{
|
|
// The pointer on the stack refers to the handle
|
|
ctx->bc.Instr(asBC_ChkRefS);
|
|
}
|
|
else
|
|
{
|
|
// The pointer on the stack refers to the object
|
|
ctx->bc.Instr(asBC_CHKREF);
|
|
}
|
|
}
|
|
|
|
ctx->type.dataType.MakeHandle(false);
|
|
}
|
|
|
|
// A const object can be converted to a non-const object through a copy
|
|
if( ctx->type.dataType.IsReadOnly() && !to.IsReadOnly() &&
|
|
allowObjectConstruct )
|
|
{
|
|
// Does the object type allow a copy to be made?
|
|
if( ctx->type.dataType.CanBeCopied() )
|
|
{
|
|
if( generateCode )
|
|
{
|
|
// Make a temporary object with the copy
|
|
PrepareTemporaryVariable(node, ctx);
|
|
}
|
|
|
|
// In case the object was already in a temporary variable, then the function
|
|
// didn't really do anything so we need to remove the constness here
|
|
ctx->type.dataType.MakeReadOnly(false);
|
|
|
|
// Add the cost for the copy
|
|
cost += asCC_TO_OBJECT_CONV;
|
|
}
|
|
}
|
|
|
|
if( ctx->type.dataType.IsReference() )
|
|
{
|
|
// This may look strange, but a value type allocated on the stack is already
|
|
// correct, so nothing should be done other than remove the mark as reference.
|
|
// For types allocated on the heap, it is necessary to dereference the pointer
|
|
// that is currently on the stack
|
|
if( IsVariableOnHeap(ctx->type.stackOffset) )
|
|
Dereference(ctx, generateCode);
|
|
else
|
|
ctx->type.dataType.MakeReference(false);
|
|
}
|
|
|
|
// A non-const object can be converted to a const object directly
|
|
if( !ctx->type.dataType.IsReadOnly() && to.IsReadOnly() )
|
|
{
|
|
ctx->type.dataType.MakeReadOnly(true);
|
|
}
|
|
}
|
|
else // if( to.IsReference() )
|
|
{
|
|
// reference to handle -> reference
|
|
// handle -> reference
|
|
// object -> reference
|
|
|
|
if( ctx->type.dataType.IsReference() )
|
|
{
|
|
if( ctx->type.isExplicitHandle && ctx->type.dataType.GetTypeInfo() && (ctx->type.dataType.GetTypeInfo()->flags & asOBJ_ASHANDLE) )
|
|
{
|
|
// ASHANDLE objects are really value types, so explicit handle can be removed
|
|
ctx->type.isExplicitHandle = false;
|
|
ctx->type.dataType.MakeHandle(false);
|
|
}
|
|
|
|
// A reference to a handle can be converted to a reference to an object
|
|
// by first reading the address, then verifying that it is not null
|
|
if( !to.IsObjectHandle() && ctx->type.dataType.IsObjectHandle() && !ctx->type.isExplicitHandle )
|
|
{
|
|
ctx->type.dataType.MakeHandle(false);
|
|
if( generateCode )
|
|
ctx->bc.Instr(asBC_ChkRefS);
|
|
}
|
|
|
|
// A reference to a non-const can be converted to a reference to a const
|
|
if( to.IsReadOnly() )
|
|
ctx->type.dataType.MakeReadOnly(true);
|
|
else if( ctx->type.dataType.IsReadOnly() && allowObjectConstruct )
|
|
{
|
|
// A reference to a const can be converted to a reference to a
|
|
// non-const by copying the object to a temporary variable
|
|
ctx->type.dataType.MakeReadOnly(false);
|
|
|
|
if( generateCode )
|
|
{
|
|
// If the object already is a temporary variable, then the copy
|
|
// doesn't have to be made as it is already a unique object
|
|
PrepareTemporaryVariable(node, ctx);
|
|
}
|
|
|
|
// Add the cost for the copy
|
|
cost += asCC_TO_OBJECT_CONV;
|
|
}
|
|
}
|
|
else // if( !ctx->type.dataType.IsReference() )
|
|
{
|
|
// A non-reference handle can be converted to a non-handle reference by checking against null handle
|
|
if( ctx->type.dataType.IsObjectHandle() )
|
|
{
|
|
bool readOnly = false;
|
|
if( ctx->type.dataType.IsHandleToConst() )
|
|
readOnly = true;
|
|
|
|
if( generateCode )
|
|
{
|
|
if( ctx->type.isVariable )
|
|
ctx->bc.InstrSHORT(asBC_ChkNullV, ctx->type.stackOffset);
|
|
else
|
|
ctx->bc.Instr(asBC_CHKREF);
|
|
}
|
|
ctx->type.dataType.MakeHandle(false);
|
|
ctx->type.dataType.MakeReference(true);
|
|
|
|
// Make sure a handle to const isn't converted to non-const reference
|
|
if( readOnly )
|
|
ctx->type.dataType.MakeReadOnly(true);
|
|
}
|
|
else
|
|
{
|
|
// A value type allocated on the stack is differentiated
|
|
// by it not being a reference. But it can be handled as
|
|
// reference by pushing the pointer on the stack
|
|
if( (ctx->type.dataType.GetTypeInfo()->GetFlags() & asOBJ_VALUE) &&
|
|
(ctx->type.isVariable || ctx->type.isTemporary) &&
|
|
!IsVariableOnHeap(ctx->type.stackOffset) )
|
|
{
|
|
// Actually the pointer is already pushed on the stack in
|
|
// CompileVariableAccess, so we don't need to do anything else
|
|
}
|
|
else if( generateCode )
|
|
{
|
|
// A non-reference can be converted to a reference,
|
|
// by putting the value in a temporary variable
|
|
|
|
// If the input type is read-only we'll need to temporarily
|
|
// remove this constness, otherwise the assignment will fail
|
|
bool typeIsReadOnly = ctx->type.dataType.IsReadOnly();
|
|
ctx->type.dataType.MakeReadOnly(false);
|
|
|
|
// If the object already is a temporary variable, then the copy
|
|
// doesn't have to be made as it is already a unique object
|
|
PrepareTemporaryVariable(node, ctx);
|
|
|
|
ctx->type.dataType.MakeReadOnly(typeIsReadOnly);
|
|
|
|
// Add the cost for the copy
|
|
cost += asCC_TO_OBJECT_CONV;
|
|
}
|
|
|
|
// This may look strange as the conversion was to make the expression a reference
|
|
// but a value type allocated on the stack is a reference even without the type
|
|
// being marked as such.
|
|
ctx->type.dataType.MakeReference(IsVariableOnHeap(ctx->type.stackOffset));
|
|
}
|
|
|
|
if (to.IsReadOnly())
|
|
{
|
|
// This doesn't cost anything
|
|
ctx->type.dataType.MakeReadOnly(true);
|
|
}
|
|
|
|
if (!to.IsReadOnly() && ctx->type.dataType.IsReadOnly())
|
|
{
|
|
// A const object can be converted to a non-const object through a copy
|
|
if (allowObjectConstruct || convType == asIC_EXPLICIT_VAL_CAST)
|
|
{
|
|
ctx->type.dataType.MakeReadOnly(false);
|
|
|
|
if (generateCode)
|
|
{
|
|
// Make a temporary copy of the object in order to make it non-const
|
|
PrepareTemporaryVariable(node, ctx);
|
|
}
|
|
|
|
// Add the cost for the copy
|
|
cost += asCC_TO_OBJECT_CONV;
|
|
}
|
|
|
|
// String literals can be implicitly converted to temporary local variables in order to pass them to functions expecting non-const
|
|
// TODO: NEWSTRING: Should have an engine property to warn or error on this
|
|
if (ctx->type.isConstant && ctx->type.dataType.IsEqualExceptRefAndConst(engine->stringType))
|
|
{
|
|
if (generateCode)
|
|
PrepareTemporaryVariable(node, ctx);
|
|
else
|
|
{
|
|
ctx->type.dataType.MakeReadOnly(false);
|
|
ctx->type.isConstant = false;
|
|
}
|
|
|
|
// Add the cost for the copy
|
|
cost += asCC_TO_OBJECT_CONV;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return cost;
|
|
}
|
|
|
|
asUINT asCCompiler::ImplicitConvPrimitiveToObject(asCExprContext *ctx, const asCDataType &to, asCScriptNode *node, EImplicitConv isExplicit, bool generateCode, bool allowObjectConstruct)
|
|
{
|
|
asCObjectType *objType = CastToObjectType(to.GetTypeInfo());
|
|
asASSERT( objType || CastToFuncdefType(to.GetTypeInfo()) );
|
|
if( !objType )
|
|
return asCC_NO_CONV;
|
|
|
|
asCArray<int> funcs;
|
|
if (objType->flags & asOBJ_VALUE)
|
|
{
|
|
// For value types the object must have a constructor that takes a single primitive argument either by value or as input reference
|
|
for (asUINT n = 0; n < objType->beh.constructors.GetLength(); n++)
|
|
{
|
|
asCScriptFunction *func = engine->scriptFunctions[objType->beh.constructors[n]];
|
|
if (func->parameterTypes.GetLength() == 1 &&
|
|
func->parameterTypes[0].IsPrimitive() &&
|
|
!(func->inOutFlags[0] & asTM_OUTREF) &&
|
|
(isExplicit == asIC_EXPLICIT_VAL_CAST || !func->IsExplicit()) )
|
|
{
|
|
funcs.PushLast(func->id);
|
|
}
|
|
}
|
|
}
|
|
else if (objType->flags & asOBJ_REF)
|
|
{
|
|
// For ref types the object must have a factory that takes a single primitive argument either by value or as input reference
|
|
for (asUINT n = 0; n < objType->beh.factories.GetLength(); n++)
|
|
{
|
|
asCScriptFunction *func = engine->scriptFunctions[objType->beh.factories[n]];
|
|
if (func->parameterTypes.GetLength() == 1 &&
|
|
func->parameterTypes[0].IsPrimitive() &&
|
|
!(func->inOutFlags[0] & asTM_OUTREF) &&
|
|
(isExplicit == asIC_EXPLICIT_VAL_CAST || !func->IsExplicit()))
|
|
{
|
|
funcs.PushLast(func->id);
|
|
}
|
|
}
|
|
}
|
|
|
|
if( funcs.GetLength() == 0 )
|
|
return asCC_NO_CONV;
|
|
|
|
// Check if it is possible to choose a best match
|
|
asCExprContext arg(engine);
|
|
arg.type = ctx->type;
|
|
arg.exprNode = ctx->exprNode; // Use the same node for compiler messages
|
|
asCArray<asCExprContext*> args;
|
|
args.PushLast(&arg);
|
|
asUINT cost = asCC_TO_OBJECT_CONV + MatchFunctions(funcs, args, 0, 0, 0, objType, false, true, false);
|
|
if( funcs.GetLength() != 1 )
|
|
return asCC_NO_CONV;
|
|
|
|
if( !generateCode )
|
|
{
|
|
ctx->type.Set(to);
|
|
return cost;
|
|
}
|
|
|
|
// TODO: clean up: This part is similar to CompileConstructCall(). It should be put in a common function
|
|
|
|
// Clear the type of ctx, as the type is moved to the arg
|
|
ctx->type.SetDummy();
|
|
|
|
// Value types and script types are allocated through the constructor
|
|
asCExprValue tempObj;
|
|
bool onHeap = false;
|
|
|
|
if (!(objType->flags & asOBJ_REF))
|
|
{
|
|
tempObj.dataType = to;
|
|
tempObj.stackOffset = (short)AllocateVariable(to, true);
|
|
tempObj.dataType.MakeReference(true);
|
|
tempObj.isTemporary = true;
|
|
tempObj.isVariable = true;
|
|
|
|
onHeap = IsVariableOnHeap(tempObj.stackOffset);
|
|
|
|
// Push the address of the object on the stack
|
|
if (onHeap)
|
|
ctx->bc.InstrSHORT(asBC_VAR, tempObj.stackOffset);
|
|
}
|
|
|
|
PrepareFunctionCall(funcs[0], &ctx->bc, args);
|
|
MoveArgsToStack(funcs[0], &ctx->bc, args, false);
|
|
|
|
if( !(objType->flags & asOBJ_REF) )
|
|
{
|
|
// If the object is allocated on the stack, then call the constructor as a normal function
|
|
if( onHeap )
|
|
{
|
|
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);
|
|
}
|
|
else
|
|
ctx->bc.InstrSHORT(asBC_PSF, tempObj.stackOffset);
|
|
|
|
PerformFunctionCall(funcs[0], ctx, onHeap, &args, CastToObjectType(tempObj.dataType.GetTypeInfo()));
|
|
|
|
// Add tag that the object has been initialized
|
|
ctx->bc.ObjInfo(tempObj.stackOffset, asOBJ_INIT);
|
|
|
|
// The constructor doesn't return anything,
|
|
// so we have to manually inform the type of
|
|
// the return value
|
|
ctx->type = tempObj;
|
|
if( !onHeap )
|
|
ctx->type.dataType.MakeReference(false);
|
|
|
|
// Push the address of the object on the stack again
|
|
ctx->bc.InstrSHORT(asBC_PSF, tempObj.stackOffset);
|
|
}
|
|
else
|
|
{
|
|
// Call the factory to create the reference type
|
|
PerformFunctionCall(funcs[0], ctx, false, &args);
|
|
|
|
// Make another pass to make sure the result has the correct handle and reference settings
|
|
ImplicitConversion(ctx, to, node, isExplicit, generateCode, allowObjectConstruct);
|
|
}
|
|
|
|
return cost;
|
|
}
|
|
|
|
void asCCompiler::ImplicitConversionConstant(asCExprContext *from, const asCDataType &to, asCScriptNode *node, EImplicitConv convType)
|
|
{
|
|
asASSERT(from->type.isConstant);
|
|
|
|
// TODO: node should be the node of the value that is
|
|
// converted (not the operator that provokes the implicit
|
|
// conversion)
|
|
|
|
// If the base type is correct there is no more to do
|
|
if( to.IsEqualExceptRefAndConst(from->type.dataType) ) return;
|
|
|
|
// References cannot be constants
|
|
if( from->type.dataType.IsReference() ) return;
|
|
|
|
if( (to.IsIntegerType() && to.GetSizeInMemoryDWords() == 1 && !to.IsEnumType()) ||
|
|
(to.IsEnumType() && convType == asIC_EXPLICIT_VAL_CAST) )
|
|
{
|
|
if( from->type.dataType.IsFloatType() ||
|
|
from->type.dataType.IsDoubleType() ||
|
|
from->type.dataType.IsUnsignedType() ||
|
|
from->type.dataType.IsIntegerType() )
|
|
{
|
|
asCDataType targetDt;
|
|
if (to.IsEnumType())
|
|
targetDt = to;
|
|
else
|
|
targetDt = asCDataType::CreatePrimitive(ttInt, true);
|
|
|
|
// Transform the value
|
|
// Float constants can be implicitly converted to int
|
|
if( from->type.dataType.IsFloatType() )
|
|
{
|
|
float fc = from->type.GetConstantF();
|
|
int ic = int(fc);
|
|
|
|
if( float(ic) != fc )
|
|
{
|
|
if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_NOT_EXACT, node);
|
|
}
|
|
|
|
from->type.SetConstantDW(targetDt, ic);
|
|
}
|
|
// Double constants can be implicitly converted to int
|
|
else if( from->type.dataType.IsDoubleType() )
|
|
{
|
|
double fc = from->type.GetConstantD();
|
|
int ic = int(fc);
|
|
|
|
if( double(ic) != fc )
|
|
{
|
|
if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_NOT_EXACT, node);
|
|
}
|
|
|
|
from->type.SetConstantDW(targetDt, ic);
|
|
}
|
|
else if( from->type.dataType.IsUnsignedType() && from->type.dataType.GetSizeInMemoryDWords() == 1 )
|
|
{
|
|
// Verify that it is possible to convert to signed without getting negative
|
|
if( from->type.dataType.GetSizeInMemoryBytes() == 4 &&
|
|
int(from->type.GetConstantDW()) < 0 &&
|
|
convType != asIC_EXPLICIT_VAL_CAST &&
|
|
node != 0 )
|
|
Warning(TXT_CHANGE_SIGN, node);
|
|
|
|
// Convert to 32bit
|
|
if( from->type.dataType.GetSizeInMemoryBytes() == 1 )
|
|
from->type.SetConstantDW(targetDt, from->type.GetConstantB());
|
|
else if (from->type.dataType.GetSizeInMemoryBytes() == 2)
|
|
from->type.SetConstantDW(targetDt, from->type.GetConstantW());
|
|
else
|
|
from->type.dataType = targetDt;
|
|
}
|
|
else if( from->type.dataType.IsUnsignedType() && from->type.dataType.GetSizeInMemoryDWords() == 2 )
|
|
{
|
|
if (asQWORD(from->type.GetConstantQW()) >> 31)
|
|
if (convType != asIC_EXPLICIT_VAL_CAST && node) Warning(TXT_VALUE_TOO_LARGE_FOR_TYPE, node);
|
|
|
|
// Convert to 32bit
|
|
from->type.SetConstantDW(targetDt, int(from->type.GetConstantQW()));
|
|
}
|
|
else if (from->type.dataType.IsIntegerType() && from->type.dataType.GetSizeInMemoryDWords() == 2)
|
|
{
|
|
if (int(from->type.GetConstantQW()) != asINT64(from->type.GetConstantQW()))
|
|
if (convType != asIC_EXPLICIT_VAL_CAST && node) Warning(TXT_VALUE_TOO_LARGE_FOR_TYPE, node);
|
|
|
|
// Convert to 32bit
|
|
from->type.SetConstantDW(targetDt, int(from->type.GetConstantQW()));
|
|
}
|
|
else if (from->type.dataType.IsIntegerType() &&
|
|
from->type.dataType.GetSizeInMemoryBytes() < 4)
|
|
{
|
|
// Convert to 32bit
|
|
if (from->type.dataType.GetSizeInMemoryBytes() == 1)
|
|
from->type.SetConstantDW(targetDt, (asINT8)from->type.GetConstantB());
|
|
else if (from->type.dataType.GetSizeInMemoryBytes() == 2)
|
|
from->type.SetConstantDW(targetDt, (asINT16)from->type.GetConstantW());
|
|
}
|
|
else
|
|
{
|
|
// Only int32 and enums should come here and as these are 32bit
|
|
// already nothing needs to be done except set the target type
|
|
asASSERT((from->type.dataType.GetTokenType() == ttInt ||
|
|
from->type.dataType.IsEnumType()) &&
|
|
from->type.dataType.GetSizeInMemoryBytes() == 4);
|
|
|
|
from->type.dataType = targetDt;
|
|
}
|
|
}
|
|
|
|
// Check if a downsize is necessary
|
|
if( to.IsIntegerType() &&
|
|
from->type.dataType.IsIntegerType() &&
|
|
from->type.dataType.GetSizeInMemoryBytes() > to.GetSizeInMemoryBytes() )
|
|
{
|
|
// Verify if it is possible
|
|
if( to.GetSizeInMemoryBytes() == 1 )
|
|
{
|
|
if( asINT8(from->type.GetConstantDW()) != int(from->type.GetConstantDW()) )
|
|
if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_VALUE_TOO_LARGE_FOR_TYPE, node);
|
|
|
|
from->type.SetConstantB(asCDataType::CreatePrimitive(to.GetTokenType(), true), asINT8(from->type.GetConstantDW()));
|
|
}
|
|
else if( to.GetSizeInMemoryBytes() == 2 )
|
|
{
|
|
if( asINT16(from->type.GetConstantDW()) != int(from->type.GetConstantDW()) )
|
|
if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_VALUE_TOO_LARGE_FOR_TYPE, node);
|
|
|
|
from->type.SetConstantW(asCDataType::CreatePrimitive(to.GetTokenType(), true), asINT16(from->type.GetConstantDW()));
|
|
}
|
|
}
|
|
}
|
|
else if( to.IsIntegerType() && to.GetSizeInMemoryDWords() == 2 )
|
|
{
|
|
// Float constants can be implicitly converted to int
|
|
if( from->type.dataType.IsFloatType() )
|
|
{
|
|
float fc = from->type.GetConstantF();
|
|
asINT64 ic = asINT64(fc);
|
|
|
|
if( float(ic) != fc )
|
|
{
|
|
if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_NOT_EXACT, node);
|
|
}
|
|
|
|
from->type.SetConstantQW(asCDataType::CreatePrimitive(ttInt64, true), ic);
|
|
}
|
|
// Double constants can be implicitly converted to int
|
|
else if( from->type.dataType.IsDoubleType() )
|
|
{
|
|
double fc = from->type.GetConstantD();
|
|
asINT64 ic = asINT64(fc);
|
|
|
|
if( double(ic) != fc )
|
|
{
|
|
if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_NOT_EXACT, node);
|
|
}
|
|
|
|
from->type.SetConstantQW(asCDataType::CreatePrimitive(ttInt64, true), ic);
|
|
}
|
|
else if( from->type.dataType.IsUnsignedType() )
|
|
{
|
|
// Convert to 64bit
|
|
if( from->type.dataType.GetSizeInMemoryBytes() == 1 )
|
|
from->type.SetConstantQW(asCDataType::CreatePrimitive(ttInt64, true), from->type.GetConstantB());
|
|
else if( from->type.dataType.GetSizeInMemoryBytes() == 2 )
|
|
from->type.SetConstantQW(asCDataType::CreatePrimitive(ttInt64, true), from->type.GetConstantW());
|
|
else if( from->type.dataType.GetSizeInMemoryBytes() == 4 )
|
|
from->type.SetConstantQW(asCDataType::CreatePrimitive(ttInt64, true), from->type.GetConstantDW());
|
|
else if( from->type.dataType.GetSizeInMemoryBytes() == 8 )
|
|
{
|
|
if( asINT64(from->type.GetConstantQW()) < 0 )
|
|
{
|
|
if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_CHANGE_SIGN, node);
|
|
}
|
|
from->type.dataType = asCDataType::CreatePrimitive(ttInt64, true);
|
|
}
|
|
}
|
|
else if( from->type.dataType.IsIntegerType() )
|
|
{
|
|
// Convert to 64bit
|
|
if( from->type.dataType.GetSizeInMemoryBytes() == 1 )
|
|
from->type.SetConstantQW(asCDataType::CreatePrimitive(ttInt64, true), (asINT8)from->type.GetConstantB());
|
|
else if( from->type.dataType.GetSizeInMemoryBytes() == 2 )
|
|
from->type.SetConstantQW(asCDataType::CreatePrimitive(ttInt64, true), (asINT16)from->type.GetConstantW());
|
|
else if( from->type.dataType.GetSizeInMemoryBytes() == 4 )
|
|
from->type.SetConstantQW(asCDataType::CreatePrimitive(ttInt64, true), (int)from->type.GetConstantDW());
|
|
}
|
|
}
|
|
else if( to.IsUnsignedType() && to.GetSizeInMemoryDWords() == 1 )
|
|
{
|
|
if( from->type.dataType.IsFloatType() )
|
|
{
|
|
float fc = from->type.GetConstantF();
|
|
// Some compilers set the value to 0 when converting a negative float to unsigned int.
|
|
// To maintain a consistent behaviour across compilers we convert to int first.
|
|
asUINT uic = asUINT(int(fc));
|
|
|
|
if( float(uic) != fc )
|
|
{
|
|
if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_NOT_EXACT, node);
|
|
}
|
|
|
|
from->type.SetConstantDW(asCDataType::CreatePrimitive(ttUInt, true), uic);
|
|
|
|
// Try once more, in case of a smaller type
|
|
ImplicitConversionConstant(from, to, node, convType);
|
|
}
|
|
else if( from->type.dataType.IsDoubleType() )
|
|
{
|
|
double fc = from->type.GetConstantD();
|
|
// Some compilers set the value to 0 when converting a negative double to unsigned int.
|
|
// To maintain a consistent behaviour across compilers we convert to int first.
|
|
asUINT uic = asUINT(int(fc));
|
|
|
|
if( double(uic) != fc )
|
|
{
|
|
if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_NOT_EXACT, node);
|
|
}
|
|
|
|
from->type.SetConstantDW(asCDataType::CreatePrimitive(ttUInt, true), uic);
|
|
|
|
// Try once more, in case of a smaller type
|
|
ImplicitConversionConstant(from, to, node, convType);
|
|
}
|
|
else if( from->type.dataType.IsIntegerType() )
|
|
{
|
|
// Verify that it is possible to convert to unsigned without loosing negative
|
|
if( (from->type.dataType.GetSizeInMemoryBytes() > 4 && asINT64(from->type.GetConstantQW()) < 0) ||
|
|
(from->type.dataType.GetSizeInMemoryBytes() == 4 && int(from->type.GetConstantDW()) < 0) ||
|
|
(from->type.dataType.GetSizeInMemoryBytes() == 2 && asINT16(from->type.GetConstantW()) < 0) ||
|
|
(from->type.dataType.GetSizeInMemoryBytes() == 1 && asINT8(from->type.GetConstantB()) < 0))
|
|
{
|
|
if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_CHANGE_SIGN, node);
|
|
}
|
|
|
|
// Check if any data is lost
|
|
if( from->type.dataType.GetSizeInMemoryBytes() > 4 && (from->type.GetConstantQW() >> 32) != 0 && (from->type.GetConstantQW() >> 32) != 0xFFFFFFFF )
|
|
{
|
|
if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_VALUE_TOO_LARGE_FOR_TYPE, node);
|
|
}
|
|
|
|
// Convert to 32bit
|
|
if( from->type.dataType.GetSizeInMemoryBytes() == 1 )
|
|
from->type.SetConstantDW(asCDataType::CreatePrimitive(ttUInt, true), (asINT8)from->type.GetConstantB());
|
|
else if( from->type.dataType.GetSizeInMemoryBytes() == 2 )
|
|
from->type.SetConstantDW(asCDataType::CreatePrimitive(ttUInt, true), (asINT16)from->type.GetConstantW());
|
|
else if (from->type.dataType.GetSizeInMemoryBytes() == 4 )
|
|
from->type.SetConstantDW(asCDataType::CreatePrimitive(ttUInt, true), (int)from->type.GetConstantDW());
|
|
else
|
|
from->type.SetConstantDW(asCDataType::CreatePrimitive(ttUInt, true), (int)(asINT64)from->type.GetConstantQW());
|
|
|
|
// Try once more, in case of a smaller type
|
|
ImplicitConversionConstant(from, to, node, convType);
|
|
}
|
|
else if( from->type.dataType.IsUnsignedType() &&
|
|
from->type.dataType.GetSizeInMemoryBytes() < 4 )
|
|
{
|
|
// Convert to 32bit
|
|
if( from->type.dataType.GetSizeInMemoryBytes() == 1 )
|
|
from->type.SetConstantDW(asCDataType::CreatePrimitive(ttUInt, true), from->type.GetConstantB());
|
|
else if( from->type.dataType.GetSizeInMemoryBytes() == 2 )
|
|
from->type.SetConstantDW(asCDataType::CreatePrimitive(ttUInt, true), from->type.GetConstantW());
|
|
|
|
// Try once more, in case of a smaller type
|
|
ImplicitConversionConstant(from, to, node, convType);
|
|
}
|
|
else if( from->type.dataType.IsUnsignedType() &&
|
|
from->type.dataType.GetSizeInMemoryBytes() > to.GetSizeInMemoryBytes() )
|
|
{
|
|
// Verify if it is possible
|
|
if( to.GetSizeInMemoryBytes() == 1 )
|
|
{
|
|
if( asBYTE(from->type.GetConstantDW()) != from->type.GetConstantDW() )
|
|
if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_VALUE_TOO_LARGE_FOR_TYPE, node);
|
|
|
|
from->type.SetConstantB(asCDataType::CreatePrimitive(to.GetTokenType(), true), asBYTE(from->type.GetConstantDW()));
|
|
}
|
|
else if( to.GetSizeInMemoryBytes() == 2 )
|
|
{
|
|
if( asWORD(from->type.GetConstantDW()) != from->type.GetConstantDW())
|
|
if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_VALUE_TOO_LARGE_FOR_TYPE, node);
|
|
|
|
from->type.SetConstantW(asCDataType::CreatePrimitive(to.GetTokenType(), true), asWORD(from->type.GetConstantDW()));
|
|
}
|
|
else if (to.GetSizeInMemoryBytes() == 4)
|
|
{
|
|
if( asDWORD(from->type.GetConstantQW()) != from->type.GetConstantQW())
|
|
if (convType != asIC_EXPLICIT_VAL_CAST && node) Warning(TXT_VALUE_TOO_LARGE_FOR_TYPE, node);
|
|
|
|
from->type.SetConstantDW(asCDataType::CreatePrimitive(to.GetTokenType(), true), asDWORD(from->type.GetConstantQW()));
|
|
}
|
|
}
|
|
}
|
|
else if( to.IsUnsignedType() && to.GetSizeInMemoryDWords() == 2 )
|
|
{
|
|
if( from->type.dataType.IsFloatType() )
|
|
{
|
|
float fc = from->type.GetConstantF();
|
|
// Convert first to int64 then to uint64 to avoid negative float becoming 0 on gnuc base compilers
|
|
asQWORD uic = asQWORD(asINT64(fc));
|
|
|
|
#if !defined(_MSC_VER) || _MSC_VER > 1200 // MSVC++ 6
|
|
// MSVC6 doesn't support this conversion
|
|
if( float(uic) != fc )
|
|
{
|
|
if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_NOT_EXACT, node);
|
|
}
|
|
#endif
|
|
|
|
from->type.SetConstantQW(asCDataType::CreatePrimitive(ttUInt64, true), uic);
|
|
}
|
|
else if( from->type.dataType.IsDoubleType() )
|
|
{
|
|
double fc = from->type.GetConstantD();
|
|
// Convert first to int64 then to uint64 to avoid negative float becoming 0 on gnuc base compilers
|
|
asQWORD uic = asQWORD(asINT64(fc));
|
|
|
|
#if !defined(_MSC_VER) || _MSC_VER > 1200 // MSVC++ 6
|
|
// MSVC6 doesn't support this conversion
|
|
if( double(uic) != fc )
|
|
{
|
|
if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_NOT_EXACT, node);
|
|
}
|
|
#endif
|
|
|
|
from->type.SetConstantQW(asCDataType::CreatePrimitive(ttUInt64, true), uic);
|
|
}
|
|
else if( from->type.dataType.IsIntegerType() && from->type.dataType.GetSizeInMemoryDWords() == 1 )
|
|
{
|
|
// Convert to 64bit
|
|
if( from->type.dataType.GetSizeInMemoryBytes() == 1 )
|
|
from->type.SetConstantQW(asCDataType::CreatePrimitive(ttUInt64, true), (asINT64)(asINT8)from->type.GetConstantB());
|
|
else if( from->type.dataType.GetSizeInMemoryBytes() == 2 )
|
|
from->type.SetConstantQW(asCDataType::CreatePrimitive(ttUInt64, true), (asINT64)(asINT16)from->type.GetConstantW());
|
|
else if( from->type.dataType.GetSizeInMemoryBytes() == 4 )
|
|
from->type.SetConstantQW(asCDataType::CreatePrimitive(ttUInt64, true), (asINT64)(int)from->type.GetConstantDW());
|
|
|
|
// Verify that it is possible to convert to unsigned without loosing negative
|
|
if( asINT64(from->type.GetConstantQW()) < 0 )
|
|
{
|
|
if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_CHANGE_SIGN, node);
|
|
}
|
|
|
|
from->type.dataType = asCDataType::CreatePrimitive(ttUInt64, true);
|
|
}
|
|
else if( from->type.dataType.IsIntegerType() && from->type.dataType.GetSizeInMemoryDWords() == 2 )
|
|
{
|
|
// Verify that it is possible to convert to unsigned without loosing negative
|
|
if( asINT64(from->type.GetConstantQW()) < 0 )
|
|
{
|
|
if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_CHANGE_SIGN, node);
|
|
}
|
|
|
|
from->type.dataType = asCDataType::CreatePrimitive(ttUInt64, true);
|
|
}
|
|
else if( from->type.dataType.IsUnsignedType() )
|
|
{
|
|
// Convert to 64bit
|
|
if( from->type.dataType.GetSizeInMemoryBytes() == 1 )
|
|
from->type.SetConstantQW(asCDataType::CreatePrimitive(ttUInt64, true), from->type.GetConstantB());
|
|
else if( from->type.dataType.GetSizeInMemoryBytes() == 2 )
|
|
from->type.SetConstantQW(asCDataType::CreatePrimitive(ttUInt64, true), from->type.GetConstantW());
|
|
else if( from->type.dataType.GetSizeInMemoryBytes() == 4 )
|
|
from->type.SetConstantQW(asCDataType::CreatePrimitive(ttUInt64, true), from->type.GetConstantDW());
|
|
}
|
|
}
|
|
else if( to.IsFloatType() )
|
|
{
|
|
if( from->type.dataType.IsDoubleType() )
|
|
{
|
|
double ic = from->type.GetConstantD();
|
|
float fc = float(ic);
|
|
|
|
from->type.SetConstantF(asCDataType::CreatePrimitive(to.GetTokenType(), true), fc);
|
|
}
|
|
else if( from->type.dataType.IsIntegerType() && from->type.dataType.GetSizeInMemoryDWords() == 1 )
|
|
{
|
|
// Must properly convert value in case the from value is smaller
|
|
int ic;
|
|
if( from->type.dataType.GetSizeInMemoryBytes() == 1 )
|
|
ic = (asINT8)from->type.GetConstantB();
|
|
else if( from->type.dataType.GetSizeInMemoryBytes() == 2 )
|
|
ic = (asINT16)from->type.GetConstantW();
|
|
else
|
|
ic = (int)from->type.GetConstantDW();
|
|
float fc = float(ic);
|
|
|
|
if( int(fc) != ic )
|
|
{
|
|
if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_NOT_EXACT, node);
|
|
}
|
|
|
|
from->type.SetConstantF(asCDataType::CreatePrimitive(to.GetTokenType(), true), fc);
|
|
}
|
|
else if( from->type.dataType.IsIntegerType() && from->type.dataType.GetSizeInMemoryDWords() == 2 )
|
|
{
|
|
float fc = float(asINT64(from->type.GetConstantQW()));
|
|
if( asINT64(fc) != asINT64(from->type.GetConstantQW()) )
|
|
{
|
|
if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_NOT_EXACT, node);
|
|
}
|
|
|
|
from->type.SetConstantF(asCDataType::CreatePrimitive(to.GetTokenType(), true), fc);
|
|
}
|
|
else if( from->type.dataType.IsUnsignedType() && from->type.dataType.GetSizeInMemoryDWords() == 1 )
|
|
{
|
|
// Must properly convert value in case the from value is smaller
|
|
unsigned int uic;
|
|
if( from->type.dataType.GetSizeInMemoryBytes() == 1 )
|
|
uic = from->type.GetConstantB();
|
|
else if( from->type.dataType.GetSizeInMemoryBytes() == 2 )
|
|
uic = from->type.GetConstantW();
|
|
else
|
|
uic = from->type.GetConstantDW();
|
|
float fc = float(uic);
|
|
|
|
if( (unsigned int)(fc) != uic )
|
|
{
|
|
if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_NOT_EXACT, node);
|
|
}
|
|
|
|
from->type.SetConstantF(asCDataType::CreatePrimitive(to.GetTokenType(), true), fc);
|
|
}
|
|
else if( from->type.dataType.IsUnsignedType() && from->type.dataType.GetSizeInMemoryDWords() == 2 )
|
|
{
|
|
float fc = float((asINT64)from->type.GetConstantQW());
|
|
|
|
if( asQWORD(fc) != from->type.GetConstantQW())
|
|
{
|
|
if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_NOT_EXACT, node);
|
|
}
|
|
|
|
from->type.SetConstantF(asCDataType::CreatePrimitive(to.GetTokenType(), true), fc);
|
|
}
|
|
}
|
|
else if( to.IsDoubleType() )
|
|
{
|
|
if( from->type.dataType.IsFloatType() )
|
|
{
|
|
float ic = from->type.GetConstantF();
|
|
double fc = double(ic);
|
|
|
|
from->type.SetConstantD(asCDataType::CreatePrimitive(to.GetTokenType(), true), fc);
|
|
}
|
|
else if( from->type.dataType.IsIntegerType() && from->type.dataType.GetSizeInMemoryDWords() == 1 )
|
|
{
|
|
// Must properly convert value in case the from value is smaller
|
|
int ic;
|
|
if( from->type.dataType.GetSizeInMemoryBytes() == 1 )
|
|
ic = (asINT8)from->type.GetConstantB();
|
|
else if( from->type.dataType.GetSizeInMemoryBytes() == 2 )
|
|
ic = (asINT16)from->type.GetConstantW();
|
|
else
|
|
ic = (int)from->type.GetConstantDW();
|
|
double fc = double(ic);
|
|
|
|
if( int(fc) != ic )
|
|
{
|
|
if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_NOT_EXACT, node);
|
|
}
|
|
|
|
from->type.SetConstantD(asCDataType::CreatePrimitive(to.GetTokenType(), true), fc);
|
|
}
|
|
else if( from->type.dataType.IsIntegerType() && from->type.dataType.GetSizeInMemoryDWords() == 2 )
|
|
{
|
|
double fc = double(asINT64(from->type.GetConstantQW()));
|
|
|
|
if( asINT64(fc) != asINT64(from->type.GetConstantQW()) )
|
|
{
|
|
if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_NOT_EXACT, node);
|
|
}
|
|
|
|
from->type.SetConstantD(asCDataType::CreatePrimitive(to.GetTokenType(), true), fc);
|
|
}
|
|
else if( from->type.dataType.IsUnsignedType() && from->type.dataType.GetSizeInMemoryDWords() == 1 )
|
|
{
|
|
// Must properly convert value in case the from value is smaller
|
|
unsigned int uic;
|
|
if( from->type.dataType.GetSizeInMemoryBytes() == 1 )
|
|
uic = from->type.GetConstantB();
|
|
else if( from->type.dataType.GetSizeInMemoryBytes() == 2 )
|
|
uic = from->type.GetConstantW();
|
|
else
|
|
uic = from->type.GetConstantDW();
|
|
double fc = double(uic);
|
|
|
|
if( (unsigned int)(fc) != uic )
|
|
{
|
|
if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_NOT_EXACT, node);
|
|
}
|
|
|
|
from->type.SetConstantD(asCDataType::CreatePrimitive(to.GetTokenType(), true), fc);
|
|
}
|
|
else if( from->type.dataType.IsUnsignedType() && from->type.dataType.GetSizeInMemoryDWords() == 2 )
|
|
{
|
|
double fc = double((asINT64)from->type.GetConstantQW());
|
|
|
|
if( asQWORD(fc) != from->type.GetConstantQW())
|
|
{
|
|
if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_NOT_EXACT, node);
|
|
}
|
|
|
|
from->type.SetConstantD(asCDataType::CreatePrimitive(to.GetTokenType(), true), fc);
|
|
}
|
|
}
|
|
}
|
|
|
|
int asCCompiler::DoAssignment(asCExprContext *ctx, asCExprContext *lctx, asCExprContext *rctx, asCScriptNode *lexpr, asCScriptNode *rexpr, eTokenType op, asCScriptNode *opNode)
|
|
{
|
|
// Don't allow any operators on expressions that take address of class method
|
|
// If methodName is set but the type is not an object, then it is a global function
|
|
if( lctx->methodName != "" || rctx->IsClassMethod() )
|
|
{
|
|
Error(TXT_INVALID_OP_ON_METHOD, opNode);
|
|
return -1;
|
|
}
|
|
|
|
// Implicit handle types should always be treated as handles in assignments
|
|
if (lctx->type.dataType.GetTypeInfo() && (lctx->type.dataType.GetTypeInfo()->flags & asOBJ_IMPLICIT_HANDLE) )
|
|
{
|
|
lctx->type.dataType.MakeHandle(true);
|
|
lctx->type.isExplicitHandle = true;
|
|
}
|
|
|
|
// Urho3D: if there is a handle type, and it does not have an overloaded assignment operator, convert to an explicit handle
|
|
// for scripting convenience. (For the Urho3D handle types, value assignment is not supported)
|
|
if (lctx->type.dataType.IsObjectHandle() && !lctx->type.dataType.IsTemplate() && !lctx->type.isExplicitHandle &&
|
|
(!lctx->type.dataType.GetBehaviour() || !lctx->type.dataType.GetBehaviour()->copy))
|
|
lctx->type.isExplicitHandle = true;
|
|
|
|
// If the left hand expression is a property accessor, then that should be used
|
|
// to do the assignment instead of the ordinary operator. The exception is when
|
|
// the property accessor is for a handle property, and the operation is a value
|
|
// assignment.
|
|
if( (lctx->property_get || lctx->property_set) &&
|
|
!(lctx->type.dataType.IsObjectHandle() && !lctx->type.isExplicitHandle) )
|
|
{
|
|
if( op != ttAssignment )
|
|
{
|
|
// Generate the code for the compound assignment, i.e. get the value, apply operator, then set the value
|
|
return ProcessPropertyGetSetAccessor(ctx, lctx, rctx, op, opNode);
|
|
}
|
|
|
|
// It is not allowed to do a handle assignment on a property
|
|
// accessor that doesn't take a handle in the set accessor.
|
|
if( lctx->property_set && lctx->type.isExplicitHandle )
|
|
{
|
|
// set_opIndex has 2 arguments, where as normal setters have only 1
|
|
asCArray<asCDataType>& parameterTypes =
|
|
builder->GetFunctionDescription(lctx->property_set)->parameterTypes;
|
|
if( !parameterTypes[parameterTypes.GetLength() - 1].IsObjectHandle() )
|
|
{
|
|
// Process the property to free the memory
|
|
ProcessPropertySetAccessor(lctx, rctx, opNode);
|
|
|
|
Error(TXT_HANDLE_ASSIGN_ON_NON_HANDLE_PROP, opNode);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
MergeExprBytecodeAndType(ctx, lctx);
|
|
|
|
return ProcessPropertySetAccessor(ctx, rctx, opNode);
|
|
}
|
|
else if( lctx->property_get && lctx->type.dataType.IsObjectHandle() && !lctx->type.isExplicitHandle )
|
|
{
|
|
// Get the handle to the object that will be used for the value assignment
|
|
ProcessPropertyGetAccessor(lctx, opNode);
|
|
}
|
|
|
|
if( lctx->type.dataType.IsPrimitive() )
|
|
{
|
|
if( !lctx->type.isLValue )
|
|
{
|
|
Error(TXT_NOT_LVALUE, lexpr);
|
|
return -1;
|
|
}
|
|
|
|
if( op != ttAssignment )
|
|
{
|
|
// Compute the operator before the assignment
|
|
asCExprValue lvalue = lctx->type;
|
|
|
|
if( lctx->type.isTemporary && !lctx->type.isVariable )
|
|
{
|
|
// The temporary variable must not be freed until the
|
|
// assignment has been performed. lvalue still holds
|
|
// the information about the temporary variable
|
|
lctx->type.isTemporary = false;
|
|
}
|
|
|
|
asCExprContext o(engine);
|
|
CompileOperator(opNode, lctx, rctx, &o);
|
|
MergeExprBytecode(rctx, &o);
|
|
rctx->type = o.type;
|
|
|
|
// Convert the rvalue to the right type and validate it
|
|
PrepareForAssignment(&lvalue.dataType, rctx, rexpr, false);
|
|
|
|
MergeExprBytecode(ctx, rctx);
|
|
lctx->type = lvalue;
|
|
|
|
// The lvalue continues the same, either it was a variable, or a reference in the register
|
|
}
|
|
else
|
|
{
|
|
// Convert the rvalue to the right type and validate it
|
|
PrepareForAssignment(&lctx->type.dataType, rctx, rexpr, false, lctx);
|
|
|
|
MergeExprBytecode(ctx, rctx);
|
|
MergeExprBytecode(ctx, lctx);
|
|
}
|
|
|
|
ReleaseTemporaryVariable(rctx->type, &ctx->bc);
|
|
|
|
PerformAssignment(&lctx->type, &rctx->type, &ctx->bc, opNode);
|
|
|
|
ctx->type = lctx->type;
|
|
}
|
|
else if( lctx->type.isExplicitHandle )
|
|
{
|
|
if( !lctx->type.isLValue )
|
|
{
|
|
Error(TXT_NOT_LVALUE, lexpr);
|
|
return -1;
|
|
}
|
|
|
|
// Object handles don't have any compound assignment operators
|
|
if( op != ttAssignment )
|
|
{
|
|
asCString str;
|
|
str.Format(TXT_ILLEGAL_OPERATION_ON_s, lctx->type.dataType.Format(outFunc->nameSpace).AddressOf());
|
|
Error(str, lexpr);
|
|
return -1;
|
|
}
|
|
|
|
if( lctx->type.dataType.GetTypeInfo() && (lctx->type.dataType.GetTypeInfo()->flags & asOBJ_ASHANDLE) )
|
|
{
|
|
// The object is a value type but that should be treated as a handle
|
|
|
|
// Make sure the right hand value is a handle
|
|
if( !rctx->type.isExplicitHandle &&
|
|
!(rctx->type.dataType.GetTypeInfo() && (rctx->type.dataType.GetTypeInfo()->flags & asOBJ_ASHANDLE)) )
|
|
{
|
|
// Function names can be considered handles already
|
|
if( rctx->methodName == "" )
|
|
{
|
|
asCDataType dt = rctx->type.dataType;
|
|
dt.MakeHandle(true);
|
|
dt.MakeReference(false);
|
|
|
|
PrepareArgument(&dt, rctx, rexpr, true, asTM_INREF);
|
|
if( !dt.IsEqualExceptRefAndConst(rctx->type.dataType) )
|
|
{
|
|
asCString str;
|
|
str.Format(TXT_CANT_IMPLICITLY_CONVERT_s_TO_s, rctx->type.dataType.Format(outFunc->nameSpace).AddressOf(), lctx->type.dataType.Format(outFunc->nameSpace).AddressOf());
|
|
Error(str, rexpr);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (!rctx->type.dataType.IsObjectHandle() && !rctx->type.dataType.SupportHandles())
|
|
{
|
|
Error(TXT_OBJECT_HANDLE_NOT_SUPPORTED, rexpr);
|
|
return -1;
|
|
}
|
|
|
|
// Mark the right hand expression as explicit handle even if the user didn't do it, otherwise
|
|
// the code for moving the argument to the stack may not know to correctly handle the argument type
|
|
// in case of variable parameter type.
|
|
rctx->type.isExplicitHandle = true;
|
|
}
|
|
|
|
if( CompileOverloadedDualOperator(opNode, lctx, rctx, false, ctx, true) )
|
|
{
|
|
// An overloaded assignment operator was found (or a compilation error occured)
|
|
return 0;
|
|
}
|
|
|
|
// The object must implement the opAssign method
|
|
asCString msg;
|
|
msg.Format(TXT_NO_APPROPRIATE_OPHNDLASSIGN_s, lctx->type.dataType.Format(outFunc->nameSpace).AddressOf());
|
|
Error(msg.AddressOf(), opNode);
|
|
return -1;
|
|
}
|
|
else
|
|
{
|
|
asCDataType dt = lctx->type.dataType;
|
|
dt.MakeReference(false);
|
|
|
|
PrepareArgument(&dt, rctx, rexpr, false, asTM_INREF , true);
|
|
if( !dt.IsEqualExceptRefAndConst(rctx->type.dataType) )
|
|
{
|
|
asCString str;
|
|
str.Format(TXT_CANT_IMPLICITLY_CONVERT_s_TO_s, rctx->type.dataType.Format(outFunc->nameSpace).AddressOf(), lctx->type.dataType.Format(outFunc->nameSpace).AddressOf());
|
|
Error(str, rexpr);
|
|
return -1;
|
|
}
|
|
|
|
MergeExprBytecode(ctx, rctx);
|
|
MergeExprBytecode(ctx, lctx);
|
|
|
|
if(!rctx->type.isRefSafe)
|
|
ctx->bc.InstrWORD(asBC_GETOBJREF, AS_PTR_SIZE);
|
|
|
|
PerformAssignment(&lctx->type, &rctx->type, &ctx->bc, opNode);
|
|
|
|
ReleaseTemporaryVariable(rctx->type, &ctx->bc);
|
|
|
|
ctx->type = lctx->type;
|
|
|
|
// After the handle assignment the original handle is left on the stack
|
|
ctx->type.dataType.MakeReference(false);
|
|
}
|
|
}
|
|
else // if( lctx->type.dataType.IsObject() )
|
|
{
|
|
// The lvalue reference may be marked as a temporary, if for example
|
|
// it was originated as a handle returned from a function. In such
|
|
// cases it must be possible to assign values to it anyway.
|
|
if( lctx->type.dataType.IsObjectHandle() && !lctx->type.isExplicitHandle )
|
|
{
|
|
// Convert the handle to a object reference
|
|
asCDataType to;
|
|
to = lctx->type.dataType;
|
|
to.MakeHandle(false);
|
|
ImplicitConversion(lctx, to, lexpr, asIC_IMPLICIT_CONV);
|
|
lctx->type.isLValue = true; // Handle may not have been an lvalue, but the dereferenced object is
|
|
}
|
|
|
|
// Check for overloaded assignment operator
|
|
if( CompileOverloadedDualOperator(opNode, lctx, rctx, false, ctx) )
|
|
{
|
|
// An overloaded assignment operator was found (or a compilation error occured)
|
|
return 0;
|
|
}
|
|
|
|
// No registered operator was found. In case the operation is a direct
|
|
// assignment and the rvalue is the same type as the lvalue, then we can
|
|
// still use the byte-for-byte copy to do the assignment
|
|
|
|
if( op != ttAssignment )
|
|
{
|
|
asCString str;
|
|
str.Format(TXT_ILLEGAL_OPERATION_ON_s, lctx->type.dataType.Format(outFunc->nameSpace).AddressOf());
|
|
Error(str, lexpr);
|
|
return -1;
|
|
}
|
|
|
|
// If the left hand expression is simple, i.e. without any
|
|
// function calls or allocations of memory, then we can avoid
|
|
// doing a copy of the right hand expression (done by PrepareArgument).
|
|
// Instead the reference to the value can be placed directly on the
|
|
// stack.
|
|
//
|
|
// This optimization should only be done for value types, where
|
|
// the application developer is responsible for making the
|
|
// implementation safe against unwanted destruction of the input
|
|
// reference before the time.
|
|
bool simpleExpr = (lctx->type.dataType.GetTypeInfo()->GetFlags() & asOBJ_VALUE) && lctx->bc.IsSimpleExpression();
|
|
|
|
// Implicitly convert the rvalue to the type of the lvalue
|
|
bool needConversion = false;
|
|
if( !lctx->type.dataType.IsEqualExceptRefAndConst(rctx->type.dataType) )
|
|
needConversion = true;
|
|
|
|
if( !simpleExpr || needConversion )
|
|
{
|
|
asCDataType dt = lctx->type.dataType;
|
|
dt.MakeReference(true);
|
|
// A funcdef can be accessed by ref, but only as read-only
|
|
if( dt.IsFuncdef() && !dt.IsObjectHandle() )
|
|
dt.MakeReadOnly(true);
|
|
int r = PrepareArgument(&dt, rctx, rexpr, true, 1, !needConversion);
|
|
if( r < 0 )
|
|
return -1;
|
|
if( !dt.IsEqualExceptRefAndConst(rctx->type.dataType) )
|
|
{
|
|
asCString str;
|
|
str.Format(TXT_CANT_IMPLICITLY_CONVERT_s_TO_s, rctx->type.dataType.Format(outFunc->nameSpace).AddressOf(), lctx->type.dataType.Format(outFunc->nameSpace).AddressOf());
|
|
Error(str, rexpr);
|
|
return -1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Process any property accessor first, before placing the final reference on the stack
|
|
ProcessPropertyGetAccessor(rctx, rexpr);
|
|
|
|
if( rctx->type.dataType.IsReference() && (!(rctx->type.isVariable || rctx->type.isTemporary) || IsVariableOnHeap(rctx->type.stackOffset)) )
|
|
rctx->bc.Instr(asBC_RDSPtr);
|
|
}
|
|
|
|
MergeExprBytecode(ctx, rctx);
|
|
MergeExprBytecode(ctx, lctx);
|
|
|
|
if( !simpleExpr || needConversion )
|
|
{
|
|
if( !rctx->type.isRefSafe && (rctx->type.isVariable || rctx->type.isTemporary) )
|
|
{
|
|
if( !IsVariableOnHeap(rctx->type.stackOffset) )
|
|
// TODO: runtime optimize: Actually the reference can be pushed on the stack directly
|
|
// as the value allocated on the stack is guaranteed to be safe.
|
|
// The bytecode optimizer should be able to determine this and optimize away the VAR + GETREF
|
|
ctx->bc.InstrWORD(asBC_GETREF, AS_PTR_SIZE);
|
|
else
|
|
ctx->bc.InstrWORD(asBC_GETOBJREF, AS_PTR_SIZE);
|
|
}
|
|
}
|
|
|
|
PerformAssignment(&lctx->type, &rctx->type, &ctx->bc, opNode);
|
|
|
|
ReleaseTemporaryVariable(rctx->type, &ctx->bc);
|
|
|
|
ctx->type = lctx->type;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int asCCompiler::CompileAssignment(asCScriptNode *expr, asCExprContext *ctx)
|
|
{
|
|
asASSERT(expr->nodeType == snAssignment);
|
|
|
|
asCScriptNode *lexpr = expr->firstChild;
|
|
if( lexpr->next )
|
|
{
|
|
// Compile the two expression terms
|
|
asCExprContext lctx(engine), rctx(engine);
|
|
int rr = CompileAssignment(lexpr->next->next, &rctx);
|
|
int lr = CompileCondition(lexpr, &lctx);
|
|
|
|
if( lr >= 0 && rr >= 0 )
|
|
return DoAssignment(ctx, &lctx, &rctx, lexpr, lexpr->next->next, lexpr->next->tokenType, lexpr->next);
|
|
|
|
// Since the operands failed, the assignment was not computed
|
|
ctx->type.SetDummy();
|
|
return -1;
|
|
}
|
|
|
|
return CompileCondition(lexpr, ctx);
|
|
}
|
|
|
|
int asCCompiler::CompileCondition(asCScriptNode *expr, asCExprContext *ctx)
|
|
{
|
|
asCExprValue ctype;
|
|
|
|
// Compile the conditional expression
|
|
asCScriptNode *cexpr = expr->firstChild;
|
|
if( cexpr->next )
|
|
{
|
|
//-------------------------------
|
|
// Compile the condition
|
|
asCExprContext e(engine);
|
|
int r = CompileExpression(cexpr, &e);
|
|
if( r < 0 )
|
|
e.type.SetConstantB(asCDataType::CreatePrimitive(ttBool, true), true);
|
|
|
|
// Allow value types to be converted to bool using 'bool opImplConv()'
|
|
if( e.type.dataType.GetTypeInfo() && (e.type.dataType.GetTypeInfo()->GetFlags() & asOBJ_VALUE) )
|
|
ImplicitConversion(&e, asCDataType::CreatePrimitive(ttBool, false), cexpr, asIC_IMPLICIT_CONV);
|
|
|
|
if( r >= 0 && !e.type.dataType.IsEqualExceptRefAndConst(asCDataType::CreatePrimitive(ttBool, true)) )
|
|
{
|
|
Error(TXT_EXPR_MUST_BE_BOOL, cexpr);
|
|
e.type.SetConstantB(asCDataType::CreatePrimitive(ttBool, true), true);
|
|
}
|
|
ctype = e.type;
|
|
|
|
ProcessPropertyGetAccessor(&e, cexpr);
|
|
|
|
if( e.type.dataType.IsReference() ) ConvertToVariable(&e);
|
|
ProcessDeferredParams(&e);
|
|
|
|
//-------------------------------
|
|
// Compile the left expression
|
|
asCExprContext le(engine);
|
|
int lr = CompileAssignment(cexpr->next, &le);
|
|
|
|
// Resolve any function names already
|
|
DetermineSingleFunc(&le, cexpr->next);
|
|
|
|
//-------------------------------
|
|
// Compile the right expression
|
|
asCExprContext re(engine);
|
|
int rr = CompileAssignment(cexpr->next->next, &re);
|
|
DetermineSingleFunc(&re, cexpr->next->next);
|
|
|
|
if( lr >= 0 && rr >= 0 )
|
|
{
|
|
// Don't allow any operators on expressions that take address of class method
|
|
if( le.IsClassMethod() || re.IsClassMethod() )
|
|
{
|
|
Error(TXT_INVALID_OP_ON_METHOD, expr);
|
|
return -1;
|
|
}
|
|
|
|
ProcessPropertyGetAccessor(&le, cexpr->next);
|
|
ProcessPropertyGetAccessor(&re, cexpr->next->next);
|
|
|
|
bool isExplicitHandle = le.type.isExplicitHandle || re.type.isExplicitHandle;
|
|
|
|
// Allow a 0 or null in the first case to be implicitly converted to the second type
|
|
if( le.type.isConstant && le.type.GetConstantData() == 0 && le.type.dataType.IsIntegerType() )
|
|
{
|
|
asCDataType to = re.type.dataType;
|
|
to.MakeReference(false);
|
|
to.MakeReadOnly(true);
|
|
ImplicitConversionConstant(&le, to, cexpr->next, asIC_IMPLICIT_CONV);
|
|
}
|
|
else if( le.type.IsNullConstant() )
|
|
{
|
|
asCDataType to = re.type.dataType;
|
|
to.MakeHandle(true);
|
|
ImplicitConversion(&le, to, cexpr->next, asIC_IMPLICIT_CONV);
|
|
}
|
|
|
|
// Allow either case to be converted to const @ if the other is const @
|
|
if( (le.type.dataType.IsHandleToConst() && !le.type.IsNullConstant()) || (re.type.dataType.IsHandleToConst() && !re.type.dataType.IsNullHandle()) )
|
|
{
|
|
le.type.dataType.MakeHandleToConst(true);
|
|
re.type.dataType.MakeHandleToConst(true);
|
|
}
|
|
|
|
// Allow an anonymous initialization list to be converted to the type in the other condition
|
|
if (le.IsAnonymousInitList() && re.type.dataType.GetBehaviour() && re.type.dataType.GetBehaviour()->listFactory)
|
|
{
|
|
asCDataType to = re.type.dataType;
|
|
to.MakeReference(false);
|
|
to.MakeReadOnly(false);
|
|
ImplicitConversion(&le, to, cexpr->next, asIC_IMPLICIT_CONV);
|
|
}
|
|
else if (re.IsAnonymousInitList() && le.type.dataType.GetBehaviour() && le.type.dataType.GetBehaviour()->listFactory)
|
|
{
|
|
asCDataType to = le.type.dataType;
|
|
to.MakeReference(false);
|
|
to.MakeReadOnly(false);
|
|
ImplicitConversion(&re, to, cexpr->next->next, asIC_IMPLICIT_CONV);
|
|
}
|
|
|
|
if (le.IsAnonymousInitList() )
|
|
{
|
|
Error(TXT_CANNOT_RESOLVE_AUTO, cexpr->next);
|
|
return -1;
|
|
}
|
|
else if (re.IsAnonymousInitList())
|
|
{
|
|
Error(TXT_CANNOT_RESOLVE_AUTO, cexpr->next->next);
|
|
return -1;
|
|
}
|
|
|
|
//---------------------------------
|
|
// Output the byte code
|
|
int afterLabel = nextLabel++;
|
|
int elseLabel = nextLabel++;
|
|
|
|
// If left expression is void, then we don't need to store the result
|
|
if( le.type.dataType.IsEqualExceptConst(asCDataType::CreatePrimitive(ttVoid, false)) )
|
|
{
|
|
// Put the code for the condition expression on the output
|
|
MergeExprBytecode(ctx, &e);
|
|
|
|
// Added the branch decision
|
|
ctx->type = e.type;
|
|
ConvertToVariable(ctx);
|
|
ctx->bc.InstrSHORT(asBC_CpyVtoR4, ctx->type.stackOffset);
|
|
ctx->bc.Instr(asBC_ClrHi);
|
|
ctx->bc.InstrDWORD(asBC_JZ, elseLabel);
|
|
ReleaseTemporaryVariable(ctx->type, &ctx->bc);
|
|
|
|
// Add the left expression
|
|
MergeExprBytecode(ctx, &le);
|
|
ctx->bc.InstrINT(asBC_JMP, afterLabel);
|
|
|
|
// Add the right expression
|
|
ctx->bc.Label((short)elseLabel);
|
|
MergeExprBytecode(ctx, &re);
|
|
ctx->bc.Label((short)afterLabel);
|
|
|
|
// Make sure both expressions have the same type
|
|
if( le.type.dataType != re.type.dataType )
|
|
Error(TXT_BOTH_MUST_BE_SAME, expr);
|
|
|
|
// Set the type of the result
|
|
ctx->type = le.type;
|
|
}
|
|
else if (le.type.IsNullConstant() && re.type.IsNullConstant())
|
|
{
|
|
// Special case for when both results are 'null'
|
|
// TODO: Other expressions where both results are identical literal constants can probably also be handled this way
|
|
|
|
// Put the code for the condition expression on the output
|
|
MergeExprBytecode(ctx, &e);
|
|
|
|
// Load the result into the register, but ignore the value since both paths give the same response
|
|
ctx->type = e.type;
|
|
ConvertToVariable(ctx);
|
|
ctx->bc.InstrSHORT(asBC_CpyVtoR4, ctx->type.stackOffset);
|
|
ReleaseTemporaryVariable(ctx->type, &ctx->bc);
|
|
|
|
// Return a null constant
|
|
ctx->bc.Instr(asBC_PshNull);
|
|
ctx->type.SetNullConstant();
|
|
}
|
|
else
|
|
{
|
|
// Allow "(a ? b : c) = d;" and "return (a ? b : c);" (where the latter returns the reference)
|
|
//
|
|
// Restrictions for the condition to be used as lvalue:
|
|
// 1. both b and c must be of the same type and be lvalue references
|
|
// 2. neither of the expressions can have any deferred arguments
|
|
// that would have to be cleaned up after the reference
|
|
// 3. neither expression can be temporary
|
|
//
|
|
// If either expression is local, the resulting lvalue is not valid
|
|
// for return since it is not allowed to return references to local
|
|
// variables.
|
|
//
|
|
// The reference to the local variable must be loaded into the register,
|
|
// the resulting expression must not be considered as a local variable
|
|
// with a stack offset (i.e. it will not be allowed to use asBC_VAR)
|
|
|
|
if( le.type.isLValue && re.type.isLValue &&
|
|
le.deferredParams.GetLength() == 0 && re.deferredParams.GetLength() ==0 &&
|
|
!le.type.isTemporary && !re.type.isTemporary &&
|
|
le.type.dataType == re.type.dataType )
|
|
{
|
|
// Put the code for the condition expression on the output
|
|
MergeExprBytecode(ctx, &e);
|
|
|
|
// Add the branch decision
|
|
ctx->type = e.type;
|
|
ConvertToVariable(ctx);
|
|
ctx->bc.InstrSHORT(asBC_CpyVtoR4, ctx->type.stackOffset);
|
|
ctx->bc.Instr(asBC_ClrHi);
|
|
ctx->bc.InstrDWORD(asBC_JZ, elseLabel);
|
|
ReleaseTemporaryVariable(ctx->type, &ctx->bc);
|
|
|
|
// Start of the left expression
|
|
MergeExprBytecode(ctx, &le);
|
|
if( !le.type.dataType.IsReference() && le.type.isVariable )
|
|
{
|
|
// Load the address of the variable into the register
|
|
ctx->bc.InstrSHORT(asBC_LDV, le.type.stackOffset);
|
|
}
|
|
|
|
ctx->bc.InstrINT(asBC_JMP, afterLabel);
|
|
|
|
// Start of the right expression
|
|
ctx->bc.Label((short)elseLabel);
|
|
|
|
MergeExprBytecode(ctx, &re);
|
|
if( !re.type.dataType.IsReference() && re.type.isVariable )
|
|
{
|
|
// Load the address of the variable into the register
|
|
ctx->bc.InstrSHORT(asBC_LDV, re.type.stackOffset);
|
|
}
|
|
|
|
ctx->bc.Label((short)afterLabel);
|
|
|
|
// In case the options were to objects, it is necessary to dereference the pointer on
|
|
// the stack so it will point to the actual object, instead of the variable
|
|
if( le.type.dataType.IsReference() && le.type.dataType.IsObject() && !le.type.dataType.IsObjectHandle() )
|
|
{
|
|
asASSERT( re.type.dataType.IsReference() && re.type.dataType.IsObject() && !re.type.dataType.IsObjectHandle() );
|
|
|
|
ctx->bc.Instr(asBC_RDSPtr);
|
|
}
|
|
|
|
// The result is an lvalue
|
|
ctx->type.isLValue = true;
|
|
ctx->type.dataType = le.type.dataType;
|
|
if( ctx->type.dataType.IsPrimitive() || ctx->type.dataType.IsObjectHandle() )
|
|
ctx->type.dataType.MakeReference(true);
|
|
else
|
|
ctx->type.dataType.MakeReference(false);
|
|
|
|
// It can't be a treated as a variable, since we don't know which one was used
|
|
ctx->type.isVariable = false;
|
|
ctx->type.isTemporary = false;
|
|
|
|
// Must remember if the reference was to a local variable, since it must not be allowed to be returned
|
|
ctx->type.isRefToLocal = le.type.isVariable || le.type.isRefToLocal || re.type.isVariable || re.type.isRefToLocal;
|
|
}
|
|
else
|
|
{
|
|
// Allocate temporary variable and copy the result to that one
|
|
asCExprValue temp;
|
|
temp = le.type;
|
|
temp.dataType.MakeReference(false);
|
|
temp.dataType.MakeReadOnly(false);
|
|
|
|
// Make sure the variable isn't used in any of the expressions,
|
|
// as it would be overwritten which may cause crashes or less visible bugs
|
|
int l = int(reservedVariables.GetLength());
|
|
e.bc.GetVarsUsed(reservedVariables);
|
|
le.bc.GetVarsUsed(reservedVariables);
|
|
re.bc.GetVarsUsed(reservedVariables);
|
|
int offset = AllocateVariable(temp.dataType, true, false);
|
|
reservedVariables.SetLength(l);
|
|
|
|
temp.SetVariable(temp.dataType, offset, true);
|
|
|
|
// TODO: copy: Use copy constructor if available. See PrepareTemporaryVariable()
|
|
|
|
CallDefaultConstructor(temp.dataType, offset, IsVariableOnHeap(offset), &ctx->bc, expr);
|
|
|
|
// Put the code for the condition expression on the output
|
|
MergeExprBytecode(ctx, &e);
|
|
|
|
// Add the branch decision
|
|
ctx->type = e.type;
|
|
ConvertToVariable(ctx);
|
|
ctx->bc.InstrSHORT(asBC_CpyVtoR4, ctx->type.stackOffset);
|
|
ctx->bc.Instr(asBC_ClrHi);
|
|
ctx->bc.InstrDWORD(asBC_JZ, elseLabel);
|
|
ReleaseTemporaryVariable(ctx->type, &ctx->bc);
|
|
|
|
// Assign the result of the left expression to the temporary variable
|
|
asCExprValue rtemp;
|
|
rtemp = temp;
|
|
if( rtemp.dataType.IsObjectHandle() )
|
|
rtemp.isExplicitHandle = true;
|
|
|
|
PrepareForAssignment(&rtemp.dataType, &le, cexpr->next, true);
|
|
MergeExprBytecode(ctx, &le);
|
|
|
|
if( !rtemp.dataType.IsPrimitive() )
|
|
{
|
|
ctx->bc.InstrSHORT(asBC_PSF, (short)offset);
|
|
rtemp.dataType.MakeReference(IsVariableOnHeap(offset));
|
|
}
|
|
asCExprValue result;
|
|
result = rtemp;
|
|
PerformAssignment(&result, &le.type, &ctx->bc, cexpr->next);
|
|
if( !result.dataType.IsPrimitive() )
|
|
ctx->bc.Instr(asBC_PopPtr); // Pop the original value (always a pointer)
|
|
|
|
// Release the old temporary variable
|
|
ReleaseTemporaryVariable(le.type, &ctx->bc);
|
|
|
|
ctx->bc.InstrINT(asBC_JMP, afterLabel);
|
|
|
|
// Start of the right expression
|
|
ctx->bc.Label((short)elseLabel);
|
|
|
|
// Copy the result to the same temporary variable
|
|
PrepareForAssignment(&rtemp.dataType, &re, cexpr->next, true);
|
|
MergeExprBytecode(ctx, &re);
|
|
|
|
if( !rtemp.dataType.IsPrimitive() )
|
|
{
|
|
ctx->bc.InstrSHORT(asBC_PSF, (short)offset);
|
|
rtemp.dataType.MakeReference(IsVariableOnHeap(offset));
|
|
}
|
|
result = rtemp;
|
|
PerformAssignment(&result, &re.type, &ctx->bc, cexpr->next);
|
|
if( !result.dataType.IsPrimitive() )
|
|
ctx->bc.Instr(asBC_PopPtr); // Pop the original value (always a pointer)
|
|
|
|
// Release the old temporary variable
|
|
ReleaseTemporaryVariable(re.type, &ctx->bc);
|
|
|
|
ctx->bc.Label((short)afterLabel);
|
|
|
|
// Make sure both expressions have the same type
|
|
if( !le.type.dataType.IsEqualExceptConst(re.type.dataType) )
|
|
Error(TXT_BOTH_MUST_BE_SAME, expr);
|
|
|
|
// Set the temporary variable as output
|
|
ctx->type = rtemp;
|
|
ctx->type.isExplicitHandle = isExplicitHandle;
|
|
|
|
if( !ctx->type.dataType.IsPrimitive() )
|
|
{
|
|
ctx->bc.InstrSHORT(asBC_PSF, (short)offset);
|
|
ctx->type.dataType.MakeReference(IsVariableOnHeap(offset));
|
|
}
|
|
|
|
// Make sure the output isn't marked as being a literal constant
|
|
ctx->type.isConstant = false;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ctx->type.SetDummy();
|
|
return -1;
|
|
}
|
|
}
|
|
else
|
|
return CompileExpression(cexpr, ctx);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int asCCompiler::CompileExpression(asCScriptNode *expr, asCExprContext *ctx)
|
|
{
|
|
asASSERT(expr->nodeType == snExpression);
|
|
|
|
// Convert to polish post fix, i.e: a+b => ab+
|
|
asCArray<asCScriptNode *> postfix;
|
|
ConvertToPostFix(expr, postfix);
|
|
|
|
// Compile the postfix formatted expression
|
|
return CompilePostFixExpression(&postfix, ctx);
|
|
}
|
|
|
|
void asCCompiler::ConvertToPostFix(asCScriptNode *expr, asCArray<asCScriptNode *> &postfix)
|
|
{
|
|
// The algorithm that I've implemented here is similar to
|
|
// Djikstra's Shunting Yard algorithm, though I didn't know it at the time.
|
|
// ref: http://en.wikipedia.org/wiki/Shunting-yard_algorithm
|
|
|
|
// Count the nodes in order to preallocate the buffers
|
|
int count = 0;
|
|
asCScriptNode *node = expr->firstChild;
|
|
while( node )
|
|
{
|
|
count++;
|
|
node = node->next;
|
|
}
|
|
|
|
asCArray<asCScriptNode *> stackA(count);
|
|
asCArray<asCScriptNode *> &stackB = postfix;
|
|
stackB.Allocate(count, false);
|
|
|
|
node = expr->firstChild;
|
|
while( node )
|
|
{
|
|
int precedence = GetPrecedence(node);
|
|
|
|
while( stackA.GetLength() > 0 &&
|
|
precedence <= GetPrecedence(stackA[stackA.GetLength()-1]) )
|
|
stackB.PushLast(stackA.PopLast());
|
|
|
|
stackA.PushLast(node);
|
|
|
|
node = node->next;
|
|
}
|
|
|
|
while( stackA.GetLength() > 0 )
|
|
stackB.PushLast(stackA.PopLast());
|
|
}
|
|
|
|
int asCCompiler::CompilePostFixExpression(asCArray<asCScriptNode *> *postfix, asCExprContext *ctx)
|
|
{
|
|
// Shouldn't send any byte code
|
|
asASSERT(ctx->bc.GetLastInstr() == -1);
|
|
|
|
// Set the context to a dummy type to avoid further
|
|
// errors in case the expression fails to compile
|
|
ctx->type.SetDummy();
|
|
|
|
// Evaluate the operands and operators
|
|
asCArray<asCExprContext*> free;
|
|
asCArray<asCExprContext*> expr;
|
|
int ret = 0;
|
|
for( asUINT n = 0; ret == 0 && n < postfix->GetLength(); n++ )
|
|
{
|
|
asCScriptNode *node = (*postfix)[n];
|
|
if( node->nodeType == snExprTerm )
|
|
{
|
|
asCExprContext *e = free.GetLength() ? free.PopLast() : asNEW(asCExprContext)(engine);
|
|
expr.PushLast(e);
|
|
e->exprNode = node;
|
|
ret = CompileExpressionTerm(node, e);
|
|
}
|
|
else
|
|
{
|
|
asCExprContext *r = expr.PopLast();
|
|
asCExprContext *l = expr.PopLast();
|
|
|
|
// Now compile the operator
|
|
asCExprContext *e = free.GetLength() ? free.PopLast() : asNEW(asCExprContext)(engine);
|
|
ret = CompileOperator(node, l, r, e);
|
|
|
|
expr.PushLast(e);
|
|
|
|
// Free the operands
|
|
l->Clear();
|
|
free.PushLast(l);
|
|
r->Clear();
|
|
free.PushLast(r);
|
|
}
|
|
}
|
|
|
|
if( ret == 0 )
|
|
{
|
|
asASSERT(expr.GetLength() == 1);
|
|
|
|
// The final result should be moved to the output context
|
|
MergeExprBytecodeAndType(ctx, expr[0]);
|
|
}
|
|
|
|
// Clean up
|
|
for( asUINT e = 0; e < expr.GetLength(); e++ )
|
|
asDELETE(expr[e], asCExprContext);
|
|
for( asUINT f = 0; f < free.GetLength(); f++ )
|
|
asDELETE(free[f], asCExprContext);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int asCCompiler::CompileAnonymousInitList(asCScriptNode *node, asCExprContext *ctx, const asCDataType &dt)
|
|
{
|
|
asASSERT(node->nodeType == snInitList);
|
|
|
|
// Do not allow constructing non-shared types in shared functions
|
|
if (outFunc->IsShared() &&
|
|
dt.GetTypeInfo() && !dt.GetTypeInfo()->IsShared())
|
|
{
|
|
asCString msg;
|
|
msg.Format(TXT_SHARED_CANNOT_USE_NON_SHARED_TYPE_s, dt.GetTypeInfo()->name.AddressOf());
|
|
Error(msg, node);
|
|
}
|
|
|
|
// If this is compiled from a default arg, then use the script code for the default arg
|
|
asCScriptCode *origCode = script;
|
|
if (ctx->origCode)
|
|
script = ctx->origCode;
|
|
|
|
// Allocate and initialize the temporary object
|
|
int offset = AllocateVariable(dt, true);
|
|
CompileInitialization(node, &ctx->bc, dt, node, offset, 0, 0);
|
|
|
|
// Push the reference to the object on the stack
|
|
ctx->bc.InstrSHORT(asBC_PSF, (short)offset);
|
|
ctx->type.SetVariable(dt, offset, true);
|
|
ctx->type.isLValue = false;
|
|
|
|
// If the variable is allocated on the heap we have a reference,
|
|
// otherwise the actual object pointer is pushed on the stack.
|
|
if (IsVariableOnHeap(offset))
|
|
ctx->type.dataType.MakeReference(true);
|
|
|
|
// Clear the flag for anonymous initalization list as it is no
|
|
// longer true now that the object has been initialized.
|
|
ctx->isAnonymousInitList = false;
|
|
ctx->origCode = 0;
|
|
|
|
script = origCode;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int asCCompiler::CompileExpressionTerm(asCScriptNode *node, asCExprContext *ctx)
|
|
{
|
|
// Shouldn't send any byte code
|
|
asASSERT(ctx->bc.GetLastInstr() == -1);
|
|
|
|
// Check if this is an initialization of a temp object with an initialization list
|
|
if (node->firstChild )
|
|
{
|
|
if (node->firstChild->nodeType == snDataType)
|
|
{
|
|
// Determine the type of the temporary object
|
|
asCDataType dt = builder->CreateDataTypeFromNode(node->firstChild, script, outFunc->nameSpace);
|
|
|
|
return CompileAnonymousInitList(node->lastChild, ctx, dt);
|
|
}
|
|
else if (node->firstChild->nodeType == snInitList)
|
|
{
|
|
// As the type is not yet known, the init list will be compiled at a
|
|
// later time when the type can be determined from the destination
|
|
ctx->SetAnonymousInitList(node->firstChild, script);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
// Set the type as a dummy by default, in case of any compiler errors
|
|
ctx->type.SetDummy();
|
|
|
|
// Compile the value node
|
|
asCScriptNode *vnode = node->firstChild;
|
|
while( vnode->nodeType != snExprValue )
|
|
vnode = vnode->next;
|
|
|
|
asCExprContext v(engine);
|
|
int r = CompileExpressionValue(vnode, &v); if( r < 0 ) return r;
|
|
|
|
// Compile post fix operators
|
|
asCScriptNode *pnode = vnode->next;
|
|
while( pnode )
|
|
{
|
|
r = CompileExpressionPostOp(pnode, &v); if( r < 0 ) return r;
|
|
pnode = pnode->next;
|
|
}
|
|
|
|
// Compile pre fix operators
|
|
pnode = vnode->prev;
|
|
while( pnode )
|
|
{
|
|
r = CompileExpressionPreOp(pnode, &v); if( r < 0 ) return r;
|
|
pnode = pnode->prev;
|
|
}
|
|
|
|
// Return the byte code and final type description
|
|
MergeExprBytecodeAndType(ctx, &v);
|
|
|
|
return 0;
|
|
}
|
|
|
|
// returns:
|
|
// SL_LOCALCONST = local constant
|
|
// SL_LOCALVAR = local variable
|
|
// SL_NOMATCH = no match
|
|
asCCompiler::SYMBOLTYPE asCCompiler::SymbolLookupLocalVar(const asCString &name, asCExprContext *outResult)
|
|
{
|
|
sVariable *v = 0;
|
|
if (variables)
|
|
v = variables->GetVariable(name.AddressOf());
|
|
if (v)
|
|
{
|
|
if (v->isPureConstant)
|
|
{
|
|
outResult->type.SetConstantData(v->type, v->constantValue);
|
|
return SL_LOCALCONST;
|
|
}
|
|
|
|
outResult->type.SetVariable(v->type, v->stackOffset, false);
|
|
return SL_LOCALVAR;
|
|
}
|
|
|
|
return SL_NOMATCH;
|
|
}
|
|
|
|
// returns:
|
|
// SL_CLASSPROPACCESS = class property accessor
|
|
// SL_CLASSPROP = class property
|
|
// SL_CLASSMETHOD = class method
|
|
// SL_CLASSTYPE = class child type
|
|
// SL_NOMATCH = no match
|
|
// SL_ERROR = error
|
|
asCCompiler::SYMBOLTYPE asCCompiler::SymbolLookupMember(const asCString &name, asCObjectType *objType, asCExprContext *outResult)
|
|
{
|
|
// See if there are any matching property accessors
|
|
asCExprContext access(engine);
|
|
access.type.Set(asCDataType::CreateType(objType, false));
|
|
access.type.dataType.MakeReference(true);
|
|
int r = 0;
|
|
// Indexed property access
|
|
asCExprContext dummyArg(engine);
|
|
r = FindPropertyAccessor(name, &access, &dummyArg, 0, 0, true);
|
|
if (r == 0)
|
|
{
|
|
// Normal property access
|
|
r = FindPropertyAccessor(name, &access, 0, 0, true);
|
|
}
|
|
if (r < 0) return SL_ERROR;
|
|
if (access.property_get || access.property_set)
|
|
{
|
|
MergeExprBytecodeAndType(outResult, &access);
|
|
outResult->type.dataType.SetTypeInfo(objType);
|
|
return SL_CLASSPROPACCESS;
|
|
}
|
|
|
|
// Look for matching properties
|
|
asCDataType dt;
|
|
dt = asCDataType::CreateType(objType, false);
|
|
asCObjectProperty *prop = builder->GetObjectProperty(dt, name.AddressOf());
|
|
if (prop)
|
|
{
|
|
outResult->type.dataType.SetTypeInfo(objType);
|
|
return SL_CLASSPROP;
|
|
}
|
|
|
|
// If it is not a property, it may still be the name of a method
|
|
asCObjectType *ot = objType;
|
|
for (asUINT n = 0; n < ot->methods.GetLength(); n++)
|
|
{
|
|
asCScriptFunction *f = engine->scriptFunctions[ot->methods[n]];
|
|
if (f->name == name &&
|
|
(builder->module->accessMask & f->accessMask))
|
|
{
|
|
outResult->type.dataType.SetTypeInfo(objType);
|
|
return SL_CLASSMETHOD;
|
|
}
|
|
}
|
|
|
|
// If it is not a method, then it can still be a child type
|
|
for (asUINT n = 0; n < ot->childFuncDefs.GetLength(); n++)
|
|
{
|
|
if (ot->childFuncDefs[n]->name == name)
|
|
{
|
|
outResult->type.dataType.SetTypeInfo(objType);
|
|
return SL_CLASSTYPE;
|
|
}
|
|
}
|
|
|
|
return SL_NOMATCH;
|
|
}
|
|
|
|
// The purpose of this function is to find the entity that matches the symbol name respecting the scope and visibility hierarchy
|
|
// The 'outResult' will be used to return info on what was identified, but no code will be produced by this function
|
|
// input:
|
|
// name = the name of the symbol to look for
|
|
// scope = explicit scope informed
|
|
// objType = used to look for symbols within object type (e.g. when compiling post op), in this case no local or global symbols will be looked up
|
|
// returns:
|
|
// SL_NOMATCH = no matching symbol
|
|
// SL_LOCALCONST = local constant
|
|
// SL_LOCALVAR = local variable
|
|
// SL_THISPTR = this pointer
|
|
// SL_CLASSPROPACCESS = class property accessor, lookupResult->dataType holds the object type in which the member was found
|
|
// SL_CLASSPROP = class property, lookupResult->dataType holds the object type in which the member was found
|
|
// SL_CLASSMETHOD = class method, lookupResult->dataType holds the object type in which the member was found
|
|
// SL_CLASSTYPE = class child type, lookupResult->dataType holds the object type in which the member was found
|
|
// SL_GLOBALPROPACCESS = global property accessor, lookupResult->symbolNamespace holds the namespace where the symbol was identified
|
|
// SL_GLOBALCONST = global constant, lookupResult->symbolNamespace holds the namespace where the symbol was identified
|
|
// SL_GLOBALVAR = global variable, lookupResult->symbolNamespace holds the namespace where the symbol was identified
|
|
// SL_GLOBALFUNC = global function, lookupResult->symbolNamespace holds the namespace where the symbol was identified
|
|
// SL_GLOBALTYPE = type, lookupResult->dataType holds the type
|
|
// SL_ENUMVAL = enum value, lookupResult->dataType holds the enum type, unless ambigious. lookupResult->symbolNamespace holds the namespace where the symbol was identified
|
|
// SL_ERROR = error
|
|
asCCompiler::SYMBOLTYPE asCCompiler::SymbolLookup(const asCString &name, const asCString &scope, asCObjectType *objType, asCExprContext *outResult)
|
|
{
|
|
asASSERT(outResult);
|
|
|
|
// It is a local variable or parameter?
|
|
// This is not accessible by default arg expressions
|
|
if (!isCompilingDefaultArg && scope == "" && !objType )
|
|
{
|
|
SYMBOLTYPE r = SymbolLookupLocalVar(name, outResult);
|
|
if (r != 0)
|
|
return r;
|
|
}
|
|
|
|
// Is it a class member?
|
|
// This is not accessible by default arg expressions
|
|
if (!isCompilingDefaultArg && scope == "" && ((objType) || (outFunc && outFunc->objectType)))
|
|
{
|
|
if (name == THIS_TOKEN && !objType)
|
|
{
|
|
asCDataType dt = asCDataType::CreateType(outFunc->objectType, outFunc->IsReadOnly());
|
|
|
|
// The object pointer is located at stack position 0
|
|
outResult->type.SetVariable(dt, 0, false);
|
|
return SL_THISPTR;
|
|
}
|
|
|
|
if (m_isConstructor && name == SUPER_TOKEN && !objType)
|
|
{
|
|
// If the class is derived from another class, then super can be used to call the base' class constructor
|
|
if (outFunc && outFunc->objectType->derivedFrom)
|
|
{
|
|
outResult->type.dataType.SetTypeInfo(outFunc->objectType->derivedFrom);
|
|
return SL_CLASSMETHOD;
|
|
}
|
|
}
|
|
|
|
// Look for members in the type
|
|
SYMBOLTYPE r = SymbolLookupMember(name, objType ? objType : outFunc->objectType, outResult);
|
|
if (r != 0)
|
|
return r;
|
|
}
|
|
|
|
// Recursively search parent namespaces for global entities
|
|
asSNameSpace *currNamespace = DetermineNameSpace("");
|
|
while( !objType && currNamespace )
|
|
{
|
|
asCString currScope = scope;
|
|
|
|
// If the scope contains ::identifier, then use the last identifier as the class name and the rest of it as the namespace
|
|
// TODO: child funcdef: A scope can include a template type, e.g. array<ns::type>
|
|
int n = currScope.FindLast("::");
|
|
asCString typeName = n >= 0 ? currScope.SubString(n + 2) : currScope;
|
|
asCString nsName = n >= 0 ? currScope.SubString(0, n) : "";
|
|
|
|
// If the scope represents a type that the current class inherits
|
|
// from then that should be used instead of going through the namespaces
|
|
if (nsName == "" && (outFunc && outFunc->objectType))
|
|
{
|
|
asCObjectType *ot = outFunc->objectType;
|
|
while (ot)
|
|
{
|
|
if (ot->name == typeName)
|
|
{
|
|
SYMBOLTYPE r = SymbolLookupMember(name, ot, outResult);
|
|
if (r != 0)
|
|
return r;
|
|
}
|
|
|
|
ot = ot->derivedFrom;
|
|
}
|
|
}
|
|
|
|
// If the scope starts with :: then search from the global scope
|
|
if (currScope.GetLength() < 2 || currScope[0] != ':')
|
|
{
|
|
if (nsName != "")
|
|
{
|
|
if (currNamespace->name != "")
|
|
nsName = currNamespace->name + "::" + nsName;
|
|
}
|
|
else
|
|
nsName = currNamespace->name;
|
|
}
|
|
else
|
|
nsName = nsName.SubString(2);
|
|
|
|
// Get the namespace for this scope
|
|
asSNameSpace *ns = engine->FindNameSpace(nsName.AddressOf());
|
|
if (ns)
|
|
{
|
|
// Is there a type with typeName in the namespace?
|
|
asCTypeInfo *scopeType = builder->GetType(typeName.AddressOf(), ns, 0);
|
|
|
|
// Check if the symbol is a member of that type
|
|
if (scopeType)
|
|
{
|
|
// Is it an object type?
|
|
if (CastToObjectType(scopeType))
|
|
{
|
|
SYMBOLTYPE r = SymbolLookupMember(name, CastToObjectType(scopeType), outResult);
|
|
if (r != 0)
|
|
return r;
|
|
}
|
|
|
|
// Is it an enum type?
|
|
if (CastToEnumType(scopeType))
|
|
{
|
|
asDWORD value = 0;
|
|
asCDataType dt;
|
|
if (builder->GetEnumValueFromType(CastToEnumType(scopeType), name.AddressOf(), dt, value))
|
|
{
|
|
// an enum value was resolved
|
|
outResult->type.SetConstantDW(dt, value);
|
|
outResult->symbolNamespace = ns;
|
|
return SL_ENUMVAL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Get the namespace for this scope. This may return null if the scope is an enum
|
|
nsName = currScope;
|
|
|
|
// If the scope starts with :: then search from the global scope
|
|
if (currScope.GetLength() < 2 || currScope[0] != ':')
|
|
{
|
|
if (nsName != "")
|
|
{
|
|
if (currNamespace->name != "")
|
|
nsName = currNamespace->name + "::" + nsName;
|
|
}
|
|
else
|
|
nsName = currNamespace->name;
|
|
}
|
|
else
|
|
nsName = nsName.SubString(2);
|
|
|
|
ns = engine->FindNameSpace(nsName.AddressOf());
|
|
|
|
// Is it a global property?
|
|
if (ns)
|
|
{
|
|
// See if there are any matching global property accessors
|
|
asCExprContext access(engine);
|
|
int r = 0;
|
|
// Indexed property access
|
|
asCExprContext dummyArg(engine);
|
|
r = FindPropertyAccessor(name, &access, &dummyArg, 0, ns);
|
|
if (r == 0)
|
|
{
|
|
// Normal property access
|
|
r = FindPropertyAccessor(name, &access, 0, ns);
|
|
}
|
|
if (r < 0) return SL_ERROR;
|
|
if (access.property_get || access.property_set)
|
|
{
|
|
MergeExprBytecodeAndType(outResult, &access);
|
|
outResult->symbolNamespace = ns;
|
|
return SL_GLOBALPROPACCESS;
|
|
}
|
|
|
|
// See if there is any matching global property
|
|
bool isCompiled = true;
|
|
bool isPureConstant = false;
|
|
bool isAppProp = false;
|
|
asQWORD constantValue = 0;
|
|
asCGlobalProperty *prop = builder->GetGlobalProperty(name.AddressOf(), ns, &isCompiled, &isPureConstant, &constantValue, &isAppProp);
|
|
if (prop)
|
|
{
|
|
// If the global property is a pure constant
|
|
// we can allow the compiler to optimize it. Pure
|
|
// constants are global constant variables that were
|
|
// initialized by literal constants.
|
|
if (isPureConstant)
|
|
{
|
|
outResult->type.SetConstantData(prop->type, constantValue);
|
|
outResult->symbolNamespace = ns;
|
|
return SL_GLOBALCONST;
|
|
}
|
|
else
|
|
{
|
|
outResult->type.Set(prop->type);
|
|
outResult->symbolNamespace = ns;
|
|
return SL_GLOBALVAR;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Is it the name of a global function?
|
|
if (ns)
|
|
{
|
|
asCArray<int> funcs;
|
|
|
|
builder->GetFunctionDescriptions(name.AddressOf(), funcs, ns);
|
|
|
|
if (funcs.GetLength() > 0)
|
|
{
|
|
// Defer the evaluation of which function until it is actually used
|
|
// Store the namespace and name of the function for later
|
|
outResult->type.SetUndefinedFuncHandle(engine);
|
|
outResult->methodName = ns ? ns->name + "::" + name : name;
|
|
outResult->symbolNamespace = ns;
|
|
return SL_GLOBALFUNC;
|
|
}
|
|
}
|
|
|
|
// Check for type names
|
|
if (ns)
|
|
{
|
|
asCTypeInfo *type = builder->GetType(name.AddressOf(), ns, 0);
|
|
if (type)
|
|
{
|
|
outResult->type.dataType = asCDataType::CreateType(type, false);
|
|
return SL_GLOBALTYPE;
|
|
}
|
|
}
|
|
|
|
// Is it an enum value?
|
|
if (ns && !engine->ep.requireEnumScope)
|
|
{
|
|
// Look for the enum value without explicitly informing the enum type
|
|
asDWORD value = 0;
|
|
asCDataType dt;
|
|
int e = builder->GetEnumValue(name.AddressOf(), dt, value, ns);
|
|
if (e)
|
|
{
|
|
if (e == 2)
|
|
{
|
|
// Ambiguous enum value: Save the name for resolution later.
|
|
// The ambiguity could be resolved now, but I hesitate
|
|
// to store too much information in the context.
|
|
outResult->enumValue = name.AddressOf();
|
|
|
|
// We cannot set a dummy value because it will pass through
|
|
// cleanly as an integer.
|
|
outResult->type.SetConstantDW(asCDataType::CreatePrimitive(ttIdentifier, true), 0);
|
|
outResult->symbolNamespace = ns;
|
|
return SL_ENUMVAL;
|
|
}
|
|
else
|
|
{
|
|
// an enum value was resolved
|
|
outResult->type.SetConstantDW(dt, value);
|
|
outResult->symbolNamespace = ns;
|
|
return SL_ENUMVAL;
|
|
}
|
|
}
|
|
}
|
|
|
|
// If the given scope starts with '::' then the search starts from global scope
|
|
if (scope.GetLength() >= 2 && scope[0] == ':')
|
|
break;
|
|
|
|
// Move up to parent namespace
|
|
currNamespace = engine->GetParentNameSpace(currNamespace);
|
|
}
|
|
|
|
// The name doesn't match any symbol
|
|
return SL_NOMATCH;
|
|
}
|
|
|
|
int asCCompiler::CompileVariableAccess(const asCString &name, const asCString &scope, asCExprContext *ctx, asCScriptNode *errNode, bool isOptional, asCObjectType *objType)
|
|
{
|
|
asCExprContext lookupResult(engine);
|
|
SYMBOLTYPE symbolType = SymbolLookup(name, scope, objType, &lookupResult);
|
|
if (symbolType < 0)
|
|
{
|
|
// Give dummy value
|
|
ctx->type.SetDummy();
|
|
|
|
return -1;
|
|
}
|
|
if (symbolType == SL_NOMATCH)
|
|
{
|
|
// Give dummy value
|
|
ctx->type.SetDummy();
|
|
|
|
if (!isOptional)
|
|
{
|
|
// No matching symbol
|
|
asCString msg;
|
|
asCString smbl;
|
|
if (scope == "::")
|
|
smbl = scope;
|
|
else if (scope != "")
|
|
smbl = scope + "::";
|
|
smbl += name;
|
|
msg.Format(TXT_NO_MATCHING_SYMBOL_s, smbl.AddressOf());
|
|
Error(msg, errNode);
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
// It is a local variable or parameter?
|
|
if( symbolType == SL_LOCALCONST || symbolType == SL_LOCALVAR )
|
|
{
|
|
// This is not accessible by default arg expressions
|
|
asASSERT(!isCompilingDefaultArg && scope == "" && !objType && variables);
|
|
|
|
sVariable *v = variables->GetVariable(name.AddressOf());
|
|
asASSERT(v);
|
|
|
|
if( v->isPureConstant )
|
|
ctx->type.SetConstantData(v->type, v->constantValue);
|
|
else if( v->type.IsPrimitive() )
|
|
{
|
|
if( v->type.IsReference() )
|
|
{
|
|
// Copy the reference into the register
|
|
ctx->bc.InstrSHORT(asBC_PshVPtr, (short)v->stackOffset);
|
|
ctx->bc.Instr(asBC_PopRPtr);
|
|
ctx->type.Set(v->type);
|
|
}
|
|
else
|
|
ctx->type.SetVariable(v->type, v->stackOffset, false);
|
|
|
|
// Set as lvalue unless it is a const variable
|
|
if( !v->type.IsReadOnly() )
|
|
ctx->type.isLValue = true;
|
|
}
|
|
else
|
|
{
|
|
ctx->bc.InstrSHORT(asBC_PSF, (short)v->stackOffset);
|
|
ctx->type.SetVariable(v->type, v->stackOffset, false);
|
|
|
|
// If the variable is allocated on the heap we have a reference,
|
|
// otherwise the actual object pointer is pushed on the stack.
|
|
if( v->onHeap || v->type.IsObjectHandle() ) ctx->type.dataType.MakeReference(true);
|
|
|
|
// Implicitly dereference handle parameters sent by reference
|
|
if( v->type.IsReference() && (!v->type.IsObject() || v->type.IsObjectHandle()) )
|
|
ctx->bc.Instr(asBC_RDSPtr);
|
|
|
|
// Mark the object as safe for access unless it is a handle, as the
|
|
// life time of the object is guaranteed throughout the scope.
|
|
if( !v->type.IsObjectHandle() )
|
|
ctx->type.isRefSafe = true;
|
|
|
|
// Set as lvalue unless it is a const variable
|
|
if (!v->type.IsReadOnly())
|
|
ctx->type.isLValue = true;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
// Is it a class member?
|
|
if (symbolType == SL_CLASSPROPACCESS || symbolType == SL_CLASSPROP || symbolType == SL_CLASSMETHOD || symbolType == SL_THISPTR)
|
|
{
|
|
// This is not accessible by default arg expressions
|
|
asASSERT(!isCompilingDefaultArg);
|
|
|
|
if (symbolType == SL_THISPTR)
|
|
{
|
|
asASSERT(name == THIS_TOKEN && !objType && scope == "");
|
|
asCDataType dt = asCDataType::CreateType(outFunc->objectType, outFunc->IsReadOnly());
|
|
|
|
// The object pointer is located at stack position 0
|
|
ctx->bc.InstrSHORT(asBC_PSF, 0);
|
|
ctx->type.SetVariable(dt, 0, false);
|
|
ctx->type.dataType.MakeReference(true);
|
|
ctx->type.isLValue = true;
|
|
|
|
// The 'this' handle is always considered safe (i.e. life time guaranteed)
|
|
ctx->type.isRefSafe = true;
|
|
|
|
return 0;
|
|
}
|
|
|
|
if (symbolType == SL_CLASSPROPACCESS)
|
|
{
|
|
if (scope != "")
|
|
{
|
|
// Cannot access non-static members like this
|
|
asCString msg;
|
|
msg.Format(TXT_CANNOT_ACCESS_NON_STATIC_MEMBER_s, name.AddressOf());
|
|
Error(msg, errNode);
|
|
return -1;
|
|
}
|
|
|
|
// See if there are any matching property accessors
|
|
asCExprContext access(engine);
|
|
if (objType)
|
|
access.type.Set(asCDataType::CreateType(objType, false));
|
|
else
|
|
access.type.Set(asCDataType::CreateType(outFunc->objectType, outFunc->IsReadOnly()));
|
|
access.type.dataType.MakeReference(true);
|
|
int r = 0;
|
|
if (errNode->next && errNode->next->tokenType == ttOpenBracket)
|
|
{
|
|
// This is an index access, check if there is a property accessor that takes an index arg
|
|
asCExprContext dummyArg(engine);
|
|
r = FindPropertyAccessor(name, &access, &dummyArg, errNode, 0, true);
|
|
}
|
|
if (r == 0)
|
|
{
|
|
// Normal property access
|
|
r = FindPropertyAccessor(name, &access, errNode, 0, true);
|
|
}
|
|
if (r < 0) return -1;
|
|
|
|
if (access.property_get == 0 && access.property_set == 0)
|
|
{
|
|
// Even though the symbol was identified in SymbolLookup, it doesn't match the arguments
|
|
asCString msg;
|
|
if (errNode->next && errNode->next->tokenType == ttOpenBracket)
|
|
msg.Format(TXT_PROP_ACCESS_s_DOES_NOT_EXPECT_INDEX, name.AddressOf());
|
|
else
|
|
msg.Format(TXT_PROP_ACCESS_s_EXPECTS_INDEX, name.AddressOf());
|
|
Error(msg, errNode);
|
|
return -1;
|
|
}
|
|
|
|
if (!objType)
|
|
{
|
|
// Prepare the bytecode for the member access
|
|
// This is only done when accessing through the implicit this pointer
|
|
ctx->bc.InstrSHORT(asBC_PSF, 0);
|
|
}
|
|
MergeExprBytecodeAndType(ctx, &access);
|
|
|
|
return 0;
|
|
}
|
|
|
|
if (symbolType == SL_CLASSPROP)
|
|
{
|
|
if (scope != "")
|
|
{
|
|
// Cannot access non-static members like this
|
|
asCString msg;
|
|
msg.Format(TXT_CANNOT_ACCESS_NON_STATIC_MEMBER_s, name.AddressOf());
|
|
Error(msg, errNode);
|
|
return -1;
|
|
}
|
|
|
|
asCDataType dt;
|
|
if (objType)
|
|
dt = asCDataType::CreateType(objType, false);
|
|
else
|
|
dt = asCDataType::CreateType(outFunc->objectType, false);
|
|
asCObjectProperty *prop = builder->GetObjectProperty(dt, name.AddressOf());
|
|
asASSERT(prop);
|
|
|
|
// Is the property access allowed?
|
|
if (prop->isPrivate && prop->isInherited)
|
|
{
|
|
if (engine->ep.privatePropAsProtected)
|
|
{
|
|
// The application is allowing inherited classes to access private properties of the parent
|
|
// class. This option is allowed to provide backwards compatibility with pre-2.30.0 versions
|
|
// as it was how the compiler behaved earlier.
|
|
asCString msg;
|
|
msg.Format(TXT_ACCESSING_PRIVATE_PROP_s, name.AddressOf());
|
|
Warning(msg, errNode);
|
|
}
|
|
else
|
|
{
|
|
asCString msg;
|
|
msg.Format(TXT_INHERITED_PRIVATE_PROP_ACCESS_s, name.AddressOf());
|
|
Error(msg, errNode);
|
|
}
|
|
}
|
|
|
|
if (!objType)
|
|
{
|
|
// The object pointer is located at stack position 0
|
|
// This is only done when accessing through the implicit this pointer
|
|
ctx->bc.InstrSHORT(asBC_PSF, 0);
|
|
ctx->type.SetVariable(dt, 0, false);
|
|
ctx->type.dataType.MakeReference(true);
|
|
Dereference(ctx, true);
|
|
}
|
|
|
|
// TODO: This is the same as what is in CompileExpressionPostOp
|
|
// Put the offset on the stack
|
|
ctx->bc.InstrSHORT_DW(asBC_ADDSi, (short)prop->byteOffset, engine->GetTypeIdFromDataType(dt));
|
|
|
|
if (prop->type.IsReference())
|
|
ctx->bc.Instr(asBC_RDSPtr);
|
|
|
|
// Reference to primitive must be stored in the temp register
|
|
if (prop->type.IsPrimitive())
|
|
{
|
|
// TODO: runtime optimize: The ADD offset command should store the reference in the register directly
|
|
ctx->bc.Instr(asBC_PopRPtr);
|
|
}
|
|
|
|
// Set the new type (keeping info about temp variable)
|
|
ctx->type.dataType = prop->type;
|
|
ctx->type.dataType.MakeReference(true);
|
|
ctx->type.isVariable = false;
|
|
ctx->type.isLValue = true;
|
|
|
|
if (ctx->type.dataType.IsObject() && !ctx->type.dataType.IsObjectHandle())
|
|
{
|
|
// Objects that are members are not references
|
|
ctx->type.dataType.MakeReference(false);
|
|
|
|
// Objects that are members but not handles are safe as long as the parent object is safe
|
|
if (!objType || ctx->type.isRefSafe)
|
|
ctx->type.isRefSafe = true;
|
|
}
|
|
else if (ctx->type.dataType.IsObjectHandle())
|
|
{
|
|
// Objects accessed through handles cannot be considered safe
|
|
// as the handle can be cleared at any time
|
|
ctx->type.isRefSafe = false;
|
|
}
|
|
|
|
// If the object reference is const, the property will also be const
|
|
ctx->type.dataType.MakeReadOnly(outFunc->IsReadOnly());
|
|
|
|
return 0;
|
|
}
|
|
|
|
if (symbolType == SL_CLASSMETHOD)
|
|
{
|
|
if (scope != "")
|
|
{
|
|
// Cannot access non-static members like this
|
|
asCString msg;
|
|
msg.Format(TXT_CANNOT_ACCESS_NON_STATIC_MEMBER_s, name.AddressOf());
|
|
Error(msg, errNode);
|
|
return -1;
|
|
}
|
|
|
|
#if AS_DEBUG
|
|
// If it is not a property, it may still be the name of a method which can be used to create delegates
|
|
asCObjectType *ot = outFunc->objectType;
|
|
asCScriptFunction *func = 0;
|
|
for (asUINT n = 0; n < ot->methods.GetLength(); n++)
|
|
{
|
|
asCScriptFunction *f = engine->scriptFunctions[ot->methods[n]];
|
|
if (f->name == name &&
|
|
(builder->module->accessMask & f->accessMask))
|
|
{
|
|
func = f;
|
|
break;
|
|
}
|
|
}
|
|
|
|
asASSERT(func);
|
|
#endif
|
|
// An object method was found. Keep the name of the method in the expression, but
|
|
// don't actually modify the bytecode at this point since it is not yet known what
|
|
// the method will be used for, or even what overloaded method should be used.
|
|
ctx->methodName = name;
|
|
|
|
// Place the object pointer on the stack, as if the expression was this.func
|
|
if (!objType)
|
|
{
|
|
// The object pointer is located at stack position 0
|
|
// This is only done when accessing through the implicit this pointer
|
|
ctx->bc.InstrSHORT(asBC_PSF, 0);
|
|
ctx->type.SetVariable(asCDataType::CreateType(outFunc->objectType, false), 0, false);
|
|
ctx->type.dataType.MakeReference(true);
|
|
Dereference(ctx, true);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if (symbolType == SL_GLOBALCONST || symbolType == SL_GLOBALPROPACCESS || symbolType == SL_GLOBALVAR || symbolType == SL_GLOBALFUNC || symbolType == SL_ENUMVAL)
|
|
{
|
|
// Get the namespace from SymbolLookup
|
|
asSNameSpace *ns = lookupResult.symbolNamespace;
|
|
|
|
if (symbolType == SL_GLOBALPROPACCESS)
|
|
{
|
|
// See if there are any matching global property accessors
|
|
asCExprContext access(engine);
|
|
int r = 0;
|
|
if (errNode->next && errNode->next->tokenType == ttOpenBracket)
|
|
{
|
|
// This is an index access, check if there is a property accessor that takes an index arg
|
|
asCExprContext dummyArg(engine);
|
|
r = FindPropertyAccessor(name, &access, &dummyArg, errNode, ns);
|
|
}
|
|
if (r == 0)
|
|
{
|
|
// Normal property access
|
|
r = FindPropertyAccessor(name, &access, errNode, ns);
|
|
}
|
|
if (r < 0) return -1;
|
|
|
|
if (access.property_get == 0 && access.property_set == 0)
|
|
{
|
|
// Even though the symbol was identified in SymbolLookup, it doesn't match the arguments
|
|
asCString msg;
|
|
if (errNode->next && errNode->next->tokenType == ttOpenBracket)
|
|
msg.Format(TXT_PROP_ACCESS_s_DOES_NOT_EXPECT_INDEX, name.AddressOf());
|
|
else
|
|
msg.Format(TXT_PROP_ACCESS_s_EXPECTS_INDEX, name.AddressOf());
|
|
Error(msg, errNode);
|
|
return -1;
|
|
}
|
|
|
|
// Prepare the bytecode for the function call
|
|
MergeExprBytecodeAndType(ctx, &access);
|
|
|
|
return 0;
|
|
}
|
|
|
|
if (symbolType == SL_GLOBALCONST || symbolType == SL_GLOBALVAR)
|
|
{
|
|
bool isCompiled = true;
|
|
bool isPureConstant = false;
|
|
bool isAppProp = false;
|
|
asQWORD constantValue = 0;
|
|
asCGlobalProperty *prop = builder->GetGlobalProperty(name.AddressOf(), ns, &isCompiled, &isPureConstant, &constantValue, &isAppProp);
|
|
asASSERT(prop);
|
|
|
|
// Verify that the global property has been compiled already
|
|
if (!isCompiled)
|
|
{
|
|
asCString str;
|
|
str.Format(TXT_UNINITIALIZED_GLOBAL_VAR_s, prop->name.AddressOf());
|
|
Error(str, errNode);
|
|
return -1;
|
|
}
|
|
|
|
// If the global property is a pure constant
|
|
// we can allow the compiler to optimize it. Pure
|
|
// constants are global constant variables that were
|
|
// initialized by literal constants.
|
|
if (isPureConstant)
|
|
ctx->type.SetConstantData(prop->type, constantValue);
|
|
else
|
|
{
|
|
// A shared type must not access global vars, unless they
|
|
// too are shared, e.g. application registered vars
|
|
if (outFunc->IsShared())
|
|
{
|
|
if (!isAppProp)
|
|
{
|
|
asCString str;
|
|
str.Format(TXT_SHARED_CANNOT_ACCESS_NON_SHARED_VAR_s, prop->name.AddressOf());
|
|
Error(str, errNode);
|
|
|
|
// Allow the compilation to continue to catch other problems
|
|
}
|
|
}
|
|
|
|
ctx->type.Set(prop->type);
|
|
ctx->type.isLValue = true;
|
|
|
|
if (ctx->type.dataType.IsPrimitive())
|
|
{
|
|
// Load the address of the variable into the register
|
|
ctx->bc.InstrPTR(asBC_LDG, prop->GetAddressOfValue());
|
|
|
|
ctx->type.dataType.MakeReference(true);
|
|
}
|
|
else
|
|
{
|
|
// Push the address of the variable on the stack
|
|
ctx->bc.InstrPTR(asBC_PGA, prop->GetAddressOfValue());
|
|
|
|
// If the object is a value type or a non-handle variable to a reference type,
|
|
// then we must validate the existance as it could potentially be accessed
|
|
// before it is initialized.
|
|
// This check is not needed for application registered properties, since they
|
|
// are guaranteed to be valid by the application itself.
|
|
if (!isAppProp &&
|
|
((ctx->type.dataType.GetTypeInfo()->flags & asOBJ_VALUE) ||
|
|
!ctx->type.dataType.IsObjectHandle()))
|
|
{
|
|
ctx->bc.Instr(asBC_ChkRefS);
|
|
}
|
|
|
|
// If the address pushed on the stack is to a value type or an object
|
|
// handle, then mark the expression as a reference. Addresses to a reference
|
|
// type aren't marked as references to get correct behaviour
|
|
if ((ctx->type.dataType.GetTypeInfo()->flags & asOBJ_VALUE) ||
|
|
ctx->type.dataType.IsObjectHandle())
|
|
{
|
|
ctx->type.dataType.MakeReference(true);
|
|
}
|
|
else
|
|
{
|
|
asASSERT((ctx->type.dataType.GetTypeInfo()->flags & asOBJ_REF) && !ctx->type.dataType.IsObjectHandle());
|
|
|
|
// It's necessary to dereference the pointer so the pointer on the stack will point to the actual object
|
|
ctx->bc.Instr(asBC_RDSPtr);
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
if (symbolType == SL_GLOBALFUNC)
|
|
{
|
|
asCArray<int> funcs;
|
|
|
|
builder->GetFunctionDescriptions(name.AddressOf(), funcs, ns);
|
|
asASSERT(funcs.GetLength() > 0);
|
|
|
|
if (funcs.GetLength() > 0)
|
|
{
|
|
// Defer the evaluation of which function until it is actually used
|
|
// Store the namespace and name of the function for later
|
|
ctx->type.SetUndefinedFuncHandle(engine);
|
|
ctx->methodName = ns ? ns->name + "::" + name : name;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
if (symbolType == SL_ENUMVAL)
|
|
{
|
|
// The enum type and namespace must be returned from SymbolLookup
|
|
asCDataType dt = lookupResult.type.dataType;
|
|
if (!dt.IsEnumType())
|
|
{
|
|
asASSERT(!engine->ep.requireEnumScope);
|
|
|
|
// It is an ambigious enum value. The evaluation needs to be deferred for when the type is known
|
|
ctx->enumValue = name.AddressOf();
|
|
ctx->symbolNamespace = lookupResult.symbolNamespace;
|
|
|
|
// We cannot set a dummy value because it will pass through
|
|
// cleanly as an integer.
|
|
ctx->type.SetConstantDW(asCDataType::CreatePrimitive(ttIdentifier, true), 0);
|
|
return 0;
|
|
}
|
|
|
|
asDWORD value = 0;
|
|
builder->GetEnumValueFromType(CastToEnumType(lookupResult.type.dataType.GetTypeInfo()), name.AddressOf(), dt, value);
|
|
|
|
// Even if the enum type is not shared, and we're compiling a shared object,
|
|
// the use of the values are still allowed, since they are treated as constants.
|
|
|
|
// an enum value was resolved
|
|
ctx->type.SetConstantDW(dt, value);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
// The result must have been identified above
|
|
if (symbolType == SL_GLOBALTYPE || symbolType == SL_CLASSTYPE)
|
|
{
|
|
// Give dummy value
|
|
ctx->type.SetDummy();
|
|
|
|
// The symbol matches a type
|
|
asCString msg;
|
|
asCString smbl;
|
|
if (scope == "::")
|
|
smbl = scope;
|
|
else if (scope != "")
|
|
smbl = scope + "::";
|
|
smbl += name;
|
|
msg.Format(TXT_EXPR_s_IS_DATA_TYPE, smbl.AddressOf());
|
|
Error(msg, errNode);
|
|
return -1;
|
|
}
|
|
|
|
// Should not come here
|
|
asASSERT(false);
|
|
return 0;
|
|
}
|
|
|
|
int asCCompiler::CompileExpressionValue(asCScriptNode *node, asCExprContext *ctx)
|
|
{
|
|
// Shouldn't receive any byte code
|
|
asASSERT(ctx->bc.GetLastInstr() == -1);
|
|
|
|
asCScriptNode *vnode = node->firstChild;
|
|
ctx->exprNode = vnode;
|
|
if( vnode->nodeType == snVariableAccess )
|
|
{
|
|
// Determine the scope resolution of the variable
|
|
asCString scope = builder->GetScopeFromNode(vnode->firstChild, script, &vnode);
|
|
|
|
// Determine the name of the variable
|
|
asASSERT(vnode->nodeType == snIdentifier );
|
|
asCString name(&script->code[vnode->tokenPos], vnode->tokenLength);
|
|
|
|
return CompileVariableAccess(name, scope, ctx, node);
|
|
}
|
|
else if( vnode->nodeType == snConstant )
|
|
{
|
|
if( vnode->tokenType == ttIntConstant )
|
|
{
|
|
asCString value(&script->code[vnode->tokenPos], vnode->tokenLength);
|
|
|
|
bool overflow = false;
|
|
asQWORD val = asStringScanUInt64(value.AddressOf(), 10, 0, &overflow);
|
|
|
|
// Is the number bigger than a 64bit word?
|
|
if (overflow)
|
|
{
|
|
Error(TXT_VALUE_TOO_LARGE_FOR_TYPE, vnode);
|
|
|
|
// Set the value to zero to avoid further warnings
|
|
val = 0;
|
|
}
|
|
|
|
// Do we need 64 bits?
|
|
// If the 31st bit is set we'll treat the value as a signed 64bit number to avoid
|
|
// incorrect warnings about changing signs if the value is assigned to a 64bit variable
|
|
if( val>>31 )
|
|
{
|
|
// Only if the value uses the last bit of a 64bit word do we consider the number unsigned
|
|
if( val>>63 )
|
|
ctx->type.SetConstantQW(asCDataType::CreatePrimitive(ttUInt64, true), val);
|
|
else
|
|
ctx->type.SetConstantQW(asCDataType::CreatePrimitive(ttInt64, true), val);
|
|
}
|
|
else
|
|
ctx->type.SetConstantDW(asCDataType::CreatePrimitive(ttInt, true), asDWORD(val));
|
|
}
|
|
else if( vnode->tokenType == ttBitsConstant )
|
|
{
|
|
asCString value(&script->code[vnode->tokenPos], vnode->tokenLength);
|
|
|
|
// Let the function determine the radix from the prefix 0x = 16, 0d = 10, 0o = 8, or 0b = 2
|
|
bool overflow = false;
|
|
asQWORD val = asStringScanUInt64(value.AddressOf(), 0, 0, &overflow);
|
|
|
|
// Is the number bigger than a 64bit word?
|
|
if (overflow)
|
|
{
|
|
Error(TXT_VALUE_TOO_LARGE_FOR_TYPE, vnode);
|
|
|
|
// Set the value to zero to avoid further warnings
|
|
val = 0;
|
|
}
|
|
|
|
// Do we need 64 bits?
|
|
if( val>>32 )
|
|
ctx->type.SetConstantQW(asCDataType::CreatePrimitive(ttUInt64, true), val);
|
|
else
|
|
ctx->type.SetConstantDW(asCDataType::CreatePrimitive(ttUInt, true), asDWORD(val));
|
|
}
|
|
else if( vnode->tokenType == ttFloatConstant )
|
|
{
|
|
asCString value(&script->code[vnode->tokenPos], vnode->tokenLength);
|
|
|
|
// TODO: Check for overflow
|
|
|
|
size_t numScanned;
|
|
float v = float(asStringScanDouble(value.AddressOf(), &numScanned));
|
|
ctx->type.SetConstantF(asCDataType::CreatePrimitive(ttFloat, true), v);
|
|
#ifndef AS_USE_DOUBLE_AS_FLOAT
|
|
// Don't check this if we have double as float, because then the whole token would be scanned (i.e. no f suffix)
|
|
asASSERT(numScanned == vnode->tokenLength - 1);
|
|
#endif
|
|
}
|
|
else if( vnode->tokenType == ttDoubleConstant )
|
|
{
|
|
asCString value(&script->code[vnode->tokenPos], vnode->tokenLength);
|
|
|
|
// TODO: Check for overflow
|
|
|
|
size_t numScanned;
|
|
double v = asStringScanDouble(value.AddressOf(), &numScanned);
|
|
ctx->type.SetConstantD(asCDataType::CreatePrimitive(ttDouble, true), v);
|
|
asASSERT(numScanned == vnode->tokenLength);
|
|
}
|
|
else if( vnode->tokenType == ttTrue ||
|
|
vnode->tokenType == ttFalse )
|
|
{
|
|
#if AS_SIZEOF_BOOL == 1
|
|
ctx->type.SetConstantB(asCDataType::CreatePrimitive(ttBool, true), vnode->tokenType == ttTrue ? VALUE_OF_BOOLEAN_TRUE : 0);
|
|
#else
|
|
ctx->type.SetConstantDW(asCDataType::CreatePrimitive(ttBool, true), vnode->tokenType == ttTrue ? VALUE_OF_BOOLEAN_TRUE : 0);
|
|
#endif
|
|
}
|
|
else if( vnode->tokenType == ttStringConstant ||
|
|
vnode->tokenType == ttMultilineStringConstant ||
|
|
vnode->tokenType == ttHeredocStringConstant )
|
|
{
|
|
asCString str;
|
|
asCScriptNode *snode = vnode->firstChild;
|
|
if( script->code[snode->tokenPos] == '\'' && engine->ep.useCharacterLiterals )
|
|
{
|
|
// Treat the single quoted string as a single character literal
|
|
str.Assign(&script->code[snode->tokenPos+1], snode->tokenLength-2);
|
|
|
|
asDWORD val = 0;
|
|
if( str.GetLength() && (asBYTE)str[0] > 127 && engine->ep.scanner == 1 )
|
|
{
|
|
// This is the start of a UTF8 encoded character. We need to decode it
|
|
val = asStringDecodeUTF8(str.AddressOf(), 0);
|
|
if( val == (asDWORD)-1 )
|
|
Error(TXT_INVALID_CHAR_LITERAL, vnode);
|
|
}
|
|
else
|
|
{
|
|
val = ProcessStringConstant(str, snode);
|
|
if( val == (asDWORD)-1 )
|
|
Error(TXT_INVALID_CHAR_LITERAL, vnode);
|
|
}
|
|
|
|
ctx->type.SetConstantDW(asCDataType::CreatePrimitive(ttUInt, true), val);
|
|
}
|
|
else
|
|
{
|
|
// Process the string constants
|
|
while( snode )
|
|
{
|
|
asCString cat;
|
|
if( snode->tokenType == ttStringConstant )
|
|
{
|
|
cat.Assign(&script->code[snode->tokenPos+1], snode->tokenLength-2);
|
|
ProcessStringConstant(cat, snode);
|
|
}
|
|
else if( snode->tokenType == ttMultilineStringConstant )
|
|
{
|
|
if( !engine->ep.allowMultilineStrings )
|
|
Error(TXT_MULTILINE_STRINGS_NOT_ALLOWED, snode);
|
|
|
|
cat.Assign(&script->code[snode->tokenPos+1], snode->tokenLength-2);
|
|
ProcessStringConstant(cat, snode);
|
|
}
|
|
else if( snode->tokenType == ttHeredocStringConstant )
|
|
{
|
|
cat.Assign(&script->code[snode->tokenPos+3], snode->tokenLength-6);
|
|
ProcessHeredocStringConstant(cat, snode);
|
|
}
|
|
|
|
str += cat;
|
|
|
|
snode = snode->next;
|
|
}
|
|
|
|
// Call the string factory function to create a string object
|
|
if(engine->stringFactory == 0 )
|
|
{
|
|
// Error
|
|
Error(TXT_STRINGS_NOT_RECOGNIZED, vnode);
|
|
|
|
// Give dummy value
|
|
ctx->type.SetDummy();
|
|
return -1;
|
|
}
|
|
else
|
|
{
|
|
void *strPtr = const_cast<void*>(engine->stringFactory->GetStringConstant(str.AddressOf(), (asUINT)str.GetLength()));
|
|
if (strPtr == 0)
|
|
{
|
|
// TODO: A better message is needed
|
|
Error(TXT_NULL_POINTER_ACCESS, vnode);
|
|
ctx->type.SetDummy();
|
|
return -1;
|
|
}
|
|
|
|
// Keep the pointer in the list for clean up at exit
|
|
usedStringConstants.PushLast(strPtr);
|
|
|
|
// Push the pointer on the stack. The string factory already guarantees that the
|
|
// string object is valid throughout the lifetime of the script so no need to add
|
|
// reference count or make local copy.
|
|
ctx->bc.InstrPTR(asBC_PGA, strPtr);
|
|
ctx->type.Set(engine->stringType);
|
|
|
|
// Mark the string as literal constant so the compiler knows it is allowed
|
|
// to treat it differently than an ordinary constant string variable
|
|
ctx->type.isConstant = true;
|
|
|
|
// Mark the reference to the string constant as safe, so the compiler can
|
|
// avoid making unnecessary temporary copies when passing the reference to
|
|
// functions.
|
|
ctx->type.isRefSafe = true;
|
|
}
|
|
}
|
|
}
|
|
else if( vnode->tokenType == ttNull )
|
|
{
|
|
ctx->bc.Instr(asBC_PshNull);
|
|
ctx->type.SetNullConstant();
|
|
}
|
|
else
|
|
asASSERT(false);
|
|
}
|
|
else if( vnode->nodeType == snFunctionCall )
|
|
{
|
|
// Determine the scope resolution
|
|
asCString scope = builder->GetScopeFromNode(vnode->firstChild, script);
|
|
|
|
return CompileFunctionCall(vnode, ctx, 0, false, scope);
|
|
}
|
|
else if( vnode->nodeType == snConstructCall )
|
|
{
|
|
return CompileConstructCall(vnode, ctx);
|
|
}
|
|
else if( vnode->nodeType == snAssignment )
|
|
{
|
|
asCExprContext e(engine);
|
|
int r = CompileAssignment(vnode, &e);
|
|
if( r < 0 )
|
|
{
|
|
ctx->type.SetDummy();
|
|
return r;
|
|
}
|
|
MergeExprBytecodeAndType(ctx, &e);
|
|
}
|
|
else if( vnode->nodeType == snCast )
|
|
{
|
|
// Implement the cast operator
|
|
return CompileConversion(vnode, ctx);
|
|
}
|
|
else if( vnode->nodeType == snUndefined && vnode->tokenType == ttVoid )
|
|
{
|
|
// This is a void expression
|
|
ctx->SetVoidExpression();
|
|
}
|
|
else if( vnode->nodeType == snFunction )
|
|
{
|
|
// This is an anonymous function
|
|
// Defer the evaluation of the function until it is known where it
|
|
// will be used, which is where the signature will be defined
|
|
ctx->SetLambda(vnode);
|
|
}
|
|
else
|
|
asASSERT(false);
|
|
|
|
return 0;
|
|
}
|
|
|
|
asUINT asCCompiler::ProcessStringConstant(asCString &cstr, asCScriptNode *node, bool processEscapeSequences)
|
|
{
|
|
int charLiteral = -1;
|
|
|
|
// Process escape sequences
|
|
asCArray<char> str((int)cstr.GetLength());
|
|
|
|
for( asUINT n = 0; n < cstr.GetLength(); n++ )
|
|
{
|
|
#ifdef AS_DOUBLEBYTE_CHARSET
|
|
// Double-byte charset is only allowed for ASCII and not UTF16 encoded strings
|
|
if( (cstr[n] & 0x80) && engine->ep.scanner == 0 && engine->ep.stringEncoding != 1 )
|
|
{
|
|
// This is the lead character of a double byte character
|
|
// include the trail character without checking it's value.
|
|
str.PushLast(cstr[n]);
|
|
n++;
|
|
str.PushLast(cstr[n]);
|
|
continue;
|
|
}
|
|
#endif
|
|
|
|
asUINT val;
|
|
|
|
if( processEscapeSequences && cstr[n] == '\\' )
|
|
{
|
|
++n;
|
|
if( n == cstr.GetLength() )
|
|
{
|
|
if( charLiteral == -1 ) charLiteral = 0;
|
|
return charLiteral;
|
|
}
|
|
|
|
// Hexadecimal escape sequences will allow the construction of
|
|
// invalid unicode sequences, but the string should also work as
|
|
// a bytearray so we must support this. The code for working with
|
|
// unicode text must be prepared to handle invalid unicode sequences
|
|
if( cstr[n] == 'x' || cstr[n] == 'X' )
|
|
{
|
|
++n;
|
|
if( n == cstr.GetLength() ) break;
|
|
|
|
val = 0;
|
|
int c = engine->ep.stringEncoding == 1 ? 4 : 2;
|
|
for( ; c > 0 && n < cstr.GetLength(); c--, n++ )
|
|
{
|
|
if( cstr[n] >= '0' && cstr[n] <= '9' )
|
|
val = val*16 + cstr[n] - '0';
|
|
else if( cstr[n] >= 'a' && cstr[n] <= 'f' )
|
|
val = val*16 + cstr[n] - 'a' + 10;
|
|
else if( cstr[n] >= 'A' && cstr[n] <= 'F' )
|
|
val = val*16 + cstr[n] - 'A' + 10;
|
|
else
|
|
break;
|
|
}
|
|
|
|
// Rewind one, since the loop will increment it again
|
|
n--;
|
|
|
|
// Hexadecimal escape sequences produce exact value, even if it is not proper unicode chars
|
|
if( engine->ep.stringEncoding == 0 )
|
|
{
|
|
str.PushLast((asBYTE)val);
|
|
}
|
|
else
|
|
{
|
|
#ifndef AS_BIG_ENDIAN
|
|
str.PushLast((asBYTE)val);
|
|
str.PushLast((asBYTE)(val>>8));
|
|
#else
|
|
str.PushLast((asBYTE)(val>>8));
|
|
str.PushLast((asBYTE)val);
|
|
#endif
|
|
}
|
|
if( charLiteral == -1 ) charLiteral = val;
|
|
continue;
|
|
}
|
|
else if( cstr[n] == 'u' || cstr[n] == 'U' )
|
|
{
|
|
// \u expects 4 hex digits
|
|
// \U expects 8 hex digits
|
|
bool expect2 = cstr[n] == 'u';
|
|
int c = expect2 ? 4 : 8;
|
|
|
|
val = 0;
|
|
|
|
for( ; c > 0; c-- )
|
|
{
|
|
++n;
|
|
if( n == cstr.GetLength() ) break;
|
|
|
|
if( cstr[n] >= '0' && cstr[n] <= '9' )
|
|
val = val*16 + cstr[n] - '0';
|
|
else if( cstr[n] >= 'a' && cstr[n] <= 'f' )
|
|
val = val*16 + cstr[n] - 'a' + 10;
|
|
else if( cstr[n] >= 'A' && cstr[n] <= 'F' )
|
|
val = val*16 + cstr[n] - 'A' + 10;
|
|
else
|
|
break;
|
|
}
|
|
|
|
if( c != 0 )
|
|
{
|
|
// Give warning about invalid code point
|
|
// TODO: Need code position for warning
|
|
asCString msg;
|
|
msg.Format(TXT_INVALID_UNICODE_FORMAT_EXPECTED_d, expect2 ? 4 : 8);
|
|
Warning(msg, node);
|
|
continue;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( cstr[n] == '"' )
|
|
val = '"';
|
|
else if( cstr[n] == '\'' )
|
|
val = '\'';
|
|
else if( cstr[n] == 'n' )
|
|
val = '\n';
|
|
else if( cstr[n] == 'r' )
|
|
val = '\r';
|
|
else if( cstr[n] == 't' )
|
|
val = '\t';
|
|
else if( cstr[n] == '0' )
|
|
val = '\0';
|
|
else if( cstr[n] == '\\' )
|
|
val = '\\';
|
|
else
|
|
{
|
|
// Invalid escape sequence
|
|
Warning(TXT_INVALID_ESCAPE_SEQUENCE, node);
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( engine->ep.scanner == 1 && (cstr[n] & 0x80) )
|
|
{
|
|
unsigned int len;
|
|
val = asStringDecodeUTF8(&cstr[n], &len);
|
|
if( val == 0xFFFFFFFF )
|
|
{
|
|
// Incorrect UTF8 encoding. Use only the first byte
|
|
// TODO: Need code position for warning
|
|
Warning(TXT_INVALID_UNICODE_SEQUENCE_IN_SRC, node);
|
|
val = (unsigned char)cstr[n];
|
|
}
|
|
else
|
|
n += len-1;
|
|
}
|
|
else
|
|
val = (unsigned char)cstr[n];
|
|
}
|
|
|
|
// Add the character to the final string
|
|
char encodedValue[5];
|
|
int len;
|
|
if( engine->ep.scanner == 1 && engine->ep.stringEncoding == 0 )
|
|
{
|
|
// Convert to UTF8 encoded
|
|
len = asStringEncodeUTF8(val, encodedValue);
|
|
}
|
|
else if( engine->ep.stringEncoding == 1 )
|
|
{
|
|
// Convert to 16bit wide character string (even if the script is scanned as ASCII)
|
|
len = asStringEncodeUTF16(val, encodedValue);
|
|
}
|
|
else
|
|
{
|
|
// Do not convert ASCII characters
|
|
encodedValue[0] = (asBYTE)val;
|
|
len = 1;
|
|
}
|
|
|
|
if( len < 0 )
|
|
{
|
|
// Give warning about invalid code point
|
|
// TODO: Need code position for warning
|
|
Warning(TXT_INVALID_UNICODE_VALUE, node);
|
|
}
|
|
else
|
|
{
|
|
// Add the encoded value to the final string
|
|
str.Concatenate(encodedValue, len);
|
|
if( charLiteral == -1 ) charLiteral = val;
|
|
}
|
|
}
|
|
|
|
cstr.Assign(str.AddressOf(), str.GetLength());
|
|
return charLiteral;
|
|
}
|
|
|
|
void asCCompiler::ProcessHeredocStringConstant(asCString &str, asCScriptNode *node)
|
|
{
|
|
// Remove first line if it only contains whitespace
|
|
bool isMultiline = false;
|
|
int start;
|
|
for( start = 0; start < (int)str.GetLength(); start++ )
|
|
{
|
|
if( str[start] == '\n' )
|
|
{
|
|
isMultiline = true;
|
|
|
|
// Remove the linebreak as well
|
|
start++;
|
|
break;
|
|
}
|
|
|
|
if( str[start] != ' ' &&
|
|
str[start] != '\t' &&
|
|
str[start] != '\r' )
|
|
{
|
|
// Don't remove anything
|
|
start = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Remove the line after the last line break if it only contains whitespaces
|
|
int end;
|
|
for( end = (int)str.GetLength() - 1; end >= 0; end-- )
|
|
{
|
|
if( str[end] == '\n' )
|
|
{
|
|
// Don't remove the last line break
|
|
end++;
|
|
break;
|
|
}
|
|
|
|
if( str[end] != ' ' &&
|
|
str[end] != '\t' &&
|
|
str[end] != '\r' )
|
|
{
|
|
// Don't remove anything
|
|
end = (int)str.GetLength();
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( end < 0 ) end = 0;
|
|
|
|
asCString tmp;
|
|
if (end > start || engine->ep.heredocTrimMode != 2 )
|
|
{
|
|
// if heredocTrimMode == 0 the string shouldn't be trimmed
|
|
// if heredocTrimMode == 1 the string should only be trimmed if it is multiline
|
|
// if heredocTrimMode == 2 the string should always be trimmed
|
|
if (engine->ep.heredocTrimMode == 2 || (isMultiline && engine->ep.heredocTrimMode == 1))
|
|
tmp.Assign(&str[start], end - start);
|
|
else
|
|
tmp = str;
|
|
}
|
|
|
|
ProcessStringConstant(tmp, node, false);
|
|
|
|
str = tmp;
|
|
}
|
|
|
|
int asCCompiler::CompileConversion(asCScriptNode *node, asCExprContext *ctx)
|
|
{
|
|
asCExprContext expr(engine);
|
|
asCDataType to;
|
|
bool anyErrors = false;
|
|
EImplicitConv convType;
|
|
if( node->nodeType == snConstructCall || node->nodeType == snFunctionCall )
|
|
{
|
|
convType = asIC_EXPLICIT_VAL_CAST;
|
|
|
|
// Verify that there is only one argument
|
|
if( node->lastChild->firstChild == 0 ||
|
|
node->lastChild->firstChild != node->lastChild->lastChild )
|
|
{
|
|
Error(TXT_ONLY_ONE_ARGUMENT_IN_CAST, node->lastChild);
|
|
expr.type.SetDummy();
|
|
anyErrors = true;
|
|
}
|
|
else if (node->lastChild->firstChild &&
|
|
node->lastChild->firstChild->nodeType == snNamedArgument)
|
|
{
|
|
Error(TXT_INVALID_USE_OF_NAMED_ARGS, node->lastChild);
|
|
expr.type.SetDummy();
|
|
anyErrors = true;
|
|
}
|
|
else
|
|
{
|
|
// Compile the expression
|
|
int r = CompileAssignment(node->lastChild->firstChild, &expr);
|
|
if( r < 0 )
|
|
anyErrors = true;
|
|
}
|
|
|
|
// Determine the requested type
|
|
to = builder->CreateDataTypeFromNode(node->firstChild, script, outFunc->nameSpace);
|
|
to.MakeReadOnly(true); // Default to const
|
|
asASSERT(to.IsPrimitive());
|
|
}
|
|
else
|
|
{
|
|
convType = asIC_EXPLICIT_REF_CAST;
|
|
|
|
// Compile the expression
|
|
int r = CompileAssignment(node->lastChild, &expr);
|
|
if( r < 0 )
|
|
anyErrors = true;
|
|
|
|
// Determine the requested type
|
|
to = builder->CreateDataTypeFromNode(node->firstChild, script, outFunc->nameSpace);
|
|
|
|
// If the type support object handles, then use it
|
|
if( to.SupportHandles() )
|
|
{
|
|
to.MakeHandle(true);
|
|
if( expr.type.dataType.IsObjectConst() )
|
|
to.MakeHandleToConst(true);
|
|
}
|
|
else if( !to.IsObjectHandle() )
|
|
{
|
|
// The cast<type> operator can only be used for reference casts
|
|
Error(TXT_ILLEGAL_TARGET_TYPE_FOR_REF_CAST, node->firstChild);
|
|
anyErrors = true;
|
|
}
|
|
}
|
|
|
|
// Do not allow casting to non shared type if we're compiling a shared method
|
|
if( outFunc->IsShared() &&
|
|
to.GetTypeInfo() && !to.GetTypeInfo()->IsShared() )
|
|
{
|
|
asCString msg;
|
|
msg.Format(TXT_SHARED_CANNOT_USE_NON_SHARED_TYPE_s, to.GetTypeInfo()->name.AddressOf());
|
|
Error(msg, node);
|
|
anyErrors = true;
|
|
}
|
|
|
|
if( anyErrors )
|
|
{
|
|
// Assume that the error can be fixed and allow the compilation to continue
|
|
ctx->type.Set(to);
|
|
return -1;
|
|
}
|
|
|
|
ProcessPropertyGetAccessor(&expr, node);
|
|
|
|
// Don't allow any operators on expressions that take address of class method
|
|
if( expr.IsClassMethod() )
|
|
{
|
|
Error(TXT_INVALID_OP_ON_METHOD, node);
|
|
return -1;
|
|
}
|
|
|
|
// We don't want a reference for conversion casts
|
|
if( convType == asIC_EXPLICIT_VAL_CAST && expr.type.dataType.IsReference() )
|
|
{
|
|
if( expr.type.dataType.IsObject() )
|
|
Dereference(&expr, true);
|
|
else
|
|
ConvertToVariable(&expr);
|
|
}
|
|
|
|
ImplicitConversion(&expr, to, node, convType);
|
|
|
|
IsVariableInitialized(&expr.type, node);
|
|
|
|
// If no type conversion is really tried ignore it
|
|
if( to == expr.type.dataType )
|
|
{
|
|
// This will keep information about constant type
|
|
MergeExprBytecode(ctx, &expr);
|
|
ctx->type = expr.type;
|
|
return 0;
|
|
}
|
|
|
|
if( to.IsEqualExceptRefAndConst(expr.type.dataType) && to.IsPrimitive() )
|
|
{
|
|
MergeExprBytecode(ctx, &expr);
|
|
ctx->type = expr.type;
|
|
ctx->type.dataType.MakeReadOnly(true);
|
|
return 0;
|
|
}
|
|
|
|
// The implicit conversion already does most of the conversions permitted,
|
|
// here we'll only treat those conversions that require an explicit cast.
|
|
|
|
bool conversionOK = false;
|
|
if( !expr.type.isConstant && expr.type.dataType != asCDataType::CreatePrimitive(ttVoid, false) )
|
|
{
|
|
if( !expr.type.dataType.IsObject() )
|
|
ConvertToTempVariable(&expr);
|
|
|
|
if( to.IsObjectHandle() &&
|
|
expr.type.dataType.IsObjectHandle() &&
|
|
!(!to.IsHandleToConst() && expr.type.dataType.IsHandleToConst()) )
|
|
{
|
|
conversionOK = CompileRefCast(&expr, to, true, node);
|
|
|
|
MergeExprBytecode(ctx, &expr);
|
|
ctx->type = expr.type;
|
|
}
|
|
}
|
|
|
|
if( conversionOK )
|
|
return 0;
|
|
|
|
// Conversion not available
|
|
ctx->type.SetDummy();
|
|
|
|
asCString strTo, strFrom;
|
|
|
|
strTo = to.Format(outFunc->nameSpace);
|
|
strFrom = expr.type.dataType.Format(outFunc->nameSpace);
|
|
|
|
asCString msg;
|
|
msg.Format(TXT_NO_CONVERSION_s_TO_s, strFrom.AddressOf(), strTo.AddressOf());
|
|
|
|
Error(msg, node);
|
|
return -1;
|
|
}
|
|
|
|
void asCCompiler::AfterFunctionCall(int funcID, asCArray<asCExprContext*> &args, asCExprContext *ctx, bool deferAll)
|
|
{
|
|
// deferAll is set to true if for example the function returns a reference, since in
|
|
// this case the function might be returning a reference to one of the arguments.
|
|
|
|
asCScriptFunction *descr = builder->GetFunctionDescription(funcID);
|
|
|
|
// Parameters that are sent by reference should be assigned
|
|
// to the evaluated expression if it is an lvalue
|
|
|
|
// Evaluate the arguments from last to first
|
|
int n = (int)descr->parameterTypes.GetLength() - 1;
|
|
for( ; n >= 0; n-- )
|
|
{
|
|
// All &out arguments must be deferred, except if the argument is clean, in which case the actual reference was passed in to the function
|
|
// If deferAll is set all objects passed by reference or handle must be deferred
|
|
if( (descr->parameterTypes[n].IsReference() && (descr->inOutFlags[n] & asTM_OUTREF) && !args[n]->isCleanArg) ||
|
|
(descr->parameterTypes[n].IsObject() && deferAll && (descr->parameterTypes[n].IsReference() || descr->parameterTypes[n].IsObjectHandle())) )
|
|
{
|
|
asASSERT( !(descr->parameterTypes[n].IsReference() && (descr->inOutFlags[n] == asTM_OUTREF) && !args[n]->isCleanArg) || args[n]->origExpr );
|
|
|
|
// For &inout, only store the argument if it is for a temporary variable
|
|
if( engine->ep.allowUnsafeReferences ||
|
|
descr->inOutFlags[n] != asTM_INOUTREF || args[n]->type.isTemporary )
|
|
{
|
|
// Store the argument for later processing
|
|
asSDeferredParam outParam;
|
|
outParam.argNode = args[n]->exprNode;
|
|
outParam.argType = args[n]->type;
|
|
outParam.argInOutFlags = descr->inOutFlags[n];
|
|
outParam.origExpr = args[n]->origExpr;
|
|
|
|
ctx->deferredParams.PushLast(outParam);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Release the temporary variable now
|
|
ReleaseTemporaryVariable(args[n]->type, &ctx->bc);
|
|
}
|
|
|
|
// Move the argument's deferred expressions over to the final expression
|
|
for( asUINT m = 0; m < args[n]->deferredParams.GetLength(); m++ )
|
|
{
|
|
ctx->deferredParams.PushLast(args[n]->deferredParams[m]);
|
|
args[n]->deferredParams[m].origExpr = 0;
|
|
}
|
|
args[n]->deferredParams.SetLength(0);
|
|
}
|
|
}
|
|
|
|
void asCCompiler::ProcessDeferredParams(asCExprContext *ctx)
|
|
{
|
|
if( isProcessingDeferredParams ) return;
|
|
|
|
isProcessingDeferredParams = true;
|
|
|
|
for( asUINT n = 0; n < ctx->deferredParams.GetLength(); n++ )
|
|
{
|
|
asSDeferredParam outParam = ctx->deferredParams[n];
|
|
if( outParam.argInOutFlags < asTM_OUTREF ) // &in, or not reference
|
|
{
|
|
// Just release the variable
|
|
ReleaseTemporaryVariable(outParam.argType, &ctx->bc);
|
|
}
|
|
else if( outParam.argInOutFlags == asTM_OUTREF )
|
|
{
|
|
asCExprContext *expr = outParam.origExpr;
|
|
outParam.origExpr = 0;
|
|
|
|
if( outParam.argType.dataType.IsObjectHandle() )
|
|
{
|
|
// Implicitly convert the value to a handle
|
|
if( expr->type.dataType.IsObjectHandle() )
|
|
expr->type.isExplicitHandle = true;
|
|
}
|
|
|
|
// Verify that the expression result in a lvalue, or a property accessor
|
|
if( IsLValue(expr->type) || expr->property_get || expr->property_set )
|
|
{
|
|
asCExprContext rctx(engine);
|
|
rctx.type = outParam.argType;
|
|
if( rctx.type.dataType.IsPrimitive() )
|
|
rctx.type.dataType.MakeReference(false);
|
|
else
|
|
{
|
|
rctx.bc.InstrSHORT(asBC_PSF, outParam.argType.stackOffset);
|
|
rctx.type.dataType.MakeReference(IsVariableOnHeap(outParam.argType.stackOffset));
|
|
if( expr->type.isExplicitHandle )
|
|
rctx.type.isExplicitHandle = true;
|
|
}
|
|
|
|
asCExprContext o(engine);
|
|
DoAssignment(&o, expr, &rctx, outParam.argNode, outParam.argNode, ttAssignment, outParam.argNode);
|
|
|
|
if( !o.type.dataType.IsPrimitive() ) o.bc.Instr(asBC_PopPtr);
|
|
|
|
// The assignment may itself have resulted in a new temporary variable, e.g. if
|
|
// the opAssign returns a non-reference. We must release this temporary variable
|
|
// since it won't be used
|
|
ReleaseTemporaryVariable(o.type, &o.bc);
|
|
|
|
MergeExprBytecode(ctx, &o);
|
|
}
|
|
else
|
|
{
|
|
// We must still evaluate the expression
|
|
MergeExprBytecode(ctx, expr);
|
|
if( !expr->IsVoidExpression() && (!expr->type.isConstant || expr->type.IsNullConstant()) )
|
|
ctx->bc.Instr(asBC_PopPtr);
|
|
|
|
// Give an error, except if the argument is void, null or 0 which indicate the argument is explicitly to be ignored
|
|
if( !expr->IsVoidExpression() && !expr->type.IsNullConstant() &&
|
|
!(expr->type.isConstant && expr->type.dataType.IsPrimitive() && expr->type.GetConstantData() == 0) )
|
|
Error(TXT_ARG_NOT_LVALUE, outParam.argNode);
|
|
|
|
ReleaseTemporaryVariable(outParam.argType, &ctx->bc);
|
|
}
|
|
|
|
ReleaseTemporaryVariable(expr->type, &ctx->bc);
|
|
|
|
// Delete the original expression context
|
|
asDELETE(expr, asCExprContext);
|
|
}
|
|
else // &inout
|
|
{
|
|
if( outParam.argType.isTemporary )
|
|
ReleaseTemporaryVariable(outParam.argType, &ctx->bc);
|
|
else if( !outParam.argType.isVariable )
|
|
{
|
|
if( outParam.argType.dataType.IsObject() &&
|
|
((outParam.argType.dataType.GetBehaviour()->addref &&
|
|
outParam.argType.dataType.GetBehaviour()->release) ||
|
|
(outParam.argType.dataType.GetTypeInfo()->flags & asOBJ_NOCOUNT)) )
|
|
{
|
|
// Release the object handle that was taken to guarantee the reference
|
|
ReleaseTemporaryVariable(outParam.argType, &ctx->bc);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ctx->deferredParams.SetLength(0);
|
|
isProcessingDeferredParams = false;
|
|
}
|
|
|
|
|
|
int asCCompiler::CompileConstructCall(asCScriptNode *node, asCExprContext *ctx)
|
|
{
|
|
// The first node is a datatype node
|
|
asCString name;
|
|
asCExprValue tempObj;
|
|
bool onHeap = true;
|
|
asCArray<int> funcs;
|
|
bool error = false;
|
|
|
|
// It is possible that the name is really a constructor
|
|
asCDataType dt;
|
|
dt = builder->CreateDataTypeFromNode(node->firstChild, script, outFunc->nameSpace);
|
|
if( dt.IsPrimitive() )
|
|
{
|
|
// This is a cast to a primitive type
|
|
return CompileConversion(node, ctx);
|
|
}
|
|
|
|
if( dt.GetTypeInfo() && (dt.GetTypeInfo()->flags & asOBJ_IMPLICIT_HANDLE) )
|
|
{
|
|
// Types declared as implicit handle must not attempt to construct a handle
|
|
dt.MakeHandle(false);
|
|
}
|
|
|
|
// Don't accept syntax like object@(expr)
|
|
if( dt.IsObjectHandle() )
|
|
{
|
|
asCString str;
|
|
str.Format(TXT_CANT_CONSTRUCT_s_USE_REF_CAST, dt.Format(outFunc->nameSpace).AddressOf());
|
|
Error(str, node);
|
|
ctx->type.SetDummy();
|
|
return -1;
|
|
}
|
|
|
|
// Make sure the desired type can actually be instantiated
|
|
// Delegates are allowed to be created through construct calls,
|
|
// even though they cannot be instantiated as variables
|
|
if( !dt.CanBeInstantiated() && !dt.IsFuncdef() )
|
|
{
|
|
asCString str;
|
|
if( dt.IsAbstractClass() )
|
|
str.Format(TXT_ABSTRACT_CLASS_s_CANNOT_BE_INSTANTIATED, dt.Format(outFunc->nameSpace).AddressOf());
|
|
else if( dt.IsInterface() )
|
|
str.Format(TXT_INTERFACE_s_CANNOT_BE_INSTANTIATED, dt.Format(outFunc->nameSpace).AddressOf());
|
|
else
|
|
// TODO: Improve error message to explain why
|
|
str.Format(TXT_DATA_TYPE_CANT_BE_s, dt.Format(outFunc->nameSpace).AddressOf());
|
|
Error(str, node);
|
|
ctx->type.SetDummy();
|
|
return -1;
|
|
}
|
|
|
|
// Do not allow constructing non-shared types in shared functions
|
|
if( outFunc->IsShared() &&
|
|
dt.GetTypeInfo() && !dt.GetTypeInfo()->IsShared() )
|
|
{
|
|
asCString msg;
|
|
msg.Format(TXT_SHARED_CANNOT_USE_NON_SHARED_TYPE_s, dt.GetTypeInfo()->name.AddressOf());
|
|
Error(msg, node);
|
|
return -1;
|
|
}
|
|
|
|
// Compile the arguments
|
|
asCArray<asCExprContext *> args;
|
|
asCArray<asSNamedArgument> namedArgs;
|
|
asCArray<asCExprValue> temporaryVariables;
|
|
if( CompileArgumentList(node->lastChild, args, namedArgs) >= 0 )
|
|
{
|
|
// Check for a value cast behaviour
|
|
if( args.GetLength() == 1 )
|
|
{
|
|
asCExprContext conv(engine);
|
|
conv.type = args[0]->type;
|
|
asUINT cost = ImplicitConversion(&conv, dt, node->lastChild, asIC_EXPLICIT_VAL_CAST, false);
|
|
|
|
// Don't use this if the cost is 0 because it would mean that nothing
|
|
// is done and the script wants a new value to be constructed
|
|
if( conv.type.dataType.IsEqualExceptRef(dt) && cost > 0 )
|
|
{
|
|
// Make sure the result is a reference, just as if to a local variable
|
|
dt.MakeReference(true);
|
|
|
|
// Make sure any property accessor is already evaluated
|
|
ProcessPropertyGetAccessor(args[0], args[0]->exprNode);
|
|
|
|
ImplicitConversion(args[0], dt, node->lastChild, asIC_EXPLICIT_VAL_CAST);
|
|
|
|
ctx->bc.AddCode(&args[0]->bc);
|
|
ctx->type = args[0]->type;
|
|
|
|
asDELETE(args[0], asCExprContext);
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
// Check for possible constructor/factory
|
|
name = dt.Format(outFunc->nameSpace);
|
|
|
|
asSTypeBehaviour *beh = dt.GetBehaviour();
|
|
|
|
if( !(dt.GetTypeInfo()->flags & asOBJ_REF) && !dt.IsFuncdef() )
|
|
{
|
|
funcs = beh->constructors;
|
|
|
|
// Value types and script types are allocated through the constructor
|
|
tempObj.dataType = dt;
|
|
tempObj.stackOffset = (short)AllocateVariable(dt, true);
|
|
tempObj.dataType.MakeReference(true);
|
|
tempObj.isTemporary = true;
|
|
tempObj.isVariable = true;
|
|
|
|
onHeap = IsVariableOnHeap(tempObj.stackOffset);
|
|
|
|
// Push the address of the object on the stack
|
|
if( onHeap )
|
|
ctx->bc.InstrSHORT(asBC_VAR, tempObj.stackOffset);
|
|
}
|
|
else if( beh )
|
|
funcs = beh->factories;
|
|
|
|
// Special case: Allow calling func(void) with a void expression.
|
|
if( args.GetLength() == 1 && args[0]->type.dataType == asCDataType::CreatePrimitive(ttVoid, false) )
|
|
{
|
|
// Evaluate the expression before the function call
|
|
MergeExprBytecode(ctx, args[0]);
|
|
asDELETE(args[0], asCExprContext);
|
|
args.SetLength(0);
|
|
}
|
|
|
|
// Special case: If this is an object constructor and there are no arguments use the default constructor.
|
|
// If none has been registered, just allocate the variable and push it on the stack.
|
|
if( args.GetLength() == 0 )
|
|
{
|
|
beh = tempObj.dataType.GetBehaviour();
|
|
if( beh && beh->construct == 0 && !(dt.GetTypeInfo()->flags & asOBJ_REF) )
|
|
{
|
|
// Call the default constructor
|
|
ctx->type = tempObj;
|
|
|
|
if( onHeap )
|
|
{
|
|
asASSERT(ctx->bc.GetLastInstr() == asBC_VAR);
|
|
ctx->bc.RemoveLastInstr();
|
|
}
|
|
|
|
CallDefaultConstructor(tempObj.dataType, tempObj.stackOffset, IsVariableOnHeap(tempObj.stackOffset), &ctx->bc, node);
|
|
|
|
// Push the reference on the stack
|
|
ctx->bc.InstrSHORT(asBC_PSF, tempObj.stackOffset);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
// Special case: If this is a construction of a delegate and the expression names an object method
|
|
if( dt.IsFuncdef() && args.GetLength() == 1 && args[0]->methodName != "" )
|
|
{
|
|
// TODO: delegate: It is possible that the argument returns a function pointer already, in which
|
|
// case no object delegate will be created, but instead a delegate for a function pointer
|
|
// In theory a simple cast would be good in this case, but this is a construct call so it
|
|
// is expected that a new object is created.
|
|
|
|
dt.MakeHandle(true);
|
|
ctx->type.Set(dt);
|
|
|
|
// The delegate must be able to hold on to a reference to the object
|
|
if( !args[0]->type.dataType.SupportHandles() )
|
|
{
|
|
Error(TXT_CANNOT_CREATE_DELEGATE_FOR_NOREF_TYPES, node);
|
|
error = true;
|
|
}
|
|
else
|
|
{
|
|
// Filter the available object methods to find the one that matches the func def
|
|
asCObjectType *type = CastToObjectType(args[0]->type.dataType.GetTypeInfo());
|
|
asCScriptFunction *bestMethod = 0;
|
|
for( asUINT n = 0; n < type->methods.GetLength(); n++ )
|
|
{
|
|
asCScriptFunction *func = engine->scriptFunctions[type->methods[n]];
|
|
|
|
if( func->name != args[0]->methodName )
|
|
continue;
|
|
|
|
// If the expression is for a const object, then only const methods should be accepted
|
|
if( args[0]->type.dataType.IsReadOnly() && !func->IsReadOnly() )
|
|
continue;
|
|
|
|
if( func->IsSignatureExceptNameAndObjectTypeEqual(CastToFuncdefType(dt.GetTypeInfo())->funcdef) )
|
|
{
|
|
bestMethod = func;
|
|
|
|
// If the expression is non-const the non-const overloaded method has priority
|
|
if( args[0]->type.dataType.IsReadOnly() == func->IsReadOnly() )
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( bestMethod )
|
|
{
|
|
// The object pointer is already on the stack
|
|
MergeExprBytecode(ctx, args[0]);
|
|
|
|
// Push the function pointer as an additional argument
|
|
ctx->bc.InstrPTR(asBC_FuncPtr, bestMethod);
|
|
|
|
// Call the factory function for the delegate
|
|
asCArray<int> delegateFuncs;
|
|
builder->GetFunctionDescriptions(DELEGATE_FACTORY, delegateFuncs, engine->nameSpaces[0]);
|
|
asASSERT(delegateFuncs.GetLength() == 1 );
|
|
ctx->bc.Call(asBC_CALLSYS , delegateFuncs[0], 2*AS_PTR_SIZE);
|
|
|
|
// Store the returned delegate in a temporary variable
|
|
int returnOffset = AllocateVariable(dt, true, false);
|
|
dt.MakeReference(true);
|
|
ctx->type.SetVariable(dt, returnOffset, true);
|
|
ctx->bc.InstrSHORT(asBC_STOREOBJ, (short)returnOffset);
|
|
|
|
// Push a reference to the temporary variable on the stack
|
|
ctx->bc.InstrSHORT(asBC_PSF, (short)returnOffset);
|
|
|
|
// Clean up arguments
|
|
ReleaseTemporaryVariable(args[0]->type, &ctx->bc);
|
|
}
|
|
else
|
|
{
|
|
asCString msg;
|
|
msg.Format(TXT_NO_MATCHING_SIGNATURES_TO_s, CastToFuncdefType(dt.GetTypeInfo())->funcdef->GetDeclaration());
|
|
Error(msg.AddressOf(), node);
|
|
error = true;
|
|
}
|
|
}
|
|
|
|
// Clean-up arg
|
|
asDELETE(args[0], asCExprContext);
|
|
return error ? -1 : 0;
|
|
}
|
|
|
|
MatchFunctions(funcs, args, node, name.AddressOf(), &namedArgs, 0, false);
|
|
|
|
if( funcs.GetLength() != 1 )
|
|
{
|
|
// The error was reported by MatchFunctions()
|
|
error = true;
|
|
|
|
// Dummy value
|
|
ctx->type.SetDummy();
|
|
}
|
|
else
|
|
{
|
|
// TODO: Clean up: Merge this with MakeFunctionCall
|
|
|
|
// Add the default values for arguments not explicitly supplied
|
|
int r = CompileDefaultAndNamedArgs(node, args, funcs[0], CastToObjectType(dt.GetTypeInfo()), &namedArgs);
|
|
|
|
if( r == asSUCCESS )
|
|
{
|
|
asCByteCode objBC(engine);
|
|
|
|
PrepareFunctionCall(funcs[0], &ctx->bc, args);
|
|
|
|
MoveArgsToStack(funcs[0], &ctx->bc, args, false);
|
|
|
|
if( !(dt.GetTypeInfo()->flags & asOBJ_REF) )
|
|
{
|
|
// If the object is allocated on the stack, then call the constructor as a normal function
|
|
if( onHeap )
|
|
{
|
|
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);
|
|
}
|
|
else
|
|
ctx->bc.InstrSHORT(asBC_PSF, tempObj.stackOffset);
|
|
|
|
PerformFunctionCall(funcs[0], ctx, onHeap, &args, CastToObjectType(tempObj.dataType.GetTypeInfo()));
|
|
|
|
// Add tag that the object has been initialized
|
|
ctx->bc.ObjInfo(tempObj.stackOffset, asOBJ_INIT);
|
|
|
|
// The constructor doesn't return anything,
|
|
// so we have to manually inform the type of
|
|
// the return value
|
|
ctx->type = tempObj;
|
|
if( !onHeap )
|
|
ctx->type.dataType.MakeReference(false);
|
|
|
|
// Push the address of the object on the stack again
|
|
ctx->bc.InstrSHORT(asBC_PSF, tempObj.stackOffset);
|
|
}
|
|
else
|
|
{
|
|
// Call the factory to create the reference type
|
|
PerformFunctionCall(funcs[0], ctx, false, &args);
|
|
}
|
|
}
|
|
else
|
|
error = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Failed to compile the argument list, set the result to the dummy type
|
|
ctx->type.SetDummy();
|
|
error = true;
|
|
}
|
|
|
|
// Cleanup
|
|
for( asUINT n = 0; n < args.GetLength(); n++ )
|
|
if( args[n] )
|
|
{
|
|
asDELETE(args[n], asCExprContext);
|
|
}
|
|
for( asUINT n = 0; n < namedArgs.GetLength(); n++ )
|
|
if( namedArgs[n].ctx )
|
|
{
|
|
asDELETE(namedArgs[n].ctx, asCExprContext);
|
|
}
|
|
|
|
return error ? -1 : 0;
|
|
}
|
|
|
|
|
|
int asCCompiler::CompileFunctionCall(asCScriptNode *node, asCExprContext *ctx, asCObjectType *objectType, bool objIsConst, const asCString &scope)
|
|
{
|
|
asCExprValue tempObj;
|
|
asCArray<int> funcs;
|
|
int localVar = -1;
|
|
bool initializeMembers = false;
|
|
asCExprContext funcExpr(engine);
|
|
|
|
asCScriptNode *nm = node->lastChild->prev;
|
|
asCString name(&script->code[nm->tokenPos], nm->tokenLength);
|
|
|
|
// Find the matching entities
|
|
// If objectType is set then this is a post op expression and we shouldn't look for local variables
|
|
asCExprContext lookupResult(engine);
|
|
SYMBOLTYPE symbolType = SymbolLookup(name, scope, objectType, &lookupResult);
|
|
if (symbolType < 0)
|
|
return -1;
|
|
if (symbolType == SL_NOMATCH)
|
|
{
|
|
// No matching symbol
|
|
asCString msg;
|
|
asCString smbl;
|
|
if (scope == "::")
|
|
smbl = scope;
|
|
else if (scope != "")
|
|
smbl = scope + "::";
|
|
smbl += name;
|
|
msg.Format(TXT_NO_MATCHING_SYMBOL_s, smbl.AddressOf());
|
|
Error(msg, node);
|
|
return -1;
|
|
}
|
|
|
|
// Is the symbol matching a variable/property?
|
|
if (symbolType == SL_LOCALCONST || symbolType == SL_LOCALVAR ||
|
|
symbolType == SL_THISPTR || symbolType == SL_CLASSPROPACCESS || symbolType == SL_CLASSPROP ||
|
|
symbolType == SL_GLOBALPROPACCESS || symbolType == SL_GLOBALCONST || symbolType == SL_GLOBALVAR || symbolType == SL_ENUMVAL)
|
|
{
|
|
// Variables/properties can be used as functions if they have the opCall
|
|
if (!(lookupResult.type.dataType.IsFuncdef() || lookupResult.type.dataType.IsObject()))
|
|
{
|
|
// The variable is not a function or object with opCall
|
|
asCString msg;
|
|
msg.Format(TXT_NOT_A_FUNC_s_IS_TYPE_s, name.AddressOf(), lookupResult.type.dataType.Format(outFunc->nameSpace).AddressOf());
|
|
Error(msg, node);
|
|
return -1;
|
|
}
|
|
|
|
// Compile the variable
|
|
// TODO: Take advantage of the known symbol, so it doesn't have to be looked up again
|
|
localVar = CompileVariableAccess(name, scope, &funcExpr, node, false, objectType);
|
|
asASSERT(localVar >= 0);
|
|
if( localVar < 0 )
|
|
return -1;
|
|
|
|
if (funcExpr.type.dataType.IsFuncdef())
|
|
{
|
|
funcs.PushLast(CastToFuncdefType(funcExpr.type.dataType.GetTypeInfo())->funcdef->id);
|
|
}
|
|
else if (funcExpr.type.dataType.IsObject())
|
|
{
|
|
// Keep information about temporary variables as deferred expression so it can be properly cleaned up after the call
|
|
if (ctx->type.isTemporary)
|
|
{
|
|
asASSERT(objectType);
|
|
|
|
asSDeferredParam deferred;
|
|
deferred.origExpr = 0;
|
|
deferred.argInOutFlags = asTM_INREF;
|
|
deferred.argNode = 0;
|
|
deferred.argType.SetVariable(ctx->type.dataType, ctx->type.stackOffset, true);
|
|
|
|
ctx->deferredParams.PushLast(deferred);
|
|
}
|
|
if (funcExpr.property_get == 0)
|
|
Dereference(ctx, true);
|
|
|
|
// Add the bytecode for accessing the object on which opCall will be called
|
|
if (ctx->type.dataType.IsObject())
|
|
{
|
|
// Make sure the ProcessPropertyGetAccess knows whether or not to
|
|
// dereference the original object before calling the get accessor
|
|
funcExpr.property_ref = ctx->type.dataType.IsReference();
|
|
}
|
|
MergeExprBytecodeAndType(ctx, &funcExpr);
|
|
ProcessPropertyGetAccessor(ctx, node);
|
|
Dereference(ctx, true);
|
|
|
|
objectType = CastToObjectType(funcExpr.type.dataType.GetTypeInfo());
|
|
|
|
// Get the opCall methods from the object type
|
|
if (funcExpr.type.dataType.IsObjectHandle())
|
|
objIsConst = funcExpr.type.dataType.IsHandleToConst();
|
|
else
|
|
objIsConst = funcExpr.type.dataType.IsReadOnly();
|
|
|
|
builder->GetObjectMethodDescriptions("opCall", CastToObjectType(funcExpr.type.dataType.GetTypeInfo()), funcs, objIsConst);
|
|
}
|
|
}
|
|
|
|
// Is the symbol matching a class method?
|
|
if (symbolType == SL_CLASSMETHOD)
|
|
{
|
|
// If we're compiling a constructor and the name of the function is super then
|
|
// the constructor of the base class is being called.
|
|
// super cannot be prefixed with a scope operator
|
|
if (scope == "" && m_isConstructor && name == SUPER_TOKEN)
|
|
{
|
|
// If the class is not derived from anyone else, calling super should give an error
|
|
if (outFunc && outFunc->objectType->derivedFrom)
|
|
funcs = outFunc->objectType->derivedFrom->beh.constructors;
|
|
|
|
// Must not allow calling base class' constructor multiple times
|
|
if (continueLabels.GetLength() > 0)
|
|
{
|
|
// If a continue label is set we are in a loop
|
|
Error(TXT_CANNOT_CALL_CONSTRUCTOR_IN_LOOPS, node);
|
|
}
|
|
else if (breakLabels.GetLength() > 0)
|
|
{
|
|
// TODO: inheritance: Should eventually allow constructors in switch statements
|
|
// If a break label is set we are either in a loop or a switch statements
|
|
Error(TXT_CANNOT_CALL_CONSTRUCTOR_IN_SWITCH, node);
|
|
}
|
|
else if (m_isConstructorCalled)
|
|
{
|
|
Error(TXT_CANNOT_CALL_CONSTRUCTOR_TWICE, node);
|
|
}
|
|
m_isConstructorCalled = true;
|
|
|
|
// We need to initialize the class members, but only after all the deferred arguments have been completed
|
|
initializeMembers = true;
|
|
}
|
|
else
|
|
{
|
|
// The scope can be used to specify the base class
|
|
builder->GetObjectMethodDescriptions(name.AddressOf(), CastToObjectType(lookupResult.type.dataType.GetTypeInfo()), funcs, objIsConst, scope, node, script);
|
|
}
|
|
|
|
// If a class method is being called implicitly, then add the this pointer for the call
|
|
if (funcs.GetLength() && !objectType && outFunc->objectType)
|
|
{
|
|
// Verify that the identified function is actually part of the class hierarchy
|
|
if (!outFunc->objectType->DerivesFrom(lookupResult.type.dataType.GetTypeInfo()))
|
|
{
|
|
asCString msg;
|
|
asCString mthd;
|
|
if (scope == "")
|
|
mthd = name;
|
|
else if (scope == "::")
|
|
mthd = scope + name;
|
|
else
|
|
mthd = scope + "::" + name;
|
|
|
|
msg.Format(TXT_METHOD_s_NOT_PART_OF_OBJECT_s, mthd.AddressOf(), outFunc->objectType->name.AddressOf());
|
|
Error(msg, node);
|
|
return -1;
|
|
}
|
|
|
|
objectType = outFunc->objectType;
|
|
|
|
asCDataType dt = asCDataType::CreateType(objectType, false);
|
|
|
|
// The object pointer is located at stack position 0
|
|
ctx->bc.InstrSHORT(asBC_PSF, 0);
|
|
ctx->type.SetVariable(dt, 0, false);
|
|
ctx->type.dataType.MakeReference(true);
|
|
|
|
Dereference(ctx, true);
|
|
}
|
|
else if (funcs.GetLength() && !objectType && !outFunc->objectType)
|
|
{
|
|
// Cannot call class methods directly without the object
|
|
asCString msg;
|
|
msg.Format(TXT_CANNOT_ACCESS_NON_STATIC_MEMBER_s, name.AddressOf());
|
|
Error(msg, node);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
// Is it a global function?
|
|
if (symbolType == SL_GLOBALFUNC)
|
|
{
|
|
// The symbol lookup identified the namespace to use
|
|
int n = lookupResult.methodName.FindLast("::");
|
|
asSNameSpace *ns = engine->FindNameSpace(lookupResult.methodName.SubString(0, n).AddressOf());
|
|
|
|
builder->GetFunctionDescriptions(name.AddressOf(), funcs, ns);
|
|
}
|
|
|
|
// Is it a type?
|
|
if (symbolType == SL_CLASSTYPE || symbolType == SL_GLOBALTYPE)
|
|
{
|
|
bool isValid = false;
|
|
asCDataType dt = builder->CreateDataTypeFromNode(node->firstChild, script, outFunc->nameSpace, false, 0, false, &isValid);
|
|
if (isValid)
|
|
return CompileConstructCall(node, ctx);
|
|
}
|
|
|
|
// Compile the arguments
|
|
asCArray<asCExprContext *> args;
|
|
asCArray<asSNamedArgument> namedArgs;
|
|
|
|
bool isOK = true;
|
|
if( CompileArgumentList(node->lastChild, args, namedArgs) >= 0 )
|
|
{
|
|
// Special case: Allow calling func(void) with an expression that evaluates to no datatype, but isn't exactly 'void'
|
|
if( args.GetLength() == 1 && args[0]->type.IsVoid() && !args[0]->IsVoidExpression() )
|
|
{
|
|
// Evaluate the expression before the function call
|
|
MergeExprBytecode(ctx, args[0]);
|
|
asDELETE(args[0], asCExprContext);
|
|
args.SetLength(0);
|
|
}
|
|
|
|
MatchFunctions(funcs, args, node, name.AddressOf(), &namedArgs, objectType, objIsConst, false, true, scope);
|
|
|
|
if( funcs.GetLength() != 1 )
|
|
{
|
|
// The error was reported by MatchFunctions()
|
|
|
|
// Dummy value
|
|
ctx->type.SetDummy();
|
|
isOK = false;
|
|
}
|
|
else
|
|
{
|
|
// Add the default values for arguments not explicitly supplied
|
|
int r = CompileDefaultAndNamedArgs(node, args, funcs[0], objectType, &namedArgs);
|
|
|
|
// TODO: funcdef: Do we have to make sure the handle is stored in a temporary variable, or
|
|
// is it enough to make sure it is in a local variable?
|
|
|
|
// For function pointer we must guarantee that the function is safe, i.e.
|
|
// by first storing the function pointer in a local variable (if it isn't already in one)
|
|
if( r == asSUCCESS )
|
|
{
|
|
asCScriptFunction *func = builder->GetFunctionDescription(funcs[0]);
|
|
if( func->funcType == asFUNC_FUNCDEF )
|
|
{
|
|
if( objectType && funcExpr.property_get <= 0 )
|
|
{
|
|
// Dereference the object pointer to access the member
|
|
Dereference(ctx, true);
|
|
}
|
|
|
|
if( funcExpr.property_get > 0 )
|
|
{
|
|
ProcessPropertyGetAccessor(&funcExpr, node);
|
|
Dereference(&funcExpr, true);
|
|
}
|
|
else
|
|
{
|
|
Dereference(&funcExpr, true);
|
|
ConvertToVariable(&funcExpr);
|
|
}
|
|
|
|
// The actual function should be called as if a global function
|
|
objectType = 0;
|
|
|
|
// The function call will be made directly from the local variable so the function pointer shouldn't be on the stack
|
|
funcExpr.bc.Instr(asBC_PopPtr);
|
|
|
|
asCExprValue tmp = ctx->type;
|
|
MergeExprBytecodeAndType(ctx, &funcExpr);
|
|
ReleaseTemporaryVariable(tmp, &ctx->bc);
|
|
}
|
|
|
|
MakeFunctionCall(ctx, funcs[0], objectType, args, node, false, 0, funcExpr.type.stackOffset);
|
|
}
|
|
else
|
|
isOK = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Failed to compile the argument list, set the dummy type and continue compilation
|
|
ctx->type.SetDummy();
|
|
isOK = false;
|
|
}
|
|
|
|
// Cleanup
|
|
for( asUINT n = 0; n < args.GetLength(); n++ )
|
|
if( args[n] )
|
|
{
|
|
asDELETE(args[n], asCExprContext);
|
|
}
|
|
for( asUINT n = 0; n < namedArgs.GetLength(); n++ )
|
|
if( namedArgs[n].ctx )
|
|
{
|
|
asDELETE(namedArgs[n].ctx, asCExprContext);
|
|
}
|
|
|
|
if( initializeMembers )
|
|
{
|
|
asASSERT( m_isConstructor );
|
|
|
|
// Need to initialize members here, as they may use the properties of the base class
|
|
// If there are multiple paths that call super(), then there will also be multiple
|
|
// locations with initializations of the members. It is not possible to consolidate
|
|
// these in one place, as the expressions for the initialization are evaluated where
|
|
// they are compiled, which means that they may access different variables depending
|
|
// on the scope where super() is called.
|
|
// Members that don't have an explicit initialization expression will be initialized
|
|
// beginning of the constructor as they are guaranteed not to use at the any
|
|
// members of the base class.
|
|
CompileMemberInitialization(&ctx->bc, false);
|
|
}
|
|
|
|
return isOK ? 0 : -1;
|
|
}
|
|
|
|
asSNameSpace *asCCompiler::DetermineNameSpace(const asCString &scope)
|
|
{
|
|
asSNameSpace *ns;
|
|
|
|
if( scope == "" )
|
|
{
|
|
// When compiling default argument expression the correct namespace is stored in the outFunc even for objects
|
|
if( outFunc->nameSpace->name != "" || isCompilingDefaultArg )
|
|
ns = outFunc->nameSpace;
|
|
else if( outFunc->objectType && outFunc->objectType->nameSpace->name != "" )
|
|
ns = outFunc->objectType->nameSpace;
|
|
else
|
|
ns = engine->nameSpaces[0];
|
|
}
|
|
else if( scope == "::" )
|
|
ns = engine->nameSpaces[0];
|
|
else
|
|
ns = engine->FindNameSpace(scope.AddressOf());
|
|
|
|
return ns;
|
|
}
|
|
|
|
int asCCompiler::CompileExpressionPreOp(asCScriptNode *node, asCExprContext *ctx)
|
|
{
|
|
int op = node->tokenType;
|
|
|
|
// Don't allow any prefix operators except handle on expressions that take address of class method
|
|
if( ctx->IsClassMethod() && op != ttHandle )
|
|
{
|
|
Error(TXT_INVALID_OP_ON_METHOD, node);
|
|
return -1;
|
|
}
|
|
|
|
// Don't allow any operators on void expressions
|
|
if( ctx->IsVoidExpression() )
|
|
{
|
|
Error(TXT_VOID_CANT_BE_OPERAND, node);
|
|
return -1;
|
|
}
|
|
|
|
IsVariableInitialized(&ctx->type, node);
|
|
|
|
if( op == ttHandle )
|
|
{
|
|
if( ctx->methodName != "" )
|
|
{
|
|
// Don't allow taking the handle of a handle
|
|
if( ctx->type.isExplicitHandle )
|
|
{
|
|
Error(TXT_OBJECT_HANDLE_NOT_SUPPORTED, node);
|
|
return -1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Don't allow taking handle of a handle, i.e. @@
|
|
if( ctx->type.isExplicitHandle )
|
|
{
|
|
Error(TXT_OBJECT_HANDLE_NOT_SUPPORTED, node);
|
|
return -1;
|
|
}
|
|
|
|
// @null is allowed even though it is implicit
|
|
if( !ctx->type.IsNullConstant() )
|
|
{
|
|
// Verify that the type allow its handle to be taken
|
|
if( !ctx->type.dataType.SupportHandles() && !ctx->type.dataType.IsObjectHandle() )
|
|
{
|
|
Error(TXT_OBJECT_HANDLE_NOT_SUPPORTED, node);
|
|
return -1;
|
|
}
|
|
|
|
// Objects that are not local variables are not references
|
|
// Objects allocated on the stack are also not marked as references
|
|
if( !ctx->type.dataType.IsReference() &&
|
|
!((ctx->type.dataType.IsObject() || ctx->type.dataType.IsFuncdef()) && !ctx->type.isVariable) &&
|
|
!(ctx->type.isVariable && !IsVariableOnHeap(ctx->type.stackOffset)) )
|
|
{
|
|
Error(TXT_NOT_VALID_REFERENCE, node);
|
|
return -1;
|
|
}
|
|
|
|
// Convert the expression to a handle
|
|
if( !ctx->type.dataType.IsObjectHandle() && !(ctx->type.dataType.GetTypeInfo()->flags & asOBJ_ASHANDLE) )
|
|
{
|
|
asCDataType to = ctx->type.dataType;
|
|
to.MakeHandle(true);
|
|
to.MakeReference(true);
|
|
to.MakeHandleToConst(ctx->type.dataType.IsReadOnly());
|
|
ImplicitConversion(ctx, to, node, asIC_IMPLICIT_CONV, true, false);
|
|
|
|
asASSERT( ctx->type.dataType.IsObjectHandle() );
|
|
}
|
|
else if( ctx->type.dataType.GetTypeInfo()->flags & asOBJ_ASHANDLE )
|
|
{
|
|
// For the ASHANDLE type we'll simply set the expression as a handle
|
|
ctx->type.dataType.MakeHandle(true);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Mark the expression as an explicit handle to avoid implicit conversions to non-handle expressions
|
|
ctx->type.isExplicitHandle = true;
|
|
}
|
|
else if( (op == ttMinus || op == ttPlus || op == ttBitNot || op == ttInc || op == ttDec) && ctx->type.dataType.IsObject() )
|
|
{
|
|
// Look for the appropriate method
|
|
// There is no overloadable operator for unary plus
|
|
const char *opName = 0;
|
|
switch( op )
|
|
{
|
|
case ttMinus: opName = "opNeg"; break;
|
|
case ttBitNot: opName = "opCom"; break;
|
|
case ttInc: opName = "opPreInc"; break;
|
|
case ttDec: opName = "opPreDec"; break;
|
|
}
|
|
|
|
if( opName )
|
|
{
|
|
// TODO: Should convert this to something similar to CompileOverloadedDualOperator2
|
|
ProcessPropertyGetAccessor(ctx, node);
|
|
|
|
// 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
|
|
|
|
// Find the correct method
|
|
bool isConst = ctx->type.dataType.IsObjectConst();
|
|
asCArray<int> funcs;
|
|
asCObjectType *ot = CastToObjectType(ctx->type.dataType.GetTypeInfo());
|
|
for( asUINT n = 0; n < ot->methods.GetLength(); n++ )
|
|
{
|
|
asCScriptFunction *func = engine->scriptFunctions[ot->methods[n]];
|
|
if( func->name == opName &&
|
|
func->parameterTypes.GetLength() == 0 &&
|
|
(!isConst || func->IsReadOnly()) )
|
|
{
|
|
funcs.PushLast(func->id);
|
|
}
|
|
}
|
|
|
|
// Did we find the method?
|
|
if( funcs.GetLength() == 1 )
|
|
{
|
|
asCArray<asCExprContext *> args;
|
|
MakeFunctionCall(ctx, funcs[0], CastToObjectType(ctx->type.dataType.GetTypeInfo()), args, node);
|
|
return 0;
|
|
}
|
|
else if( funcs.GetLength() == 0 )
|
|
{
|
|
asCString str;
|
|
str = asCString(opName) + "()";
|
|
if( isConst )
|
|
str += " const";
|
|
str.Format(TXT_FUNCTION_s_NOT_FOUND, str.AddressOf());
|
|
Error(str, node);
|
|
ctx->type.SetDummy();
|
|
return -1;
|
|
}
|
|
else if( funcs.GetLength() > 1 )
|
|
{
|
|
Error(TXT_MORE_THAN_ONE_MATCHING_OP, node);
|
|
PrintMatchingFuncs(funcs, node);
|
|
|
|
ctx->type.SetDummy();
|
|
return -1;
|
|
}
|
|
}
|
|
else if( op == ttPlus )
|
|
{
|
|
Error(TXT_ILLEGAL_OPERATION, node);
|
|
ctx->type.SetDummy();
|
|
return -1;
|
|
}
|
|
}
|
|
else if( op == ttPlus || op == ttMinus )
|
|
{
|
|
// This is only for primitives. Objects are treated in the above block
|
|
|
|
// Make sure the type is a math type
|
|
if( !(ctx->type.dataType.IsIntegerType() ||
|
|
ctx->type.dataType.IsUnsignedType() ||
|
|
ctx->type.dataType.IsFloatType() ||
|
|
ctx->type.dataType.IsDoubleType() ) )
|
|
{
|
|
Error(TXT_ILLEGAL_OPERATION, node);
|
|
return -1;
|
|
}
|
|
|
|
|
|
ProcessPropertyGetAccessor(ctx, node);
|
|
|
|
asCDataType to = ctx->type.dataType;
|
|
|
|
if( ctx->type.dataType.IsUnsignedType() )
|
|
{
|
|
if( ctx->type.dataType.GetSizeInMemoryBytes() == 1 )
|
|
to = asCDataType::CreatePrimitive(ttInt8, false);
|
|
else if( ctx->type.dataType.GetSizeInMemoryBytes() == 2 )
|
|
to = asCDataType::CreatePrimitive(ttInt16, false);
|
|
else if( ctx->type.dataType.GetSizeInMemoryBytes() == 4 )
|
|
to = asCDataType::CreatePrimitive(ttInt, false);
|
|
else if( ctx->type.dataType.GetSizeInMemoryBytes() == 8 )
|
|
to = asCDataType::CreatePrimitive(ttInt64, false);
|
|
else
|
|
{
|
|
Error(TXT_INVALID_TYPE, node);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if( ctx->type.dataType.IsReference() ) ConvertToVariable(ctx);
|
|
|
|
// Use an explicit conversion in case of constants to avoid unnecessary warning about change of sign
|
|
ImplicitConversion(ctx, to, node, ctx->type.isConstant ? asIC_EXPLICIT_VAL_CAST : asIC_IMPLICIT_CONV);
|
|
|
|
if( !ctx->type.isConstant )
|
|
{
|
|
ConvertToTempVariable(ctx);
|
|
asASSERT(!ctx->type.isLValue);
|
|
|
|
if( op == ttMinus )
|
|
{
|
|
if( ctx->type.dataType.IsIntegerType() && ctx->type.dataType.GetSizeInMemoryDWords() == 1 )
|
|
ctx->bc.InstrSHORT(asBC_NEGi, ctx->type.stackOffset);
|
|
else if( ctx->type.dataType.IsIntegerType() && ctx->type.dataType.GetSizeInMemoryDWords() == 2 )
|
|
ctx->bc.InstrSHORT(asBC_NEGi64, ctx->type.stackOffset);
|
|
else if( ctx->type.dataType.IsFloatType() )
|
|
ctx->bc.InstrSHORT(asBC_NEGf, ctx->type.stackOffset);
|
|
else if( ctx->type.dataType.IsDoubleType() )
|
|
ctx->bc.InstrSHORT(asBC_NEGd, ctx->type.stackOffset);
|
|
else
|
|
{
|
|
Error(TXT_ILLEGAL_OPERATION, node);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( op == ttMinus )
|
|
{
|
|
if (ctx->type.dataType.IsIntegerType())
|
|
{
|
|
if (ctx->type.dataType.GetSizeInMemoryBytes() == 4)
|
|
ctx->type.SetConstantDW(-(int)ctx->type.GetConstantDW());
|
|
else if (ctx->type.dataType.GetSizeInMemoryBytes() == 2)
|
|
ctx->type.SetConstantW(-(asINT16)ctx->type.GetConstantW());
|
|
else if (ctx->type.dataType.GetSizeInMemoryBytes() == 1)
|
|
ctx->type.SetConstantB(-(asINT8)ctx->type.GetConstantB());
|
|
else if (ctx->type.dataType.GetSizeInMemoryBytes() == 8)
|
|
ctx->type.SetConstantQW(-(asINT64)ctx->type.GetConstantQW());
|
|
}
|
|
else if( ctx->type.dataType.IsFloatType() )
|
|
ctx->type.SetConstantF(-ctx->type.GetConstantF());
|
|
else if( ctx->type.dataType.IsDoubleType() )
|
|
ctx->type.SetConstantD(-ctx->type.GetConstantD());
|
|
else
|
|
{
|
|
Error(TXT_ILLEGAL_OPERATION, node);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
else if( op == ttNot )
|
|
{
|
|
// Allow value types to be converted to bool using 'bool opImplConv()'
|
|
if( ctx->type.dataType.GetTypeInfo() && (ctx->type.dataType.GetTypeInfo()->GetFlags() & asOBJ_VALUE) )
|
|
ImplicitConversion(ctx, asCDataType::CreatePrimitive(ttBool, false), node, asIC_IMPLICIT_CONV);
|
|
|
|
if( ctx->type.dataType.IsEqualExceptRefAndConst(asCDataType::CreatePrimitive(ttBool, true)) )
|
|
{
|
|
if( ctx->type.isConstant )
|
|
{
|
|
#if AS_SIZEOF_BOOL == 1
|
|
ctx->type.SetConstantB(ctx->type.GetConstantB() == 0 ? VALUE_OF_BOOLEAN_TRUE : 0);
|
|
#else
|
|
ctx->type.SetConstantDW(ctx->type.GetConstantDW() == 0 ? VALUE_OF_BOOLEAN_TRUE : 0);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
ProcessPropertyGetAccessor(ctx, node);
|
|
|
|
ConvertToTempVariable(ctx);
|
|
asASSERT(!ctx->type.isLValue);
|
|
|
|
ctx->bc.InstrSHORT(asBC_NOT, ctx->type.stackOffset);
|
|
}
|
|
else
|
|
{
|
|
Error(TXT_ILLEGAL_OPERATION, node);
|
|
return -1;
|
|
}
|
|
}
|
|
else if( op == ttBitNot )
|
|
{
|
|
ProcessPropertyGetAccessor(ctx, node);
|
|
|
|
asCDataType to = ctx->type.dataType;
|
|
|
|
if( ctx->type.dataType.IsIntegerType() )
|
|
{
|
|
if( ctx->type.dataType.GetSizeInMemoryBytes() == 1 )
|
|
to = asCDataType::CreatePrimitive(ttUInt8, false);
|
|
else if( ctx->type.dataType.GetSizeInMemoryBytes() == 2 )
|
|
to = asCDataType::CreatePrimitive(ttUInt16, false);
|
|
else if( ctx->type.dataType.GetSizeInMemoryBytes() == 4 )
|
|
to = asCDataType::CreatePrimitive(ttUInt, false);
|
|
else if( ctx->type.dataType.GetSizeInMemoryBytes() == 8 )
|
|
to = asCDataType::CreatePrimitive(ttUInt64, false);
|
|
else
|
|
{
|
|
Error(TXT_INVALID_TYPE, node);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if( ctx->type.dataType.IsReference() ) ConvertToVariable(ctx);
|
|
ImplicitConversion(ctx, to, node, asIC_IMPLICIT_CONV);
|
|
|
|
if( ctx->type.dataType.IsUnsignedType() )
|
|
{
|
|
if( ctx->type.isConstant )
|
|
{
|
|
if( ctx->type.dataType.GetSizeInMemoryBytes() == 1 )
|
|
ctx->type.SetConstantB(~ctx->type.GetConstantB());
|
|
else if (ctx->type.dataType.GetSizeInMemoryBytes() == 2)
|
|
ctx->type.SetConstantW(~ctx->type.GetConstantW());
|
|
else if (ctx->type.dataType.GetSizeInMemoryBytes() == 4)
|
|
ctx->type.SetConstantDW(~ctx->type.GetConstantDW());
|
|
else
|
|
ctx->type.SetConstantQW(~ctx->type.GetConstantQW());
|
|
return 0;
|
|
}
|
|
|
|
ConvertToTempVariable(ctx);
|
|
asASSERT(!ctx->type.isLValue);
|
|
|
|
if( ctx->type.dataType.GetSizeInMemoryDWords() == 1 )
|
|
ctx->bc.InstrSHORT(asBC_BNOT, ctx->type.stackOffset);
|
|
else
|
|
ctx->bc.InstrSHORT(asBC_BNOT64, ctx->type.stackOffset);
|
|
}
|
|
else
|
|
{
|
|
Error(TXT_ILLEGAL_OPERATION, node);
|
|
return -1;
|
|
}
|
|
}
|
|
else if( op == ttInc || op == ttDec )
|
|
{
|
|
// Need a reference to the primitive that will be updated
|
|
// The result of this expression is the same reference as before
|
|
|
|
// Make sure the reference isn't a temporary variable
|
|
if( ctx->type.isTemporary )
|
|
{
|
|
Error(TXT_REF_IS_TEMP, node);
|
|
return -1;
|
|
}
|
|
if( ctx->type.dataType.IsReadOnly() )
|
|
{
|
|
Error(TXT_REF_IS_READ_ONLY, node);
|
|
return -1;
|
|
}
|
|
if( ctx->property_get || ctx->property_set )
|
|
{
|
|
Error(TXT_INVALID_REF_PROP_ACCESS, node);
|
|
return -1;
|
|
}
|
|
if( !ctx->type.isLValue )
|
|
{
|
|
Error(TXT_NOT_LVALUE, node);
|
|
return -1;
|
|
}
|
|
|
|
if( ctx->type.isVariable && !ctx->type.dataType.IsReference() )
|
|
ConvertToReference(ctx);
|
|
else if( !ctx->type.dataType.IsReference() )
|
|
{
|
|
Error(TXT_NOT_VALID_REFERENCE, node);
|
|
return -1;
|
|
}
|
|
|
|
if( ctx->type.dataType.IsEqualExceptRef(asCDataType::CreatePrimitive(ttInt64, false)) ||
|
|
ctx->type.dataType.IsEqualExceptRef(asCDataType::CreatePrimitive(ttUInt64, false)) )
|
|
{
|
|
if( op == ttInc )
|
|
ctx->bc.Instr(asBC_INCi64);
|
|
else
|
|
ctx->bc.Instr(asBC_DECi64);
|
|
}
|
|
else if( ctx->type.dataType.IsEqualExceptRef(asCDataType::CreatePrimitive(ttInt, false)) ||
|
|
ctx->type.dataType.IsEqualExceptRef(asCDataType::CreatePrimitive(ttUInt, false)) )
|
|
{
|
|
if( op == ttInc )
|
|
ctx->bc.Instr(asBC_INCi);
|
|
else
|
|
ctx->bc.Instr(asBC_DECi);
|
|
}
|
|
else if( ctx->type.dataType.IsEqualExceptRef(asCDataType::CreatePrimitive(ttInt16, false)) ||
|
|
ctx->type.dataType.IsEqualExceptRef(asCDataType::CreatePrimitive(ttUInt16, false)) )
|
|
{
|
|
if( op == ttInc )
|
|
ctx->bc.Instr(asBC_INCi16);
|
|
else
|
|
ctx->bc.Instr(asBC_DECi16);
|
|
}
|
|
else if( ctx->type.dataType.IsEqualExceptRef(asCDataType::CreatePrimitive(ttInt8, false)) ||
|
|
ctx->type.dataType.IsEqualExceptRef(asCDataType::CreatePrimitive(ttUInt8, false)) )
|
|
{
|
|
if( op == ttInc )
|
|
ctx->bc.Instr(asBC_INCi8);
|
|
else
|
|
ctx->bc.Instr(asBC_DECi8);
|
|
}
|
|
else if( ctx->type.dataType.IsEqualExceptRef(asCDataType::CreatePrimitive(ttFloat, false)) )
|
|
{
|
|
if( op == ttInc )
|
|
ctx->bc.Instr(asBC_INCf);
|
|
else
|
|
ctx->bc.Instr(asBC_DECf);
|
|
}
|
|
else if( ctx->type.dataType.IsEqualExceptRef(asCDataType::CreatePrimitive(ttDouble, false)) )
|
|
{
|
|
if( op == ttInc )
|
|
ctx->bc.Instr(asBC_INCd);
|
|
else
|
|
ctx->bc.Instr(asBC_DECd);
|
|
}
|
|
else
|
|
{
|
|
Error(TXT_ILLEGAL_OPERATION, node);
|
|
return -1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Unknown operator
|
|
asASSERT(false);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void asCCompiler::ConvertToReference(asCExprContext *ctx)
|
|
{
|
|
if( ctx->type.isVariable && !ctx->type.dataType.IsReference() )
|
|
{
|
|
ctx->bc.InstrSHORT(asBC_LDV, ctx->type.stackOffset);
|
|
ctx->type.dataType.MakeReference(true);
|
|
ctx->type.SetVariable(ctx->type.dataType, ctx->type.stackOffset, ctx->type.isTemporary);
|
|
}
|
|
}
|
|
|
|
int asCCompiler::FindPropertyAccessor(const asCString &name, asCExprContext *ctx, asCScriptNode *node, asSNameSpace *ns, bool isThisAccess)
|
|
{
|
|
return FindPropertyAccessor(name, ctx, 0, node, ns, isThisAccess);
|
|
}
|
|
|
|
int asCCompiler::FindPropertyAccessor(const asCString &name, asCExprContext *ctx, asCExprContext *arg, asCScriptNode *node, asSNameSpace *ns, bool isThisAccess)
|
|
{
|
|
if( engine->ep.propertyAccessorMode == 0 )
|
|
{
|
|
// Property accessors have been disabled by the application
|
|
return 0;
|
|
}
|
|
|
|
int getId = 0, setId = 0;
|
|
asCString getName = "get_" + name;
|
|
asCString setName = "set_" + name;
|
|
asCArray<int> multipleGetFuncs, multipleSetFuncs;
|
|
|
|
if( ctx->type.dataType.IsObject() )
|
|
{
|
|
asASSERT( ns == 0 );
|
|
|
|
// Don't look for property accessors in script classes if the script
|
|
// property accessors have been disabled by the application
|
|
if( !(ctx->type.dataType.GetTypeInfo()->flags & asOBJ_SCRIPT_OBJECT) ||
|
|
engine->ep.propertyAccessorMode == 2 )
|
|
{
|
|
// Check if the object has any methods with the corresponding accessor name(s)
|
|
asCObjectType *ot = CastToObjectType(ctx->type.dataType.GetTypeInfo());
|
|
for( asUINT n = 0; n < ot->methods.GetLength(); n++ )
|
|
{
|
|
asCScriptFunction *f = engine->scriptFunctions[ot->methods[n]];
|
|
// TODO: The type of the parameter should match the argument (unless the arg is a dummy)
|
|
if( f->name == getName && (int)f->parameterTypes.GetLength() == (arg?1:0) )
|
|
{
|
|
if( getId == 0 )
|
|
getId = ot->methods[n];
|
|
else
|
|
{
|
|
if( multipleGetFuncs.GetLength() == 0 )
|
|
multipleGetFuncs.PushLast(getId);
|
|
|
|
multipleGetFuncs.PushLast(ot->methods[n]);
|
|
}
|
|
}
|
|
// TODO: getset: If the parameter is a reference, it must not be an out reference. Should we allow inout ref?
|
|
if( f->name == setName && (int)f->parameterTypes.GetLength() == (arg?2:1) )
|
|
{
|
|
if( setId == 0 )
|
|
setId = ot->methods[n];
|
|
else
|
|
{
|
|
if( multipleSetFuncs.GetLength() == 0 )
|
|
multipleSetFuncs.PushLast(setId);
|
|
|
|
multipleSetFuncs.PushLast(ot->methods[n]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
asASSERT( ns != 0 );
|
|
|
|
// Look for appropriate global functions.
|
|
asCArray<int> funcs;
|
|
asUINT n;
|
|
builder->GetFunctionDescriptions(getName.AddressOf(), funcs, ns);
|
|
for( n = 0; n < funcs.GetLength(); n++ )
|
|
{
|
|
asCScriptFunction *f = builder->GetFunctionDescription(funcs[n]);
|
|
// TODO: The type of the parameter should match the argument (unless the arg is a dummy)
|
|
if( (int)f->parameterTypes.GetLength() == (arg?1:0) )
|
|
{
|
|
if( getId == 0 )
|
|
getId = funcs[n];
|
|
else
|
|
{
|
|
if( multipleGetFuncs.GetLength() == 0 )
|
|
multipleGetFuncs.PushLast(getId);
|
|
|
|
multipleGetFuncs.PushLast(funcs[n]);
|
|
}
|
|
}
|
|
}
|
|
|
|
funcs.SetLength(0);
|
|
builder->GetFunctionDescriptions(setName.AddressOf(), funcs, ns);
|
|
for( n = 0; n < funcs.GetLength(); n++ )
|
|
{
|
|
asCScriptFunction *f = builder->GetFunctionDescription(funcs[n]);
|
|
// TODO: getset: If the parameter is a reference, it must not be an out reference. Should we allow inout ref?
|
|
if( (int)f->parameterTypes.GetLength() == (arg?2:1) )
|
|
{
|
|
if( setId == 0 )
|
|
setId = funcs[n];
|
|
else
|
|
{
|
|
if( multipleSetFuncs.GetLength() == 0 )
|
|
multipleSetFuncs.PushLast(setId);
|
|
|
|
multipleSetFuncs.PushLast(funcs[n]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool isConst = ctx->type.dataType.IsObjectConst();
|
|
|
|
// Check for multiple matches
|
|
if( multipleGetFuncs.GetLength() > 0 )
|
|
{
|
|
// Filter the list by constness
|
|
FilterConst(multipleGetFuncs, !isConst);
|
|
|
|
if( multipleGetFuncs.GetLength() > 1 )
|
|
{
|
|
if (node)
|
|
{
|
|
asCString str;
|
|
str.Format(TXT_MULTIPLE_PROP_GET_ACCESSOR_FOR_s, name.AddressOf());
|
|
Error(str, node);
|
|
|
|
PrintMatchingFuncs(multipleGetFuncs, node);
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
else
|
|
{
|
|
// The id may have changed
|
|
getId = multipleGetFuncs[0];
|
|
}
|
|
}
|
|
|
|
if( multipleSetFuncs.GetLength() > 0 )
|
|
{
|
|
// Filter the list by constness
|
|
FilterConst(multipleSetFuncs, !isConst);
|
|
|
|
if( multipleSetFuncs.GetLength() > 1 )
|
|
{
|
|
if (node)
|
|
{
|
|
asCString str;
|
|
str.Format(TXT_MULTIPLE_PROP_SET_ACCESSOR_FOR_s, name.AddressOf());
|
|
Error(str, node);
|
|
|
|
PrintMatchingFuncs(multipleSetFuncs, node);
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
else
|
|
{
|
|
// The id may have changed
|
|
setId = multipleSetFuncs[0];
|
|
}
|
|
}
|
|
|
|
// Check for type compatibility between get and set accessor
|
|
if( getId && setId )
|
|
{
|
|
asCScriptFunction *getFunc = builder->GetFunctionDescription(getId);
|
|
asCScriptFunction *setFunc = builder->GetFunctionDescription(setId);
|
|
|
|
// It is permitted for a getter to return a handle and the setter to take a reference
|
|
int idx = (arg?1:0);
|
|
if( !getFunc->returnType.IsEqualExceptRefAndConst(setFunc->parameterTypes[idx]) &&
|
|
!((getFunc->returnType.IsObjectHandle() && !setFunc->parameterTypes[idx].IsObjectHandle()) &&
|
|
(getFunc->returnType.GetTypeInfo() == setFunc->parameterTypes[idx].GetTypeInfo())) )
|
|
{
|
|
if (node)
|
|
{
|
|
asCString str;
|
|
str.Format(TXT_GET_SET_ACCESSOR_TYPE_MISMATCH_FOR_s, name.AddressOf());
|
|
Error(str, node);
|
|
|
|
asCArray<int> funcs;
|
|
funcs.PushLast(getId);
|
|
funcs.PushLast(setId);
|
|
|
|
PrintMatchingFuncs(funcs, node);
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
// Check if we are within one of the accessors
|
|
int realGetId = getId;
|
|
int realSetId = setId;
|
|
if( outFunc->objectType && isThisAccess )
|
|
{
|
|
// The property accessors would be virtual functions, so we need to find the real implementation
|
|
asCScriptFunction *getFunc = getId ? builder->GetFunctionDescription(getId) : 0;
|
|
if( getFunc &&
|
|
getFunc->funcType == asFUNC_VIRTUAL &&
|
|
outFunc->objectType->DerivesFrom(getFunc->objectType) )
|
|
realGetId = outFunc->objectType->virtualFunctionTable[getFunc->vfTableIdx]->id;
|
|
asCScriptFunction *setFunc = setId ? builder->GetFunctionDescription(setId) : 0;
|
|
if( setFunc &&
|
|
setFunc->funcType == asFUNC_VIRTUAL &&
|
|
outFunc->objectType->DerivesFrom(setFunc->objectType) )
|
|
realSetId = outFunc->objectType->virtualFunctionTable[setFunc->vfTableIdx]->id;
|
|
}
|
|
|
|
// Avoid recursive call, by not treating this as a property accessor call.
|
|
// This will also allow having the real property with the same name as the accessors.
|
|
if( (isThisAccess || outFunc->objectType == 0) &&
|
|
((realGetId && realGetId == outFunc->id) ||
|
|
(realSetId && realSetId == outFunc->id)) )
|
|
{
|
|
getId = 0;
|
|
setId = 0;
|
|
}
|
|
|
|
// Check if the application has disabled script written property accessors
|
|
if( engine->ep.propertyAccessorMode == 1 )
|
|
{
|
|
if( getId && builder->GetFunctionDescription(getId)->funcType != asFUNC_SYSTEM )
|
|
getId = 0;
|
|
if( setId && builder->GetFunctionDescription(setId)->funcType != asFUNC_SYSTEM )
|
|
setId = 0;
|
|
}
|
|
|
|
if( getId || setId )
|
|
{
|
|
// Property accessors were found, but we don't know which is to be used yet, so
|
|
// we just prepare the bytecode for the method call, and then store the function ids
|
|
// so that the right one can be used when we get there.
|
|
ctx->property_get = getId;
|
|
ctx->property_set = setId;
|
|
|
|
bool isRefSafe = ctx->type.isRefSafe;
|
|
|
|
if( ctx->type.dataType.IsObject() )
|
|
{
|
|
// If the object is read-only then we need to remember that
|
|
if( (!ctx->type.dataType.IsObjectHandle() && ctx->type.dataType.IsReadOnly()) ||
|
|
(ctx->type.dataType.IsObjectHandle() && ctx->type.dataType.IsHandleToConst()) )
|
|
ctx->property_const = true;
|
|
else
|
|
ctx->property_const = false;
|
|
|
|
// If the object is a handle then we need to remember that
|
|
ctx->property_handle = ctx->type.dataType.IsObjectHandle();
|
|
ctx->property_ref = ctx->type.dataType.IsReference();
|
|
}
|
|
|
|
// The setter's parameter type is used as the property type,
|
|
// unless only the getter is available
|
|
asCDataType dt;
|
|
if( setId )
|
|
dt = builder->GetFunctionDescription(setId)->parameterTypes[(arg?1:0)];
|
|
else
|
|
dt = builder->GetFunctionDescription(getId)->returnType;
|
|
|
|
// Just change the type, the context must still maintain information
|
|
// about previous variable offset and the indicator of temporary variable.
|
|
int offset = ctx->type.stackOffset;
|
|
bool isTemp = ctx->type.isTemporary;
|
|
ctx->type.Set(dt);
|
|
ctx->type.stackOffset = (short)offset;
|
|
ctx->type.isTemporary = isTemp;
|
|
ctx->exprNode = node;
|
|
|
|
// Remember if the object is safe, so the invocation of the property
|
|
// accessor doesn't needlessly make a safe copy of the handle
|
|
ctx->type.isRefSafe = isRefSafe;
|
|
|
|
// Store the argument for later use
|
|
if( arg )
|
|
{
|
|
ctx->property_arg = asNEW(asCExprContext)(engine);
|
|
if( ctx->property_arg == 0 )
|
|
{
|
|
// Out of memory
|
|
return -1;
|
|
}
|
|
|
|
MergeExprBytecodeAndType(ctx->property_arg, arg);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
// No accessor was found
|
|
return 0;
|
|
}
|
|
|
|
int asCCompiler::ProcessPropertySetAccessor(asCExprContext *ctx, asCExprContext *arg, asCScriptNode *node)
|
|
{
|
|
// TODO: A lot of this code is similar to ProcessPropertyGetAccessor. Can we unify them?
|
|
|
|
if( !ctx->property_set )
|
|
{
|
|
Error(TXT_PROPERTY_HAS_NO_SET_ACCESSOR, node);
|
|
return -1;
|
|
}
|
|
|
|
asCScriptFunction *func = builder->GetFunctionDescription(ctx->property_set);
|
|
|
|
// Make sure the arg match the property
|
|
asCArray<int> funcs;
|
|
funcs.PushLast(ctx->property_set);
|
|
asCArray<asCExprContext *> args;
|
|
if( ctx->property_arg )
|
|
args.PushLast(ctx->property_arg);
|
|
args.PushLast(arg);
|
|
MatchFunctions(funcs, args, node, func->GetName(), 0, func->objectType, ctx->property_const);
|
|
if( funcs.GetLength() == 0 )
|
|
{
|
|
// MatchFunctions already reported the error
|
|
if( ctx->property_arg )
|
|
{
|
|
asDELETE(ctx->property_arg, asCExprContext);
|
|
ctx->property_arg = 0;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
if( func->objectType )
|
|
{
|
|
// Setup the context with the original type so the method call gets built correctly
|
|
ctx->type.dataType = asCDataType::CreateType(func->objectType, ctx->property_const);
|
|
if( ctx->property_handle ) ctx->type.dataType.MakeHandle(true);
|
|
if( ctx->property_ref ) ctx->type.dataType.MakeReference(true);
|
|
|
|
// Don't allow the call if the object is read-only and the property accessor is not const
|
|
if( ctx->property_const && !func->IsReadOnly() )
|
|
{
|
|
Error(TXT_NON_CONST_METHOD_ON_CONST_OBJ, node);
|
|
asCArray<int> funcCandidates;
|
|
funcCandidates.PushLast(ctx->property_set);
|
|
PrintMatchingFuncs(funcCandidates, node);
|
|
}
|
|
}
|
|
|
|
// Call the accessor
|
|
MakeFunctionCall(ctx, ctx->property_set, func->objectType, args, node);
|
|
|
|
ctx->property_get = 0;
|
|
ctx->property_set = 0;
|
|
if( ctx->property_arg )
|
|
{
|
|
asDELETE(ctx->property_arg, asCExprContext);
|
|
ctx->property_arg = 0;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int asCCompiler::ProcessPropertyGetSetAccessor(asCExprContext *ctx, asCExprContext *lctx, asCExprContext *rctx, eTokenType op, asCScriptNode *errNode)
|
|
{
|
|
// TODO: Perhaps it might be interesting to allow the definition of compound setters for better
|
|
// performance, e.g. set_add_prop, set_mul_prop, etc. With these it would also be possible
|
|
// to support value types, since it would be a single call
|
|
|
|
// Compound assignment for indexed property accessors is not supported yet
|
|
if( lctx->property_arg != 0 )
|
|
{
|
|
// Process the property to free the memory
|
|
ProcessPropertySetAccessor(lctx, rctx, errNode);
|
|
Error(TXT_COMPOUND_ASGN_WITH_IDX_PROP, errNode);
|
|
return -1;
|
|
}
|
|
|
|
// Compound assignments require both get and set accessors
|
|
if( lctx->property_set == 0 || lctx->property_get == 0 )
|
|
{
|
|
// Process the property to free the memory
|
|
ProcessPropertySetAccessor(lctx, rctx, errNode);
|
|
Error(TXT_COMPOUND_ASGN_REQUIRE_GET_SET, errNode);
|
|
return -1;
|
|
}
|
|
|
|
// Property accessors on value types (or scoped references types) are not supported since
|
|
// it is not possible to guarantee that the object will stay alive between the two calls
|
|
asCScriptFunction *func = engine->scriptFunctions[lctx->property_set];
|
|
if( func->objectType && (func->objectType->flags & (asOBJ_VALUE | asOBJ_SCOPED)) )
|
|
{
|
|
// Process the property to free the memory
|
|
ProcessPropertySetAccessor(lctx, rctx, errNode);
|
|
Error(TXT_COMPOUND_ASGN_ON_VALUE_TYPE, errNode);
|
|
return -1;
|
|
}
|
|
|
|
// Translate the compound assignment to the corresponding dual operator
|
|
switch( op )
|
|
{
|
|
case ttAddAssign: op = ttPlus; break;
|
|
case ttSubAssign: op = ttMinus; break;
|
|
case ttMulAssign: op = ttStar; break;
|
|
case ttDivAssign: op = ttSlash; break;
|
|
case ttModAssign: op = ttPercent; break;
|
|
case ttPowAssign: op = ttStarStar; break;
|
|
|
|
case ttAndAssign: op = ttAmp; break;
|
|
case ttOrAssign: op = ttBitOr; break;
|
|
case ttXorAssign: op = ttBitXor; break;
|
|
|
|
case ttShiftLeftAssign: op = ttBitShiftLeft; break;
|
|
case ttShiftRightAAssign: op = ttBitShiftRightArith; break;
|
|
case ttShiftRightLAssign: op = ttBitShiftRight; break;
|
|
|
|
default: op = ttUnrecognizedToken; break;
|
|
}
|
|
|
|
if( op == ttUnrecognizedToken )
|
|
{
|
|
// Shouldn't happen
|
|
asASSERT(false);
|
|
|
|
// Process the property to free the memory
|
|
ProcessPropertySetAccessor(lctx, rctx, errNode);
|
|
return -1;
|
|
}
|
|
|
|
asCExprContext before(engine);
|
|
if( func->objectType && (func->objectType->flags & (asOBJ_REF|asOBJ_SCOPED)) == asOBJ_REF )
|
|
{
|
|
// Keep a reference to the object in a local variable
|
|
before.bc.AddCode(&lctx->bc);
|
|
|
|
asUINT len = reservedVariables.GetLength();
|
|
rctx->bc.GetVarsUsed(reservedVariables);
|
|
before.bc.GetVarsUsed(reservedVariables);
|
|
|
|
asCDataType dt = asCDataType::CreateObjectHandle(func->objectType, false);
|
|
int offset = AllocateVariable(dt, true);
|
|
|
|
reservedVariables.SetLength(len);
|
|
|
|
before.type.SetVariable(dt, offset, true);
|
|
|
|
if( lctx->property_ref )
|
|
before.bc.Instr(asBC_RDSPtr);
|
|
before.bc.InstrSHORT(asBC_PSF, (short)offset);
|
|
before.bc.InstrPTR(asBC_REFCPY, func->objectType);
|
|
before.bc.Instr(asBC_PopPtr);
|
|
|
|
if( lctx->type.isTemporary )
|
|
{
|
|
// Add the release of the temporary variable as a deferred expression
|
|
asSDeferredParam deferred;
|
|
deferred.origExpr = 0;
|
|
deferred.argInOutFlags = asTM_INREF;
|
|
deferred.argNode = 0;
|
|
deferred.argType.SetVariable(ctx->type.dataType, lctx->type.stackOffset, true);
|
|
before.deferredParams.PushLast(deferred);
|
|
}
|
|
|
|
// Update the left expression to use the local variable
|
|
lctx->bc.InstrSHORT(asBC_PSF, (short)offset);
|
|
lctx->type.stackOffset = (short)offset;
|
|
lctx->property_ref = true;
|
|
|
|
// Don't release the temporary variable too early
|
|
lctx->type.isTemporary = false;
|
|
|
|
ctx->bc.AddCode(&before.bc);
|
|
}
|
|
|
|
// Keep the original information on the property
|
|
asCExprContext llctx(engine);
|
|
llctx.type = lctx->type;
|
|
llctx.property_arg = lctx->property_arg;
|
|
llctx.property_const = lctx->property_const;
|
|
llctx.property_get = lctx->property_get;
|
|
llctx.property_handle = lctx->property_handle;
|
|
llctx.property_ref = lctx->property_ref;
|
|
llctx.property_set = lctx->property_set;
|
|
|
|
// Compile the dual operator using the get accessor
|
|
CompileOperator(errNode, lctx, rctx, ctx, op, false);
|
|
|
|
// If we made a local variable to hold the reference it must be reused
|
|
if( before.type.stackOffset )
|
|
llctx.bc.InstrSHORT(asBC_PSF, before.type.stackOffset);
|
|
|
|
// Compile the assignment using the set accessor
|
|
ProcessPropertySetAccessor(&llctx, ctx, errNode);
|
|
|
|
MergeExprBytecodeAndType(ctx, &llctx);
|
|
|
|
if( before.type.stackOffset )
|
|
ReleaseTemporaryVariable(before.type.stackOffset, &ctx->bc);
|
|
|
|
asASSERT( ctx->deferredParams.GetLength() == 0 );
|
|
ctx->deferredParams = before.deferredParams;
|
|
ProcessDeferredParams(ctx);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void asCCompiler::ProcessPropertyGetAccessor(asCExprContext *ctx, asCScriptNode *node)
|
|
{
|
|
// If no property accessor has been prepared then don't do anything
|
|
if( !ctx->property_get && !ctx->property_set )
|
|
return;
|
|
|
|
if( !ctx->property_get )
|
|
{
|
|
// Raise error on missing accessor
|
|
Error(TXT_PROPERTY_HAS_NO_GET_ACCESSOR, node);
|
|
ctx->type.SetDummy();
|
|
return;
|
|
}
|
|
|
|
asCExprValue objType = ctx->type;
|
|
asCScriptFunction *func = builder->GetFunctionDescription(ctx->property_get);
|
|
|
|
// Make sure the arg match the property
|
|
asCArray<int> funcs;
|
|
funcs.PushLast(ctx->property_get);
|
|
asCArray<asCExprContext *> args;
|
|
if( ctx->property_arg )
|
|
args.PushLast(ctx->property_arg);
|
|
MatchFunctions(funcs, args, node, func->GetName(), 0, func->objectType, ctx->property_const);
|
|
if( funcs.GetLength() == 0 )
|
|
{
|
|
// MatchFunctions already reported the error
|
|
if( ctx->property_arg )
|
|
{
|
|
asDELETE(ctx->property_arg, asCExprContext);
|
|
ctx->property_arg = 0;
|
|
}
|
|
ctx->type.SetDummy();
|
|
return;
|
|
}
|
|
|
|
if( func->objectType )
|
|
{
|
|
// Setup the context with the original type so the method call gets built correctly
|
|
ctx->type.dataType = asCDataType::CreateType(func->objectType, ctx->property_const);
|
|
if( ctx->property_handle ) ctx->type.dataType.MakeHandle(true);
|
|
if( ctx->property_ref ) ctx->type.dataType.MakeReference(true);
|
|
|
|
// Don't allow the call if the object is read-only and the property accessor is not const
|
|
if( ctx->property_const && !func->IsReadOnly() )
|
|
{
|
|
Error(TXT_NON_CONST_METHOD_ON_CONST_OBJ, node);
|
|
asCArray<int> funcCandidates;
|
|
funcCandidates.PushLast(ctx->property_get);
|
|
PrintMatchingFuncs(funcCandidates, node);
|
|
}
|
|
}
|
|
|
|
// The explicit handle flag must be remembered
|
|
bool isExplicitHandle = ctx->type.isExplicitHandle;
|
|
|
|
// Call the accessor
|
|
MakeFunctionCall(ctx, ctx->property_get, func->objectType, args, node);
|
|
if( isExplicitHandle )
|
|
ctx->type.isExplicitHandle = true;
|
|
|
|
// Clear the property get/set ids
|
|
ctx->property_get = 0;
|
|
ctx->property_set = 0;
|
|
if( ctx->property_arg )
|
|
{
|
|
asDELETE(ctx->property_arg, asCExprContext);
|
|
ctx->property_arg = 0;
|
|
}
|
|
}
|
|
|
|
int asCCompiler::CompileExpressionPostOp(asCScriptNode *node, asCExprContext *ctx)
|
|
{
|
|
// Don't allow any postfix operators on expressions that take address of class method
|
|
if( ctx->IsClassMethod() )
|
|
{
|
|
Error(TXT_INVALID_OP_ON_METHOD, node);
|
|
return -1;
|
|
}
|
|
|
|
// Don't allow any operators on void expressions
|
|
if( ctx->IsVoidExpression() )
|
|
{
|
|
Error(TXT_VOID_CANT_BE_OPERAND, node);
|
|
return -1;
|
|
}
|
|
|
|
// Check if the variable is initialized (if it indeed is a variable)
|
|
IsVariableInitialized(&ctx->type, node);
|
|
|
|
int op = node->tokenType;
|
|
if( (op == ttInc || op == ttDec) && ctx->type.dataType.IsObject() )
|
|
{
|
|
const char *opName = 0;
|
|
switch( op )
|
|
{
|
|
case ttInc: opName = "opPostInc"; break;
|
|
case ttDec: opName = "opPostDec"; break;
|
|
}
|
|
|
|
if( opName )
|
|
{
|
|
// TODO: Should convert this to something similar to CompileOverloadedDualOperator2
|
|
ProcessPropertyGetAccessor(ctx, node);
|
|
|
|
// 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
|
|
|
|
// Find the correct method
|
|
bool isConst = ctx->type.dataType.IsObjectConst();
|
|
asCArray<int> funcs;
|
|
asCObjectType *ot = CastToObjectType(ctx->type.dataType.GetTypeInfo());
|
|
for( asUINT n = 0; n < ot->methods.GetLength(); n++ )
|
|
{
|
|
asCScriptFunction *func = engine->scriptFunctions[ot->methods[n]];
|
|
if( func->name == opName &&
|
|
func->parameterTypes.GetLength() == 0 &&
|
|
(!isConst || func->IsReadOnly()) )
|
|
{
|
|
funcs.PushLast(func->id);
|
|
}
|
|
}
|
|
|
|
// Did we find the method?
|
|
if( funcs.GetLength() == 1 )
|
|
{
|
|
asCArray<asCExprContext *> args;
|
|
MakeFunctionCall(ctx, funcs[0], CastToObjectType(ctx->type.dataType.GetTypeInfo()), args, node);
|
|
return 0;
|
|
}
|
|
else if( funcs.GetLength() == 0 )
|
|
{
|
|
asCString str;
|
|
str = asCString(opName) + "()";
|
|
if( isConst )
|
|
str += " const";
|
|
str.Format(TXT_FUNCTION_s_NOT_FOUND, str.AddressOf());
|
|
Error(str, node);
|
|
ctx->type.SetDummy();
|
|
return -1;
|
|
}
|
|
else if( funcs.GetLength() > 1 )
|
|
{
|
|
Error(TXT_MORE_THAN_ONE_MATCHING_OP, node);
|
|
PrintMatchingFuncs(funcs, node);
|
|
|
|
ctx->type.SetDummy();
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
else if( op == ttInc || op == ttDec )
|
|
{
|
|
// Make sure the reference isn't a temporary variable
|
|
if( ctx->type.isTemporary )
|
|
{
|
|
Error(TXT_REF_IS_TEMP, node);
|
|
return -1;
|
|
}
|
|
if( ctx->type.dataType.IsReadOnly() )
|
|
{
|
|
Error(TXT_REF_IS_READ_ONLY, node);
|
|
return -1;
|
|
}
|
|
if( ctx->property_get || ctx->property_set )
|
|
{
|
|
Error(TXT_INVALID_REF_PROP_ACCESS, node);
|
|
return -1;
|
|
}
|
|
if( !ctx->type.isLValue )
|
|
{
|
|
Error(TXT_NOT_LVALUE, node);
|
|
return -1;
|
|
}
|
|
|
|
if( ctx->type.isVariable && !ctx->type.dataType.IsReference() )
|
|
ConvertToReference(ctx);
|
|
else if( !ctx->type.dataType.IsReference() )
|
|
{
|
|
Error(TXT_NOT_VALID_REFERENCE, node);
|
|
return -1;
|
|
}
|
|
|
|
// Copy the value to a temp before changing it
|
|
ConvertToTempVariable(ctx);
|
|
asASSERT(!ctx->type.isLValue);
|
|
|
|
// Increment the value pointed to by the reference still in the register
|
|
asEBCInstr iInc = asBC_INCi, iDec = asBC_DECi;
|
|
if( ctx->type.dataType.IsDoubleType() )
|
|
{
|
|
iInc = asBC_INCd;
|
|
iDec = asBC_DECd;
|
|
}
|
|
else if( ctx->type.dataType.IsFloatType() )
|
|
{
|
|
iInc = asBC_INCf;
|
|
iDec = asBC_DECf;
|
|
}
|
|
else if( ctx->type.dataType.IsIntegerType() || ctx->type.dataType.IsUnsignedType() )
|
|
{
|
|
if( ctx->type.dataType.IsEqualExceptRef(asCDataType::CreatePrimitive(ttInt16, false)) ||
|
|
ctx->type.dataType.IsEqualExceptRef(asCDataType::CreatePrimitive(ttUInt16, false)) )
|
|
{
|
|
iInc = asBC_INCi16;
|
|
iDec = asBC_DECi16;
|
|
}
|
|
else if( ctx->type.dataType.IsEqualExceptRef(asCDataType::CreatePrimitive(ttInt8, false)) ||
|
|
ctx->type.dataType.IsEqualExceptRef(asCDataType::CreatePrimitive(ttUInt8, false)) )
|
|
{
|
|
iInc = asBC_INCi8;
|
|
iDec = asBC_DECi8;
|
|
}
|
|
else if( ctx->type.dataType.IsEqualExceptRef(asCDataType::CreatePrimitive(ttInt64, false)) ||
|
|
ctx->type.dataType.IsEqualExceptRef(asCDataType::CreatePrimitive(ttUInt64, false)) )
|
|
{
|
|
iInc = asBC_INCi64;
|
|
iDec = asBC_DECi64;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Error(TXT_ILLEGAL_OPERATION, node);
|
|
return -1;
|
|
}
|
|
|
|
if( op == ttInc ) ctx->bc.Instr(iInc); else ctx->bc.Instr(iDec);
|
|
}
|
|
else if( op == ttDot )
|
|
{
|
|
if( node->firstChild->nodeType == snIdentifier )
|
|
{
|
|
ProcessPropertyGetAccessor(ctx, node);
|
|
|
|
// Get the property name
|
|
asCString name(&script->code[node->firstChild->tokenPos], node->firstChild->tokenLength);
|
|
|
|
if( ctx->type.dataType.IsObject() )
|
|
{
|
|
// We need to look for get/set property accessors.
|
|
// If found, the context stores information on the get/set accessors
|
|
// until it is known which is to be used.
|
|
int r = 0;
|
|
if( node->next && node->next->tokenType == ttOpenBracket )
|
|
{
|
|
// The property accessor should take an index arg
|
|
asCExprContext dummyArg(engine);
|
|
r = FindPropertyAccessor(name, ctx, &dummyArg, node, 0);
|
|
}
|
|
if( r == 0 )
|
|
r = FindPropertyAccessor(name, ctx, node, 0);
|
|
if( r != 0 )
|
|
return r;
|
|
|
|
if( !ctx->type.dataType.IsPrimitive() )
|
|
Dereference(ctx, true);
|
|
|
|
if( ctx->type.dataType.IsObjectHandle() )
|
|
{
|
|
// Convert the handle to a normal object
|
|
asCDataType dt = ctx->type.dataType;
|
|
dt.MakeHandle(false);
|
|
|
|
ImplicitConversion(ctx, dt, node, asIC_IMPLICIT_CONV);
|
|
|
|
// The handle may not have been an lvalue, but the dereferenced object is
|
|
ctx->type.isLValue = true;
|
|
}
|
|
|
|
bool isConst = ctx->type.dataType.IsObjectConst();
|
|
|
|
asCObjectProperty *prop = builder->GetObjectProperty(ctx->type.dataType, name.AddressOf());
|
|
if( prop )
|
|
{
|
|
// Is the property access allowed?
|
|
if( (prop->isPrivate || prop->isProtected) && (!outFunc || outFunc->objectType != ctx->type.dataType.GetTypeInfo()) )
|
|
{
|
|
asCString msg;
|
|
if( prop->isPrivate )
|
|
msg.Format(TXT_PRIVATE_PROP_ACCESS_s, name.AddressOf());
|
|
else
|
|
msg.Format(TXT_PROTECTED_PROP_ACCESS_s, name.AddressOf());
|
|
Error(msg, node);
|
|
}
|
|
|
|
// Adjust the pointer for composite member
|
|
// This must always be done even if the offset is 0 because the asCWriter needs the meta data in ADDSi to identify the composite property
|
|
if( prop->compositeOffset || prop->isCompositeIndirect )
|
|
ctx->bc.InstrSHORT_DW(asBC_ADDSi, (short)prop->compositeOffset, engine->GetTypeIdFromDataType(asCDataType::CreateType(ctx->type.dataType.GetTypeInfo(), false)));
|
|
if (prop->isCompositeIndirect)
|
|
ctx->bc.Instr(asBC_RDSPtr);
|
|
|
|
// Put the offset on the stack
|
|
// This must always be done even if the offset is 0 so the type info is stored
|
|
ctx->bc.InstrSHORT_DW(asBC_ADDSi, (short)prop->byteOffset, engine->GetTypeIdFromDataType(asCDataType::CreateType(ctx->type.dataType.GetTypeInfo(), false)));
|
|
|
|
if( prop->type.IsReference() )
|
|
ctx->bc.Instr(asBC_RDSPtr);
|
|
|
|
// Reference to primitive must be stored in the temp register
|
|
if( prop->type.IsPrimitive() )
|
|
{
|
|
ctx->bc.Instr(asBC_PopRPtr);
|
|
}
|
|
|
|
// Keep information about temporary variables as deferred expression
|
|
if( ctx->type.isTemporary )
|
|
{
|
|
// Add the release of this reference, as a deferred expression
|
|
asSDeferredParam deferred;
|
|
deferred.origExpr = 0;
|
|
deferred.argInOutFlags = asTM_INREF;
|
|
deferred.argNode = 0;
|
|
deferred.argType.SetVariable(ctx->type.dataType, ctx->type.stackOffset, true);
|
|
|
|
ctx->deferredParams.PushLast(deferred);
|
|
}
|
|
|
|
// Set the new type and make sure it is not treated as a variable anymore
|
|
ctx->type.dataType = prop->type;
|
|
ctx->type.dataType.MakeReference(true);
|
|
ctx->type.isVariable = false;
|
|
ctx->type.isTemporary = false;
|
|
|
|
if( (ctx->type.dataType.IsObject() || ctx->type.dataType.IsFuncdef()) && !ctx->type.dataType.IsObjectHandle() )
|
|
{
|
|
// Objects that are members are not references
|
|
ctx->type.dataType.MakeReference(false);
|
|
|
|
// The object is safe (life time guaranteed) if the parent object is also safe
|
|
}
|
|
else if (ctx->type.dataType.IsObjectHandle())
|
|
{
|
|
// A object accessed through a handle cannot be considered safe,
|
|
// as it can be cleared at any time
|
|
ctx->type.isRefSafe = false;
|
|
}
|
|
|
|
ctx->type.dataType.MakeReadOnly(isConst ? true : prop->type.IsReadOnly());
|
|
}
|
|
else
|
|
{
|
|
// If the name is not a property, the compiler must check if the name matches
|
|
// a method, which can be used for constructing delegates
|
|
asIScriptFunction *func = 0;
|
|
asCObjectType *ot = CastToObjectType(ctx->type.dataType.GetTypeInfo());
|
|
for( asUINT n = 0; n < ot->methods.GetLength(); n++ )
|
|
{
|
|
if( engine->scriptFunctions[ot->methods[n]]->name == name )
|
|
{
|
|
func = engine->scriptFunctions[ot->methods[n]];
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( func )
|
|
{
|
|
// An object method was found. Keep the name of the method in the expression, but
|
|
// don't actually modify the bytecode at this point since it is not yet known what
|
|
// the method will be used for, or even what overloaded method should be used.
|
|
ctx->methodName = name;
|
|
}
|
|
else
|
|
{
|
|
asCString str;
|
|
str.Format(TXT_s_NOT_MEMBER_OF_s, name.AddressOf(), ctx->type.dataType.Format(outFunc->nameSpace).AddressOf());
|
|
Error(str, node);
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
asCString str;
|
|
str.Format(TXT_s_NOT_MEMBER_OF_s, name.AddressOf(), ctx->type.dataType.Format(outFunc->nameSpace).AddressOf());
|
|
Error(str, node);
|
|
return -1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Make sure it is an object we are accessing
|
|
if( !ctx->type.dataType.IsObject() )
|
|
{
|
|
asCString str;
|
|
str.Format(TXT_ILLEGAL_OPERATION_ON_s, ctx->type.dataType.Format(outFunc->nameSpace).AddressOf());
|
|
Error(str, node);
|
|
return -1;
|
|
}
|
|
|
|
// Process the get property accessor
|
|
ProcessPropertyGetAccessor(ctx, node);
|
|
|
|
// Compile function call
|
|
int r = CompileFunctionCall(node->firstChild, ctx, CastToObjectType(ctx->type.dataType.GetTypeInfo()), ctx->type.dataType.IsObjectConst());
|
|
if( r < 0 ) return r;
|
|
}
|
|
}
|
|
else if( op == ttOpenBracket )
|
|
{
|
|
// If the property access takes an index arg and the argument hasn't been evaluated yet,
|
|
// then we should use that instead of processing it now. If the argument has already been
|
|
// evaluated, then we should process the property accessor as a get access now as the new
|
|
// index operator is on the result of that accessor.
|
|
asCString propertyName;
|
|
asSNameSpace *ns = 0;
|
|
if( ((ctx->property_get && builder->GetFunctionDescription(ctx->property_get)->GetParamCount() == 1) ||
|
|
(ctx->property_set && builder->GetFunctionDescription(ctx->property_set)->GetParamCount() == 2)) &&
|
|
(ctx->property_arg && ctx->property_arg->type.dataType.GetTokenType() == ttUnrecognizedToken) )
|
|
{
|
|
// Determine the name of the property accessor
|
|
asCScriptFunction *func = 0;
|
|
if( ctx->property_get )
|
|
func = builder->GetFunctionDescription(ctx->property_get);
|
|
else
|
|
func = builder->GetFunctionDescription(ctx->property_set);
|
|
propertyName = func->GetName();
|
|
propertyName = propertyName.SubString(4);
|
|
|
|
// Set the original type of the expression so we can re-evaluate the property accessor
|
|
if( func->objectType )
|
|
{
|
|
ctx->type.dataType = asCDataType::CreateType(func->objectType, ctx->property_const);
|
|
if( ctx->property_handle ) ctx->type.dataType.MakeHandle(true);
|
|
if( ctx->property_ref ) ctx->type.dataType.MakeReference(true);
|
|
}
|
|
else
|
|
{
|
|
// Store the namespace where the function is declared
|
|
// so the same function can be found later
|
|
ctx->type.SetDummy();
|
|
ns = func->nameSpace;
|
|
}
|
|
|
|
ctx->property_get = ctx->property_set = 0;
|
|
if( ctx->property_arg )
|
|
{
|
|
asDELETE(ctx->property_arg, asCExprContext);
|
|
ctx->property_arg = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( !ctx->type.dataType.IsObject() )
|
|
{
|
|
asCString str;
|
|
str.Format(TXT_OBJECT_DOESNT_SUPPORT_INDEX_OP, ctx->type.dataType.Format(outFunc->nameSpace).AddressOf());
|
|
Error(str, node);
|
|
return -1;
|
|
}
|
|
|
|
ProcessPropertyGetAccessor(ctx, node);
|
|
}
|
|
|
|
// Compile the expression
|
|
bool isOK = true;
|
|
asCArray<asCExprContext *> args;
|
|
asCArray<asSNamedArgument> namedArgs;
|
|
asASSERT( node->firstChild->nodeType == snArgList );
|
|
if( CompileArgumentList(node->firstChild, args, namedArgs) >= 0 )
|
|
{
|
|
// Check for the existence of the opIndex method
|
|
bool lookForProperty = true;
|
|
if( propertyName == "" )
|
|
{
|
|
bool isConst = ctx->type.dataType.IsObjectConst();
|
|
asCObjectType *objectType = CastToObjectType(ctx->type.dataType.GetTypeInfo());
|
|
|
|
asCArray<int> funcs;
|
|
builder->GetObjectMethodDescriptions("opIndex", objectType, funcs, isConst);
|
|
if( funcs.GetLength() > 0 )
|
|
{
|
|
// Since there are opIndex methods, the compiler should not look for get/set_opIndex accessors
|
|
lookForProperty = false;
|
|
|
|
// Determine which of opIndex methods that match
|
|
MatchFunctions(funcs, args, node, "opIndex", 0, objectType, isConst);
|
|
if( funcs.GetLength() != 1 )
|
|
{
|
|
// The error has already been reported by MatchFunctions
|
|
isOK = false;
|
|
}
|
|
else
|
|
{
|
|
// Add the default values for arguments not explicitly supplied
|
|
int r = CompileDefaultAndNamedArgs(node, args, funcs[0], objectType);
|
|
|
|
if( r == 0 )
|
|
MakeFunctionCall(ctx, funcs[0], objectType, args, node, false, 0, ctx->type.stackOffset);
|
|
else
|
|
isOK = false;
|
|
}
|
|
}
|
|
}
|
|
if( lookForProperty && isOK )
|
|
{
|
|
if( args.GetLength() != 1 )
|
|
{
|
|
// TODO: opIndex: Implement support for multiple index arguments in set_opIndex too
|
|
Error(TXT_PROP_ACCESS_WITH_INDEX_ONE_ARG, node);
|
|
isOK = false;
|
|
}
|
|
else
|
|
{
|
|
Dereference(ctx, true);
|
|
asCExprContext lctx(engine);
|
|
MergeExprBytecodeAndType(&lctx, ctx);
|
|
|
|
// Check for accessors methods for the opIndex, either as get/set_opIndex or as get/set with the property name
|
|
int r = FindPropertyAccessor(propertyName == "" ? "opIndex" : propertyName.AddressOf(), &lctx, args[0], node, ns);
|
|
if (r == 0)
|
|
{
|
|
asCString str;
|
|
str.Format(TXT_OBJECT_DOESNT_SUPPORT_INDEX_OP, ctx->type.dataType.Format(outFunc->nameSpace).AddressOf());
|
|
Error(str, node);
|
|
isOK = false;
|
|
}
|
|
else if (r < 0)
|
|
isOK = false;
|
|
|
|
if (isOK)
|
|
MergeExprBytecodeAndType(ctx, &lctx);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
isOK = false;
|
|
|
|
// Cleanup
|
|
for( asUINT n = 0; n < args.GetLength(); n++ )
|
|
if( args[n] )
|
|
{
|
|
asDELETE(args[n], asCExprContext);
|
|
}
|
|
|
|
if( !isOK )
|
|
return -1;
|
|
}
|
|
else if( op == ttOpenParanthesis )
|
|
{
|
|
// TODO: Most of this is already done by CompileFunctionCall(). Can we share the code?
|
|
|
|
// Make sure the expression is a funcdef or an object that may have opCall methods
|
|
if( !ctx->type.dataType.GetTypeInfo() || (!ctx->type.dataType.IsFuncdef() && !ctx->type.dataType.IsObject()) )
|
|
{
|
|
Error(TXT_EXPR_DOESNT_EVAL_TO_FUNC, node);
|
|
return -1;
|
|
}
|
|
|
|
// Compile arguments
|
|
asCArray<asCExprContext *> args;
|
|
asCArray<asSNamedArgument> namedArgs;
|
|
if( CompileArgumentList(node->lastChild, args, namedArgs) >= 0 )
|
|
{
|
|
// Match arguments with the funcdef
|
|
asCArray<int> funcs;
|
|
if( ctx->type.dataType.IsFuncdef() )
|
|
{
|
|
funcs.PushLast(CastToFuncdefType(ctx->type.dataType.GetTypeInfo())->funcdef->id);
|
|
MatchFunctions(funcs, args, node, ctx->type.dataType.GetTypeInfo()->name.AddressOf(), &namedArgs);
|
|
}
|
|
else
|
|
{
|
|
bool isConst = ctx->type.dataType.IsObjectConst();
|
|
|
|
builder->GetObjectMethodDescriptions("opCall", CastToObjectType(ctx->type.dataType.GetTypeInfo()), funcs, isConst);
|
|
MatchFunctions(funcs, args, node, "opCall", &namedArgs, CastToObjectType(ctx->type.dataType.GetTypeInfo()), isConst);
|
|
}
|
|
|
|
if( funcs.GetLength() != 1 )
|
|
{
|
|
// The error was reported by MatchFunctions()
|
|
|
|
// Dummy value
|
|
ctx->type.SetDummy();
|
|
}
|
|
else
|
|
{
|
|
// Add the default values for arguments not explicitly supplied
|
|
int r = CompileDefaultAndNamedArgs(node, args, funcs[0], CastToObjectType(ctx->type.dataType.GetTypeInfo()), &namedArgs);
|
|
|
|
// TODO: funcdef: Do we have to make sure the handle is stored in a temporary variable, or
|
|
// is it enough to make sure it is in a local variable?
|
|
|
|
// For function pointer we must guarantee that the function is safe, i.e.
|
|
// by first storing the function pointer in a local variable (if it isn't already in one)
|
|
if( r == asSUCCESS )
|
|
{
|
|
Dereference(ctx, true);
|
|
if( ctx->type.dataType.IsFuncdef() )
|
|
{
|
|
if( !ctx->type.isVariable )
|
|
ConvertToVariable(ctx);
|
|
|
|
// Remove the reference from the stack as the asBC_CALLPTR instruction takes the variable as argument
|
|
ctx->bc.Instr(asBC_PopPtr);
|
|
}
|
|
|
|
MakeFunctionCall(ctx, funcs[0], ctx->type.dataType.IsFuncdef() ? 0 : CastToObjectType(ctx->type.dataType.GetTypeInfo()), args, node, false, 0, ctx->type.stackOffset);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
ctx->type.SetDummy();
|
|
|
|
// Cleanup
|
|
for( asUINT n = 0; n < args.GetLength(); n++ )
|
|
if( args[n] )
|
|
{
|
|
asDELETE(args[n], asCExprContext);
|
|
}
|
|
for( asUINT n = 0; n < namedArgs.GetLength(); n++ )
|
|
if( namedArgs[n].ctx )
|
|
{
|
|
asDELETE(namedArgs[n].ctx, asCExprContext);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int asCCompiler::GetPrecedence(asCScriptNode *op)
|
|
{
|
|
// x ** y
|
|
// x * y, x / y, x % y
|
|
// x + y, x - y
|
|
// x <= y, x < y, x >= y, x > y
|
|
// x = =y, x != y, x xor y, x is y, x !is y
|
|
// x and y
|
|
// x or y
|
|
|
|
// The following are not used in this function,
|
|
// but should have lower precedence than the above
|
|
// x ? y : z
|
|
// x = y
|
|
|
|
// The expression term have the highest precedence
|
|
if( op->nodeType == snExprTerm )
|
|
return 1;
|
|
|
|
// Evaluate operators by token
|
|
int tokenType = op->tokenType;
|
|
if( tokenType == ttStarStar )
|
|
return 0;
|
|
|
|
if( tokenType == ttStar || tokenType == ttSlash || tokenType == ttPercent )
|
|
return -1;
|
|
|
|
if( tokenType == ttPlus || tokenType == ttMinus )
|
|
return -2;
|
|
|
|
if( tokenType == ttBitShiftLeft ||
|
|
tokenType == ttBitShiftRight ||
|
|
tokenType == ttBitShiftRightArith )
|
|
return -3;
|
|
|
|
if( tokenType == ttAmp )
|
|
return -4;
|
|
|
|
if( tokenType == ttBitXor )
|
|
return -5;
|
|
|
|
if( tokenType == ttBitOr )
|
|
return -6;
|
|
|
|
if( tokenType == ttLessThanOrEqual ||
|
|
tokenType == ttLessThan ||
|
|
tokenType == ttGreaterThanOrEqual ||
|
|
tokenType == ttGreaterThan )
|
|
return -7;
|
|
|
|
if( tokenType == ttEqual || tokenType == ttNotEqual || tokenType == ttXor || tokenType == ttIs || tokenType == ttNotIs )
|
|
return -8;
|
|
|
|
if( tokenType == ttAnd )
|
|
return -9;
|
|
|
|
if( tokenType == ttOr )
|
|
return -10;
|
|
|
|
// Unknown operator
|
|
asASSERT(false);
|
|
|
|
return 0;
|
|
}
|
|
|
|
asUINT asCCompiler::MatchArgument(asCArray<int> &funcs, asCArray<asSOverloadCandidate> &matches, const asCExprContext *argExpr, int paramNum, bool allowObjectConstruct)
|
|
{
|
|
matches.SetLength(0);
|
|
|
|
for( asUINT n = 0; n < funcs.GetLength(); n++ )
|
|
{
|
|
asCScriptFunction *desc = builder->GetFunctionDescription(funcs[n]);
|
|
|
|
// Does the function have arguments enough?
|
|
if( (int)desc->parameterTypes.GetLength() <= paramNum )
|
|
continue;
|
|
|
|
int cost = MatchArgument(desc, argExpr, paramNum, allowObjectConstruct);
|
|
if( cost != -1 )
|
|
matches.PushLast(asSOverloadCandidate(funcs[n], asUINT(cost)));
|
|
}
|
|
|
|
return (asUINT)matches.GetLength();
|
|
}
|
|
|
|
int asCCompiler::MatchArgument(asCScriptFunction *desc, const asCExprContext *argExpr, int paramNum, bool allowObjectConstruct)
|
|
{
|
|
// void expressions can match any out parameter, but nothing else
|
|
if( argExpr->IsVoidExpression() )
|
|
{
|
|
if( desc->inOutFlags[paramNum] == asTM_OUTREF )
|
|
return 0;
|
|
return -1;
|
|
}
|
|
|
|
// Anonymous init lists can only match parameters that can be initialized with a list
|
|
if (argExpr->IsAnonymousInitList())
|
|
{
|
|
if ((desc->parameterTypes[paramNum].IsReference() && desc->inOutFlags[paramNum] != asTM_INREF) ||
|
|
desc->parameterTypes[paramNum].GetBehaviour() == 0 ||
|
|
desc->parameterTypes[paramNum].GetBehaviour()->listFactory == 0)
|
|
{
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// Can we make the match by implicit conversion?
|
|
asCExprContext ti(engine);
|
|
ti.type = argExpr->type;
|
|
ti.methodName = argExpr->methodName;
|
|
ti.enumValue = argExpr->enumValue;
|
|
ti.exprNode = argExpr->exprNode;
|
|
if( argExpr->type.dataType.IsPrimitive() )
|
|
ti.type.dataType.MakeReference(false);
|
|
|
|
// Don't allow the implicit conversion to make a copy in case the argument is expecting a reference to the true value
|
|
if (desc->parameterTypes[paramNum].IsReference() && desc->inOutFlags[paramNum] == asTM_INOUTREF)
|
|
allowObjectConstruct = false;
|
|
|
|
int cost = ImplicitConversion(&ti, desc->parameterTypes[paramNum], 0, asIC_IMPLICIT_CONV, false, allowObjectConstruct);
|
|
|
|
// If the function parameter is an inout-reference then it must not be possible to call the
|
|
// function with an incorrect argument type, even though the type can normally be converted.
|
|
if( desc->parameterTypes[paramNum].IsReference() &&
|
|
desc->inOutFlags[paramNum] == asTM_INOUTREF &&
|
|
desc->parameterTypes[paramNum].GetTokenType() != ttQuestion )
|
|
{
|
|
// Observe, that the below checks are only necessary for when unsafe references have been
|
|
// enabled by the application. Without this the &inout reference form wouldn't be allowed
|
|
// for these value types.
|
|
|
|
// Don't allow a primitive to be converted to a reference of another primitive type
|
|
if( desc->parameterTypes[paramNum].IsPrimitive() &&
|
|
desc->parameterTypes[paramNum].GetTokenType() != argExpr->type.dataType.GetTokenType() )
|
|
{
|
|
asASSERT( engine->ep.allowUnsafeReferences );
|
|
return -1;
|
|
}
|
|
|
|
// Don't allow an enum to be converted to a reference of another enum type
|
|
if( desc->parameterTypes[paramNum].IsEnumType() &&
|
|
desc->parameterTypes[paramNum].GetTypeInfo() != argExpr->type.dataType.GetTypeInfo() )
|
|
{
|
|
asASSERT( engine->ep.allowUnsafeReferences );
|
|
return -1;
|
|
}
|
|
|
|
// Don't allow a non-handle expression to be converted to a reference to a handle
|
|
if( desc->parameterTypes[paramNum].IsObjectHandle() &&
|
|
!argExpr->type.dataType.IsObjectHandle() )
|
|
{
|
|
asASSERT( engine->ep.allowUnsafeReferences );
|
|
return -1;
|
|
}
|
|
|
|
// Don't allow a value type to be converted
|
|
if( (desc->parameterTypes[paramNum].GetTypeInfo() && (desc->parameterTypes[paramNum].GetTypeInfo()->GetFlags() & asOBJ_VALUE)) &&
|
|
(desc->parameterTypes[paramNum].GetTypeInfo() != argExpr->type.dataType.GetTypeInfo()) )
|
|
{
|
|
asASSERT( engine->ep.allowUnsafeReferences );
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
// How well does the argument match the function parameter?
|
|
if( desc->parameterTypes[paramNum].IsEqualExceptRef(ti.type.dataType) )
|
|
return cost;
|
|
|
|
// No match is available
|
|
return -1;
|
|
}
|
|
|
|
int asCCompiler::PrepareArgument2(asCExprContext *ctx, asCExprContext *arg, asCDataType *paramType, bool isFunction, int refType, bool isMakingCopy)
|
|
{
|
|
// Reference parameters whose value won't be used don't evaluate the expression
|
|
// Clean arguments (i.e. default value) will be passed in directly as there is nothing to protect
|
|
if( paramType->IsReference() && !(refType & asTM_INREF) && !arg->isCleanArg )
|
|
{
|
|
// Store the original bytecode so that it can be reused when processing the deferred output parameter
|
|
asCExprContext *orig = asNEW(asCExprContext)(engine);
|
|
if( orig == 0 )
|
|
{
|
|
// Out of memory
|
|
return -1;
|
|
}
|
|
MergeExprBytecodeAndType(orig, arg);
|
|
arg->origExpr = orig;
|
|
}
|
|
|
|
int r = PrepareArgument(paramType, arg, arg->exprNode, isFunction, refType, isMakingCopy);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
// arg still holds the original expression for output parameters
|
|
ctx->bc.AddCode(&arg->bc);
|
|
|
|
return 0;
|
|
}
|
|
|
|
bool asCCompiler::CompileOverloadedDualOperator(asCScriptNode *node, asCExprContext *lctx, asCExprContext *rctx, bool leftToRight, asCExprContext *ctx, bool isHandle, eTokenType token)
|
|
{
|
|
DetermineSingleFunc(lctx, node);
|
|
DetermineSingleFunc(rctx, node);
|
|
|
|
ctx->exprNode = node;
|
|
|
|
// What type of operator is it?
|
|
if( token == ttUnrecognizedToken )
|
|
token = node->tokenType;
|
|
if( token == ttUnrecognizedToken )
|
|
{
|
|
// This happens when the compiler is inferring an assignment
|
|
// operation from another action, for example in preparing a value
|
|
// as a function argument
|
|
token = ttAssignment;
|
|
}
|
|
|
|
// boolean operators are not overloadable
|
|
if( token == ttAnd ||
|
|
token == ttOr ||
|
|
token == ttXor )
|
|
return false;
|
|
|
|
// Dual operators can also be implemented as class methods
|
|
if( token == ttEqual ||
|
|
token == ttNotEqual )
|
|
{
|
|
// TODO: Should evaluate which of the two have the best match. If both have equal match, the first version should be used
|
|
// Find the matching opEquals method
|
|
int r = CompileOverloadedDualOperator2(node, "opEquals", lctx, rctx, leftToRight, ctx, true, asCDataType::CreatePrimitive(ttBool, false));
|
|
if( r == 0 )
|
|
{
|
|
// Try again by switching the order of the operands
|
|
r = CompileOverloadedDualOperator2(node, "opEquals", rctx, lctx, !leftToRight, ctx, true, asCDataType::CreatePrimitive(ttBool, false));
|
|
}
|
|
|
|
if( r == 1 )
|
|
{
|
|
if( token == ttNotEqual )
|
|
ctx->bc.InstrSHORT(asBC_NOT, ctx->type.stackOffset);
|
|
|
|
// Success, don't continue
|
|
return true;
|
|
}
|
|
else if( r < 0 )
|
|
{
|
|
// Compiler error, don't continue
|
|
ctx->type.SetConstantDW(asCDataType::CreatePrimitive(ttBool, true), true);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if( token == ttEqual ||
|
|
token == ttNotEqual ||
|
|
token == ttLessThan ||
|
|
token == ttLessThanOrEqual ||
|
|
token == ttGreaterThan ||
|
|
token == ttGreaterThanOrEqual )
|
|
{
|
|
bool swappedOrder = false;
|
|
|
|
// TODO: Should evaluate which of the two have the best match. If both have equal match, the first version should be used
|
|
// Find the matching opCmp method
|
|
int r = CompileOverloadedDualOperator2(node, "opCmp", lctx, rctx, leftToRight, ctx, true, asCDataType::CreatePrimitive(ttInt, false));
|
|
if( r == 0 )
|
|
{
|
|
// Try again by switching the order of the operands
|
|
swappedOrder = true;
|
|
r = CompileOverloadedDualOperator2(node, "opCmp", rctx, lctx, !leftToRight, ctx, true, asCDataType::CreatePrimitive(ttInt, false));
|
|
}
|
|
|
|
if( r == 1 )
|
|
{
|
|
ReleaseTemporaryVariable(ctx->type, &ctx->bc);
|
|
|
|
int a = AllocateVariable(asCDataType::CreatePrimitive(ttBool, false), true);
|
|
|
|
ctx->bc.InstrW_DW(asBC_CMPIi, ctx->type.stackOffset, 0);
|
|
|
|
if( token == ttEqual )
|
|
ctx->bc.Instr(asBC_TZ);
|
|
else if( token == ttNotEqual )
|
|
ctx->bc.Instr(asBC_TNZ);
|
|
else if( (token == ttLessThan && !swappedOrder) ||
|
|
(token == ttGreaterThan && swappedOrder) )
|
|
ctx->bc.Instr(asBC_TS);
|
|
else if( (token == ttLessThanOrEqual && !swappedOrder) ||
|
|
(token == ttGreaterThanOrEqual && swappedOrder) )
|
|
ctx->bc.Instr(asBC_TNP);
|
|
else if( (token == ttGreaterThan && !swappedOrder) ||
|
|
(token == ttLessThan && swappedOrder) )
|
|
ctx->bc.Instr(asBC_TP);
|
|
else if( (token == ttGreaterThanOrEqual && !swappedOrder) ||
|
|
(token == ttLessThanOrEqual && swappedOrder) )
|
|
ctx->bc.Instr(asBC_TNS);
|
|
|
|
ctx->bc.InstrSHORT(asBC_CpyRtoV4, (short)a);
|
|
|
|
ctx->type.SetVariable(asCDataType::CreatePrimitive(ttBool, false), a, true);
|
|
|
|
// Success, don't continue
|
|
return true;
|
|
}
|
|
else if( r < 0 )
|
|
{
|
|
// Compiler error, don't continue
|
|
#if AS_SIZEOF_BOOL == 1
|
|
ctx->type.SetConstantB(asCDataType::CreatePrimitive(ttBool, true), true);
|
|
#else
|
|
ctx->type.SetConstantDW(asCDataType::CreatePrimitive(ttBool, true), true);
|
|
#endif
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// The rest of the operators are not commutative, and doesn't require specific return type
|
|
const char *op = 0, *op_r = 0;
|
|
switch( int(token) ) // convert to int to avoid warning in gnuc that not all values are tested
|
|
{
|
|
case ttPlus: op = "opAdd"; op_r = "opAdd_r"; break;
|
|
case ttMinus: op = "opSub"; op_r = "opSub_r"; break;
|
|
case ttStar: op = "opMul"; op_r = "opMul_r"; break;
|
|
case ttSlash: op = "opDiv"; op_r = "opDiv_r"; break;
|
|
case ttPercent: op = "opMod"; op_r = "opMod_r"; break;
|
|
case ttStarStar: op = "opPow"; op_r = "opPow_r"; break;
|
|
case ttBitOr: op = "opOr"; op_r = "opOr_r"; break;
|
|
case ttAmp: op = "opAnd"; op_r = "opAnd_r"; break;
|
|
case ttBitXor: op = "opXor"; op_r = "opXor_r"; break;
|
|
case ttBitShiftLeft: op = "opShl"; op_r = "opShl_r"; break;
|
|
case ttBitShiftRight: op = "opShr"; op_r = "opShr_r"; break;
|
|
case ttBitShiftRightArith: op = "opUShr"; op_r = "opUShr_r"; break;
|
|
}
|
|
|
|
// TODO: Might be interesting to support a concatenation operator, e.g. ~
|
|
|
|
if( op && op_r )
|
|
{
|
|
// TODO: Should evaluate which of the two have the best match. If both have equal match, the first version should be used
|
|
// Find the matching operator method
|
|
int r = CompileOverloadedDualOperator2(node, op, lctx, rctx, leftToRight, ctx);
|
|
if( r == 0 )
|
|
{
|
|
// Try again by switching the order of the operands, and using the reversed operator
|
|
r = CompileOverloadedDualOperator2(node, op_r, rctx, lctx, !leftToRight, ctx);
|
|
}
|
|
|
|
if( r == 1 )
|
|
{
|
|
// Success, don't continue
|
|
return true;
|
|
}
|
|
else if( r < 0 )
|
|
{
|
|
// Compiler error, don't continue
|
|
ctx->type.SetDummy();
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// Assignment operators
|
|
op = 0;
|
|
if( isHandle )
|
|
{
|
|
// Only asOBJ_ASHANDLE types can get here
|
|
asASSERT( lctx->type.dataType.GetTypeInfo() && (lctx->type.dataType.GetTypeInfo()->flags & asOBJ_ASHANDLE) );
|
|
asASSERT( token == ttAssignment );
|
|
|
|
if( token == ttAssignment )
|
|
op = "opHndlAssign";
|
|
}
|
|
else
|
|
{
|
|
switch( int(token) ) // convert to int to avoid warning in gnuc that not all values are tested
|
|
{
|
|
case ttAssignment: op = "opAssign"; break;
|
|
case ttAddAssign: op = "opAddAssign"; break;
|
|
case ttSubAssign: op = "opSubAssign"; break;
|
|
case ttMulAssign: op = "opMulAssign"; break;
|
|
case ttDivAssign: op = "opDivAssign"; break;
|
|
case ttModAssign: op = "opModAssign"; break;
|
|
case ttPowAssign: op = "opPowAssign"; break;
|
|
case ttOrAssign: op = "opOrAssign"; break;
|
|
case ttAndAssign: op = "opAndAssign"; break;
|
|
case ttXorAssign: op = "opXorAssign"; break;
|
|
case ttShiftLeftAssign: op = "opShlAssign"; break;
|
|
case ttShiftRightLAssign: op = "opShrAssign"; break;
|
|
case ttShiftRightAAssign: op = "opUShrAssign"; break;
|
|
}
|
|
}
|
|
|
|
if( op )
|
|
{
|
|
if( builder->engine->ep.disallowValueAssignForRefType &&
|
|
lctx->type.dataType.GetTypeInfo() && (lctx->type.dataType.GetTypeInfo()->flags & asOBJ_REF) && !(lctx->type.dataType.GetTypeInfo()->flags & asOBJ_SCOPED) )
|
|
{
|
|
if( token == ttAssignment )
|
|
Error(TXT_DISALLOW_ASSIGN_ON_REF_TYPE, node);
|
|
else
|
|
Error(TXT_DISALLOW_COMPOUND_ASSIGN_ON_REF_TYPE, node);
|
|
|
|
// Set a dummy output
|
|
ctx->type.Set(lctx->type.dataType);
|
|
return true;
|
|
}
|
|
|
|
// TODO: Shouldn't accept const lvalue with the assignment operators
|
|
|
|
// Find the matching operator method
|
|
int r = CompileOverloadedDualOperator2(node, op, lctx, rctx, false, ctx);
|
|
if( r == 1 )
|
|
{
|
|
// Success, don't continue
|
|
return true;
|
|
}
|
|
else if( r < 0 )
|
|
{
|
|
// Compiler error, don't continue
|
|
ctx->type.SetDummy();
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// No suitable operator was found
|
|
return false;
|
|
}
|
|
|
|
// Returns negative on compile error
|
|
// zero on no matching operator
|
|
// one on matching operator
|
|
int asCCompiler::CompileOverloadedDualOperator2(asCScriptNode *node, const char *methodName, asCExprContext *lctx, asCExprContext *rctx, bool leftToRight, asCExprContext *ctx, bool specificReturn, const asCDataType &returnType)
|
|
{
|
|
// Find the matching method
|
|
if( lctx->type.dataType.IsObject() &&
|
|
(!lctx->type.isExplicitHandle ||
|
|
lctx->type.dataType.GetTypeInfo()->flags & asOBJ_ASHANDLE) &&
|
|
!lctx->type.IsNullConstant() )
|
|
{
|
|
asUINT n;
|
|
|
|
// Is the left value a const?
|
|
bool isConst = lctx->type.dataType.IsObjectConst();
|
|
|
|
asCArray<int> funcs;
|
|
asCObjectType *ot = CastToObjectType(lctx->type.dataType.GetTypeInfo());
|
|
asASSERT(ot);
|
|
for( n = 0; ot && n < ot->methods.GetLength(); n++ )
|
|
{
|
|
asCScriptFunction *func = engine->scriptFunctions[ot->methods[n]];
|
|
asASSERT( func );
|
|
if( func && func->name == methodName &&
|
|
(!specificReturn || func->returnType == returnType) &&
|
|
func->parameterTypes.GetLength() == 1 &&
|
|
(!isConst || func->IsReadOnly()) )
|
|
{
|
|
// Make sure the method is accessible by the module
|
|
if( builder->module->accessMask & func->accessMask )
|
|
{
|
|
funcs.PushLast(func->id);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Which is the best matching function?
|
|
asCArray<asSOverloadCandidate> tempFuncs;
|
|
MatchArgument(funcs, tempFuncs, rctx, 0);
|
|
|
|
// Find the lowest cost operator(s)
|
|
asCArray<int> ops;
|
|
asUINT bestCost = asUINT(-1);
|
|
for( n = 0; n < tempFuncs.GetLength(); ++n )
|
|
{
|
|
asUINT cost = tempFuncs[n].cost;
|
|
if( cost < bestCost )
|
|
{
|
|
ops.SetLength(0);
|
|
bestCost = cost;
|
|
}
|
|
if( cost == bestCost )
|
|
ops.PushLast(tempFuncs[n].funcId);
|
|
}
|
|
|
|
// If the object is not const, then we need to prioritize non-const methods
|
|
if( !isConst )
|
|
FilterConst(ops);
|
|
|
|
// Did we find an operator?
|
|
if( ops.GetLength() == 1 )
|
|
{
|
|
// Reserve the variables used in the right expression so the new temporary
|
|
// variable allocated for the left operand isn't accidentally overwritten.
|
|
int l = int(reservedVariables.GetLength());
|
|
rctx->bc.GetVarsUsed(reservedVariables);
|
|
|
|
// Process the lctx expression as get accessor
|
|
ProcessPropertyGetAccessor(lctx, node);
|
|
|
|
reservedVariables.SetLength(l);
|
|
|
|
asCExprContext tmpCtx(engine);
|
|
if (leftToRight)
|
|
{
|
|
// Make sure lctx is in fact a variable. If it is a reference there is no
|
|
// guarantee that the reference will stay alive throughout the evaluation of rctx
|
|
if (!lctx->type.isVariable)
|
|
{
|
|
// Reserve the variables used in the right expression so the new temporary
|
|
// variable allocated for the left operand isn't accidentally overwritten.
|
|
l = int(reservedVariables.GetLength());
|
|
rctx->bc.GetVarsUsed(reservedVariables);
|
|
|
|
if (engine->ep.allowUnsafeReferences && lctx->type.dataType.IsObject() && (lctx->type.dataType.GetTypeInfo()->flags & asOBJ_VALUE))
|
|
{
|
|
// If the application allows unsafe references, then it is not necessary to
|
|
// make a copy of the object, just store the reference as a local variable
|
|
|
|
// Allocate a temporary variable as reference to the type
|
|
asCDataType dt = lctx->type.dataType;
|
|
dt.MakeReference(true);
|
|
int offset = AllocateVariable(dt, true, false, true);
|
|
|
|
Dereference(lctx, true);
|
|
|
|
// Copy the pointer to the temporary variable
|
|
lctx->bc.InstrSHORT(asBC_PSF, (short)offset);
|
|
if (lctx->type.dataType.IsFuncdef())
|
|
lctx->bc.InstrPTR(asBC_REFCPY, &engine->functionBehaviours);
|
|
else
|
|
lctx->bc.InstrPTR(asBC_REFCPY, lctx->type.dataType.GetTypeInfo());
|
|
|
|
lctx->type.SetVariable(dt, offset, true);
|
|
}
|
|
else
|
|
{
|
|
if (lctx->type.dataType.SupportHandles())
|
|
lctx->type.dataType.MakeHandle(true);
|
|
PrepareTemporaryVariable(node, lctx);
|
|
}
|
|
|
|
reservedVariables.SetLength(l);
|
|
}
|
|
|
|
// Move the bytecode for the left operand to a temporary context
|
|
// so we can later make sure this is computed first
|
|
tmpCtx.bc.AddCode(&lctx->bc);
|
|
tmpCtx.bc.Instr(asBC_PopPtr);
|
|
|
|
// Add bytecode to push the object pointer computed in the left operand on the stack as the this pointer
|
|
// This will be placed after rctx by MakeFunctionCall below
|
|
lctx->bc.InstrWORD(asBC_PSF, lctx->type.stackOffset);
|
|
|
|
// Implicitly dereference handle parameters sent by reference
|
|
sVariable *v = variables->GetVariableByOffset(lctx->type.stackOffset);
|
|
if (v && v->type.IsReference() && (!v->type.IsObject() || v->type.IsObjectHandle()))
|
|
lctx->bc.Instr(asBC_RDSPtr);
|
|
}
|
|
else
|
|
{
|
|
// Make sure the rvalue doesn't have deferred temporary variables that are also used in the lvalue,
|
|
// since that would cause the VM to overwrite the variable while executing the bytecode for the lvalue.
|
|
asCArray<int> usedVars;
|
|
lctx->bc.GetVarsUsed(usedVars);
|
|
asUINT oldReservedVars = reservedVariables.GetLength();
|
|
for (n = 0; n < rctx->deferredParams.GetLength(); n++)
|
|
{
|
|
if (rctx->deferredParams[n].argType.isTemporary &&
|
|
usedVars.Exists(rctx->deferredParams[n].argType.stackOffset))
|
|
{
|
|
if (reservedVariables.GetLength() == oldReservedVars)
|
|
reservedVariables.Concatenate(usedVars);
|
|
|
|
// Allocate a new variable for the deferred argument
|
|
int offset = AllocateVariableNotIn(rctx->deferredParams[n].argType.dataType, true, false, rctx);
|
|
int oldVar = rctx->deferredParams[n].argType.stackOffset;
|
|
rctx->deferredParams[n].argType.stackOffset = short(offset);
|
|
rctx->bc.ExchangeVar(oldVar, offset);
|
|
ReleaseTemporaryVariable(oldVar, 0);
|
|
}
|
|
}
|
|
reservedVariables.SetLength(oldReservedVars);
|
|
}
|
|
|
|
// Merge the bytecode so that it forms lvalue.methodName(rvalue)
|
|
asCArray<asCExprContext *> args;
|
|
args.PushLast(rctx);
|
|
MergeExprBytecode(ctx, lctx);
|
|
ctx->type = lctx->type;
|
|
MakeFunctionCall(ctx, ops[0], CastToObjectType(ctx->type.dataType.GetTypeInfo()), args, node);
|
|
|
|
// Rearrange the bytecode so the left argument is computed first
|
|
if (leftToRight)
|
|
{
|
|
tmpCtx.bc.AddCode(&ctx->bc);
|
|
ctx->bc.AddCode(&tmpCtx.bc);
|
|
}
|
|
|
|
// Found matching operator
|
|
return 1;
|
|
}
|
|
else if( ops.GetLength() > 1 )
|
|
{
|
|
Error(TXT_MORE_THAN_ONE_MATCHING_OP, node);
|
|
PrintMatchingFuncs(ops, node);
|
|
|
|
ctx->type.SetDummy();
|
|
|
|
// Compiler error
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
// No matching operator
|
|
return 0;
|
|
}
|
|
|
|
void asCCompiler::MakeFunctionCall(asCExprContext *ctx, int funcId, asCObjectType *objectType, asCArray<asCExprContext*> &args, asCScriptNode *node, bool useVariable, int stackOffset, int funcPtrVar)
|
|
{
|
|
if( objectType )
|
|
Dereference(ctx, true);
|
|
|
|
// Store the expression node for error reporting
|
|
if( ctx->exprNode == 0 )
|
|
ctx->exprNode = node;
|
|
|
|
asCByteCode objBC(engine);
|
|
objBC.AddCode(&ctx->bc);
|
|
|
|
int r = PrepareFunctionCall(funcId, &ctx->bc, args);
|
|
if (r < 0)
|
|
return;
|
|
|
|
// Verify if any of the args variable offsets are used in the other code.
|
|
// If they are exchange the offset for a new one
|
|
asUINT n;
|
|
for( n = 0; n < args.GetLength(); n++ )
|
|
{
|
|
if( args[n]->type.isTemporary && objBC.IsVarUsed(args[n]->type.stackOffset) )
|
|
{
|
|
// Release the current temporary variable
|
|
ReleaseTemporaryVariable(args[n]->type, 0);
|
|
|
|
asCDataType dt = args[n]->type.dataType;
|
|
dt.MakeReference(false);
|
|
|
|
int l = int(reservedVariables.GetLength());
|
|
objBC.GetVarsUsed(reservedVariables);
|
|
ctx->bc.GetVarsUsed(reservedVariables);
|
|
int newOffset = AllocateVariable(dt, true, IsVariableOnHeap(args[n]->type.stackOffset));
|
|
reservedVariables.SetLength(l);
|
|
|
|
asASSERT( IsVariableOnHeap(args[n]->type.stackOffset) == IsVariableOnHeap(newOffset) );
|
|
|
|
ctx->bc.ExchangeVar(args[n]->type.stackOffset, newOffset);
|
|
args[n]->type.stackOffset = (short)newOffset;
|
|
args[n]->type.isTemporary = true;
|
|
args[n]->type.isVariable = true;
|
|
}
|
|
}
|
|
|
|
// If the function will return a value type on the stack, then we must allocate space
|
|
// for that here and push the address on the stack as a hidden argument to the function
|
|
asCScriptFunction *func = builder->GetFunctionDescription(funcId);
|
|
if( func->DoesReturnOnStack() )
|
|
{
|
|
asASSERT(!useVariable);
|
|
|
|
useVariable = true;
|
|
stackOffset = AllocateVariable(func->returnType, true);
|
|
ctx->bc.InstrSHORT(asBC_PSF, short(stackOffset));
|
|
}
|
|
|
|
ctx->bc.AddCode(&objBC);
|
|
|
|
MoveArgsToStack(funcId, &ctx->bc, args, objectType ? true : false);
|
|
|
|
PerformFunctionCall(funcId, ctx, false, &args, 0, useVariable, stackOffset, funcPtrVar);
|
|
}
|
|
|
|
int asCCompiler::CompileOperator(asCScriptNode *node, asCExprContext *lctx, asCExprContext *rctx, asCExprContext *ctx, eTokenType op, bool leftToRight)
|
|
{
|
|
// Don't allow any operators on expressions that take address of class method, but allow it on global functions
|
|
if( (lctx->IsClassMethod()) || (rctx->IsClassMethod()) )
|
|
{
|
|
Error(TXT_INVALID_OP_ON_METHOD, node);
|
|
return -1;
|
|
}
|
|
|
|
// Don't allow any operators on void expressions
|
|
if( lctx->IsVoidExpression() || rctx->IsVoidExpression() )
|
|
{
|
|
Error(TXT_VOID_CANT_BE_OPERAND, node);
|
|
return -1;
|
|
}
|
|
|
|
if( op == ttUnrecognizedToken )
|
|
op = node->tokenType;
|
|
|
|
IsVariableInitialized(&lctx->type, node);
|
|
IsVariableInitialized(&rctx->type, node);
|
|
|
|
if( lctx->type.isExplicitHandle || rctx->type.isExplicitHandle ||
|
|
lctx->type.IsNullConstant() || rctx->type.IsNullConstant() ||
|
|
op == ttIs || op == ttNotIs )
|
|
{
|
|
CompileOperatorOnHandles(node, lctx, rctx, ctx, op);
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
// Compile an overloaded operator for the two operands
|
|
if( CompileOverloadedDualOperator(node, lctx, rctx, leftToRight, ctx, false, op) )
|
|
return 0;
|
|
|
|
// If both operands are objects, then we shouldn't continue
|
|
if( lctx->type.dataType.IsObject() && rctx->type.dataType.IsObject() )
|
|
{
|
|
asCString str;
|
|
str.Format(TXT_NO_MATCHING_OP_FOUND_FOR_TYPES_s_AND_s, lctx->type.dataType.Format(outFunc->nameSpace).AddressOf(), rctx->type.dataType.Format(outFunc->nameSpace).AddressOf());
|
|
Error(str, node);
|
|
ctx->type.SetDummy();
|
|
return -1;
|
|
}
|
|
|
|
// Process the property get accessors (if any)
|
|
ProcessPropertyGetAccessor(lctx, node);
|
|
ProcessPropertyGetAccessor(rctx, node);
|
|
|
|
// Make sure we have two variables or constants
|
|
if( lctx->type.dataType.IsReference() ) ConvertToVariableNotIn(lctx, rctx);
|
|
if( rctx->type.dataType.IsReference() ) ConvertToVariableNotIn(rctx, lctx);
|
|
|
|
// Make sure lctx doesn't end up with a variable used in rctx
|
|
if( lctx->type.isTemporary && rctx->bc.IsVarUsed(lctx->type.stackOffset) )
|
|
{
|
|
int offset = AllocateVariableNotIn(lctx->type.dataType, true, false, rctx);
|
|
rctx->bc.ExchangeVar(lctx->type.stackOffset, offset);
|
|
ReleaseTemporaryVariable(offset, 0);
|
|
}
|
|
|
|
// Math operators
|
|
// + - * / % ** += -= *= /= %= **=
|
|
if( op == ttPlus || op == ttAddAssign ||
|
|
op == ttMinus || op == ttSubAssign ||
|
|
op == ttStar || op == ttMulAssign ||
|
|
op == ttSlash || op == ttDivAssign ||
|
|
op == ttPercent || op == ttModAssign ||
|
|
op == ttStarStar || op == ttPowAssign )
|
|
{
|
|
CompileMathOperator(node, lctx, rctx, ctx, op);
|
|
return 0;
|
|
}
|
|
|
|
// Bitwise operators
|
|
// << >> >>> & | ^ <<= >>= >>>= &= |= ^=
|
|
if( op == ttAmp || op == ttAndAssign ||
|
|
op == ttBitOr || op == ttOrAssign ||
|
|
op == ttBitXor || op == ttXorAssign ||
|
|
op == ttBitShiftLeft || op == ttShiftLeftAssign ||
|
|
op == ttBitShiftRight || op == ttShiftRightLAssign ||
|
|
op == ttBitShiftRightArith || op == ttShiftRightAAssign )
|
|
{
|
|
CompileBitwiseOperator(node, lctx, rctx, ctx, op);
|
|
return 0;
|
|
}
|
|
|
|
// Comparison operators
|
|
// == != < > <= >=
|
|
if( op == ttEqual || op == ttNotEqual ||
|
|
op == ttLessThan || op == ttLessThanOrEqual ||
|
|
op == ttGreaterThan || op == ttGreaterThanOrEqual )
|
|
{
|
|
CompileComparisonOperator(node, lctx, rctx, ctx, op);
|
|
return 0;
|
|
}
|
|
|
|
// Boolean operators
|
|
// && || ^^
|
|
if( op == ttAnd || op == ttOr || op == ttXor )
|
|
{
|
|
CompileBooleanOperator(node, lctx, rctx, ctx, op);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
asASSERT(false);
|
|
return -1;
|
|
}
|
|
|
|
void asCCompiler::ConvertToTempVariableNotIn(asCExprContext *ctx, asCExprContext *exclude)
|
|
{
|
|
int l = int(reservedVariables.GetLength());
|
|
if( exclude ) exclude->bc.GetVarsUsed(reservedVariables);
|
|
ConvertToTempVariable(ctx);
|
|
reservedVariables.SetLength(l);
|
|
}
|
|
|
|
void asCCompiler::ConvertToTempVariable(asCExprContext *ctx)
|
|
{
|
|
// This is only used for primitive types and null handles
|
|
asASSERT( ctx->type.dataType.IsPrimitive() || ctx->type.dataType.IsNullHandle() );
|
|
|
|
ConvertToVariable(ctx);
|
|
if( !ctx->type.isTemporary )
|
|
{
|
|
if( ctx->type.dataType.IsPrimitive() )
|
|
{
|
|
// Copy the variable to a temporary variable
|
|
int offset = AllocateVariable(ctx->type.dataType, true);
|
|
if( ctx->type.dataType.GetSizeInMemoryDWords() == 1 )
|
|
ctx->bc.InstrW_W(asBC_CpyVtoV4, offset, ctx->type.stackOffset);
|
|
else
|
|
ctx->bc.InstrW_W(asBC_CpyVtoV8, offset, ctx->type.stackOffset);
|
|
ctx->type.SetVariable(ctx->type.dataType, offset, true);
|
|
}
|
|
else
|
|
{
|
|
// We should never get here
|
|
asASSERT(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
void asCCompiler::ConvertToVariable(asCExprContext *ctx)
|
|
{
|
|
// We should never get here while the context is still an unprocessed property accessor
|
|
asASSERT(ctx->property_get == 0 && ctx->property_set == 0);
|
|
|
|
int offset;
|
|
if( !ctx->type.isVariable &&
|
|
(ctx->type.dataType.IsObjectHandle() ||
|
|
(ctx->type.dataType.IsObject() && ctx->type.dataType.SupportHandles())) )
|
|
{
|
|
offset = AllocateVariable(ctx->type.dataType, true);
|
|
if( ctx->type.IsNullConstant() )
|
|
{
|
|
if( ctx->bc.GetLastInstr() == asBC_PshNull )
|
|
ctx->bc.Instr(asBC_PopPtr); // Pop the null constant pushed onto the stack
|
|
ctx->bc.InstrSHORT(asBC_ClrVPtr, (short)offset);
|
|
}
|
|
else
|
|
{
|
|
Dereference(ctx, true);
|
|
|
|
// Copy the object handle to a variable
|
|
ctx->bc.InstrSHORT(asBC_PSF, (short)offset);
|
|
if( ctx->type.dataType.IsFuncdef() )
|
|
ctx->bc.InstrPTR(asBC_REFCPY, &engine->functionBehaviours);
|
|
else
|
|
ctx->bc.InstrPTR(asBC_REFCPY, ctx->type.dataType.GetTypeInfo());
|
|
ctx->bc.Instr(asBC_PopPtr);
|
|
}
|
|
|
|
// As this is an object the reference must be placed on the stack
|
|
ctx->bc.InstrSHORT(asBC_PSF, (short)offset);
|
|
|
|
ReleaseTemporaryVariable(ctx->type, &ctx->bc);
|
|
ctx->type.SetVariable(ctx->type.dataType, offset, true);
|
|
ctx->type.dataType.MakeHandle(true);
|
|
ctx->type.dataType.MakeReference(true);
|
|
}
|
|
else if( (!ctx->type.isVariable || ctx->type.dataType.IsReference()) &&
|
|
ctx->type.dataType.IsPrimitive() )
|
|
{
|
|
if( ctx->type.isConstant )
|
|
{
|
|
offset = AllocateVariable(ctx->type.dataType, true);
|
|
if( ctx->type.dataType.GetSizeInMemoryBytes() == 1 )
|
|
ctx->bc.InstrSHORT_B(asBC_SetV1, (short)offset, ctx->type.GetConstantB());
|
|
else if( ctx->type.dataType.GetSizeInMemoryBytes() == 2 )
|
|
ctx->bc.InstrSHORT_W(asBC_SetV2, (short)offset, ctx->type.GetConstantW());
|
|
else if( ctx->type.dataType.GetSizeInMemoryBytes() == 4 )
|
|
ctx->bc.InstrSHORT_DW(asBC_SetV4, (short)offset, ctx->type.GetConstantDW());
|
|
else
|
|
ctx->bc.InstrSHORT_QW(asBC_SetV8, (short)offset, ctx->type.GetConstantQW());
|
|
|
|
ctx->type.SetVariable(ctx->type.dataType, offset, true);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
asASSERT(ctx->type.dataType.IsPrimitive());
|
|
asASSERT(ctx->type.dataType.IsReference());
|
|
|
|
ctx->type.dataType.MakeReference(false);
|
|
offset = AllocateVariable(ctx->type.dataType, true);
|
|
|
|
// Read the value from the address in the register directly into the variable
|
|
if( ctx->type.dataType.GetSizeInMemoryBytes() == 1 )
|
|
ctx->bc.InstrSHORT(asBC_RDR1, (short)offset);
|
|
else if( ctx->type.dataType.GetSizeInMemoryBytes() == 2 )
|
|
ctx->bc.InstrSHORT(asBC_RDR2, (short)offset);
|
|
else if( ctx->type.dataType.GetSizeInMemoryDWords() == 1 )
|
|
ctx->bc.InstrSHORT(asBC_RDR4, (short)offset);
|
|
else
|
|
ctx->bc.InstrSHORT(asBC_RDR8, (short)offset);
|
|
}
|
|
|
|
ReleaseTemporaryVariable(ctx->type, &ctx->bc);
|
|
ctx->type.SetVariable(ctx->type.dataType, offset, true);
|
|
}
|
|
}
|
|
|
|
void asCCompiler::ConvertToVariableNotIn(asCExprContext *ctx, asCExprContext *exclude)
|
|
{
|
|
int l = int(reservedVariables.GetLength());
|
|
if( exclude ) exclude->bc.GetVarsUsed(reservedVariables);
|
|
ConvertToVariable(ctx);
|
|
reservedVariables.SetLength(l);
|
|
}
|
|
|
|
void asCCompiler::ImplicitConvObjectToBestMathType(asCExprContext *ctx, asCScriptNode *node)
|
|
{
|
|
asCArray<int> funcs;
|
|
asCObjectType *ot = CastToObjectType(ctx->type.dataType.GetTypeInfo());
|
|
if( ot )
|
|
{
|
|
for( unsigned int n = 0; n < ot->methods.GetLength(); n++ )
|
|
{
|
|
// Consider only implicit casts
|
|
asCScriptFunction *func = engine->scriptFunctions[ot->methods[n]];
|
|
if( func->name == "opImplConv" &&
|
|
func->returnType.IsPrimitive() &&
|
|
func->parameterTypes.GetLength() == 0 )
|
|
funcs.PushLast(ot->methods[n]);
|
|
}
|
|
|
|
// Use the one with the highest precision
|
|
const eTokenType match[10] = {ttDouble, ttFloat, ttInt64, ttUInt64, ttInt, ttUInt, ttInt16, ttUInt16, ttInt8, ttUInt8};
|
|
while( funcs.GetLength() > 1 )
|
|
{
|
|
eTokenType returnType = builder->GetFunctionDescription(funcs[0])->returnType.GetTokenType();
|
|
int value1 = 11, value2 = 11;
|
|
for( asUINT i = 0; i < 10; i++ )
|
|
{
|
|
if( returnType == match[i] )
|
|
{
|
|
value1 = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
for( asUINT n = 1; n < funcs.GetLength(); n++ )
|
|
{
|
|
returnType = builder->GetFunctionDescription(funcs[n])->returnType.GetTokenType();
|
|
for( asUINT i = 0; i < 10; i++ )
|
|
{
|
|
if( returnType == match[i] )
|
|
{
|
|
value2 = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( value2 >= value1 )
|
|
{
|
|
// Remove this and continue searching
|
|
funcs.RemoveIndexUnordered(n--);
|
|
}
|
|
else
|
|
{
|
|
// Remove the first, and start over
|
|
funcs.RemoveIndexUnordered(0);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Do the conversion
|
|
if( funcs.GetLength() )
|
|
ImplicitConvObjectToPrimitive(ctx, builder->GetFunctionDescription(funcs[0])->returnType, node, asIC_IMPLICIT_CONV);
|
|
}
|
|
}
|
|
|
|
void asCCompiler::CompileMathOperator(asCScriptNode *node, asCExprContext *lctx, asCExprContext *rctx, asCExprContext *ctx, eTokenType op)
|
|
{
|
|
// TODO: If a constant is only using 32bits, then a 32bit operation is preferred
|
|
|
|
// TODO: clean up: This initial part is identical to CompileComparisonOperator. Make a common function out of it
|
|
|
|
// If either operand is a non-primitive then use the primitive type
|
|
if( !lctx->type.dataType.IsPrimitive() )
|
|
{
|
|
int l = int(reservedVariables.GetLength());
|
|
rctx->bc.GetVarsUsed(reservedVariables);
|
|
ImplicitConvObjectToBestMathType(lctx, node);
|
|
reservedVariables.SetLength(l);
|
|
}
|
|
if( !rctx->type.dataType.IsPrimitive() )
|
|
{
|
|
int l = int(reservedVariables.GetLength());
|
|
lctx->bc.GetVarsUsed(reservedVariables);
|
|
ImplicitConvObjectToBestMathType(rctx, node);
|
|
reservedVariables.SetLength(l);
|
|
}
|
|
|
|
// Both types must now be primitives. Implicitly convert them so they match
|
|
asCDataType to;
|
|
if( lctx->type.dataType.IsDoubleType() || rctx->type.dataType.IsDoubleType() )
|
|
to.SetTokenType(ttDouble);
|
|
else if( lctx->type.dataType.IsFloatType() || rctx->type.dataType.IsFloatType() )
|
|
to.SetTokenType(ttFloat);
|
|
else if( lctx->type.dataType.GetSizeInMemoryDWords() == 2 || rctx->type.dataType.GetSizeInMemoryDWords() == 2 )
|
|
{
|
|
// Convert to int64 if both are signed or if one is non-constant and signed
|
|
if( (lctx->type.dataType.IsIntegerType() && !lctx->type.isConstant) ||
|
|
(rctx->type.dataType.IsIntegerType() && !rctx->type.isConstant) )
|
|
to.SetTokenType(ttInt64);
|
|
else if( lctx->type.dataType.IsUnsignedType() || rctx->type.dataType.IsUnsignedType() )
|
|
to.SetTokenType(ttUInt64);
|
|
else
|
|
to.SetTokenType(ttInt64);
|
|
}
|
|
else
|
|
{
|
|
// Convert to int32 if both are signed or if one is non-constant and signed
|
|
if( (lctx->type.dataType.IsIntegerType() && !lctx->type.isConstant) ||
|
|
(rctx->type.dataType.IsIntegerType() && !rctx->type.isConstant) )
|
|
to.SetTokenType(ttInt);
|
|
else if( lctx->type.dataType.IsUnsignedType() || rctx->type.dataType.IsUnsignedType() )
|
|
to.SetTokenType(ttUInt);
|
|
else
|
|
to.SetTokenType(ttInt);
|
|
}
|
|
|
|
// If doing an operation with double constant and float variable, the constant should be converted to float
|
|
if( (lctx->type.isConstant && lctx->type.dataType.IsDoubleType() && !rctx->type.isConstant && rctx->type.dataType.IsFloatType()) ||
|
|
(rctx->type.isConstant && rctx->type.dataType.IsDoubleType() && !lctx->type.isConstant && lctx->type.dataType.IsFloatType()) )
|
|
to.SetTokenType(ttFloat);
|
|
|
|
if( op == ttUnrecognizedToken )
|
|
op = node->tokenType;
|
|
|
|
// If integer division is disabled, convert to floating-point
|
|
if( engine->ep.disableIntegerDivision &&
|
|
(op == ttSlash || op == ttDivAssign) &&
|
|
(to.IsIntegerType() || to.IsUnsignedType()) )
|
|
{
|
|
// Use double to avoid losing precision when dividing with 32bit ints
|
|
// For 64bit ints there is unfortunately no greater type so with those
|
|
// there is still a risk of loosing precision
|
|
to.SetTokenType(ttDouble);
|
|
}
|
|
|
|
// Do the actual conversion
|
|
int l = int(reservedVariables.GetLength());
|
|
rctx->bc.GetVarsUsed(reservedVariables);
|
|
lctx->bc.GetVarsUsed(reservedVariables);
|
|
|
|
if( lctx->type.dataType.IsReference() )
|
|
ConvertToVariable(lctx);
|
|
if( rctx->type.dataType.IsReference() )
|
|
ConvertToVariable(rctx);
|
|
|
|
if( to.IsPrimitive() )
|
|
{
|
|
// ttStarStar allows an integer, right-hand operand and a double
|
|
// left-hand operand.
|
|
if( (op == ttStarStar || op == ttPowAssign) &&
|
|
lctx->type.dataType.IsDoubleType() &&
|
|
(rctx->type.dataType.IsIntegerType() ||
|
|
rctx->type.dataType.IsUnsignedType()) )
|
|
{
|
|
to.SetTokenType(ttInt);
|
|
ImplicitConversion(rctx, to, node, asIC_IMPLICIT_CONV, true);
|
|
to.SetTokenType(ttDouble);
|
|
}
|
|
else
|
|
{
|
|
ImplicitConversion(lctx, to, node, asIC_IMPLICIT_CONV, true);
|
|
ImplicitConversion(rctx, to, node, asIC_IMPLICIT_CONV, true);
|
|
}
|
|
}
|
|
reservedVariables.SetLength(l);
|
|
|
|
// Verify that the conversion was successful
|
|
if( !lctx->type.dataType.IsIntegerType() &&
|
|
!lctx->type.dataType.IsUnsignedType() &&
|
|
!lctx->type.dataType.IsFloatType() &&
|
|
!lctx->type.dataType.IsDoubleType() )
|
|
{
|
|
asCString str;
|
|
str.Format(TXT_NO_CONVERSION_s_TO_MATH_TYPE, lctx->type.dataType.Format(outFunc->nameSpace).AddressOf());
|
|
Error(str, node);
|
|
|
|
ctx->type.SetDummy();
|
|
return;
|
|
}
|
|
|
|
if( !rctx->type.dataType.IsIntegerType() &&
|
|
!rctx->type.dataType.IsUnsignedType() &&
|
|
!rctx->type.dataType.IsFloatType() &&
|
|
!rctx->type.dataType.IsDoubleType() )
|
|
{
|
|
asCString str;
|
|
str.Format(TXT_NO_CONVERSION_s_TO_MATH_TYPE, rctx->type.dataType.Format(outFunc->nameSpace).AddressOf());
|
|
Error(str, node);
|
|
|
|
ctx->type.SetDummy();
|
|
return;
|
|
}
|
|
|
|
bool isConstant = lctx->type.isConstant && rctx->type.isConstant;
|
|
|
|
// Verify if we are dividing with a constant zero
|
|
if( rctx->type.isConstant &&
|
|
(op == ttSlash || op == ttDivAssign ||
|
|
op == ttPercent || op == ttModAssign) &&
|
|
((rctx->type.dataType.GetSizeInMemoryBytes() == 4 && rctx->type.GetConstantDW() == 0) ||
|
|
(rctx->type.dataType.GetSizeInMemoryBytes() == 8 && rctx->type.GetConstantQW() == 0) ||
|
|
(rctx->type.dataType.GetSizeInMemoryBytes() == 1 && rctx->type.GetConstantB() == 0) ||
|
|
(rctx->type.dataType.GetSizeInMemoryBytes() == 2 && rctx->type.GetConstantW() == 0)) )
|
|
{
|
|
Error(TXT_DIVIDE_BY_ZERO, node);
|
|
}
|
|
|
|
if( !isConstant )
|
|
{
|
|
ConvertToVariableNotIn(lctx, rctx);
|
|
ConvertToVariableNotIn(rctx, lctx);
|
|
ReleaseTemporaryVariable(lctx->type, &lctx->bc);
|
|
ReleaseTemporaryVariable(rctx->type, &rctx->bc);
|
|
|
|
if( op == ttAddAssign || op == ttSubAssign ||
|
|
op == ttMulAssign || op == ttDivAssign ||
|
|
op == ttModAssign || op == ttPowAssign )
|
|
{
|
|
// Merge the operands in the different order so that they are evaluated correctly
|
|
MergeExprBytecode(ctx, rctx);
|
|
MergeExprBytecode(ctx, lctx);
|
|
|
|
// We must not process the deferred parameters yet, as
|
|
// it may overwrite the lvalue kept in the register
|
|
}
|
|
else
|
|
{
|
|
MergeExprBytecode(ctx, lctx);
|
|
MergeExprBytecode(ctx, rctx);
|
|
|
|
ProcessDeferredParams(ctx);
|
|
}
|
|
|
|
asEBCInstr instruction = asBC_ADDi;
|
|
if( lctx->type.dataType.IsIntegerType() ||
|
|
lctx->type.dataType.IsUnsignedType() )
|
|
{
|
|
if( lctx->type.dataType.GetSizeInMemoryDWords() == 1 )
|
|
{
|
|
if( op == ttPlus || op == ttAddAssign )
|
|
instruction = asBC_ADDi;
|
|
else if( op == ttMinus || op == ttSubAssign )
|
|
instruction = asBC_SUBi;
|
|
else if( op == ttStar || op == ttMulAssign )
|
|
instruction = asBC_MULi;
|
|
else if( op == ttSlash || op == ttDivAssign )
|
|
{
|
|
if( lctx->type.dataType.IsIntegerType() )
|
|
instruction = asBC_DIVi;
|
|
else
|
|
instruction = asBC_DIVu;
|
|
}
|
|
else if( op == ttPercent || op == ttModAssign )
|
|
{
|
|
if( lctx->type.dataType.IsIntegerType() )
|
|
instruction = asBC_MODi;
|
|
else
|
|
instruction = asBC_MODu;
|
|
}
|
|
else if( op == ttStarStar || op == ttPowAssign )
|
|
{
|
|
if( lctx->type.dataType.IsIntegerType() )
|
|
instruction = asBC_POWi;
|
|
else
|
|
instruction = asBC_POWu;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( op == ttPlus || op == ttAddAssign )
|
|
instruction = asBC_ADDi64;
|
|
else if( op == ttMinus || op == ttSubAssign )
|
|
instruction = asBC_SUBi64;
|
|
else if( op == ttStar || op == ttMulAssign )
|
|
instruction = asBC_MULi64;
|
|
else if( op == ttSlash || op == ttDivAssign )
|
|
{
|
|
if( lctx->type.dataType.IsIntegerType() )
|
|
instruction = asBC_DIVi64;
|
|
else
|
|
instruction = asBC_DIVu64;
|
|
}
|
|
else if( op == ttPercent || op == ttModAssign )
|
|
{
|
|
if( lctx->type.dataType.IsIntegerType() )
|
|
instruction = asBC_MODi64;
|
|
else
|
|
instruction = asBC_MODu64;
|
|
}
|
|
else if( op == ttStarStar || op == ttPowAssign )
|
|
{
|
|
if( lctx->type.dataType.IsIntegerType() )
|
|
instruction = asBC_POWi64;
|
|
else
|
|
instruction = asBC_POWu64;
|
|
}
|
|
}
|
|
}
|
|
else if( lctx->type.dataType.IsFloatType() )
|
|
{
|
|
if( op == ttPlus || op == ttAddAssign )
|
|
instruction = asBC_ADDf;
|
|
else if( op == ttMinus || op == ttSubAssign )
|
|
instruction = asBC_SUBf;
|
|
else if( op == ttStar || op == ttMulAssign )
|
|
instruction = asBC_MULf;
|
|
else if( op == ttSlash || op == ttDivAssign )
|
|
instruction = asBC_DIVf;
|
|
else if( op == ttPercent || op == ttModAssign )
|
|
instruction = asBC_MODf;
|
|
else if( op == ttStarStar || op == ttPowAssign )
|
|
instruction = asBC_POWf;
|
|
}
|
|
else if( lctx->type.dataType.IsDoubleType() )
|
|
{
|
|
if( rctx->type.dataType.IsIntegerType() )
|
|
{
|
|
asASSERT(rctx->type.dataType.GetSizeInMemoryDWords() == 1);
|
|
|
|
if( op == ttStarStar || op == ttPowAssign )
|
|
instruction = asBC_POWdi;
|
|
else
|
|
asASSERT(false); // Should not be possible
|
|
}
|
|
else
|
|
{
|
|
if( op == ttPlus || op == ttAddAssign )
|
|
instruction = asBC_ADDd;
|
|
else if( op == ttMinus || op == ttSubAssign )
|
|
instruction = asBC_SUBd;
|
|
else if( op == ttStar || op == ttMulAssign )
|
|
instruction = asBC_MULd;
|
|
else if( op == ttSlash || op == ttDivAssign )
|
|
instruction = asBC_DIVd;
|
|
else if( op == ttPercent || op == ttModAssign )
|
|
instruction = asBC_MODd;
|
|
else if( op == ttStarStar || op == ttPowAssign )
|
|
instruction = asBC_POWd;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Shouldn't be possible
|
|
asASSERT(false);
|
|
}
|
|
|
|
// Do the operation
|
|
int a = AllocateVariable(lctx->type.dataType, true);
|
|
int b = lctx->type.stackOffset;
|
|
int c = rctx->type.stackOffset;
|
|
|
|
ctx->bc.InstrW_W_W(instruction, a, b, c);
|
|
|
|
ctx->type.SetVariable(lctx->type.dataType, a, true);
|
|
}
|
|
else
|
|
{
|
|
// Both values are constants
|
|
if( lctx->type.dataType.IsIntegerType() ||
|
|
lctx->type.dataType.IsUnsignedType() )
|
|
{
|
|
if( lctx->type.dataType.GetSizeInMemoryDWords() == 1 )
|
|
{
|
|
int v = 0;
|
|
if( op == ttPlus )
|
|
v = int(lctx->type.GetConstantDW()) + int(rctx->type.GetConstantDW());
|
|
else if( op == ttMinus )
|
|
v = int(lctx->type.GetConstantDW()) - int(rctx->type.GetConstantDW());
|
|
else if( op == ttStar )
|
|
v = int(lctx->type.GetConstantDW()) * int(rctx->type.GetConstantDW());
|
|
else if( op == ttSlash )
|
|
{
|
|
// TODO: Should probably report an error, rather than silently convert the value to 0
|
|
if( rctx->type.GetConstantDW() == 0 || (int(rctx->type.GetConstantDW()) == -1 && lctx->type.GetConstantDW() == 0x80000000) )
|
|
v = 0;
|
|
else
|
|
if( lctx->type.dataType.IsIntegerType() )
|
|
v = int(lctx->type.GetConstantDW()) / int(rctx->type.GetConstantDW());
|
|
else
|
|
v = lctx->type.GetConstantDW() / rctx->type.GetConstantDW();
|
|
}
|
|
else if( op == ttPercent )
|
|
{
|
|
// TODO: Should probably report an error, rather than silently convert the value to 0
|
|
if( rctx->type.GetConstantDW() == 0 || (int(rctx->type.GetConstantDW()) == -1 && lctx->type.GetConstantDW() == 0x80000000) )
|
|
v = 0;
|
|
else
|
|
if( lctx->type.dataType.IsIntegerType() )
|
|
v = int(lctx->type.GetConstantDW()) % int(rctx->type.GetConstantDW());
|
|
else
|
|
v = lctx->type.GetConstantDW() % rctx->type.GetConstantDW();
|
|
}
|
|
else if( op == ttStarStar )
|
|
{
|
|
bool isOverflow;
|
|
if( lctx->type.dataType.IsIntegerType() )
|
|
v = as_powi(int(lctx->type.GetConstantDW()), int(rctx->type.GetConstantDW()), isOverflow);
|
|
else
|
|
v = as_powu(lctx->type.GetConstantDW(), rctx->type.GetConstantDW(), isOverflow);
|
|
|
|
if( isOverflow )
|
|
Error(TXT_POW_OVERFLOW, node);
|
|
}
|
|
|
|
ctx->type.SetConstantDW(lctx->type.dataType, v);
|
|
|
|
// If the right value is greater than the left value in a minus operation, then we need to convert the type to int
|
|
if( lctx->type.dataType.GetTokenType() == ttUInt && op == ttMinus && lctx->type.GetConstantDW() < rctx->type.GetConstantDW())
|
|
ctx->type.dataType.SetTokenType(ttInt);
|
|
}
|
|
else
|
|
{
|
|
asQWORD v = 0;
|
|
if( op == ttPlus )
|
|
v = asINT64(lctx->type.GetConstantQW()) + asINT64(rctx->type.GetConstantQW());
|
|
else if( op == ttMinus )
|
|
v = asINT64(lctx->type.GetConstantQW()) - asINT64(rctx->type.GetConstantQW());
|
|
else if( op == ttStar )
|
|
v = asINT64(lctx->type.GetConstantQW()) * asINT64(rctx->type.GetConstantQW());
|
|
else if( op == ttSlash )
|
|
{
|
|
// TODO: Should probably report an error, rather than silently convert the value to 0
|
|
if( rctx->type.GetConstantQW() == 0 || (rctx->type.GetConstantQW() == asQWORD(-1) && lctx->type.GetConstantQW() == (asQWORD(1)<<63)) )
|
|
v = 0;
|
|
else
|
|
if( lctx->type.dataType.IsIntegerType() )
|
|
v = asINT64(lctx->type.GetConstantQW()) / asINT64(rctx->type.GetConstantQW());
|
|
else
|
|
v = lctx->type.GetConstantQW() / rctx->type.GetConstantQW();
|
|
}
|
|
else if( op == ttPercent )
|
|
{
|
|
// TODO: Should probably report an error, rather than silently convert the value to 0
|
|
if( rctx->type.GetConstantQW() == 0 || (rctx->type.GetConstantQW() == asQWORD(-1) && lctx->type.GetConstantQW() == (asQWORD(1)<<63)) )
|
|
v = 0;
|
|
else
|
|
if( lctx->type.dataType.IsIntegerType() )
|
|
v = asINT64(lctx->type.GetConstantQW()) % asINT64(rctx->type.GetConstantQW());
|
|
else
|
|
v = lctx->type.GetConstantQW() % rctx->type.GetConstantQW();
|
|
}
|
|
else if( op == ttStarStar )
|
|
{
|
|
bool isOverflow;
|
|
if( lctx->type.dataType.IsIntegerType() )
|
|
v = as_powi64(asINT64(lctx->type.GetConstantQW()), asINT64(rctx->type.GetConstantQW()), isOverflow);
|
|
else
|
|
v = as_powu64(lctx->type.GetConstantQW(), rctx->type.GetConstantQW(), isOverflow);
|
|
|
|
if( isOverflow )
|
|
Error(TXT_POW_OVERFLOW, node);
|
|
}
|
|
|
|
ctx->type.SetConstantQW(lctx->type.dataType, v);
|
|
|
|
// If the right value is greater than the left value in a minus operation, then we need to convert the type to int
|
|
if( lctx->type.dataType.GetTokenType() == ttUInt64 && op == ttMinus && lctx->type.GetConstantQW() < rctx->type.GetConstantQW())
|
|
ctx->type.dataType.SetTokenType(ttInt64);
|
|
}
|
|
}
|
|
else if( lctx->type.dataType.IsFloatType() )
|
|
{
|
|
float v = 0.0f;
|
|
if( op == ttPlus )
|
|
v = lctx->type.GetConstantF() + rctx->type.GetConstantF();
|
|
else if( op == ttMinus )
|
|
v = lctx->type.GetConstantF() - rctx->type.GetConstantF();
|
|
else if( op == ttStar )
|
|
v = lctx->type.GetConstantF() * rctx->type.GetConstantF();
|
|
else if( op == ttSlash )
|
|
{
|
|
if( rctx->type.GetConstantF() == 0 )
|
|
v = 0;
|
|
else
|
|
v = lctx->type.GetConstantF() / rctx->type.GetConstantF();
|
|
}
|
|
else if( op == ttPercent )
|
|
{
|
|
if( rctx->type.GetConstantF() == 0 )
|
|
v = 0;
|
|
else
|
|
v = fmodf(lctx->type.GetConstantF(), rctx->type.GetConstantF());
|
|
}
|
|
else if( op == ttStarStar )
|
|
{
|
|
v = powf(lctx->type.GetConstantF(), rctx->type.GetConstantF());
|
|
|
|
if( v == HUGE_VAL )
|
|
Error(TXT_POW_OVERFLOW, node);
|
|
}
|
|
|
|
ctx->type.SetConstantF(lctx->type.dataType, v);
|
|
}
|
|
else if( lctx->type.dataType.IsDoubleType() )
|
|
{
|
|
double v = 0.0;
|
|
if( rctx->type.dataType.IsIntegerType() )
|
|
{
|
|
asASSERT(rctx->type.dataType.GetSizeInMemoryDWords() == 1);
|
|
|
|
if( op == ttStarStar || op == ttPowAssign )
|
|
{
|
|
v = pow(lctx->type.GetConstantD(), int(rctx->type.GetConstantDW()));
|
|
if( v == HUGE_VAL )
|
|
Error(TXT_POW_OVERFLOW, node);
|
|
}
|
|
else
|
|
asASSERT(false); // Should not be possible
|
|
}
|
|
else
|
|
{
|
|
if( op == ttPlus )
|
|
v = lctx->type.GetConstantD() + rctx->type.GetConstantD();
|
|
else if( op == ttMinus )
|
|
v = lctx->type.GetConstantD() - rctx->type.GetConstantD();
|
|
else if( op == ttStar )
|
|
v = lctx->type.GetConstantD() * rctx->type.GetConstantD();
|
|
else if( op == ttSlash )
|
|
{
|
|
if( rctx->type.GetConstantD() == 0 )
|
|
v = 0;
|
|
else
|
|
v = lctx->type.GetConstantD() / rctx->type.GetConstantD();
|
|
}
|
|
else if( op == ttPercent )
|
|
{
|
|
if( rctx->type.GetConstantD() == 0 )
|
|
v = 0;
|
|
else
|
|
v = fmod(lctx->type.GetConstantD(), rctx->type.GetConstantD());
|
|
}
|
|
else if( op == ttStarStar )
|
|
{
|
|
v = pow(lctx->type.GetConstantD(), rctx->type.GetConstantD());
|
|
if( v == HUGE_VAL )
|
|
Error(TXT_POW_OVERFLOW, node);
|
|
}
|
|
}
|
|
|
|
ctx->type.SetConstantD(lctx->type.dataType, v);
|
|
}
|
|
else
|
|
{
|
|
// Shouldn't be possible
|
|
asASSERT(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
void asCCompiler::CompileBitwiseOperator(asCScriptNode *node, asCExprContext *lctx, asCExprContext *rctx, asCExprContext *ctx, eTokenType op)
|
|
{
|
|
// TODO: If a constant is only using 32bits, then a 32bit operation is preferred
|
|
|
|
if( op == ttUnrecognizedToken )
|
|
op = node->tokenType;
|
|
if( op == ttAmp || op == ttAndAssign ||
|
|
op == ttBitOr || op == ttOrAssign ||
|
|
op == ttBitXor || op == ttXorAssign )
|
|
{
|
|
// Also do not permit float/double to be implicitly converted to integer in this case
|
|
// as the user may think the result is a bitwise operation on the float value but it's not
|
|
if (lctx->type.dataType.IsFloatType() || lctx->type.dataType.IsDoubleType())
|
|
{
|
|
asCString str;
|
|
str.Format(TXT_ILLEGAL_OPERATION_ON_s, lctx->type.dataType.Format(outFunc->nameSpace).AddressOf());
|
|
Error(str, node);
|
|
|
|
// Set an integer value and allow the compiler to continue
|
|
ctx->type.SetConstantDW(asCDataType::CreatePrimitive(ttInt, true), 0);
|
|
return;
|
|
}
|
|
if (rctx->type.dataType.IsFloatType() || rctx->type.dataType.IsDoubleType())
|
|
{
|
|
asCString str;
|
|
str.Format(TXT_ILLEGAL_OPERATION_ON_s, rctx->type.dataType.Format(outFunc->nameSpace).AddressOf());
|
|
Error(str, node);
|
|
|
|
// Set an integer value and allow the compiler to continue
|
|
ctx->type.SetConstantDW(asCDataType::CreatePrimitive(ttInt, true), 0);
|
|
return;
|
|
}
|
|
|
|
// Convert left hand operand to integer if it's not already one
|
|
asCDataType to;
|
|
if( lctx->type.dataType.GetSizeInMemoryDWords() == 2 ||
|
|
rctx->type.dataType.GetSizeInMemoryDWords() == 2 )
|
|
to.SetTokenType(ttInt64);
|
|
else
|
|
to.SetTokenType(ttInt);
|
|
|
|
// Do the actual conversion (keep sign/unsigned if possible)
|
|
int l = int(reservedVariables.GetLength());
|
|
rctx->bc.GetVarsUsed(reservedVariables);
|
|
if( lctx->type.dataType.IsUnsignedType() )
|
|
to.SetTokenType( to.GetSizeOnStackDWords() == 1 ? ttUInt : ttUInt64 );
|
|
else
|
|
to.SetTokenType( to.GetSizeOnStackDWords() == 1 ? ttInt : ttInt64 );
|
|
ImplicitConversion(lctx, to, node, asIC_IMPLICIT_CONV, true);
|
|
reservedVariables.SetLength(l);
|
|
|
|
// Verify that the conversion was successful
|
|
if( lctx->type.dataType != to )
|
|
{
|
|
asCString str;
|
|
str.Format(TXT_NO_CONVERSION_s_TO_s, lctx->type.dataType.Format(outFunc->nameSpace).AddressOf(), to.Format(outFunc->nameSpace).AddressOf());
|
|
Error(str, node);
|
|
}
|
|
|
|
// Convert right hand operand to same size as left hand
|
|
l = int(reservedVariables.GetLength());
|
|
lctx->bc.GetVarsUsed(reservedVariables);
|
|
if( rctx->type.dataType.IsUnsignedType() )
|
|
to.SetTokenType( to.GetSizeOnStackDWords() == 1 ? ttUInt : ttUInt64 );
|
|
else
|
|
to.SetTokenType( to.GetSizeOnStackDWords() == 1 ? ttInt : ttInt64 );
|
|
ImplicitConversion(rctx, to, node, asIC_IMPLICIT_CONV, true);
|
|
reservedVariables.SetLength(l);
|
|
if( rctx->type.dataType != to )
|
|
{
|
|
asCString str;
|
|
str.Format(TXT_NO_CONVERSION_s_TO_s, rctx->type.dataType.Format(outFunc->nameSpace).AddressOf(), lctx->type.dataType.Format(outFunc->nameSpace).AddressOf());
|
|
Error(str, node);
|
|
}
|
|
|
|
bool isConstant = lctx->type.isConstant && rctx->type.isConstant;
|
|
|
|
if( !isConstant )
|
|
{
|
|
ConvertToVariableNotIn(lctx, rctx);
|
|
ConvertToVariableNotIn(rctx, lctx);
|
|
ReleaseTemporaryVariable(lctx->type, &lctx->bc);
|
|
ReleaseTemporaryVariable(rctx->type, &rctx->bc);
|
|
|
|
if( op == ttAndAssign || op == ttOrAssign || op == ttXorAssign )
|
|
{
|
|
// Compound assignments execute the right hand value first
|
|
MergeExprBytecode(ctx, rctx);
|
|
MergeExprBytecode(ctx, lctx);
|
|
}
|
|
else
|
|
{
|
|
MergeExprBytecode(ctx, lctx);
|
|
MergeExprBytecode(ctx, rctx);
|
|
}
|
|
ProcessDeferredParams(ctx);
|
|
|
|
asEBCInstr instruction = asBC_BAND;
|
|
if( lctx->type.dataType.GetSizeInMemoryDWords() == 1 )
|
|
{
|
|
if( op == ttAmp || op == ttAndAssign )
|
|
instruction = asBC_BAND;
|
|
else if( op == ttBitOr || op == ttOrAssign )
|
|
instruction = asBC_BOR;
|
|
else if( op == ttBitXor || op == ttXorAssign )
|
|
instruction = asBC_BXOR;
|
|
}
|
|
else
|
|
{
|
|
if( op == ttAmp || op == ttAndAssign )
|
|
instruction = asBC_BAND64;
|
|
else if( op == ttBitOr || op == ttOrAssign )
|
|
instruction = asBC_BOR64;
|
|
else if( op == ttBitXor || op == ttXorAssign )
|
|
instruction = asBC_BXOR64;
|
|
}
|
|
|
|
// Do the operation
|
|
int a = AllocateVariable(lctx->type.dataType, true);
|
|
int b = lctx->type.stackOffset;
|
|
int c = rctx->type.stackOffset;
|
|
|
|
ctx->bc.InstrW_W_W(instruction, a, b, c);
|
|
|
|
ctx->type.SetVariable(lctx->type.dataType, a, true);
|
|
}
|
|
else
|
|
{
|
|
if( lctx->type.dataType.GetSizeInMemoryDWords() == 2 )
|
|
{
|
|
asQWORD v = 0;
|
|
if( op == ttAmp )
|
|
v = lctx->type.GetConstantQW() & rctx->type.GetConstantQW();
|
|
else if( op == ttBitOr )
|
|
v = lctx->type.GetConstantQW() | rctx->type.GetConstantQW();
|
|
else if( op == ttBitXor )
|
|
v = lctx->type.GetConstantQW() ^ rctx->type.GetConstantQW();
|
|
|
|
// Remember the result
|
|
ctx->type.SetConstantQW(lctx->type.dataType, v);
|
|
}
|
|
else
|
|
{
|
|
asDWORD v = 0;
|
|
if( op == ttAmp )
|
|
v = lctx->type.GetConstantDW() & rctx->type.GetConstantDW();
|
|
else if( op == ttBitOr )
|
|
v = lctx->type.GetConstantDW() | rctx->type.GetConstantDW();
|
|
else if( op == ttBitXor )
|
|
v = lctx->type.GetConstantDW() ^ rctx->type.GetConstantDW();
|
|
|
|
// Remember the result
|
|
ctx->type.SetConstantDW(lctx->type.dataType, v);
|
|
}
|
|
}
|
|
}
|
|
else if( op == ttBitShiftLeft || op == ttShiftLeftAssign ||
|
|
op == ttBitShiftRight || op == ttShiftRightLAssign ||
|
|
op == ttBitShiftRightArith || op == ttShiftRightAAssign )
|
|
{
|
|
// Don't permit object to primitive conversion, since we don't know which integer type is the correct one
|
|
// Also do not permit float/double to be implicitly converted to integer in this case
|
|
if( lctx->type.dataType.IsObject() || lctx->type.dataType.IsFloatType() || lctx->type.dataType.IsDoubleType() )
|
|
{
|
|
asCString str;
|
|
str.Format(TXT_ILLEGAL_OPERATION_ON_s, lctx->type.dataType.Format(outFunc->nameSpace).AddressOf());
|
|
Error(str, node);
|
|
|
|
// Set an integer value and allow the compiler to continue
|
|
ctx->type.SetConstantDW(asCDataType::CreatePrimitive(ttInt, true), 0);
|
|
return;
|
|
}
|
|
|
|
// Convert left hand operand to integer if it's not already one
|
|
asCDataType to = lctx->type.dataType;
|
|
if( lctx->type.dataType.IsUnsignedType() &&
|
|
lctx->type.dataType.GetSizeInMemoryBytes() < 4 )
|
|
{
|
|
// Upgrade to 32bit
|
|
to = asCDataType::CreatePrimitive(ttUInt, false);
|
|
}
|
|
else if( !lctx->type.dataType.IsUnsignedType() )
|
|
{
|
|
if (lctx->type.dataType.GetSizeInMemoryDWords() == 2)
|
|
to = asCDataType::CreatePrimitive(ttInt64, false);
|
|
else
|
|
to = asCDataType::CreatePrimitive(ttInt, false);
|
|
}
|
|
|
|
// Do the actual conversion
|
|
int l = int(reservedVariables.GetLength());
|
|
rctx->bc.GetVarsUsed(reservedVariables);
|
|
ImplicitConversion(lctx, to, node, asIC_IMPLICIT_CONV, true);
|
|
reservedVariables.SetLength(l);
|
|
|
|
// Verify that the conversion was successful
|
|
if( lctx->type.dataType != to )
|
|
{
|
|
asCString str;
|
|
str.Format(TXT_NO_CONVERSION_s_TO_s, lctx->type.dataType.Format(outFunc->nameSpace).AddressOf(), to.Format(outFunc->nameSpace).AddressOf());
|
|
Error(str, node);
|
|
}
|
|
|
|
// Right operand must be 32bit uint
|
|
l = int(reservedVariables.GetLength());
|
|
lctx->bc.GetVarsUsed(reservedVariables);
|
|
ImplicitConversion(rctx, asCDataType::CreatePrimitive(ttUInt, true), node, asIC_IMPLICIT_CONV, true);
|
|
reservedVariables.SetLength(l);
|
|
if( !rctx->type.dataType.IsUnsignedType() )
|
|
{
|
|
asCString str;
|
|
str.Format(TXT_NO_CONVERSION_s_TO_s, rctx->type.dataType.Format(outFunc->nameSpace).AddressOf(), "uint");
|
|
Error(str, node);
|
|
}
|
|
|
|
bool isConstant = lctx->type.isConstant && rctx->type.isConstant;
|
|
|
|
if( !isConstant )
|
|
{
|
|
ConvertToVariableNotIn(lctx, rctx);
|
|
ConvertToVariableNotIn(rctx, lctx);
|
|
ReleaseTemporaryVariable(lctx->type, &lctx->bc);
|
|
ReleaseTemporaryVariable(rctx->type, &rctx->bc);
|
|
|
|
if( op == ttShiftLeftAssign || op == ttShiftRightLAssign || op == ttShiftRightAAssign )
|
|
{
|
|
// Compound assignments execute the right hand value first
|
|
MergeExprBytecode(ctx, rctx);
|
|
MergeExprBytecode(ctx, lctx);
|
|
}
|
|
else
|
|
{
|
|
MergeExprBytecode(ctx, lctx);
|
|
MergeExprBytecode(ctx, rctx);
|
|
}
|
|
ProcessDeferredParams(ctx);
|
|
|
|
asEBCInstr instruction = asBC_BSLL;
|
|
if( lctx->type.dataType.GetSizeInMemoryDWords() == 1 )
|
|
{
|
|
if( op == ttBitShiftLeft || op == ttShiftLeftAssign )
|
|
instruction = asBC_BSLL;
|
|
else if( op == ttBitShiftRight || op == ttShiftRightLAssign )
|
|
instruction = asBC_BSRL;
|
|
else if( op == ttBitShiftRightArith || op == ttShiftRightAAssign )
|
|
instruction = asBC_BSRA;
|
|
}
|
|
else
|
|
{
|
|
if( op == ttBitShiftLeft || op == ttShiftLeftAssign )
|
|
instruction = asBC_BSLL64;
|
|
else if( op == ttBitShiftRight || op == ttShiftRightLAssign )
|
|
instruction = asBC_BSRL64;
|
|
else if( op == ttBitShiftRightArith || op == ttShiftRightAAssign )
|
|
instruction = asBC_BSRA64;
|
|
}
|
|
|
|
// Do the operation
|
|
int a = AllocateVariable(lctx->type.dataType, true);
|
|
int b = lctx->type.stackOffset;
|
|
int c = rctx->type.stackOffset;
|
|
|
|
ctx->bc.InstrW_W_W(instruction, a, b, c);
|
|
|
|
ctx->type.SetVariable(lctx->type.dataType, a, true);
|
|
}
|
|
else
|
|
{
|
|
if( lctx->type.dataType.GetSizeInMemoryDWords() == 1 )
|
|
{
|
|
asDWORD v = 0;
|
|
if( op == ttBitShiftLeft )
|
|
v = lctx->type.GetConstantDW() << rctx->type.GetConstantDW();
|
|
else if( op == ttBitShiftRight )
|
|
v = lctx->type.GetConstantDW() >> rctx->type.GetConstantDW();
|
|
else if( op == ttBitShiftRightArith )
|
|
v = int(lctx->type.GetConstantDW()) >> rctx->type.GetConstantDW();
|
|
|
|
ctx->type.SetConstantDW(lctx->type.dataType, v);
|
|
}
|
|
else
|
|
{
|
|
asQWORD v = 0;
|
|
if( op == ttBitShiftLeft )
|
|
v = lctx->type.GetConstantQW() << rctx->type.GetConstantDW();
|
|
else if( op == ttBitShiftRight )
|
|
v = lctx->type.GetConstantQW() >> rctx->type.GetConstantDW();
|
|
else if( op == ttBitShiftRightArith )
|
|
v = asINT64(lctx->type.GetConstantQW()) >> rctx->type.GetConstantDW();
|
|
|
|
ctx->type.SetConstantQW(lctx->type.dataType, v);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void asCCompiler::CompileComparisonOperator(asCScriptNode *node, asCExprContext *lctx, asCExprContext *rctx, asCExprContext *ctx, eTokenType op)
|
|
{
|
|
// Both operands must be of the same type
|
|
|
|
// If either operand is a non-primitive then first convert them to the best number type
|
|
if( !lctx->type.dataType.IsPrimitive() )
|
|
{
|
|
int l = int(reservedVariables.GetLength());
|
|
rctx->bc.GetVarsUsed(reservedVariables);
|
|
ImplicitConvObjectToBestMathType(lctx, node);
|
|
reservedVariables.SetLength(l);
|
|
}
|
|
if( !rctx->type.dataType.IsPrimitive() )
|
|
{
|
|
int l = int(reservedVariables.GetLength());
|
|
lctx->bc.GetVarsUsed(reservedVariables);
|
|
ImplicitConvObjectToBestMathType(rctx, node);
|
|
reservedVariables.SetLength(l);
|
|
}
|
|
|
|
// Implicitly convert the operands to matching types
|
|
asCDataType to;
|
|
if( lctx->type.dataType.IsDoubleType() || rctx->type.dataType.IsDoubleType() )
|
|
to.SetTokenType(ttDouble);
|
|
else if( lctx->type.dataType.IsFloatType() || rctx->type.dataType.IsFloatType() )
|
|
to.SetTokenType(ttFloat);
|
|
else if( lctx->type.dataType.GetSizeInMemoryDWords() == 2 || rctx->type.dataType.GetSizeInMemoryDWords() == 2 )
|
|
{
|
|
// Convert to int64 if both are signed or if one is non-constant and signed
|
|
if( (lctx->type.dataType.IsIntegerType() && !lctx->type.isConstant) ||
|
|
(rctx->type.dataType.IsIntegerType() && !rctx->type.isConstant) )
|
|
to.SetTokenType(ttInt64);
|
|
else if( lctx->type.dataType.IsUnsignedType() || rctx->type.dataType.IsUnsignedType() )
|
|
to.SetTokenType(ttUInt64);
|
|
else
|
|
to.SetTokenType(ttInt64);
|
|
}
|
|
else
|
|
{
|
|
// Convert to int32 if both are signed or if one is non-constant and signed
|
|
if( (lctx->type.dataType.IsIntegerType() && !lctx->type.isConstant) ||
|
|
(rctx->type.dataType.IsIntegerType() && !rctx->type.isConstant) )
|
|
to.SetTokenType(ttInt);
|
|
else if( lctx->type.dataType.IsUnsignedType() || rctx->type.dataType.IsUnsignedType() )
|
|
to.SetTokenType(ttUInt);
|
|
else if( lctx->type.dataType.IsBooleanType() || rctx->type.dataType.IsBooleanType() )
|
|
to.SetTokenType(ttBool);
|
|
else
|
|
to.SetTokenType(ttInt);
|
|
}
|
|
|
|
// If doing an operation with double constant and float variable, the constant should be converted to float
|
|
if( (lctx->type.isConstant && lctx->type.dataType.IsDoubleType() && !rctx->type.isConstant && rctx->type.dataType.IsFloatType()) ||
|
|
(rctx->type.isConstant && rctx->type.dataType.IsDoubleType() && !lctx->type.isConstant && lctx->type.dataType.IsFloatType()) )
|
|
to.SetTokenType(ttFloat);
|
|
|
|
asASSERT( to.GetTokenType() != ttUnrecognizedToken );
|
|
|
|
// Do we have a mismatch between the sign of the operand?
|
|
bool signMismatch = false;
|
|
for( int n = 0; !signMismatch && n < 2; n++ )
|
|
{
|
|
asCExprContext *opCtx = n ? rctx : lctx;
|
|
|
|
if( opCtx->type.dataType.IsUnsignedType() != to.IsUnsignedType() )
|
|
{
|
|
// We have a mismatch, unless the value is a literal constant and the conversion won't affect its value
|
|
signMismatch = true;
|
|
if( opCtx->type.isConstant )
|
|
{
|
|
if( opCtx->type.dataType.GetTokenType() == ttUInt64 || opCtx->type.dataType.GetTokenType() == ttInt64 )
|
|
{
|
|
if( !(opCtx->type.GetConstantQW() & (asQWORD(1)<<63)) )
|
|
signMismatch = false;
|
|
}
|
|
else if(opCtx->type.dataType.GetTokenType() == ttUInt || opCtx->type.dataType.GetTokenType() == ttInt || opCtx->type.dataType.IsEnumType() )
|
|
{
|
|
if( !(opCtx->type.GetConstantDW() & (1<<31)) )
|
|
signMismatch = false;
|
|
}
|
|
else if (opCtx->type.dataType.GetTokenType() == ttUInt16 || opCtx->type.dataType.GetTokenType() == ttInt16)
|
|
{
|
|
if (!(opCtx->type.GetConstantW() & (1 << 15)))
|
|
signMismatch = false;
|
|
}
|
|
else if (opCtx->type.dataType.GetTokenType() == ttUInt8 || opCtx->type.dataType.GetTokenType() == ttInt8)
|
|
{
|
|
if (!(opCtx->type.GetConstantB() & (1 << 7)))
|
|
signMismatch = false;
|
|
}
|
|
|
|
// It's not necessary to check for floats or double, because if
|
|
// it was then the types for the conversion will never be unsigned
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check for signed/unsigned mismatch
|
|
if( signMismatch )
|
|
Warning(TXT_SIGNED_UNSIGNED_MISMATCH, node);
|
|
|
|
// Attempt to resolve ambiguous enumerations
|
|
if( lctx->type.dataType.IsEnumType() && rctx->enumValue != "" )
|
|
ImplicitConversion(rctx, lctx->type.dataType, node, asIC_IMPLICIT_CONV);
|
|
else if( rctx->type.dataType.IsEnumType() && lctx->enumValue != "" )
|
|
ImplicitConversion(lctx, rctx->type.dataType, node, asIC_IMPLICIT_CONV);
|
|
|
|
// Do the actual conversion
|
|
int l = int(reservedVariables.GetLength());
|
|
rctx->bc.GetVarsUsed(reservedVariables);
|
|
|
|
if( lctx->type.dataType.IsReference() )
|
|
ConvertToVariable(lctx);
|
|
if( rctx->type.dataType.IsReference() )
|
|
ConvertToVariable(rctx);
|
|
|
|
ImplicitConversion(lctx, to, node, asIC_IMPLICIT_CONV);
|
|
ImplicitConversion(rctx, to, node, asIC_IMPLICIT_CONV);
|
|
reservedVariables.SetLength(l);
|
|
|
|
// Verify that the conversion was successful
|
|
bool ok = true;
|
|
if( !lctx->type.dataType.IsEqualExceptConst(to) )
|
|
{
|
|
asCString str;
|
|
str.Format(TXT_NO_CONVERSION_s_TO_s, lctx->type.dataType.Format(outFunc->nameSpace).AddressOf(), to.Format(outFunc->nameSpace).AddressOf());
|
|
Error(str, node);
|
|
ok = false;
|
|
}
|
|
|
|
if( !rctx->type.dataType.IsEqualExceptConst(to) )
|
|
{
|
|
asCString str;
|
|
str.Format(TXT_NO_CONVERSION_s_TO_s, rctx->type.dataType.Format(outFunc->nameSpace).AddressOf(), to.Format(outFunc->nameSpace).AddressOf());
|
|
Error(str, node);
|
|
ok = false;
|
|
}
|
|
|
|
if( !ok )
|
|
{
|
|
// It wasn't possible to get two valid operands, so we just return
|
|
// a boolean result and let the compiler continue.
|
|
#if AS_SIZEOF_BOOL == 1
|
|
ctx->type.SetConstantB(asCDataType::CreatePrimitive(ttBool, true), true);
|
|
#else
|
|
ctx->type.SetConstantDW(asCDataType::CreatePrimitive(ttBool, true), true);
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
bool isConstant = lctx->type.isConstant && rctx->type.isConstant;
|
|
|
|
if( op == ttUnrecognizedToken )
|
|
op = node->tokenType;
|
|
|
|
if( !isConstant )
|
|
{
|
|
if( to.IsBooleanType() )
|
|
{
|
|
if( op == ttEqual || op == ttNotEqual )
|
|
{
|
|
// Must convert to temporary variable, because we are changing the value before comparison
|
|
ConvertToTempVariableNotIn(lctx, rctx);
|
|
ConvertToTempVariableNotIn(rctx, lctx);
|
|
ReleaseTemporaryVariable(lctx->type, &lctx->bc);
|
|
ReleaseTemporaryVariable(rctx->type, &rctx->bc);
|
|
|
|
// Make sure they are equal if not false
|
|
lctx->bc.InstrWORD(asBC_NOT, lctx->type.stackOffset);
|
|
rctx->bc.InstrWORD(asBC_NOT, rctx->type.stackOffset);
|
|
|
|
MergeExprBytecode(ctx, lctx);
|
|
MergeExprBytecode(ctx, rctx);
|
|
ProcessDeferredParams(ctx);
|
|
|
|
int a = AllocateVariable(asCDataType::CreatePrimitive(ttBool, true), true);
|
|
int b = lctx->type.stackOffset;
|
|
int c = rctx->type.stackOffset;
|
|
|
|
if( op == ttEqual )
|
|
{
|
|
ctx->bc.InstrW_W(asBC_CMPi,b,c);
|
|
ctx->bc.Instr(asBC_TZ);
|
|
ctx->bc.InstrSHORT(asBC_CpyRtoV4, (short)a);
|
|
}
|
|
else if( op == ttNotEqual )
|
|
{
|
|
ctx->bc.InstrW_W(asBC_CMPi,b,c);
|
|
ctx->bc.Instr(asBC_TNZ);
|
|
ctx->bc.InstrSHORT(asBC_CpyRtoV4, (short)a);
|
|
}
|
|
|
|
ctx->type.SetVariable(asCDataType::CreatePrimitive(ttBool, true), a, true);
|
|
}
|
|
else
|
|
{
|
|
// TODO: Use TXT_ILLEGAL_OPERATION_ON
|
|
Error(TXT_ILLEGAL_OPERATION, node);
|
|
#if AS_SIZEOF_BOOL == 1
|
|
ctx->type.SetConstantB(asCDataType::CreatePrimitive(ttBool, true), 0);
|
|
#else
|
|
ctx->type.SetConstantDW(asCDataType::CreatePrimitive(ttBool, true), 0);
|
|
#endif
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ConvertToVariableNotIn(lctx, rctx);
|
|
ConvertToVariableNotIn(rctx, lctx);
|
|
ReleaseTemporaryVariable(lctx->type, &lctx->bc);
|
|
ReleaseTemporaryVariable(rctx->type, &rctx->bc);
|
|
|
|
MergeExprBytecode(ctx, lctx);
|
|
MergeExprBytecode(ctx, rctx);
|
|
ProcessDeferredParams(ctx);
|
|
|
|
asEBCInstr iCmp = asBC_CMPi, iT = asBC_TZ;
|
|
|
|
if( lctx->type.dataType.IsIntegerType() && lctx->type.dataType.GetSizeInMemoryDWords() == 1 )
|
|
iCmp = asBC_CMPi;
|
|
else if( lctx->type.dataType.IsUnsignedType() && lctx->type.dataType.GetSizeInMemoryDWords() == 1 )
|
|
iCmp = asBC_CMPu;
|
|
else if( lctx->type.dataType.IsIntegerType() && lctx->type.dataType.GetSizeInMemoryDWords() == 2 )
|
|
iCmp = asBC_CMPi64;
|
|
else if( lctx->type.dataType.IsUnsignedType() && lctx->type.dataType.GetSizeInMemoryDWords() == 2 )
|
|
iCmp = asBC_CMPu64;
|
|
else if( lctx->type.dataType.IsFloatType() )
|
|
iCmp = asBC_CMPf;
|
|
else if( lctx->type.dataType.IsDoubleType() )
|
|
iCmp = asBC_CMPd;
|
|
else
|
|
asASSERT(false);
|
|
|
|
if( op == ttEqual )
|
|
iT = asBC_TZ;
|
|
else if( op == ttNotEqual )
|
|
iT = asBC_TNZ;
|
|
else if( op == ttLessThan )
|
|
iT = asBC_TS;
|
|
else if( op == ttLessThanOrEqual )
|
|
iT = asBC_TNP;
|
|
else if( op == ttGreaterThan )
|
|
iT = asBC_TP;
|
|
else if( op == ttGreaterThanOrEqual )
|
|
iT = asBC_TNS;
|
|
|
|
int a = AllocateVariable(asCDataType::CreatePrimitive(ttBool, true), true);
|
|
int b = lctx->type.stackOffset;
|
|
int c = rctx->type.stackOffset;
|
|
|
|
ctx->bc.InstrW_W(iCmp, b, c);
|
|
ctx->bc.Instr(iT);
|
|
ctx->bc.InstrSHORT(asBC_CpyRtoV4, (short)a);
|
|
|
|
ctx->type.SetVariable(asCDataType::CreatePrimitive(ttBool, true), a, true);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( to.IsBooleanType() )
|
|
{
|
|
if( op == ttEqual || op == ttNotEqual )
|
|
{
|
|
asDWORD lv, rv;
|
|
#if AS_SIZEOF_BOOL == 1
|
|
lv = lctx->type.GetConstantB();
|
|
rv = rctx->type.GetConstantB();
|
|
#else
|
|
lv = lctx->type.GetConstantDW();
|
|
rv = rctx->type.GetConstantDW();
|
|
#endif
|
|
|
|
// Make sure they are equal if not false
|
|
if (lv != 0) lv = VALUE_OF_BOOLEAN_TRUE;
|
|
if (rv != 0) rv = VALUE_OF_BOOLEAN_TRUE;
|
|
|
|
asDWORD v = 0;
|
|
if (op == ttEqual)
|
|
v = (lv == rv) ? VALUE_OF_BOOLEAN_TRUE : 0;
|
|
else if (op == ttNotEqual)
|
|
v = (lv != rv) ? VALUE_OF_BOOLEAN_TRUE : 0;
|
|
|
|
#if AS_SIZEOF_BOOL == 1
|
|
ctx->type.SetConstantB(asCDataType::CreatePrimitive(ttBool, true), (asBYTE)v);
|
|
#else
|
|
ctx->type.SetConstantDW(asCDataType::CreatePrimitive(ttBool, true), v);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
// TODO: Use TXT_ILLEGAL_OPERATION_ON
|
|
Error(TXT_ILLEGAL_OPERATION, node);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int i = 0;
|
|
if( lctx->type.dataType.IsIntegerType() && lctx->type.dataType.GetSizeInMemoryDWords() == 1 )
|
|
{
|
|
int v = int(lctx->type.GetConstantDW()) - int(rctx->type.GetConstantDW());
|
|
if( v < 0 ) i = -1;
|
|
if( v > 0 ) i = 1;
|
|
}
|
|
else if( lctx->type.dataType.IsUnsignedType() && lctx->type.dataType.GetSizeInMemoryDWords() == 1 )
|
|
{
|
|
asDWORD v1 = lctx->type.GetConstantDW();
|
|
asDWORD v2 = rctx->type.GetConstantDW();
|
|
if( v1 < v2 ) i = -1;
|
|
if( v1 > v2 ) i = 1;
|
|
}
|
|
else if( lctx->type.dataType.IsIntegerType() && lctx->type.dataType.GetSizeInMemoryDWords() == 2 )
|
|
{
|
|
asINT64 v = asINT64(lctx->type.GetConstantQW()) - asINT64(rctx->type.GetConstantQW());
|
|
if( v < 0 ) i = -1;
|
|
if( v > 0 ) i = 1;
|
|
}
|
|
else if( lctx->type.dataType.IsUnsignedType() && lctx->type.dataType.GetSizeInMemoryDWords() == 2 )
|
|
{
|
|
asQWORD v1 = lctx->type.GetConstantQW();
|
|
asQWORD v2 = rctx->type.GetConstantQW();
|
|
if( v1 < v2 ) i = -1;
|
|
if( v1 > v2 ) i = 1;
|
|
}
|
|
else if( lctx->type.dataType.IsFloatType() )
|
|
{
|
|
float v = lctx->type.GetConstantF() - rctx->type.GetConstantF();
|
|
if( v < 0 ) i = -1;
|
|
if( v > 0 ) i = 1;
|
|
}
|
|
else if( lctx->type.dataType.IsDoubleType() )
|
|
{
|
|
double v = lctx->type.GetConstantD() - rctx->type.GetConstantD();
|
|
if( v < 0 ) i = -1;
|
|
if( v > 0 ) i = 1;
|
|
}
|
|
|
|
|
|
if( op == ttEqual )
|
|
i = (i == 0 ? VALUE_OF_BOOLEAN_TRUE : 0);
|
|
else if( op == ttNotEqual )
|
|
i = (i != 0 ? VALUE_OF_BOOLEAN_TRUE : 0);
|
|
else if( op == ttLessThan )
|
|
i = (i < 0 ? VALUE_OF_BOOLEAN_TRUE : 0);
|
|
else if( op == ttLessThanOrEqual )
|
|
i = (i <= 0 ? VALUE_OF_BOOLEAN_TRUE : 0);
|
|
else if( op == ttGreaterThan )
|
|
i = (i > 0 ? VALUE_OF_BOOLEAN_TRUE : 0);
|
|
else if( op == ttGreaterThanOrEqual )
|
|
i = (i >= 0 ? VALUE_OF_BOOLEAN_TRUE : 0);
|
|
|
|
#if AS_SIZEOF_BOOL == 1
|
|
ctx->type.SetConstantB(asCDataType::CreatePrimitive(ttBool, true), (asBYTE)i);
|
|
#else
|
|
ctx->type.SetConstantDW(asCDataType::CreatePrimitive(ttBool, true), i);
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
void asCCompiler::PushVariableOnStack(asCExprContext *ctx, bool asReference)
|
|
{
|
|
// Put the result on the stack
|
|
if( asReference )
|
|
{
|
|
ctx->bc.InstrSHORT(asBC_PSF, ctx->type.stackOffset);
|
|
ctx->type.dataType.MakeReference(true);
|
|
}
|
|
else
|
|
{
|
|
if( ctx->type.dataType.GetSizeInMemoryDWords() == 1 )
|
|
ctx->bc.InstrSHORT(asBC_PshV4, ctx->type.stackOffset);
|
|
else
|
|
ctx->bc.InstrSHORT(asBC_PshV8, ctx->type.stackOffset);
|
|
}
|
|
}
|
|
|
|
void asCCompiler::CompileBooleanOperator(asCScriptNode *node, asCExprContext *lctx, asCExprContext *rctx, asCExprContext *ctx, eTokenType op)
|
|
{
|
|
// Both operands must be booleans
|
|
asCDataType to;
|
|
to.SetTokenType(ttBool);
|
|
|
|
// Do the actual conversion
|
|
int l = int(reservedVariables.GetLength());
|
|
rctx->bc.GetVarsUsed(reservedVariables);
|
|
lctx->bc.GetVarsUsed(reservedVariables);
|
|
|
|
// Allow value types to be converted to bool using 'bool opImplConv()'
|
|
if( lctx->type.dataType.GetTypeInfo() && (lctx->type.dataType.GetTypeInfo()->GetFlags() & asOBJ_VALUE) )
|
|
ImplicitConversion(lctx, to, node, asIC_IMPLICIT_CONV);
|
|
if( rctx->type.dataType.GetTypeInfo() && (rctx->type.dataType.GetTypeInfo()->GetFlags() & asOBJ_VALUE) )
|
|
ImplicitConversion(rctx, to, node, asIC_IMPLICIT_CONV);
|
|
reservedVariables.SetLength(l);
|
|
|
|
// Verify that the conversion was successful
|
|
if( !lctx->type.dataType.IsBooleanType() )
|
|
{
|
|
asCString str;
|
|
str.Format(TXT_NO_CONVERSION_s_TO_s, lctx->type.dataType.Format(outFunc->nameSpace).AddressOf(), "bool");
|
|
Error(str, node);
|
|
// Force the conversion to allow compilation to proceed
|
|
lctx->type.SetConstantB(asCDataType::CreatePrimitive(ttBool, true), true);
|
|
}
|
|
|
|
if( !rctx->type.dataType.IsBooleanType() )
|
|
{
|
|
asCString str;
|
|
str.Format(TXT_NO_CONVERSION_s_TO_s, rctx->type.dataType.Format(outFunc->nameSpace).AddressOf(), "bool");
|
|
Error(str, node);
|
|
// Force the conversion to allow compilation to proceed
|
|
rctx->type.SetConstantB(asCDataType::CreatePrimitive(ttBool, true), true);
|
|
}
|
|
|
|
bool isConstant = lctx->type.isConstant && rctx->type.isConstant;
|
|
|
|
ctx->type.Set(asCDataType::CreatePrimitive(ttBool, true));
|
|
|
|
// What kind of operator is it?
|
|
if( op == ttUnrecognizedToken )
|
|
op = node->tokenType;
|
|
if( op == ttXor )
|
|
{
|
|
if( !isConstant )
|
|
{
|
|
// Must convert to temporary variable, because we are changing the value before comparison
|
|
ConvertToTempVariableNotIn(lctx, rctx);
|
|
ConvertToTempVariableNotIn(rctx, lctx);
|
|
ReleaseTemporaryVariable(lctx->type, &lctx->bc);
|
|
ReleaseTemporaryVariable(rctx->type, &rctx->bc);
|
|
|
|
// Make sure they are equal if not false
|
|
lctx->bc.InstrWORD(asBC_NOT, lctx->type.stackOffset);
|
|
rctx->bc.InstrWORD(asBC_NOT, rctx->type.stackOffset);
|
|
|
|
MergeExprBytecode(ctx, lctx);
|
|
MergeExprBytecode(ctx, rctx);
|
|
ProcessDeferredParams(ctx);
|
|
|
|
int a = AllocateVariable(ctx->type.dataType, true);
|
|
int b = lctx->type.stackOffset;
|
|
int c = rctx->type.stackOffset;
|
|
|
|
ctx->bc.InstrW_W_W(asBC_BXOR,a,b,c);
|
|
|
|
ctx->type.SetVariable(asCDataType::CreatePrimitive(ttBool, true), a, true);
|
|
}
|
|
else
|
|
{
|
|
// Make sure they are equal if not false
|
|
#if AS_SIZEOF_BOOL == 1
|
|
if( lctx->type.GetConstantB() != 0 ) lctx->type.SetConstantB(VALUE_OF_BOOLEAN_TRUE);
|
|
if( rctx->type.GetConstantB() != 0 ) rctx->type.SetConstantB(VALUE_OF_BOOLEAN_TRUE);
|
|
|
|
asBYTE v = 0;
|
|
v = lctx->type.GetConstantB() - rctx->type.GetConstantB();
|
|
if( v != 0 ) v = VALUE_OF_BOOLEAN_TRUE; else v = 0;
|
|
|
|
ctx->type.isConstant = true;
|
|
ctx->type.SetConstantB(v);
|
|
#else
|
|
if( lctx->type.GetConstantDW() != 0 ) lctx->type.SetConstantDW(VALUE_OF_BOOLEAN_TRUE);
|
|
if( rctx->type.GetConstantDW() != 0 ) rctx->type.SetConstantDW(VALUE_OF_BOOLEAN_TRUE);
|
|
|
|
asDWORD v = 0;
|
|
v = lctx->type.GetConstantDW() - rctx->type.GetConstantDW();
|
|
if( v != 0 ) v = VALUE_OF_BOOLEAN_TRUE; else v = 0;
|
|
|
|
ctx->type.isConstant = true;
|
|
ctx->type.SetConstantDW(v);
|
|
#endif
|
|
}
|
|
}
|
|
else if( op == ttAnd ||
|
|
op == ttOr )
|
|
{
|
|
if( !isConstant )
|
|
{
|
|
// If or-operator and first value is 1 the second value shouldn't be calculated
|
|
// if and-operator and first value is 0 the second value shouldn't be calculated
|
|
ConvertToVariable(lctx);
|
|
ReleaseTemporaryVariable(lctx->type, &lctx->bc);
|
|
MergeExprBytecode(ctx, lctx);
|
|
|
|
int offset = AllocateVariable(asCDataType::CreatePrimitive(ttBool, false), true);
|
|
|
|
int label1 = nextLabel++;
|
|
int label2 = nextLabel++;
|
|
|
|
ctx->bc.InstrSHORT(asBC_CpyVtoR4, lctx->type.stackOffset);
|
|
ctx->bc.Instr(asBC_ClrHi);
|
|
if( op == ttAnd )
|
|
{
|
|
ctx->bc.InstrDWORD(asBC_JNZ, label1);
|
|
ctx->bc.InstrW_DW(asBC_SetV4, (asWORD)offset, 0);
|
|
ctx->bc.InstrINT(asBC_JMP, label2);
|
|
}
|
|
else if( op == ttOr )
|
|
{
|
|
ctx->bc.InstrDWORD(asBC_JZ, label1);
|
|
#if AS_SIZEOF_BOOL == 1
|
|
ctx->bc.InstrSHORT_B(asBC_SetV1, (short)offset, VALUE_OF_BOOLEAN_TRUE);
|
|
#else
|
|
ctx->bc.InstrSHORT_DW(asBC_SetV4, (short)offset, VALUE_OF_BOOLEAN_TRUE);
|
|
#endif
|
|
ctx->bc.InstrINT(asBC_JMP, label2);
|
|
}
|
|
|
|
ctx->bc.Label((short)label1);
|
|
ConvertToVariable(rctx);
|
|
ReleaseTemporaryVariable(rctx->type, &rctx->bc);
|
|
rctx->bc.InstrW_W(asBC_CpyVtoV4, offset, rctx->type.stackOffset);
|
|
MergeExprBytecode(ctx, rctx);
|
|
ctx->bc.Label((short)label2);
|
|
|
|
ctx->type.SetVariable(asCDataType::CreatePrimitive(ttBool, false), offset, true);
|
|
}
|
|
else
|
|
{
|
|
#if AS_SIZEOF_BOOL == 1
|
|
asBYTE v = 0;
|
|
if( op == ttAnd )
|
|
v = lctx->type.GetConstantB() && rctx->type.GetConstantB();
|
|
else if( op == ttOr )
|
|
v = lctx->type.GetConstantB() || rctx->type.GetConstantB();
|
|
|
|
// Remember the result
|
|
ctx->type.isConstant = true;
|
|
ctx->type.SetConstantB(v);
|
|
#else
|
|
asDWORD v = 0;
|
|
if( op == ttAnd )
|
|
v = lctx->type.GetConstantDW() && rctx->type.GetConstantDW();
|
|
else if( op == ttOr )
|
|
v = lctx->type.GetConstantDW() || rctx->type.GetConstantDW();
|
|
|
|
// Remember the result
|
|
ctx->type.isConstant = true;
|
|
ctx->type.SetConstantDW(v);
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
void asCCompiler::CompileOperatorOnHandles(asCScriptNode *node, asCExprContext *lctx, asCExprContext *rctx, asCExprContext *ctx, eTokenType opToken)
|
|
{
|
|
// Process the property accessor as get
|
|
ProcessPropertyGetAccessor(lctx, node);
|
|
ProcessPropertyGetAccessor(rctx, node);
|
|
|
|
DetermineSingleFunc(lctx, node);
|
|
DetermineSingleFunc(rctx, node);
|
|
|
|
// Make sure lctx doesn't end up with a variable used in rctx
|
|
if( lctx->type.isTemporary && rctx->bc.IsVarUsed(lctx->type.stackOffset) )
|
|
{
|
|
asCArray<int> vars;
|
|
rctx->bc.GetVarsUsed(vars);
|
|
int offset = AllocateVariable(lctx->type.dataType, true);
|
|
rctx->bc.ExchangeVar(lctx->type.stackOffset, offset);
|
|
ReleaseTemporaryVariable(offset, 0);
|
|
}
|
|
|
|
if( opToken == ttUnrecognizedToken )
|
|
opToken = node->tokenType;
|
|
|
|
// Warn if not both operands are explicit handles or null handles
|
|
if( (opToken == ttEqual || opToken == ttNotEqual) &&
|
|
((!(lctx->type.isExplicitHandle || lctx->type.IsNullConstant()) && !(lctx->type.dataType.GetTypeInfo() && (lctx->type.dataType.GetTypeInfo()->flags & asOBJ_IMPLICIT_HANDLE))) ||
|
|
(!(rctx->type.isExplicitHandle || rctx->type.IsNullConstant()) && !(rctx->type.dataType.GetTypeInfo() && (rctx->type.dataType.GetTypeInfo()->flags & asOBJ_IMPLICIT_HANDLE)))) )
|
|
{
|
|
Warning(TXT_HANDLE_COMPARISON, node);
|
|
}
|
|
|
|
// If one of the operands is a value type used as handle, we should look for the opEquals method
|
|
if( ((lctx->type.dataType.GetTypeInfo() && (lctx->type.dataType.GetTypeInfo()->flags & asOBJ_ASHANDLE)) ||
|
|
(rctx->type.dataType.GetTypeInfo() && (rctx->type.dataType.GetTypeInfo()->flags & asOBJ_ASHANDLE))) &&
|
|
(opToken == ttEqual || opToken == ttIs ||
|
|
opToken == ttNotEqual || opToken == ttNotIs) )
|
|
{
|
|
// TODO: Should evaluate which of the two have the best match. If both have equal match, the first version should be used
|
|
// Find the matching opEquals method
|
|
int r = CompileOverloadedDualOperator2(node, "opEquals", lctx, rctx, true, ctx, true, asCDataType::CreatePrimitive(ttBool, false));
|
|
if( r == 0 )
|
|
{
|
|
// Try again by switching the order of the operands
|
|
r = CompileOverloadedDualOperator2(node, "opEquals", rctx, lctx, false, ctx, true, asCDataType::CreatePrimitive(ttBool, false));
|
|
}
|
|
|
|
if( r == 1 )
|
|
{
|
|
if( opToken == ttNotEqual || opToken == ttNotIs )
|
|
ctx->bc.InstrSHORT(asBC_NOT, ctx->type.stackOffset);
|
|
|
|
// Success, don't continue
|
|
return;
|
|
}
|
|
else if( r == 0 )
|
|
{
|
|
// Couldn't find opEquals method
|
|
Error(TXT_NO_APPROPRIATE_OPEQUALS, node);
|
|
}
|
|
|
|
// Compiler error, don't continue
|
|
#if AS_SIZEOF_BOOL == 1
|
|
ctx->type.SetConstantB(asCDataType::CreatePrimitive(ttBool, true), true);
|
|
#else
|
|
ctx->type.SetConstantDW(asCDataType::CreatePrimitive(ttBool, true), true);
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
|
|
// Implicitly convert null to the other type
|
|
asCDataType to;
|
|
if( lctx->type.IsNullConstant() )
|
|
to = rctx->type.dataType;
|
|
else if( rctx->type.IsNullConstant() )
|
|
to = lctx->type.dataType;
|
|
else
|
|
{
|
|
// Find a common base type
|
|
asCExprContext tmp(engine);
|
|
tmp.type = rctx->type;
|
|
ImplicitConversion(&tmp, lctx->type.dataType, 0, asIC_IMPLICIT_CONV, false);
|
|
if( tmp.type.dataType.GetTypeInfo() == lctx->type.dataType.GetTypeInfo() )
|
|
to = lctx->type.dataType;
|
|
else
|
|
to = rctx->type.dataType;
|
|
|
|
// Assume handle-to-const as it is not possible to convert handle-to-const to handle-to-non-const
|
|
to.MakeHandleToConst(true);
|
|
}
|
|
|
|
// Need to pop the value if it is a null constant
|
|
if( lctx->type.IsNullConstant() )
|
|
lctx->bc.Instr(asBC_PopPtr);
|
|
if( rctx->type.IsNullConstant() )
|
|
rctx->bc.Instr(asBC_PopPtr);
|
|
|
|
// Convert both sides to explicit handles
|
|
to.MakeHandle(true);
|
|
to.MakeReference(false);
|
|
|
|
if( !to.IsObjectHandle() )
|
|
{
|
|
// Compiler error, don't continue
|
|
Error(TXT_OPERANDS_MUST_BE_HANDLES, node);
|
|
#if AS_SIZEOF_BOOL == 1
|
|
ctx->type.SetConstantB(asCDataType::CreatePrimitive(ttBool, true), true);
|
|
#else
|
|
ctx->type.SetConstantDW(asCDataType::CreatePrimitive(ttBool, true), true);
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
// Do the conversion
|
|
ImplicitConversion(lctx, to, node, asIC_IMPLICIT_CONV);
|
|
ImplicitConversion(rctx, to, node, asIC_IMPLICIT_CONV);
|
|
|
|
// Both operands must be of the same type
|
|
|
|
// Verify that the conversion was successful
|
|
if( !lctx->type.dataType.IsEqualExceptConst(to) )
|
|
{
|
|
asCString str;
|
|
str.Format(TXT_NO_CONVERSION_s_TO_s, lctx->type.dataType.Format(outFunc->nameSpace).AddressOf(), to.Format(outFunc->nameSpace).AddressOf());
|
|
Error(str, node);
|
|
}
|
|
|
|
if( !rctx->type.dataType.IsEqualExceptConst(to) )
|
|
{
|
|
asCString str;
|
|
str.Format(TXT_NO_CONVERSION_s_TO_s, rctx->type.dataType.Format(outFunc->nameSpace).AddressOf(), to.Format(outFunc->nameSpace).AddressOf());
|
|
Error(str, node);
|
|
}
|
|
|
|
// Make sure it really is handles that are being compared
|
|
if( !lctx->type.dataType.IsObjectHandle() )
|
|
{
|
|
Error(TXT_OPERANDS_MUST_BE_HANDLES, node);
|
|
}
|
|
|
|
ctx->type.Set(asCDataType::CreatePrimitive(ttBool, true));
|
|
|
|
if( opToken == ttEqual || opToken == ttNotEqual || opToken == ttIs || opToken == ttNotIs )
|
|
{
|
|
// Make sure handles received as parameters by reference are copied to a local variable before the
|
|
// asBC_CmpPtr, so we don't end up comparing the reference to the handle instead of the handle itself
|
|
if( lctx->type.isVariable && !lctx->type.isTemporary && lctx->type.stackOffset <= 0 )
|
|
lctx->type.isVariable = false;
|
|
if( rctx->type.isVariable && !rctx->type.isTemporary && rctx->type.stackOffset <= 0 )
|
|
rctx->type.isVariable = false;
|
|
|
|
// TODO: runtime optimize: don't do REFCPY if not necessary
|
|
ConvertToVariableNotIn(lctx, rctx);
|
|
ConvertToVariable(rctx);
|
|
|
|
// Pop the pointers from the stack as they will not be used
|
|
lctx->bc.Instr(asBC_PopPtr);
|
|
rctx->bc.Instr(asBC_PopPtr);
|
|
|
|
MergeExprBytecode(ctx, lctx);
|
|
MergeExprBytecode(ctx, rctx);
|
|
|
|
int a = AllocateVariable(ctx->type.dataType, true);
|
|
int b = lctx->type.stackOffset;
|
|
int c = rctx->type.stackOffset;
|
|
|
|
ctx->bc.InstrW_W(asBC_CmpPtr, b, c);
|
|
|
|
if( opToken == ttEqual || opToken == ttIs )
|
|
ctx->bc.Instr(asBC_TZ);
|
|
else if( opToken == ttNotEqual || opToken == ttNotIs )
|
|
ctx->bc.Instr(asBC_TNZ);
|
|
|
|
ctx->bc.InstrSHORT(asBC_CpyRtoV4, (short)a);
|
|
|
|
ctx->type.SetVariable(asCDataType::CreatePrimitive(ttBool, true), a, true);
|
|
|
|
ReleaseTemporaryVariable(lctx->type, &ctx->bc);
|
|
ReleaseTemporaryVariable(rctx->type, &ctx->bc);
|
|
ProcessDeferredParams(ctx);
|
|
}
|
|
else
|
|
{
|
|
// TODO: Use TXT_ILLEGAL_OPERATION_ON
|
|
Error(TXT_ILLEGAL_OPERATION, node);
|
|
}
|
|
}
|
|
|
|
|
|
void asCCompiler::PerformFunctionCall(int funcId, asCExprContext *ctx, bool isConstructor, asCArray<asCExprContext*> *args, asCObjectType *objType, bool useVariable, int varOffset, int funcPtrVar)
|
|
{
|
|
asCScriptFunction *descr = builder->GetFunctionDescription(funcId);
|
|
|
|
// A shared object may not call non-shared functions
|
|
if( outFunc->IsShared() && !descr->IsShared() )
|
|
{
|
|
asCString msg;
|
|
msg.Format(TXT_SHARED_CANNOT_CALL_NON_SHARED_FUNC_s, descr->GetDeclarationStr().AddressOf());
|
|
Error(msg, ctx->exprNode);
|
|
}
|
|
|
|
// Check if the function is private or protected
|
|
if (descr->IsPrivate())
|
|
{
|
|
asCObjectType *type = descr->objectType;
|
|
if (type == 0 && descr->traits.GetTrait(asTRAIT_CONSTRUCTOR))
|
|
type = CastToObjectType(descr->returnType.GetTypeInfo());
|
|
|
|
asASSERT(type);
|
|
|
|
if( (type != outFunc->GetObjectType()) )
|
|
{
|
|
asCString msg;
|
|
msg.Format(TXT_PRIVATE_METHOD_CALL_s, descr->GetDeclarationStr().AddressOf());
|
|
Error(msg, ctx->exprNode);
|
|
}
|
|
}
|
|
else if (descr->IsProtected())
|
|
{
|
|
asCObjectType *type = descr->objectType;
|
|
if (type == 0 && descr->traits.GetTrait(asTRAIT_CONSTRUCTOR))
|
|
type = CastToObjectType(descr->returnType.GetTypeInfo());
|
|
|
|
asASSERT(type);
|
|
|
|
if (!(type == outFunc->objectType || (outFunc->objectType && outFunc->objectType->DerivesFrom(type))))
|
|
{
|
|
asCString msg;
|
|
msg.Format(TXT_PROTECTED_METHOD_CALL_s, descr->GetDeclarationStr().AddressOf());
|
|
Error(msg, ctx->exprNode);
|
|
}
|
|
}
|
|
|
|
int argSize = descr->GetSpaceNeededForArguments();
|
|
|
|
// If we're calling a class method we must make sure the object is guaranteed to stay
|
|
// alive throughout the call by holding on to a reference in a local variable. This must
|
|
// be done for any methods that return references, and any calls on script objects.
|
|
// Application registered objects are assumed to know to keep themselves alive even
|
|
// if the method doesn't return a reference.
|
|
if( !ctx->type.isRefSafe &&
|
|
descr->objectType &&
|
|
(ctx->type.dataType.IsObjectHandle() || ctx->type.dataType.SupportHandles()) &&
|
|
(descr->returnType.IsReference() || (ctx->type.dataType.GetTypeInfo()->GetFlags() & asOBJ_SCRIPT_OBJECT)) &&
|
|
!(ctx->type.isVariable || ctx->type.isTemporary) &&
|
|
!(ctx->type.dataType.GetTypeInfo()->GetFlags() & asOBJ_SCOPED) &&
|
|
!(ctx->type.dataType.GetTypeInfo()->GetFlags() & asOBJ_ASHANDLE) )
|
|
{
|
|
// TODO: runtime optimize: Avoid this for global variables, by storing a reference to the global variable once in a
|
|
// local variable and then refer to the same for each call. An alias for the global variable
|
|
// should be stored in the variable scope so that the compiler can find it. For loops and
|
|
// scopes that will always be executed, i.e. non-if scopes the alias should be stored in the
|
|
// higher scope to increase the probability of re-use.
|
|
|
|
int tempRef = AllocateVariable(ctx->type.dataType, true);
|
|
ctx->bc.InstrSHORT(asBC_PSF, (short)tempRef);
|
|
ctx->bc.InstrPTR(asBC_REFCPY, ctx->type.dataType.GetTypeInfo());
|
|
|
|
// Add the release of this reference as a deferred expression
|
|
asSDeferredParam deferred;
|
|
deferred.origExpr = 0;
|
|
deferred.argInOutFlags = asTM_INREF;
|
|
deferred.argNode = 0;
|
|
deferred.argType.SetVariable(ctx->type.dataType, tempRef, true);
|
|
ctx->deferredParams.PushLast(deferred);
|
|
|
|
// Forget the current type
|
|
ctx->type.SetDummy();
|
|
}
|
|
|
|
// Check if there is a need to add a hidden pointer for when the function returns an object by value
|
|
if( descr->DoesReturnOnStack() && !useVariable )
|
|
{
|
|
useVariable = true;
|
|
varOffset = AllocateVariable(descr->returnType, true);
|
|
|
|
// Push the pointer to the pre-allocated space for the return value
|
|
ctx->bc.InstrSHORT(asBC_PSF, short(varOffset));
|
|
|
|
if( descr->objectType )
|
|
{
|
|
// The object pointer is already on the stack, but should be the top
|
|
// one, so we need to swap the pointers in order to get the correct
|
|
ctx->bc.Instr(asBC_SwapPtr);
|
|
}
|
|
}
|
|
|
|
if( isConstructor )
|
|
{
|
|
// Sometimes the value types are allocated on the heap,
|
|
// which is when this way of constructing them is used.
|
|
|
|
asASSERT(useVariable == false);
|
|
|
|
if( (objType->flags & asOBJ_TEMPLATE) )
|
|
{
|
|
asASSERT( descr->funcType == asFUNC_SCRIPT );
|
|
|
|
// Find the id of the real constructor and not the generated stub
|
|
asUINT id = 0;
|
|
asDWORD *bc = descr->scriptData->byteCode.AddressOf();
|
|
while( bc )
|
|
{
|
|
if( (*(asBYTE*)bc) == asBC_CALLSYS )
|
|
{
|
|
id = asBC_INTARG(bc);
|
|
break;
|
|
}
|
|
bc += asBCTypeSize[asBCInfo[*(asBYTE*)bc].type];
|
|
}
|
|
|
|
asASSERT( id );
|
|
|
|
ctx->bc.InstrPTR(asBC_OBJTYPE, objType);
|
|
ctx->bc.Alloc(asBC_ALLOC, objType, id, argSize + AS_PTR_SIZE + AS_PTR_SIZE);
|
|
}
|
|
else
|
|
ctx->bc.Alloc(asBC_ALLOC, objType, descr->id, argSize+AS_PTR_SIZE);
|
|
|
|
// The instruction has already moved the returned object to the variable
|
|
ctx->type.Set(asCDataType::CreatePrimitive(ttVoid, false));
|
|
ctx->type.isLValue = false;
|
|
|
|
// Clean up arguments
|
|
if( args )
|
|
AfterFunctionCall(funcId, *args, ctx, false);
|
|
|
|
ProcessDeferredParams(ctx);
|
|
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
if( descr->objectType )
|
|
argSize += AS_PTR_SIZE;
|
|
|
|
// If the function returns an object by value the address of the location
|
|
// where the value should be stored is passed as an argument too
|
|
if( descr->DoesReturnOnStack() )
|
|
argSize += AS_PTR_SIZE;
|
|
|
|
// TODO: runtime optimize: If it is known that a class method cannot be overridden the call
|
|
// should be made with asBC_CALL as it is faster. Examples where this
|
|
// is known is for example finalled methods where the class doesn't derive
|
|
// from any other, or even non-finalled methods but where it is known
|
|
// at compile time the true type of the object. The first should be
|
|
// quite easy to determine, but the latter will be quite complex and possibly
|
|
// not worth it.
|
|
if( descr->funcType == asFUNC_IMPORTED )
|
|
ctx->bc.Call(asBC_CALLBND , descr->id, argSize);
|
|
// TODO: Maybe we need two different byte codes
|
|
else if( descr->funcType == asFUNC_INTERFACE || descr->funcType == asFUNC_VIRTUAL )
|
|
ctx->bc.Call(asBC_CALLINTF, descr->id, argSize);
|
|
else if( descr->funcType == asFUNC_SCRIPT )
|
|
ctx->bc.Call(asBC_CALL , descr->id, argSize);
|
|
else if( descr->funcType == asFUNC_SYSTEM )
|
|
{
|
|
// Check if we can use the faster asBC_Thiscall1 instruction, i.e. one of
|
|
// type &obj::func(int)
|
|
// type &obj::func(uint)
|
|
if( descr->GetObjectType() && descr->returnType.IsReference() &&
|
|
descr->parameterTypes.GetLength() == 1 &&
|
|
(descr->parameterTypes[0].IsIntegerType() || descr->parameterTypes[0].IsUnsignedType()) &&
|
|
descr->parameterTypes[0].GetSizeInMemoryBytes() == 4 &&
|
|
!descr->parameterTypes[0].IsReference() )
|
|
ctx->bc.Call(asBC_Thiscall1, descr->id, argSize);
|
|
else
|
|
ctx->bc.Call(asBC_CALLSYS , descr->id, argSize);
|
|
}
|
|
else if( descr->funcType == asFUNC_FUNCDEF )
|
|
ctx->bc.CallPtr(asBC_CallPtr, funcPtrVar, argSize);
|
|
}
|
|
|
|
if( (descr->returnType.IsObject() || descr->returnType.IsFuncdef()) && !descr->returnType.IsReference() )
|
|
{
|
|
int returnOffset = 0;
|
|
|
|
asCExprValue tmpExpr = ctx->type;
|
|
|
|
if( descr->DoesReturnOnStack() )
|
|
{
|
|
asASSERT( useVariable );
|
|
|
|
// The variable was allocated before the function was called
|
|
returnOffset = varOffset;
|
|
ctx->type.SetVariable(descr->returnType, returnOffset, true);
|
|
|
|
// The variable was initialized by the function, so we need to mark it as initialized here
|
|
ctx->bc.ObjInfo(varOffset, asOBJ_INIT);
|
|
}
|
|
else
|
|
{
|
|
if( useVariable )
|
|
{
|
|
// Use the given variable
|
|
returnOffset = varOffset;
|
|
ctx->type.SetVariable(descr->returnType, returnOffset, false);
|
|
}
|
|
else
|
|
{
|
|
// Allocate a temporary variable for the returned object
|
|
// The returned object will actually be allocated on the heap, so
|
|
// we must force the allocation of the variable to do the same
|
|
returnOffset = AllocateVariable(descr->returnType, true, !descr->returnType.IsObjectHandle());
|
|
ctx->type.SetVariable(descr->returnType, returnOffset, true);
|
|
}
|
|
|
|
// Move the pointer from the object register to the temporary variable
|
|
ctx->bc.InstrSHORT(asBC_STOREOBJ, (short)returnOffset);
|
|
}
|
|
|
|
ReleaseTemporaryVariable(tmpExpr, &ctx->bc);
|
|
|
|
ctx->type.dataType.MakeReference(IsVariableOnHeap(returnOffset));
|
|
ctx->type.isLValue = false; // It is a reference, but not an lvalue
|
|
|
|
// Clean up arguments
|
|
if( args )
|
|
AfterFunctionCall(funcId, *args, ctx, false);
|
|
|
|
ProcessDeferredParams(ctx);
|
|
|
|
ctx->bc.InstrSHORT(asBC_PSF, (short)returnOffset);
|
|
}
|
|
else if( descr->returnType.IsReference() )
|
|
{
|
|
asASSERT(useVariable == false);
|
|
|
|
// We cannot clean up the arguments yet, because the
|
|
// reference might be pointing to one of them.
|
|
if( args )
|
|
AfterFunctionCall(funcId, *args, ctx, true);
|
|
|
|
// Do not process the output parameters yet, because it
|
|
// might invalidate the returned reference
|
|
|
|
// If the context holds a variable that needs cleanup
|
|
// store it as a deferred parameter so it will be cleaned up
|
|
// afterwards.
|
|
if( ctx->type.isTemporary )
|
|
{
|
|
asSDeferredParam defer;
|
|
defer.argNode = 0;
|
|
defer.argType = ctx->type;
|
|
defer.argInOutFlags = asTM_INOUTREF;
|
|
defer.origExpr = 0;
|
|
ctx->deferredParams.PushLast(defer);
|
|
}
|
|
|
|
ctx->type.Set(descr->returnType);
|
|
if( !descr->returnType.IsPrimitive() )
|
|
{
|
|
ctx->bc.Instr(asBC_PshRPtr);
|
|
if( descr->returnType.IsObject() &&
|
|
!descr->returnType.IsObjectHandle() )
|
|
{
|
|
// We are getting the pointer to the object
|
|
// not a pointer to a object variable
|
|
ctx->type.dataType.MakeReference(false);
|
|
}
|
|
}
|
|
|
|
// A returned reference can be used as lvalue
|
|
ctx->type.isLValue = true;
|
|
}
|
|
else
|
|
{
|
|
asCExprValue tmpExpr = ctx->type;
|
|
|
|
if( descr->returnType.GetSizeInMemoryBytes() )
|
|
{
|
|
int offset;
|
|
if (useVariable)
|
|
offset = varOffset;
|
|
else
|
|
{
|
|
// Allocate a temporary variable to hold the value, but make sure
|
|
// the temporary variable isn't used in any of the deferred arguments
|
|
int l = int(reservedVariables.GetLength());
|
|
for (asUINT n = 0; args && n < args->GetLength(); n++)
|
|
{
|
|
asCExprContext *expr = (*args)[n]->origExpr;
|
|
if (expr)
|
|
expr->bc.GetVarsUsed(reservedVariables);
|
|
}
|
|
offset = AllocateVariable(descr->returnType, true);
|
|
reservedVariables.SetLength(l);
|
|
}
|
|
|
|
ctx->type.SetVariable(descr->returnType, offset, true);
|
|
|
|
// Move the value from the return register to the variable
|
|
if( descr->returnType.GetSizeOnStackDWords() == 1 )
|
|
ctx->bc.InstrSHORT(asBC_CpyRtoV4, (short)offset);
|
|
else if( descr->returnType.GetSizeOnStackDWords() == 2 )
|
|
ctx->bc.InstrSHORT(asBC_CpyRtoV8, (short)offset);
|
|
}
|
|
else
|
|
ctx->type.Set(descr->returnType);
|
|
|
|
ReleaseTemporaryVariable(tmpExpr, &ctx->bc);
|
|
|
|
ctx->type.isLValue = false;
|
|
|
|
// Clean up arguments
|
|
if( args )
|
|
AfterFunctionCall(funcId, *args, ctx, false);
|
|
|
|
ProcessDeferredParams(ctx);
|
|
}
|
|
}
|
|
|
|
// This only merges the bytecode, but doesn't modify the type of the final context
|
|
void asCCompiler::MergeExprBytecode(asCExprContext *before, asCExprContext *after)
|
|
{
|
|
before->bc.AddCode(&after->bc);
|
|
|
|
for( asUINT n = 0; n < after->deferredParams.GetLength(); n++ )
|
|
{
|
|
before->deferredParams.PushLast(after->deferredParams[n]);
|
|
after->deferredParams[n].origExpr = 0;
|
|
}
|
|
|
|
after->deferredParams.SetLength(0);
|
|
}
|
|
|
|
// This merges both bytecode and the type of the final context
|
|
void asCCompiler::MergeExprBytecodeAndType(asCExprContext *before, asCExprContext *after)
|
|
{
|
|
MergeExprBytecode(before, after);
|
|
|
|
before->Merge(after);
|
|
}
|
|
|
|
void asCCompiler::FilterConst(asCArray<int> &funcs, bool removeConst)
|
|
{
|
|
if( funcs.GetLength() == 0 ) return;
|
|
|
|
// This is only done for object methods
|
|
asCScriptFunction *desc = builder->GetFunctionDescription(funcs[0]);
|
|
if( !desc || desc->objectType == 0 ) return;
|
|
|
|
// Check if there are any non-const matches
|
|
asUINT n;
|
|
bool foundNonConst = false;
|
|
for( n = 0; n < funcs.GetLength(); n++ )
|
|
{
|
|
desc = builder->GetFunctionDescription(funcs[n]);
|
|
if( desc && desc->IsReadOnly() != removeConst )
|
|
{
|
|
foundNonConst = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( foundNonConst )
|
|
{
|
|
// Remove all const methods
|
|
for( n = 0; n < funcs.GetLength(); n++ )
|
|
{
|
|
desc = builder->GetFunctionDescription(funcs[n]);
|
|
if( desc && desc->IsReadOnly() == removeConst )
|
|
{
|
|
if( n == funcs.GetLength() - 1 )
|
|
funcs.PopLast();
|
|
else
|
|
funcs[n] = funcs.PopLast();
|
|
|
|
n--;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
asCExprValue::asCExprValue()
|
|
{
|
|
isTemporary = false;
|
|
stackOffset = 0;
|
|
isConstant = false;
|
|
isVariable = false;
|
|
isExplicitHandle = false;
|
|
qwordValue = 0;
|
|
isLValue = false;
|
|
isRefToLocal = false;
|
|
isRefSafe = false;
|
|
}
|
|
|
|
void asCExprValue::Set(const asCDataType &dt)
|
|
{
|
|
dataType = dt;
|
|
|
|
isTemporary = false;
|
|
stackOffset = 0;
|
|
isConstant = false;
|
|
isVariable = false;
|
|
isExplicitHandle = false;
|
|
qwordValue = 0;
|
|
isLValue = false;
|
|
isRefToLocal = false;
|
|
isRefSafe = false;
|
|
}
|
|
|
|
void asCExprValue::SetVariable(const asCDataType &in_dt, int in_stackOffset, bool in_isTemporary)
|
|
{
|
|
Set(in_dt);
|
|
|
|
this->isVariable = true;
|
|
this->isTemporary = in_isTemporary;
|
|
this->stackOffset = (short)in_stackOffset;
|
|
}
|
|
|
|
void asCExprValue::SetConstantQW(const asCDataType &dt, asQWORD value)
|
|
{
|
|
Set(dt);
|
|
|
|
isConstant = true;
|
|
SetConstantQW(value);
|
|
}
|
|
|
|
void asCExprValue::SetConstantDW(const asCDataType &dt, asDWORD value)
|
|
{
|
|
Set(dt);
|
|
|
|
isConstant = true;
|
|
SetConstantDW(value);
|
|
}
|
|
|
|
void asCExprValue::SetConstantB(const asCDataType &dt, asBYTE value)
|
|
{
|
|
Set(dt);
|
|
|
|
isConstant = true;
|
|
SetConstantB(value);
|
|
}
|
|
|
|
void asCExprValue::SetConstantW(const asCDataType &dt, asWORD value)
|
|
{
|
|
Set(dt);
|
|
|
|
isConstant = true;
|
|
SetConstantW(value);
|
|
}
|
|
|
|
void asCExprValue::SetConstantF(const asCDataType &dt, float value)
|
|
{
|
|
Set(dt);
|
|
|
|
isConstant = true;
|
|
SetConstantF(value);
|
|
}
|
|
|
|
void asCExprValue::SetConstantD(const asCDataType &dt, double value)
|
|
{
|
|
Set(dt);
|
|
|
|
isConstant = true;
|
|
SetConstantD(value);
|
|
}
|
|
|
|
void asCExprValue::SetConstantQW(asQWORD value)
|
|
{
|
|
asASSERT(dataType.GetSizeInMemoryBytes() == 8);
|
|
qwordValue = value;
|
|
}
|
|
|
|
void asCExprValue::SetConstantDW(asDWORD value)
|
|
{
|
|
asASSERT(dataType.GetSizeInMemoryBytes() == 4);
|
|
dwordValue = value;
|
|
}
|
|
|
|
void asCExprValue::SetConstantW(asWORD value)
|
|
{
|
|
asASSERT(dataType.GetSizeInMemoryBytes() == 2);
|
|
wordValue = value;
|
|
}
|
|
|
|
void asCExprValue::SetConstantB(asBYTE value)
|
|
{
|
|
asASSERT(dataType.GetSizeInMemoryBytes() == 1);
|
|
byteValue = value;
|
|
}
|
|
|
|
void asCExprValue::SetConstantF(float value)
|
|
{
|
|
asASSERT(dataType.GetSizeInMemoryBytes() == 4);
|
|
floatValue = value;
|
|
}
|
|
|
|
void asCExprValue::SetConstantD(double value)
|
|
{
|
|
asASSERT(dataType.GetSizeInMemoryBytes() == 8);
|
|
doubleValue = value;
|
|
}
|
|
|
|
asQWORD asCExprValue::GetConstantQW()
|
|
{
|
|
asASSERT(dataType.GetSizeInMemoryBytes() == 8);
|
|
return qwordValue;
|
|
}
|
|
|
|
asDWORD asCExprValue::GetConstantDW()
|
|
{
|
|
asASSERT(dataType.GetSizeInMemoryBytes() == 4);
|
|
return dwordValue;
|
|
}
|
|
|
|
asWORD asCExprValue::GetConstantW()
|
|
{
|
|
asASSERT(dataType.GetSizeInMemoryBytes() == 2);
|
|
return wordValue;
|
|
}
|
|
|
|
asBYTE asCExprValue::GetConstantB()
|
|
{
|
|
asASSERT(dataType.GetSizeInMemoryBytes() == 1);
|
|
return byteValue;
|
|
}
|
|
|
|
float asCExprValue::GetConstantF()
|
|
{
|
|
asASSERT(dataType.GetSizeInMemoryBytes() == 4);
|
|
return floatValue;
|
|
}
|
|
|
|
double asCExprValue::GetConstantD()
|
|
{
|
|
asASSERT(dataType.GetSizeInMemoryBytes() == 8);
|
|
return doubleValue;
|
|
}
|
|
|
|
void asCExprValue::SetConstantData(const asCDataType &dt, asQWORD qw)
|
|
{
|
|
Set(dt);
|
|
|
|
isConstant = true;
|
|
|
|
// This code is necessary to guarantee that the code
|
|
// works on both big endian and little endian CPUs.
|
|
if (dataType.GetSizeInMemoryBytes() == 1)
|
|
byteValue = (asBYTE)qw;
|
|
if (dataType.GetSizeInMemoryBytes() == 2)
|
|
wordValue = (asWORD)qw;
|
|
if (dataType.GetSizeInMemoryBytes() == 4)
|
|
dwordValue = (asDWORD)qw;
|
|
else
|
|
qwordValue = qw;
|
|
}
|
|
|
|
asQWORD asCExprValue::GetConstantData()
|
|
{
|
|
asQWORD qw = 0;
|
|
// This code is necessary to guarantee that the code
|
|
// works on both big endian and little endian CPUs.
|
|
if (dataType.GetSizeInMemoryBytes() == 1)
|
|
qw = byteValue;
|
|
if (dataType.GetSizeInMemoryBytes() == 2)
|
|
qw = wordValue;
|
|
if (dataType.GetSizeInMemoryBytes() == 4)
|
|
qw = dwordValue;
|
|
else
|
|
qw = qwordValue;
|
|
return qw;
|
|
}
|
|
|
|
void asCExprValue::SetUndefinedFuncHandle(asCScriptEngine *engine)
|
|
{
|
|
// This is used for when the expression evaluates to a
|
|
// function, but it is not yet known exactly which. The
|
|
// owner expression will hold the name of the function
|
|
// to determine the exact function when the signature is
|
|
// known.
|
|
Set(asCDataType::CreateObjectHandle(&engine->functionBehaviours, true));
|
|
isConstant = true;
|
|
isExplicitHandle = false;
|
|
qwordValue = 1; // Set to a different value than 0 to differentiate from null constant
|
|
isLValue = false;
|
|
}
|
|
|
|
bool asCExprValue::IsUndefinedFuncHandle() const
|
|
{
|
|
if (isConstant == false) return false;
|
|
if (qwordValue == 0) return false;
|
|
if (isLValue) return false;
|
|
if (dataType.GetTypeInfo() == 0) return false;
|
|
if (dataType.GetTypeInfo()->name != "$func") return false;
|
|
if (dataType.IsFuncdef()) return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
void asCExprValue::SetNullConstant()
|
|
{
|
|
Set(asCDataType::CreateNullHandle());
|
|
isConstant = true;
|
|
isExplicitHandle = false;
|
|
qwordValue = 0;
|
|
isLValue = false;
|
|
}
|
|
|
|
bool asCExprValue::IsNullConstant() const
|
|
{
|
|
// We can't check the actual object type, because the null constant may have been cast to another type
|
|
if (isConstant && dataType.IsObjectHandle() && qwordValue == 0)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
void asCExprValue::SetVoid()
|
|
{
|
|
Set(asCDataType::CreatePrimitive(ttVoid, false));
|
|
isLValue = false;
|
|
isConstant = true;
|
|
}
|
|
|
|
bool asCExprValue::IsVoid() const
|
|
{
|
|
if (dataType.GetTokenType() == ttVoid)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
void asCExprValue::SetDummy()
|
|
{
|
|
SetConstantDW(asCDataType::CreatePrimitive(ttInt, true), 0);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
asCExprContext::asCExprContext(asCScriptEngine *engine) : bc(engine)
|
|
{
|
|
property_arg = 0;
|
|
|
|
Clear();
|
|
}
|
|
|
|
asCExprContext::~asCExprContext()
|
|
{
|
|
if (property_arg)
|
|
asDELETE(property_arg, asCExprContext);
|
|
}
|
|
|
|
void asCExprContext::Clear()
|
|
{
|
|
bc.ClearAll();
|
|
type.Set(asCDataType());
|
|
deferredParams.SetLength(0);
|
|
if (property_arg)
|
|
asDELETE(property_arg, asCExprContext);
|
|
property_arg = 0;
|
|
exprNode = 0;
|
|
origExpr = 0;
|
|
property_get = 0;
|
|
property_set = 0;
|
|
property_const = false;
|
|
property_handle = false;
|
|
property_ref = false;
|
|
methodName = "";
|
|
enumValue = "";
|
|
symbolNamespace = 0;
|
|
isVoidExpression = false;
|
|
isCleanArg = false;
|
|
isAnonymousInitList = false;
|
|
origCode = 0;
|
|
}
|
|
|
|
bool asCExprContext::IsClassMethod() const
|
|
{
|
|
if (type.dataType.GetTypeInfo() == 0) return false;
|
|
if (methodName == "") return false;
|
|
if (type.dataType.GetTypeInfo() == &type.dataType.GetTypeInfo()->engine->functionBehaviours) return false;
|
|
if (isAnonymousInitList) return false;
|
|
return true;
|
|
}
|
|
|
|
bool asCExprContext::IsGlobalFunc() const
|
|
{
|
|
if (type.dataType.GetTypeInfo() == 0) return false;
|
|
if (methodName == "") return false;
|
|
if (type.dataType.GetTypeInfo() != &type.dataType.GetTypeInfo()->engine->functionBehaviours) return false;
|
|
if (isAnonymousInitList) return false;
|
|
return true;
|
|
}
|
|
|
|
void asCExprContext::SetLambda(asCScriptNode *funcDecl)
|
|
{
|
|
asASSERT(funcDecl && funcDecl->nodeType == snFunction);
|
|
asASSERT(bc.GetLastInstr() == -1);
|
|
|
|
Clear();
|
|
type.SetUndefinedFuncHandle(bc.GetEngine());
|
|
exprNode = funcDecl;
|
|
}
|
|
|
|
bool asCExprContext::IsLambda() const
|
|
{
|
|
if (type.IsUndefinedFuncHandle() && exprNode && exprNode->nodeType == snFunction)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
void asCExprContext::SetVoidExpression()
|
|
{
|
|
Clear();
|
|
type.SetVoid();
|
|
isVoidExpression = true;
|
|
}
|
|
|
|
bool asCExprContext::IsVoidExpression() const
|
|
{
|
|
if (isVoidExpression && type.IsVoid() && exprNode == 0)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
void asCExprContext::SetAnonymousInitList(asCScriptNode *initList, asCScriptCode *script)
|
|
{
|
|
Clear();
|
|
exprNode = initList;
|
|
origCode = script;
|
|
isAnonymousInitList = true;
|
|
}
|
|
|
|
bool asCExprContext::IsAnonymousInitList() const
|
|
{
|
|
if (isAnonymousInitList && exprNode && exprNode->nodeType == snInitList)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
void asCExprContext::Merge(asCExprContext *after)
|
|
{
|
|
type = after->type;
|
|
property_get = after->property_get;
|
|
property_set = after->property_set;
|
|
property_const = after->property_const;
|
|
property_handle = after->property_handle;
|
|
property_ref = after->property_ref;
|
|
property_arg = after->property_arg;
|
|
exprNode = after->exprNode;
|
|
methodName = after->methodName;
|
|
enumValue = after->enumValue;
|
|
isVoidExpression = after->isVoidExpression;
|
|
isCleanArg = after->isCleanArg;
|
|
isAnonymousInitList = after->isAnonymousInitList;
|
|
origCode = after->origCode;
|
|
|
|
after->property_arg = 0;
|
|
|
|
// Do not copy the origExpr member
|
|
}
|
|
|
|
|
|
|
|
END_AS_NAMESPACE
|
|
|
|
#endif // AS_NO_COMPILER
|
|
|
|
|
|
|