1 | /* |
2 | * Copyright (C) 1999-2001 Harri Porten ([email protected]) |
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" , &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(StringObject) }; |
34 | |
35 | StringObject::StringObject(VM& vm, Structure* structure) |
36 | : Base(vm, structure) |
37 | { |
38 | } |
39 | |
40 | void StringObject::finishCreation(VM& vm, JSString* string) |
41 | { |
42 | Base::finishCreation(vm); |
43 | ASSERT(inherits(vm, 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 | RELEASE_AND_RETURN(scope, ordinarySetSlow(exec, thisObject, propertyName, value, slot.thisValue(), slot.isStrictMode())); |
72 | |
73 | if (propertyName == vm.propertyNames->length) |
74 | return typeError(exec, scope, slot.isStrictMode(), ReadonlyPropertyWriteError); |
75 | if (Optional<uint32_t> index = parseIndex(propertyName)) |
76 | RELEASE_AND_RETURN(scope, putByIndex(cell, exec, index.value(), value, slot.isStrictMode())); |
77 | RELEASE_AND_RETURN(scope, 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 typeError(exec, scope, shouldThrow, ReadonlyPropertyWriteError); |
88 | RELEASE_AND_RETURN(scope, JSObject::putByIndex(cell, exec, propertyName, value, shouldThrow)); |
89 | } |
90 | |
91 | static bool isStringOwnProperty(ExecState* exec, StringObject* object, PropertyName propertyName) |
92 | { |
93 | VM& vm = exec->vm(); |
94 | if (propertyName == vm.propertyNames->length) |
95 | return true; |
96 | if (Optional<uint32_t> index = parseIndex(propertyName)) { |
97 | if (object->internalValue()->canGetIndex(index.value())) |
98 | return true; |
99 | } |
100 | return false; |
101 | } |
102 | |
103 | bool StringObject::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName propertyName, const PropertyDescriptor& descriptor, bool throwException) |
104 | { |
105 | VM& vm = exec->vm(); |
106 | auto scope = DECLARE_THROW_SCOPE(vm); |
107 | StringObject* thisObject = jsCast<StringObject*>(object); |
108 | |
109 | if (isStringOwnProperty(exec, thisObject, propertyName)) { |
110 | // The current PropertyDescriptor is always |
111 | // PropertyDescriptor{[[Value]]: value, [[Writable]]: false, [[Enumerable]]: true, [[Configurable]]: false}. |
112 | // This ensures that any property descriptor cannot change the existing one. |
113 | // Here, simply return the result of validateAndApplyPropertyDescriptor. |
114 | // https://tc39.github.io/ecma262/#sec-string-exotic-objects-getownproperty-p |
115 | PropertyDescriptor current; |
116 | bool isCurrentDefined = thisObject->getOwnPropertyDescriptor(exec, propertyName, current); |
117 | EXCEPTION_ASSERT(!scope.exception() == isCurrentDefined); |
118 | RETURN_IF_EXCEPTION(scope, false); |
119 | bool isExtensible = thisObject->isExtensible(exec); |
120 | RETURN_IF_EXCEPTION(scope, false); |
121 | RELEASE_AND_RETURN(scope, validateAndApplyPropertyDescriptor(exec, nullptr, propertyName, isExtensible, descriptor, isCurrentDefined, current, throwException)); |
122 | } |
123 | |
124 | RELEASE_AND_RETURN(scope, Base::defineOwnProperty(object, exec, propertyName, descriptor, throwException)); |
125 | } |
126 | |
127 | bool StringObject::deleteProperty(JSCell* cell, ExecState* exec, PropertyName propertyName) |
128 | { |
129 | VM& vm = exec->vm(); |
130 | StringObject* thisObject = jsCast<StringObject*>(cell); |
131 | if (propertyName == vm.propertyNames->length) |
132 | return false; |
133 | Optional<uint32_t> index = parseIndex(propertyName); |
134 | if (index && thisObject->internalValue()->canGetIndex(index.value())) |
135 | return false; |
136 | return JSObject::deleteProperty(thisObject, exec, propertyName); |
137 | } |
138 | |
139 | bool StringObject::deletePropertyByIndex(JSCell* cell, ExecState* exec, unsigned i) |
140 | { |
141 | StringObject* thisObject = jsCast<StringObject*>(cell); |
142 | if (thisObject->internalValue()->canGetIndex(i)) |
143 | return false; |
144 | return JSObject::deletePropertyByIndex(thisObject, exec, i); |
145 | } |
146 | |
147 | void StringObject::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) |
148 | { |
149 | StringObject* thisObject = jsCast<StringObject*>(object); |
150 | if (propertyNames.includeStringProperties()) { |
151 | int size = thisObject->internalValue()->length(); |
152 | for (int i = 0; i < size; ++i) |
153 | propertyNames.add(Identifier::from(exec, i)); |
154 | } |
155 | return JSObject::getOwnPropertyNames(thisObject, exec, propertyNames, mode); |
156 | } |
157 | |
158 | void StringObject::getOwnNonIndexPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) |
159 | { |
160 | VM& vm = exec->vm(); |
161 | StringObject* thisObject = jsCast<StringObject*>(object); |
162 | if (mode.includeDontEnumProperties()) |
163 | propertyNames.add(vm.propertyNames->length); |
164 | return JSObject::getOwnNonIndexPropertyNames(thisObject, exec, propertyNames, mode); |
165 | } |
166 | |
167 | StringObject* constructString(VM& vm, JSGlobalObject* globalObject, JSValue string) |
168 | { |
169 | StringObject* object = StringObject::create(vm, globalObject->stringObjectStructure()); |
170 | object->setInternalValue(vm, string); |
171 | return object; |
172 | } |
173 | |
174 | } // namespace JSC |
175 | |