// Based on ErrorPrototype's errorProtoFuncToString(), but is modified to // have no observable side effects to the user (i.e. does not call proxies, // and getters). String ErrorInstance::sanitizedToString(ExecState* exec) { VM& vm = exec->vm(); auto scope = DECLARE_THROW_SCOPE(vm); JSValue nameValue; auto namePropertName = vm.propertyNames->name; PropertySlot nameSlot(this, PropertySlot::InternalMethodType::VMInquiry); JSValue currentObj = this; unsigned prototypeDepth = 0; // We only check the current object and its prototype (2 levels) because normal // Error objects may have a name property, and if not, its prototype should have // a name property for the type of error e.g. "SyntaxError". while (currentObj.isCell() && prototypeDepth++ < 2) { JSObject* obj = jsCast<JSObject*>(currentObj); if (JSObject::getOwnPropertySlot(obj, exec, namePropertName, nameSlot) && nameSlot.isValue()) { nameValue = nameSlot.getValue(exec, namePropertName); break; } currentObj = obj->getPrototypeDirect(); } ASSERT(!scope.exception()); String nameString; if (!nameValue) nameString = ASCIILiteral("Error"); else { nameString = nameValue.toWTFString(exec); RETURN_IF_EXCEPTION(scope, String()); } JSValue messageValue; auto messagePropertName = vm.propertyNames->message; PropertySlot messageSlot(this, PropertySlot::InternalMethodType::VMInquiry); if (JSObject::getOwnPropertySlot(this, exec, messagePropertName, messageSlot) && messageSlot.isValue()) messageValue = messageSlot.getValue(exec, messagePropertName); ASSERT(!scope.exception()); String messageString; if (!messageValue) messageString = String(); else { messageString = messageValue.toWTFString(exec); RETURN_IF_EXCEPTION(scope, String()); } if (!nameString.length()) return messageString; if (!messageString.length()) return nameString; StringBuilder builder; builder.append(nameString); builder.appendLiteral(": "); builder.append(messageString); return builder.toString(); }
// ECMA 8.7.2 bool JSValue::putToPrimitive(ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot) { VM& vm = exec->vm(); auto scope = DECLARE_THROW_SCOPE(vm); if (Optional<uint32_t> index = parseIndex(propertyName)) return putToPrimitiveByIndex(exec, index.value(), value, slot.isStrictMode()); // Check if there are any setters or getters in the prototype chain JSObject* obj = synthesizePrototype(exec); if (UNLIKELY(!obj)) return false; JSValue prototype; if (propertyName != exec->propertyNames().underscoreProto) { for (; !obj->structure()->hasReadOnlyOrGetterSetterPropertiesExcludingProto(); obj = asObject(prototype)) { prototype = obj->getPrototypeDirect(); if (prototype.isNull()) { if (slot.isStrictMode()) throwTypeError(exec, scope, StrictModeReadonlyPropertyWriteError); return false; } } } for (; ; obj = asObject(prototype)) { unsigned attributes; PropertyOffset offset = obj->structure()->get(vm, propertyName, attributes); if (offset != invalidOffset) { if (attributes & ReadOnly) { if (slot.isStrictMode()) throwTypeError(exec, scope, StrictModeReadonlyPropertyWriteError); return false; } JSValue gs = obj->getDirect(offset); if (gs.isGetterSetter()) return callSetter(exec, *this, gs, value, slot.isStrictMode() ? StrictMode : NotStrictMode); if (gs.isCustomGetterSetter()) return callCustomSetter(exec, gs, attributes & CustomAccessor, obj, slot.thisValue(), value); // If there's an existing property on the object or one of its // prototypes it should be replaced, so break here. break; } prototype = obj->getPrototype(vm, exec); if (vm.exception()) return false; if (prototype.isNull()) break; } if (slot.isStrictMode()) throwTypeError(exec, scope, StrictModeReadonlyPropertyWriteError); return false; }
JSValueRef JSObjectGetPrototype(JSContextRef ctx, JSObjectRef object) { if (!ctx) { ASSERT_NOT_REACHED(); return 0; } ExecState* exec = toJS(ctx); JSLockHolder locker(exec); JSObject* jsObject = toJS(object); return toRef(exec, jsObject->getPrototypeDirect(exec->vm())); }