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

Decouple Object from Value #577

Closed
HalidOdat opened this issue Jul 19, 2020 · 18 comments
Closed

Decouple Object from Value #577

HalidOdat opened this issue Jul 19, 2020 · 18 comments
Assignees
Labels
discussion Issues needing more discussion E-Hard Hard difficulty problem execution Issues or PRs related to code execution help wanted Extra attention is needed performance Performance related changes and issues technical debt
Milestone

Comments

@HalidOdat
Copy link
Member

HalidOdat commented Jul 19, 2020

What is the problem with the current implementation?
The problem is that we treat Value as an Object which is not always the case. for example Value has a .set_field() which sets an object field, this does not make sense for a couple of reasons:

  • what if Value is not an object should calling .set_field panic?, should it ignore it? should it return an option or result? or something else? (we ignore non object values which can become a really hard bug to debug), as we can see it does not make sense for Value to have Object behaviour
  • This also forces us to do checks if the value is an object.

If we need object behaviour we can call as_object method.

Changes:

  • Move all object behaivour from Value to Object
  • Places were we need (or places were the type is only an object for example: the global object) should be GcObjects
  • function like .to_object should return a GcObject not Value done

Doing these thing should remove unnecessary checks and make the API more intuitive

Any suggestions on how to improve this?

@HalidOdat HalidOdat added help wanted Extra attention is needed technical debt discussion Issues needing more discussion execution Issues or PRs related to code execution labels Jul 19, 2020
@HalidOdat HalidOdat added this to the v0.10.0 milestone Jul 19, 2020
@HalidOdat HalidOdat self-assigned this Jul 19, 2020
@HalidOdat HalidOdat added the performance Performance related changes and issues label Jul 19, 2020
@HalidOdat HalidOdat added the E-Hard Hard difficulty problem label Jul 24, 2020
@RageKnify
Copy link
Contributor

RageKnify commented Sep 14, 2020

Hey @HalidOdat I found this interesting and am trying to tackle it.
To ensure I'm following your "vision", methods from builtins like Array would stop receiving &Value as this and instead receive &Object/&mut Object, right?

Ps: Also, functions like Array::new_array would change to return Result<GcObject> or Result<Object> rather than Result<Value>, or no?

@jasonwilliams
Copy link
Member

@HalidOdat how are you handling boxing, unboxing of primitive values? I think I left set_field and get_field on value so things like “hello world ”.trim() would still work

@HalidOdat
Copy link
Member Author

HalidOdat commented Sep 15, 2020

@HalidOdat how are you handling boxing, unboxing of primitive values? I think I left set_field and get_field on value so things like “hello world ”.trim() would still work

We store some primitives on the stack (number, boolean, undefined, null) and string, bigint, symbol on the heap with Rcs (since they can't have cycles, less work for the gc) and object with Gc. we discussed this in #465 .

Why would removing .set_field and .get_field cause “hello world ”.trim() to break?

@jasonwilliams
Copy link
Member

jasonwilliams commented Sep 15, 2020

Ok sounds good.

Why would removing .set_field and .get_field cause “hello world ”.trim() to break?

I think originally the unboxing happened inside of get_field() where the value was checked and upgraded to an object of it was a primitive. .trim() would have called get_field(“trim”)

I think we refactored quite a few times since this though so you can ignore this.

@HalidOdat
Copy link
Member Author

HalidOdat commented Sep 15, 2020

Hey @HalidOdat I found this interesting and am trying to tackle it.
To ensure I'm following your "vision", methods from builtins like Array would stop receiving &Value as this and instead receive &Object/&mut Object, right?

Changing this from &Value to anything else wont work, since they would not match the NativeFunction signature, and we should not change the NativeFunction to take object since this can be some value that is not an object.

We need to the check as described in the spec for when this is not a object for example: Array.prototype.push

  1. Let O be ? ToObject(this value).
  2. Let len be ? LengthOfArrayLike(O).

It calls .to_object on this which converts it to a object (if it is not already), so we handle the case for when this is not an object but .set_field and .get_field does not and just ignore it if it isn't an object, this is not spec compliant.
There is also another problem that to_object returns a Result<Value> instead of Result<GcObject>, there are many places where this happens and that's why is why it's hard to remove them directly (generates ~1000 errors). Many PRs I did were to make this transition easier. So the way I would suggest on doing this, would be in small incremental changes, doing it all at once is very hard. For example making a PR so .to_object returns a Result<GcObject> and another PR fixing a something else and so on.

Ps: Also, functions like Array::new_array would change to return Result<GcObject> or Result<Object> rather than Result<Value>, or no?

If it is a helper function then, yes it should.

@RageKnify
Copy link
Contributor

Yeah, I started at .to_object, I'll try to make it in small stages like you said.

One thing I was wondering, would the global_obj in Realm become an Object/GcObject or would it remain a Value?
And if it does change, which would be more aproppriate?

@HalidOdat
Copy link
Member Author

One thing I was wondering, would the global_obj in Realm become an Object/GcObject or would it remain a Value?
And if it does change, which would be more aproppriate?

It should be GcObject since it can be referenced, GcObject is like Rc<...> (but it handles cyclic references) and the global object can be referenced form anywhere so we need to store it as such.

If it is referenced it should be GcObject, but if it is owned it's better to store it as Object

RageKnify added a commit to RageKnify/boa that referenced this issue Sep 24, 2020
Start work on boa-dev#577
Value::new_object_from_prototype becomes Object::new_object_from_prototype

Introduce some small changes in other files to accommodate for the change
in API, I assume once boa-dev#577 is finished everything will be cleaned up
@RageKnify
Copy link
Contributor

I have created my PR, #712, I'll leave a note here as to how this should also eventually be done since I did it for new_object_with_prototype:

  • Value::new_object -> Object::new_object

@RageKnify
Copy link
Contributor

Hey @HalidOdat any idea on what I should try to change next? I was trying to change Value::new_object to return GcObject, which should probably also move to Object, but it would require a lot of changes in other places. So I wonder if there's anything smaller you have in mind.

@HalidOdat
Copy link
Member Author

Hey @HalidOdat any idea on what I should try to change next? I was trying to change Value::new_object to return GcObject, which should probably also move to Object, but it would require a lot of changes in other places. So I wonder if there's anything smaller you have in mind.

I think it would be a good idea to wait until #722 lands since it also tackles this issue in a way and will generate a lot of merge conflicts.

@jasonwilliams
Copy link
Member

@HalidOdat from my work on #854 it would be good to stack-allocate Values, but as some of them hold RCObjects its not possible. Should there be some pointer which through a method gives back the object?

@HalidOdat
Copy link
Member Author

@HalidOdat from my work on #854 it would be good to stack-allocate Values, but as some of them hold RCObjects its not possible.

There is a problem with objects being stack allocated, many operations (eq, some object methods, object internal methods) require to know the exact location in memory which means we can move the objects once there created (say for example pop two and push one in the case of #854), if we do something will break.

Should there be some pointer which through a method gives back the object?

Sorry, could you explain a bit more?

@Razican Razican modified the milestones: v0.11.0, v0.12.0 Jan 11, 2021
@RageKnify
Copy link
Contributor

Hey @HalidOdat I got started on changing the global object to a GcObject rather than Value. One thing I ran into is that there are some methods of GcObject that receive &mut self, I think it stayed like that from when they were methods of Object, do you think we should keep it to signal mutability or change it to just &self?

@sphinxc0re
Copy link
Contributor

Screenshot from 2021-01-28 11-54-16

In Firefox, setting a field on a Number is just ignored.

@RageKnify
Copy link
Contributor

In Firefox, setting a field on a Number is just ignored.

boa seems to match that behaviour for me, was there a situation where it seemed to not match @sphinxc0re ?

@sphinxc0re
Copy link
Contributor

No, I was just commenting on the "should set_field panic" part. It shouldn't

@Razican
Copy link
Member

Razican commented Feb 5, 2022

I don't think this will be ready for 0.14, so I'm moving it out.

@Razican Razican modified the milestones: v0.14.0, v0.15.0 Feb 5, 2022
bors bot pushed a commit that referenced this issue Feb 24, 2022
This Pull Request is related to #577 .

It changes the following:
- Remove `JsValue::set_field`
- Remove `JsValue::set_property`
- Remove almost all uses of `JsValue::get_field`
- Use `.get_v()` instead of `get_field` according to spec in `serialize_json_property`
- Remove `Array::new_array()`
- Remove `Array::add_to_array_object()`
bors bot pushed a commit that referenced this issue Feb 26, 2022
This Pull Request is related to #577 .

It changes the following:
- Remove `JsValue::set_field`
- Remove `JsValue::set_property`
- Remove almost all uses of `JsValue::get_field`
- Use `.get_v()` instead of `get_field` according to spec in `serialize_json_property`
- Remove `Array::new_array()`
- Remove `Array::add_to_array_object()`
bors bot pushed a commit that referenced this issue Mar 3, 2022
This PR is also related to #577 

Changes:
- Implements `IteratorValue` (`IteratorResult::value()`)
- Implements `IteratorComplete` (`IteratorResult::complete()`)
- Implements `IteratorStep` (`IteratorRecord::step()`)
- Makes  `IteratorNext` (`IteratorRecord::next()`) spec compliant
- Deprecates/removes `JsValue::get_field()`.
@Razican Razican modified the milestones: v0.15.0, v0.16.0 Jun 1, 2022
@Razican Razican modified the milestones: v0.16.0, v0.17.0 Sep 19, 2022
@HalidOdat
Copy link
Member Author

Many refactors/changes have been done since this issue was created and they have been resolved. So closing this :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
discussion Issues needing more discussion E-Hard Hard difficulty problem execution Issues or PRs related to code execution help wanted Extra attention is needed performance Performance related changes and issues technical debt
Projects
None yet
Development

No branches or pull requests

6 participants