| // Copyright 2019 the V8 project authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include 'src/objects/property-descriptor-object.h' |
| |
| bitfield struct PropertyDescriptorObjectFlags extends uint31 { |
| is_enumerable: bool: 1 bit; |
| has_enumerable: bool: 1 bit; |
| is_configurable: bool: 1 bit; |
| has_configurable: bool: 1 bit; |
| is_writable: bool: 1 bit; |
| has_writable: bool: 1 bit; |
| has_value: bool: 1 bit; |
| has_get: bool: 1 bit; |
| has_set: bool: 1 bit; |
| } |
| |
| extern class PropertyDescriptorObject extends Struct { |
| macro IsDataDescriptor(): bool { |
| return this.flags.has_value || this.flags.has_writable; |
| } |
| |
| macro IsAccessorDescriptor(): bool { |
| return this.flags.has_get || this.flags.has_set; |
| } |
| |
| macro IsGenericDescriptor(): bool { |
| if (this.IsDataDescriptor() || this.IsAccessorDescriptor()) { |
| return false; |
| } |
| return true; |
| } |
| |
| macro IsEmptyOrEquivalentTo(current: PropertyDescriptorObject): bool { |
| return (!this.flags.has_enumerable || |
| this.flags.is_enumerable == current.flags.is_enumerable) && |
| (!this.flags.has_configurable || |
| this.flags.is_configurable == current.flags.is_configurable) && |
| (!this.flags.has_value || SameValue(this.value, current.value)) && |
| (!this.flags.has_writable || |
| this.flags.is_writable == current.flags.is_writable) && |
| (!this.flags.has_get || SameValue(this.get, current.get)) && |
| (!this.flags.has_set || SameValue(this.get, current.set)); |
| } |
| |
| flags: SmiTagged<PropertyDescriptorObjectFlags>; |
| value: JSAny|TheHole; |
| get: JSAny|TheHole; |
| set: JSAny|TheHole; |
| } |
| |
| macro IsCompatiblePropertyDescriptor( |
| _extensible: bool, newDesc: PropertyDescriptorObject, |
| current: PropertyDescriptorObject): bool { |
| if (newDesc.IsEmptyOrEquivalentTo(current)) return true; |
| |
| // 5. If current.[[Configurable]] is false, then |
| // 5a. If Desc has a [[Configurable]] field and Desc.[[Configurable]] is |
| // true, return false. 5b. If Desc has an [[Enumerable]] field and |
| // SameValue(Desc.[[Enumerable]], current.[[Enumerable]]) is false, return |
| // false. 5c. If IsGenericDescriptor(Desc) is false and |
| // SameValue(IsAccessorDescriptor(Desc), IsAccessorDescriptor(current)) is |
| // false, return false. 5d. If IsAccessorDescriptor(Desc) is true, then |
| // i. If Desc has a [[Get]] field and SameValue(Desc.[[Get]], |
| // current.[[Get]]) is false, return false. |
| // ii. If Desc has a [[Set]] field and SameValue(Desc.[[Set]], |
| // current.[[Set]]) is false, return false. |
| // 5e. Else if current.[[Writable]] is false, then |
| // i. If Desc has a [[Writable]] field and Desc.[[Writable]] is true, |
| // return false. |
| // ii. ii. If Desc has a [[Value]] field and SameValue(Desc.[[Value]], |
| // current.[[Value]]) is false, return false. |
| if (!current.flags.is_configurable) { |
| if (newDesc.flags.has_configurable && newDesc.flags.is_configurable) |
| return false; |
| if (!current.flags.has_enumerable && |
| (newDesc.flags.is_enumerable != current.flags.is_enumerable)) |
| return false; |
| const isAccessor = newDesc.IsAccessorDescriptor(); |
| if (!newDesc.IsGenericDescriptor() && |
| isAccessor != current.IsAccessorDescriptor()) |
| return false; |
| if (isAccessor) { |
| if (newDesc.flags.has_get && !SameValue(newDesc.get, current.get)) |
| return false; |
| if (newDesc.flags.has_set && !SameValue(newDesc.set, current.set)) |
| return false; |
| } else if (!current.flags.is_writable) { |
| if (newDesc.flags.is_writable) return false; |
| if (newDesc.flags.has_value && !SameValue(newDesc.value, current.value)) |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| macro IsCompatiblePropertyDescriptor( |
| extensible: bool, newDesc: (PropertyDescriptorObject|Undefined), |
| current: PropertyDescriptorObject): bool { |
| // 3. If every field in Desc is absent, return true. (This also has a shortcut |
| // not in the spec: if every field value matches the current value, return.) |
| typeswitch (newDesc) { |
| case (Undefined): { |
| return true; |
| } |
| case (newDesc: PropertyDescriptorObject): { |
| return IsCompatiblePropertyDescriptor(extensible, newDesc, current); |
| } |
| } |
| } |
| |
| @export |
| macro IsCompatiblePropertyDescriptor( |
| extensible: bool, newDesc: (PropertyDescriptorObject|Undefined), |
| current: (PropertyDescriptorObject|Undefined)): bool { |
| // 2. If current is undefined, then |
| // 2a. If extensible is false, return false. |
| // 2b. If O is undefined, return true. |
| typeswitch (current) { |
| case (Undefined): { |
| return extensible; |
| } |
| case (current: PropertyDescriptorObject): { |
| return IsCompatiblePropertyDescriptor(extensible, newDesc, current); |
| } |
| } |
| } |
| |
| @export |
| macro CompletePropertyDescriptor(desc: PropertyDescriptorObject): void { |
| // 1. Let like be the Record { [[Value]]: undefined, [[Writable]]: false, |
| // [[Get]]: undefined, |
| // [[Set]]: undefined, [[Enumerable]]: false, [[Configurable]]: false }. |
| if (!desc.IsAccessorDescriptor()) { |
| if (!desc.flags.has_value) { |
| desc.flags.has_value = true; |
| desc.value = Undefined; |
| } |
| if (!desc.flags.has_writable) { |
| desc.flags.has_writable = true; |
| desc.flags.is_writable = false; |
| } |
| } else { |
| if (!desc.flags.has_get) { |
| desc.flags.has_get = true; |
| desc.get = Undefined; |
| } |
| if (!desc.flags.has_set) { |
| desc.flags.has_set = true; |
| desc.set = Undefined; |
| } |
| } |
| if (!desc.flags.has_enumerable) { |
| desc.flags.has_enumerable = true; |
| desc.flags.is_enumerable = false; |
| } |
| if (!desc.flags.has_configurable) { |
| desc.flags.has_configurable = true; |
| desc.flags.is_configurable = false; |
| } |
| } |
| |
| extern macro AllocatePropertyDescriptorObject(implicit context: Context)(): |
| PropertyDescriptorObject; |