示例#1
0
// internal
void asCModule::ResolveInterfaceIds()
{
	// For each of the interfaces declared in the script find identical interface in the engine.
	// If an identical interface was found then substitute the current id for the identical interface's id, 
	// then remove this interface declaration. If an interface was modified by the declaration, then 
	// retry the detection of identical interface for it since it may now match another.

	// For an interface to be equal to another the name and methods must match. If the interface
	// references another interface, then that must be checked as well, which can lead to circular references.

	// Example:
	//
	// interface A { void f(B@); }
	// interface B { void f(A@); void f(C@); }
	// interface C { void f(A@); }
	//
	// A1 equals A2 if and only if B1 equals B2
	// B1 equals B2 if and only if A1 equals A2 and C1 equals C2
	// C1 equals C2 if and only if A1 equals A2


	unsigned int i;

	// The interface can only be equal to interfaces declared in other modules. 
	// Interfaces registered by the application will conflict with this one if it has the same name.
	// This means that we only need to look for the interfaces in the engine->classTypes, but not in engine->objectTypes.
	asCArray<sObjectTypePair> equals;
	for( i = 0; i < classTypes.GetLength(); i++ )
	{
		asCObjectType *intf1 = classTypes[i];
		if( !intf1->IsInterface() )
			continue;

		// The interface may have been determined to be equal to another already
		bool found = false;
		for( unsigned int e = 0; e < equals.GetLength(); e++ )
		{
			if( equals[e].a == intf1 )
			{
				found = true;
				break;
			}
		}
		
		if( found )
			break;

		for( unsigned int n = 0; n < engine->classTypes.GetLength(); n++ )
		{
			// Don't compare against self
			if( engine->classTypes[n] == intf1 )
				continue;
	
			asCObjectType *intf2 = engine->classTypes[n];

			// Assume the interface are equal, then validate this
			sObjectTypePair pair = {intf1,intf2};
			equals.PushLast(pair);

			if( AreInterfacesEqual(intf1, intf2, equals) )
				break;
			
			// Since they are not equal, remove them from the list again
			equals.PopLast();
		}
	}

	// For each of the interfaces that have been found to be equal we need to 
	// remove the new declaration and instead have the module use the existing one.
	for( i = 0; i < equals.GetLength(); i++ )
	{
		// Substitute the old object type from the module's class types
		unsigned int c;
		for( c = 0; c < classTypes.GetLength(); c++ )
		{
			if( classTypes[c] == equals[i].a )
			{
				classTypes[c] = equals[i].b;
				equals[i].b->AddRef();
				break;
			}
		}

		// Remove the old object type from the engine's class types
		for( c = 0; c < engine->classTypes.GetLength(); c++ )
		{
			if( engine->classTypes[c] == equals[i].a )
			{
				engine->classTypes[c] = engine->classTypes[engine->classTypes.GetLength()-1];
				engine->classTypes.PopLast();
				break;
			}
		}

		// Substitute all uses of this object type
		// Only interfaces in the module is using the type so far
		for( c = 0; c < classTypes.GetLength(); c++ )
		{
			if( classTypes[c]->IsInterface() )
			{
				asCObjectType *intf = classTypes[c];
				for( int m = 0; m < intf->GetMethodCount(); m++ )
				{
					asCScriptFunction *func = engine->GetScriptFunction(intf->methods[m]);
					if( func )
					{
						if( func->returnType.GetObjectType() == equals[i].a )
							func->returnType.SetObjectType(equals[i].b);

						for( int p = 0; p < func->GetParamCount(); p++ )
						{
							if( func->parameterTypes[p].GetObjectType() == equals[i].a )
								func->parameterTypes[p].SetObjectType(equals[i].b);
						}
					}
				}
			}
		}
// internal
bool asCModule::AreInterfacesEqual(asCObjectType *a, asCObjectType *b, asCArray<sObjectTypePair> &equals)
{
	// An interface is considered equal to another if the following criterias apply:
	//
	// - The interface names are equal
	// - The number of methods is equal
	// - All the methods are equal
	// - The order of the methods is equal
	// - If a method returns or takes an interface by handle or reference, both interfaces must be equal


	// ------------
	// TODO: Study the possiblity of allowing interfaces where methods are declared in different orders to
	// be considered equal. The compiler and VM can handle this, but it complicates the comparison of interfaces
	// where multiple methods take different interfaces as parameters (or return values). Example:
	// 
	// interface A
	// {
	//    void f(B, C);
	//    void f(B);
	//    void f(C);
	// }
	//
	// If 'void f(B)' in module A is compared against 'void f(C)' in module B, then the code will assume
	// interface B in module A equals interface C in module B. Thus 'void f(B, C)' in module A won't match
	// 'void f(C, B)' in module B.
	// ------------


	// Are both interfaces?
	if( !a->IsInterface() || !b->IsInterface() )
		return false;

	// Are the names equal?
	if( a->name != b->name )
		return false;

	// Are the number of methods equal?
	if( a->methods.GetLength() != b->methods.GetLength() )
		return false;

	// Keep the number of equals in the list so we can restore it later if necessary
	int prevEquals = (int)equals.GetLength();

	// Are the methods equal to each other?
	bool match = true;
	for( unsigned int n = 0; n < a->methods.GetLength(); n++ )
	{
		match = false;

		asCScriptFunction *funcA = (asCScriptFunction*)engine->GetFunctionDescriptorById(a->methods[n]);
		asCScriptFunction *funcB = (asCScriptFunction*)engine->GetFunctionDescriptorById(b->methods[n]);

		// funcB can be null if the module that created the interface has been  
		// discarded but the type has not yet been released by the engine.
		if( funcB == 0 )
			break;

		// The methods must have the same name and the same number of parameters
		if( funcA->name != funcB->name ||
			funcA->parameterTypes.GetLength() != funcB->parameterTypes.GetLength() )
			break;
		
		// The return types must be equal. If the return type is an interface the interfaces must match.
		if( !AreTypesEqual(funcA->returnType, funcB->returnType, equals) )
			break;

		match = true;
		for( unsigned int p = 0; p < funcA->parameterTypes.GetLength(); p++ )
		{
			if( !AreTypesEqual(funcA->parameterTypes[p], funcB->parameterTypes[p], equals) ||
				funcA->inOutFlags[p] != funcB->inOutFlags[p] )
			{
				match = false;
				break;
			}
		}

		if( !match )
			break;
	}

	// For each of the new interfaces that we're assuming to be equal, we need to validate this
	if( match )
	{
		for( unsigned int n = prevEquals; n < equals.GetLength(); n++ )
		{
			if( !AreInterfacesEqual(equals[n].a, equals[n].b, equals) )
			{
				match = false;
				break;
			}
		}
	}

	if( !match )
	{
		// The interfaces doesn't match. 
		// Restore the list of previous equals before we go on, so  
		// the caller can continue comparing with another interface
		equals.SetLength(prevEquals);
	}

	return match;
}