File indexing completed on 2024-05-12 15:43:33

0001 /*
0002     This file is part of the KDE libraries
0003     Copyright 2012  Bernd Buschinski <b.buschinski@googlemail.com>
0004 
0005     This library is free software; you can redistribute it and/or
0006     modify it under the terms of the GNU Lesser General Public
0007     License as published by the Free Software Foundation; either
0008     version 2.1 of the License, or (at your option) any later version.
0009 
0010     This library is distributed in the hope that it will be useful,
0011     but WITHOUT ANY WARRANTY; without even the implied warranty of
0012     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0013     Lesser General Public License for more details.
0014 
0015     You should have received a copy of the GNU Lesser General Public
0016     License along with this library.  If not, see <http://www.gnu.org/licenses/>.
0017 */
0018 
0019 #include "propertydescriptor.h"
0020 #include "object.h"
0021 #include "operations.h"
0022 
0023 #include <stdio.h>
0024 
0025 namespace KJS
0026 {
0027 
0028 PropertyDescriptor::PropertyDescriptor()
0029     : m_attributes(DontEnum | DontDelete | ReadOnly),
0030       m_setAttributes(0),
0031       m_value(nullptr),
0032       m_getter(nullptr),
0033       m_setter(nullptr)
0034 {
0035 }
0036 
0037 //ECMAScript Edition 5.1r6 - 8.10.1
0038 bool PropertyDescriptor::isAccessorDescriptor() const
0039 {
0040     return (m_getter || m_setter);
0041 }
0042 
0043 //ECMAScript Edition 5.1r6 - 8.10.2
0044 bool PropertyDescriptor::isDataDescriptor() const
0045 {
0046     if (!m_value && !(writableSet())) {
0047         return false;
0048     }
0049     return true;
0050 }
0051 
0052 //ECMAScript Edition 5.1r6 - 8.10.3
0053 bool PropertyDescriptor::isGenericDescriptor() const
0054 {
0055     return (!isAccessorDescriptor() && !isDataDescriptor());
0056 }
0057 
0058 //ECMAScript Edition 5.1r6 - 8.10.4 - FromPropertyDescriptor
0059 JSObject *PropertyDescriptor::fromPropertyDescriptor(ExecState *exec)
0060 {
0061     JSObject *ret = new JSObject(exec->lexicalInterpreter()->builtinObjectPrototype());
0062 
0063     if (isDataDescriptor()) {
0064         ret->put(exec, exec->propertyNames().writable, jsBoolean(writable()));
0065         ret->put(exec, exec->propertyNames().value, value() ? value() : jsUndefined());
0066     } else {
0067         ret->put(exec, exec->propertyNames().get, getter() ? getter() : jsUndefined());
0068         ret->put(exec, exec->propertyNames().set, setter() ? setter() : jsUndefined());
0069     }
0070 
0071     ret->put(exec, exec->propertyNames().enumerable, jsBoolean(enumerable()));
0072     ret->put(exec, exec->propertyNames().configurable, jsBoolean(configurable()));
0073 
0074     return ret;
0075 }
0076 
0077 //ECMAScript Edition 5.1r6 - 8.10.5 - ToPropertyDescriptor
0078 bool PropertyDescriptor::setPropertyDescriptorFromObject(ExecState *exec, JSValue *jsValue)
0079 {
0080     JSObject *obj = JSValue::getObject(jsValue);
0081     if (!obj) {
0082         throwError(exec, TypeError, "not an Object");
0083         return false;
0084     }
0085 
0086     if (obj->hasProperty(exec, exec->propertyNames().enumerable)) {
0087         setEnumerable(JSValue::toBoolean(obj->get(exec, exec->propertyNames().enumerable), exec));
0088     }
0089 
0090     if (obj->hasProperty(exec, exec->propertyNames().configurable)) {
0091         setConfigureable(JSValue::toBoolean(obj->get(exec, exec->propertyNames().configurable), exec));
0092     }
0093 
0094     if (obj->hasProperty(exec, exec->propertyNames().value)) {
0095         setValue(obj->get(exec, exec->propertyNames().value));
0096     }
0097 
0098     if (obj->hasProperty(exec, exec->propertyNames().writable)) {
0099         setWritable(JSValue::toBoolean(obj->get(exec, exec->propertyNames().writable), exec));
0100     }
0101 
0102     if (obj->hasProperty(exec, exec->propertyNames().get)) {
0103         JSValue *getter = obj->get(exec, exec->propertyNames().get);
0104         if (!JSValue::isUndefined(getter)) {
0105             if (!JSValue::implementsCall(getter)) {
0106                 throwError(exec, TypeError, "Getter: \'" + JSValue::toString(getter, exec) + "\' is not Callable");
0107                 return false;
0108             }
0109         }
0110         setGetter(getter);
0111     }
0112 
0113     if (obj->hasProperty(exec, exec->propertyNames().set)) {
0114         JSValue *setter = obj->get(exec, exec->propertyNames().set);
0115         if (!JSValue::isUndefined(setter)) {
0116             if (!JSValue::implementsCall(setter)) {
0117                 throwError(exec, TypeError, "Setter: \'" + JSValue::toString(setter, exec) + "\' is not Callable");
0118                 return false;
0119             }
0120         }
0121         setSetter(setter);
0122     }
0123 
0124     if (getter() || setter()) {
0125         if (value() || writableSet()) {
0126             throwError(exec, TypeError, "can not mix accessor descriptor and data descriptor");
0127             return false;
0128         }
0129     }
0130     return true;
0131 }
0132 
0133 bool PropertyDescriptor::setPropertyDescriptorValues(ExecState *, JSValue *value, unsigned int attributes)
0134 {
0135     setEnumerable(!(attributes & DontEnum));
0136     setConfigureable(!(attributes & DontDelete));
0137 
0138     if (!value) {
0139         return false;
0140     }
0141     if (JSValue::isUndefined(value) || JSValue::type(value) != GetterSetterType) {
0142         setValue(value);
0143         setWritable(!(attributes & ReadOnly));
0144     } else {
0145         GetterSetterImp *gs = static_cast<GetterSetterImp *>(value);
0146         setGetter(gs->getGetter() ? gs->getGetter() : jsUndefined());
0147         setSetter(gs->getSetter() ? gs->getSetter() : jsUndefined());
0148     }
0149     return true;
0150 }
0151 
0152 bool PropertyDescriptor::configurable() const
0153 {
0154     return !(m_attributes & DontDelete);
0155 }
0156 
0157 bool PropertyDescriptor::enumerable() const
0158 {
0159     return !(m_attributes & DontEnum);
0160 }
0161 
0162 bool PropertyDescriptor::writable() const
0163 {
0164     return !(m_attributes & ReadOnly);
0165 }
0166 
0167 bool PropertyDescriptor::configureSet() const
0168 {
0169     return m_setAttributes & ConfigurableSet;
0170 }
0171 
0172 bool PropertyDescriptor::enumerableSet() const
0173 {
0174     return m_setAttributes & EnumerableSet;
0175 }
0176 
0177 bool PropertyDescriptor::writableSet() const
0178 {
0179     return m_setAttributes & WritableSet;
0180 }
0181 
0182 JSValue *PropertyDescriptor::getter() const
0183 {
0184     return m_getter;
0185 }
0186 
0187 JSValue *PropertyDescriptor::setter() const
0188 {
0189     return m_setter;
0190 }
0191 
0192 JSValue *PropertyDescriptor::value() const
0193 {
0194     return m_value;
0195 }
0196 
0197 void PropertyDescriptor::setEnumerable(bool enumerable)
0198 {
0199     if (enumerable) {
0200         m_attributes &= ~DontEnum;
0201     } else {
0202         m_attributes |= DontEnum;
0203     }
0204     m_setAttributes |= EnumerableSet;
0205 }
0206 
0207 void PropertyDescriptor::setConfigureable(bool configurable)
0208 {
0209     if (configurable) {
0210         m_attributes &= ~DontDelete;
0211     } else {
0212         m_attributes |= DontDelete;
0213     }
0214     m_setAttributes |= ConfigurableSet;
0215 }
0216 
0217 void PropertyDescriptor::setValue(JSValue *value)
0218 {
0219     m_value = value;
0220 }
0221 
0222 void PropertyDescriptor::setWritable(bool writable)
0223 {
0224     if (writable) {
0225         m_attributes &= ~ReadOnly;
0226     } else {
0227         m_attributes |= ReadOnly;
0228     }
0229     m_setAttributes |= WritableSet;
0230 }
0231 
0232 void PropertyDescriptor::setGetter(JSValue *getter)
0233 {
0234     m_getter = getter;
0235     m_attributes &= ~ReadOnly;
0236 }
0237 
0238 void PropertyDescriptor::setSetter(JSValue *setter)
0239 {
0240     m_setter = setter;
0241     m_attributes &= ~ReadOnly;
0242 }
0243 
0244 unsigned int PropertyDescriptor::attributes() const
0245 {
0246     return m_attributes;
0247 }
0248 
0249 bool PropertyDescriptor::isEmpty() const
0250 {
0251     return !m_setAttributes && !m_getter && !m_setter && !m_value;
0252 }
0253 
0254 inline bool compareValue(ExecState *exec, JSValue *a, JSValue *b)
0255 {
0256     return (a == b || (a && b && sameValue(exec, a, b)));
0257 }
0258 
0259 // different from compareValue, if "own" getter/setter is missing (is 0) we are still the same
0260 inline bool compareFunction(ExecState *exec, JSValue *a, JSValue *b)
0261 {
0262     return (a == b || (b != nullptr && a == nullptr) || (a && b && sameValue(exec, a, b)));
0263 }
0264 
0265 bool PropertyDescriptor::equalTo(ExecState *exec, PropertyDescriptor &other) const
0266 {
0267     return (compareValue(exec, m_value, other.value()) &&
0268             compareFunction(exec, m_getter, other.getter()) &&
0269             compareFunction(exec, m_setter, other.setter()) &&
0270             attributes() == other.attributes());
0271 }
0272 
0273 unsigned int PropertyDescriptor::attributesWithOverride(PropertyDescriptor &other) const
0274 {
0275     unsigned int mismatch = other.m_attributes ^ m_attributes;
0276     unsigned int sharedSeen = other.m_setAttributes & m_setAttributes;
0277     unsigned int newAttributes = m_attributes & (DontEnum | DontDelete | ReadOnly);
0278 
0279     if ((sharedSeen & WritableSet) && (mismatch & ReadOnly)) {
0280         newAttributes ^= ReadOnly;
0281     }
0282     if ((sharedSeen & ConfigurableSet) && (mismatch & DontDelete)) {
0283         newAttributes ^= DontDelete;
0284     }
0285     if ((sharedSeen & EnumerableSet) && (mismatch & DontEnum)) {
0286         newAttributes ^= DontEnum;
0287     }
0288 
0289     return newAttributes;
0290 }
0291 
0292 bool PropertyDescriptor::operator==(PropertyDescriptor &other) const
0293 {
0294     return (m_value == other.value() &&
0295             m_setter == other.setter() &&
0296             m_getter == other.getter() &&
0297             m_attributes == other.m_attributes &&
0298             writableSet() == other.writableSet() &&
0299             enumerableSet() == other.enumerableSet() &&
0300             configureSet() == other.configureSet());
0301 }
0302 
0303 }
0304