[JSC] Unify how we throw TypeError from C++
[WebKit-https.git] / Source / JavaScriptCore / runtime / ObjectPrototype.cpp
index e94edfa..6e0e7cb 100644 (file)
 #include "ObjectPrototype.h"
 
 #include "Error.h"
+#include "GetterSetter.h"
 #include "JSFunction.h"
 #include "JSString.h"
-#include "JSStringBuilder.h"
+#include "JSCInlines.h"
+#include "PropertySlot.h"
+#include "StructureInlines.h"
+#include "StructureRareDataInlines.h"
 
 namespace JSC {
 
@@ -38,165 +42,214 @@ static EncodedJSValue JSC_HOST_CALL objectProtoFuncLookupSetter(ExecState*);
 static EncodedJSValue JSC_HOST_CALL objectProtoFuncPropertyIsEnumerable(ExecState*);
 static EncodedJSValue JSC_HOST_CALL objectProtoFuncToLocaleString(ExecState*);
 
-}
-
-#include "ObjectPrototype.lut.h"
+STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(ObjectPrototype);
 
-namespace JSC {
+const ClassInfo ObjectPrototype::s_info = { "Object", &JSNonFinalObject::s_info, 0, CREATE_METHOD_TABLE(ObjectPrototype) };
 
-ASSERT_HAS_TRIVIAL_DESTRUCTOR(ObjectPrototype);
-
-const ClassInfo ObjectPrototype::s_info = { "Object", &JSNonFinalObject::s_info, 0, ExecState::objectPrototypeTable, CREATE_METHOD_TABLE(ObjectPrototype) };
-
-/* Source for ObjectPrototype.lut.h
-@begin objectPrototypeTable
-  toString              objectProtoFuncToString                 DontEnum|Function 0
-  toLocaleString        objectProtoFuncToLocaleString           DontEnum|Function 0
-  valueOf               objectProtoFuncValueOf                  DontEnum|Function 0
-  hasOwnProperty        objectProtoFuncHasOwnProperty           DontEnum|Function 1
-  propertyIsEnumerable  objectProtoFuncPropertyIsEnumerable     DontEnum|Function 1
-  isPrototypeOf         objectProtoFuncIsPrototypeOf            DontEnum|Function 1
-  __defineGetter__      objectProtoFuncDefineGetter             DontEnum|Function 2
-  __defineSetter__      objectProtoFuncDefineSetter             DontEnum|Function 2
-  __lookupGetter__      objectProtoFuncLookupGetter             DontEnum|Function 1
-  __lookupSetter__      objectProtoFuncLookupSetter             DontEnum|Function 1
-@end
-*/
-
-ObjectPrototype::ObjectPrototype(ExecState* exec, Structure* stucture)
-    : JSNonFinalObject(exec->globalData(), stucture)
+ObjectPrototype::ObjectPrototype(VM& vm, Structure* stucture)
+    : JSNonFinalObject(vm, stucture)
 {
 }
 
-void ObjectPrototype::finishCreation(JSGlobalData& globalData, JSGlobalObject*)
+void ObjectPrototype::finishCreation(VM& vm, JSGlobalObject* globalObject)
 {
-    Base::finishCreation(globalData);
-    ASSERT(inherits(&s_info));
-    notifyUsedAsPrototype(globalData);
+    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(JSCell* cell, ExecState* exec, PropertyName propertyName, PropertySlot &slot)
+ObjectPrototype* ObjectPrototype::create(VM& vm, JSGlobalObject* globalObject, Structure* structure)
 {
-    return getStaticFunctionSlot<JSNonFinalObject>(exec, ExecState::objectPrototypeTable(exec), jsCast<ObjectPrototype*>(cell), propertyName, slot);
-}
-
-bool ObjectPrototype::getOwnPropertyDescriptor(JSObject* object, ExecState* exec, PropertyName propertyName, PropertyDescriptor& descriptor)
-{
-    return getStaticFunctionDescriptor<JSNonFinalObject>(exec, ExecState::objectPrototypeTable(exec), jsCast<ObjectPrototype*>(object), propertyName, descriptor);
+    ObjectPrototype* prototype = new (NotNull, allocateCell<ObjectPrototype>(vm.heap)) ObjectPrototype(vm, structure);
+    prototype->finishCreation(vm, globalObject);
+    return prototype;
 }
 
 // ------------------------------ Functions --------------------------------
 
 EncodedJSValue JSC_HOST_CALL objectProtoFuncValueOf(ExecState* exec)
 {
-    JSValue thisValue = exec->hostThisValue();
-    return JSValue::encode(thisValue.toObject(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.toObject(exec)->hasOwnProperty(exec, Identifier(exec, exec->argument(0).toString(exec)->value(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();
+    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)
 {
-    JSObject* thisObject = exec->hostThisValue().toObject(exec);
+    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(get, callData) == CallTypeNone)
-        return throwVMError(exec, createTypeError(exec, ASCIILiteral("invalid getter usage")));
+    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);
-    thisObject->methodTable()->defineOwnProperty(thisObject, exec, Identifier(exec, exec->argument(0).toString(exec)->value(exec)), descriptor, false);
+
+    bool shouldThrow = true;
+    thisObject->methodTable(exec->vm())->defineOwnProperty(thisObject, exec, propertyName, descriptor, shouldThrow);
 
     return JSValue::encode(jsUndefined());
 }
 
 EncodedJSValue JSC_HOST_CALL objectProtoFuncDefineSetter(ExecState* exec)
 {
-    JSObject* thisObject = exec->hostThisValue().toObject(exec);
+    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(set, callData) == CallTypeNone)
-        return throwVMError(exec, createTypeError(exec, ASCIILiteral("invalid setter usage")));
+    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);
-    thisObject->methodTable()->defineOwnProperty(thisObject, exec, Identifier(exec, exec->argument(0).toString(exec)->value(exec)), descriptor, false);
+
+    bool shouldThrow = true;
+    thisObject->methodTable(exec->vm())->defineOwnProperty(thisObject, exec, propertyName, descriptor, shouldThrow);
 
     return JSValue::encode(jsUndefined());
 }
 
 EncodedJSValue JSC_HOST_CALL objectProtoFuncLookupGetter(ExecState* exec)
 {
-    JSObject* thisObject = exec->hostThisValue().toObject(exec);
+    JSObject* thisObject = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
     if (exec->hadException())
         return JSValue::encode(jsUndefined());
 
-    PropertyDescriptor descriptor;
-    if (thisObject->getPropertyDescriptor(exec, Identifier(exec, exec->argument(0).toString(exec)->value(exec)), descriptor)
-        && descriptor.getterPresent())
-        return JSValue::encode(descriptor.getter());
+    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)
 {
-    JSObject* thisObject = exec->hostThisValue().toObject(exec);
+    JSObject* thisObject = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
     if (exec->hadException())
         return JSValue::encode(jsUndefined());
 
-    PropertyDescriptor descriptor;
-    if (thisObject->getPropertyDescriptor(exec, Identifier(exec, exec->argument(0).toString(exec)->value(exec)), descriptor)
-        && descriptor.setterPresent())
-        return JSValue::encode(descriptor.setter());
+    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.toObject(exec)->propertyIsEnumerable(exec, Identifier(exec, exec->argument(0).toString(exec)->value(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)
 {
     // 1. Let O be the result of calling ToObject passing the this value as the argument.
-    JSObject* object = exec->hostThisValue().toObject(exec);
+    JSObject* object = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
     if (exec->hadException())
         return JSValue::encode(jsUndefined());
 
@@ -206,7 +259,7 @@ EncodedJSValue JSC_HOST_CALL objectProtoFuncToLocaleString(ExecState* exec)
     // 3. If IsCallable(toString) is false, throw a TypeError exception.
     CallData callData;
     CallType callType = getCallData(toString, callData);
-    if (callType == CallTypeNone)
+    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.
@@ -215,21 +268,44 @@ EncodedJSValue JSC_HOST_CALL objectProtoFuncToLocaleString(ExecState* exec)
 
 EncodedJSValue JSC_HOST_CALL objectProtoFuncToString(ExecState* exec)
 {
-    JSValue thisValue = exec->hostThisValue();
+    VM& vm = exec->vm();
+    JSValue thisValue = exec->thisValue().toThis(exec, StrictMode);
     if (thisValue.isUndefinedOrNull())
-        return JSValue::encode(jsNontrivialString(exec, String(thisValue.isUndefined() ? ASCIILiteral("[object Undefined]") : ASCIILiteral("[object Null]"))));
+        return JSValue::encode(thisValue.isUndefined() ? vm.smallStrings.undefinedObjectString() : vm.smallStrings.nullObjectString());
     JSObject* thisObject = thisValue.toObject(exec);
+    if (!thisObject)
+        return JSValue::encode(jsUndefined());
 
-    JSString* result = thisObject->structure()->objectToStringValue();
-    if (!result) {
-        RefPtr<StringImpl> newString = WTF::tryMakeString("[object ", thisObject->methodTable()->className(thisObject), "]");
+    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 JSValue::encode(throwOutOfMemoryError(exec));
+            return throwOutOfMemoryError(exec);
 
-        result = jsNontrivialString(exec, newString.release());
-        thisObject->structure()->setObjectToStringValue(exec->globalData(), thisObject, result);
-    }
-    return JSValue::encode(result);
+        auto result = jsNontrivialString(&vm, newString);
+        thisObject->structure(vm)->setObjectToStringValue(exec, vm, result, toStringTagSlot);
+        return result;
+    }));
 }
 
 } // namespace JSC