Can Javascript objects have an "on garbage collect" callback? - javascript

I'm writing an Android app which uses Javascript in a WebView for most of the UI. I've exposed to JS a simple interface to create temporary files. In Java, I provide a function to create a temporary file, which returns a file ID, and a set of functions to operate on temporary files given an ID. In JS, I wrap these functions with a TempFile object which keeps track of its own ID internally.
The problem with this is the Java side has a list of files, but knows nothing about the objects that represent them in Javascript. So when a TempFile is collected in JS, the file object is still around on the Java side. Is there an "oncollect" or such callback that I can use to tell Java to free the file corresponding to a TempFile that's been collected?

Look at Finalizer
Since ES12 you can find FinalizationRegistry. Use it to tell Java to free the file corresponding to a TempFile that's been collected.
const registry = new FinalizationRegistry((fileName) => {
console.log(fileName);
// ... here your function to comunicate with Java
});
let tempFileObject;
registry.register(tempFileObject, filename);
Here we create an instance of FinalizationRegistry and attached the js 'tempFile' to it using .register. Soon as the 'tempFile' is garbage collected, the callback function passed to the FinalizationRegistry method communicate with java.
Possible duplicate
How to implement function that fires when JavaScript object is garbage collected?

I'm fairly sure the answer is "no" - you can't hook into the JS garbage collection process in the same way that you can with Java's finalizers (or ReferenceQueues).
Can you instead deal with this more declaratively? Your question seems to imply that the only way your system "knows" when a file is no longer needed, is when it is GCed (or in other words, your system doesn't know at all). Are you really referencing these files all over the application? Good practice would be for these resources to be used with a well-defined lifecycle, such that the part of the system that creates a temp file is also responsible for destroying it after it's finished with it.
Under that model, when you (explicitly) destroy the file in JS, you can call back to Java to perform the required clean up.
Releasing a resource by simply removing all references to it, as you're doing at the moment, is not particularly good practice.

No. You will need to call a Java function from your JavaScript when you don't need the files anymore.

Javascript does not have such hook.
You need to explicitly create close() function or such method for this use case and call it.
Some more info
http://forums.mozillazine.org/viewtopic.php?f=19&p=5517055

Related

Possible to serialize node.js vm.script?

I'm experimenting with ways to pre-compile a large number of JS functions using node.js vm.script, so that I can call the functions multiple times without the overhead of recompiling each time. Note that I can't just include the functions in a module, because they are architecturally completely separate from the "core" code. That works fine--each compiled script is stored in an object and can be run as-needed.
What I'm trying to figure out is how to use this same model with multiple fork'ed child processes, so any child process can be used to run the compiled script. The question is how to serialize it, so I can pass it to the child. And more importantly, is there anything inherent in the script variable that would "tie" it to the process it was created in?
I'm afraid is not possible. The first hint is that the Script object doesn't have any method to retrieve the compiled code. Looking further inside the source code of node here and here, it seems that the compiled object is stored inside a native C object. Therefore there's no means to serialize it from JS.

Is just calling deleteLater() enough to avoid memory leaks in Javascript with Embind?

I'm a longtime Java/C++ programmer and novice Javascript programmer. I'm trying to make a web app with a class I have previously coded in C++.
In my Javascript web app, I'm using Embind to create and use the class originally coded in C++. On the Embind documentation page it says,
JavaScript code must explicitly delete any C++ object handles it has received, or the Emscripten heap will grow indefinitely.
and the examples on the page show the created object being deleted immediately after use:
var x = new Module.MyClass;
x.method();
x.delete();
In my web app, I want my object from C++ to persist for the lifetime of the webpage. I want to be able to press a button on the page and update the state of my object. If I .delete() the object at the end of the script, it won't persist when I try pushing the button later.
In the Embind example, embind.test.js, it is possible to call .deleteLater() on a newly created object:
var v = (new cm.ValHolder({})).deleteLater();
My question is, if I simply call .deleteLater() upon the object creation, is this enough for the object to be deleted when the app is done running or when the page is closed? I'm trying to avoid growing the heap indefinitely or cause any memory leaks.
Again, I'm new to Javascript so please point out if I'm missing anything obvious or if ignorant of a best practice concerning memory leaks and pointers in Javascript.
Let me know if I need to clarify anything. Thanks!
reference: https://kripken.github.io/emscripten-site/docs/porting/connecting_cpp_and_javascript/embind.html#memory-management
I'm on the same path and really don't have a concrete answer for you, but as a JS developer I can collaborate with this:
Calling delete() on an already deleted object throws an ugly exception (uncatchable from JS). There is an undocumented method to check this: obj.isDeleted(). Also obj.SS.count will be 0 when the object is "deleted"
In the browser not deleting objects could for sure break your application but from the point of view of a C++ developer this only happens in the context of the document - so by just reloading the page - you get all the memory back (is not necessary to kill the browser)
when exceptions are thrown from C++ code , like sigint, bad memory allocation, etc, it seems is not possible to catch them, even using DISABLE_EXCEPTION_CATCHING or the rest of the debug flags. Module.onAbort or similar also won't handle them. So If anybody will handle deleteLater() registered objects when the program throws must be at C++ side.
I see there's no documentation about delete(), isDeleted() deleteLater() I think those would be great candidates for a PR.

"Some JS files act more like libraries - they [...] never manipulate QML component instances directly" - middle ground?

Quote from Defining JavaScript Resources In QML:
Some JavaScript files act more like libraries - they provide a set of helper functions that take input and compute output, but never manipulate QML component instances directly.
What if I want a JS file that both:
manipulates QML component instances (that are passed to it as arguments)
doesn't get a copy of its code and data stored for every QML component instance that imports it?
I could get the "no data stored on every instance" part by, well, not putting global variables in the JS file. But, for some strange reason, a copy of the "code" part seems to be stored for every instance as well. I don't get why this is but I want to know whether I can circumvent it, and at what cost.
I think that the line you quoted from the documentation is incorrect, or at least very poorly worded; you can still have a JS file with .pragma library in it and manipulate QML objects that are passed in as arguments to its functions. The sentence was probably referring to the previous section.
To share data across qml files, consider using a qml Singleton.
For data sharing purpose, I would not suggest using .pragma library (#Mitch) for following reasons.
.pragma library js provides limited functionality in qml object manipulation. While simple qml object manipulation (like property reading/writing) could be done with a .pragma library js, it does NOT allow creating/deleting qml objects (as you can in regular non-library js). It will suck when your application becomes dynamic.
.pragma library creating only one instance is merely an optimization in Qt implementation. It's never guaranteed that Qt creates exactly one instance, nor that your data would actually be shared.
Well, .pragma library is not designed to do data sharing work from the very beginning. Just, don't try to do it this way.

Protecting a JavaScript object against external scripts

Web App Model
Suppose I have a sensitive JS object by which I can do critical stuff. My requirement is that I would like to wrap this object entirely such that no one can access it. Here is my pattern to wrap this object.
var proxy = (function (window){
// A private reference to my critical object (i.e. big apple)
var bigApple = window.bigApple;
// Delete this property so that no one else can access it
delete window.bigApple;
// Oooah, It's mine! I'm now eating it :)
// Public APIs exposed globally
return {
doStuffWithBigApple: function (){
// The Script element being executed now
var who = document.currentScript;
// Access control
if(isLegitimate(who)){
return bigApple.doStuff();
}
}
};
}) (window);
By this code I export a public literal object named proxy so that every one can access it.
What is that isLegitimate? It is an abstract function to be implemented which decides which script elements access to which methods of my big apple. The decision is made with regard to src attribute of the script element. (i.e. their domain)
Others use this public API like this:
proxy.doStuffWithBigApple();
Attack Model
In my web app there are placeholders for advertising such that external contents including JavaScript codes could be loaded and get executed. All of these external resources eagerly would want to access my big apple.
Note: Those are added after my scripts resulting in there is no access to the original window.bigApple.
My Question
Is there any circumventing way for my security model?
Critical edges:
Changing src attribute at parse-time. --- Not possible, because src can only be set once.
Adding script element at run-time --- No problem is raised
Your idea of creating a proxy is good imo, however, if you have access to ES6, why not looking into Proxy? I think it does what you want out-of-the-box.
The MDN provides good examples on how do traps for value validation in a setter, etc.
EDIT :
Possible trap I have imagined :
document.currentScript is not supported in IE. So if you care about it and decide to polyfill it/use a pre-exisiting polyfill, make sure it is secure. Or it could be used to modify on the fly the external script url returned by document.currentScript and skew the proxy. I don't know if this could happen in real life tho.
This way for protecting JavaScript objects has a very significant issue which should be addressed, otherwise this way will not work properly.
MDN noted on this API that:
It's important to note that this will not reference the <script> element if the code in the script is being called as a callback or event handler; it will only reference the element while it's initially being processed.
Thus, any call to proxy.doStuffWithBigApple(); inside callbacks and event handlers might lead to misbehaving of your framework.

C++ Object & Javascript Object life cycle mis-synchronization

I got a question regarding C++ Object & Javascript Object life cycle mis-synchronization and hope here your gurus could help me out.
Specifically, I am embedding SpiderMonkey in my C++ program. I am not sure if my approach is right or not. What I am doing is like
(1) C++ program loads a Javascript file and call an entry function.
(2) In the entry function, some C++ functions are called to initialize the program. During these calls, some C++ objects will be created.
(3) Based on user interaction, corresponding script function will be called as event handler.
This approach works, but with one problem (there could be unidentified more :)
That is,
In my JS event handler function, I need to fetch a C++ object to the Javascript context and call it member function. For this, in my C++ fetching function, I check if its corresponding JS peer object has been created or not. If not, I use JS_NewObject to create one and return it. Then the JS function could call native function on it.
The problem is that in some cases, the result of such native function call will lead the death of the C++ object. However, I can not find a way to notify the JS context to delete its JS peer object too. I failed to find a JSAPI function to do so.
In my current program, all the JS objects created using JS_NewObject are destroyed when finally the JS runtime is destroyed.
I guess this has something do with SipderMonkey's "garbage collection". But I have not found a good guide yet. Many thanks for any suggestionto
JS is a GC'd environment so you can't simply "delete" a GC allocated object. There are basically 2 options you can take:
Make your C++ object be dependent on the JS wrapper object, if you were using refcounting for instance you would increment the C++ object's ref when you created a wrapper, and decrement the ref in the wrapper objects finalizer.
When you destroy the C++ object, fetch the wrapper object (if it exists) as clear the reference to the C++ object. All your callbacks will now need to null check prior to using the C++ object, but you won't crash (you could throw a JS exception in response perhaps?)
In most cases option 1 is what users expect.
I'd point to the required API but i don't know the SM API (I know the JSC API instead, but they same concepts apply)

Categories