Use the reject() helper function for conditionally throwing TypeErrors.
[WebKit-https.git] / Source / JavaScriptCore / runtime / StringObject.cpp
1 /*
2  *  Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
3  *  Copyright (C) 2004-2008, 2016 Apple Inc. All rights reserved.
4  *
5  *  This library is free software; you can redistribute it and/or
6  *  modify it under the terms of the GNU Lesser General Public
7  *  License as published by the Free Software Foundation; either
8  *  version 2 of the License, or (at your option) any later version.
9  *
10  *  This library is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  *  Lesser General Public License for more details.
14  *
15  *  You should have received a copy of the GNU Lesser General Public
16  *  License along with this library; if not, write to the Free Software
17  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
18  *
19  */
20
21 #include "config.h"
22 #include "StringObject.h"
23
24 #include "Error.h"
25 #include "JSGlobalObject.h"
26 #include "JSCInlines.h"
27 #include "PropertyNameArray.h"
28
29 namespace JSC {
30
31 STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(StringObject);
32
33 const ClassInfo StringObject::s_info = { "String", &JSWrapperObject::s_info, 0, CREATE_METHOD_TABLE(StringObject) };
34
35 StringObject::StringObject(VM& vm, Structure* structure)
36     : JSWrapperObject(vm, structure)
37 {
38 }
39
40 void StringObject::finishCreation(VM& vm, JSString* string)
41 {
42     Base::finishCreation(vm);
43     ASSERT(inherits(info()));
44     setInternalValue(vm, string);
45 }
46
47 bool StringObject::getOwnPropertySlot(JSObject* cell, ExecState* exec, PropertyName propertyName, PropertySlot& slot)
48 {
49     StringObject* thisObject = jsCast<StringObject*>(cell);
50     if (thisObject->internalValue()->getStringPropertySlot(exec, propertyName, slot))
51         return true;
52     return JSObject::getOwnPropertySlot(thisObject, exec, propertyName, slot);
53 }
54     
55 bool StringObject::getOwnPropertySlotByIndex(JSObject* object, ExecState* exec, unsigned propertyName, PropertySlot& slot)
56 {
57     StringObject* thisObject = jsCast<StringObject*>(object);
58     if (thisObject->internalValue()->getStringPropertySlot(exec, propertyName, slot))
59         return true;    
60     return JSObject::getOwnPropertySlot(thisObject, exec, Identifier::from(exec, propertyName), slot);
61 }
62
63 bool StringObject::put(JSCell* cell, ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot)
64 {
65     VM& vm = exec->vm();
66     auto scope = DECLARE_THROW_SCOPE(vm);
67
68     StringObject* thisObject = jsCast<StringObject*>(cell);
69
70     if (UNLIKELY(isThisValueAltered(slot, thisObject)))
71         return ordinarySetSlow(exec, thisObject, propertyName, value, slot.thisValue(), slot.isStrictMode());
72
73     if (propertyName == vm.propertyNames->length)
74         return reject(exec, scope, slot.isStrictMode(), ASCIILiteral(ReadonlyPropertyWriteError));
75     if (Optional<uint32_t> index = parseIndex(propertyName))
76         return putByIndex(cell, exec, index.value(), value, slot.isStrictMode());
77     return JSObject::put(cell, exec, propertyName, value, slot);
78 }
79
80 bool StringObject::putByIndex(JSCell* cell, ExecState* exec, unsigned propertyName, JSValue value, bool shouldThrow)
81 {
82     VM& vm = exec->vm();
83     auto scope = DECLARE_THROW_SCOPE(vm);
84
85     StringObject* thisObject = jsCast<StringObject*>(cell);
86     if (thisObject->internalValue()->canGetIndex(propertyName))
87         return reject(exec, scope, shouldThrow, ASCIILiteral(ReadonlyPropertyWriteError));
88     return JSObject::putByIndex(cell, exec, propertyName, value, shouldThrow);
89 }
90
91 static bool isStringOwnProperty(ExecState* exec, StringObject* object, PropertyName propertyName)
92 {
93     if (propertyName == exec->propertyNames().length)
94         return true;
95     if (Optional<uint32_t> index = parseIndex(propertyName)) {
96         if (object->internalValue()->canGetIndex(index.value()))
97             return true;
98     }
99     return false;
100 }
101
102 bool StringObject::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName propertyName, const PropertyDescriptor& descriptor, bool throwException)
103 {
104     VM& vm = exec->vm();
105     auto scope = DECLARE_THROW_SCOPE(vm);
106     StringObject* thisObject = jsCast<StringObject*>(object);
107
108     if (isStringOwnProperty(exec, thisObject, propertyName)) {
109         // The current PropertyDescriptor is always
110         // PropertyDescriptor{[[Value]]: value, [[Writable]]: false, [[Enumerable]]: true, [[Configurable]]: false}.
111         // This ensures that any property descriptor cannot change the existing one.
112         // Here, simply return the result of validateAndApplyPropertyDescriptor.
113         // https://tc39.github.io/ecma262/#sec-string-exotic-objects-getownproperty-p
114         PropertyDescriptor current;
115         bool isCurrentDefined = thisObject->getOwnPropertyDescriptor(exec, propertyName, current);
116         ASSERT(isCurrentDefined);
117         bool isExtensible = thisObject->isExtensible(exec);
118         RETURN_IF_EXCEPTION(scope, false);
119         return validateAndApplyPropertyDescriptor(exec, nullptr, propertyName, isExtensible, descriptor, isCurrentDefined, current, throwException);
120     }
121
122     return Base::defineOwnProperty(object, exec, propertyName, descriptor, throwException);
123 }
124
125 bool StringObject::deleteProperty(JSCell* cell, ExecState* exec, PropertyName propertyName)
126 {
127     StringObject* thisObject = jsCast<StringObject*>(cell);
128     if (propertyName == exec->propertyNames().length)
129         return false;
130     Optional<uint32_t> index = parseIndex(propertyName);
131     if (index && thisObject->internalValue()->canGetIndex(index.value()))
132         return false;
133     return JSObject::deleteProperty(thisObject, exec, propertyName);
134 }
135
136 bool StringObject::deletePropertyByIndex(JSCell* cell, ExecState* exec, unsigned i)
137 {
138     StringObject* thisObject = jsCast<StringObject*>(cell);
139     if (thisObject->internalValue()->canGetIndex(i))
140         return false;
141     return JSObject::deletePropertyByIndex(thisObject, exec, i);
142 }
143
144 void StringObject::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
145 {
146     StringObject* thisObject = jsCast<StringObject*>(object);
147     if (propertyNames.includeStringProperties()) {
148         int size = thisObject->internalValue()->length();
149         for (int i = 0; i < size; ++i)
150             propertyNames.add(Identifier::from(exec, i));
151     }
152     if (mode.includeDontEnumProperties())
153         propertyNames.add(exec->propertyNames().length);
154     return JSObject::getOwnPropertyNames(thisObject, exec, propertyNames, mode);
155 }
156
157 StringObject* constructString(VM& vm, JSGlobalObject* globalObject, JSValue string)
158 {
159     StringObject* object = StringObject::create(vm, globalObject->stringObjectStructure());
160     object->setInternalValue(vm, string);
161     return object;
162 }
163
164 } // namespace JSC