Skip to content

Instantly share code, notes, and snippets.

@Ayms
Created May 16, 2012 10:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Ayms/edd064e5b29e67ebe493 to your computer and use it in GitHub Desktop.
Save Ayms/edd064e5b29e67ebe493 to your computer and use it in GitHub Desktop.
Attempt for strict mode |with| statement redesign

|with|'s redesign attempt in strict mode which is supposed to address at least 4 subjects mentionned below and which is supposed to eliminate the non strict mode |with| issues.

The concern here is more to address the subject of binding variables to properties than using properties of an object passed as an argument to |with| (then it is somewhere the contrary of what is doing a usual |with|). Then the suggestion here is to pass nothing to |with| and implicitely declare in the with statement the |this| binding, with a different way of acting for |with| so it is not supposed to mix/interfer different scopes.

##Proposal

"use strict"

var a='a';

with () {

//with (a) { --> syntax error
	
	console.log(this); //undefined
	
	var window=new require('WINDOW')(); //load a WINDOW module
										//can be loaded outside but then will be accessible to other windows
	var b="b";
										
	this=window; 	//disruptive assignment
					//LHS lexical this
					//|this| is now window within the |with| statement
	
	console.log(window.window); //window - "var window" defined above gets bound to |this|, then to window, this defines (automatically) a window property for window
								//this is not uninteresting, if not you should not forget to do manually window.window=window
	
	console.log(window.b); //"b"
	
	var c="c";
	
	console.log(window.c); //"c"
	
	console.log(c); //"c"
	
	window.d="d";
	
	console.log(d); //"d"
	
	var code=module.load(web_api_code);

	eval(code); //strict mode eval, |this| is window
	
	//code : a='A' --> console.log(a); //A
	//code : d='d' --> syntax error, code must follow strict mode, small limitation...
	//code : var e='e' --> console.log(e or window.e) //e
	
	console.log(window.parseInt); 	//undefined
									//global object properties could be copied but do we really need it ?
	
	console.log(parseInt); //parseInt
	
	var f=function() {console.log(this)};
	
	setTimeout(f,1000);//window - assuming that setTimeout is here using a strict eval
	
	this=a; //syntax error, can not redefine |this|
}

##Rationale :

This is supposed to be related to at least four topics :

  • SES securization : |with| is used to separate initial context from new window context, if something was to be passed to |with| then it would be impossible to ensure separation of both contexts. You could then spend your time wondering in what context things get executed depending on where you define it, example (see Annex below too) :
var window=new WINDOW();

with(window) {

var a='a';

window.document.onload=function() {console.log(a)};//"a"

};

window.document.onload=function() {console.log(a)};//undefined

Of course depending on what developers are doing, both contexts (and mainly what is defined within the |with| statement) can communicate, if not what is defined within the |with| statement is not accessible. Depending on what needs to be done other security features can be added (freeze global properties, etc)

  • modules loader : unless I am wrong, I did not see in modules proposals something that can load just the code of a web API (so not a module designed to work as such) for example and that can execute in a way that it can interact with the code from where it is requested, as far as I saw and as far as it exists today, modules contexts are always separated with a possibility to import things but not to interact directly. Anyway, loading js and executing it is something that everybody know how to do

  • multiple globals : global properties are not assigned to the new virtual global (window), but again do we need it ? I am thinking since some time about what is written in the multiple globals strawman : "the presence of multiple global objects interacting with one another, but this has long been the reality on the web". Web projects defining multiple globals, OK, but interacting between each others I could not find any example up to now, except maybe the small and limited interaction between window and things such as iframe's windows. Anyway, the above proposal might fit, if you want your multiple window to interact between each other then give access to what you want of windows objects.

  • VMs : this could simplify the concepts used in VMs, in particular node.js's VM (which has some defaults like not binding things correctly in some situations)

##Annex :

Note : examples given below are simplified to try to get a better understanding.

One approach can be (node.js) :

var window=new WINDOW();

window=Object.createContext(window); //sandbox object

eval(code,window); //creates a new global, clone sandbox properties to it, execute code, clone global properties to sandbox

As mentionned above, this is a complete headheck where you spend your time wondering where things get executed, example :

window.setTimeout=setTimeout;

code='setTimeout(function(){console.log(this)},1000)';

eval(code,window); //window is expected but it does echo the global of initial context

In addition, contexts can not really communicate :

window.document.onload=function() {
	eval('var b=document.body',window);
	var web_stuff=require('console.log(b)'); //undefined
}

Another approach can be (cajaVM) :

var import=Object.clone(global); //clone global var

var freevar=extract_var_assignment_from_code=['a','b','c']

var scopedObject={
	a: {get:scopedGet, set:scopedSet},
    b: {get:scopedGet, set:scopedSet},
    c: {get:scopedGet, set:scopedSet}
}

Where scopedGet/Set gets/sets the (a or b or c) property of import

with(scopedObject) {
	eval(code,import)
}

This approach is not trivial to understand, you have to extract the free var and then set multiple getters/setters, so probably there is an impact on performances

And another one can be based on (subset of shadow which is intented for other use, but the example is interesting) :

var compiled=Function.apply(null,['document','screen',code]); //properties of the global object

//function anonymous(document, screen) {code}

var glob={document:value_of_document,screen:value_of_screen}; //the new global

compiled.apply(glob,[glob.document,glob.screen]); //execute code with |this| bound to glob and scope knowing glob's var and their values

Not easy too, requires manipulations, if code='var a="a"', a does not get bound to glob, would require freevar manipulations as above

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment