Crossing contexts/engines (module loader)

Jul 5, 2011 at 2:32 PM

I've been working on a module-loader which operates similar to CommonJs spec.

The way I've been accomplishing this is by creating a new JintEngine for each new module, returning the exports object back out as a JsObject and then giving that object to each module that asks for it.  This way I'm only loading and interpreting each module only once.  This has worked really well so far, but today I hit a bit of a snag.

I have a system-io module which (very roughly) contains the following:

console.log();
console.LogLevel; // (a number)
console.setLogLevel = function(level){
    console.LogLevel = level;
}

The problem I'm hitting is that if I call console.setLogLevel() from my main process (a JintEngine), then the assignment "console.LogLevel = level;", fails.  I've also tried having this function call out into my C#, then I go find the original module's JintEngine and make basically the same call in that context...this is failing as well.  I could probably make it happen by pushing more of the functionality out into the C#, but I'd rather not.  I'd really like for the modules to be coded in javascript as much as possible.

Am I missing something here that would allow me to do what I'm wanting?

Jul 6, 2011 at 5:28 AM

What exceptions are you getting? Any particular message strings in your exceptions?

Jul 6, 2011 at 12:19 PM
Edited Jul 6, 2011 at 12:31 PM

Sorry, I should have added that...I'll double-check, but I'm pretty sure it was not throwing any exceptions at all, it was just kind of disappearing.

Edit:  Yes, that's (sorta) correct.  I've got a try-catch block in the javascript itself and nothing is happening there.  I've also got a try-catch around my execution routine and what I'm seeing is that the debugger is stopping at the catch, but the exception variable itself is null.  I've never seen that happen before.

Jul 6, 2011 at 12:41 PM
Edited Jul 6, 2011 at 4:08 PM

Ok, gonna fess up...I've encountered a few other oddities while building this thing up, or maybe I've just missed some details.  I've been using some workarounds to deal with these oddities and as a consequence had to do a few things I'm not really proud of :) .  I'm going to go ahead and post the code, because I think it's possible they might be related.

Here's my execution routine.  I wanted to make a few things easier to use and I encountered a few strange things, see the comments for details, let me know if there's a better way (C#):

                public object Exec(string script){
			//NOTE:  running with .Run(string,false) and returning the dictionary object .Value, because
			//otherwise we are unable to cast some types of objects when they are returned from this function
			//one example is String[], such as used by Directory.GetFiles().  If we don't do this here, then
			//we are unable to get a reference to the actual array without doing this after we call this function.
			//So it's been put in here for convenience.
			object genericObj = null; 
			
			try{
				genericObj = myEntryPoint.Engine.Run(script,false);
			}
			
			catch(Exception ex){
				//It is bad form to swallow exceptions, but I'm going to do it anyway...
				//the reason this is needed (for now) is that for some reason I'm getting a null reference exception when I run
				//this against the console.log() that I created, which is a method that should return void.
				var whatIMightLog = ex.ToString();
			}
			
			object toReturn = null;
			
			Jint.Native.JsDictionaryObject returnVal = null; 
			
			if(genericObj != null){
			   	returnVal = (Jint.Native.JsDictionaryObject)genericObj;
				toReturn = returnVal.Value;
			}
			
			return toReturn;
		}

My console.log code consists of these pieces (C#):

Engine.SetFunction("__log", new Action<object>(Console.WriteLine));

and in javascript:

var console = {};

console.__log = function(args){ var toCall = '';
for(var i = 0, l = arguments.length; i <l; i++){ toCall += arguments[i] + ' '; }
__log(toCall);};

console.log =
function(args){
	if(console.LogLevel >= LOG_LEVEL.Info){
		console.__log.apply(this,arguments);
	}
};

and the offending piece of code which this thread refers to (javascript):

console.setLogLevel =
function(level){
	console.trace('Inside setLogLevel.');
	console.trace('Attempting to set log level to ' + level);
	
	if(level < LOG_LEVEL.Off || level > LOG_LEVEL.All){
		var toThrow = "LogLevel can only be a value between " + LOG_LEVEL.Off + " and " + LOG_LEVEL.All;
		console.fatal(toThrow);
		throw toThrow;
	}
	
	console.trace('Type of console.Loglevel = ' + (typeof console.LogLevel));
	console.trace('Value of console.LogLevel = ' + console.LogLevel);
	
	//__updateLevel(level);
	
	try{
		console.LogLevel = level;
	}catch(err){
		console.log(err);
	}
	
	console.trace('Exiting console.setLogLevel');
}

Jul 6, 2011 at 7:22 PM

Can you post the full code including definition of console.trace and the log level? I'm having difficulties attempting to stub your JS around. I see that you're setting the 'setLogLevel' property to a large function, but

then you reassign it to a function that reassigns it again. I'm trying my best to be neutral in opinion, but it's difficult to analyze without the rest of the code. I don't even know which version of setLogLevel is causing the problem.

Jul 7, 2011 at 2:45 AM

Sure, will do tomorrow when I have more time, but the console.setLogLevel() never gets re-assigned.

This function really boils down to these pieces, which ought to capture the problem:

var console = {};

console.LogLevel = 4; //(a number)

console.setLogLevel =

function(level){

console.LogLevel = level; //(which is a number that is passed in)

//this assignment just kind of disappears, and any following code doesn't seem to execute

};

That's the essence of the problem right there...I just included the rest as context.

Jul 7, 2011 at 3:00 AM

Oh sorry, I mistook LogLevel as setLogLevel, alright, after defining all your bunch, a simple call to console.logLevel will reproduce this failure?

Jul 7, 2011 at 3:10 PM
Edited Jul 7, 2011 at 3:48 PM

Yes, calling setLogLevel(), from the second context will reproduce it.  

....I decided to go ahead and write a bare-bones version to try and reproduce the error, and in that version I'm not getting it (which actually I find a good thing).  So I think I'll go ahead and try to build this version up in complexity until either I hit the bug again or until I'm able to pull that code into my main project.

Jul 7, 2011 at 4:06 PM

Update:  Problem found and fixed...it wasn't a bug in Jint at all, but a bug in my test.  Even though I feel kind of stupid about it, I'm glad I found it because it's going to be important for my project that I'm able to use Jint in this way.  Thanks for the responses ChazZeromus

Jul 7, 2011 at 4:27 PM

Glad everything worked out!

Coordinator
Jul 7, 2011 at 5:21 PM

Glad to see the community worked nicely here :)