[JSC] Unify how we throw TypeError from C++
[WebKit-https.git] / Source / JavaScriptCore / runtime / ObjectPrototype.cpp
index 57a8a31..6e0e7cb 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
- *  Copyright (C) 2008 Apple Inc. All rights reserved.
+ *  Copyright (C) 2008, 2011 Apple Inc. All rights reserved.
  *
  *  This library is free software; you can redistribute it and/or
  *  modify it under the terms of the GNU Lesser General Public
 #include "ObjectPrototype.h"
 
 #include "Error.h"
+#include "GetterSetter.h"
 #include "JSFunction.h"
 #include "JSString.h"
-#include "JSStringBuilder.h"
-#include "PrototypeFunction.h"
+#include "JSCInlines.h"
+#include "PropertySlot.h"
+#include "StructureInlines.h"
+#include "StructureRareDataInlines.h"
 
 namespace JSC {
 
-ASSERT_CLASS_FITS_IN_CELL(ObjectPrototype);
-
 static EncodedJSValue JSC_HOST_CALL objectProtoFuncValueOf(ExecState*);
 static EncodedJSValue JSC_HOST_CALL objectProtoFuncHasOwnProperty(ExecState*);
 static EncodedJSValue JSC_HOST_CALL objectProtoFuncIsPrototypeOf(ExecState*);
@@ -41,125 +42,270 @@ static EncodedJSValue JSC_HOST_CALL objectProtoFuncLookupSetter(ExecState*);
 static EncodedJSValue JSC_HOST_CALL objectProtoFuncPropertyIsEnumerable(ExecState*);
 static EncodedJSValue JSC_HOST_CALL objectProtoFuncToLocaleString(ExecState*);
 
-ObjectPrototype::ObjectPrototype(ExecState* exec, JSGlobalObject* globalObject, NonNullPassRefPtr<Structure> stucture, Structure* prototypeFunctionStructure)
-    : JSObject(stucture)
-    , m_hasNoPropertiesWithUInt32Names(true)
+STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(ObjectPrototype);
+
+const ClassInfo ObjectPrototype::s_info = { "Object", &JSNonFinalObject::s_info, 0, CREATE_METHOD_TABLE(ObjectPrototype) };
+
+ObjectPrototype::ObjectPrototype(VM& vm, Structure* stucture)
+    : JSNonFinalObject(vm, stucture)
 {
-    putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 0, exec->propertyNames().toString, objectProtoFuncToString), DontEnum);
-    putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 0, exec->propertyNames().toLocaleString, objectProtoFuncToLocaleString), DontEnum);
-    putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 0, exec->propertyNames().valueOf, objectProtoFuncValueOf), DontEnum);
-    putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 1, exec->propertyNames().hasOwnProperty, objectProtoFuncHasOwnProperty), DontEnum);
-    putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 1, exec->propertyNames().propertyIsEnumerable, objectProtoFuncPropertyIsEnumerable), DontEnum);
-    putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 1, exec->propertyNames().isPrototypeOf, objectProtoFuncIsPrototypeOf), DontEnum);
-
-    // Mozilla extensions
-    putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 2, exec->propertyNames().__defineGetter__, objectProtoFuncDefineGetter), DontEnum);
-    putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 2, exec->propertyNames().__defineSetter__, objectProtoFuncDefineSetter), DontEnum);
-    putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 1, exec->propertyNames().__lookupGetter__, objectProtoFuncLookupGetter), DontEnum);
-    putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 1, exec->propertyNames().__lookupSetter__, objectProtoFuncLookupSetter), DontEnum);
 }
 
-void ObjectPrototype::put(ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot)
+void ObjectPrototype::finishCreation(VM& vm, JSGlobalObject* globalObject)
 {
-    JSObject::put(exec, propertyName, value, slot);
-
-    if (m_hasNoPropertiesWithUInt32Names) {
-        bool isUInt32;
-        propertyName.toUInt32(isUInt32);
-        m_hasNoPropertiesWithUInt32Names = !isUInt32;
-    }
+    Base::finishCreation(vm);
+    ASSERT(inherits(info()));
+    vm.prototypeMap.addPrototype(this);
+    
+    JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->toString, objectProtoFuncToString, DontEnum, 0);
+    JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->toLocaleString, objectProtoFuncToLocaleString, DontEnum, 0);
+    JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->valueOf, objectProtoFuncValueOf, DontEnum, 0);
+    JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->hasOwnProperty, objectProtoFuncHasOwnProperty, DontEnum, 1);
+    JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->propertyIsEnumerable, objectProtoFuncPropertyIsEnumerable, DontEnum, 1);
+    JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->isPrototypeOf, objectProtoFuncIsPrototypeOf, DontEnum, 1);
+    JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->__defineGetter__, objectProtoFuncDefineGetter, DontEnum, 2);
+    JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->__defineSetter__, objectProtoFuncDefineSetter, DontEnum, 2);
+    JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->__lookupGetter__, objectProtoFuncLookupGetter, DontEnum, 1);
+    JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->__lookupSetter__, objectProtoFuncLookupSetter, DontEnum, 1);
 }
 
-bool ObjectPrototype::getOwnPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot)
+ObjectPrototype* ObjectPrototype::create(VM& vm, JSGlobalObject* globalObject, Structure* structure)
 {
-    if (m_hasNoPropertiesWithUInt32Names)
-        return false;
-    return JSObject::getOwnPropertySlot(exec, propertyName, slot);
+    ObjectPrototype* prototype = new (NotNull, allocateCell<ObjectPrototype>(vm.heap)) ObjectPrototype(vm, structure);
+    prototype->finishCreation(vm, globalObject);
+    return prototype;
 }
 
 // ------------------------------ Functions --------------------------------
 
-// ECMA 15.2.4.2, 15.2.4.4, 15.2.4.5, 15.2.4.7
-
 EncodedJSValue JSC_HOST_CALL objectProtoFuncValueOf(ExecState* exec)
 {
-    JSValue thisValue = exec->hostThisValue();
-    return JSValue::encode(thisValue.toThisObject(exec));
+    JSValue thisValue = exec->thisValue().toThis(exec, StrictMode);
+    JSObject* valueObj = thisValue.toObject(exec);
+    if (!valueObj)
+        return JSValue::encode(JSValue());
+    return JSValue::encode(valueObj);
 }
 
 EncodedJSValue JSC_HOST_CALL objectProtoFuncHasOwnProperty(ExecState* exec)
 {
-    JSValue thisValue = exec->hostThisValue();
-    return JSValue::encode(jsBoolean(thisValue.toThisObject(exec)->hasOwnProperty(exec, Identifier(exec, exec->argument(0).toString(exec)))));
+    JSValue thisValue = exec->thisValue().toThis(exec, StrictMode);
+    auto propertyName = exec->argument(0).toPropertyKey(exec);
+    if (exec->hadException())
+        return JSValue::encode(jsUndefined());
+    JSObject* thisObject = thisValue.toObject(exec);
+    if (!thisObject)
+        return JSValue::encode(JSValue());
+    return JSValue::encode(jsBoolean(thisObject->hasOwnProperty(exec, propertyName)));
 }
 
 EncodedJSValue JSC_HOST_CALL objectProtoFuncIsPrototypeOf(ExecState* exec)
 {
-    JSValue thisValue = exec->hostThisValue();
-    JSObject* thisObj = thisValue.toThisObject(exec);
+    JSValue thisValue = exec->thisValue().toThis(exec, StrictMode);
+    JSObject* thisObj = thisValue.toObject(exec);
+    if (!thisObj)
+        return JSValue::encode(JSValue());
 
     if (!exec->argument(0).isObject())
         return JSValue::encode(jsBoolean(false));
 
-    JSValue v = asObject(exec->argument(0))->prototype();
+    VM& vm = exec->vm();
+    JSValue v = asObject(exec->argument(0))->getPrototype(vm, exec);
+    if (UNLIKELY(vm.exception()))
+        return JSValue::encode(JSValue());
 
     while (true) {
         if (!v.isObject())
             return JSValue::encode(jsBoolean(false));
         if (v == thisObj)
             return JSValue::encode(jsBoolean(true));
-        v = asObject(v)->prototype();
+        v = asObject(v)->getPrototype(vm, exec);
+        if (UNLIKELY(vm.exception()))
+            return JSValue::encode(JSValue());
     }
 }
 
 EncodedJSValue JSC_HOST_CALL objectProtoFuncDefineGetter(ExecState* exec)
 {
-    JSValue thisValue = exec->hostThisValue();
+    JSObject* thisObject = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
+    if (exec->hadException())
+        return JSValue::encode(jsUndefined());
+
+    JSValue get = exec->argument(1);
     CallData callData;
-    if (getCallData(exec->argument(1), callData) == CallTypeNone)
-        return throwVMError(exec, createSyntaxError(exec, "invalid getter usage"));
-    thisValue.toThisObject(exec)->defineGetter(exec, Identifier(exec, exec->argument(0).toString(exec)), asObject(exec->argument(1)));
+    if (getCallData(get, callData) == CallType::None)
+        return throwVMTypeError(exec, ASCIILiteral("invalid getter usage"));
+
+    auto propertyName = exec->argument(0).toPropertyKey(exec);
+    if (exec->hadException())
+        return JSValue::encode(jsUndefined());
+
+    PropertyDescriptor descriptor;
+    descriptor.setGetter(get);
+    descriptor.setEnumerable(true);
+    descriptor.setConfigurable(true);
+
+    bool shouldThrow = true;
+    thisObject->methodTable(exec->vm())->defineOwnProperty(thisObject, exec, propertyName, descriptor, shouldThrow);
+
     return JSValue::encode(jsUndefined());
 }
 
 EncodedJSValue JSC_HOST_CALL objectProtoFuncDefineSetter(ExecState* exec)
 {
-    JSValue thisValue = exec->hostThisValue();
+    JSObject* thisObject = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
+    if (exec->hadException())
+        return JSValue::encode(jsUndefined());
+
+    JSValue set = exec->argument(1);
     CallData callData;
-    if (getCallData(exec->argument(1), callData) == CallTypeNone)
-        return throwVMError(exec, createSyntaxError(exec, "invalid setter usage"));
-    thisValue.toThisObject(exec)->defineSetter(exec, Identifier(exec, exec->argument(0).toString(exec)), asObject(exec->argument(1)));
+    if (getCallData(set, callData) == CallType::None)
+        return throwVMTypeError(exec, ASCIILiteral("invalid setter usage"));
+
+    auto propertyName = exec->argument(0).toPropertyKey(exec);
+    if (exec->hadException())
+        return JSValue::encode(jsUndefined());
+
+    PropertyDescriptor descriptor;
+    descriptor.setSetter(set);
+    descriptor.setEnumerable(true);
+    descriptor.setConfigurable(true);
+
+    bool shouldThrow = true;
+    thisObject->methodTable(exec->vm())->defineOwnProperty(thisObject, exec, propertyName, descriptor, shouldThrow);
+
     return JSValue::encode(jsUndefined());
 }
 
 EncodedJSValue JSC_HOST_CALL objectProtoFuncLookupGetter(ExecState* exec)
 {
-    JSValue thisValue = exec->hostThisValue();
-    return JSValue::encode(thisValue.toThisObject(exec)->lookupGetter(exec, Identifier(exec, exec->argument(0).toString(exec))));
+    JSObject* thisObject = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
+    if (exec->hadException())
+        return JSValue::encode(jsUndefined());
+
+    auto propertyName = exec->argument(0).toPropertyKey(exec);
+    if (exec->hadException())
+        return JSValue::encode(jsUndefined());
+
+    PropertySlot slot(thisObject, PropertySlot::InternalMethodType::GetOwnProperty);
+    if (thisObject->getPropertySlot(exec, propertyName, slot)) {
+        if (slot.isAccessor()) {
+            GetterSetter* getterSetter = slot.getterSetter();
+            return getterSetter->isGetterNull() ? JSValue::encode(jsUndefined()) : JSValue::encode(getterSetter->getter());
+        }
+        if (slot.attributes() & CustomAccessor) {
+            PropertyDescriptor descriptor;
+            ASSERT(slot.slotBase());
+            if (slot.slotBase()->getOwnPropertyDescriptor(exec, propertyName, descriptor))
+                return descriptor.getterPresent() ? JSValue::encode(descriptor.getter()) : JSValue::encode(jsUndefined());
+        }
+    }
+
+    return JSValue::encode(jsUndefined());
 }
 
 EncodedJSValue JSC_HOST_CALL objectProtoFuncLookupSetter(ExecState* exec)
 {
-    JSValue thisValue = exec->hostThisValue();
-    return JSValue::encode(thisValue.toThisObject(exec)->lookupSetter(exec, Identifier(exec, exec->argument(0).toString(exec))));
+    JSObject* thisObject = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
+    if (exec->hadException())
+        return JSValue::encode(jsUndefined());
+
+    auto propertyName = exec->argument(0).toPropertyKey(exec);
+    if (exec->hadException())
+        return JSValue::encode(jsUndefined());
+
+    PropertySlot slot(thisObject, PropertySlot::InternalMethodType::GetOwnProperty);
+    if (thisObject->getPropertySlot(exec, propertyName, slot)) {
+        if (slot.isAccessor()) {
+            GetterSetter* getterSetter = slot.getterSetter();
+            return getterSetter->isSetterNull() ? JSValue::encode(jsUndefined()) : JSValue::encode(getterSetter->setter());
+        }
+        if (slot.attributes() & CustomAccessor) {
+            PropertyDescriptor descriptor;
+            ASSERT(slot.slotBase());
+            if (slot.slotBase()->getOwnPropertyDescriptor(exec, propertyName, descriptor))
+                return descriptor.setterPresent() ? JSValue::encode(descriptor.setter()) : JSValue::encode(jsUndefined());
+        }
+    }
+
+    return JSValue::encode(jsUndefined());
 }
 
 EncodedJSValue JSC_HOST_CALL objectProtoFuncPropertyIsEnumerable(ExecState* exec)
 {
-    JSValue thisValue = exec->hostThisValue();
-    return JSValue::encode(jsBoolean(thisValue.toThisObject(exec)->propertyIsEnumerable(exec, Identifier(exec, exec->argument(0).toString(exec)))));
+    auto propertyName = exec->argument(0).toPropertyKey(exec);
+    if (exec->hadException())
+        return JSValue::encode(jsUndefined());
+
+    JSObject* thisObject = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
+    if (exec->hadException())
+        return JSValue::encode(jsUndefined());
+    PropertyDescriptor descriptor;
+    bool enumerable = thisObject->getOwnPropertyDescriptor(exec, propertyName, descriptor) && descriptor.enumerable();
+    return JSValue::encode(jsBoolean(enumerable));
 }
 
+// 15.2.4.3 Object.prototype.toLocaleString()
 EncodedJSValue JSC_HOST_CALL objectProtoFuncToLocaleString(ExecState* exec)
 {
-    JSValue thisValue = exec->hostThisValue();
-    return JSValue::encode(thisValue.toThisJSString(exec));
+    // 1. Let O be the result of calling ToObject passing the this value as the argument.
+    JSObject* object = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
+    if (exec->hadException())
+        return JSValue::encode(jsUndefined());
+
+    // 2. Let toString be the result of calling the [[Get]] internal method of O passing "toString" as the argument.
+    JSValue toString = object->get(exec, exec->propertyNames().toString);
+
+    // 3. If IsCallable(toString) is false, throw a TypeError exception.
+    CallData callData;
+    CallType callType = getCallData(toString, callData);
+    if (callType == CallType::None)
+        return JSValue::encode(jsUndefined());
+
+    // 4. Return the result of calling the [[Call]] internal method of toString passing O as the this value and no arguments.
+    return JSValue::encode(call(exec, toString, callType, callData, object, exec->emptyList()));
 }
 
 EncodedJSValue JSC_HOST_CALL objectProtoFuncToString(ExecState* exec)
 {
-    JSValue thisValue = exec->hostThisValue();
-    return JSValue::encode(jsMakeNontrivialString(exec, "[object ", thisValue.toThisObject(exec)->className(), "]"));
+    VM& vm = exec->vm();
+    JSValue thisValue = exec->thisValue().toThis(exec, StrictMode);
+    if (thisValue.isUndefinedOrNull())
+        return JSValue::encode(thisValue.isUndefined() ? vm.smallStrings.undefinedObjectString() : vm.smallStrings.nullObjectString());
+    JSObject* thisObject = thisValue.toObject(exec);
+    if (!thisObject)
+        return JSValue::encode(jsUndefined());
+
+    auto result = thisObject->structure(vm)->objectToStringValue();
+    if (result)
+        return JSValue::encode(result);
+
+    PropertyName toStringTagSymbol = exec->propertyNames().toStringTagSymbol;
+    return JSValue::encode(thisObject->getPropertySlot(exec, toStringTagSymbol, [&] (bool found, PropertySlot& toStringTagSlot) -> JSValue {
+        if (found) {
+            JSValue stringTag = toStringTagSlot.getValue(exec, toStringTagSymbol);
+            if (UNLIKELY(vm.exception()))
+                return jsUndefined();
+            if (stringTag.isString()) {
+                JSRopeString::RopeBuilder ropeBuilder(vm);
+                ropeBuilder.append(vm.smallStrings.objectStringStart());
+                ropeBuilder.append(jsCast<JSString*>(stringTag));
+                ropeBuilder.append(vm.smallStrings.singleCharacterString(']'));
+                JSString* result = ropeBuilder.release();
+
+                thisObject->structure(vm)->setObjectToStringValue(exec, vm, result, toStringTagSlot);
+                return result;
+            }
+        }
+
+        String newString = WTF::tryMakeString("[object ", thisObject->methodTable(exec->vm())->className(thisObject), "]");
+        if (!newString)
+            return throwOutOfMemoryError(exec);
+
+        auto result = jsNontrivialString(&vm, newString);
+        thisObject->structure(vm)->setObjectToStringValue(exec, vm, result, toStringTagSlot);
+        return result;
+    }));
 }
 
 } // namespace JSC