Jump to content

Jason Goepel

Member Since 15 May 2013
Offline Last Active Today, 05:47 AM

Topics I've Started

Left-to-right Evaluation of opEquals method.

23 February 2017 - 10:01 AM

I have a bit of an odd construct in my application which is at odds with a change made in revision 2358 to the compiler.  I have registered a value type.  This value type contains configuration information for an application variable.  The assignment operator for this type modifies the underlying application variable, not the script variable.  For instance (something similar to this):

MyValueType& MyValueType::opAssign(double x)
{
    m_pApplicationVariable->SetValue(x);
    return *this;
}

MyValueType& MyValueType::opAssign(const MyValueType& x)
{
    opAssign(x.m_pApplicationVariable->GetValue());
    return *this;
}

I also have a comparison operator registered which compares a value to the application variable.

bool MyValueType::opEquals(double x)
{
    return m_pApplicationVariable->GetValue() == x;
}

My problem occurs when I try to compare an instance of this value type.  With the new left-to-right evaluation order for overloaded operators, a temporary variable is created for my value type object.  Unfortunately, the constructor isn't capable of setting up the configuration to my application variable, so when the assignment operator is called, my application generates an access violation.

// declared earlier: MyValueType my_val 

if (my_val == 3.5)

// generates...

MyValueType::MyValueType()  // temporary variable
MyValueType& MyValueType::opAssign(const MyValueType&inout)  // <-- crash
bool MyValueType::opEquals(double) // comparison with temporary variable
MyValueType::~MyValueType()  // destruction of temporary variable

How do you recommend I handle this?  Script writers cannot generate these objects directly.  Before using them they must be passed to an application function for configuration.

 

 


JIT Compiler Suspend / BlindMindStudios

08 April 2016 - 07:40 AM

When using the BlindMindStudio JIT compiler, functions that suspect the active context are called twice.  The first time within a JIT block, the second from the AngelScript VM, and with the stack not properly set up for the call.

 

I suspect this issue is a bug in BlindMindStudio's JIT compiler rather than in AngelScript, but I don't know quite enough yet to be sure.

 

When a context is suspended from a function called with a JIT block, it appears that the program pointer doesn't properly advance to the "next line."  The VM advances from the asBC_JitEntry instruction to the asBC_CALLSYS instruction.  If I remove the call to asIScriptContext::Suspend, then the VM doesn't ever process the asBC_CALLSYS instruction.

 

I have included an example below that demonstrates the problem.

#include <iostream>
#include "angelscript.h"
#include "as_jit.h"
using namespace std;

void Yield(int arg)
{
	cout << "Performing Yield... arg = " << arg << endl;
	asGetActiveContext()->Suspend();
}

int main(int argc, char* argv[])
{
	asIScriptEngine* engine = asCreateScriptEngine(ANGELSCRIPT_VERSION);
	asCJITCompiler* jit = new asCJITCompiler(0);
	engine->SetEngineProperty(asEP_INCLUDE_JIT_INSTRUCTIONS, 1);
	engine->SetJITCompiler(jit);

	int r;
	r = engine->RegisterGlobalFunction("void Yield(int)", asFUNCTION(Yield), asCALL_CDECL);
	asIScriptModule* mod = engine->GetModule("module", asGM_ALWAYS_CREATE);

	const char* sz_code = "int main() { Yield(5); return 0; }";

	mod->AddScriptSection("main", sz_code);
	r = mod->Build();

	asIScriptContext* ctx = engine->CreateContext();
	asIScriptFunction* func = mod->GetFunctionByName("main");
	ctx->Prepare(func);
	do
	{
		r = ctx->Execute();
	} while (r == asEXECUTION_SUSPENDED);

	int return_value = ctx->GetReturnDWord();
	ctx->Release();

	engine->Release();
	delete jit;
	return return_value;
}

CallFuction Exceptions

07 December 2015 - 01:59 PM

The call to CallSystemFunctionNative is wrapped in a try/catch block so that it can catch exceptions thrown by a registered function.  However, if the function is registered with asCALL_GENERIC then there is no try/catch block.  Is this intentional or an oversight?  My application has a mix of function registrations, some of them using asCALL_GENERIC, so the effect is that some exceptions thrown by a registered function are caught while others are not.


Unsafe const &in

25 September 2015 - 03:40 PM

Turning on unsafe references allows in-out references to be used for primitives and value types, meaning a function prototyped like:

   void func(const ValueType &inout x) 

will pass a reference to the function and not make a copy.  This works just fine, but it seems like this should also work for a constant in reference:

   void func(const ValueType &in x)

 

However, a copy will always be made for a value type passed to an in reference, even if the in reference is constant.  This construct causes my users confusion, so I took a quick look at trying to modify the code in asCCompiler::PrepareArgument. The solution looks like it would be more complicated than simply changing what PrepareArgument does in response to an in reference.  It looks like there are a lot of checks in the compiler that check the reference type and make assumptions about what an argument is allowed to be.  Is this an accurate understanding?

 

It seems like the checks against the reference type in asCCompiler::MoveArgsToStack would also need to be modified, but I don't quite grasp what the new tests would be.

 

I would appreciate any help, thanks.

 

(P.S. It would also be nice to have a constant in-out reference be able to construct a temporary object if the argument passed in is found in the parameter type's constructor)

 


Calling Convention Bug?

27 April 2015 - 12:26 PM

I've discovered some sort of bug in the "CallSystemFunction."  I have only encountered it in x64 Windows builds (Visual Studio 2013).

 

It looks like the code cleaning up the arguments incorrectly attempts to clean up the return variable.  To trigger the bug, I register a class with a destructor and register a function that returns an instance of this object and takes one as an argument, so that the argument needs to be cleaned up.  The cleanup code will then attempt to free the argument, but it will mistakenly pass a pointer to the return variable to be freed.  This pointer is not a valid heap pointer, so there is a crash.

 

The attached file can be added to the test project to demonstrate the problem.

 

The fix may be something as simple as adding the following code the the cleanup section, but I am not familiar enough with how the AngelScript compiler manages its memory and sets up function calls to make that judgement.

 

as_callfunc.cpp(820)

// Clean up arguments
const asUINT cleanCount = sysFunc->cleanArgs.GetLength();
if( cleanCount )
{
    args = context->m_regs.stackPointer;
    if( callConv >= ICC_THISCALL )
        args += AS_PTR_SIZE;

    //** New code to address bug **/
    if( descr->DoesReturnOnStack() )
        args += AS_PTR_SIZE;

    asSSystemFunctionInterface::SClean *clean = sysFunc->cleanArgs.AddressOf();
    for( asUINT n = 0; n < cleanCount; n++, clean++ )
    {
        void **addr = (void**)&args[clean->off];
        if( clean->op == 0 )
        {
            if( *addr != 0 )
            {
                engine->CallObjectMethod(*addr, clean->ot->beh.release);
                *addr = 0;
            }
        }
        else 
        {
            asASSERT( clean->op == 1 || clean->op == 2 );
            asASSERT( *addr );

            if( clean->op == 2 )
                engine->CallObjectMethod(*addr, clean->ot->beh.destruct);
            
            engine->CallFree(*addr);
        }
    }
}