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

FEAT: Implement Symbol.hasInstance for Function.prototype #1751

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

0xe
Copy link
Contributor

@0xe 0xe commented Dec 9, 2024

No description provided.

@0xe 0xe changed the title FEAT: Implement Symbol.hasInstance FEAT: Implement Symbol.hasInstance for Function.prototype Dec 9, 2024
@@ -31,7 +31,8 @@ static void init(Context cx, Scriptable scope, boolean sealed) {
if (cx.getLanguageVersion() >= Context.VERSION_ES6) {
obj.setStandardPropertyAttributes(READONLY | DONTENUM);
}
obj.exportAsJSClass(MAX_PROTOTYPE_ID, scope, sealed);
IdFunctionObject constructor = obj.exportAsJSClass(MAX_PROTOTYPE_ID, scope, sealed);
ScriptRuntimeES6.addSymbolHasInstance(cx, scope, constructor);
Copy link
Collaborator

Choose a reason for hiding this comment

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

do we need a language version check here (cx.getLanguageVersion() >= Context.VERSION_ES6)

+ "};\n"
+ "var g = {};\n"
+ "`${f.hasOwnProperty(Symbol.hasInstance)}:${g.hasOwnProperty(Symbol.hasInstance)}`";
Utils.runWithAllOptimizationLevels(
Copy link
Collaborator

Choose a reason for hiding this comment

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

maybe you can use assertWithAllOptimizationLevelsES6 here

@@ -265,6 +268,11 @@ protected void fillConstructorProperties(IdFunctionObject ctor) {

@Override
protected void initPrototypeId(int id) {
if (id == SymbolId_hasInstance) {
initPrototypeValue(id, SymbolKey.HAS_INSTANCE, makeHasInstance(), 0x0F);
Copy link
Collaborator

Choose a reason for hiding this comment

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

please use the real attributes instead of 0x0F for readability

int instanceIdInfo, Scriptable scope, Object value, int attr) {
if (instanceIdInfo == SymbolId_hasInstance) {
return buildDataDescriptor(scope, value, attr, SymbolKey.HAS_INSTANCE.toString(), 1);
} else {
Copy link
Collaborator

Choose a reason for hiding this comment

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

else not requred if you return before

@Override
protected int findPrototypeId(Symbol k) {
if (SymbolKey.HAS_INSTANCE.equals(k)) return SymbolId_hasInstance;
else return 0;
Copy link
Collaborator

Choose a reason for hiding this comment

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

else not required

*
* @return ScriptableObject
*/
ScriptableObject buildDataDescriptorHelper(
Copy link
Collaborator

@rbri rbri Dec 9, 2024

Choose a reason for hiding this comment

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

please add @OverRide... same for the next and maybe you can also copy the comment

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 these as they don't actually solve the problem with name and length attributes.

ScriptableObject desc = new NativeObject();
ScriptRuntime.setBuiltinProtoAndParent(desc, scope, TopLevel.Builtins.Object);
desc.defineProperty("value", value, EMPTY);
desc.setCommonDescriptorProperties(attributes, true);
desc.setCommonDescriptorProperties(attributes, true, name, length);
Copy link
Collaborator

Choose a reason for hiding this comment

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

is it a good idea to introduce this new function with two (at least for me magic parameters) including all the forwarder for the old calls if we need the additional functionality only at one place?

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 these as they don't actually solve the problem with name and length attributes.

}
throw ScriptRuntime.typeErrorById(
"msg.instanceof.bad.prototype", getFunctionName());
} else {
Copy link
Collaborator

Choose a reason for hiding this comment

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

else not needed because of the throw before

String name = null;
if (valueArray[nameSlot] instanceof String)
name = (String) valueArray[nameSlot];
else if (valueArray[nameSlot] instanceof SymbolKey) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Should be Symbol and not SymbolKey I think

@0xe 0xe requested review from rbri and andreabergia December 12, 2024 01:24
@0xe 0xe force-pushed the scratch/satish/function-prototype-hasinstance branch 2 times, most recently from 732d044 to 5aa5f4a Compare December 12, 2024 01:38
@0xe 0xe marked this pull request as ready for review December 12, 2024 01:40
@0xe 0xe force-pushed the scratch/satish/function-prototype-hasinstance branch 2 times, most recently from 013b02b to 680b2a2 Compare December 12, 2024 05:13
@rbri
Copy link
Collaborator

rbri commented Dec 12, 2024

@0xe thanks for taking care of my comments

Copy link
Collaborator

@gbrail gbrail left a comment

Choose a reason for hiding this comment

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

Thanks for working on this!

Unless I'm missing something (and in JavaScript I'm usually missing something) there is an existing pattern to add a new symbol-keyed property to a native object -- see the implementation of "SymbolId_Iterator" in NativeString, for instance. This change seems to be doing some of that and some of something else. Can you please try to do it that way? Thanks!

@@ -46,4 +46,15 @@ public static void addSymbolUnscopables(
ScriptableObject.putProperty(unScopablesDescriptor, "writable", false);
constructor.defineOwnProperty(cx, SymbolKey.UNSCOPABLES, unScopablesDescriptor, false);
}

/** Registers the symbol <code>[Symbol.hasInstance]</code> on the given constructor function. */
public static void addSymbolHasInstance(
Copy link
Collaborator

Choose a reason for hiding this comment

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

I get what you're trying to do here, but I don't think that we should add a new function to a "ScriptRuntime" class that we're only going to use in one place. I'd suggest that instead of all this, you just call "defineProperty" on the "constructor" object back in BaseFunction and pass it the bitset (I'll put a comment there too).

Context cx = Context.getCurrentContext();
Object hasInstance = ScriptRuntime.getObjectElem(this, SymbolKey.HAS_INSTANCE, cx);
if (hasInstance instanceof Callable) {
return (boolean)
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm not sure that I know all the semantics of just casting to boolean here, but if I know anything about JavaScript, it's 90% edge cases.

So, since the function here could literally return anything, you should use "ScriptRuntime.toBoolean" here, which can cast any number of objects correctly to a "boolean".

obj.exportAsJSClass(MAX_PROTOTYPE_ID, scope, sealed);
IdFunctionObject constructor = obj.exportAsJSClass(MAX_PROTOTYPE_ID, scope, sealed);
if (cx.getLanguageVersion() >= Context.VERSION_ES6) {
ScriptRuntimeES6.addSymbolHasInstance(cx, scope, constructor);
Copy link
Collaborator

Choose a reason for hiding this comment

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

I was going to ask why you don't just call "defineProperty" here, but actually I'm wondering why we need this at all -- adding the new symbol case in the other places that you added it should be enough for this to work.


if (cx != null) {
Scriptable scope = this.getParentScope();
obj =
Copy link
Collaborator

Choose a reason for hiding this comment

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

It's great to use a Lambda function here, but since this class still inherits from "IdScriptableObject", you should be able to make it work by adding a case to the switch in the "exec" function for your new "SymbolId_hasInstance" case.

@0xe
Copy link
Contributor Author

0xe commented Dec 13, 2024

Thanks for working on this!

Unless I'm missing something (and in JavaScript I'm usually missing something) there is an existing pattern to add a new symbol-keyed property to a native object -- see the implementation of "SymbolId_Iterator" in NativeString, for instance. This change seems to be doing some of that and some of something else. Can you please try to do it that way? Thanks!

Thank you. I will try to address these in the coming days.

@0xe 0xe force-pushed the scratch/satish/function-prototype-hasinstance branch from 680b2a2 to 5e7ebb0 Compare December 18, 2024 08:38
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.

4 participants