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);
}
Esempio n. 2
0
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);
}
Esempio n. 3
0
// 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;
}
Esempio n. 4
0
// 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;
}
Esempio n. 5
0
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;
}
Esempio n. 6
0
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());
    }
}
Esempio n. 7
0
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());
    }
}
Esempio n. 8
0
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() + "]");
    }
}
Esempio n. 9
0
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;
}