1/*
2 *  Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
3 *  Copyright (C) 2008, 2011 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 "ObjectPrototype.h"
23
24#include "Error.h"
25#include "GetterSetter.h"
26#include "JSFunction.h"
27#include "JSString.h"
28#include "JSCInlines.h"
29#include "StructureRareDataInlines.h"
30
31namespace JSC {
32
33static EncodedJSValue JSC_HOST_CALL objectProtoFuncValueOf(ExecState*);
34static EncodedJSValue JSC_HOST_CALL objectProtoFuncHasOwnProperty(ExecState*);
35static EncodedJSValue JSC_HOST_CALL objectProtoFuncIsPrototypeOf(ExecState*);
36static EncodedJSValue JSC_HOST_CALL objectProtoFuncDefineGetter(ExecState*);
37static EncodedJSValue JSC_HOST_CALL objectProtoFuncDefineSetter(ExecState*);
38static EncodedJSValue JSC_HOST_CALL objectProtoFuncLookupGetter(ExecState*);
39static EncodedJSValue JSC_HOST_CALL objectProtoFuncLookupSetter(ExecState*);
40static EncodedJSValue JSC_HOST_CALL objectProtoFuncPropertyIsEnumerable(ExecState*);
41static EncodedJSValue JSC_HOST_CALL objectProtoFuncToLocaleString(ExecState*);
42
43STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(ObjectPrototype);
44
45const ClassInfo ObjectPrototype::s_info = { "Object", &JSNonFinalObject::s_info, 0, 0, CREATE_METHOD_TABLE(ObjectPrototype) };
46
47ObjectPrototype::ObjectPrototype(VM& vm, Structure* stucture)
48    : JSNonFinalObject(vm, stucture)
49{
50}
51
52void ObjectPrototype::finishCreation(VM& vm, JSGlobalObject* globalObject)
53{
54    Base::finishCreation(vm);
55    ASSERT(inherits(info()));
56    vm.prototypeMap.addPrototype(this);
57
58    JSC_NATIVE_FUNCTION(vm.propertyNames->toString, objectProtoFuncToString, DontEnum, 0);
59    JSC_NATIVE_FUNCTION(vm.propertyNames->toLocaleString, objectProtoFuncToLocaleString, DontEnum, 0);
60    JSC_NATIVE_FUNCTION(vm.propertyNames->valueOf, objectProtoFuncValueOf, DontEnum, 0);
61    JSC_NATIVE_FUNCTION(vm.propertyNames->hasOwnProperty, objectProtoFuncHasOwnProperty, DontEnum, 1);
62    JSC_NATIVE_FUNCTION(vm.propertyNames->propertyIsEnumerable, objectProtoFuncPropertyIsEnumerable, DontEnum, 1);
63    JSC_NATIVE_FUNCTION(vm.propertyNames->isPrototypeOf, objectProtoFuncIsPrototypeOf, DontEnum, 1);
64    JSC_NATIVE_FUNCTION(vm.propertyNames->__defineGetter__, objectProtoFuncDefineGetter, DontEnum, 2);
65    JSC_NATIVE_FUNCTION(vm.propertyNames->__defineSetter__, objectProtoFuncDefineSetter, DontEnum, 2);
66    JSC_NATIVE_FUNCTION(vm.propertyNames->__lookupGetter__, objectProtoFuncLookupGetter, DontEnum, 1);
67    JSC_NATIVE_FUNCTION(vm.propertyNames->__lookupSetter__, objectProtoFuncLookupSetter, DontEnum, 1);
68}
69
70ObjectPrototype* ObjectPrototype::create(VM& vm, JSGlobalObject* globalObject, Structure* structure)
71{
72    ObjectPrototype* prototype = new (NotNull, allocateCell<ObjectPrototype>(vm.heap)) ObjectPrototype(vm, structure);
73    prototype->finishCreation(vm, globalObject);
74    return prototype;
75}
76
77// ------------------------------ Functions --------------------------------
78
79EncodedJSValue JSC_HOST_CALL objectProtoFuncValueOf(ExecState* exec)
80{
81    JSValue thisValue = exec->thisValue().toThis(exec, StrictMode);
82    return JSValue::encode(thisValue.toObject(exec));
83}
84
85EncodedJSValue JSC_HOST_CALL objectProtoFuncHasOwnProperty(ExecState* exec)
86{
87    JSValue thisValue = exec->thisValue().toThis(exec, StrictMode);
88    return JSValue::encode(jsBoolean(thisValue.toObject(exec)->hasOwnProperty(exec, exec->argument(0).toString(exec)->toIdentifier(exec))));
89}
90
91EncodedJSValue JSC_HOST_CALL objectProtoFuncIsPrototypeOf(ExecState* exec)
92{
93    JSValue thisValue = exec->thisValue().toThis(exec, StrictMode);
94    JSObject* thisObj = thisValue.toObject(exec);
95
96    if (!exec->argument(0).isObject())
97        return JSValue::encode(jsBoolean(false));
98
99    JSValue v = asObject(exec->argument(0))->prototype();
100
101    while (true) {
102        if (!v.isObject())
103            return JSValue::encode(jsBoolean(false));
104        if (v == thisObj)
105            return JSValue::encode(jsBoolean(true));
106        v = asObject(v)->prototype();
107    }
108}
109
110EncodedJSValue JSC_HOST_CALL objectProtoFuncDefineGetter(ExecState* exec)
111{
112    JSObject* thisObject = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
113    if (exec->hadException())
114        return JSValue::encode(jsUndefined());
115
116    JSValue get = exec->argument(1);
117    CallData callData;
118    if (getCallData(get, callData) == CallTypeNone)
119        return throwVMError(exec, createTypeError(exec, ASCIILiteral("invalid getter usage")));
120
121    PropertyDescriptor descriptor;
122    descriptor.setGetter(get);
123    descriptor.setEnumerable(true);
124    descriptor.setConfigurable(true);
125    thisObject->methodTable(exec->vm())->defineOwnProperty(thisObject, exec, exec->argument(0).toString(exec)->toIdentifier(exec), descriptor, false);
126
127    return JSValue::encode(jsUndefined());
128}
129
130EncodedJSValue JSC_HOST_CALL objectProtoFuncDefineSetter(ExecState* exec)
131{
132    JSObject* thisObject = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
133    if (exec->hadException())
134        return JSValue::encode(jsUndefined());
135
136    JSValue set = exec->argument(1);
137    CallData callData;
138    if (getCallData(set, callData) == CallTypeNone)
139        return throwVMError(exec, createTypeError(exec, ASCIILiteral("invalid setter usage")));
140
141    PropertyDescriptor descriptor;
142    descriptor.setSetter(set);
143    descriptor.setEnumerable(true);
144    descriptor.setConfigurable(true);
145    thisObject->methodTable(exec->vm())->defineOwnProperty(thisObject, exec, exec->argument(0).toString(exec)->toIdentifier(exec), descriptor, false);
146
147    return JSValue::encode(jsUndefined());
148}
149
150EncodedJSValue JSC_HOST_CALL objectProtoFuncLookupGetter(ExecState* exec)
151{
152    JSObject* thisObject = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
153    if (exec->hadException())
154        return JSValue::encode(jsUndefined());
155
156    PropertySlot slot(thisObject);
157    if (thisObject->getPropertySlot(exec, exec->argument(0).toString(exec)->toIdentifier(exec), slot)
158        && slot.isAccessor()) {
159        JSObject* getter = slot.getterSetter()->getter();
160        return getter ? JSValue::encode(getter) : JSValue::encode(jsUndefined());
161    }
162
163    return JSValue::encode(jsUndefined());
164}
165
166EncodedJSValue JSC_HOST_CALL objectProtoFuncLookupSetter(ExecState* exec)
167{
168    JSObject* thisObject = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
169    if (exec->hadException())
170        return JSValue::encode(jsUndefined());
171
172    PropertySlot slot(thisObject);
173    if (thisObject->getPropertySlot(exec, exec->argument(0).toString(exec)->toIdentifier(exec), slot)
174        && slot.isAccessor()) {
175        JSObject* setter = slot.getterSetter()->setter();
176        return setter ? JSValue::encode(setter) : JSValue::encode(jsUndefined());
177    }
178
179    return JSValue::encode(jsUndefined());
180}
181
182EncodedJSValue JSC_HOST_CALL objectProtoFuncPropertyIsEnumerable(ExecState* exec)
183{
184    JSObject* thisObject = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
185    Identifier propertyName(exec, exec->argument(0).toString(exec)->value(exec));
186
187    PropertyDescriptor descriptor;
188    bool enumerable = thisObject->getOwnPropertyDescriptor(exec, propertyName, descriptor) && descriptor.enumerable();
189    return JSValue::encode(jsBoolean(enumerable));
190}
191
192// 15.2.4.3 Object.prototype.toLocaleString()
193EncodedJSValue JSC_HOST_CALL objectProtoFuncToLocaleString(ExecState* exec)
194{
195    // 1. Let O be the result of calling ToObject passing the this value as the argument.
196    JSObject* object = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
197    if (exec->hadException())
198        return JSValue::encode(jsUndefined());
199
200    // 2. Let toString be the result of calling the [[Get]] internal method of O passing "toString" as the argument.
201    JSValue toString = object->get(exec, exec->propertyNames().toString);
202
203    // 3. If IsCallable(toString) is false, throw a TypeError exception.
204    CallData callData;
205    CallType callType = getCallData(toString, callData);
206    if (callType == CallTypeNone)
207        return JSValue::encode(jsUndefined());
208
209    // 4. Return the result of calling the [[Call]] internal method of toString passing O as the this value and no arguments.
210    return JSValue::encode(call(exec, toString, callType, callData, object, exec->emptyList()));
211}
212
213EncodedJSValue JSC_HOST_CALL objectProtoFuncToString(ExecState* exec)
214{
215    VM& vm = exec->vm();
216    JSValue thisValue = exec->thisValue().toThis(exec, StrictMode);
217    if (thisValue.isUndefinedOrNull())
218        return JSValue::encode(thisValue.isUndefined() ? vm.smallStrings.undefinedObjectString() : vm.smallStrings.nullObjectString());
219    JSObject* thisObject = thisValue.toObject(exec);
220
221    JSString* result = thisObject->structure(vm)->objectToStringValue();
222    if (!result) {
223        RefPtr<StringImpl> newString = WTF::tryMakeString("[object ", thisObject->methodTable(exec->vm())->className(thisObject), "]");
224        if (!newString)
225            return JSValue::encode(throwOutOfMemoryError(exec));
226
227        result = jsNontrivialString(&vm, newString.release());
228        thisObject->structure(vm)->setObjectToStringValue(vm, result);
229    }
230    return JSValue::encode(result);
231}
232
233} // namespace JSC
234