Commit graph

71 commits

Author SHA1 Message Date
Idan Horowitz
795786387b LibJS: Remove the NativeProperty mechanism from LibJS
These were an ad-hoc way to implement special behaviour when reading or
writing to specific object properties. Because these were effectively
replaced by the abillity to override the internal methods of Object,
they are no longer needed.
2021-07-07 21:47:22 +01:00
Idan Horowitz
a6b8291a9b LibJS: Add define_direct_property and remove the define_property helper
This removes all usages of the non-standard define_property helper
method and replaces all it's usages with the specification required
alternative or with define_direct_property where appropriate.
2021-07-06 14:20:30 +01:00
Idan Horowitz
6787e86a3a LibJS: Add and use the CreateNonEnumerableDataPropertyOrThrow AO 2021-07-06 14:20:30 +01:00
Andreas Kling
77fa33e61a LibJS: Finish implementing mapped arguments exotic objects :^)
Now that the Object rewrite is in place, we have enough tools to
implement the mapped `arguments` propreties according to spec.

The basic mechanism is that the `arguments` object installs a hidden
parameter mapping object that property accesses get filtered through.
This is how accessing numeric properties on `arguments` are proxied
to the named identifier in the function scope.

When `arguments` is instantiated, getters and setters are created
for all the numeric properties on the object that correspond to
function arguments. These getters and setters can be deleted from the
object. This is all pretty intricate, so refer to the spec for details.

Note that the `arguments` object itself is still lazily instantiated
on first access within a function. This is non-conforming, and we'll
have to revisit this once we get around to improving function calls.
2021-07-05 02:38:31 +02:00
Linus Groh
cb20baebae LibJS: Make ObjectPrototype an immutable prototype exotic object
To make this happen, this patch implements the SetImmutablePrototype
abstract operation (as a method on Object) and then overrides
[[SetPrototypeOf]] on ObjectPrototype.
2021-07-05 00:53:57 +01:00
Linus Groh
9555ca99a0 LibJS: Remove unnecessary value_or() from get()
Object::get() never returns an empty value anymore, as per the spec, so
having a value_or() fallback is no longer needed.
2021-07-05 00:03:25 +02:00
Linus Groh
09bd5f8772 LibJS: Rewrite most of Object for spec compliance :^)
This is a huge patch, I know. In hindsight this perhaps could've been
done slightly more incremental, but I started and then fixed everything
until it worked, and here we are. I tried splitting of some completely
unrelated changes into separate commits, however. Anyway.

This is a rewrite of most of Object, and by extension large parts of
Array, Proxy, Reflect, String, TypedArray, and some other things.

What we already had worked fine for about 90% of things, but getting the
last 10% right proved to be increasingly difficult with the current code
that sort of grew organically and is only very loosely based on the
spec - this became especially obvious when we started fixing a large
number of test262 failures.

Key changes include:

- 1:1 matching function names and parameters of all object-related
  functions, to avoid ambiguity. Previously we had things like put(),
  which the spec doesn't have - as a result it wasn't always clear which
  need to be used.
- Better separation between object abstract operations and internal
  methods - the former are always the same, the latter can be overridden
  (and are therefore virtual). The internal methods (i.e. [[Foo]] in the
  spec) are now prefixed with 'internal_' for clarity - again, it was
  previously not always clear which AO a certain method represents,
  get() could've been both Get and [[Get]] (I don't know which one it
  was closer to right now).
  Note that some of the old names have been kept until all code relying
  on them is updated, but they are now simple wrappers around the
  closest matching standard abstract operation.
- Simplifications of the storage layer: functions that write values to
  storage are now prefixed with 'storage_' to make their purpose clear,
  and as they are not part of the spec they should not contain any steps
  specified by it. Much functionality is now covered by the layers above
  it and was removed (e.g. handling of accessors, attribute checks).
- PropertyAttributes has been greatly simplified, and is being replaced
  by PropertyDescriptor - a concept similar to the current
  implementation, but more aligned with the actual spec. See the commit
  message of the previous commit where it was introduced for details.
- As a bonus, and since I had to look at the spec a whole lot anyway, I
  introduced more inline comments with the exact steps from the spec -
  this makes it super easy to verify correctness.
- East-const all the things.

As a result of all of this, things are much more correct but a bit
slower now. Retaining speed wasn't a consideration at all, I have done
no profiling of the new code - there might be low hanging fruits, which
we can then harvest separately.

Special thanks to Idan for helping me with this by tracking down bugs,
updating everything outside of LibJS to work with these changes (LibWeb,
Spreadsheet, HackStudio), as well as providing countless patches to fix
regressions I introduced - there still are very few (we got it down to
5), but we also get many new passing test262 tests in return. :^)

Co-authored-by: Idan Horowitz <idan.horowitz@gmail.com>
2021-07-04 22:07:36 +01:00
Idan Horowitz
e480d69130 LibJS: Bring ArrayCreate and ArrayConstructor closer to spec
Specifically, this now explicitly takes the length, adds missing
exceptions checks to calls with user-supplied lengths, takes and uses
the prototype argument, and fixes some spec non-conformance in
ArrayConstructor and its native functions around the use of ArrayCreate
2021-07-04 00:51:43 +01:00
Andreas Kling
1bd52ce789 LibJS: Stop qualifying AK::Function
Now that JS function objects are JS::FunctionObject, we can stop
qualifying AK::Function and just say "Function" everywhere. Nice. :^)
2021-06-27 22:40:49 +02:00
Andreas Kling
ba9d5c4d54 LibJS: Rename Function => FunctionObject 2021-06-27 22:36:04 +02:00
Linus Groh
dbda5a9a4c LibJS: Move install_error_cause() from Object to Error
This is only used by Error and its subclasses, so it doesn't need to be
available to all objects.
2021-06-26 19:06:55 +01:00
davidot
83dd0164b2 Revert "LibJS: Fix this_value in native setters and getters"
This reverts commit f102b563
The reverted to behavior is not correct for example with a double proxy
But this change lead to problems with DOMNodes
2021-06-26 18:16:53 +01:00
davidot
19f505d320 LibJS: Fix propagation of setters and getters from prototypes
If we define a property with just a setter/getter (not both) we must:
- take the previous getter/setter if defined on the actual object
- overwrite the other to nullptr if it is from a prototype
2021-06-26 18:16:53 +01:00
Andreas Kling
c2ad599783 LibJS: Rename CallFrame => ExecutionContext
This struct represents what the ECMAScript specification calls an
"execution context" so let's use the same terminology. :^)
2021-06-24 19:28:00 +02:00
davidot
f102b56345 LibJS: Fix this_value in native setters and getters
This fixes getting values from double proxies:
var p = new Proxy(new Proxy([], {}), {});
p.length
2021-06-22 20:49:28 +01:00
davidot
91de1135a5 LibJS: Fix a number of regressions in the test262 tests
In get_own_properties:
Entries which are deleted while iterating need to be skipped

In PropertyDescriptor::from_dictionary
If the getter/setter is undefined it should still mark it as present
2021-06-22 20:49:28 +01:00
davidot
733e8472fa LibJS: Make put_own_property_by_index closer to spec
Most of the code is taken from put_own_property however the attributes
  need to be handled slightly differently it seems
2021-06-22 20:49:28 +01:00
davidot
15edad8202 LibJS: Make define_property always throw if specified
Now put uses is_strict_mode to determine define_property should throw
2021-06-22 20:49:28 +01:00
davidot
16b87b85e3 LibJS: Add parameter to delete_property since we need DeleteOrThrow 2021-06-22 20:49:28 +01:00
Ali Mohammad Pur
eb5c92d4e2 LibJS: Don't assert for empty reciever if AllowSideEffects::No is given
This parameter is only used if AllowSideEffects::Yes, so there's no
reason to pass anything to it if that's not used.
2021-06-22 00:26:25 +04:30
Andreas Kling
f86e241699 LibJS: Object.getOwnPropertyNames() should enumerate String's .length
We were incorrectly aborting property name enumeration after generating
names for all the indexable properties in the underlying string.
2021-06-19 11:46:08 +02:00
Idan Horowitz
b6a74b6bd9 LibJS: Use existing attributes if any are missing in the new descriptor
The specification defines that we should only change attributes that
exist in the incoming descriptor, but since we currently just overwrite
the existing descriptor with the new one, we can just set the missing
attributes to the existing values manually.
2021-06-18 10:22:09 +02:00
Idan Horowitz
dcb55db99b LibJS: Replace boolean without_side_effects parameters with an enum 2021-06-17 16:52:15 +02:00
Idan Horowitz
a85a95fe8d LibJS: Stop overwriting existing accessors in Object::define_property 2021-06-17 13:20:18 +01:00
Idan Horowitz
b9d9187feb LibJS: Automatically & lazily coerce PropertyNames into numbers
This commit expands on 5eef07d232 by
automatically trying to coerce Type::String PropertyNames into numbers
when a caller checks if the PropertyName is_number/is_string.
This has several benefits:
 - We no longer have to duplicate the number coercion code to every
   function that accepts a PropertyNumber. (Or more likely, forget to.)
 - This keeps the lazy nature of only doing the coercion when and if
   there is a semantic difference to the different PropertyName types,
   which means this shouldnt cause any performance drop.
 - Since this coercion changes the state of the PropertyName itself the
   result is essentially cached and can speed up any repeat uses of the
   same PropertyName instance.
2021-06-16 22:54:48 +01:00
Idan Horowitz
623eadb44e LibJS: Stop overwriting existing indexed accessors in define_accessor
The previous implementation only checked the shape, ignoring existing
accessors when using number property names.
2021-06-16 22:54:48 +01:00
Idan Horowitz
6352a33ed2 LibJS: Respect Object::get's without_side_effects parameter for numbers 2021-06-16 22:54:48 +01:00
Linus Groh
317b88a8c3 LibJS: Replace Object's create_empty() with create() taking a prototype
This now matches the spec's OrdinaryObjectCreate() across the board:
instead of implicitly setting the created object's prototype to
%Object.prototype% and then in many cases setting it to a nullptr right
away, it now has an 'Object* prototype' parameter with _no default
value_. This makes the code easier to compare with the spec, very clear
in terms of what prototype is being used as well as avoiding unnecessary
shape transitions.

Also fixes a couple of cases were we weren't setting the correct
prototype.

There's no reason to assume that the object would not be empty (as in
having own properties), so let's follow our existing pattern of
Type::create(...) and simply call it 'create'.
2021-06-16 22:49:04 +01:00
Idan Horowitz
07992c8da6 LibJS: Throw when delete-ing a non-configurable property in strict mode 2021-06-16 12:57:48 +01:00
Idan Horowitz
c619ad4fec LibJS: Allow no-op define property calls on non-configurable objects
This brings us slightly closer to the specification's 10.1.6.3
ValidateAndApplyPropertyDescriptor.
2021-06-16 12:57:48 +01:00
Andreas Kling
5eef07d232 LibJS: Avoid lots of string-to-int during global object construction
We were doing a *lot* of string-to-int conversion while creating a new
global object. This happened because Object::put() would try to convert
the property name (string) to an integer to see if it refers to an
indexed property.

Sidestep this issue by using PropertyName for the CommonPropertyNames
struct on VM (vm.names.foo), and giving PropertyName a flag that tells
us whether it's a string that *may be* a number.

All CommonPropertyNames are set up so they are known to not be numbers.
2021-06-13 19:11:29 +02:00
Linus Groh
7327a28ccc LibJS: Add ECMA-262 section/title/URL comments almost everywhere
As mentioned on Discord earlier, we'll add these to all new functions
going forward - this is the backfill. Reasons:

- It makes you look at the spec, implementing based on MDN or V8
  behavior is a no-go
- It makes finding the various functions that are non-compliant easier,
  in the future everything should either have such a comment or, if it's
  not from the spec at all, a comment explaining why that is the case
- It makes it easier to check whether a certain abstract operation is
  implemented in LibJS, not all of them use the same name as the spec.
  E.g. RejectPromise() is Promise::reject()
- It makes it easier to reason about vm.arguments(), e.g. when the
  function has a rest parameter
- It makes it easier to see whether a certain function is from a
  proposal or Annex B

Also:

- Add arguments to all functions and abstract operations that already
  had a comment
- Fix some outdated section numbers
- Replace some ecma-international.org URLs with tc39.es
2021-06-13 00:33:28 +01:00
Idan Horowitz
bd9e20ef79 LibJS: Add the Object.getOwnPropertySymbols method 2021-06-12 18:39:23 +01:00
Idan Horowitz
7f6d3818a2 LibJS: Add the Object::define_native_accessor method
This is very similar to Object::define_native_property, but here the
native functions are exported as standalone JS getter and setter
functions, instead of being transparently called by interactions with
the property.
2021-06-12 00:44:15 +01:00
Linus Groh
862ba64037 LibJS: Implement the Error Cause proposal
Currently stage 3. https://github.com/tc39/proposal-error-cause
2021-06-11 21:34:05 +01:00
Idan Horowitz
f63ef4f196 LibJS: Stop asserting in {Set,Test}IntegrityLevel on missing descriptor
As per the specification (7.3.15 SetIntegrityLevel):
i. Let currentDesc be ? O.[[GetOwnProperty]](k).
ii. If currentDesc is not undefined, then...
2021-06-10 09:20:05 +01:00
Linus Groh
7565bf0590 LibJS: Remove redundant exception checks 2021-06-07 23:09:06 +01:00
Linus Groh
3fdad563e2 LibJS: Never omit setter/getter attributes in accessor descriptor object
These should not be omitted, an accessor with out getter or setter still
yields an undefined for the attribute in its descriptor object.
2021-06-07 23:07:13 +01:00
Linus Groh
8c964ef9f8 LibJS: Fix get_own_property_descriptor_object() field name order
This is defined by the spec: enumerable and configurable come last.
2021-06-07 23:02:52 +01:00
Linus Groh
4e555fae22 LibJS: Add missing cyclic prototype check to Object.setPrototypeOf() 2021-06-07 22:56:16 +01:00
Idan Horowitz
2a8f4f097c LibJS: Throw TypeError on write to non-writable property in strict mode 2021-06-05 23:54:08 +01:00
Andreas Kling
42fcc2219d LibJS: Use PropertyName::as_string() in Object::get()
After we've already checked is_string(), we can use as_string() to
avoid a temporary String.
2021-06-05 13:00:34 +02:00
Linus Groh
0cf04d07aa LibJS: Temporarily clear exception in Object::get_without_side_effects()
This would return an empty value once it hits an exception check
otherwise. Considering that this mostly is used in situations where we
already *do* have an exception (traceback printing, for example), let's
make this easier for ourselves to use.
2021-04-24 20:11:04 +02:00
Linus Groh
ebdeed087c Everywhere: Use linusg@serenityos.org for my copyright headers 2021-04-22 22:51:19 +02:00
Brian Gianforcaro
1682f0b760 Everything: Move to SPDX license identifiers in all files.
SPDX License Identifiers are a more compact / standardized
way of representing file license information.

See: https://spdx.dev/resources/use/#identifiers

This was done with the `ambr` search and replace tool.

 ambr --no-parent-ignore --key-from-file --rep-from-file key.txt rep.txt *
2021-04-22 11:22:27 +02:00
Linus Groh
7af1d2c170 LibJS: Make Object.getOwnPropertyDescriptor() work with string indexed property
E.g. for "0" we have to contruct a PropertyName with Type::Number so it
looks in the indexed properties as expected.
2021-04-20 18:53:07 +02:00
Linus Groh
614bad86bc LibJS: Fix Object.getOwnPropertyDescriptor() attributes for numeric property
We were getting the attributes of the existing value and then
immediately assigned the default attributes instead.
2021-04-20 18:42:10 +02:00
Linus Groh
ac3e7ef791 LibJS: Fix crash in Object.{freeze,seal}() with indexed properties
This was failing to take two things into account:

- When constructing a PropertyName from a value, it won't automatically
  convert to Type::Number for something like string "0", even though
  that's how things work internally, since indexed properties are stored
  separately. This will be improved in a future patch, it's a footgun
  and should happen automatically.
- Those can't be looked up on the shape, we have to go through the
  indexed properties instead.

Additionally it now operates on the shape or indexed properties directly
as define_property() was overly strict and would throw if a property was
already non-configurable.

Fixes #6469.
2021-04-20 09:38:22 +02:00
Linus Groh
085816645f LibJS: Take PropertyName in Object::set_integrity_level() internal lambda
At least for IntegrityLevel::Frozen we already construct a PropertyName
from the key value, let's reuse that.
2021-04-20 09:38:22 +02:00
Linus Groh
51676d7c33 LibJS: Use dbgln_if() instead of '#if OBJECT_DEBUG' 2021-04-18 18:10:42 +02:00