-
Notifications
You must be signed in to change notification settings - Fork 121
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Can't prototype-chain objects defined in .NET #38
Comments
There shouldn't be any limitations on how you construct your prototype chain. Can you post sample code that reproduces the problem? |
Hi Paul, If it should be achievable, I'm almost certain that I'm doing something wrong here. If I want instances of OuterObject to have the prototype InnerObject, where should I 'set' that prototype? I have attempted the following:
I fully expect this to be a hugely obvious thing that I've overlooked :) public class OuterObjectConstructor : ClrFunction
{
public OuterObjectConstructor(ScriptEngine engine)
: base(engine.Function.InstancePrototype, "OuterObject", new OuterObjectInstance(engine.Object.InstancePrototype))
{
}
[JSConstructorFunction]
public OuterObjectInstance Construct(int i)
{
return new OuterObjectInstance(this.InstancePrototype, i);
}
public class OuterObjectInstance : ObjectInstance
{
public OuterObjectInstance(ObjectInstance prototype)
: base(prototype)
{
this.PopulateFunctions();
}
public OuterObjectInstance(ObjectInstance prototype, int i)
: base(prototype)
{
}
[JSFunction(Name = "OuterFunction")]
public string OuterFunction()
{
return "Outer function";
}
} |
Your code looks correct. What makes you think it's wrong? var engine2 = new ScriptEngine();
engine2.SetGlobalValue("O", new OuterObjectConstructor(engine2));
Console.WriteLine("instance: {0}", engine2.Evaluate<string>("var x = new O(); Object.getOwnPropertyNames(x)"));
Console.WriteLine("prototype: {0}", engine2.Evaluate<string>("x = Object.getPrototypeOf(x); Object.getOwnPropertyNames(x)"));
Console.WriteLine("prototype.prototype: {0}", engine2.Evaluate<string>("x = Object.getPrototypeOf(x); Object.getOwnPropertyNames(x)"));
Console.WriteLine("Anything else? {0}", engine2.Evaluate<string>("x = Object.getPrototypeOf(x); x")); Gives this output:
This is exactly what I would expect. The instance itself has no properties defined, the instance prototype has two (including the OuterFunction function), then you have the base object prototype which is shared by all objects. It has toString, etc. |
I think I've discovered the problem; my outerObject contains all the expected properties inherited from innerObject, but outerObject instanceof InnerObject evaluates to false. I think this is because I'm not using the same constructor instance to create the innerObject instance within the OuterObjectConstructor class, so although they are the same class on the .NET side, on the Javascript side they are entirely different objects, just with identical signatures. So my question now is; what is the correct way to provide the OuterObjectConstructor with the correct constructor (or a correct instance) of InnerObject such that instanceof will behave as I expect? Here is the Javascript of what I would like to achieve. Basically; outerObject inherits (prototypes?) from innerObject. var JSInnerObject = function() {
this.innerValue = "my inner value";
};
var JSOuterObject = function() {
this.outerValue = "my outer value";
};
// OuterObject instances prototype InnerObject
JSOuterObject.prototype = new JSInnerObject();
var jsInnerObject = new JSInnerObject();
var jsOuterObject = new JSOuterObject();
console.log(jsInnerObject instanceof JSInnerObject);
console.log(jsOuterObject instanceof JSOuterObject);
console.log(jsOuterObject instanceof JSInnerObject);
console.log(jsOuterObject.innerValue + " - " + jsOuterObject.outerValue); And the output; true
true
true
my inner value - my outer value And here are the C# classes I'm using to achieve the same thing on the .NET side InnerObject using Jurassic;
using Jurassic.Library;
public class InnerObjectConstructor : ClrFunction
{
public InnerObjectConstructor(ScriptEngine engine)
: base(engine.Function.InstancePrototype, "InnerObject", new InnerObjectInstance(engine.Object.InstancePrototype))
{
}
[JSConstructorFunction]
public InnerObjectInstance Construct(int i)
{
return new InnerObjectInstance(this.InstancePrototype, i);
}
}
public class InnerObjectInstance : ObjectInstance
{
public InnerObjectInstance(ObjectInstance prototype)
: base(prototype)
{
this.PopulateFunctions();
}
public InnerObjectInstance(ObjectInstance prototype, int i)
: base(prototype)
{
this["innerValue"] = "my inner value";
}
[JSFunction(Name = "innerFunction")]
public string InnerFunction()
{
return "Inner function";
}
} OuterObject using Jurassic;
using Jurassic.Library;
public class OuterObjectConstructor : ClrFunction
{
public OuterObjectConstructor(ScriptEngine engine)
: base(engine.Function.InstancePrototype, "OuterObject", new OuterObjectInstance(engine.Object.InstancePrototype))
{
}
[JSConstructorFunction]
public OuterObjectInstance Construct(int i)
{
return new OuterObjectInstance(new InnerObjectInstance(this.InstancePrototype, i), i);
}
}
public class OuterObjectInstance : ObjectInstance
{
public OuterObjectInstance(ObjectInstance prototype)
: base(prototype)
{
this.PopulateFunctions();
}
public OuterObjectInstance(ObjectInstance prototype, int i)
: base(prototype)
{
this["outerValue"] = "my outer value";
}
[JSFunction(Name = "OuterFunction")]
public string OuterFunction()
{
return "Outer function";
}
} Setup in Jurassic js = new ScriptEngine();
js.SetGlobalValue("InnerObject", new InnerObjectConstructor(js));
js.SetGlobalValue("OuterObject", new OuterObjectConstructor(js));
js.Evaluate("var innerObject = new InnerObject(0)");
js.Evaluate("var outerObject = new OuterObject(0)");
js.Evaluate("console.log(innerObject instanceof InnerObject)");
js.Evaluate("console.log(outerObject instanceof OuterObject)");
js.Evaluate("console.log(outerObject instanceof InnerObject)");
js.Evaluate("console.log(outerObject.innerValue + \" - \" + outerObject.outerValue)"); And the output true
true
false
my inner value - my outer value After that enormous post, I feel as though I should express my thanks for your excellent library, by the way :) |
The Pseudocode for
So the reason |
Thanks for that Paul. I managed to get the results I expected, but not quite in the way you suggested. I think this is correct though; when instantiating the OuterObjectConstructor object, I pass in the InnerObjectConstructor and use new OuterObjectInstance(innerObjectConstructor.InstancePrototype) to set the instance prototype object for OuterObjectConstructor. When instantiating an OuterObjectInstance with Construct(), I then use this.InstancePrototype as currently. See the C# code below. OuterObject using Jurassic;
using Jurassic.Library;
public class OuterObjectConstructor : ClrFunction
{
public OuterObjectConstructor(ScriptEngine engine, InnerObjectConstructor innerObjectConstructor)
: base(engine.Function.InstancePrototype, "OuterObject", new OuterObjectInstance(innerObjectConstructor.InstancePrototype))
{
}
[JSConstructorFunction]
public OuterObjectInstance Construct(ObjectInstance prot, int i)
{
return new OuterObjectInstance(this.InstancePrototype, i);
}
}
public class OuterObjectInstance : ObjectInstance
{
public OuterObjectInstance(ObjectInstance prototype)
: base(prototype)
{
this.PopulateFunctions();
this["outerValue"] = "my outer value";
}
public OuterObjectInstance(ObjectInstance prototype, int i)
: base(prototype)
{
}
[JSFunction(Name = "OuterFunction")]
public string OuterFunction()
{
return "Outer function";
}
} InnerObject using Jurassic;
using Jurassic.Library;
public class InnerObjectConstructor : ClrFunction
{
public InnerObjectConstructor(ScriptEngine engine)
: base(engine.Function.InstancePrototype, "InnerObject", new InnerObjectInstance(engine.Object.InstancePrototype))
{
}
[JSConstructorFunction]
public InnerObjectInstance Construct(int i)
{
return new InnerObjectInstance(this.InstancePrototype, i);
}
}
public class InnerObjectInstance : ObjectInstance
{
public InnerObjectInstance(ObjectInstance prototype)
: base(prototype)
{
this.PopulateFunctions();
this["innerValue"] = "my inner value";
}
public InnerObjectInstance(ObjectInstance prototype, int i)
: base(prototype)
{
}
[JSFunction(Name = "innerFunction")]
public string InnerFunction()
{
return "Inner function";
}
} Setup code js = new ScriptEngine();
InnerObjectConstructor innerObjectConstructor = new InnerObjectConstructor(js);
OuterObjectConstructor outerObjectConstructor = new OuterObjectConstructor(js, innerObjectConstructor);
js.SetGlobalValue("InnerObject", innerObjectConstructor);
js.SetGlobalValue("OuterObject", outerObjectConstructor);
js.Evaluate("var innerObject = new InnerObject(0)");
js.Evaluate("var outerObject = new OuterObject(0)");
js.Evaluate("console.log(innerObject instanceof InnerObject)");
js.Evaluate("console.log(outerObject instanceof OuterObject)");
js.Evaluate("console.log(outerObject instanceof InnerObject)");
js.Evaluate("console.log(outerObject.innerValue + \" - \" + outerObject.outerValue)"); And the output true
true
true
my inner value - my outer value |
You're right, this is the correct way to do it. Prototypical inheritance is hard! :-) |
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?
The text was updated successfully, but these errors were encountered: