-
-
Notifications
You must be signed in to change notification settings - Fork 400
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 the instanceof
operator
#796
Comments
Hi, I think I can take on this as my first issue. Ill be working on this if it's okay. |
I have implemented part of the instanceof operator functionality based on given specification to the best of my knowledge and skill. With that said, there are certain tasks, or rather points in the specification that are somehow difficult to understand and therefore interpret correctly within the project and I believe that the best course of action in this situation is to consult these with someone. I am not sure wheter it is customary to continue a conversation like this in the issue thread, or move it elsewhere. How should I proceed with this? Thank you. |
We can continue the conversation here or on the PR (when created) |
Understood, I believe a verbose comment will be most fitting considering the state of the problem. In each step, ECMAScript spec point will be provided, with possible code solution and comments if necessary. Spec: InstanceofOperator ( V, target ): Spec: 1. If Type(target) is not Object, throw a TypeError exception. if !y.is_object() { /* throw */ } Spec: 2. Let instOfHandler be ? GetMethod(target, @@hasInstance). let key: RcSymbol = interpreter.well_known_symbols().has_instance_symbol();
let instance_of_handler: Option<Property> = x.get_property(key); Comments: Spec: 3. If instOfHandler is not undefined, then if instance_of_handler.is_some() { ... } Comments: Spec: 4. If IsCallable(target) is false, throw a TypeError exception. if !y.as_object().unwrap().is_callable() { /* throw */ } Comments: Spec: 5. Return ? OrdinaryHasInstance(target, V). Spec: OrdinaryHasInstance ( target, V ) Spec: 1. If IsCallable(target) is false, return false. if !y.as_object().unwrap().is_callable() { /* return false */ } Spec: 2. If target has a [[BoundTargetFunction]] internal slot, then Spec: 3. If Type(V) is not Object, return false. if !x.is_object() { /* return false */ } Spec: 4. Let P be ? Get(target, "prototype"). let borrowed_y: GcCellRef<Object> = y.as_object().unwrap();
let prototype_of_y = borrowed_y.prototype_instance(); Comments: Spec: 5. If Type(P) is not Object, throw a TypeError exception. if !prototype_of_y.is_object() { /* throw */ } Spec: 6. Repeat, The parts that confuse me the most are @@hasinstance symbol, recursive call of InstanceofOperator() and the prototype chain. I understand that if these are decided to be trivial, or easily solveable by someone else, this issue will be unassigned from me. |
Good catch! This is most definetly a bug, we should return the
Yes. but when we implement it is should work. (does not have to be in this issue/PR).
Yes. if its known than it is we can use Fist I think its best to implement the fn get_method<K>(&self, context: &mut Context, key: K) -> Result<Option<GcObject>> {
let key = key.into();
let value = self.get(&key);
if value.is_null_or_undefined() {
return Ok(None);
}
match value.as_object() {
Some(object) if object.is_callable() => {
Ok(object)
}
_ => Err(context.construct_type_error("..."))
}
} The fn ordinary_has_instance(&self, context: &mut Context, object: &GcObject) -> Result<bool> {
if !self.is_callable() {
return Ok(false);
}
// TODO: If C has a [[BoundTargetFunction]] internal slot, then
// Let BC be C.[[BoundTargetFunction]].
// Return ? InstanceofOperator(O, BC).
if let Some(object) = value.as_object() {
if let Some(prototype) = self.get("prototype".into()).as_object() {
let mut object = object.get_prototype_of();
while let Some(object_prototype) = o.as_object() {
if GcObject::equals(&prototype, &object_prototype) { // the equals function is used for samevalue for objects
return Ok(true);
}
object = object_prototype.into();
}
Ok(false) // if its not an object its null so we return. step 6.b
} else {
Err(context.construct_type_error("..."))
}
} else {
return Ok(false);
}
} and the insteanceofOperator: if let Some(object) = target.as_object() {
let key = interpreter.well_known_symbols().has_instance_symbol();
match object.get_method(key)? {
Some(instance_of_handler) => {
Ok(instance_of_handler.call(target, &[v], context)?.to_boolean().into())
}
None if target.is_callable() => {
Ok(target.ordinary_has_instance(context, v)?.into())
}
None => {
context.throw_type_error("...")
}
}
} else {
context.throw_type_error("...")
} NOTE: The type errors should have proper error messages. |
Thank you for the reply, it helped me understand the interpretation of the specification within the project much better.
|
Yes. this is correct.
Yes. after some refactoring made some mistakes, the second should be: fn ordinary_has_instance(&self, context: &mut Context, value: &Value) -> Result<bool> {
if !self.is_callable() {
return Ok(false);
}
// TODO: If C has a [[BoundTargetFunction]] internal slot, then
// Let BC be C.[[BoundTargetFunction]].
// Return ? InstanceofOperator(O, BC).
if let Some(object) = value.as_object() {
if let Some(prototype) = self.get("prototype".into()).as_object() {
let mut object = object.get_prototype_of();
while let Some(object_prototype) = object.as_object() {
if GcObject::equals(&prototype, &object_prototype) { // the equals function is used for samevalue for objects
return Ok(true);
}
object = object_prototype.into();
}
Ok(false) // if its not an object its null so we return. step 6.b
} else {
Err(context.construct_type_error("..."))
}
} else {
return Ok(false);
}
} and also we have to change if let Some(object) = target.as_object() {
let key = interpreter.well_known_symbols().has_instance_symbol();
match object.get_method(key)? {
Some(instance_of_handler) => {
Ok(instance_of_handler.call(target, &[v], context)?.to_boolean().into())
}
None if target.is_callable() => {
Ok(target.ordinary_has_instance(context, v)?.into())
}
None => {
context.throw_type_error("...")
}
}
} else {
context.throw_type_error("...")
}
Yes. |
You can also discuss on boa's discord, and put the summary here. And you can submit a PR draft with the stuff you have :) . Perhaps the issue can be solved in a couple of PRs |
ECMASCript feature
We currently don't implement the
instanceof
ECMAScript operator, which would be a nice addition, since it's generating many panics in the ECMAScript test suite.The specification of the
instanceof
operator can be found here.Example code
Some example code can be found in MDN. But as an example:
This code should now work and give the expected result:
The expected output is
true
.The implementation code that needs a change is located in here.
The text was updated successfully, but these errors were encountered: