Code Monkey home page Code Monkey logo

jurassic's Introduction

Jurassic

Build status

What is Jurassic?

Jurassic is an implementation of the ECMAScript language and runtime. It aims to provide the best performing and most standards-compliant implementation of JavaScript for .NET. Jurassic is not intended for end-users; instead it is intended to be integrated into .NET programs. If you are the author of a .NET program, you can use Jurassic to compile and execute JavaScript code.

Features

  • Supports all ECMAScript 3 and ECMAScript 5 functionality, including ES5 strict mode
  • Well tested - passes over five thousand unit tests (with over thirty thousand asserts)
  • Simple yet powerful API
  • Compiles JavaScript into .NET bytecode (CIL); not an interpreter
  • Deployed as a single .NET assembly (no native code)
  • Basic support for integrated debugging within Visual Studio
  • Uses light-weight code generation, so generated code is fully garbage collected
  • Tested on .NET 3.5, .NET 4 and Silverlight

How do I get it?

Install the NuGet package.

Usage

See the wiki for full usage details.

ECMAScript 6 status

Support for ECMAScript 6 is in progress. See http://kangax.github.io/compat-table/es6/ for the definition of each feature. The table below is correct as of version 3.1.

Feature Status
Optimisation
  proper tail calls (tail call optimisation)
Syntax
  default function parameters 4/7
  rest parameters
  spread syntax for iterable objects
  object literal extensions ✅ 6/6
  for..of loops 6/9
  octal and binary literals ✅ 4/4
  template literals 6/7
  RegExp "y" and "u" flags
  destructuring, declarations
  destructuring, assignment
  destructuring, parameters
  Unicode code point escapes ✅ 4/4
  new.target ✅ 2/2
Bindings
  const ✅ 18/18
  let 14/16
  block-level function declaration[18]
Functions
  arrow functions
  class ✅ 24/24
  super ✅ 8/8
  generators
Built-ins
  typed arrays 45/46
  Map 18/19
  Set 18/19
  WeakMap 11/12
  WeakSet 10/11
  Proxy [25] 33/34
  Reflect [26] 18/20
  Promise 4/8
  Symbol ✅ 12/12
  well-known symbols[27] 23/26
Built-in extensions
  Object static methods ✅ 4/4
  function "name" property 10/17
  String static methods ✅ 2/2
  String.prototype methods ✅ 10/10
  RegExp.prototype properties ✅ 6/6
  Array static methods 8/11
  Array.prototype methods ✅ 10/10
  Number properties ✅ 9/9
  Math methods ✅ 17/17
  Date.prototype[Symbol.toPrimitive] ✅ 1/1
Subclassing
  Array is subclassable 9/11
  RegExp is subclassable ✅ 4/4
  Function is subclassable 4/6
  Promise is subclassable
  miscellaneous subclassables
Misc
  prototype of bound functions 1/5
  Proxy, internal 'get' calls 19/36
  Proxy, internal 'set' calls 7/11
  Proxy, internal 'defineProperty' calls
  Proxy, internal 'deleteProperty' calls
  Proxy, internal 'getOwnPropertyDescriptor' calls 2/4
  Proxy, internal 'ownKeys' calls ✅ 3/3
  Object static methods accept primitives ✅ 10/10
  own property order 5/7
  Updated identifier syntax 1/3
  miscellaneous 8/9
Annex b
  non-strict function semantics[35] 2/3
  __proto__ in object literals [36]
  Object.prototype.__proto__ 1/6
  String.prototype HTML methods ✅ 3/3
  RegExp.prototype.compile 1/2
  RegExp syntax extensions 4/8
  HTML-style comments

jurassic's People

Contributors

dependabot[bot] avatar kpreisser avatar miroslav22 avatar objmalloc avatar paulbartrum avatar robpaveza avatar sjroche avatar taritsyn avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

jurassic's Issues

Code generation build task

Hi,

I would like if is possible to create a build task (that can be added to project with a nuget package) to add some code generation like your project "Attribute Code Generation" does, so javascript C# objects can be written correctly, according to Jurassic coding philosophy.

Thanks.

MapInstance.Set() does not overwrite existing values

Hi,

when running the following code in Firefox, Chrome or other browsers:

new Map().set(1, 2).set(0, 9).set(1, 8).get(1);

the return value is 8 (as set(1, 8) should overwrite the existing entry with value 2).
However, on Jurassic it returns 2.

MapInstance.Set() looks like the following:

    [JSInternalFunction(Name = "set")]
    public MapInstance Set(object key, object value)
    {
        if (this.store.ContainsKey(key))
            return this;
        if (key is double && TypeUtilities.IsNegativeZero((double)key))
            key = 0;
        var node = this.list.AddLast(new KeyValuePair<object, object>(key, value));
        this.store.Add(key, node);
        return this;
    }

So it checks whether the key already exists, but in that case does not set the new value.
ES2015 says at 23.1.3.9:

5. Repeat for each Record {[[key]], [[value]]} p that is an element of entries,
a. If p.[[key]] is not empty and SameValueZero(p.[[key]], key) is true, then
i. Set p.[[value]] to value.
ii. Return M.

So it seems the first if branch misses the code to set the new value.

Thanks!

Creating a JavaScript object from .NET code

Hi, I see from here that I can pass a Jurassic.Library.ObjectInstance (or a derived type) through the .SetGlobalValue method to create a Javascript Object. However, using Jurassic 2.1.0 from NuGet I see that the constructor for ObjectInstance is protected.

Would you be able to give me (or a link to) a quick example of creating an ObjectInstance with a property? Cheers.

ClrStaticTypeWrapper.PopulateMembers method incorrectly setups property for .NET indexed property

Reproduction:

  1. Pass ArrayList CLR object to JS and try to try to access element from the array like:

var element = arrayList.Item[0];

or

var element = arrayList.get_item(0);

Observed behavior:

First code fails with error "No overload for method 'get_Item' takes 0 arguments".
Second code fails with error "'arrayList.get_Item' is not a function".

Expected behavior:

The first code will obviously not work because there are no indexed properties in JS. The second code should work.

Propoposed solution:

ClrStaticTypeWrapper.PopulateMembers method must be modified to call PropertyInfo.GetIndexParameters method to verify if property is indexed. If property is indexed, it should not be setup as JS property. This is the code I propose:

                case MemberTypes.Property:
                    PropertyInfo property = (PropertyInfo)member;
                    if ( property.GetIndexParameters().Length > 0 )
                    {   // Indexed property. Do not setup as property.
                        continue;
                    }

                    ...

Feature Request: Unity3d support

I'm still diagnosing the issue but Jurrasic from asset store and from github both crash Unity3d.
Somewhere in the IL code generation.
I'm using Unity 5.4.0b20 (64-bit).

It's a pain, but the -unsafe and mono .net 2.0 is very limiting in JS choices.

Thanks.

Add a wiki page showing how to apply a timeout to script execution

Hi,

sometimes it is needed to abort a long-running script, e.g. because the user accidently wrote an infinite loop in the Javascript. I found issues #41 and #7 which ask how to interrupt a script after a timeout.

AFAIK, Jurassic doesn't have a built-in functionaliy to set a timeout for script execution, e.g. in a way that Jint has with Options.TimeoutInterval(TimeSpan timeoutInterval), which will throw a TimeoutException when executing a script is taking longer than the specified timeout. The timeout will only apply to script code, so e.g. if the script calls back a .NET method and the timeout has passed, the .NET method will complete before the TimeoutException is thrown by Jint.

However, for Jurassic, it should be possible to use Thread.Abort() to abort script execution. I have written a wiki page that shows how to use a Helper class to set a timeout for script execution, while being able to mark .NET callbacks as "critcal code" in which a ThreadAbortException should not occur.
See the wiki page here: (EDIT: Removed as the page has been adapted, see the reference link below)

Do you think it is possible to add such a wiki page to the Jurassic wiki? (Feel free to modify it in any way if needed.)

Thank you!

Error on build MacOS

Hello,

i'm trying to build jurassic on my MacOS, but I get some errors with the projects that they can't find the jurassic library. And I get this error also:

/Library/Frameworks/Mono.framework/Versions/4.2.1/lib/mono/4.5/Microsoft.Common.targets: Warning: Unable to find framework corresponding to the target framework moniker '.NETFramework,Version=v3.5,Profile=Client'. Framework assembly references will be resolved from the GAC, which might not be the intended behavior. (Jurassic)

and a lot of:

..../workspace/jurassic/Jurassic/CSC: Error CS0518: The predefined type `System.Object' is not defined or imported (CS0518) (Jurassic)

I really would love to contribute in this project. I love this idea and use this framework on my Unity3D projects.

Loop statements mid-execution handles

HI, there.
I am writing a hacking game somewhat based on your marvelous framework right now.
In that game (netbreakers is working title) users run a virtual terminal and can launch programs which are basically javascript files, memory file system is already setup and running and everything seems fine.
However, because of the nature of the game (i am using internal classes to run multiple engines and their instances) I am pretty damn certain that smart-arse (and inexperienced) users will hang the game just by creating while(true) {} (or for(;;) loops which will hang execution if they not contain some sort of break or sleep statement.
So, i have a question. Is there a quick way to force loop statements to pause from software side and not from the user side? say, some automatic analysis which can pass handle to resume execution to a c# code once a loop statement reaches it's looping point?

Interrupt a script/eval

Hi, im using jurassic to let users run scripts, but i need to add a timeout for them so they can't mess up my server with "while(true) ;"

Is there any callback or something to do that?
Or do I have to go the ugly route of Thread.Abort() ?? That'd be pretty sad :(

How will the Promise's onFulfilled handlers be implemented to be postponed until no script code is on the stack?

Hi,

I noticed you have started working on Promises, but did not yet implement handling the onFulfilled and onRejected handlers.
After reading through the Promises/A+ specification, I realized that these handlers are not invoked recursively, but instead postponed until no other script function is currently on the stack (which makes sense to avoid a StackOverflow when converting a loop to use promises).

For example. if you run:

console.log("1. Before resolve");
Promise.resolve(123).then(function() {
  console.log("2. Inside onResolved handler");
});
console.log("3. After resolve");

then the program prints:

1. Before resolve
3. After resolve
2. Inside onResolved handler

Do you have a thought of how to implement this so that the onFulfilled and onRejected handler's execution is postponed until no further script function is on the stack?

My thought was to have the ScriptEngine declaring a list of FunctionInstances that are to be invoked later, and a boolean that specifies if a function of this script engine is already on the stack:

internal bool ScriptIsExecuting { get; set; }

internal List<FunctionInstance> ExecutionContextFunctionBuffer { get; } =
    new List<FunctionInstance>();

Then, maybe the FunctionInstance's CallLateBound() method could be changed to be not abstract but instead call an abstract CallLateBoundCore() function (and subclasses override the latter one instead of CallLateBound()), and the CallLateBound() can check if it needs to call the buffered function instances:

public object CallLateBound(object thisObject, params object[] argumentValues)
{
    bool firstExecution = !Engine.ScriptIsExecuting;
    if (firstExecution)
        Engine.ScriptIsExecuting = true;

    try
    {
        var returnValue = CallLateBoundCore(thisObject, argumentValues);

        // Check if we are the first script function on the stack from this script engine
        // and we need to call buffered functions.
        if (firstExecution)
        {
            while (Engine.ExecutionContextFunctionBuffer.Count > 0)
            {
                List<FunctionInstance> copy =
                    new List<FunctionInstance>(Engine.ExecutionContextFunctionBuffer);
                Engine.ExecutionContextFunctionBuffer.Clear();

                foreach (var func in copy)
                    func.Call(Null.Value); // TODO

            }
        }

        return returnValue;
    }
    finally
    {
        if (firstExecution)
            Engine.ScriptIsExecuting = false;
    }
}

This way it would be ensured that if functions from multiple ScriptEngines are on the stack, the first function of each ScriptEngine on the stack will call the buffered FunctionInstances (this is currently how I do it in my code with a ES6-Promises Polyfill).

However, one issue is how to handle exceptions when FunctionInstances are buffered. For example, if you run this in Chrome:

Promise.resolve(123).then(function() {
  console.log("Inside onResolved handler");
});
throw new Error("My Error");

it will print on the console:

Inside onResolved handler
Uncaught Error: My Error(…)

So it will still call the onFulfilled handler and then throw the exception. This could be implemented by catching exceptions in the CallLateBound e.g. with ExceptionDispatchInfo.Capture(ex) to preserve the StackTrace when rethrowing it later, then running the buffered FunctionInstances and then rethrowing the exception. (I think the FunctionInstances shouldn't be run in a finally block as it marks a protected region of code and cannot be aborted with Thread.Abort()).

Thanks!

Suggestion for object exchange between .net and Javascript

Hi,

First I wanted to thank you for the amazing work. Keep the great work going.

Next, I wanted to point you to a solution that I have been using for exchanging data between C# and Javascript.
While using ObjectInstance is theoretically the best practice in many situations, regarding the dynamic nature of the Javascript objects, most of the times the easiest way of exchanging data between C# and Javascript is a simple Json serialization.

While, I usually use Json.NET for doing so, but .net's builtin JavaScriptSerializer class also does an acceptable job.

This is just some idea for making things a little easier. Maybe it comes in handy in some situation.

Thanks for the great work.

Can't prototype-chain objects defined in .NET

I'm having trouble creating a prototype chain of objects that I have defined on the .NET side. I'm creating my objects using the pattern described under the Building and instance class header.

Regardless of what other ObjectInstances I pass through the constructor and instance function constructors (on the .NET side of the code) I always end up with objects that have an Object prototype rather than the specific object that I wanted the new instance to 'prototype' from.

Is this currently possible to achieve with Jurassic, or am I going about this the wrong way?

PopulateFunctions of inherited types

Hi,

I have made a C# class inheritance for javascript object types :

ObjectInstance
+- MyBaseObject
    +- MyRealObject
    +- MyRealObject

The [Js*] attributes of MyBaseObject are not recognized by the PopulateFunctions(), only the final type.

Is it possible to add a way to allow C# type inheritance for ObjectInstance derivation ?

Thanks.

Performance/ Implement type tracking

SpiderMonkey and V8 run close to native speeds by tracking the flow of variable types throughout the system and essentially being able to directly call a method/ read a field rather than performing a prototypical lookup.

We're currently in the process of implementing this with first looks being very encouraging - the changes are very widespread though so more updates and a repo will be coming shortly for you guys to play with. I'm personally focusing on getting Route9.js to function at a respectable framerate which also requires full typed array support, so I'll likely be finishing off that as well unless someone else gets there first.

The current change set that has occurred in order to make this possible is roughly as follows:

  • No longer need special classes to declare functionality; it 'just works' with general .NET types.
  • Automatically drops the case of the first letter of a .NET method name unless told otherwise.
  • All public properties on an exposed type are exposed to JS unless they are explicitly hidden.
  • Removed stack frames and instead used the .NET internal stack. (Method call overhead reduced).
  • Removed unsafe code requirements.
  • Globals resolve as fast as ordinary .NET fields do.
  • Scope chain is statically resolved. This now means that method signatures have nothing particularly special about them so methods can be directly invoked at the call site.
  • Added a traditional hoisting mechanism (incomplete).
  • Reduced property overhead significantly by removing the schema duplication process as it ended up being unnecessary.
  • Removed the boxing-but-not-really construction of e.g. a Number object around the double value to access its methods. It just remains a double at all times, and those methods were made static instead.
  • Removed a lot of type casting as it was no longer necessary
  • We need it to precompile so it's also heading towards this as well.

But it did come at the cost of a few things at the moment:

  • Eval can only access variables in the global scope (because the scope chain is static).
  • With statement got a little broken. Fixing!
  • Hoisting through more than 1 scope depth is also broken. Also fixing!
  • Can't set a hoisted variable in its host scope, essentially 'un-hoisting' it. Although possible to implement, I'm personally viewing this as a minor at the moment.
  • Generally making a bundle assumption that all code is loaded at once, although this assumption will not affect the final result.

The end result is a totally different beast which I've been nicknaming Nitrassic (a combo of Nitro, our own home-grown .NET JS engine which runs fast but has very limited spec coverage). I wanted to drop this here so we could a) thank you guys, Jurassic rocks! and b) open up a discussion on the type tracking system as there are probably thoughts floating around on the implementation. The current approach that is in the process of being implemented is roughly summarized as follows:

Type introduction into the system.
Types are introduced into the system in a set number of ways. These points essentially provide the 'roots' of the type flow graph:

  • A .NET constructor call such as var a=new Date(); // a is Library.Date
  • Inline constants such as var a='hello!'; // a is System.String
  • A .NET method call such as var a=Math.abs(-2); // a is System.double
  • External imports, i.e. ScriptEngine.CallGlobalFunction('myFunction',known,type,objects); or SetGlobalValue.
  • Creating a JS object with {} notation.
  • Creating a JS object with the new keyword and a function.

The premise
We make the assumption that a variables type is always known by the compiler. There are three major unknowns and those are function arguments, new object properties and global variables - everywhere else the types can be known - so we must essentially find a way of tracking their types too.

Function arguments
Fortunately this one's relatively simple - we simply compile a method when it gets called. Based on our assumption, the call site always knows the types of the variables its passing as arguments. Hoisted variables will all be known too. Thus, we can know exactly which types are in use at a given call site and compile the method accordingly. A method can potentially be compiled multiple times if it's being called in a few different ways.

Object properties
We need to know all properties that are being added to a given object prototype. As we're compiling at the function scope level, then we essentially have to watch for new properties being added either in this function scope or any function scope that is being called from. In order to do this, a function tracks the side effects it has on its arguments whilst its being compiled. The call site of that function can then be aware of any new properties that may be getting added and their type too.

Global variables
Globals are awkward because 'anyone' can change them. Whenever a global is set, we check to see if the type being set matches that of the global. If they are different, i.e. a global is being set to some different type, then the global is said to have 'collapsed' and has its type set to typeof(object). However, functions may have been compiled that used it as its previous type. So, each global tracks the functions which use it. If that global collapses, it then simply requests that those functions recompile. When a function attempts to compile with a collapsed global, it emits runtime type checking. Interestingly, this is the only situation where runtime type checking actually occurs - essentially only if someone sets a global to more than one type.

Thoughts/ opinions/ 'you're crazy!'s/ ideas all welcome!

JavaScriptExceptions are caught by foreign ScriptEngines

Hi,

as ObjectInstances etc. have a reference to the ScriptEngine to which they belong, I assume one shouldn't take an ObjectInstance originating from one ScriptEngine and set it as property on another ScriptEngine. Is this correct?

In that case, I think the catch clauses for JavaScriptExceptions need to be adjusted. Currently, it is possible that one ScriptEngine throws a JavaScriptException, and if called recursively by another ScriptEngine, its JavaScript code may catch the exception. If that happens, it probably was unintended that a "foreign" JavaScriptException is catched in JS code.

We use Jurassic for extending an application with JavaScripts, where the transitions on the stack can be Native (.NET) -> JS (event handler or callback which calls a FunctionInstance), JS->Native (Script calls an API) and again Native -> JS (another callback).

Consider this simplified example:
EventProducer:

        class EventProducer
        {
            public event Action MyEvent;

            protected void OnMyEvent() => MyEvent?.Invoke();

            public void Run()
            {
                // Run a script which raises the event.
                string script = @"
try {
    api.raiseEvent();
} catch (e) {
    console.log('e.myGlobalVariable: ' + e.myGlobalVariable);
}
";

                ScriptEngine engine = new ScriptEngine();
                //engine.Compile
                engine.ForceStrictMode = true;
                engine.SetGlobalValue("console", new FirebugConsole(engine));
                engine.SetGlobalValue("api", new Api(engine, this));
                engine.Execute(script);
            }


            private class Api : ObjectInstance
            {
                private EventProducer producer;

                public Api(ScriptEngine engine, EventProducer producer)
                    : base(engine)
                {
                    this.producer = producer;
                    PopulateFunctions();
                }

                [JSFunction(Name = "raiseEvent")]
                public void RaiseEvent()
                {
                    producer.OnMyEvent();
                }
            }
        }

EventConsumer:

       class EventConsumer
        {
            private EventProducer producer;

            public EventConsumer(EventProducer producer)
            {
                this.producer = producer;
            }

            public void Run()
            {
                // Run a script which adds an event handler.
                string script = @"
var myGlobalVariable = 'Hi, this is a global variable.';

(function (globalObject) {
    api.addEventHandler(function () {
        // Throw the global object
        throw globalObject;
    });
})(this);
";

                ScriptEngine engine = new ScriptEngine();
                engine.ForceStrictMode = true;
                engine.SetGlobalValue("console", new FirebugConsole(engine));
                engine.SetGlobalValue("api", new Api(engine, this));
                engine.Execute(script);

            }


            private class Api : ObjectInstance
            {
                private EventConsumer consumer;

                public Api(ScriptEngine engine, EventConsumer consumer)
                    : base(engine)
                {
                    this.consumer = consumer;
                    PopulateFunctions();
                }

                [JSFunction(Name = "addEventHandler")]
                public void AddEventHandler(FunctionInstance instance)
                {
                    consumer.producer.MyEvent += () => instance.Call(null);
                }
            }

Main Program:

            var producer = new EventProducer();
            var consumer = new EventConsumer(producer);

            consumer.Run();
            producer.Run();

In that example, EventProducer has a event which is raised by JS code. EventConsumer runs JS code which adds an event handler to the producer's event, and in that handler, throws a JS object.
Both EventConsumer and EventProducer may not know that each other will also run JS code. However, the JS code in the EventProducer can use a catch clause to catch the JavaScriptException and get access to a foreign ScriptEngine's objects (the global object in that case).

To change this, I think the clauses like catch (JavaScriptException ex) would need to be adjusted to catch (JavaScriptException ex) when (ex.ScriptEngine == this.ScriptEngine) or something similar, if this is possible (e.g. in the TryCatchFinallyStatement class, however I don't know how to do this correctly).

Thank you!

Performance issue during first script execution on Azure cloud service

Hi,

I have a big JS script which looks like

function executeScript(param1, param2) {
    // It's about 1000 lines of code here
}

During the first run I compile the script and put FunctionInstance to a cache to be able to reuse it in future:

var engine = new ScriptEngine
{
    EnableExposedClrTypes = true
};
var compiledScript = engine.Compile(new StringScriptSource(script));
compiledScript.Execute();
var executeScriptFunc = (FunctionInstance)engine.GetGlobalValue("executeScript");

cache.Add('SCRIPT_ID', executeScriptFunc);

After that I execute the function like this:

Trace.TraceInformation($"{DateTime.Now:T} => Before");

var result = executeScriptFunc.Call(null, "Some value 1", "Some value 2");

Trace.TraceInformation($"{DateTime.Now:T} => After");

On any local environment, the first run takes about 1.5 sec to start executing the first line of the script. Further runs don't have this lag at all

But if I deploy the same code to Azure cloud service, the first run takes about 5 minutes. It doesn't depend on how powerful Azure cloud service is. It's always about 5 minutes. Second and future runs are immediate.

For second and further runs I use cached FunctionInstance

Don't you guys know why it happens? Where could I look at to fix this?

How to expose a .NET class to JavaScript while being subclassable?

Hi,

Jurassic is a really great library, and in our product at work where we use it to allow the user to program the application with JavaScripts/TypeScripts (using the Monaco Editor), gradually more advanced use cases appear while building the interface between JavaScript and .NET.

The wiki page Exposing a .NET class to JavaScript shows an example how to create a constructor and instance for static and instance functions.
However as I understand it, when the Random function is called as a constructor using new Random(...), its JSConstructorFunction always creates and returns a new RandomInstance object. This means the class cannot be subclassed in JavaScript because in that case the constructor function would need to be run with an existing ObjectInstance that has a prototype of the subclass.

For example, consider the following TypeScript that tries to subclass the existing Random class that we exposed from .NET, using a new EnhancedRandom class:

// Declare the existing class
declare class Random {
    constructor(seed: number);
    nextDouble(): number;
}


class EnhancedRandom extends Random {
    constructor(seed: number) {
        super(seed);
    }

    nextInt(maxValue: number) {
        return Math.floor(this.nextDouble() * maxValue);
    }
}


let myRandom = new Random(1000);
let enhanced = new EnhancedRandom(1000);

console.log(myRandom.nextDouble());
console.log(myRandom.nextDouble());

console.log(enhanced.nextDouble());
console.log(enhanced.nextInt(5000));

When compiling down to ECMAScript 5, the following JS file is produced by TS:

var __extends = (this && this.__extends) || function (d, b) {
    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
    function __() { this.constructor = d; }
    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
var EnhancedRandom = (function (_super) {
    __extends(EnhancedRandom, _super);
    function EnhancedRandom(seed) {
        _super.call(this, seed);
    }
    EnhancedRandom.prototype.nextInt = function (maxValue) {
        return Math.floor(this.nextDouble() * maxValue);
    };
    return EnhancedRandom;
}(Random));
var myRandom = new Random(1000);
var enhanced = new EnhancedRandom(1000);
console.log(myRandom.nextDouble());
console.log(myRandom.nextDouble());
console.log(enhanced.nextDouble());
console.log(enhanced.nextInt(5000));

When subclassing the Random class, a new function EnhancedRandom is created and its prototype property is set to a new object that has a prototype of Random.prototype. When the script calls new EnhancedRandom(1000), a new object is created with a prototype of EnhancedRandom, and the EnhancedRandom function is called with the 'this' object being the just created new object.

Now, when running the JS code, the call new EnhancedRandom(1000) produces an InvalidCastException:

System.InvalidCastException: Unable to cast object of type 'SubclassExample.RandomConstructor' to type '<>c'.
   at binder_for_Jurassic.Library.ClrFunction+<>c.<.ctor>b__3_0(ScriptEngine , Object , Object[] )
   at Jurassic.Compiler.Binder.Call(ScriptEngine engine, Object thisObject, Object[] arguments) in C:\Users\Name\Desktop\JurassicTest\jurassic\Jurassic\Compiler\Binders\Binder.cs:line 72
   at Jurassic.Library.ClrFunction.CallLateBound(Object thisObject, Object[] arguments) in C:\Users\Name\Desktop\JurassicTest\jurassic\Jurassic\Library\Function\ClrFunction.cs:line 178
  (...)

(Maybe an JavaScriptException should be thrown in this case so the JS code has a change to catch the exception.)

Now, in order to make Random subclassable, AFAIK what needs to be changed is:

  1. Change the RandomConstructor to not create and return a new object, but instead work with the supplied, existing ObjectInstance.
  2. Due to the above change, the RandomInstance class cannot be used as instance class anymore (containing the private Random random; field as shown in the Wiki page), because we may have to work with an existing ObjectInstance.

To fix 2), one can use a ConditionalWeakTable to attach an extension object to existing ObjectInstances where the private data can be stored.

To fix 1), I think one cannot use the ClrFunction class because I did not found a way to have a declared .NET function be called with the correct 'this' object (it seems that the 'this' object is always the FunctionInstance, not the new created object), and it overwrites FunctionInstance.ConstructLateBound() to do its own logic, whereas I would need the logic of the FunctionInstance which is the JavaScript logic:

  1. Create new object with a prototype of FunctionInstance.InstancePrototype
  2. Call the function, using the newly created object as 'this' value
  3. If the function returned an ObjectInstance, the value of the new ... expression is the returned object, otherwise it is the previously created object.

The only way I found to do this is to directly extend the FunctionInstance class in the following way:

using System;
using System.Runtime.CompilerServices;
using Jurassic;
using Jurassic.Library;

namespace SubclassExample
{
    public class RandomConstructor : FunctionInstance
    {
        private const int typicalArgumentsLength = 1;

        private ScriptEngine engine;
        private RandomInstance instancePrototype;

        public RandomConstructor(ScriptEngine engine, string name)
        : base(engine.Function.InstancePrototype)
        {
            this.engine = engine;
            this.instancePrototype = new RandomInstance(engine.Object.InstancePrototype);

            // Set function properties.
            this.DefineProperty("name", new PropertyDescriptor(
                name, PropertyAttributes.Configurable), true);
            this.DefineProperty("length", new PropertyDescriptor(
                typicalArgumentsLength, PropertyAttributes.Configurable), true);
            this.DefineProperty("prototype", new PropertyDescriptor(
                this.instancePrototype, PropertyAttributes.Writable), true);

            this.instancePrototype.DefineProperty("constructor", new PropertyDescriptor(
                this, PropertyAttributes.Configurable | PropertyAttributes.Writable), true);

            PopulateFunctions();
        }

        private void CheckPrototype(object obj)
        {
            // Check if the object's prototype is correct.
            ObjectInstance prototype = obj as ObjectInstance;
            while (true)
            {
                prototype = prototype?.Prototype;

                if (prototype == null)
                    throw new JavaScriptException(this.Engine, ErrorType.TypeError,
                        "Cannot call a class as a function");
                if (prototype == this.instancePrototype)
                    break;
            }
        }

        public override object CallLateBound(
            object thisObject, params object[] argumentValues)
        {
            CheckPrototype(thisObject);

            int? seed = null;
            if (argumentValues.Length > 0)
            {
                var seedArg = argumentValues[0];
                if (seedArg is int)
                    seed = (int)seedArg;
                else if (seedArg is double)
                    seed = (int)(double)seedArg;
            }
            if (!seed.HasValue)
                throw new JavaScriptException(this.Engine, ErrorType.TypeError,
                    "Invalid argument");

            ConstructCore(thisObject as ObjectInstance, seed.Value);

            // Don't return a value. When the function is called with the "new" keyword, the
            // value of the "new ...()" expression will be the previously created object.
            return Undefined.Value;
        }

        /// <summary>
        /// Constructs a new <see cref="RandomInstance"/> from .NET code.
        /// </summary>
        /// <param name="seed"></param>
        public ObjectInstance ConstructNew(int seed)
        {
            // Create a new object with our prototype.
            ObjectInstance newObject = ObjectConstructor.Create(
                this.engine, this.instancePrototype);
            ConstructCore(newObject, seed);
            return newObject;
        }

        /// <summary>
        /// Runs the constructor on an existing object.
        /// </summary>
        /// <param name="thisObject"></param>
        /// <param name="seed"></param>
        protected virtual void ConstructCore(ObjectInstance thisObject, int seed)
        {
            // Construct the instance.
            RandomInstance.ConstructInstance(thisObject, seed);
        }
    }

    public class RandomInstance : ObjectInstance
    {
        private static ConditionalWeakTable<ObjectInstance, RandomInstanceExtension>
            instanceTable =
            new ConditionalWeakTable<ObjectInstance, RandomInstanceExtension>();

        /// <summary>
        /// Creates the instance prototype for the constructor function.
        /// </summary>
        /// <param name="prototype"></param>
        public RandomInstance(ObjectInstance prototype)
            : base(prototype)
        {
            // Construct the Prototype.
            this.PopulateFunctions();
        }

        public static void ConstructInstance(ObjectInstance obj, int seed)
        {
            // Run the instance constructor code.
            RandomInstanceExtension instance;
            if (instanceTable.TryGetValue(obj, out instance))
                throw new JavaScriptException(obj.Engine, ErrorType.Error,
                    "Constructor has already been called");

            instance = new RandomInstanceExtension()
            {
                Random = new Random(seed)
            };
            instanceTable.Add(obj, instance);
        }

        private static RandomInstanceExtension GetInstanceExtension(
            ObjectInstance thisObj)
        {
            RandomInstanceExtension instance;
            if (!instanceTable.TryGetValue(thisObj, out instance))
                throw new JavaScriptException(thisObj.Engine, ErrorType.TypeError,
                    "Method is not generic");
            return instance;
        }


        [JSFunction(Name = "nextDouble", Flags = JSFunctionFlags.HasThisObject)]
        public static double NextDouble(ObjectInstance thisObject)
        {
            RandomInstanceExtension instance = GetInstanceExtension(thisObject);
            return instance.Random.NextDouble();
        }


        private class RandomInstanceExtension
        {
            public Random Random { get; set; }
        }
    }
}

In the above code, .NET code can still construct a new RandomInstance with RandomConstructor.ConstructNew().

Running the script then produces the result:

 0.15155745910087481
 0.2359429496507826
 0.15155745910087481
 1179

Is this the correct way to expose build native classes that are subclassable?

If yes, I think this could be added to the Wiki page.

Thanks!

Error message for incorrect "instanceof" operand always displays "undefined" as type

Hi,

I noticed the error message that occurs when using the instanceof operator with an non-function right side operand always displays undefined as its type.
For example:

    var engine = new ScriptEngine();
    try
    {
        engine.Execute("var x = ({} instanceof 123);");
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
    }

displays:

TypeError: The instanceof operator expected a function, but found 'undefined' instead

instead of:

TypeError: The instanceof operator expected a function, but found 'number' instead

I think this is due to the following lines in Compiler/Expressions/BinaryExpression.cs (line 716-720):

            // Check the right-hand side is a function - if not, throw an exception.
            generator.IsInstance(typeof(Library.FunctionInstance));  // (1)
            generator.Duplicate();                                   // (2)
            var endOfTypeCheck = generator.CreateLabel();
            generator.BranchIfNotNull(endOfTypeCheck);

According to its documentation generator.IsInstance produces null when the object on the stack is not an instance of the specified type. The result is duplicated (I think for the Branch call), and in the error message branch the current stack value (null) is converted to undefined.

I think lines (1) and (2) need to be swapped, to retain the correct object on the stack for producing the error message.

Thanks!

Running on Mono : System.MissingMethodException: Method 'Array.Empty' not found.

Hi,

I have a script that I want to run under Mono, but I have a different behavior between Windows and Mono.

Javascript is equivalent to navigators' :

function draw()
{
}

window.requestAnimationFrame(draw);

On Microsoft Framework on Windows, works fine.

On Mono :

System.MissingMethodException: Method 'Array.Empty' not found.

It happens in Binder.cs, in method public object Call(ScriptEngine engine, object thisObject, params object[] arguments) when trying to execute return binderDelegate(engine, thisObject, arguments);

Running on Mono : 4.2.3 (Stable 4.2.3.4/832de4b)
Environment.Version : 4.0.30319.17020

Partial stacktrace :

System.MissingMethodException: Method 'Array.Empty' not found.
  at (wrapper dynamic-method) Jurassic.Compiler.JSBinder:binder_for_RpiMono.JsObjects.WindowInstance.RequestAnimationFrame (Jurassic.ScriptEngine,object,object[])
  at Jurassic.Compiler.Binder.Call (Jurassic.ScriptEngine engine, System.Object thisObject, System.Object[] arguments) <0x6d65ec50 + 0x00043> in <filename unknown>:0 
  at Jurassic.Library.ClrFunction.CallLateBound (System.Object thisObject, System.Object[] arguments) <0x6d65e8c0 + 0x00133> in <filename unknown>:0 
  at Jurassic.Library.FunctionInstance.CallWithStackTrace (System.String path, System.String function, Int32 line, System.Object thisObject, System.Object[] argumentValues) <0x6d657a60 + 0x00067> in <filename unknown>:0 
  at JavaScriptClass375.anonymous (Jurassic.ScriptEngine scriptEngine, Jurassic.Compiler.Scope scope, System.Object thisValue, Jurassic.Library.FunctionInstance , System.Object[] ) <0x6cbcda60 + 0x03b1b> in <filename unknown>:0 
  at (wrapper delegate-invoke) <Module>:invoke_object_ScriptEngine_Scope_object_FunctionInstance_object[] (Jurassic.ScriptEngine,Jurassic.Compiler.Scope,object,Jurassic.Library.FunctionInstance,object[])
  at Jurassic.Library.UserDefinedFunction.CallLateBound (System.Object thisObject, System.Object[] argumentValues) <0x6d657c10 + 0x00057> in <filename unknown>:0 
  at Jurassic.Library.FunctionInstance.CallWithStackTrace (System.String path, System.String function, Int32 line, System.Object thisObject, System.Object[] argumentValues) <0x6d657a60 + 0x00067> in <filename unknown>:0 
  at JavaScriptClass370.anonymous (Jurassic.ScriptEngine scriptEngine, Jurassic.Compiler.Scope scope, System.Object thisValue, Jurassic.Library.FunctionInstance , System.Object[] ) <0x6cb06000 + 0x1c72f> in <filename unknown>:0 
  at (wrapper delegate-invoke) <Module>:invoke_object_ScriptEngine_Scope_object_FunctionInstance_object[] (Jurassic.ScriptEngine,Jurassic.Compiler.Scope,object,Jurassic.Library.FunctionInstance,object[])
  at Jurassic.Library.UserDefinedFunction.CallLateBound (System.Object thisObject, System.Object[] argumentValues) <0x6d657c10 + 0x00057> in <filename unknown>:0 
  at Jurassic.Library.FunctionInstance.CallWithStackTrace (System.String path, System.String function, Int32 line, System.Object thisObject, System.Object[] argumentValues) <0x6d657a60 + 0x00067> in <filename unknown>:0 
  at JavaScriptClass369.global_sampleCube_js (Jurassic.ScriptEngine scriptEngine, Jurassic.Compiler.Scope scope, System.Object thisValue) <0x6cbca670 + 0x0015b> in <filename unknown>:0 
  at Jurassic.Compiler.GlobalMethodGenerator.Execute () <0x6d656848 + 0x00083> in <filename unknown>:0 
  at Jurassic.CompiledScript.Execute () <0x6d6567f8 + 0x0001f> in <filename unknown>:0 
  at Jurassic.ScriptEngine.Execute (Jurassic.ScriptSource source) <0x6e431228 + 0x0006f> in <filename unknown>:0 

Large JS library compatibility

I understand this can execute simple js scripts. Is it possible to run npm style modules? I have a lib that needs instantiating, and can then be used via numerous methods.. Some include Ajax or websocket usage behind the scenes executing call backs when done etc.

I can't figure out how to use it in Jurassic, i can't even get a reference to the instantiated object.

Change the Global object

Hi,

I would like to know if I can replace the actual Global object with the one I have defined ?

I am thinking about the navigators window object, that is the this in global scope, and each window functions are available as global functions.

Thanks.

Javascript objects defined in .NET don't Stringify correctly

If I define a Javascript object in C# code using the pattern shown here: Building an instance class, and I call JSON.stringify on an instance of that object, none of the expected fields are shown in the output. However, I am still able to access those fields in the same Javascript code (i.e I can use their values, etc). If I set an 'invisible' field from Javascript code and then perform the stringify, it appears in the output as I would expect.

C# class

using Jurassic;
using Jurassic.Library;

public class MyJsObjectConstructor : ClrFunction
{
    public MyJsObjectConstructor(ScriptEngine js)
        : base(js.Function.InstancePrototype, "MyJsObject", new MyJsObjectInstance(js.Object.InstancePrototype))
    {
    }

    [JSConstructorFunction]
    public MyJsObjectInstance Construct(int i)
    {
        return new MyJsObjectInstance(this.InstancePrototype, i);
    }
}


public class MyJsObjectInstance : ObjectInstance
{
    public MyJsObjectInstance(ObjectInstance prototype)
        : base(prototype)
    {
        this.PopulateFields();
        this.PopulateFunctions();
    }

    public MyJsObjectInstance(ObjectInstance prototype, int i)
        : base(prototype)
    {
    }

    [JSField]
    public string myString = "This is my string";
}

Javascript
var myJsObject = new MyJsObject(); console.log(JSON.stringify(myJsObject)); myJsObject.myString = "Updated the string"; console.log(JSON.stringify(myJsObject));

Javascript console output
This is my string {} {"myString":"Updated the string"}

jurassic 2.2.0 doesn't work in 4.6 runtime when running handlebars.js

When using the latest nuget package (2.2.0):

`var scriptEngine = new ScriptEngine();
var hpath = Server.MapPath("~/scripts/handlebars-v2.0.0.js");
scriptEngine.ExecuteFile(hpath);
scriptEngine.Execute(@"var precompile = Handlebars.precompile;");

var html = "this is a sample template {{variable}}";
result = scriptEngine.CallGlobalFunction("precompile", html).ToString();`

The above code results in: Common Language Runtime detected an invalid program.
This happens only when running on ASP.NET Version:4.6.1055.0. Worked in previous asp.net versions.

I compiled from the latest source and the problem is now gone.
Maybe its a good idea to publish a new package?

thank you

Mono support

Firstly, thanks for this project. Great work.

I see in your "Future plans" section you hope to get Mono compatibility working, have you had a look at https://github.com/Ormikon/Jurassic which apparently has made the necessary changes to make it work on Mono.

Changes didn't look to major. Would be great to see this merged.

Support porting of Javascript to CSharp through Jurassic - feasible?

Jurassic compile Javascript to IL, has anyone tried to convert the generated IL to cSharp? Currently I am evaluating if Jurassic could be used to support the porting of WebGL-based codes to C#.

Step1 involves wrapping non-3D Canvas dependent javascript functions with Jarassic. This would allow testing of 3D canvas dependent codes in C#.

Step2 (it would be great) if it is possible to compile non-3D canvas javascript codes to DLL. and reference the DLLs with C#

Step3 test the feasibility if the DLL could be converted to Csharp using tool like ILSpy. This would complete the complete porting from Javascript to C#.

Has anyone attempted? Could this be done? There are plenty of libraries support porting of Csharp to Javascript. There are hardly any robust Javascript to CSharp converter. Perhaps I am wrong.

Compilation to a dll

Hi, from what I understand Jurassic creates IL code from the JavaScript code and then runs the IL. This creates some overhead in the first calls of functions. Of course, this is necessary if you want to run scripts at runtime from C#. However, I am interested in using some JavaScript libraries from C# and so the Javascript code would remain the same from one run to the next. So I was wondering, is there some way to save the generated IL as a dll to effectively remove the startup overhead? Cheers

Question : Promise implementation

Hi,

I would like to know if the "Promise" object will be implemented soon...

My implementation seams odd, and does not really work like expected in some cases.

I tried to rewrite and add it directly in Jurassic by trying to follow your design pattern, but reading the ECMA spec is not so easy : I wrote the skeleton (with NotImplementedException), and a way to handle promises that resolve after script execution, but some things remains complex...

My attempt is here : https://github.com/MaitreDede/jurassic/commits/promise

ArrayInstance.ParseArrayIndex() does not check if key is a canonical numeric String

Hi,
first of all, thank you for this great library. It is a good alternative to Jint due to the Javascript code being compiled instead of being interpreted.

It seems Jurassic has a similar bug that Jint had with detecting if a property name is a valid array index - see sebastienros/jint#228.

Consider this Javascript code:

var arr = ['A', 'B'];
console.log(arr['1'] === arr['001']);

arr['002'] = 'C';
console.log(arr.length);

If you run this code in Edge, Firefox, Chrome or Node.js, it will output:

false
2

However, on Jurassic (current master branch), it will output:

true
3

This seems to be caused by ArrayInstance.ParseArrayIndex() not correctly checking if key is a canonical numeric string (a "String representation of a Number that would be produced by ToString").

ECMAScript 2015 says at 6.1.7 (The Object Type):

An integer index is a String-valued property key that is a canonical numeric String (see 7.1.16) and whose numeric value is either +0 or a positive integer ≤ 2^53−1. An array index is an integer index whose numeric value i is in the range +0 ≤ i < 2^32−1.

7.1.16 CanonicalNumericIndexString ( argument ):

The abstract operation CanonicalNumericIndexString returns argument converted to a numeric value if it is a String representation of a Number that would be produced by ToString, or the string "-0".

9.4.2 Array Exotic Objects (similar to ECMAScript 5.1 at 15.4):

NOTE: A String property name P is an array index if and only if ToString(ToUint32(P)) is equal to P and ToUint32(P) is not equal to 2^32−1.

This means that the string "002" shouldn't be a valid array index because
ToString(ToUint32('002')) != '002'.

Thank you!

Social Media Updates?

This isn't an issue, so it can be closed as soon as you see this.
I just wasn't sure how else to message you about if there was a social media account to receive updates about Jurassic, or I just need to pop my head over here every once in a while.

JsProperty, properties enumeration

Hi,

I have followed the sample on the wiki.

In the C# code, I create a new Constructor, then call Construct() to get an instance, that I expose with engine.SetGlobalValue("myobj", objInstance).

On my instance class, I have defined readonly properties :

        [JSProperty(Name = "console", IsConfigurable = false, IsEnumerable = true)]
        public ObjectInstance Console { get { return this.m_core.Console; } }

In this sample, the m_core variable is set in the constructor called by the Construct method.

When I run my script, when I try to read the property, the property is read from the prototype, not from the instance (the m_core variable is null).

Test.zip

If I understand well, if I do a for(var prop in obj), when you make some checks against the property descriptor, but the downside is that it also retreive the property value, that in the case of the prototype is invalid/not readable.

What is the recommanded behavior : return null in case of the prototype ? or does it need a ScriptEngine modification ?

Thanks.

Threading and script compilation

Hi,
I know that Engine is not thread safe, but I would like to know if I can call "Engine.Compile(ScriptSource)" in another thread, and then call "compiledScript.Execute()" on main thread without issues...
Thanks.

Jurassic.dll not strong named signed

Hi Paul,

I see few threads around this but still don't find the strong named signed binary at http://nuget.org. I am using this in a project that uses strong name signing and has lot of dependencies on other projects. Can you refresh the nuget package with a signed binary?

Wiki page "Supported types" does not mention ConcatenatedString

Hi,

the Wiki page Supported types shows which JS type maps to which .NET type. The table contains an entry for JS type string which maps to .NET type System.String.

However, I found that sometimes Jurassic supplies an Jurassic.ConcatenatedString instead of a regular System.String when calling a function, if the JS string has been constructed using the + operator. E.g.:

    ScriptEngine engine = new ScriptEngine();
    engine.SetGlobalFunction("myFunc", new Action<object>(arg =>
    {
        Console.WriteLine(arg.GetType() + ": " + arg.ToString());
    }));

    engine.Execute(@"
myFunc('abc');
myFunc('ab' + 'c');
");

displays:

System.String: abc
Jurassic.ConcatenatedString: abc

I think this could be added to the wiki page to avoid confusion. (When the .NET declares the parameter as string Jurassic automatically converts it to a string, but not when declaring it as object which is sometimes needed if the function should allow parameters of different types.)

Should I add this to the Wiki page?
Thanks!

Unable to get the value of constant field of .NET type

During executing the following code:

var jsEngine = new ScriptEngine
{
    EnableExposedClrTypes = true
};
jsEngine.SetGlobalValue("Math2", typeof(Math));

object result = jsEngine.Evaluate("Math2.PI");

Error occurs:

Unhandled Exception: System.NotSupportedException: Specified method is not supported.
   at System.Reflection.MdFieldInfo.get_FieldHandle()
   at Jurassic.Compiler.DynamicILGenerator.GetToken(FieldInfo field) in c:\temp\jurassic\Jurassic\Compiler\Emit\ILGenerator\DynamicILGenerator.cs:line 476
   at Jurassic.Compiler.DynamicILGenerator.LoadField(FieldInfo field) in c:\temp\jurassic\Jurassic\Compiler\Emit\ILGenerator\DynamicILGenerator.cs:line 1885
   at Jurassic.Compiler.FieldGetterBinder.GenerateStub(ILGenerator generator, Int32 argumentCount) in c:\temp\jurassic\Jurassic\Compiler\Binders\FieldBinders.cs:line 91
   at Jurassic.Compiler.Binder.CreateDelegateCore(Int32 argumentCount) in c:\temp\jurassic\Jurassic\Compiler\Binders\Binder.cs:line 148
   at Jurassic.Compiler.Binder.CreateDelegate(Int32 argumentCount) in c:\temp\jurassic\Jurassic\Compiler\Binders\Binder.cs:line 99
   at Jurassic.Compiler.Binder.Call(ScriptEngine engine, Object thisObject, Object[] arguments) in c:\temp\jurassic\Jurassic\Compiler\Binders\Binder.cs:line 74
   at Jurassic.Library.ClrFunction.CallLateBound(Object thisObject, Object[] arguments) in c:\temp\jurassic\Jurassic\Library\Function\ClrFunction.cs:line 179
   at Jurassic.Library.PropertyAccessorValue.GetValue(ObjectInstance thisObject) in c:\temp\jurassic\Jurassic\Library\Object\PropertyAccessorValue.cs:line 52
   at Jurassic.Library.ObjectInstance.InlineGetPropertyValue(String name, Int32& cachedIndex, Object& cacheKey) in c:\temp\jurassic\Jurassic\Library\Object\ObjectInstance.cs:line 298
   at eval(ScriptEngine , Scope , Object )
   at Jurassic.Compiler.EvalMethodGenerator.Execute() in c:\temp\jurassic\Jurassic\Compiler\MethodGenerator\EvalMethodGenerator.cs:line 119
   at Jurassic.ScriptEngine.Evaluate(ScriptSource source) in c:\temp\jurassic\Jurassic\Core\ScriptEngine.cs:line 621
   at Jurassic.ScriptEngine.Evaluate(String code) in c:\temp\jurassic\Jurassic\Core\ScriptEngine.cs:line 572
   ...

External references?

I have a few unique issues while using Jurassic that have prompted me to ask, is there a way to perform some type of Include or reference within the JS source i am feeding in?

We know we could probably duck-tape it and simply append the script in at run-time but we are hoping to avoid skewing line numbers that way.

Is there already such support?

Problems with ObjectInstance returning reference to C# struct/class

I am not sure if it is appropriate to ask questions here, please close if not.

I have a script as such:

namespace ScriptAPI
{
    public struct SensorData
    {
        public float Distance;
        public string Tag;
    }

    public class Sensor : ObjectInstance
    {
        public readonly string Name;
        public SensorData Data { get; set; }

        public Sensor(ScriptEngine engine, string name)
            : base(engine)
        {
            PopulateFunctions();
            Name = name;
        }

        [JSFunction(Name = "GetData")]
        public SensorData GetData()
        {
            return Data;
        }
    }
}

The Sensor instance has a Data property that is set elsewhere in my C# code and is not meant to be accessible via Javascript, but rather the GetData() method should return the data. (I also have a GetDistance and GetTag shortcut method but left those out for simplicity)

This is the code I use to execute the Javascript:

// Prepare script engine
engine = new ScriptEngine()
{
    EnableExposedClrTypes = true
};

// Data types for robot parts
engine.SetGlobalValue("SensorData", typeof(SensorData));

// Robot
engine.SetGlobalValue("robot", machine);

engine.Execute(Code);

The code variable is the Javascript code and the machine variable is another ObjectInstance that contains arrays of Sensor instances. As soon as I try to run some Javascript that run the GetData() method I get the following exception:

NotImplementedException: Unsupported type: ScriptAPI.SensorData
Jurassic.Compiler.PrimitiveTypeUtilities.ToPrimitiveType (System.Type) <IL 0x00104, 0x00631>
...

I am assuming this is related to the Supported Types section in the wiki, but I thought using engine.SetGlobalValue("SensorData", typeof(SensorData)); would allow me to use the C# type. I've also tried changing SensorData from a struct to a class but there's no difference.

Is there something I'm missing that would allow me to work past this, or do I have to store it as separate variables inside Sensor instead of a container struct?

Thank you in advance!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.