Jump to content

  • Log In with Google      Sign In   
  • Create Account


#Actualcellulose

Posted 06 September 2012 - 05:54 PM

Below is my current progress with CompileFunctionCall. It looks like the CompileVariableAccess that looks for a callable variable is already doing the work of pushing the called object onto the stack. It's hilarious to think this might work in some naive way. However, I doubt it does. I'm guessing I need to deal with the format of the variable (reference/dereference it?) and possibly store a temporary copy, as is done for funcdefs toward the bottom.

The supposed necessity of a temporary variable here actually baffles me a little, given the only eligible identifiers are local, class and global variables of funcdef type.

int asCCompiler::CompileFunctionCall(asCScriptNode *node, asSExprContext *ctx, asCObjectType *objectType, bool objIsConst, const asCString &scope)
{
asCString name;
asCTypeInfo tempObj;
asCArray<int> funcs;
int r = -1;
asCScriptNode *nm = node->lastChild->prev;
name.Assign(&script->code[nm->tokenPos], nm->tokenLength);
// If we're compiling a class method, then the call may be to a class method
// even though it looks like an ordinary call to a global function. If it is
// to a class method it is necessary to implicitly add the this pointer.
if( objectType == 0 && outFunc && outFunc->objectType && scope != "::" )
{
  // The special keyword 'super' may be used in constructors to invoke the base
  // class' constructor. This can only be used without any scoping operator
  if( m_isConstructor && name == SUPER_TOKEN && scope == "" )
  {
   // We are calling the base class' constructor, so set the objectType
   objectType = outFunc->objectType;
  }
  else
  {
   // Are there any class methods that may match?
   // TODO: namespace: Should really make sure the scope also match. Because the scope
   //				  may match a base class, or it may match a global namespace. If it is
   //				  matching a global scope then we're not calling a class method even
   //				  if there is a method with the same name.
   asCArray<int> funcs;
   builder->GetObjectMethodDescriptions(name.AddressOf(), outFunc->objectType, funcs, false);
   if( funcs.GetLength() )
   {
	// We're calling a class method, so set the objectType
	objectType = outFunc->objectType;
   }
  }

  // If a class method is being called then implicitly add the this pointer for the call
  if( objectType )
  {
   asCDataType dt = asCDataType::CreateObject(objectType, false);
   // The object pointer is located at stack position 0
   ctx->bc.InstrSHORT(asBC_PSF, 0);
   ctx->type.SetVariable(dt, 0, false);
   ctx->type.dataType.MakeReference(true);
   Dereference(ctx, true);
  }  
}
// First check for a local variable of a callable type
// Must not allow function names, nor global variables to be returned in this instance
asSExprContext callableVar(engine);
if( objectType == 0 )
{
  //In the case of a callable object, this adds the this pointer for the call.
  r = CompileVariableAccess(name, scope, &callableVar, node, true, true);
}
if( r < 0 )
{
  if( objectType )
  {
   // If we're compiling a constructor and the name of the function is super then
   // the constructor of the base class is being called.
   // super cannot be prefixed with a scope operator
   if( scope == "" && m_isConstructor && name == SUPER_TOKEN )
   {
	// If the class is not derived from anyone else, calling super should give an error
	if( objectType->derivedFrom )
	 funcs = objectType->derivedFrom->beh.constructors;
	// Must not allow calling base class' constructor multiple times
	if( continueLabels.GetLength() > 0 )
	{
	 // If a continue label is set we are in a loop
	 Error(TXT_CANNOT_CALL_CONSTRUCTOR_IN_LOOPS, node);
	}
	else if( breakLabels.GetLength() > 0 )
	{
	 // TODO: inheritance: Should eventually allow constructors in switch statements
	 // If a break label is set we are either in a loop or a switch statements
	 Error(TXT_CANNOT_CALL_CONSTRUCTOR_IN_SWITCH, node);
	}
	else if( m_isConstructorCalled )
	{
	 Error(TXT_CANNOT_CALL_CONSTRUCTOR_TWICE, node);
	}
	m_isConstructorCalled = true;
   }
   else
   {
	// The scope is can be used to specify the base class
	builder->GetObjectMethodDescriptions(name.AddressOf(), objectType, funcs, objIsConst, scope);
   }
   // It is still possible that there is a class member of a callable type
   if( funcs.GetLength() == 0 )
	CompileVariableAccess(name, scope, &callableVar, node, true, true, objectType);
  }
  else
  {
   // The scope is used to define the namespace
   asSNameSpace *ns = DetermineNameSpace(scope);
   if( ns )
	builder->GetFunctionDescriptions(name.AddressOf(), funcs, ns);
   else
   {
	asCString msg;
	msg.Format(TXT_NAMESPACE_s_DOESNT_EXIST, scope.AddressOf());
	Error(msg.AddressOf(), node);
	return -1;
   }
   // TODO: funcdef: It is still possible that there is a global variable of a callable type
   //	...is it?  (Code above should account for this)  -- EDB 09/2012
  }
}
// Handle the callable variable
if ( callableVar.type.dataType.IsValid() )
{
  //Determine callability of object
  r = -1;
  if( callableVar.type.dataType.GetFuncDef() )
  {
   //Function pointers are always callable
   funcs.PushLast(callableVar.type.dataType.GetFuncDef()->id);
   r = 0;
  }
  else if( callableVar.type.dataType.IsObject() )
  {
   //Check whether object implements opCall
   const char *opCall = "opCall";
   asCObjectType *callType = callableVar.type.dataType.GetObjectType();
   builder->GetObjectMethodDescriptions(opCall, callType, funcs, false);
   if ( funcs.GetLength() )
   {
	r = 0;
	objectType = callType;
   }
  }
  if (r < 0)
  {
   // The variable is not a function
   asCString msg;
   msg.Format(TXT_NOT_A_FUNC_s_IS_VAR, name.AddressOf());
   Error(msg.AddressOf(), node);
   return -1;
  }
}

// Compile the arguments
asCArray<asSExprContext *> args;
asCArray<asCTypeInfo> temporaryVariables;
if( CompileArgumentList(node->lastChild, args) >= 0 )
{
  // Special case: Allow calling func(void) with a void expression.
  if( args.GetLength() == 1 && args[0]->type.dataType == asCDataType::CreatePrimitive(ttVoid, false) )
  {
   // Evaluate the expression before the function call
   MergeExprBytecode(ctx, args[0]);
   asDELETE(args[0],asSExprContext);
   args.SetLength(0);
  }
  MatchFunctions(funcs, args, node, name.AddressOf(), objectType, objIsConst, false, true, scope);
  if( funcs.GetLength() != 1 )
  {
   // The error was reported by MatchFunctions()
   // Dummy value
   ctx->type.SetDummy();
  }
  else
  {
   int r = asSUCCESS;
   // Add the default values for arguments not explicitly supplied
   asCScriptFunction *func = (funcs[0] & 0xFFFF0000) == 0 ? engine->scriptFunctions[funcs[0]] : 0;
   if( func && args.GetLength() < (asUINT)func->GetParamCount() )
	r = CompileDefaultArgs(node, args, func);
   // TODO: funcdef: Do we have to make sure the handle is stored in a temporary variable, or
   //				is it enough to make sure it is in a local variable?
   // For function pointer we must guarantee that the function is safe, i.e.
   // by first storing the function pointer in a local variable (if it isn't already in one)
   if( r == asSUCCESS )
   {
	if( func && func->funcType == asFUNC_FUNCDEF )
	{
	 if( objectType )
	 {
	  Dereference(ctx, true); // Dereference the object pointer to access the member
	  // The actual function should be called as if a global function
	  objectType = 0;
	 }
	 Dereference(&callableVar, true);
	 ConvertToVariable(&funcPtr);
	 ctx->bc.AddCode(&funcPtr.bc);
	 if( !funcPtr.type.isTemporary )
	  ctx->bc.Instr(asBC_PopPtr);
	}
	MakeFunctionCall(ctx, funcs[0], objectType, args, node, false, 0, callableVar.type.stackOffset);
	// If the function pointer was copied to a local variable for the call, then
	// release it again (temporary local variable)
	if( func && func->funcType == asFUNC_FUNCDEF )
	{
	 ReleaseTemporaryVariable(funcPtr.type, &ctx->bc);
	}
   }
  }
}
else
{
  // Failed to compile the argument list, set the dummy type and continue compilation
  ctx->type.SetDummy();
}
// Cleanup
for( asUINT n = 0; n < args.GetLength(); n++ )
  if( args[n] )
  {
   asDELETE(args[n],asSExprContext);
  }
return 0;
}

EDIT: Just realized why that global-variable lookup would be necessary. I'll probably be adding code there...

EDIT: I can't seem to determine what disqualifies global variables from the first variable lookup attempt. Any insight into that?
CompileVariableAccess(name, scope, &callableVar, node, true, true);

#3cellulose

Posted 06 September 2012 - 05:54 PM

Below is my current progress with CompileFunctionCall. It looks like the CompileVariableAccess that looks for a callable variable is already doing the work of pushing the called object onto the stack. It's hilarious to think this might work in some naive way. However, I doubt it does. I'm guessing I need to deal with the format of the variable (reference/dereference it?) and possibly store a temporary copy, as is done for funcdefs toward the bottom.

The supposed necessity of a temporary variable here actually baffles me a little, given the only eligible identifiers are local, class and global variables of funcdef type.

int asCCompiler::CompileFunctionCall(asCScriptNode *node, asSExprContext *ctx, asCObjectType *objectType, bool objIsConst, const asCString &scope)
{
asCString name;
asCTypeInfo tempObj;
asCArray<int> funcs;
int r = -1;
asCScriptNode *nm = node->lastChild->prev;
name.Assign(&script->code[nm->tokenPos], nm->tokenLength);
// If we're compiling a class method, then the call may be to a class method
// even though it looks like an ordinary call to a global function. If it is
// to a class method it is necessary to implicitly add the this pointer.
if( objectType == 0 && outFunc && outFunc->objectType && scope != "::" )
{
  // The special keyword 'super' may be used in constructors to invoke the base
  // class' constructor. This can only be used without any scoping operator
  if( m_isConstructor && name == SUPER_TOKEN && scope == "" )
  {
   // We are calling the base class' constructor, so set the objectType
   objectType = outFunc->objectType;
  }
  else
  {
   // Are there any class methods that may match?
   // TODO: namespace: Should really make sure the scope also match. Because the scope
   //				  may match a base class, or it may match a global namespace. If it is
   //				  matching a global scope then we're not calling a class method even
   //				  if there is a method with the same name.
   asCArray<int> funcs;
   builder->GetObjectMethodDescriptions(name.AddressOf(), outFunc->objectType, funcs, false);
   if( funcs.GetLength() )
   {
	// We're calling a class method, so set the objectType
	objectType = outFunc->objectType;
   }
  }

  // If a class method is being called then implicitly add the this pointer for the call
  if( objectType )
  {
   asCDataType dt = asCDataType::CreateObject(objectType, false);
   // The object pointer is located at stack position 0
   ctx->bc.InstrSHORT(asBC_PSF, 0);
   ctx->type.SetVariable(dt, 0, false);
   ctx->type.dataType.MakeReference(true);
   Dereference(ctx, true);
  }  
}
// First check for a local variable of a callable type
// Must not allow function names, nor global variables to be returned in this instance
asSExprContext callableVar(engine);
if( objectType == 0 )
{
  //In the case of a callable object, this adds the this pointer for the call.
  r = CompileVariableAccess(name, scope, &callableVar, node, true, true);
}
if( r < 0 )
{
  if( objectType )
  {
   // If we're compiling a constructor and the name of the function is super then
   // the constructor of the base class is being called.
   // super cannot be prefixed with a scope operator
   if( scope == "" && m_isConstructor && name == SUPER_TOKEN )
   {
	// If the class is not derived from anyone else, calling super should give an error
	if( objectType->derivedFrom )
	 funcs = objectType->derivedFrom->beh.constructors;
	// Must not allow calling base class' constructor multiple times
	if( continueLabels.GetLength() > 0 )
	{
	 // If a continue label is set we are in a loop
	 Error(TXT_CANNOT_CALL_CONSTRUCTOR_IN_LOOPS, node);
	}
	else if( breakLabels.GetLength() > 0 )
	{
	 // TODO: inheritance: Should eventually allow constructors in switch statements
	 // If a break label is set we are either in a loop or a switch statements
	 Error(TXT_CANNOT_CALL_CONSTRUCTOR_IN_SWITCH, node);
	}
	else if( m_isConstructorCalled )
	{
	 Error(TXT_CANNOT_CALL_CONSTRUCTOR_TWICE, node);
	}
	m_isConstructorCalled = true;
   }
   else
   {
	// The scope is can be used to specify the base class
	builder->GetObjectMethodDescriptions(name.AddressOf(), objectType, funcs, objIsConst, scope);
   }
   // It is still possible that there is a class member of a callable type
   if( funcs.GetLength() == 0 )
	CompileVariableAccess(name, scope, &callableVar, node, true, true, objectType);
  }
  else
  {
   // The scope is used to define the namespace
   asSNameSpace *ns = DetermineNameSpace(scope);
   if( ns )
	builder->GetFunctionDescriptions(name.AddressOf(), funcs, ns);
   else
   {
	asCString msg;
	msg.Format(TXT_NAMESPACE_s_DOESNT_EXIST, scope.AddressOf());
	Error(msg.AddressOf(), node);
	return -1;
   }
   // TODO: funcdef: It is still possible that there is a global variable of a callable type
   //	...is it?  (Code above should account for this)  -- EDB 09/2012
  }
}
// Handle the callable variable
if ( callableVar.type.dataType.IsValid() )
{
  //Determine callability of object
  r = -1;
  if( callableVar.type.dataType.GetFuncDef() )
  {
   //Function pointers are always callable
   funcs.PushLast(callableVar.type.dataType.GetFuncDef()->id);
   r = 0;
  }
  else if( callableVar.type.dataType.IsObject() )
  {
   //Check whether object implements opCall
   const char *opCall = "opCall";
   asCObjectType *callType = callableVar.type.dataType.GetObjectType();
   builder->GetObjectMethodDescriptions(opCall, callType, funcs, false);
   if ( funcs.GetLength() )
   {
	r = 0;
	objectType = callType;
   }
  }
  if (r < 0)
  {
   // The variable is not a function
   asCString msg;
   msg.Format(TXT_NOT_A_FUNC_s_IS_VAR, name.AddressOf());
   Error(msg.AddressOf(), node);
   return -1;
  }
}

// Compile the arguments
asCArray<asSExprContext *> args;
asCArray<asCTypeInfo> temporaryVariables;
if( CompileArgumentList(node->lastChild, args) >= 0 )
{
  // Special case: Allow calling func(void) with a void expression.
  if( args.GetLength() == 1 && args[0]->type.dataType == asCDataType::CreatePrimitive(ttVoid, false) )
  {
   // Evaluate the expression before the function call
   MergeExprBytecode(ctx, args[0]);
   asDELETE(args[0],asSExprContext);
   args.SetLength(0);
  }
  MatchFunctions(funcs, args, node, name.AddressOf(), objectType, objIsConst, false, true, scope);
  if( funcs.GetLength() != 1 )
  {
   // The error was reported by MatchFunctions()
   // Dummy value
   ctx->type.SetDummy();
  }
  else
  {
   int r = asSUCCESS;
   // Add the default values for arguments not explicitly supplied
   asCScriptFunction *func = (funcs[0] & 0xFFFF0000) == 0 ? engine->scriptFunctions[funcs[0]] : 0;
   if( func && args.GetLength() < (asUINT)func->GetParamCount() )
	r = CompileDefaultArgs(node, args, func);
   // TODO: funcdef: Do we have to make sure the handle is stored in a temporary variable, or
   //				is it enough to make sure it is in a local variable?
   // For function pointer we must guarantee that the function is safe, i.e.
   // by first storing the function pointer in a local variable (if it isn't already in one)
   if( r == asSUCCESS )
   {
	if( func && func->funcType == asFUNC_FUNCDEF )
	{
	 if( objectType )
	 {
	  Dereference(ctx, true); // Dereference the object pointer to access the member
	  // The actual function should be called as if a global function
	  objectType = 0;
	 }
	 Dereference(&callableVar, true);
	 ConvertToVariable(&funcPtr);
	 ctx->bc.AddCode(&funcPtr.bc);
	 if( !funcPtr.type.isTemporary )
	  ctx->bc.Instr(asBC_PopPtr);
	}
	MakeFunctionCall(ctx, funcs[0], objectType, args, node, false, 0, callableVar.type.stackOffset);
	// If the function pointer was copied to a local variable for the call, then
	// release it again (temporary local variable)
	if( func && func->funcType == asFUNC_FUNCDEF )
	{
	 ReleaseTemporaryVariable(funcPtr.type, &ctx->bc);
	}
   }
  }
}
else
{
  // Failed to compile the argument list, set the dummy type and continue compilation
  ctx->type.SetDummy();
}
// Cleanup
for( asUINT n = 0; n < args.GetLength(); n++ )
  if( args[n] )
  {
   asDELETE(args[n],asSExprContext);
  }
return 0;
}

EDIT: Just realized why that global-variable lookup would be necessary. I'll probably be adding code there...

EDIT: I can't seem to determine what disqualifies global variables from the first variable lookup attempt. Any insight into that?

#2cellulose

Posted 06 September 2012 - 05:31 PM

Below is my current progress with CompileFunctionCall. It looks like the CompileVariableAccess that looks for a callable variable is already doing the work of pushing the called object onto the stack. It's hilarious to think this might work in some naive way. However, I doubt it does. I'm guessing I need to deal with the format of the variable (reference/dereference it?) and possibly store a temporary copy, as is done for funcdefs toward the bottom.

The supposed necessity of a temporary variable here actually baffles me a little, given the only eligible identifiers are local, class and global variables of funcdef type.

int asCCompiler::CompileFunctionCall(asCScriptNode *node, asSExprContext *ctx, asCObjectType *objectType, bool objIsConst, const asCString &scope)
{
asCString name;
asCTypeInfo tempObj;
asCArray<int> funcs;
int r = -1;
asCScriptNode *nm = node->lastChild->prev;
name.Assign(&script->code[nm->tokenPos], nm->tokenLength);
// If we're compiling a class method, then the call may be to a class method
// even though it looks like an ordinary call to a global function. If it is
// to a class method it is necessary to implicitly add the this pointer.
if( objectType == 0 && outFunc && outFunc->objectType && scope != "::" )
{
  // The special keyword 'super' may be used in constructors to invoke the base
  // class' constructor. This can only be used without any scoping operator
  if( m_isConstructor && name == SUPER_TOKEN && scope == "" )
  {
   // We are calling the base class' constructor, so set the objectType
   objectType = outFunc->objectType;
  }
  else
  {
   // Are there any class methods that may match?
   // TODO: namespace: Should really make sure the scope also match. Because the scope
   //				  may match a base class, or it may match a global namespace. If it is
   //				  matching a global scope then we're not calling a class method even
   //				  if there is a method with the same name.
   asCArray<int> funcs;
   builder->GetObjectMethodDescriptions(name.AddressOf(), outFunc->objectType, funcs, false);
   if( funcs.GetLength() )
   {
	// We're calling a class method, so set the objectType
	objectType = outFunc->objectType;
   }
  }

  // If a class method is being called then implicitly add the this pointer for the call
  if( objectType )
  {
   asCDataType dt = asCDataType::CreateObject(objectType, false);
   // The object pointer is located at stack position 0
   ctx->bc.InstrSHORT(asBC_PSF, 0);
   ctx->type.SetVariable(dt, 0, false);
   ctx->type.dataType.MakeReference(true);
   Dereference(ctx, true);
  }  
}
// First check for a local variable of a callable type
// Must not allow function names, nor global variables to be returned in this instance
asSExprContext callableVar(engine);
if( objectType == 0 )
{
  //In the case of a callable object, this adds the this pointer for the call.
  r = CompileVariableAccess(name, scope, &callableVar, node, true, true);
}
if( r < 0 )
{
  if( objectType )
  {
   // If we're compiling a constructor and the name of the function is super then
   // the constructor of the base class is being called.
   // super cannot be prefixed with a scope operator
   if( scope == "" && m_isConstructor && name == SUPER_TOKEN )
   {
	// If the class is not derived from anyone else, calling super should give an error
	if( objectType->derivedFrom )
	 funcs = objectType->derivedFrom->beh.constructors;
	// Must not allow calling base class' constructor multiple times
	if( continueLabels.GetLength() > 0 )
	{
	 // If a continue label is set we are in a loop
	 Error(TXT_CANNOT_CALL_CONSTRUCTOR_IN_LOOPS, node);
	}
	else if( breakLabels.GetLength() > 0 )
	{
	 // TODO: inheritance: Should eventually allow constructors in switch statements
	 // If a break label is set we are either in a loop or a switch statements
	 Error(TXT_CANNOT_CALL_CONSTRUCTOR_IN_SWITCH, node);
	}
	else if( m_isConstructorCalled )
	{
	 Error(TXT_CANNOT_CALL_CONSTRUCTOR_TWICE, node);
	}
	m_isConstructorCalled = true;
   }
   else
   {
	// The scope is can be used to specify the base class
	builder->GetObjectMethodDescriptions(name.AddressOf(), objectType, funcs, objIsConst, scope);
   }
   // It is still possible that there is a class member of a callable type
   if( funcs.GetLength() == 0 )
	CompileVariableAccess(name, scope, &callableVar, node, true, true, objectType);
  }
  else
  {
   // The scope is used to define the namespace
   asSNameSpace *ns = DetermineNameSpace(scope);
   if( ns )
	builder->GetFunctionDescriptions(name.AddressOf(), funcs, ns);
   else
   {
	asCString msg;
	msg.Format(TXT_NAMESPACE_s_DOESNT_EXIST, scope.AddressOf());
	Error(msg.AddressOf(), node);
	return -1;
   }
   // TODO: funcdef: It is still possible that there is a global variable of a callable type
   //	...is it?  (Code above should account for this)  -- EDB 09/2012
  }
}
// Handle the callable variable
if ( callableVar.type.dataType.IsValid() )
{
  //Determine callability of object
  r = -1;
  if( callableVar.type.dataType.GetFuncDef() )
  {
   //Function pointers are always callable
   funcs.PushLast(callableVar.type.dataType.GetFuncDef()->id);
   r = 0;
  }
  else if( callableVar.type.dataType.IsObject() )
  {
   //Check whether object implements opCall
   const char *opCall = "opCall";
   asCObjectType *callType = callableVar.type.dataType.GetObjectType();
   builder->GetObjectMethodDescriptions(opCall, callType, funcs, false);
   if ( funcs.GetLength() )
   {
	r = 0;
	objectType = callType;
   }
  }
  if (r < 0)
  {
   // The variable is not a function
   asCString msg;
   msg.Format(TXT_NOT_A_FUNC_s_IS_VAR, name.AddressOf());
   Error(msg.AddressOf(), node);
   return -1;
  }
}

// Compile the arguments
asCArray<asSExprContext *> args;
asCArray<asCTypeInfo> temporaryVariables;
if( CompileArgumentList(node->lastChild, args) >= 0 )
{
  // Special case: Allow calling func(void) with a void expression.
  if( args.GetLength() == 1 && args[0]->type.dataType == asCDataType::CreatePrimitive(ttVoid, false) )
  {
   // Evaluate the expression before the function call
   MergeExprBytecode(ctx, args[0]);
   asDELETE(args[0],asSExprContext);
   args.SetLength(0);
  }
  MatchFunctions(funcs, args, node, name.AddressOf(), objectType, objIsConst, false, true, scope);
  if( funcs.GetLength() != 1 )
  {
   // The error was reported by MatchFunctions()
   // Dummy value
   ctx->type.SetDummy();
  }
  else
  {
   int r = asSUCCESS;
   // Add the default values for arguments not explicitly supplied
   asCScriptFunction *func = (funcs[0] & 0xFFFF0000) == 0 ? engine->scriptFunctions[funcs[0]] : 0;
   if( func && args.GetLength() < (asUINT)func->GetParamCount() )
	r = CompileDefaultArgs(node, args, func);
   // TODO: funcdef: Do we have to make sure the handle is stored in a temporary variable, or
   //				is it enough to make sure it is in a local variable?
   // For function pointer we must guarantee that the function is safe, i.e.
   // by first storing the function pointer in a local variable (if it isn't already in one)
   if( r == asSUCCESS )
   {
	if( func && func->funcType == asFUNC_FUNCDEF )
	{
	 if( objectType )
	 {
	  Dereference(ctx, true); // Dereference the object pointer to access the member
	  // The actual function should be called as if a global function
	  objectType = 0;
	 }
	 Dereference(&callableVar, true);
	 ConvertToVariable(&funcPtr);
	 ctx->bc.AddCode(&funcPtr.bc);
	 if( !funcPtr.type.isTemporary )
	  ctx->bc.Instr(asBC_PopPtr);
	}
	MakeFunctionCall(ctx, funcs[0], objectType, args, node, false, 0, callableVar.type.stackOffset);
	// If the function pointer was copied to a local variable for the call, then
	// release it again (temporary local variable)
	if( func && func->funcType == asFUNC_FUNCDEF )
	{
	 ReleaseTemporaryVariable(funcPtr.type, &ctx->bc);
	}
   }
  }
}
else
{
  // Failed to compile the argument list, set the dummy type and continue compilation
  ctx->type.SetDummy();
}
// Cleanup
for( asUINT n = 0; n < args.GetLength(); n++ )
  if( args[n] )
  {
   asDELETE(args[n],asSExprContext);
  }
return 0;
}

EDIT: Just realized why that global-variable lookup would be necessary. I'll probably be adding code there...

#1cellulose

Posted 06 September 2012 - 05:29 PM

Below is my current progress with CompileFunctionCall. It looks like the CompileVariableAccess that looks for a callable variable is already doing the work of pushing the called object onto the stack. It's hilarious to think this might work in some naive way. However, I doubt it does. I'm guessing I need to deal with the format of the variable (reference/dereference it?) and possibly store a temporary copy, as is done for funcdefs toward the bottom.

The supposed necessity of a temporary variable here actually baffles me a little, given the only eligible identifiers are local, class and global variables of funcdef type.

int asCCompiler::CompileFunctionCall(asCScriptNode *node, asSExprContext *ctx, asCObjectType *objectType, bool objIsConst, const asCString &scope)
{
asCString name;
asCTypeInfo tempObj;
asCArray<int> funcs;
int r = -1;
asCScriptNode *nm = node->lastChild->prev;
name.Assign(&script->code[nm->tokenPos], nm->tokenLength);
// If we're compiling a class method, then the call may be to a class method
// even though it looks like an ordinary call to a global function. If it is
// to a class method it is necessary to implicitly add the this pointer.
if( objectType == 0 && outFunc && outFunc->objectType && scope != "::" )
{
  // The special keyword 'super' may be used in constructors to invoke the base
  // class' constructor. This can only be used without any scoping operator
  if( m_isConstructor && name == SUPER_TOKEN && scope == "" )
  {
   // We are calling the base class' constructor, so set the objectType
   objectType = outFunc->objectType;
  }
  else
  {
   // Are there any class methods that may match?
   // TODO: namespace: Should really make sure the scope also match. Because the scope
   //				  may match a base class, or it may match a global namespace. If it is
   //				  matching a global scope then we're not calling a class method even
   //				  if there is a method with the same name.
   asCArray<int> funcs;
   builder->GetObjectMethodDescriptions(name.AddressOf(), outFunc->objectType, funcs, false);
   if( funcs.GetLength() )
   {
    // We're calling a class method, so set the objectType
    objectType = outFunc->objectType;
   }
  }

  // If a class method is being called then implicitly add the this pointer for the call
  if( objectType )
  {
   asCDataType dt = asCDataType::CreateObject(objectType, false);
   // The object pointer is located at stack position 0
   ctx->bc.InstrSHORT(asBC_PSF, 0);
   ctx->type.SetVariable(dt, 0, false);
   ctx->type.dataType.MakeReference(true);
   Dereference(ctx, true);
  }  
}
// First check for a local variable of a callable type
// Must not allow function names, nor global variables to be returned in this instance
asSExprContext callableVar(engine);
if( objectType == 0 )
{
  //In the case of a callable object, this adds the this pointer for the call.
  r = CompileVariableAccess(name, scope, &callableVar, node, true, true);
}
if( r < 0 )
{
  if( objectType )
  {
   // If we're compiling a constructor and the name of the function is super then
   // the constructor of the base class is being called.
   // super cannot be prefixed with a scope operator
   if( scope == "" && m_isConstructor && name == SUPER_TOKEN )
   {
    // If the class is not derived from anyone else, calling super should give an error
    if( objectType->derivedFrom )
	 funcs = objectType->derivedFrom->beh.constructors;
    // Must not allow calling base class' constructor multiple times
    if( continueLabels.GetLength() > 0 )
    {
	 // If a continue label is set we are in a loop
	 Error(TXT_CANNOT_CALL_CONSTRUCTOR_IN_LOOPS, node);
    }
    else if( breakLabels.GetLength() > 0 )
    {
	 // TODO: inheritance: Should eventually allow constructors in switch statements
	 // If a break label is set we are either in a loop or a switch statements
	 Error(TXT_CANNOT_CALL_CONSTRUCTOR_IN_SWITCH, node);
    }
    else if( m_isConstructorCalled )
    {
	 Error(TXT_CANNOT_CALL_CONSTRUCTOR_TWICE, node);
    }
    m_isConstructorCalled = true;
   }
   else
   {
    // The scope is can be used to specify the base class
    builder->GetObjectMethodDescriptions(name.AddressOf(), objectType, funcs, objIsConst, scope);
   }
   // It is still possible that there is a class member of a callable type
   if( funcs.GetLength() == 0 )
    CompileVariableAccess(name, scope, &callableVar, node, true, true, objectType);
  }
  else
  {
   // The scope is used to define the namespace
   asSNameSpace *ns = DetermineNameSpace(scope);
   if( ns )
    builder->GetFunctionDescriptions(name.AddressOf(), funcs, ns);
   else
   {
    asCString msg;
    msg.Format(TXT_NAMESPACE_s_DOESNT_EXIST, scope.AddressOf());
    Error(msg.AddressOf(), node);
    return -1;
   }
   // TODO: funcdef: It is still possible that there is a global variable of a callable type
   //    ...is it?  (Code above should account for this)  -- EDB 09/2012
  }
}
// Handle the callable variable
if ( callableVar.type.dataType.IsValid() )
{
  //Determine callability of object
  r = -1;
  if( callableVar.type.dataType.GetFuncDef() )
  {
   //Function pointers are always callable
   funcs.PushLast(callableVar.type.dataType.GetFuncDef()->id);
   r = 0;
  }
  else if( callableVar.type.dataType.IsObject() )
  {
   //Check whether object implements opCall
   const char *opCall = "opCall";
   asCObjectType *callType = callableVar.type.dataType.GetObjectType();
   builder->GetObjectMethodDescriptions(opCall, callType, funcs, false);
   if ( funcs.GetLength() )
   {
    r = 0;
    objectType = callType;
   }
  }
  if (r < 0)
  {
   // The variable is not a function
   asCString msg;
   msg.Format(TXT_NOT_A_FUNC_s_IS_VAR, name.AddressOf());
   Error(msg.AddressOf(), node);
   return -1;
  }
}

// Compile the arguments
asCArray<asSExprContext *> args;
asCArray<asCTypeInfo> temporaryVariables;
if( CompileArgumentList(node->lastChild, args) >= 0 )
{
  // Special case: Allow calling func(void) with a void expression.
  if( args.GetLength() == 1 && args[0]->type.dataType == asCDataType::CreatePrimitive(ttVoid, false) )
  {
   // Evaluate the expression before the function call
   MergeExprBytecode(ctx, args[0]);
   asDELETE(args[0],asSExprContext);
   args.SetLength(0);
  }
  MatchFunctions(funcs, args, node, name.AddressOf(), objectType, objIsConst, false, true, scope);
  if( funcs.GetLength() != 1 )
  {
   // The error was reported by MatchFunctions()
   // Dummy value
   ctx->type.SetDummy();
  }
  else
  {
   int r = asSUCCESS;
   // Add the default values for arguments not explicitly supplied
   asCScriptFunction *func = (funcs[0] & 0xFFFF0000) == 0 ? engine->scriptFunctions[funcs[0]] : 0;
   if( func && args.GetLength() < (asUINT)func->GetParamCount() )
    r = CompileDefaultArgs(node, args, func);
   // TODO: funcdef: Do we have to make sure the handle is stored in a temporary variable, or
   //			    is it enough to make sure it is in a local variable?
   // For function pointer we must guarantee that the function is safe, i.e.
   // by first storing the function pointer in a local variable (if it isn't already in one)
   if( r == asSUCCESS )
   {
    if( func && func->funcType == asFUNC_FUNCDEF )
    {
	 if( objectType )
	 {
	  Dereference(ctx, true); // Dereference the object pointer to access the member
	  // The actual function should be called as if a global function
	  objectType = 0;
	 }
	 Dereference(&callableVar, true);
	 ConvertToVariable(&funcPtr);
	 ctx->bc.AddCode(&funcPtr.bc);
	 if( !funcPtr.type.isTemporary )
	  ctx->bc.Instr(asBC_PopPtr);
    }
    MakeFunctionCall(ctx, funcs[0], objectType, args, node, false, 0, callableVar.type.stackOffset);
    // If the function pointer was copied to a local variable for the call, then
    // release it again (temporary local variable)
    if( func && func->funcType == asFUNC_FUNCDEF )
    {
	 ReleaseTemporaryVariable(funcPtr.type, &ctx->bc);
    }
   }
  }
}
else
{
  // Failed to compile the argument list, set the dummy type and continue compilation
  ctx->type.SetDummy();
}
// Cleanup
for( asUINT n = 0; n < args.GetLength(); n++ )
  if( args[n] )
  {
   asDELETE(args[n],asSExprContext);
  }
return 0;
}

PARTNERS