1 | // -*- c-basic-offset: 2 -*- |
2 | // krazy:excludeall=doublequote_chars (UStrings aren't QStrings) |
3 | /* |
4 | * This file is part of the KDE libraries |
5 | * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) |
6 | * |
7 | * This library is free software; you can redistribute it and/or |
8 | * modify it under the terms of the GNU Lesser General Public |
9 | * License as published by the Free Software Foundation; either |
10 | * version 2 of the License, or (at your option) any later version. |
11 | * |
12 | * This library is distributed in the hope that it will be useful, |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
15 | * Lesser General Public License for more details. |
16 | * |
17 | * You should have received a copy of the GNU Lesser General Public |
18 | * License along with this library; if not, write to the Free Software |
19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
20 | * |
21 | */ |
22 | |
23 | #include "object_object.h" |
24 | #include <config-kjs.h> |
25 | |
26 | #include "operations.h" |
27 | #include "function_object.h" |
28 | #include "propertydescriptor.h" |
29 | #include <stdio.h> |
30 | |
31 | namespace KJS { |
32 | |
33 | |
34 | /** |
35 | * @internal |
36 | * |
37 | * Class to implement all methods that are properties of the |
38 | * Object object |
39 | */ |
40 | class ObjectObjectFuncImp : public InternalFunctionImp { |
41 | public: |
42 | ObjectObjectFuncImp(ExecState *, FunctionPrototype *, int i, int len, const Identifier& ); |
43 | |
44 | virtual JSValue *callAsFunction(ExecState *, JSObject *thisObj, const List &args); |
45 | |
46 | enum { GetOwnPropertyDescriptor, DefineProperty, GetPrototypeOf, |
47 | GetOwnPropertyNames, Keys, DefineProperties, Create, IsExtensible, |
48 | PreventExtensible, IsSealed, Seal, IsFrozen, Freeze, |
49 | //ES6 |
50 | Is |
51 | }; |
52 | |
53 | private: |
54 | int id; |
55 | }; |
56 | |
57 | // ------------------------------ ObjectPrototype -------------------------------- |
58 | |
59 | ObjectPrototype::ObjectPrototype(ExecState* exec, FunctionPrototype* funcProto) |
60 | : JSObject() // [[Prototype]] is null |
61 | { |
62 | static const Identifier* hasOwnPropertyPropertyName = new Identifier("hasOwnProperty" ); |
63 | static const Identifier* propertyIsEnumerablePropertyName = new Identifier("propertyIsEnumerable" ); |
64 | static const Identifier* isPrototypeOfPropertyName = new Identifier("isPrototypeOf" ); |
65 | static const Identifier* defineGetterPropertyName = new Identifier("__defineGetter__" ); |
66 | static const Identifier* defineSetterPropertyName = new Identifier("__defineSetter__" ); |
67 | static const Identifier* lookupGetterPropertyName = new Identifier("__lookupGetter__" ); |
68 | static const Identifier* lookupSetterPropertyName = new Identifier("__lookupSetter__" ); |
69 | |
70 | putDirectFunction(new ObjectProtoFunc(exec, funcProto, ObjectProtoFunc::ToString, 0, exec->propertyNames().toString), DontEnum); |
71 | putDirectFunction(new ObjectProtoFunc(exec, funcProto, ObjectProtoFunc::ToLocaleString, 0, exec->propertyNames().toLocaleString), DontEnum); |
72 | putDirectFunction(new ObjectProtoFunc(exec, funcProto, ObjectProtoFunc::ValueOf, 0, exec->propertyNames().valueOf), DontEnum); |
73 | putDirectFunction(new ObjectProtoFunc(exec, funcProto, ObjectProtoFunc::HasOwnProperty, 1, *hasOwnPropertyPropertyName), DontEnum); |
74 | putDirectFunction(new ObjectProtoFunc(exec, funcProto, ObjectProtoFunc::PropertyIsEnumerable, 1, *propertyIsEnumerablePropertyName), DontEnum); |
75 | putDirectFunction(new ObjectProtoFunc(exec, funcProto, ObjectProtoFunc::IsPrototypeOf, 1, *isPrototypeOfPropertyName), DontEnum); |
76 | |
77 | // Mozilla extensions |
78 | putDirectFunction(new ObjectProtoFunc(exec, funcProto, ObjectProtoFunc::DefineGetter, 2, *defineGetterPropertyName), DontEnum); |
79 | putDirectFunction(new ObjectProtoFunc(exec, funcProto, ObjectProtoFunc::DefineSetter, 2, *defineSetterPropertyName), DontEnum); |
80 | putDirectFunction(new ObjectProtoFunc(exec, funcProto, ObjectProtoFunc::LookupGetter, 1, *lookupGetterPropertyName), DontEnum); |
81 | putDirectFunction(new ObjectProtoFunc(exec, funcProto, ObjectProtoFunc::LookupSetter, 1, *lookupSetterPropertyName), DontEnum); |
82 | } |
83 | |
84 | JSObject* ObjectPrototype::self(ExecState* exec) |
85 | { |
86 | return exec->lexicalInterpreter()->builtinObjectPrototype(); |
87 | } |
88 | |
89 | // ------------------------------ ObjectProtoFunc -------------------------------- |
90 | |
91 | ObjectProtoFunc::ObjectProtoFunc(ExecState* exec, FunctionPrototype* funcProto, int i, int len, const Identifier& name) |
92 | : InternalFunctionImp(funcProto, name) |
93 | , id(i) |
94 | { |
95 | putDirect(exec->propertyNames().length, len, DontDelete|ReadOnly|DontEnum); |
96 | } |
97 | |
98 | |
99 | // ECMA 15.2.4.2, 15.2.4.4, 15.2.4.5, 15.2.4.7 |
100 | |
101 | JSValue *ObjectProtoFunc::callAsFunction(ExecState *exec, JSObject *thisObj, const List &args) |
102 | { |
103 | switch (id) { |
104 | case ValueOf: |
105 | return thisObj; |
106 | case HasOwnProperty: { |
107 | PropertySlot slot; |
108 | return jsBoolean(thisObj->getOwnPropertySlot(exec, Identifier(args[0]->toString(exec)), slot)); |
109 | } |
110 | case IsPrototypeOf: { |
111 | if (!args[0]->isObject()) |
112 | return jsBoolean(false); |
113 | |
114 | JSValue *v = static_cast<JSObject *>(args[0])->prototype(); |
115 | |
116 | while (true) { |
117 | if (!v->isObject()) |
118 | return jsBoolean(false); |
119 | |
120 | if (thisObj == static_cast<JSObject *>(v)) |
121 | return jsBoolean(true); |
122 | |
123 | v = static_cast<JSObject *>(v)->prototype(); |
124 | } |
125 | } |
126 | case DefineGetter: |
127 | case DefineSetter: { |
128 | if (!args[1]->isObject() || |
129 | !static_cast<JSObject *>(args[1])->implementsCall()) { |
130 | if (id == DefineGetter) |
131 | return throwError(exec, SyntaxError, "invalid getter usage" ); |
132 | else |
133 | return throwError(exec, SyntaxError, "invalid setter usage" ); |
134 | } |
135 | |
136 | if (id == DefineGetter) |
137 | thisObj->defineGetter(exec, Identifier(args[0]->toString(exec)), static_cast<JSObject *>(args[1])); |
138 | else |
139 | thisObj->defineSetter(exec, Identifier(args[0]->toString(exec)), static_cast<JSObject *>(args[1])); |
140 | return jsUndefined(); |
141 | } |
142 | case LookupGetter: |
143 | case LookupSetter: { |
144 | Identifier propertyName = Identifier(args[0]->toString(exec)); |
145 | |
146 | JSObject *obj = thisObj; |
147 | while (true) { |
148 | JSValue *v = obj->getDirect(propertyName); |
149 | |
150 | if (v) { |
151 | if (v->type() != GetterSetterType) |
152 | return jsUndefined(); |
153 | |
154 | JSObject *funcObj; |
155 | |
156 | if (id == LookupGetter) |
157 | funcObj = static_cast<GetterSetterImp *>(v)->getGetter(); |
158 | else |
159 | funcObj = static_cast<GetterSetterImp *>(v)->getSetter(); |
160 | |
161 | if (!funcObj) |
162 | return jsUndefined(); |
163 | else |
164 | return funcObj; |
165 | } |
166 | |
167 | if (!obj->prototype() || !obj->prototype()->isObject()) |
168 | return jsUndefined(); |
169 | |
170 | obj = static_cast<JSObject *>(obj->prototype()); |
171 | } |
172 | } |
173 | case PropertyIsEnumerable: |
174 | return jsBoolean(thisObj->propertyIsEnumerable(exec, Identifier(args[0]->toString(exec)))); |
175 | case ToLocaleString: |
176 | return jsString(thisObj->toString(exec)); |
177 | case ToString: |
178 | default: |
179 | return jsString("[object " + thisObj->className() + "]" ); |
180 | } |
181 | } |
182 | |
183 | // ------------------------------ ObjectObjectImp -------------------------------- |
184 | |
185 | ObjectObjectImp::ObjectObjectImp(ExecState* exec, ObjectPrototype* objProto, FunctionPrototype* funcProto) |
186 | : InternalFunctionImp(funcProto) |
187 | { |
188 | static const Identifier* getOwnPropertyDescriptorName = new Identifier("getOwnPropertyDescriptor" ); |
189 | static const Identifier* createName = new Identifier("create" ); |
190 | static const Identifier* definePropertyName = new Identifier("defineProperty" ); |
191 | static const Identifier* definePropertiesName = new Identifier("defineProperties" ); |
192 | static const Identifier* getPrototypeOf = new Identifier("getPrototypeOf" ); |
193 | static const Identifier* getOwnPropertyNames = new Identifier("getOwnPropertyNames" ); |
194 | static const Identifier* sealName = new Identifier("seal" ); |
195 | static const Identifier* freezeName = new Identifier("freeze" ); |
196 | static const Identifier* preventExtensionsName = new Identifier("preventExtensions" ); |
197 | static const Identifier* isSealedName = new Identifier("isSealed" ); |
198 | static const Identifier* isFrozenName = new Identifier("isFrozen" ); |
199 | static const Identifier* isExtensibleName = new Identifier("isExtensible" ); |
200 | static const Identifier* keys = new Identifier("keys" ); |
201 | static const Identifier* isName = new Identifier("is" ); |
202 | |
203 | // ECMA 15.2.3.1 |
204 | putDirect(exec->propertyNames().prototype, objProto, DontEnum|DontDelete|ReadOnly); |
205 | |
206 | putDirectFunction(new ObjectObjectFuncImp(exec, funcProto, ObjectObjectFuncImp::GetOwnPropertyDescriptor, 2, *getOwnPropertyDescriptorName), DontEnum); |
207 | putDirectFunction(new ObjectObjectFuncImp(exec, funcProto, ObjectObjectFuncImp::Create, 2, *createName), DontEnum); |
208 | putDirectFunction(new ObjectObjectFuncImp(exec, funcProto, ObjectObjectFuncImp::DefineProperty, 3, *definePropertyName), DontEnum); |
209 | putDirectFunction(new ObjectObjectFuncImp(exec, funcProto, ObjectObjectFuncImp::DefineProperties, 2, *definePropertiesName), DontEnum); |
210 | putDirectFunction(new ObjectObjectFuncImp(exec, funcProto, ObjectObjectFuncImp::GetPrototypeOf, 1, *getPrototypeOf), DontEnum); |
211 | putDirectFunction(new ObjectObjectFuncImp(exec, funcProto, ObjectObjectFuncImp::GetOwnPropertyNames, 1, *getOwnPropertyNames), DontEnum); |
212 | putDirectFunction(new ObjectObjectFuncImp(exec, funcProto, ObjectObjectFuncImp::Seal, 1, *sealName), DontEnum); |
213 | putDirectFunction(new ObjectObjectFuncImp(exec, funcProto, ObjectObjectFuncImp::Freeze, 1, *freezeName), DontEnum); |
214 | putDirectFunction(new ObjectObjectFuncImp(exec, funcProto, ObjectObjectFuncImp::PreventExtensible, 1, *preventExtensionsName), DontEnum); |
215 | putDirectFunction(new ObjectObjectFuncImp(exec, funcProto, ObjectObjectFuncImp::IsSealed, 1, *isSealedName), DontEnum); |
216 | putDirectFunction(new ObjectObjectFuncImp(exec, funcProto, ObjectObjectFuncImp::IsFrozen, 1, *isFrozenName), DontEnum); |
217 | putDirectFunction(new ObjectObjectFuncImp(exec, funcProto, ObjectObjectFuncImp::IsExtensible, 1, *isExtensibleName), DontEnum); |
218 | putDirectFunction(new ObjectObjectFuncImp(exec, funcProto, ObjectObjectFuncImp::Keys, 1, *keys), DontEnum); |
219 | //ES6 |
220 | putDirectFunction(new ObjectObjectFuncImp(exec, funcProto, ObjectObjectFuncImp::Is, 2, *isName), DontEnum); |
221 | |
222 | // no. of arguments for constructor |
223 | putDirect(exec->propertyNames().length, jsNumber(1), ReadOnly|DontDelete|DontEnum); |
224 | } |
225 | |
226 | |
227 | bool ObjectObjectImp::implementsConstruct() const |
228 | { |
229 | return true; |
230 | } |
231 | |
232 | // ECMA 15.2.2 |
233 | JSObject* ObjectObjectImp::construct(ExecState* exec, const List& args) |
234 | { |
235 | JSValue* arg = args[0]; |
236 | switch (arg->type()) { |
237 | case StringType: |
238 | case BooleanType: |
239 | case NumberType: |
240 | case ObjectType: |
241 | return arg->toObject(exec); |
242 | case NullType: |
243 | case UndefinedType: |
244 | return new JSObject(exec->lexicalInterpreter()->builtinObjectPrototype()); |
245 | default: |
246 | //### ASSERT_NOT_REACHED(); |
247 | return 0; |
248 | } |
249 | } |
250 | |
251 | JSValue* ObjectObjectImp::callAsFunction(ExecState* exec, JSObject* /*thisObj*/, const List &args) |
252 | { |
253 | return construct(exec, args); |
254 | } |
255 | |
256 | // ------------------------------ ObjectObjectFuncImp ---------------------------- |
257 | |
258 | ObjectObjectFuncImp::ObjectObjectFuncImp(ExecState* exec, FunctionPrototype* funcProto, int i, int len, const Identifier& name) |
259 | : InternalFunctionImp(funcProto, name), id(i) |
260 | { |
261 | putDirect(exec->propertyNames().length, len, DontDelete|ReadOnly|DontEnum); |
262 | } |
263 | |
264 | static JSValue* defineProperties(ExecState* exec, JSObject* object, JSValue* properties) |
265 | { |
266 | JSObject* props = properties->toObject(exec); |
267 | if (exec->hadException()) |
268 | return object; |
269 | PropertyNameArray names; |
270 | props->getOwnPropertyNames(exec, names, PropertyMap::ExcludeDontEnumProperties); |
271 | int size = names.size(); |
272 | Vector<PropertyDescriptor> descriptors; |
273 | for (int i = 0; i < size; ++i) { |
274 | PropertyDescriptor desc; |
275 | if (!desc.setPropertyDescriptorFromObject(exec, props->get(exec, names[i]))) |
276 | return jsUndefined(); |
277 | descriptors.append(desc); |
278 | } |
279 | for (int i = 0; i < size; ++i) { |
280 | object->defineOwnProperty(exec, names[i], descriptors[i], true); |
281 | if (exec->hadException()) |
282 | return jsUndefined(); |
283 | } |
284 | return object; |
285 | } |
286 | |
287 | JSValue *ObjectObjectFuncImp::callAsFunction(ExecState* exec, JSObject*, const List& args) |
288 | { |
289 | switch (id) { |
290 | case GetPrototypeOf: { //ECMA Edition 5.1r6 - 15.2.3.2 |
291 | JSObject* jso = args[0]->getObject(); |
292 | if (!jso) |
293 | return throwError(exec, TypeError, "Not an Object" ); |
294 | return jso->prototype(); |
295 | } |
296 | case GetOwnPropertyDescriptor: { //ECMA Edition 5.1r6 - 15.2.3.3 |
297 | JSObject* jso = args[0]->getObject(); |
298 | if (!jso) |
299 | return throwError(exec, TypeError, "Not an Object" ); |
300 | |
301 | UString name = args[1]->toString(exec); |
302 | PropertyDescriptor desc; |
303 | if (!jso->getOwnPropertyDescriptor(exec, Identifier(name), desc)) |
304 | return jsUndefined(); |
305 | return desc.fromPropertyDescriptor(exec); |
306 | } |
307 | case GetOwnPropertyNames: //ECMA Edition 5.1r6 - 15.2.3.4 |
308 | case Keys: { //ECMA Edition 5.1r6 - 15.2.3.14 |
309 | JSObject* jso = args[0]->getObject(); |
310 | if (!jso) |
311 | return throwError(exec, TypeError, "Not an Object" ); |
312 | |
313 | JSObject *ret = static_cast<JSObject *>(exec->lexicalInterpreter()->builtinArray()->construct(exec, List::empty())); |
314 | PropertyNameArray propertyNames; |
315 | |
316 | if (id == Keys) |
317 | jso->getOwnPropertyNames(exec, propertyNames, PropertyMap::ExcludeDontEnumProperties); |
318 | else // id == GetOwnPropertyNames |
319 | jso->getOwnPropertyNames(exec, propertyNames, PropertyMap::IncludeDontEnumProperties); |
320 | PropertyNameArrayIterator propEnd = propertyNames.end(); |
321 | unsigned int n = 0; |
322 | for (PropertyNameArrayIterator propIter = propertyNames.begin(); propIter != propEnd; ++propIter) { |
323 | Identifier name = *propIter; |
324 | ret->put(exec, n, jsString(name.ustring()), None); |
325 | ++n; |
326 | } |
327 | ret->put(exec, exec->propertyNames().length, jsNumber(n), DontEnum | DontDelete); |
328 | return ret; |
329 | } |
330 | case Create: { //ECMA Edition 5.1r6 - 15.2.3.5 |
331 | JSObject *proto = args[0]->getObject(); |
332 | if (!proto && !args[0]->isNull()) |
333 | return throwError(exec, TypeError, "Not an Object" ); |
334 | |
335 | JSObject *ret = static_cast<JSObject *>(exec->lexicalInterpreter()->builtinObject()->construct(exec, List::empty())); |
336 | if (proto) |
337 | ret->setPrototype(proto); |
338 | else |
339 | ret->setPrototype(jsNull()); |
340 | if (args.size() >= 2 && !args[1]->isUndefined()) |
341 | return defineProperties(exec, ret, args[1]); |
342 | return ret; |
343 | } |
344 | case DefineProperty: { //ECMA Edition 5.1r6 - 15.2.3.6 |
345 | JSObject* jso = args[0]->getObject(); |
346 | if (!jso) |
347 | return throwError(exec, TypeError, "Not an Object" ); |
348 | |
349 | UString name = args[1]->toString(exec); |
350 | PropertyDescriptor desc; |
351 | if (!desc.setPropertyDescriptorFromObject(exec, args[2])) |
352 | return jsUndefined(); |
353 | if (!jso->defineOwnProperty(exec, Identifier(name), desc, true)) |
354 | return jsUndefined(); |
355 | return jso; |
356 | } |
357 | case DefineProperties: { //ECMA Edition 5.1r6 - 15.2.3.7 |
358 | if (!args[0]->isObject()) |
359 | return throwError(exec, TypeError, "Not an Object" ); |
360 | |
361 | JSObject* jso = args[0]->getObject(); |
362 | return defineProperties(exec, jso, args[1]); |
363 | } |
364 | case Seal: { //ECMA Edition 5.1r6 - 15.2.3.8 |
365 | JSObject* jso = args[0]->getObject(); |
366 | if (!jso) |
367 | return throwError(exec, TypeError, "Not an Object" ); |
368 | |
369 | PropertyNameArray names; |
370 | jso->getOwnPropertyNames(exec, names, PropertyMap::IncludeDontEnumProperties); |
371 | int size = names.size(); |
372 | |
373 | PropertyDescriptor desc; |
374 | for (int i = 0; i < size; ++i) { |
375 | jso->getOwnPropertyDescriptor(exec, names[i], desc); |
376 | if (desc.configurable()) { |
377 | desc.setConfigureable(false); |
378 | if (!jso->defineOwnProperty(exec, names[i], desc, true)) |
379 | return jsUndefined(); |
380 | } |
381 | } |
382 | jso->preventExtensions(); |
383 | return jso; |
384 | } |
385 | case Freeze: { //ECMA Edition 5.1r6 - 15.2.3.9 |
386 | JSObject* jso = args[0]->getObject(); |
387 | if (!jso) |
388 | return throwError(exec, TypeError, "Not an Object" ); |
389 | |
390 | PropertyNameArray names; |
391 | jso->getOwnPropertyNames(exec, names, PropertyMap::IncludeDontEnumProperties); |
392 | int size = names.size(); |
393 | |
394 | PropertyDescriptor desc; |
395 | for (int i = 0; i < size; ++i) { |
396 | jso->getOwnPropertyDescriptor(exec, names[i], desc); |
397 | if (desc.isDataDescriptor()) |
398 | if (desc.writable()) |
399 | desc.setWritable(false); |
400 | if (desc.configurable()) |
401 | desc.setConfigureable(false); |
402 | if (!jso->defineOwnProperty(exec, names[i], desc, true)) |
403 | return jsUndefined(); |
404 | } |
405 | jso->preventExtensions(); |
406 | return jso; |
407 | } |
408 | case PreventExtensible: { //ECMA Edition 5.1r6 - 15.2.3.10 |
409 | JSObject* jso = args[0]->getObject(); |
410 | if (!jso) |
411 | return throwError(exec, TypeError, "Not an Object" ); |
412 | jso->preventExtensions(); |
413 | return jso; |
414 | } |
415 | case IsSealed: { //ECMA Edition 5.1r6 - 15.2.3.11 |
416 | JSObject* jso = args[0]->getObject(); |
417 | if (!jso) |
418 | return throwError(exec, TypeError, "Not an Object" ); |
419 | |
420 | PropertyNameArray names; |
421 | jso->getOwnPropertyNames(exec, names, PropertyMap::IncludeDontEnumProperties); |
422 | int size = names.size(); |
423 | |
424 | PropertyDescriptor desc; |
425 | for (int i = 0; i < size; ++i) { |
426 | jso->getOwnPropertyDescriptor(exec, names[i], desc); |
427 | if (desc.configurable()) |
428 | return jsBoolean(false); |
429 | } |
430 | return jsBoolean(!jso->isExtensible()); |
431 | } |
432 | case IsFrozen: { //ECMA Edition 5.1r6 - 15.2.3.12 |
433 | JSObject* jso = args[0]->getObject(); |
434 | if (!jso) |
435 | return throwError(exec, TypeError, "Not an Object" ); |
436 | |
437 | PropertyNameArray names; |
438 | jso->getOwnPropertyNames(exec, names, PropertyMap::IncludeDontEnumProperties); |
439 | int size = names.size(); |
440 | |
441 | PropertyDescriptor desc; |
442 | for (int i = 0; i < size; ++i) { |
443 | jso->getOwnPropertyDescriptor(exec, names[i], desc); |
444 | if (desc.isDataDescriptor()) |
445 | if (desc.writable()) |
446 | return jsBoolean(false); |
447 | if (desc.configurable()) |
448 | return jsBoolean(false); |
449 | } |
450 | return jsBoolean(!jso->isExtensible()); |
451 | } |
452 | case IsExtensible: { //ECMA Edition 5.1r6 - 15.2.3.13 |
453 | JSObject* jso = args[0]->getObject(); |
454 | if (!jso) |
455 | return throwError(exec, TypeError, "Not an Object" ); |
456 | return jsBoolean(jso->isExtensible()); |
457 | } |
458 | case Is: { //ES6 (Draft 08.11.2013) - 19.1.2.10 |
459 | return jsBoolean(sameValue(exec, args[0], args[1])); |
460 | } |
461 | default: |
462 | return jsUndefined(); |
463 | } |
464 | } |
465 | |
466 | } // namespace KJS |
467 | |