Merge branch 'refactor-network-string' of github.com:supertuxkart/stk-code into refactor-network-string

This commit is contained in:
hiker 2016-03-07 12:20:43 +11:00
commit 68e394702b
177 changed files with 4789 additions and 2300 deletions

View File

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<stkgui>
<div x="5%" y="5%" width="90%" height="90%" layout="vertical-row">
<div x="5%" y="0%" width="90%" proportion="6" layout="horizontal-row">
<div width="40%" height="100%" layout="vertical-row">
<icon id="icon" align="center" proportion="8" width="100%" icon="gui/loading.png" />
<spacer proportion="1" />
</div>
<spacer proportion="1" />
<div width="60%" height="50%" layout="vertical-row">
<label id="name" width="100%" text_align="left"/>
<spacer height="10"/>
</div>
</div>
<div width="80%" proportion="5" align="center">
<buttonbar id="actions" x="0" y="0" height="100%" width="100%" align="center">
<icon-button id="start" width="128" height="128"
icon="gui/green_check.png"
I18N="Ghost replay info screen action" text="Start Race" />
<icon-button id="remove" width="128" height="128"
icon="gui/remove.png"
I18N="Ghost replay info action" text="Remove" />
<icon-button id="back" width="128" height="128"
icon="gui/back.png"
I18N="Ghost replay info action" text="Back" />
</buttonbar>
</div>
</div>
</stkgui>

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<stkgui>
<div x="0%" y="1%" width="100%" height="98%" layout="vertical-row" >
<div x="0" y="0" width="100%" layout="horizontal-row" height="8%">
<icon-button id="back" height="100%" icon="gui/back.png"/>
<header text_align="center" proportion="1" text="Ghost Replay Selection" align="center"/>
<icon-button id="reload" height="90%" icon="gui/restart.png"/>
</div>
<box proportion="1" width="98%" align="center" layout="vertical-row" padding="6">
<list id="replay_list" x="0" y="0" width="100%" height="100%"/>
</box>
</div>
</stkgui>

Binary file not shown.

BIN
data/gui/mode_ghost.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View File

@ -1,63 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<stkgui>
<div x="0" y="0" width="100%" height="fit" layout="vertical-row">
<button id="user-id" width="20%" height="fit" align="right"/>
</div>
<div x="2%" y="2%" width="96%" height="96%" layout="vertical-row" >
<header text_align="center" width="80%" align="center" I18N="In the online multiplayer screen" text="Online Multiplayer"/>
<spacer height="5%" width="25"/>
<box width="100%" height="38%" padding="10" layout="vertical-row">
<bright width="100%" text="Local Networking" align="center" text_align="left" />
<buttonbar id="lan" proportion="2" width="90%" align="center">
<icon-button id="find_lan_server" width="128" height="128"
icon="gui/online/menu_find_server.png" focus_icon="gui/online/menu_find_server_hover.png"
I18N="In the online multiplayer screen" text="Find Server"/>
<icon-button id="create_lan_server" width="128" height="128"
icon="gui/online/menu_create_server.png" focus_icon="gui/online/menu_create_server_hover.png"
I18N="In the online multiplayer screen" text="Create Server"/>
<icon-button id="manage_user" width="128" height="128"
icon="gui/options_players.png"
I18N="In the online multiplayer screen" text="Users"/>
</buttonbar>
</box>
<spacer height="5%" width="25"/>
<box width="100%" height="38%" padding="10" layout="vertical-row">
<bright width="100%" text="Global Networking" align="center" text_align="left" />
<buttonbar id="menu_top_row" proportion="2" width="90%" align="center">
<icon-button id="find_wan_server" width="128" height="128"
icon="gui/online/menu_find_server.png" focus_icon="gui/online/menu_find_server_hover.png"
I18N="In the online multiplayer screen" text="Find Server"/>
<icon-button id="create_wan_server" width="128" height="128"
icon="gui/online/menu_create_server.png" focus_icon="gui/online/menu_create_server_hover.png"
I18N="In the online multiplayer screen" text="Create Server"/>
<icon-button id="quick_wan_play" width="128" height="128"
icon="gui/online/menu_quick_play.png" focus_icon="gui/online/menu_quick_play_hover.png"
I18N="In the online multiplayer screen" text="Quick Play"/>
</buttonbar>
</box>
<bottombar x="2%" width="96%" height="10%" layout="horizontal-row">
<label text_align="left" align="center" height="100%" id="online_status" proportion="1" text=""/>
<spacer width="10" height="10" />
<buttonbar id="menu_bottomrow" x="0" y="0" width="12%" height="100%" align="center">
<icon-button id="profile" width="64" height="64" icon="gui/green_check.png" extend_label="50"
I18N="In the online multiplayer screen" text="Profile" label_location="hover"/>
<icon-button id="sign_out" width="64" height="64" icon="gui/main_quit.png" extend_label="70"
I18N="In the online multiplayer screen" text="Log Out" label_location="hover"/>
</buttonbar>
</bottombar>
</div>
<icon-button id="back" x="0" y="0" height="8%" icon="gui/back.png"/>
</stkgui>

View File

@ -9,6 +9,7 @@
<spacer height="25" width="10"/>
<tabs id="profile_tabs" height="10%" max_height="110" x="2%" width="98%" align="center">
<icon-button id="tab_servers" width="128" height="128" icon="gui/main_network.png"/>
<icon-button id="tab_achievements" width="128" height="128" icon="gui/gp_copy.png"
I18N="Section in the profile screen" text="Achievements"/>
<icon-button id="tab_friends" width="128" height="128" icon="gui/options_players.png"/>

View File

@ -9,6 +9,7 @@
<spacer height="25" width="10"/>
<tabs id="profile_tabs" height="10%" max_height="110" x="2%" width="98%" align="center">
<icon-button id="tab_servers" width="128" height="128" icon="gui/main_network.png"/>
<icon-button id="tab_achievements" width="128" height="128" icon="gui/gp_copy.png"/>
<icon-button id="tab_friends" width="128" height="128" icon="gui/options_players.png"
I18N="Section in the profile screen" text="Friends"/>

View File

@ -0,0 +1,61 @@
<?xml version="1.0" encoding="UTF-8"?>
<stkgui>
<icon-button id="back" x="0" y="0" height="8%" icon="gui/back.png"/>
<div x="1%" y="1%" width="98%" height="98%" layout="vertical-row" >
<header id="title" text_align="center" width="80%" align="center" text="..."/>
<spacer height="25" width="10"/>
<tabs id="profile_tabs" height="10%" max_height="110" x="2%" width="98%" align="center">
<icon-button id="tab_servers" width="128" height="128" icon="gui/main_network.png" I18N="Section in the profile screen" text="Servers"/>
<icon-button id="tab_achievements" width="128" height="128" icon="gui/gp_copy.png"/>
<icon-button id="tab_friends" width="128" height="128" icon="gui/options_players.png" />
<icon-button id="tab_settings" width="128" height="128" icon="gui/main_options.png" />
</tabs>
<box proportion="1" width="100%" layout="vertical-row">
<div x="1%" y="2%" width="98%" height="96%" layout="vertical-row" >
<spacer height="5%" width="25"/>
<box width="100%" proportion="1" padding="10" layout="vertical-row">
<bright width="100%" text="Local Networking" align="center" text_align="left" />
<buttonbar id="lan" proportion="2" width="90%" align="center">
<icon-button id="find_lan_server" width="128" height="128"
icon="gui/online/menu_find_server.png" focus_icon="gui/online/menu_find_server_hover.png"
I18N="In the online multiplayer screen" text="Find Server"/>
<icon-button id="create_lan_server" width="128" height="128"
icon="gui/online/menu_create_server.png" focus_icon="gui/online/menu_create_server_hover.png"
I18N="In the online multiplayer screen" text="Create Server"/>
<!--
<icon-button id="manage_user" width="128" height="128"
icon="gui/options_players.png"
I18N="In the online multiplayer screen" text="Users"/>
-->
</buttonbar>
</box>
<spacer height="5%" width="25"/>
<box width="100%" proportion="1" padding="10" layout="vertical-row">
<bright width="100%" text="Global Networking" align="center" text_align="left" />
<buttonbar id="wan" proportion="2" width="90%" align="center">
<icon-button id="find_wan_server" width="128" height="128"
icon="gui/online/menu_find_server.png" focus_icon="gui/online/menu_find_server_hover.png"
I18N="In the online multiplayer screen" text="Find Server"/>
<icon-button id="create_wan_server" width="128" height="128"
icon="gui/online/menu_create_server.png" focus_icon="gui/online/menu_create_server_hover.png"
I18N="In the online multiplayer screen" text="Create Server"/>
<icon-button id="quick_wan_play" width="128" height="128"
icon="gui/online/menu_quick_play.png" focus_icon="gui/online/menu_quick_play_hover.png"
I18N="In the online multiplayer screen" text="Quick Play"/>
</buttonbar>
</box>
</div>
</box>
</div>
</stkgui>

View File

@ -8,6 +8,7 @@
<spacer height="25" width="10"/>
<tabs id="profile_tabs" height="10%" max_height="110" x="2%" width="98%" align="center">
<icon-button id="tab_servers" width="128" height="128" icon="gui/main_network.png"/>
<icon-button id="tab_achievements" width="128" height="128" icon="gui/gp_copy.png"/>
<icon-button id="tab_friends" width="128" height="128" icon="gui/options_players.png" />
<icon-button id="tab_settings" width="128" height="128" icon="gui/main_options.png"

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<stkgui>
<div x="2%" y="5%" width="96%" height="90%" layout="vertical-row" >
<header id="title" width="96%" height="fit" text_align="center" I18N="Networking screen" text="Waiting for the others..."/>
<spacer height="40" width="50"/>
Waiting...
</div>
</stkgui>

View File

@ -3,9 +3,9 @@
<icon-button id="back" x="0" y="0" height="8%" icon="gui/back.png"/>
<div x="2%" y="2%" width="96%" height="96%" layout="vertical-row">
<header id="name" height="5%" width="80%" align="center" text_align="center"/>
<header id="name" height="7%" width="80%" align="center" text_align="center"/>
<spacer width="1" height="5%"/>
<spacer width="1" height="1%"/>
<box width="100%" height="40%" padding="10" layout="horizontal-row">
<!-- Left pane -->
@ -49,8 +49,8 @@
</div>
</box>
<spacer width="1" height="5%"/>
<box width="100%" height="25%" padding="15" layout="vertical-row">
<spacer width="1" height="1%"/>
<box width="100%" height="33%" padding="15" layout="vertical-row">
<div width="100%" height="fit" layout="horizontal-row" >
<label id="lap-text" proportion="1" I18N="In the track info screen" text="Number of laps" text_align="right"/>
<spacer width="40"/>
@ -78,9 +78,18 @@
</div>
</div>
</div>
<spacer width="1" height="2%"/>
<div width="100%" height="fit" layout="horizontal-row" >
<label id="record-race-text" proportion="1" I18N="In the track info screen" text="Record the race for ghost replay" text_align="right"/>
<spacer width="40"/>
<div proportion="1" height="fit" layout="horizontal-row">
<div width="50%" height="fit" text-align="center" layout="vertical-row" >
<checkbox id="record" align="center"/>
</div>
</div>
</div>
<spacer width="1" height="1%"/>
</box>
<spacer width="1" height="5%"/>
<spacer width="1" height="1%"/>
<buttonbar id="buttons" height="15%" width="100%" align="center">
<icon-button id="start" width="64" height="64" icon="gui/green_check.png"
I18N="In the track info screen" text="Start Race"/>

View File

@ -81,6 +81,10 @@ when the border that intersect at this corner are enabled.
left_border="128" right_border="13" top_border="13" bottom_border="13"
preserve_h_aspect_ratios="true" hborder_out_portion="0" vborder_out_portion="0"/>
<element type="generic-message" image="forest/generic.png"
left_border="128" right_border="13" top_border="13" bottom_border="13"
preserve_h_aspect_ratios="true" hborder_out_portion="0" vborder_out_portion="0"/>
<element type="button" state="neutral" image="forest/glassbutton.png"
left_border="13" right_border="13" top_border="13" bottom_border="13"
preserve_h_aspect_ratios="true" hborder_out_portion="0" vborder_out_portion="0"/>

View File

@ -80,6 +80,10 @@ when the border that intersect at this corner are enabled.
left_border="128" right_border="13" top_border="13" bottom_border="13"
preserve_h_aspect_ratios="true" hborder_out_portion="0" vborder_out_portion="0"/>
<element type="generic-message" image="ocean/generic.png"
left_border="128" right_border="13" top_border="13" bottom_border="13"
preserve_h_aspect_ratios="true" hborder_out_portion="0" vborder_out_portion="0"/>
<element type="button" state="neutral" image="ocean/glassbutton.png"
left_border="13" right_border="13" top_border="13" bottom_border="13"
preserve_h_aspect_ratios="true" hborder_out_portion="0" vborder_out_portion="0"/>

View File

@ -80,6 +80,10 @@ when the border that intersect at this corner are enabled.
left_border="128" right_border="13" top_border="13" bottom_border="13"
preserve_h_aspect_ratios="true" hborder_out_portion="0" vborder_out_portion="0"/>
<element type="generic-message" image="peach/generic.png"
left_border="128" right_border="13" top_border="13" bottom_border="13"
preserve_h_aspect_ratios="true" hborder_out_portion="0" vborder_out_portion="0"/>
<element type="button" state="neutral" image="peach/glassbutton.png"
left_border="13" right_border="13" top_border="13" bottom_border="13"
preserve_h_aspect_ratios="true" hborder_out_portion="0" vborder_out_portion="0"/>

View File

@ -81,6 +81,10 @@ when the border that intersect at this corner are enabled.
left_border="128" right_border="13" top_border="13" bottom_border="13"
preserve_h_aspect_ratios="true" hborder_out_portion="0" vborder_out_portion="0"/>
<element type="generic-message" image="ruby/generic.png"
left_border="128" right_border="13" top_border="13" bottom_border="13"
preserve_h_aspect_ratios="true" hborder_out_portion="0" vborder_out_portion="0"/>
<element type="button" state="neutral" image="ruby/glassbutton.png"
left_border="13" right_border="13" top_border="13" bottom_border="13"
preserve_h_aspect_ratios="true" hborder_out_portion="0" vborder_out_portion="0"/>

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

BIN
data/skins/ruby/generic.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View File

@ -58,8 +58,8 @@ BEGIN_AS_NAMESPACE
// AngelScript version
#define ANGELSCRIPT_VERSION 23000
#define ANGELSCRIPT_VERSION_STRING "2.30.0 WIP"
#define ANGELSCRIPT_VERSION 23002
#define ANGELSCRIPT_VERSION_STRING "2.30.2"
// Data types
@ -543,7 +543,7 @@ struct asSMessageInfo
extern "C"
{
// Engine
AS_API asIScriptEngine *asCreateScriptEngine(asDWORD version);
AS_API asIScriptEngine *asCreateScriptEngine(asDWORD version = ANGELSCRIPT_VERSION);
AS_API const char *asGetLibraryVersion();
AS_API const char *asGetLibraryOptions();
@ -585,20 +585,20 @@ template<typename T>
asUINT asGetTypeTraits()
{
#if defined(_MSC_VER) || defined(_LIBCPP_TYPE_TRAITS) || (__GNUC__ >= 5)
// MSVC & XCode/Clang, and gnuc 5+
// MSVC, XCode/Clang, and gnuc 5+
// C++11 compliant code
bool hasConstructor = std::is_default_constructible<T>::value && !std::is_trivially_default_constructible<T>::value;
bool hasDestructor = std::is_destructible<T>::value && !std::is_trivially_destructible<T>::value;
bool hasAssignmentOperator = std::is_copy_assignable<T>::value && !std::is_trivially_copy_assignable<T>::value;
bool hasCopyConstructor = std::is_copy_constructible<T>::value && !std::is_trivially_copy_constructible<T>::value;
#elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))
// gnuc 4.8+
// gnuc is using a mix of C++11 standard and pre-standard templates
// gnuc 4.8 is using a mix of C++11 standard and pre-standard templates
bool hasConstructor = std::is_default_constructible<T>::value && !std::has_trivial_default_constructor<T>::value;
bool hasDestructor = std::is_destructible<T>::value && !std::is_trivially_destructible<T>::value;
bool hasAssignmentOperator = std::is_copy_assignable<T>::value && !std::has_trivial_copy_assign<T>::value;
bool hasCopyConstructor = std::is_copy_constructible<T>::value && !std::has_trivial_copy_constructor<T>::value;
#else
// All other compilers and versions are assumed to use non C++11 compliant code until proven otherwise
// Not fully C++11 compliant. The has_trivial checks were used while the standard was still
// being elaborated, but were then removed in favor of the above is_trivially checks
// http://stackoverflow.com/questions/12702103/writing-code-that-works-when-has-trivial-destructor-is-defined-instead-of-is
@ -1556,8 +1556,8 @@ enum asEBCInstr
asBC_POWdi = 197,
asBC_POWi64 = 198,
asBC_POWu64 = 199,
asBC_MAXBYTECODE = 200,
asBC_Thiscall1 = 200,
asBC_MAXBYTECODE = 201,
// Temporary tokens. Can't be output to the final program
asBC_VarDecl = 251,
@ -1851,8 +1851,8 @@ const asSBCInfo asBCInfo[256] =
asBCINFO(POWdi, wW_rW_rW_ARG, 0),
asBCINFO(POWi64, wW_rW_rW_ARG, 0),
asBCINFO(POWu64, wW_rW_rW_ARG, 0),
asBCINFO(Thiscall1, DW_ARG, -AS_PTR_SIZE-1),
asBCINFO_DUMMY(200),
asBCINFO_DUMMY(201),
asBCINFO_DUMMY(202),
asBCINFO_DUMMY(203),

View File

@ -14,11 +14,15 @@ if(APPLE)
option(BUILD_FRAMEWORK "Build Framework bundle for OSX" OFF)
endif()
set(ANGELSCRIPT_VERSION_MAJOR 2)
set(ANGELSCRIPT_VERSION_MINOR 30)
set(ANGELSCRIPT_VERSION_PATCH 0)
file(READ ../../include/angelscript.h ANGELSCRIPT_H)
string(REGEX MATCH "#define ANGELSCRIPT_VERSION_STRING \"([0-9]*).([0-9]*).([0-9]*)" ANGELSCRIPT_VERSION_REGEX ${ANGELSCRIPT_H})
set(ANGELSCRIPT_VERSION_MAJOR ${CMAKE_MATCH_1})
set(ANGELSCRIPT_VERSION_MINOR ${CMAKE_MATCH_2})
set(ANGELSCRIPT_VERSION_PATCH ${CMAKE_MATCH_3})
set(PROJECT_VERSION ${ANGELSCRIPT_VERSION_MAJOR}.${ANGELSCRIPT_VERSION_MINOR}.${ANGELSCRIPT_VERSION_PATCH})
message(STATUS "Configuring angelscript ${PROJECT_VERSION}")
find_package(Threads)
set(ANGELSCRIPT_HEADERS
@ -102,12 +106,13 @@ if(MSVC AND CMAKE_CL_64)
endif()
endif()
if(ANDROID)
if(${CMAKE_SYSTEM_PROCESSOR} MATCHES "^arm")
enable_language(ASM)
if(CMAKE_ASM_COMPILER_WORKS)
set(ANGELSCRIPT_SOURCE ${ANGELSCRIPT_SOURCE} ../../source/as_callfunc_arm.cpp ../../source/as_callfunc_arm_gcc.S)
set_property(SOURCE ../../source/as_callfunc_arm_gcc.S APPEND PROPERTY COMPILE_FLAGS " -Wa,-mimplicit-it=always")
else()
message(FATAL ERROR "Android target requires a working assembler")
message(FATAL ERROR "ARM target requires a working assembler")
endif()
endif()
@ -156,4 +161,5 @@ endif()
#set(RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/../../bin)
#add_subdirectory(../../../samples/game/projects/cmake/ ./game)

View File

@ -363,9 +363,35 @@ int asCBuilder::CompileGlobalVar(const char *sectionName, const char *code, int
CompileGlobalVariables();
// It is possible that the global variable initialization included anonymous functions that must be compiled too
for( asUINT n = 0; n < functions.GetLength(); n++ )
{
asCCompiler compiler(engine);
asCScriptFunction *func = engine->scriptFunctions[functions[n]->funcId];
int r = compiler.CompileFunction(this, functions[n]->script, func->parameterNames, functions[n]->node, func, 0);
if( r < 0 )
break;
}
if( numWarnings > 0 && engine->ep.compilerWarnings == 2 )
WriteError(TXT_WARNINGS_TREATED_AS_ERROR, 0, 0);
// None of the functions should be added to the module if any error occurred,
// or it was requested that the functions wouldn't be added to the scope
if( numErrors > 0 )
{
for( asUINT n = 0; n < functions.GetLength(); n++ )
{
asCScriptFunction *func = engine->scriptFunctions[functions[n]->funcId];
if( module->globalFunctions.GetIndex(func) >= 0 )
{
module->globalFunctions.Erase(module->globalFunctions.GetIndex(func));
module->scriptFunctions.RemoveValue(func);
func->ReleaseInternal();
}
}
}
if( numErrors > 0 )
{
// Remove the variable from the module, if it was registered
@ -549,22 +575,38 @@ int asCBuilder::CompileFunction(const char *sectionName, const char *code, int l
funcDesc->paramNames = func->parameterNames;
funcDesc->isExistingShared = false;
asCCompiler compiler(engine);
compiler.CompileFunction(this, functions[0]->script, func->parameterNames, functions[0]->node, func, 0);
// This must be done in a loop, as it is possible that additional functions get declared as lambda's in the code
for( asUINT n = 0; n < functions.GetLength(); n++ )
{
asCCompiler compiler(engine);
asCScriptFunction *func = engine->scriptFunctions[functions[n]->funcId];
int r = compiler.CompileFunction(this, functions[n]->script, func->parameterNames, functions[n]->node, func, 0);
if( r < 0 )
break;
}
if( numWarnings > 0 && engine->ep.compilerWarnings == 2 )
WriteError(TXT_WARNINGS_TREATED_AS_ERROR, 0, 0);
// None of the functions should be added to the module if any error occurred,
// or it was requested that the functions wouldn't be added to the scope
if( !(compileFlags & asCOMP_ADD_TO_MODULE) || numErrors > 0 )
{
for( asUINT n = 0; n < functions.GetLength(); n++ )
{
asCScriptFunction *func = engine->scriptFunctions[functions[n]->funcId];
if( module->globalFunctions.GetIndex(func) >= 0 )
{
module->globalFunctions.Erase(module->globalFunctions.GetIndex(func));
module->scriptFunctions.RemoveValue(func);
func->ReleaseInternal();
}
}
}
if( numErrors > 0 )
{
// If the function was added to the module then remove it again
if( compileFlags & asCOMP_ADD_TO_MODULE )
{
module->globalFunctions.Erase(module->globalFunctions.GetIndex(func));
module->scriptFunctions.RemoveValue(func);
func->ReleaseInternal();
}
// Release the function pointer that would otherwise be returned if no errors occured
func->ReleaseInternal();
return asERROR;
@ -965,15 +1007,20 @@ int asCBuilder::VerifyProperty(asCDataType *dt, const char *decl, asCString &nam
if( r < 0 )
return asINVALID_DECLARATION;
// Get data type and property name
// Get data type
asCScriptNode *dataType = parser.GetScriptNode()->firstChild;
asCScriptNode *nameNode = dataType->next;
// Check if the property is declared 'by reference'
bool isReference = (dataType->next->tokenType == ttAmp);
// Get the name of the property
asCScriptNode *nameNode = isReference ? dataType->next->next : dataType->next;
// If an object property is registered, then use the
// object's namespace, otherwise use the specified namespace
type = CreateDataTypeFromNode(dataType, &source, dt ? dt->GetObjectType()->nameSpace : ns);
name.Assign(&decl[nameNode->tokenPos], nameNode->tokenLength);
type.MakeReference(isReference);
// Validate that the type really can be a registered property
// We cannot use CanBeInstantiated, as it is allowed to register
@ -1458,9 +1505,9 @@ sMixinClass *asCBuilder::GetMixinClass(const char *name, asSNameSpace *ns)
int asCBuilder::RegisterFuncDef(asCScriptNode *node, asCScriptCode *file, asSNameSpace *ns)
{
// TODO: 2.30.0: redesign: Allow funcdefs to be explicitly declared as 'shared'. In this case
// an error should be given if any of the arguments/return type is not
// shared.
// TODO: redesign: Allow funcdefs to be explicitly declared as 'shared'. In this case
// an error should be given if any of the arguments/return type is not
// shared.
// Find the name
asASSERT( node->firstChild->nodeType == snDataType );
@ -1516,6 +1563,7 @@ void asCBuilder::CompleteFuncDef(sFuncDef *funcDef)
asCScriptFunction *func = module->funcDefs[funcDef->idx];
asASSERT( func );
// TODO: It should be possible to declare funcdef as shared. In this case a compiler error will be given if any of the types it uses are not shared
GetParsedFunctionDetails(funcDef->node, funcDef->script, 0, funcDef->name, func->returnType, func->parameterNames, func->parameterTypes, func->inOutFlags, defaultArgs, isConstMethod, isConstructor, isDestructor, isPrivate, isProtected, isOverride, isFinal, isShared, func->nameSpace);
// There should not be any defaultArgs, but if there are any we need to delete them to avoid leaks
@ -1523,27 +1571,47 @@ void asCBuilder::CompleteFuncDef(sFuncDef *funcDef)
if( defaultArgs[n] )
asDELETE(defaultArgs[n], asCString);
// TODO: Should we force the use of 'shared' for this check to be done?
// Check if there is another identical funcdef from another module and if so reuse that instead
for( asUINT n = 0; n < engine->funcDefs.GetLength(); n++ )
// All funcdefs are shared, unless one of the parameter types or return type is not shared
isShared = true;
if( func->returnType.GetObjectType() && !func->returnType.GetObjectType()->IsShared() )
isShared = false;
if( func->returnType.GetFuncDef() && !func->returnType.GetFuncDef()->IsShared() )
isShared = false;
for( asUINT n = 0; isShared && n < func->parameterTypes.GetLength(); n++ )
{
asCScriptFunction *f2 = engine->funcDefs[n];
if( f2 == 0 || func == f2 )
continue;
if( func->parameterTypes[n].GetObjectType() && !func->parameterTypes[n].GetObjectType()->IsShared() )
isShared = false;
if( func->parameterTypes[n].GetFuncDef() && !func->parameterTypes[n].GetFuncDef()->IsShared() )
isShared = false;
}
func->isShared = isShared;
if( f2->name == func->name &&
f2->nameSpace == func->nameSpace &&
f2->IsSignatureExceptNameEqual(func) )
// Check if there is another identical funcdef from another module and if so reuse that instead
if( func->isShared )
{
for( asUINT n = 0; n < engine->funcDefs.GetLength(); n++ )
{
// Replace our funcdef for the existing one
funcDef->idx = f2->id;
module->funcDefs[module->funcDefs.IndexOf(func)] = f2;
f2->AddRefInternal();
asCScriptFunction *f2 = engine->funcDefs[n];
if( f2 == 0 || func == f2 )
continue;
engine->funcDefs.RemoveValue(func);
if( !f2->isShared )
continue;
func->ReleaseInternal();
break;
if( f2->name == func->name &&
f2->nameSpace == func->nameSpace &&
f2->IsSignatureExceptNameEqual(func) )
{
// Replace our funcdef for the existing one
funcDef->idx = f2->id;
module->funcDefs[module->funcDefs.IndexOf(func)] = f2;
f2->AddRefInternal();
engine->funcDefs.RemoveValue(func);
func->ReleaseInternal();
break;
}
}
}
}
@ -2463,7 +2531,7 @@ void asCBuilder::CompileInterfaces()
sClassDeclaration *intfDecl = interfaceDeclarations[n];
asCObjectType *intfType = intfDecl->objType;
// TODO: 2.28.1: Is this really at the correct place? Hasn't the vfTableIdx already been set here?
// TODO: Is this really at the correct place? Hasn't the vfTableIdx already been set here?
// Co-opt the vfTableIdx value in our own methods to indicate the
// index the function should have in the table chunk for this interface.
for( asUINT d = 0; d < intfType->methods.GetLength(); d++ )
@ -4124,6 +4192,34 @@ int asCBuilder::RegisterScriptFunctionFromNode(asCScriptNode *node, asCScriptCod
return RegisterScriptFunction(node, file, objType, isInterface, isGlobalFunction, ns, isExistingShared, isMixin, name, returnType, parameterNames, parameterTypes, inOutFlags, defaultArgs, isConstMethod, isConstructor, isDestructor, isPrivate, isProtected, isOverride, isFinal, isShared);
}
asCScriptFunction *asCBuilder::RegisterLambda(asCScriptNode *node, asCScriptCode *file, asCScriptFunction *funcDef, const asCString &name, asSNameSpace *ns)
{
// Get the parameter names from the node
asCArray<asCString> parameterNames;
asCArray<asCString*> defaultArgs;
asCScriptNode *args = node->firstChild;
while( args && args->nodeType == snIdentifier )
{
asCString argName;
argName.Assign(&file->code[args->tokenPos], args->tokenLength);
parameterNames.PushLast(argName);
defaultArgs.PushLast(0);
args = args->next;
}
// The statement block for the function must be disconnected, as the builder is going to be the owner of it
args->DisconnectParent();
// Get the return and parameter types from the funcDef
asCString funcName = name;
int r = RegisterScriptFunction(args, file, 0, 0, true, ns, false, false, funcName, funcDef->returnType, parameterNames, funcDef->parameterTypes, funcDef->inOutFlags, defaultArgs, false, false, false, false, false, false, false, false);
if( r < 0 )
return 0;
// Return the function that was just created (but that will be compiled later)
return engine->scriptFunctions[functions[functions.GetLength()-1]->funcId];
}
int asCBuilder::RegisterScriptFunction(asCScriptNode *node, asCScriptCode *file, asCObjectType *objType, bool isInterface, bool isGlobalFunction, asSNameSpace *ns, bool isExistingShared, bool isMixin, asCString &name, asCDataType &returnType, asCArray<asCString> &parameterNames, asCArray<asCDataType> &parameterTypes, asCArray<asETypeModifiers> &inOutFlags, asCArray<asCString *> &defaultArgs, bool isConstMethod, bool isConstructor, bool isDestructor, bool isPrivate, bool isProtected, bool isOverride, bool isFinal, bool isShared)
{
// Determine default namespace if not specified
@ -4734,11 +4830,15 @@ void asCBuilder::GetObjectMethodDescriptions(const char *name, asCObjectType *ob
// If searching with a scope informed, then the node and script must also be informed for potential error reporting
asASSERT( errNode && script );
// If the scope contains ::identifier, then use the last identifier as the class name and the rest of is as the namespace
// If the scope contains ::identifier, then use the last identifier as the class name and the rest of it as the namespace
int n = scope.FindLast("::");
asCString className = n >= 0 ? scope.SubString(n+2) : scope;
asCString nsName = n >= 0 ? scope.SubString(0, n) : "";
asSNameSpace *ns = GetNameSpaceByString(nsName, objectType->nameSpace, errNode, script);
// Check if the namespace actually exist, if not return silently as this cannot be the referring to a base class
asSNameSpace *ns = GetNameSpaceByString(nsName, objectType->nameSpace, errNode, script, false);
if( ns == 0 )
return;
// Find the base class with the specified scope
while( objectType && (objectType->name != className || objectType->nameSpace != ns) )
@ -4894,7 +4994,7 @@ asSNameSpace *asCBuilder::GetNameSpaceFromNode(asCScriptNode *node, asCScriptCod
return GetNameSpaceByString(scope, implicitNs, node, script);
}
asSNameSpace *asCBuilder::GetNameSpaceByString(const asCString &nsName, asSNameSpace *implicitNs, asCScriptNode *errNode, asCScriptCode *script)
asSNameSpace *asCBuilder::GetNameSpaceByString(const asCString &nsName, asSNameSpace *implicitNs, asCScriptNode *errNode, asCScriptCode *script, bool isRequired)
{
asSNameSpace *ns = implicitNs;
if( nsName == "::" )
@ -4902,7 +5002,7 @@ asSNameSpace *asCBuilder::GetNameSpaceByString(const asCString &nsName, asSNameS
else if( nsName != "" )
{
ns = engine->FindNameSpace(nsName.AddressOf());
if( ns == 0 )
if( ns == 0 && isRequired )
{
asCString msg;
msg.Format(TXT_NAMESPACE_s_DOESNT_EXIST, nsName.AddressOf());
@ -5073,6 +5173,12 @@ asCDataType asCBuilder::CreateDataTypeFromNode(asCScriptNode *node, asCScriptCod
ot = otInstance;
}
}
else if( n && n->next && n->next->nodeType == snDataType )
{
asCString str;
str.Format(TXT_TYPE_s_NOT_TEMPLATE, ot->name.AddressOf());
WriteError(str, file, n);
}
// Create object data type
if( ot )

View File

@ -174,7 +174,7 @@ protected:
asCString GetCleanExpressionString(asCScriptNode *n, asCScriptCode *file);
asSNameSpace *GetNameSpaceFromNode(asCScriptNode *node, asCScriptCode *script, asSNameSpace *implicitNs, asCScriptNode **next);
asSNameSpace *GetNameSpaceByString(const asCString &nsName, asSNameSpace *implicitNs, asCScriptNode *errNode, asCScriptCode *script);
asSNameSpace *GetNameSpaceByString(const asCString &nsName, asSNameSpace *implicitNs, asCScriptNode *errNode, asCScriptCode *script, bool isRequired = true);
asCString GetScopeFromNode(asCScriptNode *n, asCScriptCode *script, asCScriptNode **next = 0);
asCObjectType *GetObjectType(const char *type, asSNameSpace *ns);
@ -213,6 +213,7 @@ protected:
int RegisterEnum(asCScriptNode *node, asCScriptCode *file, asSNameSpace *ns);
int RegisterTypedef(asCScriptNode *node, asCScriptCode *file, asSNameSpace *ns);
int RegisterFuncDef(asCScriptNode *node, asCScriptCode *file, asSNameSpace *ns);
asCScriptFunction *RegisterLambda(asCScriptNode *node, asCScriptCode *file, asCScriptFunction *funcDef, const asCString &name, asSNameSpace *ns);
void CompleteFuncDef(sFuncDef *funcDef);
void CompileInterfaces();
void CompileClasses(asUINT originalNumTempl);

View File

@ -1015,6 +1015,21 @@ void asCByteCode::OptimizeLocally(const asCArray<int> &tempVariableOffsets)
ChangeFirstDeleteNext(curr, asBC_PSF);
instr = GoForward(curr);
}
// VAR a, GETOBJREF 0 -> PshVPtr a
else if( curr->next && curr->next->op == asBC_GETOBJREF && curr->next->wArg[0] == 0 )
{
ChangeFirstDeleteNext(curr, asBC_PshVPtr);
instr = GoForward(curr);
}
// VAR, PSF, GETREF {PTR_SIZE} -> PSF, PSF
if( curr->next && curr->next->op == asBC_PSF &&
curr->next->next && curr->next->next->op == asBC_GETREF &&
curr->next->next->wArg[0] == AS_PTR_SIZE )
{
curr->op = asBC_PSF;
DeleteInstruction(curr->next->next);
instr = GoForward(curr);
}
}
}
@ -2220,6 +2235,13 @@ void asCByteCode::DebugOutput(const char *name, asCScriptEngine *engine, asCScri
}
break;
case asBC_FuncPtr:
{
asCScriptFunction *func = *(asCScriptFunction**)ARG_DW(instr->arg);
fprintf(file, " %-8s 0x%x (func:%s)\n", asBCInfo[instr->op].name, (asUINT)*ARG_DW(instr->arg), func->GetDeclaration());
}
break;
case asBC_PshC4:
case asBC_Cast:
fprintf(file, " %-8s 0x%x (i:%d, f:%g)\n", asBCInfo[instr->op].name, (asUINT)*ARG_DW(instr->arg), *((int*) ARG_DW(instr->arg)), *((float*) ARG_DW(instr->arg)));
@ -2233,6 +2255,7 @@ void asCByteCode::DebugOutput(const char *name, asCScriptEngine *engine, asCScri
case asBC_CALLSYS:
case asBC_CALLBND:
case asBC_CALLINTF:
case asBC_Thiscall1:
{
int funcID = *(int*)ARG_DW(instr->arg);
asCString decl = engine->GetFunctionDeclaration(funcID);
@ -2268,11 +2291,18 @@ void asCByteCode::DebugOutput(const char *name, asCScriptEngine *engine, asCScri
{
case asBC_OBJTYPE:
{
asCObjectType *ot = *(asCObjectType**)ARG_DW(instr->arg);
asCObjectType *ot = *(asCObjectType**)ARG_QW(instr->arg);
fprintf(file, " %-8s 0x%x (type:%s)\n", asBCInfo[instr->op].name, (asUINT)*ARG_QW(instr->arg), ot->GetName());
}
break;
case asBC_FuncPtr:
{
asCScriptFunction *func = *(asCScriptFunction**)ARG_QW(instr->arg);
fprintf(file, " %-8s 0x%x (func:%s)\n", asBCInfo[instr->op].name, (asUINT)*ARG_QW(instr->arg), func->GetDeclaration());
}
break;
default:
#ifdef __GNUC__
#ifdef _LP64
@ -2288,15 +2318,27 @@ void asCByteCode::DebugOutput(const char *name, asCScriptEngine *engine, asCScri
case asBCTYPE_wW_QW_ARG:
case asBCTYPE_rW_QW_ARG:
switch( instr->op )
{
case asBC_RefCpyV:
case asBC_FREE:
{
asCObjectType *ot = *(asCObjectType**)ARG_QW(instr->arg);
fprintf(file, " %-8s v%d, 0x%x (type:%s)\n", asBCInfo[instr->op].name, instr->wArg[0], (asUINT)*ARG_QW(instr->arg), ot->GetName());
}
break;
default:
#ifdef __GNUC__
#ifdef _LP64
fprintf(file, " %-8s v%d, 0x%lx (i:%ld, f:%g)\n", asBCInfo[instr->op].name, instr->wArg[0], *ARG_QW(instr->arg), *((asINT64*) ARG_QW(instr->arg)), *((double*) ARG_QW(instr->arg)));
fprintf(file, " %-8s v%d, 0x%lx (i:%ld, f:%g)\n", asBCInfo[instr->op].name, instr->wArg[0], *ARG_QW(instr->arg), *((asINT64*) ARG_QW(instr->arg)), *((double*) ARG_QW(instr->arg)));
#else
fprintf(file, " %-8s v%d, 0x%llx (i:%lld, f:%g)\n", asBCInfo[instr->op].name, instr->wArg[0], *ARG_QW(instr->arg), *((asINT64*) ARG_QW(instr->arg)), *((double*) ARG_QW(instr->arg)));
fprintf(file, " %-8s v%d, 0x%llx (i:%lld, f:%g)\n", asBCInfo[instr->op].name, instr->wArg[0], *ARG_QW(instr->arg), *((asINT64*) ARG_QW(instr->arg)), *((double*) ARG_QW(instr->arg)));
#endif
#else
fprintf(file, " %-8s v%d, 0x%I64x (i:%I64d, f:%g)\n", asBCInfo[instr->op].name, instr->wArg[0], *ARG_QW(instr->arg), *((asINT64*) ARG_QW(instr->arg)), *((double*) ARG_QW(instr->arg)));
fprintf(file, " %-8s v%d, 0x%I64x (i:%I64d, f:%g)\n", asBCInfo[instr->op].name, instr->wArg[0], *ARG_QW(instr->arg), *((asINT64*) ARG_QW(instr->arg)), *((double*) ARG_QW(instr->arg)));
#endif
}
break;
case asBCTYPE_DW_DW_ARG:

View File

@ -1,6 +1,6 @@
/*
AngelCode Scripting Library
Copyright (c) 2003-2012 Andreas Jonsson
Copyright (c) 2003-2015 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
@ -130,6 +130,8 @@ public:
int InstrW_W(asEBCInstr bc, int w, int b);
int InstrSHORT_DW_DW(asEBCInstr bc, short a, asDWORD b, asDWORD c);
asCScriptEngine *GetEngine() const { return engine; };
asCArray<int> lineNumbers;
asCArray<int> sectionIdxs;
int largestStackUsed;

View File

@ -130,10 +130,11 @@ int DetectCallingConvention(bool isMethod, const asSFuncPtr &ptr, int callConv,
internal->callConv = (internalCallConv)(thisCallConv + 2);
#endif
internal->baseOffset = ( int )MULTI_BASE_OFFSET(ptr);
#if defined(AS_ARM) && (defined(__GNUC__) || defined(AS_PSVITA))
#if (defined(AS_ARM) || defined(AS_MIPS)) && (defined(__GNUC__) || defined(AS_PSVITA))
// As the least significant bit in func is used to switch to THUMB mode
// on ARM processors, the LSB in the __delta variable is used instead of
// the one in __pfn on ARM processors.
// MIPS also appear to use the base offset to indicate virtual method.
if( (size_t(internal->baseOffset) & 1) )
internal->callConv = (internalCallConv)(thisCallConv + 2);
#endif
@ -577,10 +578,11 @@ int CallSystemFunction(int id, asCContext *context)
}
// Add the base offset for multiple inheritance
#if (defined(__GNUC__) && defined(AS_ARM)) || defined(AS_PSVITA)
#if (defined(__GNUC__) && (defined(AS_ARM) || defined(AS_MIPS))) || defined(AS_PSVITA)
// On GNUC + ARM the lsb of the offset is used to indicate a virtual function
// and the whole offset is thus shifted one bit left to keep the original
// offset resolution
// MIPS also work like ARM in this regard
obj = (void*)(asPWORD(obj) + (sysFunc->baseOffset>>1));
#else
obj = (void*)(asPWORD(obj) + sysFunc->baseOffset);
@ -634,10 +636,11 @@ int CallSystemFunction(int id, asCContext *context)
}
// Add the base offset for multiple inheritance
#if (defined(__GNUC__) && defined(AS_ARM)) || defined(AS_PSVITA)
#if (defined(__GNUC__) && (defined(AS_ARM) || defined(AS_MIPS))) || defined(AS_PSVITA)
// On GNUC + ARM the lsb of the offset is used to indicate a virtual function
// and the whole offset is thus shifted one bit left to keep the original
// offset resolution
// MIPS also work like ARM in this regard
tempPtr = (void*)(asPWORD(tempPtr) + (sysFunc->baseOffset>>1));
#else
tempPtr = (void*)(asPWORD(tempPtr) + sysFunc->baseOffset);
@ -822,7 +825,15 @@ int CallSystemFunction(int id, asCContext *context)
if( cleanCount )
{
args = context->m_regs.stackPointer;
if( callConv >= ICC_THISCALL )
// Skip the hidden argument for the return pointer
// TODO: runtime optimize: This check and increment should have been done in PrepareSystemFunction
if( descr->DoesReturnOnStack() )
args += AS_PTR_SIZE;
// Skip the object pointer on the stack
// TODO: runtime optimize: This check and increment should have been done in PrepareSystemFunction
if( callConv >= ICC_THISCALL && sysFunc->objForThiscall == 0 )
args += AS_PTR_SIZE;
asSSystemFunctionInterface::SClean *clean = sysFunc->cleanArgs.AddressOf();

View File

@ -1,6 +1,6 @@
/*
AngelCode Scripting Library
Copyright (c) 2003-2014 Andreas Jonsson
Copyright (c) 2003-2015 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
@ -39,11 +39,13 @@
The assembler routines for Linux were written by Carlos Luna in December 2012
*/
#if !defined(AS_MAX_PORTABILITY)
#if defined(__arm__) || defined(__ARM__) || defined(I3D_ARCH_ARM)
#if !defined(__linux__) || defined(__ANDROID__) || defined(ANDROID) || defined(__SOFTFP__)
/* iOS, Android, and Marmalade goes here */
/* iOS, Android, Marmalade, and Linux with soft-float ABI goes here */
.global armFunc
.global armFuncR0
@ -85,7 +87,12 @@ stackargsloop:
bne stackargsloop
mov sp, r12
nomoreargs:
#if defined (__ARM_ARCH_4T__) || defined (__ARM_ARCH_4__)
mov lr, pc /* older ARM didn't support blx */
mov pc, r4
#else
blx r4
#endif
add sp, sp, r8
ldmia sp!, {r4-r8, pc}
@ -133,7 +140,12 @@ stackargslooparmFuncObjLast:
bne stackargslooparmFuncObjLast
mov sp, r12
nomoreargsarmFuncObjLast:
#if defined (__ARM_ARCH_4T__) || defined (__ARM_ARCH_4__)
mov lr, pc /* older ARM didn't support blx */
mov pc, r4
#else
blx r4
#endif
add sp, sp, r8
ldmia sp!, {r4-r8, pc}
@ -180,7 +192,12 @@ stackargslooparmFuncR0ObjLast:
bne stackargslooparmFuncR0ObjLast
mov sp, r12
nomoreargsarmFuncR0ObjLast:
#if defined (__ARM_ARCH_4T__) || defined (__ARM_ARCH_4__)
mov lr, pc /* older ARM didn't support blx */
mov pc, r4
#else
blx r4
#endif
add sp, sp, r8
ldmia sp!, {r4-r8, pc}
@ -218,7 +235,12 @@ stackargslooparmFuncR0:
bne stackargslooparmFuncR0
mov sp, r12
nomoreargsarmFuncR0:
#if defined (__ARM_ARCH_4T__) || defined (__ARM_ARCH_4__)
mov lr, pc /* older ARM didn't support blx */
mov pc, r4
#else
blx r4
#endif
add sp, sp, r8
ldmia sp!, {r4-r8, pc}
@ -255,14 +277,19 @@ stackargslooparmFuncR0R1:
bne stackargslooparmFuncR0R1
mov sp, r12
nomoreargsarmFuncR0R1:
#if defined (__ARM_ARCH_4T__) || defined (__ARM_ARCH_4__)
mov lr, pc /* older ARM didn't support blx */
mov pc, r4
#else
blx r4
#endif
add sp, sp, r8
ldmia sp!, {r4-r8, pc}
/* --------------------------------------------------------------------------------------------*/
#elif defined(__linux__) && !defined(__SOFTFP__)
/* The Linux code goes here */
/* The Linux with hard-float ABI code goes here */
/* These codes are suitable for armeabi + vfp / armeabihf */
@ -340,7 +367,7 @@ stackargsloop:
mov sp, r12
nomoreargs:
#if defined (___ARM_ARCH_4T__) || defined (__ARM_ARCH_4__)
#if defined (__ARM_ARCH_4T__) || defined (__ARM_ARCH_4__)
mov lr, pc /* older ARM didn't support blx */
mov pc, r4
#else
@ -427,8 +454,8 @@ stackargslooparmFuncObjLast:
mov sp, r12
nomoreargsarmFuncObjLast:
#if defined (___ARM_ARCH_4T__) || defined (__ARM_ARCH_4__)
mov lr, pc
#if defined (__ARM_ARCH_4T__) || defined (__ARM_ARCH_4__)
mov lr, pc /* older ARM didn't support blx */
mov pc, r4
#else
blx r4
@ -521,8 +548,8 @@ stackargslooparmFuncR0ObjLast:
mov sp, r12
nomoreargsarmFuncR0ObjLast:
#if defined (___ARM_ARCH_4T__) || defined (__ARM_ARCH_4__)
mov lr, pc
#if defined (__ARM_ARCH_4T__) || defined (__ARM_ARCH_4__)
mov lr, pc /* older ARM didn't support blx */
mov pc, r4
#else
blx r4
@ -597,8 +624,8 @@ stackargslooparmFuncR0:
mov sp, r12
nomoreargsarmFuncR0:
#if defined (___ARM_ARCH_4T__) || defined (__ARM_ARCH_4__)
mov lr, pc
#if defined (__ARM_ARCH_4T__) || defined (__ARM_ARCH_4__)
mov lr, pc /* older ARM didn't support blx */
mov pc, r4
#else
blx r4
@ -677,8 +704,8 @@ stackargslooparmFuncR0R1:
mov sp, r12
nomoreargsarmFuncR0R1:
#if defined (___ARM_ARCH_4T__) || defined (__ARM_ARCH_4__)
mov lr, pc
#if defined (__ARM_ARCH_4T__) || defined (__ARM_ARCH_4__)
mov lr, pc /* older ARM didn't support blx */
mov pc, r4
#else
blx r4
@ -697,3 +724,6 @@ nomoreargsarmFuncR0R1:
#endif /* arm */
#endif /* !AS_MAX_PORTABILITY */

View File

@ -1,6 +1,6 @@
/*
AngelCode Scripting Library
Copyright (c) 2003-2013 Andreas Jonsson
Copyright (c) 2003-2015 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
@ -34,6 +34,8 @@
This code was adapted from as_callfunc_arm_gcc (ARM, Linux hard float) by Brandon Bare on October 2014.
*/
#if !defined(AS_MAX_PORTABILITY)
#ifdef __psp2__
.syntax unified
@ -61,7 +63,7 @@
/* --------------------------------------------------------------------------------------------*/
armFunc:
.fnstart
.fnstart
push {r4-r8, r10, r11, lr} /* sp must be 8-byte alignment for ABI compliance, so the pushed registers must be even */
@ -81,19 +83,19 @@ armFunc:
/* Load the first 4 arguments into r0-r3 */
cmp r7, #4
it ge
it ge
ldrge r0, [r6]
cmp r7, #8
it ge
it ge
ldrge r1, [r6, #4]
cmp r7, #12
it ge
it ge
ldrge r2, [r6, #8]
cmp r7, #16
it ge
it ge
ldrge r3, [r6, #12]
stackargs:
@ -128,11 +130,11 @@ nomoreargs:
pop {r4-r8, r10, r11, pc}
.fnend
.fnend
/* --------------------------------------------------------------------------------------------*/
armFuncObjLast:
.fnstart
.fnstart
push {r4-r8, r10, r11, lr} /* We´re storing r11 just to keep the stack aligned to an 8 byte boundary */
@ -157,28 +159,28 @@ armFuncObjLast:
/* Load the first 4 arguments into r0-r3 */
cmp r7, #4
it ge
it ge
ldrge r0, [r6]
cmp r7, #8
it ge
it ge
ldrge r1, [r6,#4]
it lt
it lt
movlt r1, r5
cmp r7, #12
it ge
it ge
ldrge r2, [r6,#8]
it lt
it lt
movlt r2, r5
cmp r7, #16
it ge
it ge
ldrge r3, [r6,#12]
ittt lt
ittt lt
movlt r3, r5
movlt r5, #0 /* If objlast got placed into a register, r5 = 0 */
blt stackargsFuncObjLast /* If objlast got placed into a register, go to stackargsFuncObjLast */
@ -220,11 +222,11 @@ nomoreargsarmFuncObjLast:
pop {r4-r8, r10,r11, pc}
.fnend
.fnend
/* --------------------------------------------------------------------------------------------*/
armFuncR0ObjLast:
.fnstart
.fnstart
push {r4-r8, r10, r11, lr}
@ -252,35 +254,35 @@ armFuncR0ObjLast:
/* Load the first 3 arguments into r1-r3 */
cmp r7, #4
it ge
it ge
ldrge r1, [r6]
cmp r7, #8
it ge
it ge
ldrge r2, [r6,#4]
it lt
it lt
movlt r2, r5
cmp r7, #12
it ge
it ge
ldrge r3, [r6,#8]
ittt lt
ittt lt
movlt r3, r5
movlt r5, #0 /* If objlast got placed into a register, r5 = 0 */
blt stackargsFuncR0ObjLast /* If objlast got placed into a register, go to stackargsFuncR0ObjLast */
cmp r7, #16 /* Else if we have one last arg set the offset accordingly and store the arg in the array */
itt ge
itt ge
ldrge r7, [r6, #12]
strge r7, [r6, #8]
str r5, [r6, #12] /* Put objlast in r6 + 12 */
mov r5, #0
mov r5, #0
it ge
it ge
movge r5, #4 /* Set r5 with an offset of #4 if there´s one last arg that couldn´t be placed in r registers */
add r5, r5, #4 /* Set r5 with an offset of + #4, so objlast can be loaded into the stack */
@ -318,11 +320,11 @@ nomoreargsarmFuncR0ObjLast:
pop {r4-r8, r10, r11, pc}
.fnend
.fnend
/* --------------------------------------------------------------------------------------------*/
armFuncR0:
.fnstart
.fnstart
push {r4-r8, r10, r11, lr}
@ -344,19 +346,19 @@ armFuncR0:
/* Load the first 3 arguments into r1-r3 */
cmp r7, #4
it ge
it ge
ldrge r1, [r6]
cmp r7, #8
it ge
it ge
ldrge r2, [r6, #4]
cmp r7, #12
it ge
it ge
ldrge r3, [r6, #8]
cmp r7, #16
it ge
it ge
movge r11, #4 /* If there is still one arg to be placed, set the offset in r11 to #4 */
stackargsarmFuncR0:
@ -393,11 +395,11 @@ nomoreargsarmFuncR0:
pop {r4-r8, r10, r11, pc}
.fnend
.fnend
/* --------------------------------------------------------------------------------------------*/
armFuncR0R1:
.fnstart
.fnstart
push {r4-r8, r10, r11, lr}
@ -421,23 +423,23 @@ armFuncR0R1:
/* Load the first 2 arguments into r2-r3 */
cmp r7, #4
it ge
it ge
ldrge r2, [r6]
cmp r7, #8
it ge
it ge
ldrge r3, [r6, #4]
cmp r7, #12
it ge
it ge
movge r11, #4 /* If there is a third arg to be placed, set the offset in r11 to #4 */
cmp r7, #16
it ge
it ge
movge r11, #8 /* If there is a fourth arg to be placed, set the offset in r11 to #8 */
itt lt
itt lt
ldrlt r7, [r6, #8] /* Else copy the third arg to the correct place in the array */
strlt r7, [r6, #12]
@ -475,6 +477,9 @@ nomoreargsarmFuncR0R1:
pop {r4-r8, r10, r11, pc}
.fnend
.fnend
#endif
#endif /* !AS_MAX_PORTABILITY */

View File

@ -1,6 +1,6 @@
/*
AngelCode Scripting Library
Copyright (c) 2003-2009 Andreas Jonsson
Copyright (c) 2003-2015 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
@ -35,6 +35,8 @@
// Adapted to GNUC by darktemplar216 in September 2009
// Small fixed to work under XCode GCC by Gilad Novik in October 2009
#if !defined(AS_MAX_PORTABILITY)
#if defined(__arm__) || defined(__ARM__)
.align 2
@ -235,3 +237,6 @@ nomoreargsarmFuncR0R1:
ldmia sp!, {r4-r8, pc}
#endif
#endif /* !AS_MAX_PORTABILITY */

View File

@ -1,6 +1,6 @@
/*
AngelCode Scripting Library
Copyright (c) 2003-2014 Andreas Jonsson
Copyright (c) 2003-2015 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
@ -35,7 +35,9 @@
// These functions handle the actual calling of system functions
//
// This version is MIPS specific and was originally written
// by Manu Evans in April, 2006
// by Manu Evans in April, 2006 for Playstation Portable (PSP)
//
// Support for Linux with MIPS was added by Andreas Jonsson in April, 2015
//
@ -52,10 +54,320 @@
#include <stdio.h>
#include <stdlib.h>
#if !defined(AS_ANDROID)
#include <regdef.h>
#endif
BEGIN_AS_NAMESPACE
#if defined(__linux__) && defined(_ABIO32)
// The MIPS ABI used by Linux is implemented here
// (Tested on CI20 MIPS Creator with Debian Linux)
//
// ref: SYSTEM V
// APPLICATION BINARY INTERFACE
// MIPS RISC Processor
// http://math-atlas.sourceforge.net/devel/assembly/mipsabi32.pdf
//
// ref: MIPS Instruction Reference
// http://www.mrc.uidaho.edu/mrc/people/jff/digital/MIPSir.html
union SFloatRegs
{
union { double d0; struct { float f0; asDWORD dummy0; };};
union { double d1; struct { float f1; asDWORD dummy1; };};
} ;
extern "C" asQWORD mipsFunc(asUINT argSize, asDWORD *argBuffer, void *func, SFloatRegs &floatRegs);
asDWORD GetReturnedFloat();
asQWORD GetReturnedDouble();
asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, void *obj, asDWORD *args, void *retPointer, asQWORD &/*retQW2*/, void *secondObject)
{
asCScriptEngine *engine = context->m_engine;
asSSystemFunctionInterface *sysFunc = descr->sysFuncIntf;
int callConv = sysFunc->callConv;
asQWORD retQW = 0;
void *func = (void*)sysFunc->func;
void **vftable;
asDWORD argBuffer[128]; // Ought to be big enough
asASSERT( sysFunc->paramSize < 128 );
asDWORD argOffset = 0;
SFloatRegs floatRegs;
asDWORD floatOffset = 0;
// If the application function returns the value in memory then
// the first argument must be the pointer to that memory
if( sysFunc->hostReturnInMemory )
{
asASSERT( retPointer );
argBuffer[argOffset++] = (asPWORD)retPointer;
}
if( callConv == ICC_CDECL_OBJFIRST || callConv == ICC_CDECL_OBJFIRST_RETURNINMEM ||
callConv == ICC_THISCALL || callConv == ICC_THISCALL_RETURNINMEM ||
callConv == ICC_VIRTUAL_THISCALL || callConv == ICC_VIRTUAL_THISCALL_RETURNINMEM ||
callConv == ICC_THISCALL_OBJFIRST || callConv == ICC_VIRTUAL_THISCALL_OBJFIRST ||
callConv == ICC_THISCALL_OBJFIRST_RETURNINMEM || callConv == ICC_VIRTUAL_THISCALL_OBJFIRST_RETURNINMEM ||
callConv == ICC_THISCALL_OBJLAST || callConv == ICC_VIRTUAL_THISCALL_OBJLAST ||
callConv == ICC_THISCALL_OBJLAST_RETURNINMEM || callConv == ICC_VIRTUAL_THISCALL_OBJLAST_RETURNINMEM )
{
// Add the object pointer as the first argument
argBuffer[argOffset++] = (asPWORD)obj;
}
if( callConv == ICC_THISCALL_OBJFIRST || callConv == ICC_VIRTUAL_THISCALL_OBJFIRST ||
callConv == ICC_THISCALL_OBJFIRST_RETURNINMEM || callConv == ICC_VIRTUAL_THISCALL_OBJFIRST_RETURNINMEM )
{
// Add the second object pointer
argBuffer[argOffset++] = (asPWORD)secondObject;
}
int spos = 0;
for( asUINT n = 0; n < descr->parameterTypes.GetLength(); n++ )
{
asCDataType &paramType = descr->parameterTypes[n];
if( paramType.IsObject() && !paramType.IsObjectHandle() && !paramType.IsReference() )
{
if( paramType.GetObjectType()->flags & COMPLEX_MASK )
{
// The object is passed by reference
argBuffer[argOffset++] = args[spos++];
}
else
{
// Ensure 8byte alignment for classes that need it
if( (paramType.GetObjectType()->flags & asOBJ_APP_CLASS_ALIGN8) && (argOffset & 1) )
argOffset++;
// Copy the object's memory to the buffer
memcpy(&argBuffer[argOffset], *(void**)(args+spos), paramType.GetSizeInMemoryBytes());
// Delete the original memory
engine->CallFree(*(char**)(args+spos));
spos++;
argOffset += paramType.GetSizeInMemoryDWords();
}
}
else if( paramType.GetTokenType() == ttQuestion )
{
// Copy both pointer and type id
argBuffer[argOffset++] = args[spos++];
argBuffer[argOffset++] = args[spos++];
}
else
{
// The first 2 floats or doubles are loaded into the float registers.
// Actually this is only done if they are the first arguments to the function,
// but it doesn't cause any harm to load them into the registers even if they
// won't be used so we don't need to check if they really are the first args.
if( floatOffset == 0 )
{
if( paramType.GetTokenType() == ttFloat )
floatRegs.f0 = *reinterpret_cast<float*>(&args[spos]);
else if( paramType.GetTokenType() == ttDouble )
floatRegs.d0 = *reinterpret_cast<double*>(&args[spos]);
floatOffset++;
}
else if( floatOffset == 1 )
{
if( paramType.GetTokenType() == ttFloat )
floatRegs.f1 = *reinterpret_cast<float*>(&args[spos]);
else if( paramType.GetTokenType() == ttDouble )
floatRegs.d1 = *reinterpret_cast<double*>(&args[spos]);
floatOffset++;
}
// Copy the value directly
if( paramType.GetSizeOnStackDWords() > 1 )
{
// Make sure the argument is 8byte aligned
if( argOffset & 1 )
argOffset++;
*reinterpret_cast<asQWORD*>(&argBuffer[argOffset]) = *reinterpret_cast<asQWORD*>(&args[spos]);
argOffset += 2;
spos += 2;
}
else
argBuffer[argOffset++] = args[spos++];
}
}
if( callConv == ICC_CDECL_OBJLAST || callConv == ICC_CDECL_OBJLAST_RETURNINMEM )
{
// Add the object pointer as the last argument
argBuffer[argOffset++] = (asPWORD)obj;
}
if( callConv == ICC_THISCALL_OBJLAST || callConv == ICC_VIRTUAL_THISCALL_OBJLAST ||
callConv == ICC_THISCALL_OBJLAST_RETURNINMEM || callConv == ICC_VIRTUAL_THISCALL_OBJLAST_RETURNINMEM )
{
// Add the second object pointer
argBuffer[argOffset++] = (asPWORD)secondObject;
}
switch( callConv )
{
case ICC_CDECL:
case ICC_CDECL_RETURNINMEM:
case ICC_STDCALL:
case ICC_STDCALL_RETURNINMEM:
case ICC_CDECL_OBJLAST:
case ICC_CDECL_OBJLAST_RETURNINMEM:
case ICC_CDECL_OBJFIRST:
case ICC_CDECL_OBJFIRST_RETURNINMEM:
case ICC_THISCALL:
case ICC_THISCALL_RETURNINMEM:
case ICC_THISCALL_OBJFIRST:
case ICC_THISCALL_OBJFIRST_RETURNINMEM:
case ICC_THISCALL_OBJLAST:
case ICC_THISCALL_OBJLAST_RETURNINMEM:
retQW = mipsFunc(argOffset*4, argBuffer, func, floatRegs);
break;
case ICC_VIRTUAL_THISCALL:
case ICC_VIRTUAL_THISCALL_RETURNINMEM:
case ICC_VIRTUAL_THISCALL_OBJFIRST:
case ICC_VIRTUAL_THISCALL_OBJFIRST_RETURNINMEM:
case ICC_VIRTUAL_THISCALL_OBJLAST:
case ICC_VIRTUAL_THISCALL_OBJLAST_RETURNINMEM:
// Get virtual function table from the object pointer
vftable = *(void***)obj;
retQW = mipsFunc(argOffset*4, argBuffer, vftable[asPWORD(func)>>2], floatRegs);
break;
default:
context->SetInternalException(TXT_INVALID_CALLING_CONVENTION);
}
// If the return is a float value we need to get the value from the FP register
if( sysFunc->hostReturnFloat )
{
if( sysFunc->hostReturnSize == 1 )
*(asDWORD*)&retQW = GetReturnedFloat();
else
retQW = GetReturnedDouble();
}
return retQW;
}
asDWORD GetReturnedFloat()
{
asDWORD f;
asm("swc1 $f0, %0\n" : "=m"(f));
return f;
}
asQWORD GetReturnedDouble()
{
asQWORD d = 0;
asm("sdc1 $f0, %0\n" : "=m"(d));
return d;
}
// asQWORD mipsFunc(asUINT argSize, asDWORD *argBuffer, void *func, SFloatRegs &floatRegs);
// $2,$3 $4 $5 $6 $7
asm(
" .text\n"
//" .align 2\n"
" .cfi_startproc\n"
" .global mipsFunc\n"
" .ent mipsFunc\n"
"mipsFunc:\n"
//" .frame $fp,64,$31 # vars= 0, regs= 0/0, args= 0, gp= 0\n"
//" .mask 0x00000000,0\n"
//" .fmask 0x00000000,0\n"
" .set noreorder\n"
" .set nomacro\n"
// align the stack frame to 8 bytes
" addiu $12, $4, 7\n" // t4 ($12) = argSize ($4) + 7
" li $13, -8\n" // t5 ($13) = 0xfffffffffffffff8
" and $12, $12, $13\n" // t4 ($12) &= t5 ($13). t4 holds the size of the argument block
// It is required that the caller reserves space for at least 16 bytes even if there are less than 4 arguments
// and add 8 bytes for the return pointer and s0 ($16) backup
" addiu $13, $12, 24\n" // t5 = t4 + 24. t5 ($13) holds the total size of the stack frame (including return pointer)
// save the s0 register (so we can use it to remember where our return pointer is lives)
" sw $16, -4($sp)\n" // store the s0 register (so we can use it to remember how big our stack frame is)
" .cfi_offset 16, -4\n"
// store the return pointer
" sw $31, -8($sp)\n"
" .cfi_offset 31, -8\n"
// keep original stack pointer
" move $16, $sp\n"
" .cfi_def_cfa_register 16\n"
// push the stack
" subu $sp, $sp, $13\n"
// store the argument in temporary registers
" addiu $25, $6, 0\n" // t9 ($25) holds the function pointer (must be t9 for position independent code)
" addiu $3, $4, 0\n" // v1 ($3) holds the size of the argument buffer
" move $15, $5\n" // t7 ($15) holds the pointer to the argBuffer
" move $14, $7\n" // t6 ($14) holds the values for the float registers
// load integer registers
" lw $4, 0($15)\n" // a0 ($4)
" lw $5, 4($15)\n" // a1 ($5)
" lw $6, 8($15)\n" // a2 ($6)
" lw $7, 12($15)\n" // a3 ($7)
// load float registers
" ldc1 $f12, 8($14)\n"
" ldc1 $f14, 0($14)\n"
// skip stack parameters if there are 4 or less as they are moved into the registers
" addi $14, $3, -16\n" // The first 4 args were already loaded into registers
" blez $14, andCall\n"
" nop\n"
// push stack parameters
"pushArgs:\n"
" addi $3, -4\n"
// load from $15 + stack bytes ($3)
" addu $14, $15, $3\n"
" lw $14, 0($14)\n"
// store to $sp + stack bytes ($3)
" addu $13, $sp, $3\n"
" sw $14, 0($13)\n"
// if there are more, loop...
" bne $3, $0, pushArgs\n"
" nop\n"
// and call the function
"andCall:\n"
" jalr $25\n"
" nop\n"
// restore original stack pointer
" move $sp, $16\n"
// restore the return pointer
" lw $31, -8($sp)\n"
// restore the original value of $16
" lw $16, -4($sp)\n"
// and return from the function
" jr $31\n"
" nop\n"
" .set macro\n"
" .set reorder\n"
" .end mipsFunc\n"
" .cfi_endproc\n"
" .size mipsFunc, .-mipsFunc\n"
);
#else // !(defined(__linux__) && defined(_ABIO32))
// The MIPS ABI used by PSP and PS2 is implemented here
#define AS_MIPS_MAX_ARGS 32
#define AS_NUM_REG_FLOATS 8
#define AS_NUM_REG_INTS 8
@ -191,33 +503,12 @@ asDWORD GetReturnedFloat()
return f;
}
/*
asDWORD GetReturnedFloat();
asm(
" .align 4\n"
" .global GetReturnedFloat\n"
"GetReturnedFloat:\n"
" .set noreorder\n"
" .set nomacro\n"
" j $ra\n"
" mfc1 $v0, $f0\n"
" .set macro\n"
" .set reorder\n"
" .end Func\n"
*/
// sizeof(double) == 4 with sh-elf-gcc (3.4.0) -m4
// so this isn't really used...
asQWORD GetReturnedDouble()
{
asQWORD d = 0;
printf("Broken!!!");
/*
asm("sw $v0, %0\n" : "=m"(d));
*/
asm("sdc1 $f0, %0\n" : "=m"(d));
return d;
}
@ -352,7 +643,7 @@ asm(
" .set nomacro\n"
// align the stack frame to 8 bytes
" addiu $12, $6, 7\n"
" li $13, -8\n" // 0xfffffffffffffffc
" li $13, -8\n" // 0xfffffffffffffff8
" and $12, $12, $13\n" // t4 holds the size of the argument block
// and add 8 bytes for the return pointer and s0 backup
" addiu $13, $12, 8\n" // t5 holds the total size of the stack frame (including return pointer)
@ -432,6 +723,8 @@ asm(
" .size mipsFunc, .-mipsFunc\n"
);
#endif // PSP and PS2 MIPS ABI
END_AS_NAMESPACE
#endif // AS_MIPS

View File

@ -1,6 +1,6 @@
/*
AngelCode Scripting Library
Copyright (c) 2003-2014 Andreas Jonsson
Copyright (c) 2003-2015 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
@ -73,14 +73,18 @@ static asQWORD __attribute__((noinline)) X64_CallFunction(const asQWORD *args, i
__asm__ __volatile__ (
" movq %0, %%rcx \n" // rcx = cnt
" movq %0, %%rcx \n" // rcx = cnt
" movq %1, %%r10 \n" // r10 = args
" movq %2, %%r11 \n" // r11 = func
// Backup stack pointer in R15 that is guaranteed to maintain its value over function calls
" movq %%rsp, %%r15 \n"
#ifdef __OPTIMIZE__
// Make sure the stack unwind logic knows we've backed up the stack pointer in register r15
// This should only be done if any optimization is done. If no optimization (-O0) is used,
// then the compiler already backups the rsp before entering the inline assembler code
" .cfi_def_cfa_register r15 \n"
#endif
// Skip the first 128 bytes on the stack frame, called "red zone",
// that might be used by the compiler to store temporary values
@ -100,7 +104,7 @@ static asQWORD __attribute__((noinline)) X64_CallFunction(const asQWORD *args, i
" jle endstack \n"
" subl $1, %%esi \n"
" xorl %%edx, %%edx \n"
" leaq 8(, %%rsi, 8), %%rcx \n"
" leaq 8(, %%rsi, 8), %%rcx \n"
"loopstack: \n"
" movq 112(%%r10, %%rdx), %%rax \n"
" pushq %%rax \n"
@ -128,12 +132,16 @@ static asQWORD __attribute__((noinline)) X64_CallFunction(const asQWORD *args, i
" movsd 56(%%rax), %%xmm7 \n"
// Call the function
" call *%%r11 \n"
" call *%%r11 \n"
// Restore stack pointer
" mov %%r15, %%rsp \n"
#ifdef __OPTIMIZE__
// Inform the stack unwind logic that the stack pointer has been restored
// This should only be done if any optimization is done. If no optimization (-O0) is used,
// then the compiler already backups the rsp before entering the inline assembler code
" .cfi_def_cfa_register rsp \n"
#endif
// Put return value in retQW1 and retQW2, using either RAX:RDX or XMM0:XMM1 depending on type of return value
" movl %5, %%ecx \n"

View File

@ -1,6 +1,6 @@
/*
AngelCode Scripting Library
Copyright (c) 2003-2014 Andreas Jonsson
Copyright (c) 2003-2015 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
@ -1263,13 +1263,13 @@ endcopy:
"subl $4, %%ecx \n"
"jne copyloop3 \n"
"endcopy3: \n"
#if defined(__MINGW32__) && ((__GNUC__ == 4 && __GNUC_MINOR__ >= 7) || __GNUC__ > 4)
// MinGW made some strange choices with 4.7, and the thiscall calling convention
// when returning an object in memory is completely different from when not returning
// in memory
"pushl 0(%%ebx) \n" // push obj on the stack
"movl 16(%%ebx), %%ecx \n" // move the return pointer into ECX
"call *12(%%ebx) \n" // call the function
#ifdef AS_MINGW47
// MinGW made some strange choices with 4.7 and the thiscall calling convention,
// returning an object in memory is completely different from when not returning
// in memory
"pushl 0(%%ebx) \n" // push obj on the stack
"movl 16(%%ebx), %%ecx \n" // move the return pointer into ECX
"call *12(%%ebx) \n" // call the function
#else
"movl 0(%%ebx), %%ecx \n" // move obj into ECX
#ifdef THISCALL_PASS_OBJECT_POINTER_ON_THE_STACK
@ -1286,7 +1286,7 @@ endcopy:
"addl $4, %%esp \n" // pop the object pointer
#endif
#endif
#endif // MINGW
#endif // AS_MINGW47
// Pop the alignment bytes
"popl %%esp \n"
"popl %%ebx \n"

View File

@ -1,6 +1,6 @@
/*
AngelCode Scripting Library
Copyright (c) 2003-2014 Andreas Jonsson
Copyright (c) 2003-2015 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
@ -43,7 +43,9 @@
// various fixes in asm ppcFunc
// fix for variable arguments
//
// Modified by Anthony Clark May 2015
// Fixed the issue where int64 and uint64 could not be passed nativly
// few minor fixes within asm ppcFunc to handle int64 and uint64
// XBox 360 calling convention
@ -87,7 +89,6 @@
// References:
// https://www-01.ibm.com/chips/techlib/techlib.nsf/techdocs/852569B20050FF77852569970071B0D6/$file/eabi_app.pdf
//
// TODO: The code doesn't handle int64 and uint64 parameters
// TODO: The code doesn't handle objects passed by value (unless they are max 4 bytes in size)
@ -128,7 +129,7 @@ enum argTypes
// pArgs is the array of the argument values
// pArgTypes is an array containing a byte indicating the type (enum argTypes) for each argument.
// dwFunc is the address of the function that will be called
asQWORD __declspec( naked ) ppcFunc(const asDWORD* pArgs, asDWORD dwFunc, const asBYTE* pArgTypes)
asQWORD __declspec( naked ) ppcFunc(const asQWORD* pArgs, asDWORD dwFunc, const asBYTE* pArgTypes)
{
__asm
{
@ -202,7 +203,7 @@ ppcNextArg:
//////////////////////////////////////////////////////////////////////////
ppcArgIsInteger:
// Get the arg from the stack
lwz r12, 0(r26)
ld r12, 0(r26)
// r23 holds the integer arg count so far
cmplwi cr6, r23, 0
@ -251,11 +252,11 @@ ppcArgIsInteger:
b ppcLoadIntRegUpd
ppcLoadIntRegUpd:
stw r12, 0(r31) // push on the stack
std r12, 0(r31) // push on the stack
addi r31, r31, 8 // inc stack by 1 reg
addi r23, r23, 1 // Increment used int register count
addi r26, r26, 4 // Increment pArgs
addi r26, r26, 8 // Increment pArgs
b ppcNextArg // Call next arg
//////////////////////////////////////////////////////////////////////////
@ -511,7 +512,7 @@ asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr,
// Pack the arguments into an array that ppcFunc() can use to load each CPU register properly
asBYTE ppcArgsType[AS_PPC_MAX_ARGS + AS_PPC_RETURNINMEM_REG + AS_PPC_THISCALL_REG + AS_PPC_ENDOFARGS];
asDWORD ppcArgs[AS_PPC_MAX_ARGS + AS_PPC_RETURNINMEM_REG + AS_PPC_THISCALL_REG];
asQWORD ppcArgs[AS_PPC_MAX_ARGS + AS_PPC_RETURNINMEM_REG + AS_PPC_THISCALL_REG];
int argsCnt = 0;
// If the function returns an object in memory, we allocate the memory and put the ptr to the front (will go to r3)
@ -625,35 +626,34 @@ asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr,
}
else
{
// TODO: How should int64 and uint64 be passed natively?
// Currently the code doesn't handle these types
// TODO: The code also ignore the fact that large objects
// passed by value has been copied to the stack
// in the above loop.
*pCurArgType++ = ppcINTARG;
*((int*) pCurFixedArgValue) = *((int*) pCurStackArgValue);
*((asQWORD*) pCurFixedArgValue) = *((asUINT*) pCurStackArgValue);
if( !descr->parameterTypes[n].IsReference() )
{
// If the arg is less that 4 bytes, then move the
// bytes to the higher bytes within the dword
// If the arg is not 4 bytes which we coppied, lets do it again the right way
asUINT numBytes = descr->parameterTypes[n].GetSizeInMemoryBytes();
if( numBytes == 1 )
{
pCurFixedArgValue[3] = pCurFixedArgValue[0];
pCurFixedArgValue[0] = 0;
*((asQWORD*) pCurFixedArgValue) = *((asBYTE*) pCurStackArgValue);
}
else if( numBytes == 2 )
{
*(asWORD*)&pCurFixedArgValue[2] = *(asWORD*)&pCurFixedArgValue[0];
*(asWORD*)&pCurFixedArgValue[0] = 0;
*((asQWORD*) pCurFixedArgValue) = *((asWORD*) pCurStackArgValue);
}
else if( numBytes == 8 )
{
*((asQWORD*) pCurFixedArgValue) = *((asQWORD*) pCurStackArgValue);
pCurStackArgValue += 4; // Increase our cur stack arg value by 4 bytes to = 8 total later
}
}
pCurFixedArgValue += 4;
pCurFixedArgValue += 8;
pCurStackArgValue += 4;
// if it is a variable argument, account for the typeId
@ -703,7 +703,7 @@ asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr,
// Add the object pointer as the last argument
ppcArgsType[argsCnt++] = ppcINTARG;
ppcArgsType[argsCnt] = ppcENDARG;
*((asPWORD*)pCurFixedArgValue) = (asPWORD)obj;
*((asQWORD*)pCurFixedArgValue) = (asPWORD)obj;
retQW = ppcFunc( ppcArgs, (asDWORD)func, ppcArgsType );
break;
}

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
/*
AngelCode Scripting Library
Copyright (c) 2003-2014 Andreas Jonsson
Copyright (c) 2003-2015 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
@ -73,14 +73,9 @@ struct asSExprContext
{
asSExprContext(asCScriptEngine *engine) : bc(engine)
{
exprNode = 0;
origExpr = 0;
property_get = 0;
property_set = 0;
property_const = false;
property_handle = false;
property_ref = false;
property_arg = 0;
property_arg = 0;
Clear();
}
~asSExprContext()
{
@ -90,43 +85,95 @@ struct asSExprContext
void Clear()
{
bc.ClearAll();
type.SetDummy();
type.Set(asCDataType());
deferredParams.SetLength(0);
if( property_arg )
asDELETE(property_arg, asSExprContext);
property_arg = 0;
deferredParams.SetLength(0);
exprNode = 0;
origExpr = 0;
property_get = 0;
property_set = 0;
property_const = false;
property_handle = false;
property_ref = false;
methodName = "";
enumValue = "";
property_arg = 0;
exprNode = 0;
origExpr = 0;
property_get = 0;
property_set = 0;
property_const = false;
property_handle = false;
property_ref = false;
methodName = "";
enumValue = "";
isVoidExpression = false;
isCleanArg = false;
}
bool IsClassMethod()
bool IsClassMethod() const
{
if( type.dataType.GetObjectType() == 0 ) return false;
if( methodName == "" ) return false;
if( type.dataType.GetObjectType() == &type.dataType.GetObjectType()->engine->functionBehaviours ) return false;
return true;
}
bool IsGlobalFunc()
bool IsGlobalFunc() const
{
if( type.dataType.GetObjectType() == 0 ) return false;
if( methodName == "" ) return false;
if( type.dataType.GetObjectType() != &type.dataType.GetObjectType()->engine->functionBehaviours ) return false;
return true;
}
void SetLambda(asCScriptNode *funcDecl)
{
asASSERT( funcDecl && funcDecl->nodeType == snFunction );
asASSERT( bc.GetLastInstr() == -1 );
Clear();
type.SetUndefinedFuncHandle(bc.GetEngine());
exprNode = funcDecl;
}
bool IsLambda() const
{
if( type.IsUndefinedFuncHandle() && exprNode && exprNode->nodeType == snFunction )
return true;
return false;
}
void SetVoidExpression()
{
Clear();
type.SetVoid();
isVoidExpression = true;
}
bool IsVoidExpression() const
{
if( isVoidExpression && type.IsVoid() && exprNode == 0 )
return true;
return false;
}
void Merge(asSExprContext *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;
after->property_arg = 0;
// Do not copy the origExpr member
}
asCByteCode bc;
asCTypeInfo type;
int property_get;
int property_set;
bool property_const; // If the object that is being accessed through property accessor is read-only
bool property_handle; // If the property accessor is called on an object stored in a handle
bool property_ref; // If the property accessor is called on a reference
bool property_const; // If the object that is being accessed through property accessor is read-only
bool property_handle; // If the property accessor is called on an object stored in a handle
bool property_ref; // If the property accessor is called on a reference
bool isVoidExpression; // Set to true if the expression is an explicit 'void', e.g. used to ignore out parameters in func calls
bool isCleanArg; // Set to true if the expression has only been initialized with default constructor
asSExprContext *property_arg;
asCArray<asSDeferredParam> deferredParams;
asCScriptNode *exprNode;
@ -212,8 +259,8 @@ protected:
int CompileExpressionPostOp(asCScriptNode *node, asSExprContext *out);
int CompileExpressionValue(asCScriptNode *node, asSExprContext *out);
int CompileFunctionCall(asCScriptNode *node, asSExprContext *out, asCObjectType *objectType, bool objIsConst, const asCString &scope = "");
void CompileConstructCall(asCScriptNode *node, asSExprContext *out);
void CompileConversion(asCScriptNode *node, asSExprContext *out);
int CompileConstructCall(asCScriptNode *node, asSExprContext *out);
int CompileConversion(asCScriptNode *node, asSExprContext *out);
int CompileOperator(asCScriptNode *node, asSExprContext *l, asSExprContext *r, asSExprContext *out, eTokenType opToken = ttUnrecognizedToken);
void CompileOperatorOnHandles(asCScriptNode *node, asSExprContext *l, asSExprContext *r, asSExprContext *out, eTokenType opToken = ttUnrecognizedToken);
void CompileMathOperator(asCScriptNode *node, asSExprContext *l, asSExprContext *r, asSExprContext *out, eTokenType opToken = ttUnrecognizedToken);
@ -239,6 +286,7 @@ protected:
void CompileInitAsCopy(asCDataType &type, int offset, asCByteCode *bc, asSExprContext *arg, asCScriptNode *node, bool derefDestination);
// Helper functions
void ConvertToPostFix(asCScriptNode *expr, asCArray<asCScriptNode *> &postfix);
void ProcessPropertyGetAccessor(asSExprContext *ctx, asCScriptNode *node);
int ProcessPropertySetAccessor(asSExprContext *ctx, asSExprContext *arg, asCScriptNode *node);
int ProcessPropertyGetSetAccessor(asSExprContext *ctx, asSExprContext *lctx, asSExprContext *rctx, eTokenType op, asCScriptNode *errNode);
@ -288,6 +336,7 @@ protected:
asUINT ImplicitConvObjectValue(asSExprContext *ctx, const asCDataType &to, asCScriptNode *node, EImplicitConv convType, bool generateCode);
void ImplicitConversionConstant(asSExprContext *ctx, const asCDataType &to, asCScriptNode *node, EImplicitConv convType);
void ImplicitConvObjectToBestMathType(asSExprContext *ctx, asCScriptNode *node);
asUINT ImplicitConvLambdaToFunc(asSExprContext *ctx, const asCDataType &to, asCScriptNode *node, EImplicitConv convType, bool generateCode = true);
void LineInstr(asCByteCode *bc, size_t pos);
@ -307,16 +356,18 @@ protected:
bool hasCompileErrors;
int nextLabel;
int numLambdas;
asCVariableScope *variables;
asCBuilder *builder;
asCScriptEngine *engine;
asCScriptCode *script;
asCVariableScope *variables;
asCBuilder *builder;
asCScriptEngine *engine;
asCScriptCode *script;
asCScriptFunction *outFunc;
bool m_isConstructor;
bool m_isConstructorCalled;
sClassDeclaration *m_classDecl;
bool m_isConstructor;
bool m_isConstructorCalled;
sClassDeclaration *m_classDecl;
sGlobalVariableDescription *m_globalVar;
asCArray<int> breakLabels;
asCArray<int> continueLabels;
@ -353,7 +404,7 @@ protected:
bool isCompilingDefaultArg;
bool isProcessingDeferredParams;
int noCodeOutput;
int noCodeOutput;
};
END_AS_NAMESPACE

View File

@ -586,6 +586,7 @@
#define AS_NO_MEMORY_H
#define AS_MIPS
#define AS_PSP
#define AS_USE_DOUBLE_AS_FLOAT
// PSVita
#elif defined(__psp2__)
#define AS_PSVITA
@ -799,6 +800,14 @@
// As of version 4.7 MinGW changed the ABI, presumably
// to be better aligned with how MSVC works
#if (__GNUC__ == 4 && __GNUC_MINOR__ >= 7) || __GNUC__ > 4
#define AS_MINGW47
#endif
#if (__clang_major__ == 3 && __clang_minor__ > 4) || __clang_major > 3
#define AS_MINGW47
#endif
#ifdef AS_MINGW47
#undef CALLEE_POPS_HIDDEN_RETURN_POINTER
#define THISCALL_CALLEE_POPS_ARGUMENTS
#else
@ -845,7 +854,7 @@
// STDCALL is not available on 64bit Linux
#undef STDCALL
#define STDCALL
#elif defined(__ARMEL__) || defined(__arm__)
#elif (defined(__ARMEL__) || defined(__arm__)) && !(defined(__ARM_ARCH_4__) || defined(__ARM_ARCH_4T__))
#define AS_ARM
// TODO: The stack unwind on exceptions currently fails due to the assembler code in as_callfunc_arm_gcc.S
@ -884,11 +893,24 @@
#elif defined(__mips__)
#define AS_MIPS
#define AS_BIG_ENDIAN
#define AS_USE_DOUBLE_AS_FLOAT
#undef STDCALL
#define STDCALL
// Native calling conventions for Linux/Mips do not work yet.
#define AS_MAX_PORTABILITY
#ifdef _ABIO32
#define AS_MIPS
// All structures are returned in memory regardless of size or complexity
#define THISCALL_RETURN_SIMPLE_IN_MEMORY
#define THISCALL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE 0
#define CDECL_RETURN_SIMPLE_IN_MEMORY
#define CDECL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE 0
#define STDCALL_RETURN_SIMPLE_IN_MEMORY
#define CDECL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE 0
#undef AS_NO_THISCALL_FUNCTOR_METHOD
#else
// For other ABIs the native calling convention is not available (yet)
#define AS_MAX_PORTABILITY
#endif
#else
#define AS_MAX_PORTABILITY
#endif
@ -936,6 +958,7 @@
// Support native calling conventions on MIPS architecture
#if (defined(_MIPS_ARCH) || defined(_mips) || defined(__MIPSEL__)) && !defined(__LP64__)
#define AS_MIPS
#define AS_USE_DOUBLE_AS_FLOAT
#else
#define AS_MAX_PORTABILITY
#endif
@ -988,6 +1011,9 @@
#if (defined(_ARM_) || defined(__arm__))
// Android ARM
// TODO: The stack unwind on exceptions currently fails due to the assembler code in as_callfunc_arm_gcc.S
#define AS_NO_EXCEPTIONS
#undef THISCALL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE
#undef CDECL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE
#undef STDCALL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE
@ -1013,6 +1039,26 @@
#define THISCALL_PASS_OBJECT_POINTER_ON_THE_STACK
#define AS_X86
#undef AS_NO_THISCALL_FUNCTOR_METHOD
#elif defined(__mips__)
#define AS_MIPS
#undef STDCALL
#define STDCALL
#ifdef _ABIO32
#define AS_MIPS
// All structures are returned in memory regardless of size or complexity
#define THISCALL_RETURN_SIMPLE_IN_MEMORY
#define THISCALL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE 0
#define CDECL_RETURN_SIMPLE_IN_MEMORY
#define CDECL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE 0
#define STDCALL_RETURN_SIMPLE_IN_MEMORY
#define CDECL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE 0
#undef AS_NO_THISCALL_FUNCTOR_METHOD
#else
// For other ABIs the native calling convention is not available (yet)
#define AS_MAX_PORTABILITY
#endif
#endif
// Haiku OS
@ -1047,7 +1093,7 @@
// Support native calling conventions on Intel 32bit CPU
#define THISCALL_PASS_OBJECT_POINTER_ON_THE_STACK
#define AS_X86
#elif defined(__LP64__)
#elif defined(__x86_64__)
#define AS_X64_GCC
#define HAS_128_BIT_PRIMITIVES
#define SPLIT_OBJS_BY_MEMBER_TYPES
@ -1112,11 +1158,6 @@
// Nothing special here
#endif
// MIPS architecture (generally PS2 and PSP consoles, potentially supports N64 as well)
#if defined(_MIPS_ARCH) || defined(_mips) || defined(__MIPSEL__) || defined(__PSP__) || defined(__psp__) || defined(_EE_) || defined(_PSP) || defined(_PS2)
#define AS_USE_DOUBLE_AS_FLOAT // use 32bit floats instead of doubles
#endif
// PowerPC, e.g. Mac, GameCube, PS3, XBox 360, Wii
#if defined(__PPC__) || defined(__ppc__) || defined(_PPC_) || defined(EPPC)
#define AS_BIG_ENDIAN

View File

@ -134,17 +134,32 @@ public:
#endif
// interface
AS_API asIScriptContext *asGetActiveContext()
{
asCThreadLocalData *tld = asCThreadManager::GetLocalData();
if( tld->activeContexts.GetLength() == 0 )
// tld can be 0 if asGetActiveContext is called before any engine has been created.
// Observe! I've seen a case where an application linked with the library twice
// and thus ended up with two separate instances of the code and global variables.
// The application somehow mixed the two instances so that a function called from
// a script ended up calling asGetActiveContext from the other instance that had
// never been initialized.
if( tld == 0 || tld->activeContexts.GetLength() == 0 )
return 0;
return tld->activeContexts[tld->activeContexts.GetLength()-1];
}
// internal
// Note: There is no asPopActiveContext(), just call tld->activeContexts.PopLast() instead
asCThreadLocalData *asPushActiveContext(asIScriptContext *ctx)
{
asCThreadLocalData *tld = asCThreadManager::GetLocalData();
asASSERT( tld );
if( tld == 0 )
return 0;
tld->activeContexts.PushLast(ctx);
return tld;
}
@ -1304,8 +1319,9 @@ int asCContext::Execute()
}
// Pop the active context
asASSERT(tld->activeContexts[tld->activeContexts.GetLength()-1] == this);
tld->activeContexts.PopLast();
asASSERT(tld && tld->activeContexts[tld->activeContexts.GetLength()-1] == this);
if( tld )
tld->activeContexts.PopLast();
if( m_status == asEXECUTION_FINISHED )
{
@ -4301,6 +4317,67 @@ void asCContext::ExecuteNext()
}
l_bc += 2;
break;
case asBC_Thiscall1:
// This instruction is a faster version of asBC_CALLSYS. It is faster because
// it has much less runtime overhead with determining the calling convention
// and no dynamic code for loading the parameters. The instruction can only
// be used to call functions with the following signatures:
//
// type &obj::func(int)
// type &obj::func(uint)
// void obj::func(int)
// void obj::func(uint)
{
// Get function ID from the argument
int i = asBC_INTARG(l_bc);
// Need to move the values back to the context as the called functions
// may use the debug interface to inspect the registers
m_regs.programPointer = l_bc;
m_regs.stackPointer = l_sp;
m_regs.stackFramePointer = l_fp;
// Pop the thispointer from the stack
void *obj = *(void**)l_sp;
l_sp += AS_PTR_SIZE;
// Pop the int arg from the stack
int arg = *(int*)l_sp;
l_sp++;
// Call the method
m_callingSystemFunction = m_engine->scriptFunctions[i];
void *ptr = m_engine->CallObjectMethodRetPtr(obj, arg, m_callingSystemFunction);
m_callingSystemFunction = 0;
*(asPWORD*)&m_regs.valueRegister = (asPWORD)ptr;
// Update the program position after the call so that line number is correct
l_bc += 2;
if( m_regs.doProcessSuspend )
{
// Should the execution be suspended?
if( m_doSuspend )
{
m_regs.programPointer = l_bc;
m_regs.stackPointer = l_sp;
m_regs.stackFramePointer = l_fp;
m_status = asEXECUTION_SUSPENDED;
return;
}
// An exception might have been raised
if( m_status != asEXECUTION_ACTIVE )
{
m_regs.programPointer = l_bc;
m_regs.stackPointer = l_sp;
m_regs.stackFramePointer = l_fp;
return;
}
}
}
break;
// Don't let the optimizer optimize for size,
// since it requires extra conditions and jumps
@ -4887,7 +4964,7 @@ int asCContext::GetExceptionLineNumber(int *column, const char **sectionName)
if( sectionName )
{
// The section index can be -1 if the exception was raised in a generated function, e.g. factstub for templates
// The section index can be -1 if the exception was raised in a generated function, e.g. $fact for templates
if( m_exceptionSectionIdx >= 0 )
*sectionName = m_engine->scriptSectionNames[m_exceptionSectionIdx]->AddressOf();
else

View File

@ -227,7 +227,7 @@ asCString asCDataType::Format(asSNameSpace *currNs, bool includeNamespace) const
str += "const";
}
if( isReference )
if( isReference )
str += "&";
return str;

View File

@ -1,6 +1,6 @@
/*
AngelCode Scripting Library
Copyright (c) 2003-2014 Andreas Jonsson
Copyright (c) 2003-2015 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
@ -74,11 +74,19 @@ typedef double float64_t;
#include <direct.h>
#endif
#endif // AS_PSVITA
#endif // _WIN32_WCE
#endif // AS_WII
#endif // !defined(AS_DEBUG)
#if defined(_MSC_VER) && defined(AS_PROFILE)
// Currently only do profiling with MSVC++
#include <mmsystem.h>
#include <direct.h>
#include "as_string.h"
#include "as_map.h"
#include "as_string_util.h"
@ -144,7 +152,7 @@ public:
return time;
}
void End(const char *name, double beginTime)
void End(const char * /*name*/, double beginTime)
{
double time = GetTime();
@ -247,7 +255,7 @@ protected:
END_AS_NAMESPACE
#else // _MSC_VER && AS_PROFILE
#else // !(_MSC_VER && AS_PROFILE)
// Define it so nothing is done
#define TimeIt(x)
@ -257,20 +265,6 @@ END_AS_NAMESPACE
#endif // AS_PSVITA
#endif // _WIN32_WCE
#endif // AS_WII
#else // !defined(AS_DEBUG)
// Define it so nothing is done
#define TimeIt(x)
#endif // !defined(AS_DEBUG)
#endif
#endif // defined(AS_DEBUG_H)

View File

@ -1,6 +1,6 @@
/*
AngelCode Scripting Library
Copyright (c) 2003-2014 Andreas Jonsson
Copyright (c) 2003-2015 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
@ -486,23 +486,18 @@ int asCGarbageCollector::ReportAndReleaseUndestroyedObjects()
engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, msg.AddressOf());
// Add additional info for builtin types
if( gcObj.type->name == "_builtin_function_" )
if( gcObj.type->name == "$func" )
{
// Unfortunately we can't show the function declaration here, because the engine may have released the parameter list already so the declaration would only be misleading
// We need to show the function type too as for example delegates do not have a name
msg.Format(TXT_PREV_FUNC_IS_NAMED_s_TYPE_IS_d, reinterpret_cast<asCScriptFunction*>(gcObj.obj)->GetName(), reinterpret_cast<asCScriptFunction*>(gcObj.obj)->GetFuncType());
engine->WriteMessage("", 0, 0, asMSGTYPE_INFORMATION, msg.AddressOf());
}
else if( gcObj.type->name == "_builtin_objecttype_" )
else if( gcObj.type->name == "$obj" )
{
msg.Format(TXT_PREV_TYPE_IS_NAMED_s, reinterpret_cast<asCObjectType*>(gcObj.obj)->GetName());
engine->WriteMessage("", 0, 0, asMSGTYPE_INFORMATION, msg.AddressOf());
}
else if( gcObj.type->name == "_builtin_globalprop_" )
{
msg.Format(TXT_PREV_TYPE_IS_NAMED_s, reinterpret_cast<asCGlobalProperty*>(gcObj.obj)->name.AddressOf());
engine->WriteMessage("", 0, 0, asMSGTYPE_INFORMATION, msg.AddressOf());
}
// Release the reference that the GC holds if the release functions is still available
if( gcObj.type->beh.release && engine->scriptFunctions[gcObj.type->beh.release] )

View File

@ -46,32 +46,6 @@
BEGIN_AS_NAMESPACE
// TODO: 2.30.0: redesign: Improved method for discarding modules (faster clean-up, less abuse of garbage collector)
//
// I need to separate the reference counter for internal references and outside references:
//
// - Internal references are for example, when the module refers to a function or object since it is declared in the module, or when
// a function refers to another function since it is being called in the code.
// - Outside references are for example object instances holding a reference to the object type, or a context currently
// executing a function.
//
// If no object instances are alive or no contexts are alive it is known that functions from a discarded module
// can be called, so they can be destroyed without any need to execute the complex garbage collection routines.
//
// If there are live objects, the entire module should be kept for safe keeping, though no longer visible.
//
// TODO: It may not be necessary to keep track of internal references. Without keeping track of internal references, can I still
// handle RemoveFunction and RemoveGlobalVariable correctly?
//
// TODO: How to avoid global variables keeping code alive? For example a script object, or a funcdef?
// Can I do a quick check of the object types and functions to count number of outside references, and then do another
// check over the global variables to subtract the outside references coming from these? What if the outside reference
// is added by an application type in a global variable that the engine doesn't know about? Example, a global dictionary
// holding object instances. Should discarding a module immediately destroy the content of the global variables? What if
// a live object tries to access the global variable after it has been discarded? Throwing a script exception is acceptable?
// Perhaps I need to allow the user to provide a clean-up routine that will be executed before destroying the objects.
// Or I might just put that responsibility on the application.
// internal
asCModule::asCModule(const char *name, asCScriptEngine *engine)
@ -315,7 +289,7 @@ int asCModule::Build()
// Don't allow the module to be rebuilt if there are still
// external references that will need the previous code
// TODO: 2.30.0: interface: The asIScriptModule must have a method for querying if the module is used
// TODO: interface: The asIScriptModule must have a method for querying if the module is used
if( HasExternalReferences(false) )
{
engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, TXT_MODULE_IS_IN_USE);
@ -944,9 +918,6 @@ int asCModule::GetGlobalVarIndexByName(const char *name) const
// interface
int asCModule::RemoveGlobalVar(asUINT index)
{
// TODO: 2.30.0: redesign: Before removing the variable, clear it to free the object
// The property shouldn't be orphaned.
asCGlobalProperty *prop = scriptGlobals.Get(index);
if( !prop )
return asINVALID_ARG;
@ -1273,6 +1244,33 @@ int asCModule::AddScriptFunction(asCScriptFunction *func)
func->AddRefInternal();
engine->AddScriptFunction(func);
// If the function that is being added is an already compiled shared function
// then it is necessary to look for anonymous functions that may be declared
// within it and add those as well
if( func->isShared && func->funcType == asFUNC_SCRIPT )
{
// Loop through the byte code and check all the
// asBC_FuncPtr instructions for anonymous functions
asDWORD *bc = func->scriptData->byteCode.AddressOf();
asUINT bcLength = (asUINT)func->scriptData->byteCode.GetLength();
for( asUINT n = 0; n < bcLength; )
{
int c = *(asBYTE*)&bc[n];
if( c == asBC_FuncPtr )
{
asCScriptFunction *f = reinterpret_cast<asCScriptFunction*>(asBC_PTRARG(&bc[n]));
// Anonymous functions start with $
// There are never two equal anonymous functions so it is not necessary to look for duplicates
if( f && f->name[0] == '$' )
{
AddScriptFunction(f);
globalFunctions.Put(f);
}
}
n += asBCTypeSize[asBCInfo[c].type];
}
}
return 0;
}
@ -1700,16 +1698,6 @@ int asCModule::CompileFunction(const char *sectionName, const char *code, int li
// interface
int asCModule::RemoveFunction(asIScriptFunction *func)
{
// TODO: 2.30.0: redesign: Check if there are any references before removing the function
// if there are, just hide it from the visible but do not destroy or
// remove it from the module.
//
// Only if the function has no live references, nor internal references
// can it be immediately removed, and its internal references released.
//
// Check if any previously hidden functions are without references,
// if so they should removed too.
// Find the global function
asCScriptFunction *f = static_cast<asCScriptFunction*>(func);
int idx = globalFunctions.GetIndex(f);
@ -1734,6 +1722,7 @@ int asCModule::AddFuncDef(const asCString &name, asSNameSpace *ns)
func->name = name;
func->nameSpace = ns;
func->module = this;
funcDefs.PushLast(func);

View File

@ -53,6 +53,7 @@ asCObjectType::asCObjectType()
module = 0;
derivedFrom = 0;
size = 0;
typeId = -1; // start as -1 to signal that it hasn't been defined
acceptValueSubType = true;
acceptRefSubType = true;
@ -74,6 +75,7 @@ asCObjectType::asCObjectType(asCScriptEngine *engine)
this->engine = engine;
module = 0;
derivedFrom = 0;
typeId = -1; // start as -1 to signal that it hasn't been defined
acceptValueSubType = true;
acceptRefSubType = true;
@ -241,7 +243,8 @@ void asCObjectType::DestroyInternal()
userData.SetLength(0);
// Remove the type from the engine
engine->RemoveFromTypeIdMap(this);
if( typeId != -1 )
engine->RemoveFromTypeIdMap(this);
// Clear the engine pointer to mark the object type as invalid
engine = 0;
@ -252,7 +255,6 @@ asCObjectType::~asCObjectType()
if( engine == 0 )
return;
// TODO: 2.30.0: redesign: Shouldn't this have been done already?
DestroyInternal();
}
@ -322,12 +324,18 @@ asUINT asCObjectType::GetSize() const
// interface
int asCObjectType::GetTypeId() const
{
// We need a non const pointer to create the asCDataType object.
// We're not breaking anything here because this function is not
// modifying the object, so this const cast is safe.
asCObjectType *ot = const_cast<asCObjectType*>(this);
if( typeId == -1 )
{
// We need a non const pointer to create the asCDataType object.
// We're not breaking anything here because this function is not
// modifying the object, so this const cast is safe.
asCObjectType *ot = const_cast<asCObjectType*>(this);
return engine->GetTypeIdFromDataType(asCDataType::CreateObject(ot, false));
// The engine will define the typeId for this object type
engine->GetTypeIdFromDataType(asCDataType::CreateObject(ot, false));
}
return typeId;
}
// interface

View File

@ -1,6 +1,6 @@
/*
AngelCode Scripting Library
Copyright (c) 2003-2014 Andreas Jonsson
Copyright (c) 2003-2015 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
@ -191,6 +191,7 @@ public:
#ifdef WIP_16BYTE_ALIGN
int alignment;
#endif
mutable int typeId;
asCArray<asCObjectProperty*> properties;
asCArray<int> methods;
asCArray<asCObjectType*> interfaces;

View File

@ -1,6 +1,6 @@
/*
AngelCode Scripting Library
Copyright (c) 2003-2014 Andreas Jonsson
Copyright (c) 2003-2015 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
@ -261,6 +261,13 @@ int asCParser::ParsePropertyDeclaration(asCScriptCode *script)
scriptNode->AddChildLast(ParseType(true));
if( isSyntaxError ) return -1;
// Allow optional '&' to indicate that the property is indirect, i.e. stored as reference
sToken t;
GetToken(&t);
RewindTo(&t);
if( t.type == ttAmp )
scriptNode->AddChildLast(ParseToken(ttAmp));
// Allow optional namespace to be defined before the identifier in case
// the declaration is to be used for searching for an existing property
ParseOptionalScope(scriptNode);
@ -269,7 +276,6 @@ int asCParser::ParsePropertyDeclaration(asCScriptCode *script)
if( isSyntaxError ) return -1;
// The declaration should end after the identifier
sToken t;
GetToken(&t);
if( t.type != ttEnd )
{
@ -402,9 +408,11 @@ asCScriptNode *asCParser::ParseType(bool allowConst, bool allowVariableType, boo
if( isSyntaxError ) return node;
// If the datatype is a template type, then parse the subtype within the < >
GetToken(&t);
RewindTo(&t);
asCScriptNode *type = node->lastChild;
tempString.Assign(&script->code[type->tokenPos], type->tokenLength);
if( engine->IsTemplateType(tempString.AddressOf()) )
if( engine->IsTemplateType(tempString.AddressOf()) && t.type == ttLessThan )
{
GetToken(&t);
if( t.type != ttLessThan )
@ -602,7 +610,7 @@ asCScriptNode *asCParser::ParseIdentifier()
return node;
}
// BNF: PARAMLIST ::= '(' ('void' | (TYPE TYPEMOD [IDENTIFIER] ['=' EXPR] {',' TYPE TYPEMOD [IDENTIFIER] ['=' EXPR]}) ')'
// BNF: PARAMLIST ::= '(' ['void' | (TYPE TYPEMOD [IDENTIFIER] ['=' EXPR] {',' TYPE TYPEMOD [IDENTIFIER] ['=' EXPR]})] ')'
asCScriptNode *asCParser::ParseParameterList()
{
asCScriptNode *node = CreateNode(snParameterList);
@ -1104,10 +1112,13 @@ bool asCParser::CheckTemplateType(sToken &t)
tempString.Assign(&script->code[t.pos], t.length);
if( engine->IsTemplateType(tempString.AddressOf()) )
{
// Expect the sub type within < >
// If the next token is a < then parse the sub-type too
GetToken(&t);
if( t.type != ttLessThan )
return false;
{
RewindTo(&t);
return true;
}
for(;;)
{
@ -1232,7 +1243,7 @@ asCScriptNode *asCParser::ParseCast()
return node;
}
// BNF: EXPRVALUE ::= 'void' | CONSTRUCTCALL | FUNCCALL | VARACCESS | CAST | LITERAL | '(' ASSIGN ')'
// BNF: EXPRVALUE ::= 'void' | CONSTRUCTCALL | FUNCCALL | VARACCESS | CAST | LITERAL | '(' ASSIGN ')' | LAMBDA
asCScriptNode *asCParser::ParseExprValue()
{
asCScriptNode *node = CreateNode(snExprValue);
@ -1250,44 +1261,54 @@ asCScriptNode *asCParser::ParseExprValue()
node->AddChildLast(ParseConstructCall());
else if( t1.type == ttIdentifier || t1.type == ttScope )
{
// Determine the last identifier in order to check if it is a type
sToken t;
if( t1.type == ttScope ) t = t2; else t = t1;
RewindTo(&t);
GetToken(&t2);
while( t.type == ttIdentifier )
// Check if the expression is an anonymous function
if( IsLambda() )
{
t2 = t;
GetToken(&t);
if( t.type == ttScope )
GetToken(&t);
else
break;
node->AddChildLast(ParseLambda());
}
bool isDataType = IsDataType(t2);
bool isTemplateType = false;
if( isDataType )
{
// Is this a template type?
tempString.Assign(&script->code[t2.pos], t2.length);
if( engine->IsTemplateType(tempString.AddressOf()) )
isTemplateType = true;
}
// Rewind so the real parsing can be done, after deciding what to parse
RewindTo(&t1);
// Check if this is a construct call
if( isDataType && (t.type == ttOpenParanthesis || // type()
t.type == ttOpenBracket) ) // type[]()
node->AddChildLast(ParseConstructCall());
else if( isTemplateType && t.type == ttLessThan ) // type<t>()
node->AddChildLast(ParseConstructCall());
else if( IsFunctionCall() )
node->AddChildLast(ParseFunctionCall());
else
node->AddChildLast(ParseVariableAccess());
{
// Determine the last identifier in order to check if it is a type
sToken t;
if( t1.type == ttScope ) t = t2; else t = t1;
RewindTo(&t);
GetToken(&t2);
while( t.type == ttIdentifier )
{
t2 = t;
GetToken(&t);
if( t.type == ttScope )
GetToken(&t);
else
break;
}
bool isDataType = IsDataType(t2);
bool isTemplateType = false;
if( isDataType )
{
// Is this a template type?
tempString.Assign(&script->code[t2.pos], t2.length);
if( engine->IsTemplateType(tempString.AddressOf()) )
isTemplateType = true;
}
GetToken(&t2);
// Rewind so the real parsing can be done, after deciding what to parse
RewindTo(&t1);
// Check if this is a construct call
if( isDataType && (t.type == ttOpenParanthesis || // type()
(t.type == ttOpenBracket && t2.type == ttCloseBracket)) ) // type[]()
node->AddChildLast(ParseConstructCall());
else if( isTemplateType && t.type == ttLessThan ) // type<t>()
node->AddChildLast(ParseConstructCall());
else if( IsFunctionCall() )
node->AddChildLast(ParseFunctionCall());
else
node->AddChildLast(ParseVariableAccess());
}
}
else if( t1.type == ttCast )
node->AddChildLast(ParseCast());
@ -1355,6 +1376,83 @@ asCScriptNode *asCParser::ParseConstant()
return node;
}
bool asCParser::IsLambda()
{
bool isLambda = false;
sToken t;
GetToken(&t);
if( t.type == ttIdentifier && IdentifierIs(t, FUNCTION_TOKEN) )
{
sToken t2;
GetToken(&t2);
if( t2.type == ttOpenParanthesis )
{
// Skip until )
while( t2.type != ttCloseParanthesis && t2.type != ttEnd )
GetToken(&t2);
// The next token must be a {
GetToken(&t2);
if( t2.type == ttStartStatementBlock )
isLambda = true;
}
}
RewindTo(&t);
return isLambda;
}
// BNF: LAMBDA ::= 'function' '(' [IDENTIFIER {',' IDENTIFIER}] ')' STATBLOCK
asCScriptNode *asCParser::ParseLambda()
{
asCScriptNode *node = CreateNode(snFunction);
if( node == 0 ) return 0;
sToken t;
GetToken(&t);
if( t.type != ttIdentifier || !IdentifierIs(t, FUNCTION_TOKEN) )
{
Error(ExpectedToken("function"), &t);
return node;
}
GetToken(&t);
if( t.type != ttOpenParanthesis )
{
Error(ExpectedToken("("), &t);
return node;
}
GetToken(&t);
if( t.type == ttIdentifier )
{
RewindTo(&t);
node->AddChildLast(ParseIdentifier());
GetToken(&t);
while( t.type == ttListSeparator )
{
node->AddChildLast(ParseIdentifier());
if( isSyntaxError ) return node;
GetToken(&t);
}
}
if( t.type != ttCloseParanthesis )
{
Error(ExpectedToken(")"), &t);
return node;
}
// We should just find the end of the statement block here. The statements
// will be parsed on request by the compiler once it starts the compilation.
node->AddChildLast(SuperficiallyParseStatementBlock());
return node;
}
asCScriptNode *asCParser::ParseStringConstant()
{
asCScriptNode *node = CreateNode(snConstant);
@ -1618,7 +1716,7 @@ asCScriptNode *asCParser::ParseCondition()
return node;
}
// BNF: EXPR ::= (TYPE '=' INILIST) | (EXPRTERM {EXPROP EXPRTERM})
// BNF: EXPR ::= (TYPE '=' INITLIST) | (EXPRTERM {EXPROP EXPRTERM})
asCScriptNode *asCParser::ParseExpression()
{
asCScriptNode *node = CreateNode(snExpression);
@ -2459,7 +2557,22 @@ bool asCParser::IsVirtualPropertyDecl()
// as it may wrongly identify the statement as a non-declaration if the user typed
// the name incorrectly. The real type is validated in ParseDeclaration where a
// proper error message can be given.
if( !IsRealType(t1.type) && t1.type != ttIdentifier )
if( t1.type == ttScope )
GetToken(&t1);
if( t1.type == ttIdentifier )
{
sToken t2;
GetToken(&t2);
while( t1.type == ttIdentifier && t2.type == ttScope )
{
GetToken(&t1);
GetToken(&t2);
}
RewindTo(&t2);
}
else if( !IsRealType(t1.type) )
{
RewindTo(&t);
return false;
@ -3201,61 +3314,37 @@ asCScriptNode *asCParser::SuperficiallyParseVarInit()
if( t.type == ttAssignment )
{
GetToken(&t);
if( t.type == ttStartStatementBlock )
sToken start = t;
// Find the end of the expression
int indentParan = 0;
int indentBrace = 0;
while( indentParan || indentBrace || (t.type != ttListSeparator && t.type != ttEndStatement && t.type != ttEndStatementBlock) )
{
sToken start = t;
// Find the end of the initialization list
int indent = 1;
while( indent )
if( t.type == ttOpenParanthesis )
indentParan++;
else if( t.type == ttCloseParanthesis )
indentParan--;
else if( t.type == ttStartStatementBlock )
indentBrace++;
else if( t.type == ttEndStatementBlock )
indentBrace--;
else if( t.type == ttNonTerminatedStringConstant )
{
GetToken(&t);
if( t.type == ttStartStatementBlock )
indent++;
else if( t.type == ttEndStatementBlock )
indent--;
else if( t.type == ttNonTerminatedStringConstant )
{
Error(TXT_NONTERMINATED_STRING, &t);
break;
}
else if( t.type == ttEnd )
{
Error(TXT_UNEXPECTED_END_OF_FILE, &t);
Info(TXT_WHILE_PARSING_INIT_LIST, &start);
break;
}
Error(TXT_NONTERMINATED_STRING, &t);
break;
}
}
else
{
sToken start = t;
// Find the end of the expression
int indent = 0;
while( indent || (t.type != ttListSeparator && t.type != ttEndStatement && t.type != ttEndStatementBlock) )
else if( t.type == ttEnd )
{
if( t.type == ttOpenParanthesis )
indent++;
else if( t.type == ttCloseParanthesis )
indent--;
else if( t.type == ttNonTerminatedStringConstant )
{
Error(TXT_NONTERMINATED_STRING, &t);
break;
}
else if( t.type == ttEnd )
{
Error(TXT_UNEXPECTED_END_OF_FILE, &t);
Info(TXT_WHILE_PARSING_EXPRESSION, &start);
break;
}
GetToken(&t);
Error(TXT_UNEXPECTED_END_OF_FILE, &t);
Info(TXT_WHILE_PARSING_EXPRESSION, &start);
break;
}
// Rewind so that the next token read is the list separator, end statement, or end statement block
RewindTo(&t);
GetToken(&t);
}
// Rewind so that the next token read is the list separator, end statement, or end statement block
RewindTo(&t);
}
else if( t.type == ttOpenParanthesis )
{
@ -3872,7 +3961,7 @@ asCScriptNode *asCParser::ParseIf()
return node;
}
// BNF: FOR ::= 'for' '(' (VAR | EXPRSTAT) EXPRSTAT [ASSIGN] ')' STATEMENT
// BNF: FOR ::= 'for' '(' (VAR | EXPRSTAT) EXPRSTAT [ASSIGN {',' ASSIGN}] ')' STATEMENT
asCScriptNode *asCParser::ParseFor()
{
asCScriptNode *node = CreateNode(snFor);
@ -3911,18 +4000,27 @@ asCScriptNode *asCParser::ParseFor()
{
RewindTo(&t);
asCScriptNode *n = CreateNode(snExpressionStatement);
if( n == 0 ) return 0;
node->AddChildLast(n);
n->AddChildLast(ParseAssignment());
if( isSyntaxError ) return node;
GetToken(&t);
if( t.type != ttCloseParanthesis )
// Parse N increment statements separated by ,
for(;;)
{
Error(ExpectedToken(")"), &t);
Error(InsteadFound(t), &t);
return node;
asCScriptNode *n = CreateNode(snExpressionStatement);
if( n == 0 ) return 0;
node->AddChildLast(n);
n->AddChildLast(ParseAssignment());
if( isSyntaxError ) return node;
GetToken(&t);
if( t.type == ttListSeparator )
continue;
else if( t.type == ttCloseParanthesis )
break;
else
{
const char *tokens[] = {",", ")"};
Error(ExpectedOneOf(tokens, 2), &t);
Error(InsteadFound(t), &t);
return node;
}
}
}

View File

@ -1,6 +1,6 @@
/*
AngelCode Scripting Library
Copyright (c) 2003-2014 Andreas Jonsson
Copyright (c) 2003-2015 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
@ -133,6 +133,8 @@ protected:
bool IsVarDecl();
bool IsVirtualPropertyDecl();
bool IsFuncDecl(bool isMethod);
bool IsLambda();
bool IsFunctionCall();
// Expressions
asCScriptNode *ParseAssignment();
@ -151,13 +153,13 @@ protected:
asCScriptNode *ParseCast();
asCScriptNode *ParseConstant();
asCScriptNode *ParseStringConstant();
asCScriptNode *ParseLambda();
bool IsConstant(int tokenType);
bool IsOperator(int tokenType);
bool IsPreOperator(int tokenType);
bool IsPostOperator(int tokenType);
bool IsAssignOperator(int tokenType);
bool IsFunctionCall();
bool CheckTemplateType(sToken &t);
#endif

View File

@ -40,6 +40,7 @@
#include "as_bytecode.h"
#include "as_scriptobject.h"
#include "as_texts.h"
#include "as_debug.h"
BEGIN_AS_NAMESPACE
@ -65,6 +66,8 @@ void asCReader::ReadData(void *data, asUINT size)
int asCReader::Read(bool *wasDebugInfoStripped)
{
TimeIt("asCReader::Read");
// Before starting the load, make sure that
// any existing resources have been freed
module->InternalReset();
@ -124,6 +127,8 @@ int asCReader::Error(const char *msg)
int asCReader::ReadInner()
{
TimeIt("asCReader::ReadInner");
// This function will load each entity one by one from the stream.
// If any error occurs, it will return to the caller who is
// responsible for cleaning up the partially loaded entities.
@ -258,31 +263,38 @@ int asCReader::ReadInner()
asCScriptFunction *func = ReadFunction(isNew, false, true);
if( func )
{
func->module = module;
module->funcDefs.PushLast(func);
engine->funcDefs.PushLast(func);
// TODO: clean up: This is also done by the builder. It should probably be moved to a method in the module
// Check if there is another identical funcdef from another module and if so reuse that instead
for( asUINT n = 0; n < engine->funcDefs.GetLength(); n++ )
if( func->isShared )
{
asCScriptFunction *f2 = engine->funcDefs[n];
if( f2 == 0 || func == f2 )
continue;
if( f2->name == func->name &&
f2->nameSpace == func->nameSpace &&
f2->IsSignatureExceptNameEqual(func) )
for( asUINT n = 0; n < engine->funcDefs.GetLength(); n++ )
{
// Replace our funcdef for the existing one
module->funcDefs[module->funcDefs.IndexOf(func)] = f2;
f2->AddRefInternal();
asCScriptFunction *f2 = engine->funcDefs[n];
if( f2 == 0 || func == f2 )
continue;
engine->funcDefs.RemoveValue(func);
if( !f2->isShared )
continue;
savedFunctions[savedFunctions.IndexOf(func)] = f2;
if( f2->name == func->name &&
f2->nameSpace == func->nameSpace &&
f2->IsSignatureExceptNameEqual(func) )
{
// Replace our funcdef for the existing one
module->funcDefs[module->funcDefs.IndexOf(func)] = f2;
f2->AddRefInternal();
func->ReleaseInternal();
break;
engine->funcDefs.RemoveValue(func);
savedFunctions[savedFunctions.IndexOf(func)] = f2;
func->ReleaseInternal();
break;
}
}
}
}
@ -549,6 +561,8 @@ int asCReader::ReadInner()
void asCReader::ReadUsedStringConstants()
{
TimeIt("asCReader::ReadUsedStringConstants");
asCString str;
asUINT count;
@ -563,6 +577,8 @@ void asCReader::ReadUsedStringConstants()
void asCReader::ReadUsedFunctions()
{
TimeIt("asCReader::ReadUsedFunctions");
asUINT count;
count = ReadEncodedUInt();
usedFunctions.SetLength(count);
@ -606,25 +622,44 @@ void asCReader::ReadUsedFunctions()
for( asUINT i = 0; i < module->bindInformations.GetLength(); i++ )
{
asCScriptFunction *f = module->bindInformations[i]->importedFunctionSignature;
if( !func.IsSignatureEqual(f) ||
func.objectType != f->objectType ||
if( func.objectType != f->objectType ||
func.funcType != f->funcType ||
func.nameSpace != f->nameSpace )
func.nameSpace != f->nameSpace ||
!func.IsSignatureEqual(f) )
continue;
usedFunctions[n] = f;
break;
}
}
else if( func.funcType == asFUNC_FUNCDEF )
{
const asCArray<asCScriptFunction *> &funcs = module->funcDefs;
for( asUINT i = 0; i < funcs.GetLength(); i++ )
{
asCScriptFunction *f = funcs[i];
if( f == 0 || func.name != f->name || !func.IsSignatureExceptNameAndObjectTypeEqual(f) )
continue;
// Funcdefs are always global so there is no need to compare object type
asASSERT( f->objectType == 0 );
usedFunctions[n] = f;
break;
}
}
else
{
// TODO: optimize: Global functions should be searched for in module->globalFunctions
// TODO: optimize: funcdefs should be searched for in module->funcDefs
// TODO: optimize: object methods should be searched for directly in the object type
for( asUINT i = 0; i < module->scriptFunctions.GetLength(); i++ )
{
asCScriptFunction *f = module->scriptFunctions[i];
if( !func.IsSignatureEqual(f) ||
func.objectType != f->objectType ||
if( func.objectType != f->objectType ||
func.funcType != f->funcType ||
func.nameSpace != f->nameSpace )
func.nameSpace != f->nameSpace ||
!func.IsSignatureEqual(f) )
continue;
usedFunctions[n] = f;
@ -634,17 +669,157 @@ void asCReader::ReadUsedFunctions()
}
else
{
for( asUINT i = 0; i < engine->scriptFunctions.GetLength(); i++ )
if( func.funcType == asFUNC_FUNCDEF )
{
asCScriptFunction *f = engine->scriptFunctions[i];
if( f == 0 ||
!func.IsSignatureEqual(f) ||
func.objectType != f->objectType ||
func.nameSpace != f->nameSpace )
continue;
// This is a funcdef (registered or shared)
const asCArray<asCScriptFunction *> &funcs = engine->funcDefs;
for( asUINT i = 0; i < funcs.GetLength(); i++ )
{
asCScriptFunction *f = funcs[i];
if( f == 0 || func.name != f->name || !func.IsSignatureExceptNameAndObjectTypeEqual(f) )
continue;
usedFunctions[n] = f;
break;
// Funcdefs are always global so there is no need to compare object type
asASSERT( f->objectType == 0 );
usedFunctions[n] = f;
break;
}
}
else if( func.name[0] == '$' )
{
// This is a special function
// Check for string factory
if( func.name == "$str" && engine->stringFactory &&
func.IsSignatureExceptNameAndObjectTypeEqual(engine->stringFactory) )
usedFunctions[n] = engine->stringFactory;
else if( func.name == "$beh0" && func.objectType )
{
// This is a class constructor, so we can search directly in the object type's constructors
for( asUINT i = 0; i < func.objectType->beh.constructors.GetLength(); i++ )
{
asCScriptFunction *f = engine->scriptFunctions[func.objectType->beh.constructors[i]];
if( f == 0 ||
!func.IsSignatureExceptNameAndObjectTypeEqual(f) )
continue;
usedFunctions[n] = f;
break;
}
}
else if( func.name == "$fact" || func.name == "$beh3" )
{
// This is a factory (or stub), so look for the function in the return type's factories
asCObjectType *objType = func.returnType.GetObjectType();
if( objType )
{
for( asUINT i = 0; i < objType->beh.factories.GetLength(); i++ )
{
asCScriptFunction *f = engine->scriptFunctions[objType->beh.factories[i]];
if( f == 0 ||
!func.IsSignatureExceptNameAndObjectTypeEqual(f) )
continue;
usedFunctions[n] = f;
break;
}
}
}
else if( func.name == "$list" )
{
// listFactory is used for both factory is global and returns a handle and constructor that is a method
asCObjectType *objType = func.objectType ? func.objectType : func.returnType.GetObjectType();
if( objType )
{
asCScriptFunction *f = engine->scriptFunctions[objType->beh.listFactory];
if( f && func.IsSignatureExceptNameAndObjectTypeEqual(f) )
usedFunctions[n] = f;
}
}
else if( func.name == "$beh2" )
{
// This is a destructor, so check the object type's destructor
asCObjectType *objType = func.objectType;
if( objType )
{
asCScriptFunction *f = engine->scriptFunctions[objType->beh.destruct];
if( f && func.IsSignatureExceptNameAndObjectTypeEqual(f) )
usedFunctions[n] = f;
}
}
else if( func.name == "$beh4" )
{
// This is a list factory, so check the return type's list factory
asCObjectType *objType = func.returnType.GetObjectType();
if( objType )
{
asCScriptFunction *f = engine->scriptFunctions[objType->beh.listFactory];
if( f && func.IsSignatureExceptNameAndObjectTypeEqual(f) )
usedFunctions[n] = f;
}
}
else if( func.name == "$dlgte" )
{
// This is the delegate factory
asCScriptFunction *f = engine->registeredGlobalFuncs.GetFirst(engine->nameSpaces[0], DELEGATE_FACTORY);
asASSERT( f && func.IsSignatureEqual(f) );
usedFunctions[n] = f;
}
}
else if( func.objectType == 0 )
{
// This is a global function
const asCArray<asUINT> &funcs = engine->registeredGlobalFuncs.GetIndexes(func.nameSpace, func.name);
for( asUINT i = 0; i < funcs.GetLength(); i++ )
{
asCScriptFunction *f = engine->registeredGlobalFuncs.Get(funcs[i]);
if( f == 0 ||
!func.IsSignatureExceptNameAndObjectTypeEqual(f) )
continue;
usedFunctions[n] = f;
break;
}
}
else if( func.objectType )
{
// It is a class member, so we can search directly in the object type's members
// TODO: virtual function is different that implemented method
for( asUINT i = 0; i < func.objectType->methods.GetLength(); i++ )
{
asCScriptFunction *f = engine->scriptFunctions[func.objectType->methods[i]];
if( f == 0 ||
!func.IsSignatureEqual(f) )
continue;
usedFunctions[n] = f;
break;
}
}
if( usedFunctions[n] == 0 )
{
// TODO: clean up: This part of the code should never happen. All functions should
// be found in the above logic. The only valid reason to come here
// is if the bytecode is wrong and the function doesn't exist anyway.
// This loop is kept temporarily until we can be certain all scenarios
// are covered.
for( asUINT i = 0; i < engine->scriptFunctions.GetLength(); i++ )
{
asCScriptFunction *f = engine->scriptFunctions[i];
if( f == 0 ||
func.objectType != f->objectType ||
func.nameSpace != f->nameSpace ||
!func.IsSignatureEqual(f) )
continue;
usedFunctions[n] = f;
break;
}
// No function is expected to be found
asASSERT(usedFunctions[n] == 0);
}
}
@ -973,6 +1148,13 @@ asCScriptFunction *asCReader::ReadFunction(bool &isNew, bool addToModule, bool a
{
func->vfTableIdx = ReadEncodedUInt();
}
else if( func->funcType == asFUNC_FUNCDEF )
{
asBYTE bits;
ReadData(&bits, 1);
if( bits )
func->isShared = true;
}
if( addToModule )
{
@ -1520,28 +1702,25 @@ asQWORD asCReader::ReadEncodedUInt64()
void asCReader::ReadString(asCString* str)
{
char b;
ReadData(&b, 1);
if( b == '\0' )
asUINT len = ReadEncodedUInt();
if( len & 1 )
{
str->SetLength(0);
asUINT idx = len/2;
if( idx < savedStrings.GetLength() )
*str = savedStrings[idx];
else
Error(TXT_INVALID_BYTECODE_d);
}
else if( b == 'n' )
else if( len > 0 )
{
asUINT len = ReadEncodedUInt();
len /= 2;
str->SetLength(len);
stream->Read(str->AddressOf(), len);
savedStrings.PushLast(*str);
}
else
{
asUINT n = ReadEncodedUInt();
if( n < savedStrings.GetLength() )
*str = savedStrings[n];
else
Error(TXT_INVALID_BYTECODE_d);
}
str->SetLength(0);
}
void asCReader::ReadGlobalProperty()
@ -1625,7 +1804,7 @@ void asCReader::ReadDataType(asCDataType *dt)
ReadData(&bits, 1);
asCScriptFunction *funcDef = 0;
if( tokenType == ttIdentifier && objType && objType->name == "_builtin_function_" )
if( tokenType == ttIdentifier && objType && objType->name == "$func" )
{
asCScriptFunction func(engine, module, asFUNC_DUMMY);
ReadFunctionSignature(&func);
@ -1790,7 +1969,7 @@ asCObjectType* asCReader::ReadObjectType()
ReadString(&ns);
asSNameSpace *nameSpace = engine->AddNameSpace(ns.AddressOf());
if( typeName.GetLength() && typeName != "_builtin_object_" && typeName != "_builtin_function_" )
if( typeName.GetLength() && typeName != "$obj" && typeName != "$func" )
{
// Find the object type
ot = module->GetObjectType(typeName.AddressOf(), nameSpace);
@ -1806,11 +1985,11 @@ asCObjectType* asCReader::ReadObjectType()
return 0;
}
}
else if( typeName == "_builtin_object_" )
else if( typeName == "$obj" )
{
ot = &engine->scriptTypeBehaviours;
}
else if( typeName == "_builtin_function_" )
else if( typeName == "$func" )
{
ot = &engine->functionBehaviours;
}
@ -2073,6 +2252,8 @@ void asCReader::ReadByteCode(asCScriptFunction *func)
void asCReader::ReadUsedTypeIds()
{
TimeIt("asCReader::ReadUsedTypeIds");
asUINT count = ReadEncodedUInt();
usedTypeIds.Allocate(count, false);
for( asUINT n = 0; n < count; n++ )
@ -2085,6 +2266,8 @@ void asCReader::ReadUsedTypeIds()
void asCReader::ReadUsedGlobalProps()
{
TimeIt("asCReader::ReadUsedGlobalProps");
int c = ReadEncodedUInt();
usedGlobalProperties.Allocate(c, false);
@ -2124,6 +2307,8 @@ void asCReader::ReadUsedGlobalProps()
void asCReader::ReadUsedObjectProps()
{
TimeIt("asCReader::ReadUsedObjectProps");
asUINT c = ReadEncodedUInt();
usedObjectProperties.SetLength(c);
@ -2293,7 +2478,8 @@ void asCReader::TranslateFunction(asCScriptFunction *func)
}
else if( c == asBC_CALL ||
c == asBC_CALLINTF ||
c == asBC_CALLSYS )
c == asBC_CALLSYS ||
c == asBC_Thiscall1 )
{
// Translate the index to the func id
int *fid = (int*)&bc[n+1];
@ -2800,7 +2986,8 @@ void asCReader::CalculateStackNeeded(asCScriptFunction *func)
{
// Determine the true delta from the instruction arguments
if( bc == asBC_CALL ||
bc == asBC_CALLSYS ||
bc == asBC_CALLSYS ||
bc == asBC_Thiscall1 ||
bc == asBC_CALLBND ||
bc == asBC_ALLOC ||
bc == asBC_CALLINTF ||
@ -3030,6 +3217,7 @@ asCScriptFunction *asCReader::GetCalledFunction(asCScriptFunction *func, asDWORD
if( bc == asBC_CALL ||
bc == asBC_CALLSYS ||
bc == asBC_Thiscall1 ||
bc == asBC_CALLINTF )
{
// Find the function from the function id in bytecode
@ -3085,11 +3273,13 @@ int asCReader::AdjustGetOffset(int offset, asCScriptFunction *func, asDWORD prog
// Find out which function that will be called
asCScriptFunction *calledFunc = 0;
int stackDelta = 0;
for( asUINT n = programPos; func->scriptData->byteCode.GetLength(); )
{
asBYTE bc = *(asBYTE*)&func->scriptData->byteCode[n];
if( bc == asBC_CALL ||
bc == asBC_CALLSYS ||
bc == asBC_Thiscall1 ||
bc == asBC_CALLINTF ||
bc == asBC_ALLOC ||
bc == asBC_CALLBND ||
@ -3106,6 +3296,10 @@ int asCReader::AdjustGetOffset(int offset, asCScriptFunction *func, asDWORD prog
return offset - (1 - AS_PTR_SIZE);
}
// Keep track of the stack size between the
// instruction that needs to be adjusted and the call
stackDelta += asBCInfo[bc].stackInc;
n += asBCTypeSize[asBCInfo[bc].type];
}
@ -3118,16 +3312,30 @@ int asCReader::AdjustGetOffset(int offset, asCScriptFunction *func, asDWORD prog
// Count the number of pointers pushed on the stack above the
// current offset, and then adjust the offset accordingly
asUINT numPtrs = 0;
int currOffset = 0;
int currOffset = -stackDelta;
if( offset > currOffset && calledFunc->GetObjectType() )
{
numPtrs++;
currOffset++;
if( currOffset > 0 )
numPtrs++;
#if AS_PTR_SIZE == 2
// For 64bit platforms it is necessary to increment the currOffset by one more
// DWORD since the stackDelta was counting the full 64bit size of the pointer
else if( stackDelta )
currOffset++;
#endif
}
if( offset > currOffset && calledFunc->DoesReturnOnStack() )
{
numPtrs++;
currOffset++;
if( currOffset > 0 )
numPtrs++;
#if AS_PTR_SIZE == 2
// For 64bit platforms it is necessary to increment the currOffset by one more
// DWORD since the stackDelta was counting the full 64bit size of the pointer
else if( stackDelta )
currOffset++;
#endif
}
for( asUINT p = 0; p < calledFunc->parameterTypes.GetLength(); p++ )
{
@ -3136,8 +3344,15 @@ int asCReader::AdjustGetOffset(int offset, asCScriptFunction *func, asDWORD prog
if( !calledFunc->parameterTypes[p].IsPrimitive() ||
calledFunc->parameterTypes[p].IsReference() )
{
numPtrs++;
currOffset++;
if( currOffset > 0 )
numPtrs++;
#if AS_PTR_SIZE == 2
// For 64bit platforms it is necessary to increment the currOffset by one more
// DWORD since the stackDelta was counting the full 64bit size of the pointer
else if( stackDelta )
currOffset++;
#endif
// The variable arg ? has an additiona 32bit integer with the typeid
if( calledFunc->parameterTypes[p].IsAnyType() )
@ -3197,6 +3412,8 @@ void asCWriter::WriteData(const void *data, asUINT size)
int asCWriter::Write()
{
TimeIt("asCWriter::Write");
unsigned long i, count;
// Store everything in the same order that the builder parses scripts
@ -3207,101 +3424,149 @@ int asCWriter::Write()
WriteData(&stripDebugInfo, sizeof(stripDebugInfo));
// Store enums
count = (asUINT)module->enumTypes.GetLength();
WriteEncodedInt64(count);
for( i = 0; i < count; i++ )
{
WriteObjectTypeDeclaration(module->enumTypes[i], 1);
WriteObjectTypeDeclaration(module->enumTypes[i], 2);
TimeIt("store enums");
count = (asUINT)module->enumTypes.GetLength();
WriteEncodedInt64(count);
for( i = 0; i < count; i++ )
{
WriteObjectTypeDeclaration(module->enumTypes[i], 1);
WriteObjectTypeDeclaration(module->enumTypes[i], 2);
}
}
// Store type declarations first
count = (asUINT)module->classTypes.GetLength();
WriteEncodedInt64(count);
for( i = 0; i < count; i++ )
{
// Store only the name of the class/interface types
WriteObjectTypeDeclaration(module->classTypes[i], 1);
TimeIt("type declarations");
count = (asUINT)module->classTypes.GetLength();
WriteEncodedInt64(count);
for( i = 0; i < count; i++ )
{
// Store only the name of the class/interface types
WriteObjectTypeDeclaration(module->classTypes[i], 1);
}
}
// Store func defs
count = (asUINT)module->funcDefs.GetLength();
WriteEncodedInt64(count);
for( i = 0; i < count; i++ )
WriteFunction(module->funcDefs[i]);
{
TimeIt("func defs");
count = (asUINT)module->funcDefs.GetLength();
WriteEncodedInt64(count);
for( i = 0; i < count; i++ )
WriteFunction(module->funcDefs[i]);
}
// Now store all interface methods
count = (asUINT)module->classTypes.GetLength();
for( i = 0; i < count; i++ )
{
if( module->classTypes[i]->IsInterface() )
WriteObjectTypeDeclaration(module->classTypes[i], 2);
TimeIt("interface methods");
count = (asUINT)module->classTypes.GetLength();
for( i = 0; i < count; i++ )
{
if( module->classTypes[i]->IsInterface() )
WriteObjectTypeDeclaration(module->classTypes[i], 2);
}
}
// Then store the class methods and behaviours
for( i = 0; i < count; ++i )
{
if( !module->classTypes[i]->IsInterface() )
WriteObjectTypeDeclaration(module->classTypes[i], 2);
TimeIt("class methods and behaviours");
for( i = 0; i < count; ++i )
{
if( !module->classTypes[i]->IsInterface() )
WriteObjectTypeDeclaration(module->classTypes[i], 2);
}
}
// Then store the class properties
for( i = 0; i < count; ++i )
{
if( !module->classTypes[i]->IsInterface() )
WriteObjectTypeDeclaration(module->classTypes[i], 3);
TimeIt("class properties");
for( i = 0; i < count; ++i )
{
if( !module->classTypes[i]->IsInterface() )
WriteObjectTypeDeclaration(module->classTypes[i], 3);
}
}
// Store typedefs
count = (asUINT)module->typeDefs.GetLength();
WriteEncodedInt64(count);
for( i = 0; i < count; i++ )
{
WriteObjectTypeDeclaration(module->typeDefs[i], 1);
WriteObjectTypeDeclaration(module->typeDefs[i], 2);
TimeIt("type defs");
count = (asUINT)module->typeDefs.GetLength();
WriteEncodedInt64(count);
for( i = 0; i < count; i++ )
{
WriteObjectTypeDeclaration(module->typeDefs[i], 1);
WriteObjectTypeDeclaration(module->typeDefs[i], 2);
}
}
// scriptGlobals[]
count = (asUINT)module->scriptGlobals.GetSize();
WriteEncodedInt64(count);
asCSymbolTable<asCGlobalProperty>::iterator it = module->scriptGlobals.List();
for( ; it; it++ )
WriteGlobalProperty(*it);
{
TimeIt("script globals");
count = (asUINT)module->scriptGlobals.GetSize();
WriteEncodedInt64(count);
asCSymbolTable<asCGlobalProperty>::iterator it = module->scriptGlobals.List();
for( ; it; it++ )
WriteGlobalProperty(*it);
}
// scriptFunctions[]
count = 0;
for( i = 0; i < module->scriptFunctions.GetLength(); i++ )
if( module->scriptFunctions[i]->objectType == 0 )
count++;
WriteEncodedInt64(count);
for( i = 0; i < module->scriptFunctions.GetLength(); ++i )
if( module->scriptFunctions[i]->objectType == 0 )
WriteFunction(module->scriptFunctions[i]);
{
TimeIt("scriptFunctions");
count = 0;
for( i = 0; i < module->scriptFunctions.GetLength(); i++ )
if( module->scriptFunctions[i]->objectType == 0 )
count++;
WriteEncodedInt64(count);
for( i = 0; i < module->scriptFunctions.GetLength(); ++i )
if( module->scriptFunctions[i]->objectType == 0 )
WriteFunction(module->scriptFunctions[i]);
}
// globalFunctions[]
count = (int)module->globalFunctions.GetSize();
asCSymbolTable<asCScriptFunction>::iterator funcIt = module->globalFunctions.List();
WriteEncodedInt64(count);
while( funcIt )
{
WriteFunction(*funcIt);
funcIt++;
TimeIt("globalFunctions");
count = (int)module->globalFunctions.GetSize();
asCSymbolTable<asCScriptFunction>::iterator funcIt = module->globalFunctions.List();
WriteEncodedInt64(count);
while( funcIt )
{
WriteFunction(*funcIt);
funcIt++;
}
}
// bindInformations[]
count = (asUINT)module->bindInformations.GetLength();
WriteEncodedInt64(count);
for( i = 0; i < count; ++i )
{
WriteFunction(module->bindInformations[i]->importedFunctionSignature);
WriteString(&module->bindInformations[i]->importFromModule);
TimeIt("bindInformations");
count = (asUINT)module->bindInformations.GetLength();
WriteEncodedInt64(count);
for( i = 0; i < count; ++i )
{
WriteFunction(module->bindInformations[i]->importedFunctionSignature);
WriteString(&module->bindInformations[i]->importFromModule);
}
}
// usedTypes[]
count = (asUINT)usedTypes.GetLength();
WriteEncodedInt64(count);
for( i = 0; i < count; ++i )
WriteObjectType(usedTypes[i]);
{
TimeIt("usedTypes");
count = (asUINT)usedTypes.GetLength();
WriteEncodedInt64(count);
for( i = 0; i < count; ++i )
WriteObjectType(usedTypes[i]);
}
// usedTypeIds[]
WriteUsedTypeIds();
@ -3335,6 +3600,8 @@ int asCWriter::FindStringConstantIndex(int id)
void asCWriter::WriteUsedStringConstants()
{
TimeIt("asCWriter::WriteUsedStringConstants");
asUINT count = (asUINT)usedStringConstants.GetLength();
WriteEncodedInt64(count);
for( asUINT i = 0; i < count; ++i )
@ -3343,6 +3610,8 @@ void asCWriter::WriteUsedStringConstants()
void asCWriter::WriteUsedFunctions()
{
TimeIt("asCWriter::WriteUsedFunctions");
asUINT count = (asUINT)usedFunctions.GetLength();
WriteEncodedInt64(count);
@ -3572,6 +3841,12 @@ void asCWriter::WriteFunction(asCScriptFunction* func)
// TODO: Do we really need to store this? It can probably be reconstructed by the reader
WriteEncodedInt64(func->vfTableIdx);
}
else if( func->funcType == asFUNC_FUNCDEF )
{
char bits = 0;
bits += func->isShared ? 1 : 0;
WriteData(&bits,1);
}
}
void asCWriter::WriteObjectTypeDeclaration(asCObjectType *ot, int phase)
@ -3755,39 +4030,28 @@ void asCWriter::WriteEncodedInt64(asINT64 i)
void asCWriter::WriteString(asCString* str)
{
// TODO: All strings should be stored in a separate section, and when
// they are used an offset into that section should be stored.
// This will make it unnecessary to store the extra byte to
// identify new versus old strings.
if( str->GetLength() == 0 )
{
char z = '\0';
WriteData(&z, 1);
return;
}
// First check if the string hasn't been saved already
asSMapNode<asCStringPointer, int> *cursor = 0;
if (stringToIdMap.MoveTo(&cursor, asCStringPointer(str)))
{
// Save a reference to the existing string
char b = 'r';
WriteData(&b, 1);
WriteEncodedInt64(cursor->value);
// The lowest bit is set to 1 to indicate a reference
WriteEncodedInt64(cursor->value*2+1);
return;
}
// Save a new string
char b = 'n';
WriteData(&b, 1);
// The lowest bit is set to 0 to indicate a new string
asUINT len = (asUINT)str->GetLength();
WriteEncodedInt64(len);
stream->Write(str->AddressOf(), (asUINT)len);
WriteEncodedInt64(len*2);
savedStrings.PushLast(*str);
stringToIdMap.Insert(asCStringPointer(str), int(savedStrings.GetLength()) - 1);
if( len > 0 )
{
stream->Write(str->AddressOf(), (asUINT)len);
savedStrings.PushLast(*str);
stringToIdMap.Insert(asCStringPointer(str), int(savedStrings.GetLength()) - 1);
}
}
void asCWriter::WriteGlobalProperty(asCGlobalProperty* prop)
@ -3851,7 +4115,7 @@ void asCWriter::WriteDataType(const asCDataType *dt)
bits.isReadOnly = dt->IsReadOnly();
WriteData(&bits, 1);
if( t == ttIdentifier && dt->GetObjectType()->name == "_builtin_function_" )
if( t == ttIdentifier && dt->GetObjectType()->name == "$func" )
{
WriteFunctionSignature(dt->GetFuncDef());
}
@ -3883,17 +4147,17 @@ void asCWriter::WriteObjectType(asCObjectType* ot)
WriteEncodedInt64(ot->templateSubTypes.GetLength());
for( asUINT n = 0; n < ot->templateSubTypes.GetLength(); n++ )
{
if( ot->templateSubTypes[0].IsObject() || ot->templateSubTypes[0].IsEnumType() )
if( ot->templateSubTypes[n].IsObject() || ot->templateSubTypes[n].IsEnumType() )
{
ch = 's';
WriteData(&ch, 1);
WriteDataType(&ot->templateSubTypes[0]);
WriteDataType(&ot->templateSubTypes[n]);
}
else
{
ch = 't';
WriteData(&ch, 1);
eTokenType t = ot->templateSubTypes[0].GetTokenType();
eTokenType t = ot->templateSubTypes[n].GetTokenType();
WriteEncodedInt64(t);
}
}
@ -4053,11 +4317,13 @@ int asCWriter::AdjustGetOffset(int offset, asCScriptFunction *func, asDWORD prog
// Find out which function that will be called
asCScriptFunction *calledFunc = 0;
int stackDelta = 0;
for( asUINT n = programPos; n < func->scriptData->byteCode.GetLength(); )
{
asBYTE bc = *(asBYTE*)&func->scriptData->byteCode[n];
if( bc == asBC_CALL ||
bc == asBC_CALLSYS ||
bc == asBC_Thiscall1 ||
bc == asBC_CALLINTF )
{
// Find the function from the function id in bytecode
@ -4120,6 +4386,10 @@ int asCWriter::AdjustGetOffset(int offset, asCScriptFunction *func, asDWORD prog
return offset + (1 - AS_PTR_SIZE);
}
// Keep track of the stack size between the
// instruction that needs to be adjusted and the call
stackDelta += asBCInfo[bc].stackInc;
n += asBCTypeSize[asBCInfo[bc].type];
}
@ -4128,16 +4398,18 @@ int asCWriter::AdjustGetOffset(int offset, asCScriptFunction *func, asDWORD prog
// Count the number of pointers pushed on the stack above the
// current offset, and then adjust the offset accordingly
asUINT numPtrs = 0;
int currOffset = 0;
int currOffset = -stackDelta;
if( offset > currOffset && calledFunc->GetObjectType() )
{
numPtrs++;
currOffset += AS_PTR_SIZE;
if( currOffset > 0 )
numPtrs++;
}
if( offset > currOffset && calledFunc->DoesReturnOnStack() )
{
numPtrs++;
currOffset += AS_PTR_SIZE;
if( currOffset > 0 )
numPtrs++;
}
for( asUINT p = 0; p < calledFunc->parameterTypes.GetLength(); p++ )
{
@ -4147,8 +4419,9 @@ int asCWriter::AdjustGetOffset(int offset, asCScriptFunction *func, asDWORD prog
calledFunc->parameterTypes[p].IsReference() )
{
// objects and references are passed by pointer
numPtrs++;
currOffset += AS_PTR_SIZE;
if( currOffset > 0 )
numPtrs++;
// The variable arg ? has an additional 32bit int with the typeid
if( calledFunc->parameterTypes[p].IsAnyType() )
@ -4262,7 +4535,8 @@ void asCWriter::WriteByteCode(asCScriptFunction *func)
}
else if( c == asBC_CALL || // DW_ARG
c == asBC_CALLINTF || // DW_ARG
c == asBC_CALLSYS ) // DW_ARG
c == asBC_CALLSYS || // DW_ARG
c == asBC_Thiscall1 ) // DW_ARG
{
// Translate the function id
*(int*)(tmp+1) = FindFunctionIndex(engine->scriptFunctions[*(int*)(tmp+1)]);
@ -4772,6 +5046,8 @@ void asCWriter::SListAdjuster::SetNextType(int typeId)
void asCWriter::WriteUsedTypeIds()
{
TimeIt("asCWriter::WriteUsedTypeIds");
asUINT count = (asUINT)usedTypeIds.GetLength();
WriteEncodedInt64(count);
for( asUINT n = 0; n < count; n++ )
@ -4792,6 +5068,8 @@ int asCWriter::FindGlobalPropPtrIndex(void *ptr)
void asCWriter::WriteUsedGlobalProps()
{
TimeIt("asCWriter::WriteUsedGlobalProps");
int c = (int)usedGlobalProperties.GetLength();
WriteEncodedInt64(c);
@ -4799,32 +5077,12 @@ void asCWriter::WriteUsedGlobalProps()
{
asPWORD *p = (asPWORD*)usedGlobalProperties[n];
// First search for the global in the module
char moduleProp = 0;
// Find the property descriptor from the address
asCGlobalProperty *prop = 0;
asCSymbolTable<asCGlobalProperty>::iterator it = module->scriptGlobals.List();
for( ; it; it++ )
asSMapNode<void*, asCGlobalProperty*> *cursor;
if( engine->varAddressMap.MoveTo(&cursor, p) )
{
if( p == (*it)->GetAddressOfValue() )
{
prop = (*it);
moduleProp = 1;
break;
}
}
// If it is not in the module, it must be an application registered property
if( !prop )
{
asCSymbolTable<asCGlobalProperty>::iterator it = engine->registeredGlobalProps.List();
for( ; it; it++ )
{
if( it->GetAddressOfValue() == p )
{
prop = *it;
break;
}
}
prop = engine->varAddressMap.GetValue(cursor);
}
asASSERT(prop);
@ -4835,12 +5093,17 @@ void asCWriter::WriteUsedGlobalProps()
WriteDataType(&prop->type);
// Also store whether the property is a module property or a registered property
char moduleProp = 0;
if( prop->realAddress == 0 )
moduleProp = 1;
WriteData(&moduleProp, 1);
}
}
void asCWriter::WriteUsedObjectProps()
{
TimeIt("asCWriter::WriteUsedObjectProps");
int c = (int)usedObjectProperties.GetLength();
WriteEncodedInt64(c);

View File

@ -118,6 +118,9 @@ AS_API const char * asGetLibraryOptions()
#ifdef WIP_16BYTE_ALIGN
"WIP_16BYTE_ALIGN "
#endif
#ifdef AS_BIG_ENDIAN
"AS_BIG_ENDIAN "
#endif
// Target system
#ifdef AS_WIN
@ -584,21 +587,22 @@ asCScriptEngine::asCScriptEngine()
// Reserve function id 0 for no function
scriptFunctions.PushLast(0);
// Reserve the first typeIds for the primitive types
typeIdSeqNbr = asTYPEID_DOUBLE + 1;
// Make sure typeId for the built-in primitives are defined according to asETypeIdFlags
int id = 0;
UNUSED_VAR(id); // It is only used in debug mode
id = GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttVoid, false)); asASSERT( id == asTYPEID_VOID );
id = GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttBool, false)); asASSERT( id == asTYPEID_BOOL );
id = GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttInt8, false)); asASSERT( id == asTYPEID_INT8 );
id = GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttInt16, false)); asASSERT( id == asTYPEID_INT16 );
id = GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttInt, false)); asASSERT( id == asTYPEID_INT32 );
id = GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttInt64, false)); asASSERT( id == asTYPEID_INT64 );
id = GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttUInt8, false)); asASSERT( id == asTYPEID_UINT8 );
id = GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttUInt16, false)); asASSERT( id == asTYPEID_UINT16 );
id = GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttUInt, false)); asASSERT( id == asTYPEID_UINT32 );
id = GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttUInt64, false)); asASSERT( id == asTYPEID_UINT64 );
id = GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttFloat, false)); asASSERT( id == asTYPEID_FLOAT );
id = GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttDouble, false)); asASSERT( id == asTYPEID_DOUBLE );
asASSERT( GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttVoid, false)) == asTYPEID_VOID );
asASSERT( GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttBool, false)) == asTYPEID_BOOL );
asASSERT( GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttInt8, false)) == asTYPEID_INT8 );
asASSERT( GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttInt16, false)) == asTYPEID_INT16 );
asASSERT( GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttInt, false)) == asTYPEID_INT32 );
asASSERT( GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttInt64, false)) == asTYPEID_INT64 );
asASSERT( GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttUInt8, false)) == asTYPEID_UINT8 );
asASSERT( GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttUInt16, false)) == asTYPEID_UINT16 );
asASSERT( GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttUInt, false)) == asTYPEID_UINT32 );
asASSERT( GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttUInt64, false)) == asTYPEID_UINT64 );
asASSERT( GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttFloat, false)) == asTYPEID_FLOAT );
asASSERT( GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttDouble, false)) == asTYPEID_DOUBLE );
defaultArrayObjectType = 0;
@ -608,9 +612,9 @@ asCScriptEngine::asCScriptEngine()
void asCScriptEngine::DeleteDiscardedModules()
{
// TODO: 2.30.0: redesign: Prevent more than one thread from entering this function at the same time.
// If a thread is already doing the work for the clean-up the other thread should
// simply return, as the first thread will continue.
// TODO: redesign: Prevent more than one thread from entering this function at the same time.
// If a thread is already doing the work for the clean-up the other thread should
// simply return, as the first thread will continue.
ACQUIRESHARED(engineRWLock);
asUINT maxCount = discardedModules.GetLength();
@ -646,7 +650,7 @@ void asCScriptEngine::DeleteDiscardedModules()
asCScriptEngine::~asCScriptEngine()
{
// TODO: 2.30.0: redesign: Clean up redundant code
// TODO: clean-up: Clean up redundant code
asUINT n = 0;
inDestructor = true;
@ -690,12 +694,8 @@ asCScriptEngine::~asCScriptEngine()
if( refCount.get() > 0 )
WriteMessage("", 0, 0, asMSGTYPE_ERROR, TXT_ENGINE_REF_COUNT_ERROR_DURING_SHUTDOWN);
asSMapNode<int,asCDataType*> *cursor = 0;
while( mapTypeIdToDataType.MoveFirst(&cursor) )
{
asDELETE(mapTypeIdToDataType.GetValue(cursor),asCDataType);
mapTypeIdToDataType.Erase(cursor);
}
mapTypeIdToObjectType.EraseAll();
mapTypeIdToFunction.EraseAll();
// First remove what is not used, so that other groups can be deleted safely
defaultGroup.RemoveConfiguration(this, true);
@ -1976,7 +1976,7 @@ int asCScriptEngine::RegisterBehaviourToObjectType(asCObjectType *objectType, as
listPattern->Destroy(this);
return ConfigError(asINVALID_DECLARATION, "RegisterObjectBehaviour", objectType->name.AddressOf(), decl);
}
func.name.Format("_beh_%d_", behaviour);
func.name.Format("$beh%d", behaviour);
if( behaviour != asBEHAVE_FACTORY && behaviour != asBEHAVE_LIST_FACTORY )
{
@ -3229,7 +3229,7 @@ int asCScriptEngine::RegisterStringFactory(const char *datatype, const asSFuncPt
return ConfigError(asOUT_OF_MEMORY, "RegisterStringFactory", datatype, 0);
}
func->name = "_string_factory_";
func->name = "$str";
func->sysFuncIntf = newInterface;
asCBuilder bld(this, 0);
@ -3591,6 +3591,9 @@ asCObjectType *asCScriptEngine::GetTemplateInstanceType(asCObjectType *templateT
{
asCScriptFunction *func = GenerateTemplateFactoryStub(templateType, ot, templateType->beh.listFactory);
// Rename the function to easily identify it in LoadByteCode
func->name = "$list";
ot->beh.listFactory = func->id;
}
@ -3787,7 +3790,7 @@ asCScriptFunction *asCScriptEngine::GenerateTemplateFactoryStub(asCObjectType *t
func->funcType = asFUNC_SCRIPT;
func->AllocateScriptFunctionData();
func->name = "factstub";
func->name = "$fact";
func->id = GetNextScriptFunctionId();
AddScriptFunction(func);
@ -4230,6 +4233,62 @@ void *asCScriptEngine::CallObjectMethodRetPtr(void *obj, int func) const
#endif
}
void *asCScriptEngine::CallObjectMethodRetPtr(void *obj, int param1, asCScriptFunction *func) const
{
asASSERT( func != 0 );
asSSystemFunctionInterface *i = func->sysFuncIntf;
#ifndef AS_NO_CLASS_METHODS
if( i->callConv == ICC_THISCALL || i->callConv == ICC_VIRTUAL_THISCALL )
{
#if defined(__GNUC__) || defined(AS_PSVITA)
// For virtual thiscalls we must call the method as a true class method so that the compiler will lookup the function address in the vftable
union
{
asSIMPLEMETHOD_t mthd;
struct
{
asFUNCTION_t func;
asPWORD baseOffset;
} f;
} p;
p.f.func = (asFUNCTION_t)(i->func);
p.f.baseOffset = asPWORD(i->baseOffset);
void *(asCSimpleDummy::*f)(int) = (void *(asCSimpleDummy::*)(int))(p.mthd);
return (((asCSimpleDummy*)obj)->*f)(param1);
#else
union
{
asSIMPLEMETHOD_t mthd;
asFUNCTION_t func;
} p;
p.func = (asFUNCTION_t)(i->func);
void *(asCSimpleDummy::*f)(int) = (void *(asCSimpleDummy::*)(int))p.mthd;
obj = (void*)(asPWORD(obj) + i->baseOffset);
return (((asCSimpleDummy*)obj)->*f)(param1);
#endif
}
else
#endif
if( i->callConv == ICC_GENERIC_METHOD )
{
asCGeneric gen(const_cast<asCScriptEngine*>(this), func, obj, reinterpret_cast<asDWORD*>(&param1));
void (*f)(asIScriptGeneric *) = (void (*)(asIScriptGeneric *))(i->func);
f(&gen);
return *(void **)gen.GetReturnPointer();
}
else if( i->callConv == ICC_CDECL_OBJLAST )
{
void *(*f)(int, void *) = (void *(*)(int, void *))(i->func);
return f(param1, obj);
}
else /*if( i->callConv == ICC_CDECL_OBJFIRST )*/
{
void *(*f)(void *, int) = (void *(*)(void *, int))(i->func);
return f(obj, param1);
}
}
void *asCScriptEngine::CallGlobalFunctionRetPtr(int func) const
{
asCScriptFunction *s = scriptFunctions[func];
@ -4496,82 +4555,154 @@ void asCScriptEngine::GCEnumCallback(void *reference)
}
// TODO: multithread: The mapTypeIdToDataType must be protected with critical sections in all functions that access it
int asCScriptEngine::GetTypeIdFromDataType(const asCDataType &dtIn) const
{
if( dtIn.IsNullHandle() ) return 0;
if( dtIn.IsNullHandle() ) return asTYPEID_VOID;
// Register the base form
asCDataType dt(dtIn);
if( dt.GetObjectType() )
dt.MakeHandle(false);
// Find the existing type id
asSMapNode<int,asCDataType*> *cursor = 0;
mapTypeIdToDataType.MoveFirst(&cursor);
while( cursor )
if( dtIn.GetObjectType() == 0 )
{
if( mapTypeIdToDataType.GetValue(cursor)->IsEqualExceptRefAndConst(dt) )
// Primitives have pre-fixed typeIds
switch( dtIn.GetTokenType() )
{
int typeId = mapTypeIdToDataType.GetKey(cursor);
if( dtIn.GetObjectType() && !(dtIn.GetObjectType()->flags & asOBJ_ASHANDLE) )
case ttVoid: return asTYPEID_VOID;
case ttBool: return asTYPEID_BOOL;
case ttInt8: return asTYPEID_INT8;
case ttInt16: return asTYPEID_INT16;
case ttInt: return asTYPEID_INT32;
case ttInt64: return asTYPEID_INT64;
case ttUInt8: return asTYPEID_UINT8;
case ttUInt16: return asTYPEID_UINT16;
case ttUInt: return asTYPEID_UINT32;
case ttUInt64: return asTYPEID_UINT64;
case ttFloat: return asTYPEID_FLOAT;
case ttDouble: return asTYPEID_DOUBLE;
default:
// All types should be covered by the above. The variable type is not really a type
asASSERT(dtIn.GetTokenType() == ttQuestion);
return -1;
}
}
int typeId = -1;
asCObjectType *ot = dtIn.GetObjectType();
if( ot != &functionBehaviours )
{
// Object's hold the typeId themselves
typeId = ot->typeId;
if( typeId == -1 )
{
ACQUIREEXCLUSIVE(engineRWLock);
// Make sure another thread didn't determine the typeId while we were waiting for the lock
if( ot->typeId == -1 )
{
// The ASHANDLE types behave like handles, but are really
// value types so the typeId is never returned as a handle
if( dtIn.IsObjectHandle() )
typeId |= asTYPEID_OBJHANDLE;
if( dtIn.IsHandleToConst() )
typeId |= asTYPEID_HANDLETOCONST;
typeId = typeIdSeqNbr++;
if( ot->flags & asOBJ_SCRIPT_OBJECT ) typeId |= asTYPEID_SCRIPTOBJECT;
else if( ot->flags & asOBJ_TEMPLATE ) typeId |= asTYPEID_TEMPLATE;
else if( ot->flags & asOBJ_ENUM ) {} // TODO: Should we have a specific bit for this?
else typeId |= asTYPEID_APPOBJECT;
ot->typeId = typeId;
mapTypeIdToObjectType.Insert(typeId, ot);
}
RELEASEEXCLUSIVE(engineRWLock);
}
}
else
{
// This a funcdef, so we'll need to look in the map for the funcdef
// TODO: optimize: It shouldn't be necessary to exclusive lock when the typeId already exists
ACQUIREEXCLUSIVE(engineRWLock);
// Find the existing type id
asCScriptFunction *func = dtIn.GetFuncDef();
asASSERT(func);
asSMapNode<int,asCScriptFunction*> *cursor = 0;
mapTypeIdToFunction.MoveFirst(&cursor);
while( cursor )
{
if( mapTypeIdToFunction.GetValue(cursor) == func )
{
typeId = mapTypeIdToFunction.GetKey(cursor);
break;
}
return typeId;
mapTypeIdToFunction.MoveNext(&cursor, cursor);
}
mapTypeIdToDataType.MoveNext(&cursor, cursor);
// The type id doesn't exist, create it
if( typeId == -1 )
{
// Setup the type id for the funcdef
typeId = typeIdSeqNbr++;
typeId |= asTYPEID_APPOBJECT;
mapTypeIdToFunction.Insert(typeId, func);
}
RELEASEEXCLUSIVE(engineRWLock);
}
// The type id doesn't exist, create it
// Setup the basic type id
int typeId = typeIdSeqNbr++;
if( dt.GetObjectType() )
// Add flags according to the requested type
if( dtIn.GetObjectType() && !(dtIn.GetObjectType()->flags & asOBJ_ASHANDLE) )
{
if( dt.GetObjectType()->flags & asOBJ_SCRIPT_OBJECT ) typeId |= asTYPEID_SCRIPTOBJECT;
else if( dt.GetObjectType()->flags & asOBJ_TEMPLATE ) typeId |= asTYPEID_TEMPLATE;
else if( dt.GetObjectType()->flags & asOBJ_ENUM ) {} // TODO: Should we have a specific bit for this?
else typeId |= asTYPEID_APPOBJECT;
// The ASHANDLE types behave like handles, but are really
// value types so the typeId is never returned as a handle
if( dtIn.IsObjectHandle() )
typeId |= asTYPEID_OBJHANDLE;
if( dtIn.IsHandleToConst() )
typeId |= asTYPEID_HANDLETOCONST;
}
// Insert the basic object type
asCDataType *newDt = asNEW(asCDataType)(dt);
if( newDt == 0 )
{
// Out of memory
return 0;
}
newDt->MakeReference(false);
newDt->MakeReadOnly(false);
newDt->MakeHandle(false);
mapTypeIdToDataType.Insert(typeId, newDt);
// Call recursively to get the correct typeId
return GetTypeIdFromDataType(dtIn);
return typeId;
}
asCDataType asCScriptEngine::GetDataTypeFromTypeId(int typeId) const
{
int baseId = typeId & (asTYPEID_MASK_OBJECT | asTYPEID_MASK_SEQNBR);
asSMapNode<int,asCDataType*> *cursor = 0;
if( mapTypeIdToDataType.MoveTo(&cursor, baseId) )
if( typeId <= asTYPEID_DOUBLE )
{
asCDataType dt(*mapTypeIdToDataType.GetValue(cursor));
eTokenType type[] = {ttVoid, ttBool, ttInt8, ttInt16, ttInt, ttInt64, ttUInt8, ttUInt16, ttUInt, ttUInt64, ttFloat, ttDouble};
return asCDataType::CreatePrimitive(type[typeId], false);
}
// First check if the typeId is an object type
asCObjectType *ot = 0;
ACQUIRESHARED(engineRWLock);
asSMapNode<int,asCObjectType*> *cursor = 0;
if( mapTypeIdToObjectType.MoveTo(&cursor, baseId) )
ot = mapTypeIdToObjectType.GetValue(cursor);
RELEASESHARED(engineRWLock);
if( ot )
{
asCDataType dt = asCDataType::CreateObject(ot, false);
if( typeId & asTYPEID_OBJHANDLE )
dt.MakeHandle(true, true);
if( typeId & asTYPEID_HANDLETOCONST )
dt.MakeHandleToConst(true);
return dt;
}
// Then check if it is a funcdef
asCScriptFunction *func = 0;
ACQUIRESHARED(engineRWLock);
asSMapNode<int,asCScriptFunction*> *cursor2 = 0;
if( mapTypeIdToFunction.MoveTo(&cursor2, baseId) )
func = mapTypeIdToFunction.GetValue(cursor2);
RELEASESHARED(engineRWLock);
if( func )
{
asCDataType dt = asCDataType::CreateFuncDef(func);
if( typeId & asTYPEID_OBJHANDLE )
dt.MakeHandle(true, true);
if( typeId & asTYPEID_HANDLETOCONST )
dt.MakeHandleToConst(true);
return dt;
}
@ -4586,19 +4717,19 @@ asCObjectType *asCScriptEngine::GetObjectTypeFromTypeId(int typeId) const
void asCScriptEngine::RemoveFromTypeIdMap(asCObjectType *type)
{
asSMapNode<int,asCDataType*> *cursor = 0;
mapTypeIdToDataType.MoveFirst(&cursor);
ACQUIREEXCLUSIVE(engineRWLock);
asSMapNode<int,asCObjectType*> *cursor = 0;
mapTypeIdToObjectType.MoveFirst(&cursor);
while( cursor )
{
asCDataType *dt = mapTypeIdToDataType.GetValue(cursor);
asSMapNode<int,asCDataType*> *old = cursor;
mapTypeIdToDataType.MoveNext(&cursor, cursor);
if( dt->GetObjectType() == type )
if( mapTypeIdToObjectType.GetValue(cursor) == type )
{
asDELETE(dt,asCDataType);
mapTypeIdToDataType.Erase(old);
mapTypeIdToObjectType.Erase(cursor);
break;
}
mapTypeIdToObjectType.MoveNext(&cursor, cursor);
}
RELEASEEXCLUSIVE(engineRWLock);
}
// interface
@ -5318,7 +5449,6 @@ void asCScriptEngine::RemoveScriptFunction(asCScriptFunction *func)
// internal
void asCScriptEngine::RemoveFuncdef(asCScriptFunction *funcdef)
{
// TODO: 2.30.0: redesign: How to avoid removing a funcdef that is shared by multiple modules?
funcDefs.RemoveValue(funcdef);
}

View File

@ -238,6 +238,7 @@ public:
bool CallObjectMethodRetBool(void *obj, int func) const;
int CallObjectMethodRetInt(void *obj, int func) const;
void *CallObjectMethodRetPtr(void *obj, int func) const;
void *CallObjectMethodRetPtr(void *obj, int param1, asCScriptFunction *func) const;
void CallGlobalFunction(void *param1, void *param2, asSSystemFunctionInterface *func, asCScriptFunction *desc) const;
bool CallGlobalFunctionRetBool(void *param1, void *param2, asSSystemFunctionInterface *func, asCScriptFunction *desc) const;
@ -400,16 +401,17 @@ public:
// This array stores the template instances types that have been automatically generated from template types
asCArray<asCObjectType *> generatedTemplateTypes;
// Stores the funcdefs
// TODO: 2.30.0: redesign: Only shared funcdefs should be stored here
// a funcdef becomes shared if all arguments and the return type are shared (or application registered)
// TODO: redesign: Only shared funcdefs should be stored here
// a funcdef becomes shared if all arguments and the return type are shared (or application registered)
asCArray<asCScriptFunction *> funcDefs; // doesn't increase ref count
// Stores the names of the script sections for debugging purposes
asCArray<asCString *> scriptSectionNames;
// Type identifiers
mutable int typeIdSeqNbr;
mutable asCMap<int, asCDataType*> mapTypeIdToDataType;
mutable int typeIdSeqNbr;
mutable asCMap<int, asCObjectType*> mapTypeIdToObjectType;
mutable asCMap<int, asCScriptFunction*> mapTypeIdToFunction;
// Garbage collector
asCGarbageCollector gc;

View File

@ -105,7 +105,7 @@ static void ScriptFunction_CreateDelegate_Generic(asIScriptGeneric *gen)
gen->SetReturnAddress(CreateDelegate(func, obj));
}
// TODO: 2.29.0: operator==
// TODO: operator==
/*static void ScriptFunction_opEquals_Generic(asIScriptGeneric *gen)
{
asCScriptFunction *funcSelf = (asCScriptFunction*)gen->GetObject();
@ -124,7 +124,7 @@ void RegisterScriptFunction(asCScriptEngine *engine)
UNUSED_VAR(r); // It is only used in debug mode
engine->functionBehaviours.engine = engine;
engine->functionBehaviours.flags = asOBJ_REF | asOBJ_GC | asOBJ_SCRIPT_FUNCTION;
engine->functionBehaviours.name = "_builtin_function_";
engine->functionBehaviours.name = "$func";
#ifndef AS_MAX_PORTABILITY
r = engine->RegisterBehaviourToObjectType(&engine->functionBehaviours, asBEHAVE_ADDREF, "void f()", asMETHOD(asCScriptFunction,AddRef), asCALL_THISCALL, 0); asASSERT( r >= 0 );
r = engine->RegisterBehaviourToObjectType(&engine->functionBehaviours, asBEHAVE_RELEASE, "void f()", asMETHOD(asCScriptFunction,Release), asCALL_THISCALL, 0); asASSERT( r >= 0 );
@ -133,7 +133,7 @@ void RegisterScriptFunction(asCScriptEngine *engine)
r = engine->RegisterBehaviourToObjectType(&engine->functionBehaviours, asBEHAVE_GETGCFLAG, "bool f()", asMETHOD(asCScriptFunction,GetFlag), asCALL_THISCALL, 0); asASSERT( r >= 0 );
r = engine->RegisterBehaviourToObjectType(&engine->functionBehaviours, asBEHAVE_ENUMREFS, "void f(int&in)", asMETHOD(asCScriptFunction,EnumReferences), asCALL_THISCALL, 0); asASSERT( r >= 0 );
r = engine->RegisterBehaviourToObjectType(&engine->functionBehaviours, asBEHAVE_RELEASEREFS, "void f(int&in)", asMETHOD(asCScriptFunction,ReleaseAllHandles), asCALL_THISCALL, 0); asASSERT( r >= 0 );
// TODO: 2.29.0: Need some way to allow the arg type to adapt when the funcdefs are instantiated
// TODO: Need some way to allow the arg type to adapt when the funcdefs are instantiated
// r = engine->RegisterMethodToObjectType(&engine->functionBehaviours, "bool opEquals(const int &in)", asMETHOD(asCScriptFunction,operator==), asCALL_THISCALL); asASSERT( r >= 0 );
#else
r = engine->RegisterBehaviourToObjectType(&engine->functionBehaviours, asBEHAVE_ADDREF, "void f()", asFUNCTION(ScriptFunction_AddRef_Generic), asCALL_GENERIC, 0); asASSERT( r >= 0 );
@ -226,7 +226,7 @@ asIScriptFunction *asCScriptFunction::GetDelegateFunction() const
return funcForDelegate;
}
// TODO: 2.29.0: operator==
// TODO: operator==
/*
// internal
bool asCScriptFunction::operator==(const asCScriptFunction &other) const
@ -419,7 +419,6 @@ asCScriptFunction::~asCScriptFunction()
// If the engine pointer is 0, then DestroyInternal has already been called and there is nothing more to do
if( engine == 0 ) return;
// TODO: 2.30.0: redesign: Shouldn't this have been done already?
DestroyInternal();
// Finally set the engine pointer to 0 because it must not be accessed again
@ -678,7 +677,7 @@ asCString asCScriptFunction::GetDeclarationStr(bool includeObjectName, bool incl
if( !(returnType.GetTokenType() == ttVoid &&
objectType &&
(name == objectType->name || (name.GetLength() > 0 && name[0] == '~') ||
name == "_beh_0_" || name == "_beh_2_")) )
name == "$beh0" || name == "$beh2")) )
{
str = returnType.Format(nameSpace, includeNamespace);
str += " ";
@ -699,13 +698,13 @@ asCString asCScriptFunction::GetDeclarationStr(bool includeObjectName, bool incl
}
if( name == "" )
str += "_unnamed_function_(";
else if( name.SubString(0,5) == "_beh_" && name.GetLength() == 7 )
else if( name.SubString(0,4) == "$beh" && name.GetLength() == 5 )
{
if( name[5] == '0' + asBEHAVE_CONSTRUCT )
if( name[4] == '0' + asBEHAVE_CONSTRUCT )
str += objectType->name + "(";
else if( name[5] == '0' + asBEHAVE_FACTORY )
else if( name[4] == '0' + asBEHAVE_FACTORY )
str += returnType.GetObjectType()->name + "(";
else if( name[5] == '0' + asBEHAVE_DESTRUCT )
else if( name[4] == '0' + asBEHAVE_DESTRUCT )
str += "~" + objectType->name + "(";
else
str += name + "(";
@ -1008,7 +1007,7 @@ void asCScriptFunction::ComputeSignatureId()
// internal
bool asCScriptFunction::IsSignatureEqual(const asCScriptFunction *func) const
{
if( !IsSignatureExceptNameEqual(func) || name != func->name ) return false;
if( name != func->name || !IsSignatureExceptNameEqual(func) ) return false;
return true;
}
@ -1043,9 +1042,9 @@ bool asCScriptFunction::IsSignatureExceptNameAndReturnTypeEqual(const asCScriptF
bool asCScriptFunction::IsSignatureExceptNameAndReturnTypeEqual(const asCArray<asCDataType> &paramTypes, const asCArray<asETypeModifiers> &paramInOut, const asCObjectType *objType, bool readOnly) const
{
if( this->isReadOnly != readOnly ) return false;
if( (this->objectType != 0) != (objType != 0) ) return false;
if( this->inOutFlags != paramInOut ) return false;
if( this->parameterTypes != paramTypes ) return false;
if( (this->objectType != 0) != (objType != 0) ) return false;
return true;
}
@ -1299,7 +1298,11 @@ void asCScriptFunction::ReleaseReferences()
if( group != 0 ) group->Release();
if( funcId )
engine->scriptFunctions[funcId]->ReleaseInternal();
{
asCScriptFunction *fptr = engine->scriptFunctions[funcId];
if( fptr )
fptr->ReleaseInternal();
}
}
break;

View File

@ -1,6 +1,6 @@
/*
AngelCode Scripting Library
Copyright (c) 2003-2014 Andreas Jonsson
Copyright (c) 2003-2015 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
@ -186,12 +186,12 @@ public:
void DestroyHalfCreated();
// TODO: 2.29.0: operator==
// TODO: 2.29.0: The asIScriptFunction should provide operator== and operator!= that should do a
// a value comparison. Two delegate objects that point to the same object and class method should compare as equal
// TODO: 2.29.0: The operator== should also be provided in script as opEquals to allow the same comparison in script
// To do this we'll need some way to adapt the argtype for opEquals for each funcdef, preferrably without instantiating lots of different methods
// Perhaps reusing 'auto' to mean the same type as the object
// TODO: operator==
// TODO: The asIScriptFunction should provide operator== and operator!= that should do a
// a value comparison. Two delegate objects that point to the same object and class method should compare as equal
// TODO: The operator== should also be provided in script as opEquals to allow the same comparison in script
// To do this we'll need some way to adapt the argtype for opEquals for each funcdef, preferrably without instantiating lots of different methods
// Perhaps reusing 'auto' to mean the same type as the object
//bool operator==(const asCScriptFunction &other) const;
void DestroyInternal();
@ -329,7 +329,7 @@ public:
asSSystemFunctionInterface *sysFuncIntf;
};
const char * const DELEGATE_FACTORY = "%delegate_factory";
const char * const DELEGATE_FACTORY = "$dlgte";
asCScriptFunction *CreateDelegate(asCScriptFunction *func, void *obj);
END_AS_NAMESPACE

View File

@ -1,6 +1,6 @@
/*
AngelCode Scripting Library
Copyright (c) 2003-2014 Andreas Jonsson
Copyright (c) 2003-2015 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
@ -173,6 +173,24 @@ static void ScriptObject_ReleaseAllHandles_Generic(asIScriptGeneric *gen)
self->ReleaseAllHandles(engine);
}
static void ScriptObject_Assignment_Generic(asIScriptGeneric *gen)
{
asCScriptObject *other = *(asCScriptObject**)gen->GetAddressOfArg(0);
asCScriptObject *self = (asCScriptObject*)gen->GetObject();
*self = *other;
*(asCScriptObject**)gen->GetAddressOfReturnLocation() = self;
}
static void ScriptObject_Construct_Generic(asIScriptGeneric *gen)
{
asCObjectType *objType = *(asCObjectType**)gen->GetAddressOfArg(0);
asCScriptObject *self = (asCScriptObject*)gen->GetObject();
ScriptObject_Construct(objType, self);
}
#endif
void RegisterScriptObject(asCScriptEngine *engine)
@ -182,7 +200,7 @@ void RegisterScriptObject(asCScriptEngine *engine)
UNUSED_VAR(r); // It is only used in debug mode
engine->scriptTypeBehaviours.engine = engine;
engine->scriptTypeBehaviours.flags = asOBJ_SCRIPT_OBJECT | asOBJ_REF | asOBJ_GC;
engine->scriptTypeBehaviours.name = "_builtin_object_";
engine->scriptTypeBehaviours.name = "$obj";
#ifndef AS_MAX_PORTABILITY
r = engine->RegisterBehaviourToObjectType(&engine->scriptTypeBehaviours, asBEHAVE_CONSTRUCT, "void f(int&in)", asFUNCTION(ScriptObject_Construct), asCALL_CDECL_OBJLAST, 0); asASSERT( r >= 0 );
r = engine->RegisterBehaviourToObjectType(&engine->scriptTypeBehaviours, asBEHAVE_ADDREF, "void f()", asMETHOD(asCScriptObject,AddRef), asCALL_THISCALL, 0); asASSERT( r >= 0 );
@ -216,14 +234,6 @@ void RegisterScriptObject(asCScriptEngine *engine)
#endif
}
void ScriptObject_Construct_Generic(asIScriptGeneric *gen)
{
asCObjectType *objType = *(asCObjectType**)gen->GetAddressOfArg(0);
asCScriptObject *self = (asCScriptObject*)gen->GetObject();
ScriptObject_Construct(objType, self);
}
void ScriptObject_Construct(asCObjectType *objType, asCScriptObject *self)
{
new(self) asCScriptObject(objType);
@ -744,16 +754,6 @@ void asCScriptObject::ReleaseAllHandles(asIScriptEngine *engine)
}
}
void ScriptObject_Assignment_Generic(asIScriptGeneric *gen)
{
asCScriptObject *other = *(asCScriptObject**)gen->GetAddressOfArg(0);
asCScriptObject *self = (asCScriptObject*)gen->GetObject();
*self = *other;
*(asCScriptObject**)gen->GetAddressOfReturnLocation() = self;
}
asCScriptObject &ScriptObject_Assignment(asCScriptObject *other, asCScriptObject *self)
{
return (*self = *other);

View File

@ -1,6 +1,6 @@
/*
AngelCode Scripting Library
Copyright (c) 2003-2014 Andreas Jonsson
Copyright (c) 2003-2015 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
@ -154,9 +154,6 @@ asCScriptObject &ScriptObject_Assignment(asCScriptObject *other, asCScriptObject
void ScriptObject_ConstructUnitialized(asCObjectType *objType, asCScriptObject *self);
void ScriptObject_Construct_Generic(asIScriptGeneric *gen);
void ScriptObject_Assignment_Generic(asIScriptGeneric *gen);
void RegisterScriptObject(asCScriptEngine *engine);
asIScriptObject *ScriptObjectFactory(const asCObjectType *objType, asCScriptEngine *engine);

View File

@ -142,6 +142,7 @@
#define TXT_INVALID_CONTINUE "Invalid 'continue'"
#define TXT_INVALID_ESCAPE_SEQUENCE "Invalid escape sequence"
#define TXT_INVALID_EXPRESSION_AMBIGUOUS_NAME "Invalid expression: ambiguous name"
#define TXT_INVALID_EXPRESSION_LAMBDA "Invalid expression: stand-alone anonymous function"
#define TXT_INVALID_OP_ON_METHOD "Invalid operation on method"
#define TXT_INVALID_REF_PROP_ACCESS "Invalid reference. Property accessors cannot be used in combined read/write operations"
#define TXT_INVALID_SCOPE "Invalid scope resolution"
@ -252,6 +253,7 @@
#define TXT_TOO_MANY_JUMP_LABELS "The function has too many jump labels to handle. Split the function into smaller ones."
#define TXT_TOO_MANY_VALUES_FOR_LIST "Too many values to match pattern"
#define TXT_TYPE_s_NOT_AVAILABLE_FOR_MODULE "Type '%s' is not available for this module"
#define TXT_TYPE_s_NOT_TEMPLATE "Type '%s' is not a template type"
#define TXT_UNEXPECTED_END_OF_FILE "Unexpected end of file"
#define TXT_UNEXPECTED_TOKEN_s "Unexpected token '%s'"

View File

@ -1,6 +1,6 @@
/*
AngelCode Scripting Library
Copyright (c) 2003-2014 Andreas Jonsson
Copyright (c) 2003-2015 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
@ -311,6 +311,7 @@ const char * const OVERRIDE_TOKEN = "override";
const char * const GET_TOKEN = "get";
const char * const SET_TOKEN = "set";
const char * const ABSTRACT_TOKEN = "abstract";
const char * const FUNCTION_TOKEN = "function";
END_AS_NAMESPACE

View File

@ -1,6 +1,6 @@
/*
AngelCode Scripting Library
Copyright (c) 2003-2014 Andreas Jonsson
Copyright (c) 2003-2015 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
@ -53,7 +53,6 @@ asCTypeInfo::asCTypeInfo()
isExplicitHandle = false;
qwordValue = 0;
isLValue = false;
isVoidExpression = false;
isRefToLocal = false;
}
@ -68,7 +67,6 @@ void asCTypeInfo::Set(const asCDataType &dt)
isExplicitHandle = false;
qwordValue = 0;
isLValue = false;
isVoidExpression = false;
isRefToLocal = false;
}
@ -135,6 +133,18 @@ void asCTypeInfo::SetUndefinedFuncHandle(asCScriptEngine *engine)
isLValue = false;
}
bool asCTypeInfo::IsUndefinedFuncHandle() const
{
if( isConstant == false ) return false;
if( qwordValue == 0 ) return false;
if( isLValue ) return false;
if( dataType.GetObjectType() == 0 ) return false;
if( dataType.GetObjectType()->name != "$func" ) return false;
if( dataType.GetFuncDef() ) return false;
return true;
}
void asCTypeInfo::SetNullConstant()
{
Set(asCDataType::CreateNullHandle());
@ -153,17 +163,19 @@ bool asCTypeInfo::IsNullConstant() const
return false;
}
void asCTypeInfo::SetVoidExpression()
void asCTypeInfo::SetVoid()
{
Set(asCDataType::CreatePrimitive(ttVoid, false));
isLValue = false;
isConstant = false;
isVoidExpression = true;
isConstant = true;
}
bool asCTypeInfo::IsVoidExpression() const
bool asCTypeInfo::IsVoid() const
{
return isVoidExpression;
if( dataType.GetTokenType() == ttVoid )
return true;
return false;
}
void asCTypeInfo::SetDummy()

View File

@ -1,6 +1,6 @@
/*
AngelCode Scripting Library
Copyright (c) 2003-2014 Andreas Jonsson
Copyright (c) 2003-2015 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
@ -61,11 +61,12 @@ struct asCTypeInfo
void SetConstantD(const asCDataType &dataType, double value);
void SetNullConstant();
void SetUndefinedFuncHandle(asCScriptEngine *engine);
void SetVoidExpression();
void SetVoid();
void SetDummy();
bool IsUndefinedFuncHandle() const;
bool IsNullConstant() const;
bool IsVoidExpression() const;
bool IsVoid() const;
asCDataType dataType;
bool isLValue : 1; // Can this value be updated in assignment, or increment operators, etc
@ -73,9 +74,8 @@ struct asCTypeInfo
bool isConstant : 1;
bool isVariable : 1;
bool isExplicitHandle : 1;
bool isVoidExpression : 1;
bool isRefToLocal : 1; // The reference may be to a local variable
short dummy : 9;
short dummy : 10;
short stackOffset;
union
{

View File

@ -593,9 +593,9 @@ namespace UserConfigParams
"stun.voxgratia.org",
"stun.xten.com") );
PARAM_PREFIX StringUserConfigParam m_packets_log_filename
PARAM_DEFAULT( StringUserConfigParam("packets_log.txt", "packets_log_filename",
"Where to log received and sent packets.") );
PARAM_PREFIX BoolUserConfigParam m_log_packets
PARAM_DEFAULT( BoolUserConfigParam(false, "log-network-packets",
"If all network packets should be logged") );
// ---- Graphic Quality
PARAM_PREFIX GroupUserConfigParam m_graphics_quality

View File

@ -36,7 +36,7 @@ private:
public:
HitSFX(const Vec3& coord, const char* explosion_sound);
~HitSFX();
virtual bool updateAndDelete(float dt);
virtual bool updateAndDelete(float dt) OVERRIDE;
virtual void setLocalPlayerKartHit() OVERRIDE;
}; // HitSFX

View File

@ -18,7 +18,6 @@
#ifndef HEADER_SHADERS_HPP
#define HEADER_SHADERS_HPP
#include "config/user_config.hpp"
#include "graphics/shader.hpp"
#include "graphics/shared_gpu_objects.hpp"
#include "graphics/texture_shader.hpp"

View File

@ -17,6 +17,7 @@
#include "graphics/shadow_matrices.hpp"
#include "config/user_config.hpp"
#include "graphics/central_settings.hpp"
#include "graphics/glwrap.hpp"
#include "graphics/irr_driver.hpp"

View File

@ -84,7 +84,7 @@ public:
virtual void collectChar(irr::video::ITexture* texture,
const irr::core::rect<irr::s32>& destRect,
const irr::core::rect<irr::s32>& sourceRect,
const irr::video::SColor* const colors);
const irr::video::SColor* const colors) OVERRIDE;
virtual void updateAbsolutePosition() OVERRIDE;
};

View File

@ -54,6 +54,8 @@ public:
m_render_type = "achievement-message::neutral";
else if (mt==MessageQueue::MT_ERROR)
m_render_type = "error-message::neutral";
else if (mt==MessageQueue::MT_GENERIC)
m_render_type = "generic-message::neutral";
else
m_render_type = "friend-message::neutral";
} // Message

View File

@ -34,7 +34,7 @@ namespace MessageQueue
* different look. This type is used to sort the messages, so it is
* important that messages that need to be shown as early as possible
* will be listed last (i.e. have highest priority). */
enum MessageType { MT_FRIEND, MT_ACHIEVEMENT, MT_ERROR};
enum MessageType { MT_FRIEND, MT_ACHIEVEMENT, MT_ERROR, MT_GENERIC};
void add(MessageType mt, const core::stringw &message);
void updatePosition();

View File

@ -126,14 +126,14 @@ void GamepadConfig::setDefaultBinds ()
{
setBinding(PA_STEER_LEFT, Input::IT_STICKMOTION, 0, Input::AD_NEGATIVE);
setBinding(PA_STEER_RIGHT, Input::IT_STICKMOTION, 0, Input::AD_POSITIVE);
setBinding(PA_ACCEL, Input::IT_STICKMOTION, 1, Input::AD_NEGATIVE);
setBinding(PA_BRAKE, Input::IT_STICKMOTION, 1, Input::AD_POSITIVE);
setBinding(PA_FIRE, Input::IT_STICKBUTTON, 0);
setBinding(PA_NITRO, Input::IT_STICKBUTTON, 1);
setBinding(PA_DRIFT, Input::IT_STICKBUTTON, 2);
setBinding(PA_RESCUE, Input::IT_STICKBUTTON, 3);
setBinding(PA_LOOK_BACK, Input::IT_STICKBUTTON, 4);
setBinding(PA_PAUSE_RACE, Input::IT_STICKBUTTON, 5);
setBinding(PA_ACCEL, Input::IT_STICKBUTTON, 0, Input::AD_NEGATIVE);
setBinding(PA_BRAKE, Input::IT_STICKBUTTON, 3, Input::AD_POSITIVE);
setBinding(PA_FIRE, Input::IT_STICKBUTTON, 1);
setBinding(PA_NITRO, Input::IT_STICKBUTTON, 4);
setBinding(PA_DRIFT, Input::IT_STICKBUTTON, 5);
setBinding(PA_RESCUE, Input::IT_STICKBUTTON, 8);
setBinding(PA_LOOK_BACK, Input::IT_STICKBUTTON, 6);
setBinding(PA_PAUSE_RACE, Input::IT_STICKBUTTON, 9);
setBinding(PA_MENU_UP, Input::IT_STICKMOTION, 1, Input::AD_NEGATIVE);
setBinding(PA_MENU_DOWN, Input::IT_STICKMOTION, 1, Input::AD_POSITIVE);

View File

@ -70,7 +70,7 @@ public:
core::stringw toString();
virtual void save(std::ofstream& stream);
virtual void save(std::ofstream& stream) OVERRIDE;
void setDefaultBinds ();
virtual core::stringw getBindingAsString(const PlayerAction action) const OVERRIDE;
virtual bool load(const XMLNode *config) OVERRIDE;
@ -81,7 +81,7 @@ public:
// ------------------------------------------------------------------------
/** Returns true if this device should desensitize its input at values
* close to 0 (to avoid 'oversteering'). */
virtual bool desensitize() const { return m_desensitize;}
virtual bool desensitize() const OVERRIDE { return m_desensitize;}
// ------------------------------------------------------------------------
/** Returns the number of buttons in this configuration. */
@ -103,9 +103,9 @@ public:
/** Return deadzone of this configuration. */
int getDeadzone() const { return m_deadzone; }
// ------------------------------------------------------------------------
virtual bool isGamePad() const { return true; }
virtual bool isGamePad() const OVERRIDE { return true; }
// ------------------------------------------------------------------------
virtual bool isKeyboard() const { return false; }
virtual bool isKeyboard() const OVERRIDE { return false; }
}; // class GamepadConfig

View File

@ -367,8 +367,8 @@ void InputManager::handleStaticAction(int key, int value)
case KEY_F10:
if(world && value)
{
if(control_is_pressed && ReplayRecorder::get())
ReplayRecorder::get()->Save();
if(control_is_pressed)
ReplayRecorder::get()->save();
else
history->Save();
}

View File

@ -213,6 +213,7 @@ FileManager::FileManager()
checkAndCreateConfigDir();
checkAndCreateAddonsDir();
checkAndCreateScreenshotDir();
checkAndCreateReplayDir();
checkAndCreateCachedTexturesDir();
checkAndCreateGPDir();
@ -641,6 +642,14 @@ std::string FileManager::getScreenshotDir() const
return m_screenshot_dir;
} // getScreenshotDir
//-----------------------------------------------------------------------------
/** Returns the directory in which replay file should be stored.
*/
std::string FileManager::getReplayDir() const
{
return m_replay_dir;
} // getReplayDir
//-----------------------------------------------------------------------------
/** Returns the directory in which resized textures should be cached.
*/
@ -910,6 +919,32 @@ void FileManager::checkAndCreateScreenshotDir()
} // checkAndCreateScreenshotDir
// ----------------------------------------------------------------------------
/** Creates the directories for replay recorded. This will set m_replay_dir
* with the appropriate path.
*/
void FileManager::checkAndCreateReplayDir()
{
#if defined(WIN32) || defined(__CYGWIN__)
m_replay_dir = m_user_config_dir + "replay/";
#elif defined(__APPLE__)
m_replay_dir = getenv("HOME");
m_replay_dir += "/Library/Application Support/SuperTuxKart/replay/";
#else
m_replay_dir = checkAndCreateLinuxDir("XDG_DATA_HOME", "supertuxkart",
".local/share", ".supertuxkart");
m_replay_dir += "replay/";
#endif
if(!checkAndCreateDirectory(m_replay_dir))
{
Log::error("FileManager", "Can not create replay directory '%s', "
"falling back to '.'.", m_replay_dir.c_str());
m_replay_dir = ".";
}
} // checkAndCreateReplayDir
// ----------------------------------------------------------------------------
/** Creates the directories for cached textures. This will set
* m_cached_textures_dir with the appropriate path.

View File

@ -75,6 +75,9 @@ private:
/** Directory to store screenshots in. */
std::string m_screenshot_dir;
/** Directory to store replays in. */
std::string m_replay_dir;
/** Directory where resized textures are cached. */
std::string m_cached_textures_dir;
@ -97,6 +100,7 @@ private:
bool isDirectory(const std::string &path) const;
void checkAndCreateAddonsDir();
void checkAndCreateScreenshotDir();
void checkAndCreateReplayDir();
void checkAndCreateCachedTexturesDir();
void checkAndCreateGPDir();
void discoverPaths();
@ -118,6 +122,7 @@ public:
XMLNode *createXMLTreeFromString(const std::string & content);
std::string getScreenshotDir() const;
std::string getReplayDir() const;
std::string getCachedTexturesDir() const;
std::string getGPDir() const;
std::string getTextureCacheLocation(const std::string& filename);
@ -146,6 +151,9 @@ public:
return fileExists(std::string(prefix) + path);
}
// ------------------------------------------------------------------------
/** Returns the name of the stdout file for log messages. */
static const std::string& getStdoutName() { return m_stdout_filename; }
// ------------------------------------------------------------------------
void listFiles (std::set<std::string>& result,
const std::string& dir,
bool make_full_path=false) const;

View File

@ -480,6 +480,7 @@ void Flyable::explode(AbstractKart *kart_hit, PhysicalObject *object,
->getKartTeam(m_owner->getWorldKartId()))
continue;
}
if (kart->isGhostKart()) continue;
// If no secondary hits should be done, only hit the
// direct hit kart.

View File

@ -294,7 +294,7 @@ void Powerup::use()
for(unsigned int i = 0 ; i < world->getNumKarts(); ++i)
{
AbstractKart *kart=world->getKart(i);
if(kart->isEliminated()) continue;
if(kart->isEliminated() || kart->isInvulnerable()) continue;
if(kart == m_owner) continue;
if(kart->getPosition() == 1)
{
@ -328,7 +328,7 @@ void Powerup::use()
for(unsigned int i = 0 ; i < world->getNumKarts(); ++i)
{
AbstractKart *kart=world->getKart(i);
if(kart->isEliminated() || kart== m_owner) continue;
if(kart->isEliminated() || kart== m_owner || kart->isInvulnerable()) continue;
if(kart->isShielded())
{
kart->decreaseShieldTime();

View File

@ -40,7 +40,14 @@ AbstractKart::AbstractKart(const std::string& ident,
{
m_world_kart_id = world_kart_id;
m_kart_properties.reset(new KartProperties());
m_kart_properties->copyForPlayer(kart_properties_manager->getKart(ident));
const KartProperties* kp = kart_properties_manager->getKart(ident);
if (kp == NULL)
{
Log::warn("Abstract_Kart", "Unknown kart %s, fallback to tux",
ident.c_str());
kp = kart_properties_manager->getKart(std::string("tux"));
}
m_kart_properties->copyForPlayer(kp);
m_difficulty = difficulty;
m_kart_animation = NULL;
assert(m_kart_properties);

View File

@ -46,6 +46,7 @@ class Material;
class Powerup;
class Skidding;
class SlipStream;
class TerrainInfo;
/** An abstract interface for the actual karts. Some functions are actually
* implemented here in order to allow inlining.
@ -421,6 +422,9 @@ public:
/** Shows the star effect for a certain time. */
virtual void showStarEffect(float t) = 0;
// ------------------------------------------------------------------------
/** Returns the terrain info oject. */
virtual const TerrainInfo *getTerrainInfo() const = 0;
// ------------------------------------------------------------------------
/** Called when the kart crashes against another kart.
* \param k The kart that was hit.
* \param update_attachments If true the attachment of this kart and the
@ -450,6 +454,12 @@ public:
// ------------------------------------------------------------------------
/** Returns whether this kart wins or loses. */
virtual bool getRaceResult() const = 0;
// ------------------------------------------------------------------------
/** Returns whether this kart is a ghost (replay) kart. */
virtual bool isGhostKart() const = 0;
// ------------------------------------------------------------------------
/** Returns whether this kart is jumping. */
virtual bool isJumping() const = 0;
}; // AbstractKart

View File

@ -63,7 +63,7 @@ protected:
void setControllerName(const std::string &name);
float steerToPoint(const Vec3 &point);
float normalizeAngle(float angle);
virtual void update (float delta) ;
virtual void update (float delta);
virtual void setSteering (float angle, float dt);
virtual bool canSkid(float steer_fraction) = 0;
// ------------------------------------------------------------------------

View File

@ -0,0 +1,60 @@
//
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2016 SuperTuxKart-Team
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "karts/controller/ghost_controller.hpp"
#include "modes/world.hpp"
GhostController::GhostController(AbstractKart *kart)
: Controller(kart)
{
} // GhostController
//-----------------------------------------------------------------------------
void GhostController::reset()
{
m_current_index = 0;
m_current_time = 0.0f;
} // reset
//-----------------------------------------------------------------------------
void GhostController::update(float dt)
{
m_current_time = World::getWorld()->getTime();
// Find (if necessary) the next index to use
if (m_current_time != 0.0f)
{
while (m_current_index + 1 < m_all_times.size() &&
m_current_time >= m_all_times[m_current_index + 1])
{
m_current_index++;
}
}
} // update
//-----------------------------------------------------------------------------
void GhostController::addReplayTime(float time)
{
// FIXME: for now avoid that transforms for the same time are set
// twice (to avoid division by zero in update). This should be
// done when saving in replay
if (m_all_times.size() > 0 && m_all_times.back() == time)
return;
m_all_times.push_back(time);
} // addReplayTime

View File

@ -0,0 +1,78 @@
//
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2016 SuperTuxKart-Team
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#ifndef HEADER_GHOST_CONTROLLER_HPP
#define HEADER_GHOST_CONTROLLER_HPP
#include "karts/controller/controller.hpp"
#include "states_screens/state_manager.hpp"
#include <vector>
/** A class for Ghost controller.
* \ingroup controller
*/
class GhostController : public Controller
{
private:
/** Pointer to the last index in m_all_times that is smaller than
* the current world time. */
unsigned int m_current_index;
/** The current world time. */
float m_current_time;
/** The list of the times at which the events of kart were reached. */
std::vector<float> m_all_times;
public:
GhostController(AbstractKart *kart);
virtual ~GhostController() {};
virtual void reset();
virtual void update (float dt);
virtual bool disableSlipstreamBonus() const { return true; }
virtual void crashed(const Material *m) {};
virtual void crashed(const AbstractKart *k) {};
virtual void handleZipper(bool play_sound) {};
virtual void finishedRace(float time) {};
virtual void collectedItem(const Item &item, int add_info=-1,
float previous_energy=0) {};
virtual void setPosition(int p) {};
virtual bool isPlayerController() const { return false; }
virtual bool isLocalPlayerController() const { return false; }
virtual void action(PlayerAction action, int value) {};
virtual void skidBonusTriggered() {};
virtual void newLap(int lap) {};
void addReplayTime(float time);
// ------------------------------------------------------------------------
bool isReplayEnd() const
{ return m_current_index + 1 >= m_all_times.size(); }
// ------------------------------------------------------------------------
float getReplayDelta() const
{
assert(m_current_index < m_all_times.size());
return ((m_current_time - m_all_times[m_current_index]) /
(m_all_times[m_current_index + 1] - m_all_times[m_current_index]));
}
// ------------------------------------------------------------------------
unsigned int getCurrentReplayIndex() const
{ return m_current_index; }
// ------------------------------------------------------------------------
}; // GhostController
#endif

View File

@ -75,7 +75,7 @@ public:
virtual bool isLocalPlayerController() const OVERRIDE {return true;}
// ------------------------------------------------------------------------
/** Returns the name of the player profile. */
core::stringw getName() const { return m_player->getProfile()->getName(); }
core::stringw getName() const OVERRIDE { return m_player->getProfile()->getName(); }
}; // LocalPlayerController

View File

@ -64,22 +64,22 @@ public:
{
} // setPosition
// ------------------------------------------------------------------------
virtual void crashed(const AbstractKart *k)
virtual void crashed(const AbstractKart *k) OVERRIDE
{
} // crashed(AbstractKart)
// ------------------------------------------------------------------------
virtual void crashed(const Material *m)
virtual void crashed(const Material *m) OVERRIDE
{
} // crashed(Material)
// ------------------------------------------------------------------------
/** Callback whenever a new lap is triggered. Used by the AI
* to trigger a recomputation of the way to use, not used for players. */
virtual void newLap(int lap)
virtual void newLap(int lap) OVERRIDE
{
}
// ------------------------------------------------------------------------
/** Player will always be able to get a slipstream bonus. */
virtual bool disableSlipstreamBonus() const
virtual bool disableSlipstreamBonus() const OVERRIDE
{
return false;
}

View File

@ -333,7 +333,8 @@ void SkiddingAI::update(float dt)
// If we are faster, try to predict the point where we will hit
// the other kart
if(m_kart_ahead->getSpeed() < m_kart->getSpeed())
if((m_kart_ahead->getSpeed() < m_kart->getSpeed()) &&
!m_kart_ahead->isGhostKart())
{
float time_till_hit = m_distance_ahead
/ (m_kart->getSpeed()-m_kart_ahead->getSpeed());
@ -1757,7 +1758,7 @@ void SkiddingAI::checkCrashes(const Vec3& pos )
{
const AbstractKart* kart = m_world->getKart(j);
// Ignore eliminated karts
if(kart==m_kart||kart->isEliminated()) continue;
if(kart==m_kart||kart->isEliminated()||kart->isGhostKart()) continue;
const AbstractKart *other_kart = m_world->getKart(j);
// Ignore karts ahead that are faster than this kart.
if(m_kart->getVelocityLC().getZ() < other_kart->getVelocityLC().getZ())

View File

@ -17,17 +17,19 @@
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "karts/ghost_kart.hpp"
#include "karts/controller/ghost_controller.hpp"
#include "karts/kart_gfx.hpp"
#include "karts/kart_model.hpp"
#include "modes/world.hpp"
#include "LinearMath/btQuaternion.h"
#include "utils/log.hpp"
GhostKart::GhostKart(const std::string& ident)
: Kart(ident, /*world kart id*/99999,
/*position*/-1, btTransform(), PLAYER_DIFFICULTY_NORMAL)
GhostKart::GhostKart(const std::string& ident, unsigned int world_kart_id,
int position)
: Kart(ident, world_kart_id,
position, btTransform(btQuaternion(0, 0, 0, 1)),
PLAYER_DIFFICULTY_NORMAL)
{
m_current_transform = 0;
m_next_event = 0;
} // GhostKart
// ----------------------------------------------------------------------------
@ -35,84 +37,108 @@ void GhostKart::reset()
{
m_node->setVisible(true);
Kart::reset();
m_current_transform = 0;
m_next_event = 0;
// This will set the correct start position
update(0);
} // reset
// ----------------------------------------------------------------------------
/** Sets the next time and transform. The current time and transform becomes
* the previous time and transform.
* \param
*/
void GhostKart::addTransform(float time, const btTransform &trans)
void GhostKart::addReplayEvent(float time,
const btTransform &trans,
const ReplayBase::PhysicInfo &pi,
const ReplayBase::KartReplayEvent &kre)
{
// FIXME: for now avoid that transforms for the same time are set
// twice (to avoid division by zero in update). This should be
// done when saving in replay
if(m_all_times.size()>0 && m_all_times.back()==time)
return;
m_all_times.push_back(time);
m_all_transform.push_back(trans);
} // addTransform
GhostController* gc = dynamic_cast<GhostController*>(getController());
gc->addReplayTime(time);
m_all_transform.push_back(trans);
m_all_physic_info.push_back(pi);
m_all_replay_events.push_back(kre);
// Use first frame of replay to calculate default suspension
if (m_all_physic_info.size() == 1)
{
float f = 0;
for (int i = 0; i < 4; i++)
f += m_all_physic_info[0].m_suspension_length[i];
m_graphical_y_offset = -f / 4 + getKartModel()->getLowestPoint();
m_kart_model->setDefaultSuspension();
}
// ----------------------------------------------------------------------------
/** Adds a replay event for this kart.
*/
void GhostKart::addReplayEvent(const ReplayBase::KartReplayEvent &kre)
{
m_replay_events.push_back(kre);
} // addReplayEvent
// ----------------------------------------------------------------------------
/** Updates the ghost data each time step. It uses interpolation to get a new
* position and rotation.
/** Updates the current event of the ghost kart using interpolation
* \param dt Time step size.
*/
void GhostKart::update(float dt)
{
float t = World::getWorld()->getTime();
// Don't do anything at startup
if(t==0) return;
updateTransform(t, dt);
while(m_next_event < m_replay_events.size() &&
m_replay_events[m_next_event].m_time <= t)
{
Log::debug("Ghost_Kart", "Handling event %d", m_next_event);
// Handle the next event now
m_next_event++;
}
}
// ----------------------------------------------------------------------------
/** Updates the current transform of the ghost kart using interpolation
* \param t Current world time.
* \param dt Time step size.
*/
void GhostKart::updateTransform(float t, float dt)
{
GhostController* gc = dynamic_cast<GhostController*>(getController());
if (gc == NULL) return;
// Find (if necessary) the next index to use
while(m_current_transform+1 < m_all_times.size() &&
t>=m_all_times[m_current_transform+1])
{
m_current_transform ++;
}
if(m_current_transform+1>=m_all_times.size())
gc->update(dt);
if (gc->isReplayEnd())
{
m_node->setVisible(false);
return;
}
float f =(t - m_all_times[m_current_transform])
/ ( m_all_times[m_current_transform+1]
- m_all_times[m_current_transform] );
setXYZ((1-f)*m_all_transform[m_current_transform ].getOrigin()
+ f *m_all_transform[m_current_transform+1].getOrigin() );
const btQuaternion q = m_all_transform[m_current_transform].getRotation()
.slerp(m_all_transform[m_current_transform+1]
.getRotation(),
f);
const unsigned int idx = gc->getCurrentReplayIndex();
const float rd = gc->getReplayDelta();
assert(idx < m_all_transform.size());
float nitro_frac = 0;
if (m_all_replay_events[idx].m_on_nitro)
{
nitro_frac = fabsf(m_all_physic_info[idx].m_speed) /
(m_kart_properties->getEngineMaxSpeed());
if (nitro_frac > 1.0f)
nitro_frac = 1.0f;
}
getKartGFX()->updateNitroGraphics(nitro_frac);
if (m_all_replay_events[idx].m_on_zipper)
showZipperFire();
setXYZ((1- rd)*m_all_transform[idx ].getOrigin()
+ rd *m_all_transform[idx + 1].getOrigin() );
const btQuaternion q = m_all_transform[idx].getRotation()
.slerp(m_all_transform[idx + 1].getRotation(), rd);
setRotation(q);
Moveable::updateGraphics(dt, Vec3(0,0,0), btQuaternion(0, 0, 0, 1));
Vec3 center_shift(0, 0, 0);
center_shift.setY(m_graphical_y_offset);
center_shift = getTrans().getBasis() * center_shift;
Moveable::updateGraphics(dt, center_shift, btQuaternion(0, 0, 0, 1));
getKartModel()->update(dt, dt*(m_all_physic_info[idx].m_speed),
m_all_physic_info[idx].m_steer, m_all_physic_info[idx].m_speed, idx);
getKartGFX()->update(dt);
Vec3 front(0, 0, getKartLength()*0.5f);
m_xyz_front = getTrans()(front);
if (m_all_replay_events[idx].m_jumping && !m_is_jumping)
{
m_is_jumping = true;
getKartModel()->setAnimation(KartModel::AF_JUMP_START);
}
else if (!m_all_replay_events[idx].m_jumping && m_is_jumping)
{
m_is_jumping = false;
getKartModel()->setAnimation(KartModel::AF_DEFAULT);
}
} // update
// ----------------------------------------------------------------------------
/** Returns the speed of the kart in meters/second. */
float GhostKart::getSpeed() const
{
const GhostController* gc =
dynamic_cast<const GhostController*>(getController());
assert(gc->getCurrentReplayIndex() < m_all_physic_info.size());
return m_all_physic_info[gc->getCurrentReplayIndex()].m_speed;
} // getSpeed

View File

@ -36,37 +36,46 @@
class GhostKart : public Kart
{
private:
/** The list of the times at which the transform were reached. */
std::vector<float> m_all_times;
/** The transforms to assume at the corresponding time in m_all_times. */
std::vector<btTransform> m_all_transform;
std::vector<btTransform> m_all_transform;
std::vector<ReplayBase::KartReplayEvent> m_replay_events;
std::vector<ReplayBase::PhysicInfo> m_all_physic_info;
/** Pointer to the last index in m_all_times that is smaller than
* the current world time. */
unsigned int m_current_transform;
std::vector<ReplayBase::KartReplayEvent> m_all_replay_events;
/** Index of the next kart replay event. */
unsigned int m_next_event;
void updateTransform(float t, float dt);
public:
GhostKart(const std::string& ident);
virtual void update (float dt);
virtual void addTransform(float time, const btTransform &trans);
virtual void addReplayEvent(const ReplayBase::KartReplayEvent &kre);
virtual void reset();
GhostKart(const std::string& ident,
unsigned int world_kart_id, int position);
virtual void update (float dt);
virtual void reset();
// ------------------------------------------------------------------------
/** No physics body for ghost kart, so nothing to adjust. */
virtual void updateWeight() {};
virtual void updateWeight() {};
// ------------------------------------------------------------------------
/** No physics for ghost kart. */
virtual void applyEngineForce (float force) {}
virtual void applyEngineForce (float force) {};
// ------------------------------------------------------------------------
// Not needed to create any physics for a ghost kart.
virtual void createPhysics() {}
virtual void createPhysics() {};
// ------------------------------------------------------------------------
const float getSuspensionLength(int index, int wheel) const
{ return m_all_physic_info[index].m_suspension_length[wheel]; }
// ------------------------------------------------------------------------
void addReplayEvent(float time,
const btTransform &trans,
const ReplayBase::PhysicInfo &pi,
const ReplayBase::KartReplayEvent &kre);
// ------------------------------------------------------------------------
/** Returns whether this kart is a ghost (replay) kart. */
virtual bool isGhostKart() const { return true; }
// ------------------------------------------------------------------------
/** Ghost can't be hunted. */
virtual bool isInvulnerable() const { return true; }
// ------------------------------------------------------------------------
/** Returns the speed of the kart in meters/second. */
virtual float getSpeed() const;
// ------------------------------------------------------------------------
virtual void kartIsInRestNow() {};
// ------------------------------------------------------------------------
}; // GhostKart

View File

@ -837,7 +837,7 @@ void Kart::finishedRace(float time, bool from_server)
// it would trigger a race end again.
if(m_finished_race) return;
if(!from_server)
/* if(!from_server)
{
if(NetworkConfig::get()->isServer())
{
@ -851,7 +851,7 @@ void Kart::finishedRace(float time, bool from_server)
return;
}
} // !from_server
*/
m_finished_race = true;
m_finish_time = time;
m_controller->finishedRace(time);
@ -889,9 +889,12 @@ void Kart::finishedRace(float time, bool from_server)
{
// Save for music handling in race result gui
setRaceResult();
setController(new EndController(this, m_controller));
if(!isGhostKart())
{
setController(new EndController(this, m_controller));
}
// Skip animation if this kart is eliminated
if (m_eliminated) return;
if (m_eliminated || isGhostKart()) return;
m_kart_model->setAnimation(m_race_result ?
KartModel::AF_WIN_START : KartModel::AF_LOSE_START);

View File

@ -64,6 +64,17 @@ class TerrainInfo;
class Kart : public AbstractKart
{
friend class Skidding;
protected:
/** Offset of the graphical kart chassis from the physical chassis. */
float m_graphical_y_offset;
/** The coordinates of the front of the kart, used to determine when a
* new lap is triggered. */
Vec3 m_xyz_front;
/** Is time flying activated */
bool m_is_jumping;
private:
/** Handles speed increase and capping due to powerup, terrain, ... */
MaxSpeed *m_max_speed;
@ -102,13 +113,6 @@ private:
/** Current race position (1-num_karts). */
int m_race_position;
/** The coordinates of the front of the kart, used to determine when a
* new lap is triggered. */
Vec3 m_xyz_front;
/** Offset of the graphical kart chassis from the physical chassis. */
float m_graphical_y_offset;
/** True if the kart wins, false otherwise. */
bool m_race_result;
@ -169,9 +173,6 @@ private:
// Graphical effects
// -----------------
/** Is time flying activated */
bool m_is_jumping;
/** The shadow of a kart. */
Shadow *m_shadow;
@ -430,7 +431,7 @@ public:
virtual void showStarEffect(float t);
// ------------------------------------------------------------------------
/** Returns the terrain info oject. */
TerrainInfo *getTerrainInfo() { return m_terrain_info; }
virtual const TerrainInfo *getTerrainInfo() const { return m_terrain_info; }
// ------------------------------------------------------------------------
virtual void setOnScreenText(const wchar_t *text);
// ------------------------------------------------------------------------
@ -447,6 +448,12 @@ public:
// ------------------------------------------------------------------------
/** Set this kart race result. */
void setRaceResult();
// ------------------------------------------------------------------------
/** Returns whether this kart is a ghost (replay) kart. */
virtual bool isGhostKart() const { return false; }
// ------------------------------------------------------------------------
/** Returns whether this kart is jumping. */
virtual bool isJumping() const { return m_is_jumping; };
}; // Kart

View File

@ -717,6 +717,11 @@ void KartModel::setAnimation(AnimationFrameType type, bool play_non_loop)
m_animated_node->setLoopMode(false);
m_animated_node->setAnimationEndCallback(this);
}
else
{
// Special animation not found, revert to default
m_current_animation = AF_DEFAULT;
}
} // setAnimation
// ----------------------------------------------------------------------------
@ -769,6 +774,14 @@ void KartModel::OnAnimationEnd(scene::IAnimatedMeshSceneNode *node)
// ----------------------------------------------------------------------------
void KartModel::setDefaultSuspension()
{
GhostKart* gk = dynamic_cast<GhostKart*>(m_kart);
if (gk)
{
for (int i = 0; i < 4; i++)
m_default_physics_suspension[i] = gk->getSuspensionLength(0, i);
return;
}
for(int i=0; i<m_kart->getVehicle()->getNumWheels(); i++)
{
const btWheelInfo &wi = m_kart->getVehicle()->getWheelInfo(i);
@ -786,30 +799,45 @@ void KartModel::setDefaultSuspension()
* \param suspension Suspension height for all four wheels.
* \param speed The speed of the kart in meters/sec, used for the
* speed-weighted objects' animations
* \param gt_replay_index The index to get replay data, used by ghost kart
*/
void KartModel::update(float dt, float distance, float steer, float speed)
void KartModel::update(float dt, float distance, float steer, float speed,
int gt_replay_index)
{
core::vector3df wheel_steer(0, steer*30.0f, 0);
for(unsigned int i=0; i<4; i++)
{
if (!m_kart || !m_wheel_node[i]) continue;
if (!m_kart || !m_wheel_node[i]) continue;
#ifdef DEBUG
if (UserConfigParams::m_physics_debug &&
!dynamic_cast<GhostKart*>(m_kart) )
{
const btWheelInfo &wi = m_kart->getVehicle()->getWheelInfo(i);
// Make wheels that are not touching the ground invisible
m_wheel_node[i]->setVisible(wi.m_raycastInfo.m_isInContact);
}
if (UserConfigParams::m_physics_debug &&
!m_kart->isGhostKart())
{
const btWheelInfo &wi = m_kart->getVehicle()->getWheelInfo(i);
// Make wheels that are not touching the ground invisible
m_wheel_node[i]->setVisible(wi.m_raycastInfo.m_isInContact);
}
#endif
core::vector3df pos = m_wheel_graphics_position[i].toIrrVector();
const btWheelInfo &wi = m_kart->getVehicle()->getWheelInfo(i);
float suspension_length = 0.0f;
GhostKart* gk = dynamic_cast<GhostKart*>(m_kart);
// Prevent using m_default_physics_suspension uninitialized
if (gk && gt_replay_index == -1) break;
if (gk)
{
suspension_length = gk->getSuspensionLength(gt_replay_index, i);
}
else
{
suspension_length = m_kart->getVehicle()->getWheelInfo(i).
m_raycastInfo.m_suspensionLength;
}
// Check documentation of Kart::updateGraphics for the following line
pos.Y += m_default_physics_suspension[i]
- wi.m_raycastInfo.m_suspensionLength
- suspension_length
- m_kart_lowest_point;
m_wheel_node[i]->setPosition(pos);

View File

@ -237,8 +237,8 @@ public:
void loadInfo(const XMLNode &node);
bool loadModels(const KartProperties &kart_properties);
void setDefaultSuspension();
void update(float dt, float distance, float steer,
float speed);
void update(float dt, float distance, float steer, float speed,
int gt_replay_index = -1);
void finishedRace();
scene::ISceneNode*
attachModel(bool animatedModels, bool always_animated);

View File

@ -23,7 +23,6 @@
#endif
#include "achievements/achievement_info.hpp"
#include "config/player_manager.hpp"
#include "karts/ghost_kart.hpp"
#include "karts/kart.hpp"
#include "karts/kart_gfx.hpp"
#include "karts/kart_properties.hpp"
@ -82,7 +81,7 @@ void Skidding::reset()
btVector3 rot(0, 0, 0);
// Only access the vehicle if the kart is not a ghost
if (dynamic_cast<GhostKart*>(m_kart)==NULL)
if (!m_kart->isGhostKart())
m_kart->getVehicle()->setTimedRotation(0, rot);
} // reset

View File

@ -527,7 +527,6 @@ void cmdLineHelp()
" spaces are allowed in the track names.\n"
" --demo-laps=n Number of laps in a demo.\n"
" --demo-karts=n Number of karts to use in a demo.\n"
" --ghost Replay ghost data together with one player kart.\n"
// " --history Replay history file 'history.dat'.\n"
// " --history=n Replay history file 'history.dat' using:\n"
// " n=1: recorded positions\n"
@ -1004,9 +1003,6 @@ int handleCmdLine()
}
} // --with-profile
if(CommandLine::has("--ghost"))
ReplayPlay::create();
if(CommandLine::has("--history", &n))
{
history->doReplayHistory( (History::HistoryReplayMode)n);
@ -1157,6 +1153,7 @@ void initRest()
// The order here can be important, e.g. KartPropertiesManager needs
// defaultKartProperties, which are defined in stk_config.
history = new History ();
ReplayPlay::create();
ReplayRecorder::create();
material_manager = new MaterialManager ();
track_manager = new TrackManager ();
@ -1581,7 +1578,6 @@ static void cleanSuperTuxKart()
irr_driver->updateConfigIfRelevant();
AchievementsManager::destroy();
Referee::cleanup();
if(ReplayPlay::get()) ReplayPlay::destroy();
if(race_manager) delete race_manager;
if(grand_prix_manager) delete grand_prix_manager;
if(highscore_manager) delete highscore_manager;
@ -1593,6 +1589,7 @@ static void cleanSuperTuxKart()
if(track_manager) delete track_manager;
if(material_manager) delete material_manager;
if(history) delete history;
ReplayPlay::destroy();
ReplayRecorder::destroy();
delete ParticleKindManager::get();
PlayerManager::destroy();

View File

@ -46,20 +46,20 @@ public:
EasterEggHunt();
virtual ~EasterEggHunt();
virtual void init();
virtual void init() OVERRIDE;
virtual bool isRaceOver();
virtual bool isRaceOver() OVERRIDE;
// overriding World methods
virtual void reset();
virtual void reset() OVERRIDE;
virtual bool raceHasLaps(){ return false; }
virtual bool raceHasLaps() OVERRIDE { return false; }
virtual const std::string& getIdent() const;
virtual void terminateRace();
virtual void update(float dt);
virtual const std::string& getIdent() const OVERRIDE;
virtual void terminateRace() OVERRIDE;
virtual void update(float dt) OVERRIDE;
virtual void getKartsDisplayInfo(
std::vector<RaceGUIBase::KartIconDisplayInfo> *info);
std::vector<RaceGUIBase::KartIconDisplayInfo> *info) OVERRIDE;
void updateKartRanks();
void collectedEasterEgg(const AbstractKart *kart);

View File

@ -48,7 +48,7 @@ public:
// overriding World methods
virtual void reset() OVERRIDE;
virtual const std::string& getIdent() const OVERRIDE;
virtual const btTransform &getStartTransform(int index);
virtual const btTransform &getStartTransform(int index) OVERRIDE;
virtual float getClockStartTime() const;
virtual void getKartsDisplayInfo(
std::vector<RaceGUIBase::KartIconDisplayInfo> *info) OVERRIDE;

View File

@ -178,9 +178,10 @@ void LinearWorld::update(float dt)
// in the position of the kart (e.g. while falling the kart
// might get too close to another part of the track, shortly
// jump to position one, then on reset fall back to last)
if (!kart_info.getTrackSector()->isOnRoad() &&
if ((!kart_info.getTrackSector()->isOnRoad() &&
(!kart->getMaterial() ||
kart->getMaterial()->isDriveReset()) )
kart->getMaterial()->isDriveReset())) &&
!kart->isGhostKart())
continue;
kart_info.getTrackSector()->update(kart->getFrontXYZ());
kart_info.m_overall_distance = kart_info.m_race_lap

View File

@ -160,7 +160,7 @@ public:
// ------------------------------------------------------------------------
/** Returns the number of laps a kart has completed.
* \param kart_index World index of the kart. */
int getKartLaps(unsigned int kart_index) const
int getKartLaps(unsigned int kart_index) const OVERRIDE
{
assert(kart_index < m_kart_info.size());
return m_kart_info[kart_index].m_race_lap;

View File

@ -37,7 +37,7 @@ class OverWorld : public WorldWithRank
protected:
/** Override from base class */
virtual void createRaceGUI();
virtual void createRaceGUI() OVERRIDE;
bool m_return_to_garage;

View File

@ -50,7 +50,7 @@ protected:
virtual AbstractKart *createKart(const std::string &kart_ident, int index,
int local_player_id, int global_player_id,
RaceManager::KartType type,
PerPlayerDifficulty difficulty);
PerPlayerDifficulty difficulty) OVERRIDE;
private:
/** Keep a pointer to the track object of soccer ball */
@ -113,27 +113,27 @@ public:
SoccerWorld();
virtual ~SoccerWorld();
virtual void init();
virtual void init() OVERRIDE;
// clock events
virtual bool isRaceOver();
virtual void terminateRace();
virtual bool isRaceOver() OVERRIDE;
virtual void terminateRace() OVERRIDE;
virtual void countdownReachedZero() OVERRIDE;
// overriding World methods
virtual void reset();
virtual void reset() OVERRIDE;
virtual unsigned int getRescuePositionIndex(AbstractKart *kart) OVERRIDE;
virtual bool useFastMusicNearEnd() const { return false; }
virtual bool useFastMusicNearEnd() const OVERRIDE { return false; }
virtual void getKartsDisplayInfo(
std::vector<RaceGUIBase::KartIconDisplayInfo> *info) {}
std::vector<RaceGUIBase::KartIconDisplayInfo> *info) OVERRIDE {}
virtual bool raceHasLaps() { return false; }
virtual bool raceHasLaps() OVERRIDE { return false; }
virtual const std::string& getIdent() const;
virtual const std::string& getIdent() const OVERRIDE;
virtual void update(float dt);
virtual void update(float dt) OVERRIDE;
// ------------------------------------------------------------------------
void onCheckGoalTriggered(bool first_goal);
// ------------------------------------------------------------------------
@ -157,23 +157,23 @@ public:
m_blue_score_times : m_red_score_times);
}
// ------------------------------------------------------------------------
const int& getKartNode(unsigned int kart_id) const
const int getKartNode(unsigned int kart_id) const
{ return m_kart_on_node[kart_id]; }
// ------------------------------------------------------------------------
const int& getBallNode() const
const int getBallNode() const
{ return m_ball_on_node; }
// ------------------------------------------------------------------------
const Vec3& getBallPosition() const
{ return m_ball_position; }
// ------------------------------------------------------------------------
const int& getGoalNode(SoccerTeam team) const
const int getGoalNode(SoccerTeam team) const
{
return (team == SOCCER_TEAM_BLUE ? m_blue_goal_node : m_red_goal_node);
}
// ------------------------------------------------------------------------
bool isCorrectGoal(unsigned int kart_id, bool first_goal) const;
// ------------------------------------------------------------------------
const int& getDefender(SoccerTeam team) const
const int getDefender(SoccerTeam team) const
{
return (team == SOCCER_TEAM_BLUE ? m_blue_defender : m_red_defender);
}

View File

@ -29,7 +29,7 @@ class StandardRace : public LinearWorld
{
protected:
// clock events
virtual bool isRaceOver();
virtual bool isRaceOver() OVERRIDE;
public:
StandardRace();

View File

@ -146,6 +146,9 @@ void World::init()
m_eliminated_karts = 0;
m_eliminated_players = 0;
m_num_players = 0;
unsigned int gk = 0;
if (race_manager->hasGhostKarts())
gk = ReplayPlay::get()->getNumGhostKart();
// Create the race gui before anything else is attached to the scene node
// (which happens when the track is loaded). This allows the race gui to
@ -179,8 +182,16 @@ void World::init()
// karts can be positioned properly on (and not in) the tracks.
m_track->loadTrackModel(race_manager->getReverseTrack());
if (gk > 0)
{
ReplayPlay::get()->load();
for (unsigned int k = 0; k < gk; k++)
m_karts.push_back(ReplayPlay::get()->getGhostKart(k));
}
for(unsigned int i=0; i<num_karts; i++)
{
if (race_manager->getKartType(i) == RaceManager::KT_GHOST) continue;
std::string kart_ident = history->replayHistory()
? history->getKartIdent(i)
: race_manager->getKartIdent(i);
@ -201,9 +212,6 @@ void World::init()
// Must be called after all karts are created
m_race_gui->init();
if(ReplayPlay::get())
ReplayPlay::get()->Load();
powerup_manager->updateWeightsForRace(num_karts);
if (UserConfigParams::m_weather_effects)
@ -249,7 +257,7 @@ void World::reset()
Camera::getCamera(i)->reset();
}
if(ReplayPlay::get())
if(race_manager->hasGhostKarts())
ReplayPlay::get()->reset();
resetAllKarts();
@ -271,7 +279,11 @@ void World::reset()
race_manager->reset();
// Make sure to overwrite the data from the previous race.
if(!history->replayHistory()) history->initRecording();
if(ReplayRecorder::get()) ReplayRecorder::get()->init();
if(race_manager->willRecordRace())
{
Log::info("World", "Start Recording race.");
ReplayRecorder::get()->init();
}
// Reset all data structures that depend on number of karts.
irr_driver->reset();
@ -381,16 +393,6 @@ World::~World()
{
irr_driver->onUnloadWorld();
if(ReplayPlay::get())
{
// Destroy the old replay object, which also stored the ghost
// karts, and create a new one (which means that in further
// races the usage of ghosts will still be enabled).
ReplayPlay::destroy();
ReplayPlay::create();
}
// In case that a race is aborted (e.g. track not found) m_track is 0.
if(m_track)
m_track->cleanup();
@ -415,9 +417,24 @@ World::~World()
delete m_weather;
for ( unsigned int i = 0 ; i < m_karts.size() ; i++ )
{
// Let ReplayPlay destroy the ghost karts
if (m_karts[i]->isGhostKart()) continue;
delete m_karts[i];
}
if(race_manager->hasGhostKarts())
{
// Destroy the old replay object, which also stored the ghost
// karts, and create a new one (which means that in further
// races the usage of ghosts will still be enabled).
ReplayPlay::destroy();
ReplayPlay::create();
}
m_karts.clear();
race_manager->setRaceGhostKarts(false);
race_manager->setRecordRace(false);
Camera::removeAllCameras();
projectile_manager->cleanup();
@ -446,6 +463,7 @@ void World::onGo()
// from sliding downhill)
for(unsigned int i=0; i<m_karts.size(); i++)
{
if (m_karts[i]->isGhostKart()) continue;
m_karts[i]->getVehicle()->setAllBrakes(0);
}
} // onGo
@ -586,12 +604,13 @@ void World::resetAllKarts()
getPhysics()->getPhysicsWorld()->resetLocalTime();
// If track checking is requested, check all rescue positions if
// they are heigh enough.
// they are high enough.
if(UserConfigParams::m_track_debug)
{
// Loop over all karts, in case that some karts are dfferent
for(unsigned int kart_id=0; kart_id<(unsigned int)m_karts.size(); kart_id++)
{
if (m_karts[kart_id]->isGhostKart()) continue;
for(unsigned int rescue_pos=0;
rescue_pos<getNumberOfRescuePositions();
rescue_pos++)
@ -619,6 +638,7 @@ void World::resetAllKarts()
//that at least one of its wheel will be on the surface of the track
for ( KartList::iterator i=m_karts.begin(); i!=m_karts.end(); i++)
{
if ((*i)->isGhostKart()) continue;
Vec3 xyz = (*i)->getXYZ();
//start projection from top of kart
Vec3 up_offset(0, 0.5f * ((*i)->getKartHeight()), 0);
@ -667,6 +687,7 @@ void World::resetAllKarts()
all_finished=true;
for ( KartList::iterator i=m_karts.begin(); i!=m_karts.end(); i++)
{
if ((*i)->isGhostKart()) continue;
if(!(*i)->isInRest())
{
Vec3 normal;
@ -945,8 +966,7 @@ void World::update(float dt)
PROFILER_PUSH_CPU_MARKER("World::update (sub-updates)", 0x20, 0x7F, 0x00);
history->update(dt);
if(ReplayRecorder::get()) ReplayRecorder::get()->update(dt);
if(ReplayPlay::get()) ReplayPlay::get()->update(dt);
if(race_manager->willRecordRace()) ReplayRecorder::get()->update(dt);
if(history->replayHistory()) dt=history->getNextDelta();
WorldStatus::update(dt);
if (m_script_engine) m_script_engine->update(dt);
@ -1146,6 +1166,7 @@ AbstractKart *World::getLocalPlayerKart(unsigned int n) const
void World::eliminateKart(int kart_id, bool notify_of_elimination)
{
AbstractKart *kart = m_karts[kart_id];
if (kart->isGhostKart()) return;
// Display a message about the eliminated kart in the race guia
if (notify_of_elimination)

View File

@ -175,7 +175,7 @@ protected:
Weather* m_weather;
virtual void onGo();
virtual void onGo() OVERRIDE;
/** Returns true if the race is over. Must be defined by all modes. */
virtual bool isRaceOver() = 0;
virtual void update(float dt);

View File

@ -65,7 +65,7 @@ public:
/** call just after instanciating. can't be moved to the contructor as child
classes must be instanciated, otherwise polymorphism will fail and the
results will be incorrect */
virtual void init();
virtual void init() OVERRIDE;
bool displayRank() const { return m_display_rank; }

View File

@ -172,10 +172,10 @@ void Network::broadcastPacket(NetworkString *data, bool reliable)
void Network::openLog()
{
m_log_file.setAtomic(NULL);
if (UserConfigParams::m_packets_log_filename.toString() != "")
if (UserConfigParams::m_log_packets)
{
std::string s = file_manager
->getUserConfigFile(UserConfigParams::m_packets_log_filename);
->getUserConfigFile(FileManager::getStdoutName()+".packet");
m_log_file.setAtomic(fopen(s.c_str(), "w+"));
}
if (!m_log_file.getData())
@ -198,8 +198,10 @@ void Network::logPacket(const BareNetworkString &ns, bool incoming)
m_log_file.lock();
fprintf(m_log_file.getData(), "[%d\t] %s ",
(int)(StkTime::getRealTime()), arrow);
fprintf(m_log_file.getData(), ns.getLogMessage().c_str());
// Indentation for all lines after the first, so that the dump
// is nicely aligned.
std::string indent(" ");
fprintf(m_log_file.getData(), "%s", ns.getLogMessage(indent).c_str());
m_log_file.unlock();
} // logPacket
// ----------------------------------------------------------------------------

View File

@ -171,9 +171,3 @@ void NetworkConsole::kickAllPlayers()
peers[i]->disconnect();
}
} // kickAllPlayers
// ----------------------------------------------------------------------------
void NetworkConsole::sendPacket(NetworkString *data, bool reliable)
{
m_localhost->broadcastPacket(data, reliable);
} // sendPacket

View File

@ -44,8 +44,6 @@ public:
virtual void run();
void kickAllPlayers();
virtual void sendPacket(NetworkString *data,
bool reliable = true);
// ------------------------------------------------------------------------
void setMaxPlayers(uint8_t count) { m_max_players = count; }
// ------------------------------------------------------------------------

View File

@ -128,7 +128,7 @@ int BareNetworkString::decodeStringW(int pos, irr::core::stringw *out) const
* to stdout or via the Log mechanism. Format
* 0000 : 1234 5678 9abc ... ASCII-
*/
std::string BareNetworkString::getLogMessage() const
std::string BareNetworkString::getLogMessage(const std::string &indent) const
{
std::ostringstream oss;
for(unsigned int line=0; line<m_buffer.size(); line+=16)
@ -154,12 +154,18 @@ std::string BareNetworkString::getLogMessage() const
for(unsigned int i=line; i<upper_limit; i++)
{
uint8_t c = m_buffer[i];
if(isprint(c) && c!=0x09) // Don't print tabs
// Don't print tabs, and characters >=128, which are often shown
// as more than one character.
if(isprint(c) && c!=0x09 && c<=0x80)
oss << char(c);
else
oss << '.';
} // for i
oss << "\n";
// If it's not the last line, add the indentation in front
// of the next line
if(line+16<m_buffer.size())
oss << indent;
} // for line
return oss.str();

Some files were not shown because too many files have changed in this diff Show More