PassRefPtr<ScriptCallStack> createScriptCallStackFromException(JSC::ExecState* exec, JSC::JSValue& exception, size_t maxStackSize) { Vector<ScriptCallFrame> frames; RefCountedArray<StackFrame> stackTrace = exec->vm().exceptionStack(); for (size_t i = 0; i < stackTrace.size() && i < maxStackSize; i++) { if (!stackTrace[i].callee && frames.size()) break; String functionName = stackTrace[i].friendlyFunctionName(exec); unsigned line; unsigned column; stackTrace[i].computeLineAndColumn(line, column); frames.append(ScriptCallFrame(functionName, stackTrace[i].sourceURL, line, column)); } // FIXME: <http://webkit.org/b/115087> Web Inspector: WebCore::reportException should not evaluate JavaScript handling exceptions // Fallback to getting at least the line and sourceURL from the exception if it has values and the exceptionStack doesn't. if (frames.size() > 0) { const ScriptCallFrame& firstCallFrame = frames.first(); JSObject* exceptionObject = exception.toObject(exec); if (exception.isObject() && firstCallFrame.sourceURL().isEmpty()) { JSValue lineValue = exceptionObject->getDirect(exec->vm(), Identifier(exec, "line")); int lineNumber = lineValue && lineValue.isNumber() ? int(lineValue.toNumber(exec)) : 0; JSValue sourceURLValue = exceptionObject->getDirect(exec->vm(), Identifier(exec, "sourceURL")); String exceptionSourceURL = sourceURLValue && sourceURLValue.isString() ? sourceURLValue.toString(exec)->value(exec) : ASCIILiteral("undefined"); frames[0] = ScriptCallFrame(firstCallFrame.functionName(), exceptionSourceURL, lineNumber, 0); } } return ScriptCallStack::create(frames); }
void reportException(ExecState* exec, JSValue exception, CachedScript* cachedScript) { if (isTerminatedExecutionException(exception)) return; Interpreter::ErrorHandlingMode mode(exec); RefPtr<ScriptCallStack> callStack(createScriptCallStackFromException(exec, exception, ScriptCallStack::maxCallStackSizeToCapture)); exec->clearException(); exec->clearSupplementaryExceptionInfo(); JSDOMGlobalObject* globalObject = jsCast<JSDOMGlobalObject*>(exec->lexicalGlobalObject()); if (JSDOMWindow* window = jsDynamicCast<JSDOMWindow*>(globalObject)) { if (!window->impl()->isCurrentlyDisplayedInFrame()) return; } int lineNumber = 0; int columnNumber = 0; String exceptionSourceURL; if (callStack->size()) { const ScriptCallFrame& frame = callStack->at(0); lineNumber = frame.lineNumber(); columnNumber = frame.columnNumber(); exceptionSourceURL = frame.sourceURL(); } else { // There may not be an exceptionStack for a <script> SyntaxError. Fallback to getting at least the line and sourceURL from the exception. JSObject* exceptionObject = exception.toObject(exec); JSValue lineValue = exceptionObject->getDirect(exec->vm(), Identifier(exec, "line")); lineNumber = lineValue && lineValue.isNumber() ? int(lineValue.toNumber(exec)) : 0; JSValue columnValue = exceptionObject->getDirect(exec->vm(), Identifier(exec, "column")); columnNumber = columnValue && columnValue.isNumber() ? int(columnValue.toNumber(exec)) : 0; JSValue sourceURLValue = exceptionObject->getDirect(exec->vm(), Identifier(exec, "sourceURL")); exceptionSourceURL = sourceURLValue && sourceURLValue.isString() ? sourceURLValue.toString(exec)->value(exec) : ASCIILiteral("undefined"); } String errorMessage; if (ExceptionBase* exceptionBase = toExceptionBase(exception)) errorMessage = exceptionBase->message() + ": " + exceptionBase->description(); else { // FIXME: <http://webkit.org/b/115087> Web Inspector: WebCore::reportException should not evaluate JavaScript handling exceptions // If this is a custon exception object, call toString on it to try and get a nice string representation for the exception. errorMessage = exception.toString(exec)->value(exec); exec->clearException(); exec->clearSupplementaryExceptionInfo(); } ScriptExecutionContext* scriptExecutionContext = globalObject->scriptExecutionContext(); scriptExecutionContext->reportException(errorMessage, lineNumber, columnNumber, exceptionSourceURL, callStack->size() ? callStack : 0, cachedScript); }
// ECMA 8.6.2.2 void JSObject::put(ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot) { ASSERT(value); ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this)); if (propertyName == exec->propertyNames().underscoreProto) { // Setting __proto__ to a non-object, non-null value is silently ignored to match Mozilla. if (!value.isObject() && !value.isNull()) return; if (!setPrototypeWithCycleCheck(value)) throwError(exec, createError(exec, "cyclic __proto__ value")); return; } // Check if there are any setters or getters in the prototype chain JSValue prototype; for (JSObject* obj = this; !obj->structure()->hasGetterSetterProperties(); obj = asObject(prototype)) { prototype = obj->prototype(); if (prototype.isNull()) { putDirectInternal(exec->globalData(), propertyName, value, 0, true, slot); return; } } unsigned attributes; JSCell* specificValue; if ((m_structure->get(propertyName, attributes, specificValue) != WTF::notFound) && attributes & ReadOnly) return; for (JSObject* obj = this; ; obj = asObject(prototype)) { if (JSValue gs = obj->getDirect(propertyName)) { if (gs.isGetterSetter()) { JSObject* setterFunc = asGetterSetter(gs)->setter(); if (!setterFunc) { throwSetterError(exec); return; } CallData callData; CallType callType = setterFunc->getCallData(callData); MarkedArgumentBuffer args; args.append(value); call(exec, setterFunc, callType, callData, this, args); return; } // If there's an existing property on the object or one of its // prototypes it should be replaced, so break here. break; } prototype = obj->prototype(); if (prototype.isNull()) break; } putDirectInternal(exec->globalData(), propertyName, value, 0, true, slot); return; }
// 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; }
JSValue* staticFunctionGetter(ExecState* exec, const Identifier& propertyName, const PropertySlot& slot) { // Look for cached value in dynamic map of properties (in JSObject) ASSERT(slot.slotBase()->isObject()); JSObject* thisObj = static_cast<JSObject*>(slot.slotBase()); JSValue* cachedVal = thisObj->getDirect(propertyName); if (cachedVal) return cachedVal; const HashEntry* entry = slot.staticEntry(); JSValue* val = new (exec) PrototypeFunction(exec, entry->length, propertyName, entry->functionValue); thisObj->putDirect(propertyName, val, entry->attributes); return val; }
JSValue JSObject::lookupSetter(ExecState*, const Identifier& propertyName) { JSObject* object = this; while (true) { if (JSValue value = object->getDirect(propertyName)) { if (!value.isGetterSetter()) return jsUndefined(); JSObject* functionObject = asGetterSetter(value)->setter(); if (!functionObject) return jsUndefined(); return functionObject; } if (!object->prototype() || !object->prototype().isObject()) return jsUndefined(); object = asObject(object->prototype()); } }
JSValue* JSObject::lookupSetter(ExecState*, const Identifier& propertyName) { JSObject* object = this; while (true) { JSValue* value = object->getDirect(propertyName); if (value) { if (!value->isGetterSetter()) return jsUndefined(); JSObject* functionObject = static_cast<GetterSetter*>(value)->setter(); if (!functionObject) return jsUndefined(); return functionObject; } if (!object->prototype() || !object->prototype()->isObject()) return jsUndefined(); object = static_cast<JSObject*>(object->prototype()); } }
JSValue *ObjectProtoFunc::callAsFunction(ExecState *exec, JSObject *thisObj, const List &args) { switch (id) { case ValueOf: return thisObj; case HasOwnProperty: { PropertySlot slot; return jsBoolean(thisObj->getOwnPropertySlot(exec, Identifier(args[0]->toString(exec)), slot)); } case IsPrototypeOf: { if (!args[0]->isObject()) return jsBoolean(false); JSValue *v = static_cast<JSObject *>(args[0])->prototype(); while (true) { if (!v->isObject()) return jsBoolean(false); if (thisObj == static_cast<JSObject *>(v)) return jsBoolean(true); v = static_cast<JSObject *>(v)->prototype(); } } case DefineGetter: case DefineSetter: { if (!args[1]->isObject() || !static_cast<JSObject *>(args[1])->implementsCall()) { if (id == DefineGetter) return throwError(exec, SyntaxError, "invalid getter usage"); else return throwError(exec, SyntaxError, "invalid setter usage"); } if (id == DefineGetter) thisObj->defineGetter(exec, Identifier(args[0]->toString(exec)), static_cast<JSObject *>(args[1])); else thisObj->defineSetter(exec, Identifier(args[0]->toString(exec)), static_cast<JSObject *>(args[1])); return jsUndefined(); } case LookupGetter: case LookupSetter: { Identifier propertyName = Identifier(args[0]->toString(exec)); JSObject *obj = thisObj; while (true) { JSValue *v = obj->getDirect(propertyName); if (v) { if (v->type() != GetterSetterType) return jsUndefined(); JSObject *funcObj; if (id == LookupGetter) funcObj = static_cast<GetterSetterImp *>(v)->getGetter(); else funcObj = static_cast<GetterSetterImp *>(v)->getSetter(); if (!funcObj) return jsUndefined(); else return funcObj; } if (!obj->prototype() || !obj->prototype()->isObject()) return jsUndefined(); obj = static_cast<JSObject *>(obj->prototype()); } } case PropertyIsEnumerable: return jsBoolean(thisObj->propertyIsEnumerable(exec, Identifier(args[0]->toString(exec)))); case ToLocaleString: return jsString(thisObj->toString(exec)); case ToString: default: return jsString("[object " + thisObj->className() + "]"); } }
JSValue JSInjectedScriptHost::subtype(ExecState* exec) { if (exec->argumentCount() < 1) return jsUndefined(); JSValue value = exec->uncheckedArgument(0); if (value.isString()) return exec->vm().smallStrings.stringString(); if (value.isBoolean()) return exec->vm().smallStrings.booleanString(); if (value.isNumber()) return exec->vm().smallStrings.numberString(); if (value.isSymbol()) return exec->vm().smallStrings.symbolString(); JSObject* object = asObject(value); if (object) { if (object->isErrorInstance()) return jsNontrivialString(exec, ASCIILiteral("error")); // Consider class constructor functions class objects. JSFunction* function = jsDynamicCast<JSFunction*>(value); if (function && function->isClassConstructorFunction()) return jsNontrivialString(exec, ASCIILiteral("class")); } if (value.inherits(JSArray::info())) return jsNontrivialString(exec, ASCIILiteral("array")); if (value.inherits(DirectArguments::info()) || value.inherits(ScopedArguments::info())) return jsNontrivialString(exec, ASCIILiteral("array")); if (value.inherits(DateInstance::info())) return jsNontrivialString(exec, ASCIILiteral("date")); if (value.inherits(RegExpObject::info())) return jsNontrivialString(exec, ASCIILiteral("regexp")); if (value.inherits(JSMap::info())) return jsNontrivialString(exec, ASCIILiteral("map")); if (value.inherits(JSSet::info())) return jsNontrivialString(exec, ASCIILiteral("set")); if (value.inherits(JSWeakMap::info())) return jsNontrivialString(exec, ASCIILiteral("weakmap")); if (value.inherits(JSWeakSet::info())) return jsNontrivialString(exec, ASCIILiteral("weakset")); if (value.inherits(JSArrayIterator::info()) || value.inherits(JSMapIterator::info()) || value.inherits(JSSetIterator::info()) || value.inherits(JSStringIterator::info()) || value.inherits(JSPropertyNameIterator::info())) return jsNontrivialString(exec, ASCIILiteral("iterator")); if (object && object->getDirect(exec->vm(), exec->vm().propertyNames->builtinNames().arrayIteratorNextIndexPrivateName())) return jsNontrivialString(exec, ASCIILiteral("iterator")); if (value.inherits(JSInt8Array::info()) || value.inherits(JSInt16Array::info()) || value.inherits(JSInt32Array::info())) return jsNontrivialString(exec, ASCIILiteral("array")); if (value.inherits(JSUint8Array::info()) || value.inherits(JSUint16Array::info()) || value.inherits(JSUint32Array::info())) return jsNontrivialString(exec, ASCIILiteral("array")); if (value.inherits(JSFloat32Array::info()) || value.inherits(JSFloat64Array::info())) return jsNontrivialString(exec, ASCIILiteral("array")); return impl().subtype(exec, value); }
// ECMA 8.6.2.2 void JSObject::put(ExecState* exec, const Identifier& propertyName, JSValue* value, PutPropertySlot& slot) { ASSERT(value); ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this)); if (propertyName == exec->propertyNames().underscoreProto) { JSObject* proto = value->getObject(); // Setting __proto__ to a non-object, non-null value is silently ignored to match Mozilla. if (!proto && !value->isNull()) return; while (proto) { if (proto == this) { throwError(exec, GeneralError, "cyclic __proto__ value"); return; } proto = proto->prototype() ? proto->prototype()->getObject() : 0; } setPrototype(value); return; } // Check if there are any setters or getters in the prototype chain JSValue* prototype; for (JSObject* obj = this; !obj->structureID()->hasGetterSetterProperties(); obj = asObject(prototype)) { prototype = obj->prototype(); if (prototype->isNull()) { putDirect(propertyName, value, 0, true, slot); return; } } unsigned attributes; if ((m_structureID->get(propertyName, attributes) != WTF::notFound) && attributes & ReadOnly) return; for (JSObject* obj = this; ; obj = asObject(prototype)) { if (JSValue* gs = obj->getDirect(propertyName)) { if (gs->isGetterSetter()) { JSObject* setterFunc = asGetterSetter(gs)->setter(); if (!setterFunc) { throwSetterError(exec); return; } CallData callData; CallType callType = setterFunc->getCallData(callData); ArgList args; args.append(value); call(exec, setterFunc, callType, callData, this, args); return; } // If there's an existing property on the object or one of its // prototypes it should be replaced, so break here. break; } prototype = obj->prototype(); if (prototype->isNull()) break; } putDirect(propertyName, value, 0, true, slot); return; }