- the rest of the fix for bug 14738 - detect JSErrors with

JSErrorReports when thrown as exceptions. Extract JSErrorReport
and convert to an xpcexception. This restores functionality that
was whacked when JS errors-as-exceptions was enabled in the JS
engine.
- add conversion support for string-with-length as part of array
support mentioned in bug 13420. All the array stuff is basically
in with minimal testcases. More comprehensive tests need to be
written to verify and tune the code.
- fix a broken #undef
- switch to using PR_Alloc/PR_Free internally in nsjsid where we
were using new/delete before. This is prompted by warren's change
to nsID::ToString that uses PR_Alloc were before it used new.
This fixes an alloc/delete mismatch detected by Purify.
r=mccabe
This commit is contained in:
jband%netscape.com 1999-10-06 21:35:27 +00:00
Родитель 295700338f
Коммит 0da203c7c3
11 изменённых файлов: 445 добавлений и 45 удалений

Просмотреть файл

@ -188,5 +188,10 @@ interface nsIXPCTestArray : nsISupports {
[array, size_is(count)] inout string valueArray);
void ReverseStringArray(in PRUint32 count,
[array, size_is(count)] inout string valueArray);
void PrintStringWithSize(in PRUint32 count,
[size_is(count)] in string str);
void DoubleString(inout PRUint32 count,
[size_is(count)] inout string str);
};

Просмотреть файл

@ -109,6 +109,7 @@
#define NS_ERROR_XPC_CANT_CONVERT_OBJECT_TO_ARRAY GENERATE_XPC_FAILURE(35)
#define NS_ERROR_XPC_NOT_ENOUGH_ELEMENTS_IN_ARRAY GENERATE_XPC_FAILURE(36)
#define NS_ERROR_XPC_CANT_GET_ARRAY_INFO GENERATE_XPC_FAILURE(37)
#define NS_ERROR_XPC_NOT_ENOUGH_CHARS_IN_STRING GENERATE_XPC_FAILURE(38)
// any new errors here should have an associated entry added in xpc.msg

Просмотреть файл

@ -72,8 +72,9 @@ XPC_MSG_DEF(NS_ERROR_XPC_JAVASCRIPT_ERROR , "JavaScript component
XPC_MSG_DEF(NS_ERROR_XPC_JAVASCRIPT_ERROR_WITH_DETAILS , "JavaScript component caused a JavaScript error (detailed report attached)")
XPC_MSG_DEF(NS_ERROR_XPC_CANT_CONVERT_PRIMITIVE_TO_ARRAY, "Can not convert primitive JavaScript value into an array")
XPC_MSG_DEF(NS_ERROR_XPC_CANT_CONVERT_OBJECT_TO_ARRAY , "Can not convert JavaScript object into an array")
XPC_MSG_DEF(NS_ERROR_XPC_NOT_ENOUGH_ELEMENTS_IN_ARRAY , "JavaScript Array does not have enough elements")
XPC_MSG_DEF(NS_ERROR_XPC_NOT_ENOUGH_ELEMENTS_IN_ARRAY , "JavaScript Array does not have as many elements as indicated by size argument")
XPC_MSG_DEF(NS_ERROR_XPC_CANT_GET_ARRAY_INFO , "Can not find array information")
XPC_MSG_DEF(NS_ERROR_XPC_NOT_ENOUGH_CHARS_IN_STRING , "JavaScript String does not have as many characters as indicated by size argument")
/* common global codes (from nsError.h) */

Просмотреть файл

@ -83,8 +83,8 @@ static uint8 xpc_reflectable_flags[XPC_FLAG_COUNT] = {
XPC_MK_FLAG( 0 , 1 , 0 , 1 ), /* T_INTERFACE */
XPC_MK_FLAG( 0 , 1 , 0 , 1 ), /* T_INTERFACE_IS */
XPC_MK_FLAG( 0 , 1 , 0 , 1 ), /* T_ARRAY */
XPC_MK_FLAG( 0 , 0 , 0 , 0 ), /* T_PSTRING_SIZE_IS */ //XXX disabled
XPC_MK_FLAG( 0 , 0 , 0 , 0 ) /* T_PWSTRING_SIZE_IS */ //XXX disabled
XPC_MK_FLAG( 0 , 1 , 0 , 1 ), /* T_PSTRING_SIZE_IS */
XPC_MK_FLAG( 0 , 1 , 0 , 1 ) /* T_PWSTRING_SIZE_IS */
};
/***********************************************************/
@ -764,9 +764,20 @@ XPCConvert::JSValToXPCException(JSContext* cx,
}
else
{
// It is a JSObject, but not a wrapped native.
// It is a JSObject, but not a wrapped native...
// If it is an engine Error with an error report then let's
// extract the report and build an xpcexception from that
const JSErrorReport* report;
if(nsnull != (report = JS_ErrorFromException(cx, s)))
{
const char* message = nsnull;
JSString* str;
if(nsnull != (str = JS_ValueToString(cx, s)))
message = JS_GetStringBytes(str);
return JSErrorToXPCException(cx, message, report);
}
// XXX should be able to extract info from 'regular' JS Exceptions
uintN ignored;
JSBool found;
@ -787,6 +798,12 @@ XPCConvert::JSValToXPCException(JSContext* cx,
}
}
// XXX we should do a check against 'js_ErrorClass' here and
// do the right thing - even though it has no JSErrorReport,
// The fact that it is a JSError exceptions means we can extract
// particular info and our 'result' should reflect that.
// otherwise we'll just try to convert it to a string
JSString* str = JS_ValueToString(cx, s);
@ -1042,8 +1059,11 @@ XPCConvert::JSArray2Native(JSContext* cx, void** d, jsval s,
{
NS_PRECONDITION(d, "bad param");
// No Action, FRee memory, RElease object
enum CleanupMode {na, fr, re};
CleanupMode cleanupMode;
JSObject* jsarray = nsnull;
void* array = nsnull;
JSUint32 initedCount;
jsval current;
@ -1054,6 +1074,18 @@ XPCConvert::JSArray2Native(JSContext* cx, void** d, jsval s,
if(JSVAL_IS_VOID(s) || JSVAL_IS_NULL(s))
{
if(0 != count)
{
if(pErr)
*pErr = NS_ERROR_XPC_NOT_ENOUGH_ELEMENTS_IN_ARRAY;
return JS_FALSE;
}
// If a non-zero capacity was indicated then we build an
// empty array rather than return nsnull.
if(0 != capacity)
goto fill_array;
*d = nsnull;
return JS_TRUE;
}
@ -1065,7 +1097,7 @@ XPCConvert::JSArray2Native(JSContext* cx, void** d, jsval s,
return JS_FALSE;
}
JSObject* jsarray = JSVAL_TO_OBJECT(s);
jsarray = JSVAL_TO_OBJECT(s);
if(!JS_IsArrayObject(cx, jsarray))
{
if(pErr)
@ -1107,6 +1139,7 @@ XPCConvert::JSArray2Native(JSContext* cx, void** d, jsval s,
// XXX make extra space at end of char* and wchar* and null termintate
fill_array:
switch(type.TagPart())
{
case nsXPTType::T_I8 : POPULATE(na, int8); break;
@ -1167,3 +1200,220 @@ failure:
#undef POPULATE
}
// static
JSBool
XPCConvert::NativeStringWithSize2JS(JSContext* cx,
jsval* d, const void* s,
const nsXPTType& type,
JSUint32 count,
nsresult* pErr)
{
NS_PRECONDITION(s, "bad param");
NS_PRECONDITION(d, "bad param");
if(pErr)
*pErr = NS_ERROR_XPC_BAD_CONVERT_NATIVE;
if(!type.IsPointer())
{
XPC_LOG_ERROR(("XPCConvert::NativeStringWithSize2JS : unsupported type"));
return JS_FALSE;
}
switch(type.TagPart())
{
case nsXPTType::T_PSTRING_SIZE_IS:
{
char* p = *((char**)s);
if(!p)
break;
JSString* str;
if(!(str = JS_NewStringCopyN(cx, p, count)))
return JS_FALSE;
*d = STRING_TO_JSVAL(str);
break;
}
case nsXPTType::T_PWSTRING_SIZE_IS:
{
jschar* p = *((jschar**)s);
if(!p)
break;
JSString* str;
if(!(str = JS_NewUCStringCopyN(cx, p, count)))
return JS_FALSE;
*d = STRING_TO_JSVAL(str);
break;
}
default:
XPC_LOG_ERROR(("XPCConvert::NativeStringWithSize2JS : unsupported type"));
return JS_FALSE;
}
return JS_TRUE;
}
// static
JSBool
XPCConvert::JSStringWithSize2Native(JSContext* cx, void* d, jsval s,
JSUint32 count, JSUint32 capacity,
const nsXPTType& type,
JSBool useAllocator,
uintN* pErr)
{
NS_PRECONDITION(s, "bad param");
NS_PRECONDITION(d, "bad param");
JSUint32 len;
if(pErr)
*pErr = NS_ERROR_XPC_BAD_CONVERT_NATIVE;
if(capacity < count)
{
if(pErr)
*pErr = NS_ERROR_XPC_NOT_ENOUGH_CHARS_IN_STRING;
return JS_FALSE;
}
if(!type.IsPointer())
{
XPC_LOG_ERROR(("XPCConvert::JSStringWithSize2Native : unsupported type"));
return JS_FALSE;
}
switch(type.TagPart())
{
case nsXPTType::T_PSTRING_SIZE_IS:
{
char* bytes=nsnull;
JSString* str;
if(JSVAL_IS_VOID(s) || JSVAL_IS_NULL(s))
{
if(0 != count)
{
if(pErr)
*pErr = NS_ERROR_XPC_NOT_ENOUGH_CHARS_IN_STRING;
return JS_FALSE;
}
if(type.IsReference())
{
if(pErr)
*pErr = NS_ERROR_XPC_BAD_CONVERT_JS_NULL_REF;
return JS_FALSE;
}
if(useAllocator && 0 != capacity)
{
len = (capacity + 1) * sizeof(char);
if(!(*((void**)d) = nsAllocator::Alloc(len)))
return JS_FALSE;
return JS_TRUE;
}
// else ...
*((char**)d) = nsnull;
return JS_TRUE;
}
if(!(str = JS_ValueToString(cx, s))||
!(bytes = JS_GetStringBytes(str)))
{
return JS_FALSE;
}
len = JS_GetStringLength(str);
if(len > count)
{
if(pErr)
*pErr = NS_ERROR_XPC_NOT_ENOUGH_CHARS_IN_STRING;
return JS_FALSE;
}
if(len < capacity)
len = capacity;
if(useAllocator)
{
JSUint32 alloc_len = (len + 1) * sizeof(char);
if(!(*((void**)d) = nsAllocator::Alloc(alloc_len)))
{
return JS_FALSE;
}
memcpy(*((char**)d), bytes, count);
((char**)d)[count] = 0;
}
else
*((char**)d) = bytes;
return JS_TRUE;
}
case nsXPTType::T_PWSTRING_SIZE_IS:
{
jschar* chars=nsnull;
JSString* str;
if(JSVAL_IS_VOID(s) || JSVAL_IS_NULL(s))
{
if(0 != count)
{
if(pErr)
*pErr = NS_ERROR_XPC_NOT_ENOUGH_CHARS_IN_STRING;
return JS_FALSE;
}
if(type.IsReference())
{
if(pErr)
*pErr = NS_ERROR_XPC_BAD_CONVERT_JS_NULL_REF;
return JS_FALSE;
}
if(useAllocator && 0 != capacity)
{
len = (capacity + 1) * sizeof(jschar);
if(!(*((void**)d) = nsAllocator::Alloc(len)))
return JS_FALSE;
return JS_TRUE;
}
// else ...
*((jschar**)d) = nsnull;
return JS_TRUE;
}
if(!(str = JS_ValueToString(cx, s))||
!(chars = JS_GetStringChars(str)))
{
return JS_FALSE;
}
len = JS_GetStringLength(str);
if(len > count)
{
if(pErr)
*pErr = NS_ERROR_XPC_NOT_ENOUGH_CHARS_IN_STRING;
return JS_FALSE;
}
if(len < capacity)
len = capacity;
if(useAllocator)
{
JSUint32 alloc_len = (len + 1) * sizeof(jschar);
if(!(*((void**)d) = nsAllocator::Alloc(alloc_len)))
{
// XXX should report error
return JS_FALSE;
}
memcpy(*((jschar**)d), chars, alloc_len);
((jschar**)d)[count] = 0;
}
else
*((jschar**)d) = chars;
return JS_TRUE;
}
default:
XPC_LOG_ERROR(("XPCConvert::JSStringWithSize2Native : unsupported type"));
return JS_FALSE;
}
}

Просмотреть файл

@ -49,7 +49,7 @@ struct ResultMap {nsresult rv; const char* name; const char* format;} map[] = {
#define XPC_MSG_DEF(val, format) \
{(val), #val, format},
#include "xpc.msg"
#undef MSG_DEF
#undef XPC_MSG_DEF
{0,0,0} // sentinel to mark end of array
};

Просмотреть файл

@ -66,9 +66,9 @@ nsJSID::nsJSID()
nsJSID::~nsJSID()
{
if(mNumber && mNumber != gNoString)
delete [] mNumber;
PR_Free(mNumber);
if(mName && mName != gNoString)
delete [] mName;
PR_Free(mName);
}
void nsJSID::Reset()
@ -76,9 +76,9 @@ void nsJSID::Reset()
mID = GetInvalidIID();
if(mNumber && mNumber != gNoString)
delete [] mNumber;
PR_Free(mNumber);
if(mName && mName != gNoString)
delete [] mName;
PR_Free(mName);
mNumber = mName = nsnull;
}
@ -89,7 +89,7 @@ nsJSID::SetName(const char* name)
NS_ASSERTION(!mName || mName == gNoString ,"name already set");
NS_ASSERTION(name,"null name");
int len = strlen(name)+1;
mName = new char[len];
mName = (char*)PR_Malloc(len);
if(!mName)
return PR_FALSE;
memcpy(mName, name, len);
@ -1065,7 +1065,7 @@ xpc_NewIDObject(JSContext *cx, const nsID& aID)
if(idString)
{
nsJSID* iid = nsJSID::NewID(idString);
delete [] idString;
nsCRT::free(idString);
if(iid)
{
nsXPConnect* xpc = nsXPConnect::GetXPConnect();

Просмотреть файл

@ -67,6 +67,7 @@
#include "xpcexception.h"
#include "xpcjsid.h"
#include "prlong.h"
#include "prmem.h"
#include "nsIJSContextStack.h"
#include "prthread.h"
@ -801,6 +802,18 @@ public:
JSBool useAllocator, const nsID* iid,
uintN* pErr);
static JSBool NativeStringWithSize2JS(JSContext* cx,
jsval* d, const void* s,
const nsXPTType& type,
JSUint32 count,
nsresult* pErr);
static JSBool JSStringWithSize2Native(JSContext* cx, void* d, jsval s,
JSUint32 count, JSUint32 capacity,
const nsXPTType& type,
JSBool useAllocator,
uintN* pErr);
static nsIXPCException* JSValToXPCException(JSContext* cx,
jsval s,
const char* ifaceName,

Просмотреть файл

@ -519,8 +519,13 @@ nsXPCWrappedJSClass::CallMethod(nsXPCWrappedJS* wrapper, uint16 methodIndex,
const nsXPTParamInfo& param = info->GetParam(i);
const nsXPTType& type = param.GetType();
nsXPTType datum_type;
JSUint32 array_count;
PRBool isArray = type.IsArray();
jsval val;
PRBool isSizedString = isArray ?
JS_FALSE :
type.TagPart() == nsXPTType::T_PSTRING_SIZE_IS ||
type.TagPart() == nsXPTType::T_PWSTRING_SIZE_IS;
if(isArray)
{
@ -547,21 +552,30 @@ nsXPCWrappedJSClass::CallMethod(nsXPCWrappedJS* wrapper, uint16 methodIndex,
&iidIsOwned, &conditional_iid))
goto pre_call_clean_up;
if(isArray)
if(isArray || isSizedString)
{
JSUint32 array_count;
if(!GetArraySizeFromParam(cx, info, param, methodIndex,
i, GET_LENGTH, nativeParams,
&array_count))
goto pre_call_clean_up;
}
if(isArray)
{
if(!XPCConvert::NativeArray2JS(cx, &val, (const void**)&pv->val,
datum_type, conditional_iid,
array_count, nsnull))
goto pre_call_clean_up;
}
else if(isSizedString)
{
if(!XPCConvert::NativeStringWithSize2JS(cx, &val,
(const void*)&pv->val,
datum_type,
array_count, nsnull))
goto pre_call_clean_up;
}
else
{
if(!XPCConvert::NativeData2JS(cx, &val, &pv->val, type,
@ -830,9 +844,14 @@ pre_call_clean_up:
jsval val;
nsXPTCMiniVariant* pv;
nsXPTType datum_type;
PRBool isArray = type.IsArray();
JSBool useAllocator = JS_FALSE;
JSUint32 array_count;
PRBool isArray = type.IsArray();
PRBool isSizedString = isArray ?
JS_FALSE :
type.TagPart() == nsXPTType::T_PSTRING_SIZE_IS ||
type.TagPart() == nsXPTType::T_PWSTRING_SIZE_IS;
pv = (nsXPTCMiniVariant*) nativeParams[i].val.p;
if(param.IsRetval())
@ -863,19 +882,32 @@ pre_call_clean_up:
else if(type.IsPointer() && !param.IsShared())
useAllocator = JS_TRUE;
if(isArray)
if(isArray || isSizedString)
{
JSUint32 array_count;
if(!GetArraySizeFromParam(cx, info, param, methodIndex,
i, GET_LENGTH, nativeParams,
&array_count) ||
!XPCConvert::JSArray2Native(cx, (void**)&pv->val, val,
&array_count))
HANDLE_OUT_CONVERSION_FAILURE;
}
if(isArray)
{
if(!XPCConvert::JSArray2Native(cx, (void**)&pv->val, val,
array_count, array_count,
datum_type,
useAllocator, conditional_iid,
nsnull))
HANDLE_OUT_CONVERSION_FAILURE;
}
else if(isSizedString)
{
if(!XPCConvert::JSStringWithSize2Native(cx,
(void*)&pv->val, val,
array_count, array_count,
datum_type, useAllocator,
nsnull))
HANDLE_OUT_CONVERSION_FAILURE;
}
else
{
if(!XPCConvert::JSData2Native(cx, &pv->val, val, type,

Просмотреть файл

@ -648,6 +648,11 @@ nsXPCWrappedNativeClass::CallWrappedMethod(JSContext* cx,
JSBool useAllocator = JS_FALSE;
PRBool isArray = type.IsArray();
PRBool isSizedString = isArray ?
JS_FALSE :
type.TagPart() == nsXPTType::T_PSTRING_SIZE_IS ||
type.TagPart() == nsXPTType::T_PWSTRING_SIZE_IS;
nsXPTCVariant* dp = &dispatchParams[i];
dp->type = type;
@ -711,8 +716,13 @@ nsXPCWrappedNativeClass::CallWrappedMethod(JSContext* cx,
}
}
if(datum_type.IsInterfacePointer() &&
!GetInterfaceTypeFromParam(cx, info, desc, param, vtblIndex,
i, datum_type, dispatchParams,
&conditional_iid))
goto done;
if(isArray)
if(isArray || isSizedString)
{
if(!GetArraySizeFromParam(cx, info, desc, param, vtblIndex, i,
GET_SIZE, dispatchParams,
@ -722,32 +732,37 @@ nsXPCWrappedNativeClass::CallWrappedMethod(JSContext* cx,
&array_count))
goto done;
if(datum_type.IsInterfacePointer() &&
!GetInterfaceTypeFromParam(cx, info, desc, param, vtblIndex,
i, datum_type, dispatchParams,
&conditional_iid))
goto done;
if(!XPCConvert::JSArray2Native(cx, (void**)&dp->val, src,
array_count, array_capacity,
datum_type,
useAllocator, conditional_iid, &err))
if(isArray)
{
// XXX need exception scheme for arrays to indicate bad element
ThrowBadParamException(err, cx, desc, i);
goto done;
if(!XPCConvert::JSArray2Native(cx, (void**)&dp->val, src,
array_count, array_capacity,
datum_type,
useAllocator,
conditional_iid, &err))
{
// XXX need exception scheme for arrays to indicate bad element
ThrowBadParamException(err, cx, desc, i);
goto done;
}
}
else // if(isSizedString)
{
if(!XPCConvert::JSStringWithSize2Native(cx, (void*)&dp->val,
src,
array_count, array_capacity,
datum_type, useAllocator,
&err))
{
ThrowBadParamException(err, cx, desc, i);
goto done;
}
}
}
else
{
if(datum_type.IsInterfacePointer() &&
!GetInterfaceTypeFromParam(cx, info, desc, param, vtblIndex,
i, datum_type, dispatchParams,
&conditional_iid))
goto done;
if(!XPCConvert::JSData2Native(cx, &dp->val, src, type,
useAllocator, conditional_iid, &err))
useAllocator, conditional_iid,
&err))
{
ThrowBadParamException(err, cx, desc, i);
goto done;
@ -785,6 +800,10 @@ nsXPCWrappedNativeClass::CallWrappedMethod(JSContext* cx,
JSUint32 array_count;
nsXPTType datum_type;
PRBool isArray = type.IsArray();
PRBool isSizedString = isArray ?
JS_FALSE :
type.TagPart() == nsXPTType::T_PSTRING_SIZE_IS ||
type.TagPart() == nsXPTType::T_PWSTRING_SIZE_IS;
if(isArray)
{
@ -798,7 +817,7 @@ nsXPCWrappedNativeClass::CallWrappedMethod(JSContext* cx,
else
datum_type = type;
if(isArray)
if(isArray || isSizedString)
{
if(!GetArraySizeFromParam(cx, info, desc, param, vtblIndex, i,
GET_LENGTH, dispatchParams,
@ -823,6 +842,17 @@ nsXPCWrappedNativeClass::CallWrappedMethod(JSContext* cx,
goto done;
}
}
else if (isSizedString)
{
if(!XPCConvert::NativeStringWithSize2JS(cx, &v,
(const void*)&dp->val,
datum_type,
array_count, &err))
{
ThrowBadParamException(err, cx, desc, i);
goto done;
}
}
else
{
if(!XPCConvert::NativeData2JS(cx, &v, &dp->val, datum_type,

Просмотреть файл

@ -251,6 +251,39 @@ xpcarraytest::ReverseStringArray(PRUint32 count, char ***valueArray)
return NS_OK;
}
/* void PrintStringWithSize (in PRUint32 count, [size_is (count)] in string str); */
NS_IMETHODIMP
xpcarraytest::PrintStringWithSize(PRUint32 count, const char *str)
{
if(mReceiver)
return mReceiver->PrintStringWithSize(count, str);
printf("\"%s\" : %d\n", str, count);
return NS_OK;
}
/* void DoubleString (inout PRUint32 count, [size_is (count)] inout string str); */
NS_IMETHODIMP
xpcarraytest::DoubleString(PRUint32 *count, char **str)
{
NS_ENSURE_ARG_POINTER(str);
if(mReceiver)
return mReceiver->DoubleString(count, str);
if(!count || !*count)
return NS_OK;
char* out = (char*) nsAllocator::Alloc(((*count * 2)+1) * sizeof(char));
if(!out)
return NS_ERROR_OUT_OF_MEMORY;
for(PRUint32 k = 0; k < *count; k++)
out[k*2] = out[(k*2)+1] = (*str)[k];
out[k*2] = '\0';
nsAllocator::Free(*str);
*str = out;
*count = *count * 2;
return NS_OK;
}
/***************************************************************************/
// static

Просмотреть файл

@ -130,6 +130,18 @@ var jsobj = {
result.value = [this, this];
count.value = 2;
},
PrintStringWithSize : function(len, a) {
print("\""+a+"\""+" ; "+len);
},
DoubleString : function(len, s) {
var out = "";
for(var k = 0; k < s.value.length; k++) {
out += s.value.charAt(k);
out += s.value.charAt(k);
}
len.value *= 2;
s.value = out;
}
};
@ -201,3 +213,26 @@ for(i = 0; i < count.value; i++)
ifaces.value[i].PrintIntegerArray(a.length, a);
print("-------------------------------------------");
print("-------------------------------------------");
print("-------------------------------------------");
obj.SetReceiver(null);
str = "This is a string";
obj.PrintStringWithSize(str.length, str);
obj.SetReceiver(jsobj);
obj.PrintStringWithSize(str.length, str);
var str2 = {value : "double me baby"};
var len2 = {value : str2.value.length};
obj.SetReceiver(null);
print("\""+str2.value+"\""+" : "+len2.value);
obj.DoubleString(len2, str2);
print("\""+str2.value+"\""+" : "+len2.value);
var str2 = {value : "double me baby"};
var len2 = {value : str2.value.length};
obj.SetReceiver(jsobj);
print("\""+str2.value+"\""+" ; "+len2.value);
obj.DoubleString(len2, str2);
print("\""+str2.value+"\""+" ; "+len2.value);