How to preserve the state of JavaScript closure? - javascript

I am working on a migration platform to migrate web applications from a device to another. I am extending it to add the support for preserving JavaScript state.
My main task is to create a file representing the current state of the executing application, to transmit it to another device and to reload the state in the destination device.
The basic solution I adopted is to navigate the window object and to save all its descendant properties using JSON as base format for exportation and extending it to implement some features:
preserving object reference, even if cyclic (dojox.json.ref library)
support for timers
Date
non-numericproperties of arrays
reference to DOM elements
The most important task I need to solve now is exportation of closures. At this moment I didn't know how to implement this feature.
I read about the internal EcmaScript property [[scope]] containing the scope chain of a function, a list-like object composed by all the nested activation context of the function. Unfortunately it is not accessible by JavaScript.
Anyone know if there is a way to directly access the [[scope]] property? Or another way to preserve the state of a closure?

This sounds like an impossible feat as you would need access to the references stored in each variable.
The best solution would probably be to first refactor your code into storing state on an available object - that way you could easily use JSON.stringify/parse to save/restore it.
So go from
var myFuncWithScope = (function() {
var variable = 0;
return function() {
return variable++;
}
})();
var serializedState = .... // no can do
to
var state = {
myScope = {
variable: 0
}
};
var myFuncWithoutScope = function(){
return state.myScope.variable++;
}
var serializedState = JSON.stringify(state);

From where are you executing? If you are a native app or web browser extension you may have some hope, via internal access to whichever scripting engine it's using. But from a script in web content, there is no hope.
[[Scope]] is one ECMAScript internal property that you cannot access or preserve from inside the interpreter, but far from the only one; almost all of the [[...]] properties are not accessible. Function code references, prototypes, properties, enumerability, owner context, listeners, everything to do with host objects (such as DOM nodes)... there are infinitely many ways to fail.
You can't preserve or migrate web applications without requiring them to follow some strict rules to avoid all but the most basic JS features.

Related

How can I do some clean up jobs when an object is being deleted / disposed in duktape?

I have a self-defined class that calls a native method to allocate buffer in the constructor method, like below:
MyClass = function () {
this.buffer = native.alloc()
}
The buffer has to be released when the instance of MyClass is being deleted.
Can I define a destructor in javascript like below? Would it be invoked when the GC happens?
MyClass.prototype.destructor = function () {
native.free(this.buffer)
}
If your question is whether there are native methods to do what you described, then the answer is -no. There are no such methods.
If your question - is whether it is possible to do it in any way, then the answer is - yes it is possible.
If you control the entire code base of the project, then no one bothers you to come up with an architecture that would allow you to delete objects using only your API. As a result, if this is your API then you can do whatever you want.
If you do not control the code, then there are several workarounds how to control the work with objects in JavaScript.
The first thing to try is a proxy. This api allows you to control all manipulations with any object, provided that you can replace the original link to the object with your link with a proxy.
The second thing to try is using Object.defineProperty. If the owner of the object did not specifically bother to close access to this API, then in fact you can hang on someone else's object any volume of your code that will allow you to control its behavior as you want.
As a result, you will be able to implement the functionality you are asking about.

How to detect if an object has been garbage collected in Javascript?

I am building a JavaScript game that creates a Level object using var:
function start() {
var myGameLevel = new Level(2);
}
This Level object has a lot of functionality, primarily adding elements to the DOM and making them interactive. A simplification:
function Level(i) {
var _difficulty = i;
this.init = function(){
jQuery("#container").append(...game elements here...);
jQuery("#button").on('click', function() {...});
}
}
My question: How can I know if the Level object created in the start function has been garbage collected or not? I aim to use only var variables so that there are no external references. When the DOM is cleared of all game elements, I EXPECT the Level object to be released from memory, but how can I be sure?
Normally in JavaScript garbage collection is non deterministic. You cant know if or when an object is garbage collected. This applies to objects that are strong referenced.
In ES12 and after, you can use a FinalizationRegistry.
Finalizers lets you handle when an object is garbage collected, using a JavaScript callback. Still the limitation is that, when the callback will get executed is non deterministic. It may take a min or an hour.
// object creation
let abc = new Array(200).fill(true);
const cleanup = new FinalizationRegistry(key => {
// your code here
});
// tagging variable abc to finalizer
cleanup.register(abc, 'werwer');
// abc = null;
Weak references are considered a security risk, and thus not available to unprivileged code in browsers.
Those concerns don't apply to privileged code or serverside javascript execution, e.g. via node.js, and thus platform-specific weak reference implementations may be available to them.E.g. firefox addons can use Components.utils.getWeakReference()
For certain programming patterns WeakMap/WeakSet may be sufficient, but they do not allow the program to observe garbage collection because to do so it would need a key to probe those data structures, but holding onto that key would prevent the objects from being collected in the first place.
An additional concern voiced by JS implementors is that depending on how powerful a hypothetical weak ref APIs would be - e.g. offering finalization notifications - it could expose considerable amounts of GC behavior which in turn could constrain future implementations because changing behavior might break web applications.
Update: There now is a proposal to standardize weak references in JS that mitigates the perceived risks by tying the release of weakly reachable objects to the JS event loop, making the behavior more deterministic.
Update 2: The proposal has been implemented in various browsers as WeakRef
I dont think you can control JavaScript garbage collection.
Normally an variable or object can be collected if there are no references to it. So you can increase your chances of having an object collected by designing your logic to make the object go out of scope.

How does browser's javascript engine interpret location?

I am trying to mock a very simple browser environment that mimics how browsers react to user's change in location variable. As far as I know, users can alter
self.location
location
document.location
window.location
one of those to navigate the current window by either
assigning a string of url directly onto the variable (e.g. self.location = 'http://www.google.com')
or assigning a string onto href inside the location object (e.g. self.location.href = 'http://www.google.com')
or maybe explicitly instantiating a location object.
So my real question is how can I instantiate the browser environment to allow users to assign location variable is anyway they wish and let me retrieve the location variable later on? Sorry if this sounds stupid, (I've never coded in javascript before) but in real world circumstances, do browsers convert string data into location objects through macro or does Javascript have some sort of "implicit constructor" mechanism that can automatically invoke the constructor of a class just by assigning a value?
(I am aware that there are dom libraries available, but I find them quite an overkill as I am only interested in the navigating mechanism.)
Well, there are actually several different answers, here.
First, don't anticipate being on the same page, when retrieving the location.
If a user changed it's value, then your page will change (and your in-memory application with it), so you'd need to do state management using some form of storage (online/offline) there.
In terms of how the object actually works, that isn't exactly JS (in all cases).
Long story short, there's JavaScript the language, and then there's the DOM/BOM.
Document-Object Model and Browser-Object Model.
The DOM is a set of functions/objects that let you interface with the HTML/CSS(as it applies to an element) of the page.
The BOM contains things that relate, not directly to the HTML, but to other parts of web functionality.
console.log( ); is a good example.
JS has no native console object or Console constructor. That's a part of the BOM that's added to a browser's environment, by the browser vendor (or third-party plugin), and is implementation a specific, with no real standard.
Same thing is true for URLs.
JS has a global object (in the BOM, it's called window), but it doesn't have a location natively.
So the implementation -- the "how", is hard to answer.
Some browsers might do it in legitimate JS, while others do it in C++ or C, and old IE even had ActiveX components.
That said, new JS engines do have magic get / set methods which can perform actions, while you set data.
Cutting-edge JS even has Proxies, which are sort of like that, but on steroids (these won't be widely supported everywhere for a few years)...
But older JS engines didn't have those features in place in the native language, so they went off into some other language and wired things up to behave in ways that wouldn't have been supported in JS itself, but were needed to fill out the BOM/DOM.
these days, using a .set might be all you need to grab an instance of a constructor.
So in terms of setting up your own object with similar functionality, you could RegEx parse (or iterate pieces of) the value handed to you.
On your object, you could have magic get/set methods assigned to a property name, where you could then, on a set, either modify all aspects of your current instance (based on value), or save a new instance to the var currently occupied by the old one, passing the new value to the constructor.
...but "where's the Location" constructor is a question that's not going to be answered in any simple way, without going browser by browser, version by version.
In a browser, window is effectively an alias for the global object so self === window.self and:
self.location
location
window.location
are all equivalent.
The location object is documented at MDN: window.location and at MSDN: The Window’s Location Object.
Users can set a location by entering it in the browser's address bar. You might also provide say an input element that they can type into, then a button that assigns the value to window.location (or uses any of the methods in the references provided).
do browsers convert string data into location objects through macro or does Javascript have some sort of "implicit constructor" mechanism that can automatically invoke the constructor of a class just by assigning a value?
The window and location objects are host objects that are not required to have any constructor, they just "are". Even if they are implemented as instances with prototype inheritance, there's no specification requiring users to have access to the constructor.
A new window can be created using:
var newWindow = window.open(...);
which I suppose is like calling a constructor.
When a location object is assigned a new URL, it behaves per the documentation referenced. How that happens is an implementation detail that is not specified, so you can implement it any way you like as long as it behaves like it should. That is the general philosophy behind javascript.
What you're looking for here is a getter/setter pair.
var window = {
get location() {
return {
get href() { return "foo"; },
set href() { /* change stored location data */; },
get port() { return 80; },
set port() { /* change stored location data */; },
/* etc ... */
};
}
set location() {
// change stored location data
}
};

extracting all used javascript variables

I have a big object defined in the global scope called global. I would like to dynamically find all the referenced properties under my variable global. That is, all the properties that were accessed during the execution of the code.
I want to do static code analysis to extract all the referenced properties under my variable. I can search for these patterns: global.PROPERTY_NAME AND global[PROPERTY_NAM]. However, what about the complicated cases like these ones
var tmp="PROPERTY_NAME";
global[tmp]
OR
var tmp=global;
tmp.PROPERTY_NAME
and the other ones?
I don't want to get all the variable's properties. I only want a list of the referenced ONES!! the properties that were referenced in my source code only
After your edit:
What you're looking for is JavaScript Proxy objects. Here is a tutorial on how to do this using them.
Proxy objects let you wrap an object and execute a method whenever its properties are accessed. Unfortunately as it currently stands they are not widely supported.
This is currently only way in JavaScript to accomplish this without changing your original global object.
You can turn them on in Chrome by enabling experimental JavaScript in the about:flags tab.
Before your edit:
The feature you're looking for is called reflection, JavaScript supports it well and natively
Here is some code that iterates through an object and gets its properties
for(var prop in global){
if(global.hasOwnProperty(prop)){ //this is to only get its properties and not its prototype's
alert(prop+" => "+global[prop]);
}
}
This is fairly cross-browser. More modern browsers allow you to do this in simpler ways like Object.keys(global) which returns an array containing all its enumerable properties, or Object.getOwnPropertyNames(global) which returns both enumerable and not-enumerable properties.
Due to the dynamic nature of JavaScript you won't achieve that with static code analysis. Think about cases like this:
var prop = document.getElementById('prop').value;
global[prop];
Impossible. The alternative, dynamic analysis, would mean that you modify your global object to log access to its properties, then run the code. This is easily possible in JavaScript but it won't help you either because how would you assure that you have covered every possible access? Especially in a 5 MB JavaScript, there are most likely edge cases that you will oversee.
So, if you can't narrow down your requirement, it won't be possible.

Passing objects to a web worker

I'm trying to pass an object to a web worker through the postMessage function.
This object is a square that has a couple of functions to draw himself on a canvas and some other things. The web worker must return an array of this objects.
The problem is that when I call the postMessage function with this object, I get an this error:
Uncaught Error: DATA_CLONE_ERR: DOM Exception 25
I get this both sending the object to the worker and the other way around.
I think the error is because javascript must serialize the object, but can't do it because the object has functions built-in.
Does anyone ever had a similar problem? Do you know some workarround to this?
Thanks in advance.
There are a few reasons why the error that you mention could have been thrown, the reasons are listed here.
When sending objects to web workers, the object is serialized, and later deserialized in the web worker if the object is a serializable object.
This means that the methods for the objects you send to your web worker are not something that can be passed to the web worker (causing the error that you have run into), and you will need to provide the necessary methods/functions to the objects on the web worker's side of the environment, and make sure they are not part of the object that is passed to the web worker(s).
As you suspected objects with functions cannot be posted. The same goes for objects with recursive references, but this has changed in some browsers lately. Instead of risking doing manual and costly redundant serialization for every post you can perform a test at the beginning of your script to determine which functions to use for sending/receiving data.
I've had the same problem and solved it by moving almost all code into the worker and just keeping a renderer (wrapping the 2d context renderer) in the main thread. In the worker I serialize the different draw calls meant for the canvas into just numbers in an (typed) array. This array is then posted to the main thread.
So for instance when I want to draw an image I invoke the drawImage() method on my worker renderer instance in the worker. The call is translated into something like [13,1,50,40] which corresponds to the draw method enum, image unique id and its xy coordinates. Multiple calls are buffered and put in the same array. At the end of the update loop the array is posted to the main thread. The receiving main renderer instance parses the array and perform the appropriate draw calls.
I recently encountered this same problem when using web workers. Anything I passed to my worker kept all its properties but mysteriously lost all its methods.
You will have to define the methods in the web worker script itself. One workaround is to importScripts the class definition and manually set the __proto__ property of anything you receive. In my case I wanted to pass a grid object, defined in grid.js (yup, I was working on 2048), and did it like so:
importScripts('grid.js')
onMessage = function(e) {
e.data.grid.__proto__ = Grid.prototype;
...
}
When you pass data to a web worker, a copy of the data is made with the structured clone algorithm. It is specified in HTML5 (see § 2.9: Safe passing of structured data).
MDN has an overview of supported types. As functions are not supported, trying to clone objects containing functions will therefore throw a DATA_CLONE_ERR exception.
What to do if you have an object with functions?
If the functions are not relevant, try to create a new object that contains only the data that you want to transfer. As long as you use only supported types, send should work. Using JSON.stringify and JSON.parse can also be used as a workaround, as stringify ignores functions.
If the functions are relevant, there is no portable way. There are attempts to use a combination of toString and eval (e.g., used by the jsonfs library), but this will not work in all cases. For instances, it will break if your function is native code. Also closures are problematic.
The real problem with object and webworkers is with the methods of that objects. A object should not have methods just properties.
Ex:
var myClass = function(){
this.a = 5;
this.myMethod = function(){}
}
var notParseableObject = new myClass();
var myClass2 = function(){
this.a = 5;
}
var parseableObject = new myClass2();
The first wont work (with the mentioned error message) with postMessage and the second will work.
Some type of objects like ArrayBuffer and ImageBitmap which have the Transferable interface implementet and can be transfered without copy the Object.
Thats very usefull in Context of Canvas + Web worker cause you can save the time of copy the data between the threads.
take a look at the vkThread plugin
http://www.eslinstructor.net/vkthread/
it can pass function to a worker, including function with context ( object's method ). It can also pass functions with dependencies, anonymous functions and lambdas.
--Vadim
Another way of handling this (as I come across this question a decade later having needed to do it myself) is to define a static clone() function on your class that constructs a new object from (the properties of) an old one; then you can simply say
MyClass cloneObj = MyClass.clone(evt.data.myObj);
at the start of your worker to get a 'real' object of type MyClass that you can then call methods on from within your worker.
if you want to pass the object with methods you can stringify it and parse it at the receiving end.
postMessage(JSON.stringify(yourObject)
In the listener
this.worker.addEventListener('message', (event) => {
const currentChunk = JSON.parse(event.data);
});

Categories