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
31namespace KJS {
32
33
34/**
35 * @internal
36 *
37 * Class to implement all methods that are properties of the
38 * Object object
39 */
40class ObjectObjectFuncImp : public InternalFunctionImp {
41public:
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
53private:
54 int id;
55};
56
57// ------------------------------ ObjectPrototype --------------------------------
58
59ObjectPrototype::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
84JSObject* ObjectPrototype::self(ExecState* exec)
85{
86 return exec->lexicalInterpreter()->builtinObjectPrototype();
87}
88
89// ------------------------------ ObjectProtoFunc --------------------------------
90
91ObjectProtoFunc::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
101JSValue *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
185ObjectObjectImp::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
227bool ObjectObjectImp::implementsConstruct() const
228{
229 return true;
230}
231
232// ECMA 15.2.2
233JSObject* 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
251JSValue* ObjectObjectImp::callAsFunction(ExecState* exec, JSObject* /*thisObj*/, const List &args)
252{
253 return construct(exec, args);
254}
255
256// ------------------------------ ObjectObjectFuncImp ----------------------------
257
258ObjectObjectFuncImp::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
264static 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
287JSValue *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