Bug 15018 - JSContext.EvaluateScript() fails to evaluate javascript function
Summary: JSContext.EvaluateScript() fails to evaluate javascript function
Status: RESOLVED NOT_REPRODUCIBLE
Alias: None
Product: iOS
Classification: Xamarin
Component: Xamarin.iOS.dll ()
Version: 7.0.0.x
Hardware: Macintosh Mac OS
: --- normal
Target Milestone: Untriaged
Assignee: Bugzilla
URL:
Depends on:
Blocks:
 
Reported: 2013-09-26 12:25 UTC by tharealjohn
Modified: 2013-09-26 15:43 UTC (History)
3 users (show)

Tags:
Is this bug a regression?: ---
Last known good build:


Attachments
Example project and sln to reproduce the error (5.21 KB, application/zip)
2013-09-26 12:25 UTC, tharealjohn
Details


Notice (2018-05-24): bugzilla.xamarin.com is now in read-only mode.

Please join us on Visual Studio Developer Community and in the Xamarin and Mono organizations on GitHub to continue tracking issues. Bugzilla will remain available for reference in read-only mode. We will continue to work on open Bugzilla bugs, copy them to the new locations as needed for follow-up, and add the new items under Related Links.

Our sincere thanks to everyone who has contributed on this bug tracker over the years. Thanks also for your understanding as we make these adjustments and improvements for the future.


Please create a new report on Developer Community or GitHub with your current version information, steps to reproduce, and relevant error messages or log files if you are hitting an issue that looks similar to this resolved bug and you do not yet see a matching new report.

Related Links:
Status:
RESOLVED NOT_REPRODUCIBLE

Description tharealjohn 2013-09-26 12:25:44 UTC
Created attachment 4998 [details]
Example project and sln to reproduce the error

Using the new MonoTouch.JavaScriptCore library, JSContext is failing to evaluate some javascript code. The method returns "undefined" for the JSValue, indicating something is wrong with the script. However, the script looks good. 

Attached is a project to reproduce. Place a break point on the line commented to do so and look at the result. 

Code:

JSContext context = new JSContext();
JSValue result = context.EvaluateScript("var square = function(x) {return x*x;}"); // Breakpoint here: returns undefined
JSValue function = result[new NSString("square")];
JSValue methodResult = function.Call(JSValue.From(2, context));
Comment 1 Sebastien Pouliot 2013-09-26 14:08:32 UTC
I'm no expert in javascript embedding (in any platform) but this (basic) unit test works correctly.

		[Test]
		public void EvaluateScript ()
		{
			using (var c = new JSContext ())
			using (JSValue r = c.EvaluateScript ("function FourthyTwo () { return 42; }; FourthyTwo ()")) {
				Assert.That (r.ToInt32, Is.EqualTo (42), "42");
			}
		}

The string (for the script) is not interpreted by Xamarin, are you sure this should be working ?
Comment 2 Sebastien Pouliot 2013-09-26 14:13:43 UTC
This is, naively, how I would have done it (and it works).

			using (var c = new JSContext ())
			using (JSValue square = c.EvaluateScript ("function Square (x) { return x * x; };")) 
			using (JSValue input = JSValue.From (2, c))
			using (JSValue r = square.Call (input)) {
				Assert.That (r.ToInt32, Is.EqualTo (4), "4");
			}

Do you have any reason (e.g. ObjC sample code) to believe your original code should be working too ?
Comment 3 tharealjohn 2013-09-26 14:36:11 UTC
Hey Sebastien,

I am trying to stumble through this framework, so I may be doing things incorrectly. Here is where I learned about the JavscirptCore framework: http://blog.bignerdranch.com/3784-javascriptcore-and-ios-7/

Along with the WWDC videos on the JSFramework, which has similar code. 

That blog shows code in obj-c exactly how I tried to implement above. Also, I copied your code into my project, and it does NOT run. square is returned as undefined. Also, what if your script was a file, full of multiple functions like shown in the WWDC JavaScriptCore Framework video? How would JSValue square be that particular method?
Comment 4 tharealjohn 2013-09-26 14:44:07 UTC
Hey Sebastien,

I am trying to stumble through this framework, so I may be doing things incorrectly. Here is where I learned about the JavscirptCore framework: http://blog.bignerdranch.com/3784-javascriptcore-and-ios-7/

Along with the WWDC videos on the JSFramework, which has similar code. 

That blog shows code in obj-c exactly how I tried to implement above. Also, I copied your code into my project, and it does NOT run. square is returned as undefined. Also, what if your script was a file, full of multiple functions like shown in the WWDC JavaScriptCore Framework video? How would JSValue square be that particular method?
Comment 5 tharealjohn 2013-09-26 14:46:58 UTC
Sorry for the duplicate. 

Here is the relevant obj-c code from that blog post which would indicate that my code should work:

[context evaluateScript:@"var square = function(x) {return x*x;}"];
    JSValue *squareFunction = context[@"square"];
    NSLog(@"%@", squareFunction);
    JSValue *aSquared = [squareFunction callWithArguments:@[context[@"a"]]];
    NSLog(@"a^2: %@", aSquared);
    JSValue *nineSquared = [squareFunction callWithArguments:@[@9]];
    NSLog(@"9^2: %@", nineSquared);
Comment 6 Sebastien Pouliot 2013-09-26 15:01:54 UTC
hmm.. I could have sworn it worked fine (but it now fails for me too). Looks like the cold I have is still affecting my mind...

I think the root of your issue is how Apple returns a description of the JSValue object that represent your script. What XS shows in the watch window (or tooltip) is that Apple returns for the `description` selector (which sadly is not always very helpful).

	using (var context = new JSContext ())
	using (JSValue script = context.EvaluateScript ("var square = function (x) { return x * x; }")) 
	using (JSValue function = context [(NSString) "square"])
	using (JSValue input = JSValue.From (3, context))
	using (JSValue result = function.Call (input)) {
		Assert.That (result.ToInt32, Is.EqualTo (9), "9");
	}

In the above code every object instance (but `script`) has a "valid" description. My guess it that it's because it's really undefined (i.e. evaluate did not return anything).

FWIW the blog post does not show (or use) return value:

    [context evaluateScript:@"var square = function(x) {return x*x;}"];
  
because it's not useful since what's being done resides inside the `context`. OTOH some other evaluation might return something (which is why it returns something other than void).
Comment 7 Sebastien Pouliot 2013-09-26 15:09:57 UTC
Using a variable inside the context works too.

	using (var context = new JSContext ())
	using (JSValue value = context.EvaluateScript ("a = 3")) 
	using (JSValue script = context.EvaluateScript ("var square = function (x) { return x * x; }")) 
	using (JSValue function = context [(NSString) "square"])
	using (JSValue result = function.Call (context [(NSString) "a"])) {
		Assert.That (result.ToInt32, Is.EqualTo (9), "9");
	}

I think things works the way they should (and like the blog post).
Comment 8 Sebastien Pouliot 2013-09-26 15:13:12 UTC
Forgot to mention at `value` description is not "undefined" (like `script`) but `3` in the above sample.
Comment 9 tharealjohn 2013-09-26 15:23:26 UTC
Thank you Sebastien, you are awesome. 

The above does work (comment 6 and 7), and I appreciate your patience and help.
Comment 10 Sebastien Pouliot 2013-09-26 15:43:36 UTC
np :-) it's a new framework and one where documentation/samples are sorely missing.

whenever in doubt it's better to file a bug and have it confirmed