Skip to content
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

Implement Operator Overloading #893

Merged
merged 10 commits into from May 13, 2021
Merged

Implement Operator Overloading #893

merged 10 commits into from May 13, 2021

Conversation

KurtGokhan
Copy link
Contributor

Normally, Ecmascript does not allow operator overloading and the specs are pretty clear on how to handle operators. But since we are running on C#, we may as well use operators that are defined on the C# side. So I added an option for that. The option is off by default.

Binary operators will try to find a C# side overloaded operator first and use it. If it cannot find, will fall back to Ecmascript behavior. (I forgot to do it for unary so the PR is draft for now. Will do asap.)
Also, when this option is turned on, the default type converter will try to find implicit/explicit cast operator and use it as a last resort.

Copy link
Collaborator

@lahma lahma left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A great addition with a perfect use case demonstrated by the test, thanks for preparing this! I had just one comment of possible performance impact.

@@ -17,6 +30,85 @@ private JintBinaryExpression(Engine engine, BinaryExpression expression) : base(
_right = Build(engine, expression.Right);
}

protected abstract object EvaluateBinaryExpression();

protected override object EvaluateInternal()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this part is a bit iffy for me, this is on performance hot path and now introduces another layer of indirection

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

might require sealing, also minimize code size for non-default case, i.e. put if if (!_engine.Options._IsOperatorOverloadingAllowed) first and then call separate method for doing the operator overloading version?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed the calls to virtual methods. Please check now.

@KurtGokhan KurtGokhan marked this pull request as ready for review May 12, 2021 17:42
@KurtGokhan
Copy link
Contributor Author

Done all (most) binary and unary methods.

I did not implement operator overloading for true and false operators. I guess that operator can be used in a context where Ecmascript spec asks to convert the value to boolean. For example if(here) or here && .... I would implement it but I don't know where to start yet. Also seems like it may affect performance if not done right. I can do it in the future as another PR.

@lahma lahma requested a review from sebastienros May 13, 2021 03:07
@lahma
Copy link
Collaborator

lahma commented May 13, 2021

Looking good, @sebastienros please take a look.

}
else
{
if (argType.GetMethods(BindingFlags.Public | BindingFlags.Static)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a test for that and the scrore thing to understand what is happening?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Basically this method finds the best match when there are multiple methods with the same name and number of arguments. Before my changes, this code was checking for a method that was perfectly matching to the passed arguments. If it could not find a perfect match, it would choose the first method it encountered (and this would cause an error in most cases).

My code gives each method a penalty based on how close each argument is to the parameter it is going to be casted to. Score is divided into different levels and assigned with a number in base 10. This is kind of a heuristic algorithm.

This is demonstrated in MethodAmbiguityTests.cs I just added.

To be honest, this became deeper than I initially thought. It deserves its own PR. I did not want to go overkill here. C# compiler can resolve the best method even better. But that is probably only possible with static analysis. Also when there are multiple matching methods, C# calls it an ambiguity and does not let you compile.

Also this can become a breaking change for people who use interop a lot and have methods with same names. Perfect match will still be same but when there are multiple non-matching methods, people may experience different results with this change. But in my opinion, before this change, the result would be undefined behavior.

@sebastienros sebastienros merged commit 517900f into sebastienros:dev May 13, 2021
@KurtGokhan KurtGokhan deleted the operator-overloading branch May 14, 2021 11:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants