1

I am using webassembly in order to do some calculation using a standard library. In webassembly we can pass only 32bit integers and 64 bits integers. That means that we can pass pointers to arrays as well. That's how we are able to return strings as an array of characters i.e. example

char* EMSCRIPTEN_KEEPALIVE returnStringCharacterArray()
{
  string stringToReturn = "I am learning web assembly";

   char* char_array = new char [stringToReturn.length()+1];
   strcpy (char_array, stringToReturn.c_str());

   char* arrayPtr = &char_array[0];
   // delete [] char_array;
   return arrayPtr;

}

Notice the operator "new" in that function. And notice how " delete [] char_array;" is never called there. Does that mean that if i forget to call a delete here there is memory leak here? I notice a very strange thing here. If i do call delete on the array then this example is still working!!! That means i am able to consume the string (in bought scenarios i.e. with delete and without) in java script like that:

 var ptr = Module._returnStringCharacterArray();
 var ptr = new Uint8Array(Module.HEAPU8.buffer, ptr, length);
 var theStringObj = new TextDecoder('utf8').decode(ptr);
 console.log(newstring)

What happens with "char_array" during the next function call if delete is call on it and if not? Why i am still able to consume the string even when " delete [] char_array;" was called?

The reason i am asking this is because i have very similar situation with a vector i.e. but instead of pointer of to chars i have pointer to uint8_t i.e.:

const vector<uint8_t>&  someString;

and when i try to send the pointer, poiting to the first value of the vector back to java script i.e. like this

const uint8_t* wasmVectorArrayRecostructedPtr = &someString[0];
uint8_t* nonConstBufferReconstructed = const_cast<uint8_t*>(wasmVectorArrayRecostructedPtr);
char* charArrayPtrCasted = (char*) nonConstBufferReconstructed;

then i get some random garbage instead of the string. At first I was thinking that this is because the vector is "automatically" cleaned since it is "living" on the stack, in contrary to the "char_array" which is "living" on the heap (free store). But that does not seems to be the case. What i am missing here.

How can i manually free the memory that was dynamically allocated here with the new operator after it has been consumed by javaScript .

Module._free(ptr); 

does not seems to be working. How can i make sure that the "char_array" object is cleaned and it's memory is freed after being consumed by JavaScript?

Tito
  • 2,234
  • 6
  • 31
  • 65
  • @NicolBolas sorry i posted the second method with delete i fixed that – Tito Jun 03 '20 at 18:01
  • I think i found the answer here https://stackoverflow.com/questions/51544240/how-can-i-free-memory-allocated-by-rust-code-exposed-in-webassembly i.e. WebAssembly does not offer any instructions to deallocate memory., does that mean that delete is not working in this example? – Tito Jun 03 '20 at 20:56

2 Answers2

1

WebAssembly memory can grow, but never shrink. delete allows memory to be overwritten, but it remains reserved for future use by your WebAssembly application.

when i try to send the pointer, poiting to the first value of the vector back to java script

then i get some random garbage instead of the string.

JavaScript doesn't understand the vector type.

Community
  • 1
  • 1
GirkovArpa
  • 4,427
  • 4
  • 14
  • 43
  • most likelly i did not express myself clearly when i mean send i pointer to javascript i mean that this pointer is used to create a Uint8Array which is holding the string , as shown in the example above in the quesiton. What works is if i copy the memroy from the vector to another "char* char_array " using the new operator and then send the pointer of that char_array. Thus my question was is the vector "garbage collected" in this case since it is living on the stack and was that the reason why direct pointer from vector is not producing the results i wanted? – Tito Jun 06 '20 at 08:08
  • Direct pointer from vector doesn't produce the results you wanted because there is no such thing as a vector in JavaScript. Not because the vector is "garbage collected". Typed Array pointers work because they exist in JavaScript. You would need a JavaScript implementation of vector that is compatible with your C++ vector implementation in order to do what you're trying to do. – GirkovArpa Jun 06 '20 at 22:16
  • thank you for the responce, I want to clear which pointer i am sending i.e. here is the code const vector& v; const uint8_t *PtrSentToJavaScript = &v[0]; . Here i am not sending a pointer to the vector itself but a pointer to the first element in the vector that represent effectivelly the file/blog/string that i want to read in JavaScript – Tito Jun 11 '20 at 08:26
  • You cannot reliably access vector elements by pointers in pure C++, much less in WebAssembly, because vectors invalidate such pointers as part of automatic memory management. – GirkovArpa Jun 11 '20 at 20:31
  • 1
    Another way of putting it is, it causes undefined behavior. – GirkovArpa Jun 11 '20 at 20:41
  • @GirkovArpha now that all makes sense, do you know such undefined behavior has been described in the official documentation? – Tito Jun 12 '20 at 07:11
  • 1
    "[Storing an iterator, reference, or pointer to an element within a container for any length of time comes with a risk that the underlying container may be modified such that the stored iterator, pointer, or reference becomes invalid. For instance, when a sequence container such as std::vector requires an underlying reallocation, outstanding iterators, pointers, and references will be invalidated](https://wiki.sei.cmu.edu/confluence/display/cplusplus/CTR51-CPP.+Use+valid+references%2C+pointers%2C+and+iterators+to+reference+elements+of+a+container)." – GirkovArpa Jun 12 '20 at 07:42
0

I think you should have another function from JavaScript to Wasm informing the wasm module it could free the memory backing the string, and inside that function calling delete to free the linear memory that would otherwise be held.

Something like:

void EMSCRIPTEN_KEEPALIVE freeMemory(char* ptr)
{
     delete [] ptr;
}

Otherwise you will keep instantiating a bunch of bytes at every call, leaking memory along the way.

This has nothing to do with the fact that Wasm memory is only growable. Memory leaks should still taken care of.