JavaScriptCore: Bug fix for multiple typemap out values.

This is a fix for Issue #42 
https://github.com/oliver----/swig/issues/42


I found a bug with the way multiple return values are handled with the JSCore implementation.

Basically, I have this function that takes 5 parameters where arg2-5 are out-values. (The function also returns an int value.)
result = (int)SDL_QueryTexture(arg1,arg2,arg3,arg4,arg5);

The implementation tries to put all the out-value arguments into an array to pass back.
The problem arises in the first step and how SWIGJSC_AppendOutput works.

Below is an excerpt from the generated code.
1) Note that the return value is first put into the jsresult.
2) Then SWIGJSC_AppendOutput passes jsresult as a parameter with the expectation that that the function will return a new array containing both objects
3) Then SWIGJSC_AppendOutput is used again to keep appending arguments.

static JSValueRef _wrap_QueryTexture(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argc, const JSValueRef argv[], JSValueRef* exception)
{
...

result = (int)SDL_QueryTexture(arg1,arg2,arg3,arg4,arg5);
jsresult = SWIG_From_int SWIG_JSC_FROM_CALL_ARGS((int)(result));
if (SWIG_IsTmpObj(res2)) {
jsresult = SWIGJSC_AppendOutput(context, jsresult, SWIG_From_int SWIG_JSC_FROM_CALL_ARGS((arg2)));
} else {
int new_flags = SWIG_IsNewObj(res2) ? (SWIG_POINTER_OWN | 0 ) : 0 ;
jsresult = SWIGJSC_AppendOutput(context, jsresult, SWIG_NewPointerObj((void)(arg2), SWIGTYPE_p_unsigned_int, new_flags));
}
if (SWIG_IsTmpObj(res3)) {
jsresult = SWIGJSC_AppendOutput(context, jsresult, SWIG_From_int SWIG_JSC_FROM_CALL_ARGS((arg3)));
} else {
int new_flags = SWIG_IsNewObj(res3) ? (SWIG_POINTER_OWN | 0 ) : 0 ;
jsresult = SWIGJSC_AppendOutput(context, jsresult, SWIG_NewPointerObj((void)(arg3), SWIGTYPE_p_int, new_flags));
...
return jsresult;
goto fail;
}

The problem is that it is not possible for SWIGJSC_AppendOutput to distinguish between an array and some other object. This code won't work:

if (JSValueIsUndefined(context, value)) {
arr = JSObjectMakeArray(context, 0, 0, 0);
} else {
arr = JSValueToObject(context, value, 0);
}

The problem is that the first return value is not a JSUndefined. It is actually an object (a number object for the int return value in this specific case).

To further complicate things, it doesn't seem possible to distinguish between an array vs. any other JSObjectRef in the JSCore API. It looks like you are expected to implement your own conventions if you need it.

This patch takes the simple solution and injects a JSObjectMakeArray() right after the return result and puts the return result in the array. Then the code can carry on as before.
  result = (int)SDL_QueryTexture(arg1,arg2,arg3,arg4,arg5);
  jsresult = SWIG_From_int  SWIG_JSC_FROM_CALL_ARGS((int)(result));
  {
    JSObjectRef out_array = JSObjectMakeArray(context, 0, 0, NULL);
    jsresult = SWIGJSC_AppendOutput(context, out_array, jsresult);
  }
  // do as before

So for the following .i file which tests the edge cases of no return values through multiple return values, the corresponding output follows.

%include "typemaps.i"

void emptyCall(void);
int returnInt();
%apply int *OUTPUT { int *w, int *h };
%inline %{
int  SDL_QueryTexture2WithRet(SDL_Texture * texture, int *w, int *h);
%}

%inline %{
void SDL_QueryTexture2NoRet(SDL_Texture * texture, int *w, int *h);
%}
%apply int *OUTPUT { int *w };

%inline %{
int  SDL_QueryTexture1WithRet(SDL_Texture * texture, int *w);
%}

%inline %{
void SDL_QueryTexture1NoRet(SDL_Texture * texture, int *w);
%}

The output is:

static JSValueRef _wrap_emptyCall(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argc, const JSValueRef argv[], JSValueRef* exception)
{
  JSValueRef jsresult;

  if(argc != 0) SWIG_exception_fail(SWIG_ERROR, "Illegal number of arguments for _wrap_emptyCall.");

  emptyCall();
  jsresult = JSValueMakeUndefined(context);

  return jsresult;

  goto fail;
fail:
  return NULL;
}

static JSValueRef _wrap_returnInt(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argc, const JSValueRef argv[], JSValueRef* exception)
{
  int result;

  JSValueRef jsresult;

  if(argc != 0) SWIG_exception_fail(SWIG_ERROR, "Illegal number of arguments for _wrap_returnInt.");

  result = (int)returnInt();
  jsresult = SWIG_From_int  SWIG_JSC_FROM_CALL_ARGS((int)(result));

  return jsresult;

  goto fail;
fail:
  return NULL;
}

static JSValueRef _wrap_SDL_QueryTexture2WithRet(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argc, const JSValueRef argv[], JSValueRef* exception)
{
  SDL_Texture *arg1 = (SDL_Texture *) 0 ;
  int *arg2 = (int *) 0 ;
  int *arg3 = (int *) 0 ;
  void *argp1 = 0 ;
  int res1 = 0 ;
  int temp2 ;
  int res2 = SWIG_TMPOBJ ;
  int temp3 ;
  int res3 = SWIG_TMPOBJ ;
  int result;

  JSValueRef jsresult;

  if(argc != 1) SWIG_exception_fail(SWIG_ERROR, "Illegal number of arguments for _wrap_SDL_QueryTexture2WithRet.");

  res1 = SWIG_ConvertPtr(argv[0], &argp1,SWIGTYPE_p_SDL_Texture, 0 |  0 );
  if (!SWIG_IsOK(res1)) {
    SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "SDL_QueryTexture2WithRet" "', argument " "1"" of type '" "SDL_Texture *""'");
  }
  arg1 = (SDL_Texture *)(argp1);
  arg2 = &temp2;
  arg3 = &temp3;
  result = (int)SDL_QueryTexture2WithRet(arg1,arg2,arg3);
  jsresult = SWIG_From_int  SWIG_JSC_FROM_CALL_ARGS((int)(result));
  {
    JSObjectRef out_array = JSObjectMakeArray(context, 0, 0, NULL);
    jsresult = SWIGJSC_AppendOutput(context, out_array, jsresult);
  }
  if (SWIG_IsTmpObj(res2)) {
    jsresult = SWIGJSC_AppendOutput(context, jsresult, SWIG_From_int  SWIG_JSC_FROM_CALL_ARGS((*arg2)));
  } else {
    int new_flags = SWIG_IsNewObj(res2) ? (SWIG_POINTER_OWN |  0 ) :  0 ;
    jsresult = SWIGJSC_AppendOutput(context, jsresult, SWIG_NewPointerObj((void*)(arg2), SWIGTYPE_p_int, new_flags));
  }
  if (SWIG_IsTmpObj(res3)) {
    jsresult = SWIGJSC_AppendOutput(context, jsresult, SWIG_From_int  SWIG_JSC_FROM_CALL_ARGS((*arg3)));
  } else {
    int new_flags = SWIG_IsNewObj(res3) ? (SWIG_POINTER_OWN |  0 ) :  0 ;
    jsresult = SWIGJSC_AppendOutput(context, jsresult, SWIG_NewPointerObj((void*)(arg3), SWIGTYPE_p_int, new_flags));
  }

  return jsresult;

  goto fail;
fail:
  return NULL;
}

static JSValueRef _wrap_SDL_QueryTexture2NoRet(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argc, const JSValueRef argv[], JSValueRef* exception)
{
  SDL_Texture *arg1 = (SDL_Texture *) 0 ;
  int *arg2 = (int *) 0 ;
  int *arg3 = (int *) 0 ;
  void *argp1 = 0 ;
  int res1 = 0 ;
  int temp2 ;
  int res2 = SWIG_TMPOBJ ;
  int temp3 ;
  int res3 = SWIG_TMPOBJ ;

  JSValueRef jsresult;

  if(argc != 1) SWIG_exception_fail(SWIG_ERROR, "Illegal number of arguments for _wrap_SDL_QueryTexture2NoRet.");

  res1 = SWIG_ConvertPtr(argv[0], &argp1,SWIGTYPE_p_SDL_Texture, 0 |  0 );
  if (!SWIG_IsOK(res1)) {
    SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "SDL_QueryTexture2NoRet" "', argument " "1"" of type '" "SDL_Texture *""'");
  }
  arg1 = (SDL_Texture *)(argp1);
  arg2 = &temp2;
  arg3 = &temp3;
  SDL_QueryTexture2NoRet(arg1,arg2,arg3);
  jsresult = JSValueMakeUndefined(context);
  {
    JSObjectRef out_array = JSObjectMakeArray(context, 0, 0, NULL);
    jsresult = SWIGJSC_AppendOutput(context, out_array, jsresult);
  }
  if (SWIG_IsTmpObj(res2)) {
    jsresult = SWIGJSC_AppendOutput(context, jsresult, SWIG_From_int  SWIG_JSC_FROM_CALL_ARGS((*arg2)));
  } else {
    int new_flags = SWIG_IsNewObj(res2) ? (SWIG_POINTER_OWN |  0 ) :  0 ;
    jsresult = SWIGJSC_AppendOutput(context, jsresult, SWIG_NewPointerObj((void*)(arg2), SWIGTYPE_p_int, new_flags));
  }
  if (SWIG_IsTmpObj(res3)) {
    jsresult = SWIGJSC_AppendOutput(context, jsresult, SWIG_From_int  SWIG_JSC_FROM_CALL_ARGS((*arg3)));
  } else {
    int new_flags = SWIG_IsNewObj(res3) ? (SWIG_POINTER_OWN |  0 ) :  0 ;
    jsresult = SWIGJSC_AppendOutput(context, jsresult, SWIG_NewPointerObj((void*)(arg3), SWIGTYPE_p_int, new_flags));
  }

  return jsresult;

  goto fail;
fail:
  return NULL;
}

static JSValueRef _wrap_SDL_QueryTexture1WithRet(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argc, const JSValueRef argv[], JSValueRef* exception)
{
  SDL_Texture *arg1 = (SDL_Texture *) 0 ;
  int *arg2 = (int *) 0 ;
  void *argp1 = 0 ;
  int res1 = 0 ;
  int temp2 ;
  int res2 = SWIG_TMPOBJ ;
  int result;

  JSValueRef jsresult;

  if(argc != 1) SWIG_exception_fail(SWIG_ERROR, "Illegal number of arguments for _wrap_SDL_QueryTexture1WithRet.");

  res1 = SWIG_ConvertPtr(argv[0], &argp1,SWIGTYPE_p_SDL_Texture, 0 |  0 );
  if (!SWIG_IsOK(res1)) {
    SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "SDL_QueryTexture1WithRet" "', argument " "1"" of type '" "SDL_Texture *""'");
  }
  arg1 = (SDL_Texture *)(argp1);
  arg2 = &temp2;
  result = (int)SDL_QueryTexture1WithRet(arg1,arg2);
  jsresult = SWIG_From_int  SWIG_JSC_FROM_CALL_ARGS((int)(result));
  {
    JSObjectRef out_array = JSObjectMakeArray(context, 0, 0, NULL);
    jsresult = SWIGJSC_AppendOutput(context, out_array, jsresult);
  }
  if (SWIG_IsTmpObj(res2)) {
    jsresult = SWIGJSC_AppendOutput(context, jsresult, SWIG_From_int  SWIG_JSC_FROM_CALL_ARGS((*arg2)));
  } else {
    int new_flags = SWIG_IsNewObj(res2) ? (SWIG_POINTER_OWN |  0 ) :  0 ;
    jsresult = SWIGJSC_AppendOutput(context, jsresult, SWIG_NewPointerObj((void*)(arg2), SWIGTYPE_p_int, new_flags));
  }

  return jsresult;

  goto fail;
fail:
  return NULL;
}

static JSValueRef _wrap_SDL_QueryTexture1NoRet(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argc, const JSValueRef argv[], JSValueRef* exception)
{
  SDL_Texture *arg1 = (SDL_Texture *) 0 ;
  int *arg2 = (int *) 0 ;
  void *argp1 = 0 ;
  int res1 = 0 ;
  int temp2 ;
  int res2 = SWIG_TMPOBJ ;

  JSValueRef jsresult;

  if(argc != 1) SWIG_exception_fail(SWIG_ERROR, "Illegal number of arguments for _wrap_SDL_QueryTexture1NoRet.");

  res1 = SWIG_ConvertPtr(argv[0], &argp1,SWIGTYPE_p_SDL_Texture, 0 |  0 );
  if (!SWIG_IsOK(res1)) {
    SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "SDL_QueryTexture1NoRet" "', argument " "1"" of type '" "SDL_Texture *""'");
  }
  arg1 = (SDL_Texture *)(argp1);
  arg2 = &temp2;
  SDL_QueryTexture1NoRet(arg1,arg2);
  jsresult = JSValueMakeUndefined(context);
  {
    JSObjectRef out_array = JSObjectMakeArray(context, 0, 0, NULL);
    jsresult = SWIGJSC_AppendOutput(context, out_array, jsresult);
  }
  if (SWIG_IsTmpObj(res2)) {
    jsresult = SWIGJSC_AppendOutput(context, jsresult, SWIG_From_int  SWIG_JSC_FROM_CALL_ARGS((*arg2)));
  } else {
    int new_flags = SWIG_IsNewObj(res2) ? (SWIG_POINTER_OWN |  0 ) :  0 ;
    jsresult = SWIGJSC_AppendOutput(context, jsresult, SWIG_NewPointerObj((void*)(arg2), SWIGTYPE_p_int, new_flags));
  }

  return jsresult;

  goto fail;
fail:
  return NULL;
}