Ignore:
Timestamp:
Jul 18, 2008, 6:44:24 PM (17 years ago)
Author:
[email protected]
Message:

Bug 18774: SQUIRRELFISH: print meaningful error messages <https://bugs.webkit.org/show_bug.cgi?id=18774>
<rdar://problem/5769353> SQUIRRELFISH: JavaScript error messages are missing informative text

Reviewed by Cameron Zwarich

Add support for decent error messages in JavaScript. This patch achieves this by providing
ensuring the common errors and exceptions have messages that provide the text of expression
that trigger the exception. In addition it attaches a number of properties to the exception
object detailing where in the source the expression came from.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/JavaScriptCore/VM/ExceptionHelpers.cpp

    r34842 r35245  
    3030#include "ExceptionHelpers.h"
    3131
     32#include "CodeBlock.h"
    3233#include "ExecState.h"
    3334#include "nodes.h"
    3435#include "JSObject.h"
     36#include "JSNotAnObject.h"
    3537
    3638namespace KJS {
     
    6870}
    6971
    70 JSValue* createError(ExecState* exec, ErrorType e, const char* msg, JSValue* v, Node* expr)
    71 {
    72     UString message = msg;
    73     substitute(message, v->toString(exec));
    74     if (expr)
    75         substitute(message, expr->toString());
    76     return Error::create(exec, e, message, -1, -1, 0);
    77 }
    78 
    7972JSValue* createError(ExecState* exec, ErrorType e, const char* msg, JSValue* v)
    8073{
     
    8982}
    9083
    91 JSValue* createUndefinedVariableError(ExecState* exec, const Identifier& ident)
    92 {
    93     return createError(exec, ReferenceError, "Can't find variable: %s", ident);
    94 }
    95 
    96 JSValue* createInvalidParamError(ExecState* exec, const char* op, JSValue* v)
    97 {
    98     UString message = "'%s' is not a valid argument for '%s'";
    99     substitute(message,  v->toString(exec));
    100     substitute(message, op);
    101     return Error::create(exec, TypeError, message, -1, -1, 0);
    102 }
    103 
    104 JSValue* createNotAConstructorError(ExecState* exec, JSValue* value, Node* expr)
    105 {
    106     if (expr)
    107         return createError(exec, TypeError, "Value %s (result of expression %s) is not a constructor. Cannot be used with new.", value, expr);
    108     return createError(exec, TypeError, "Value %s is not a constructor. Cannot be used with new.", value);
    109 }
    110 
    111 JSValue* createNotAFunctionError(ExecState* exec, JSValue* value, Node* expr)
    112 {
    113     if (expr)
    114         return createError(exec, TypeError, "Value %s (result of expression %s) does not allow function calls.", value, expr);
    115     return createError(exec, TypeError, "Value %s does not allow function calls.", value);
     84JSValue* createUndefinedVariableError(ExecState* exec, const Identifier& ident, const Instruction* vPC, CodeBlock* codeBlock)
     85{
     86    int startOffset = 0;
     87    int endOffset = 0;
     88    int divotPoint = 0;
     89    int line = codeBlock->expressionRangeForVPC(vPC, divotPoint, startOffset, endOffset);
     90    UString message = "Can't find variable: ";
     91    message.append(ident.ustring());
     92    JSObject* exception = Error::create(exec, ReferenceError, message, line, codeBlock->ownerNode->sourceId(), codeBlock->ownerNode->sourceURL());
     93    exception->putWithAttributes(exec, Identifier(exec, expressionBeginOffsetPropertyName), jsNumber(exec, divotPoint - startOffset), ReadOnly | DontDelete);
     94    exception->putWithAttributes(exec, Identifier(exec, expressionCaretOffsetPropertyName), jsNumber(exec, divotPoint), ReadOnly | DontDelete);
     95    exception->putWithAttributes(exec, Identifier(exec, expressionEndOffsetPropertyName), jsNumber(exec, divotPoint + endOffset), ReadOnly | DontDelete);
     96    return exception;
     97}
     98   
     99bool isStrWhiteSpace(UChar c);
     100
     101static UString createErrorMessage(ExecState* exec, CodeBlock* codeBlock, int, int expressionStart, int expressionStop, JSValue* value, UString error)
     102{
     103    if (!expressionStop || expressionStart > codeBlock->source->length()) {
     104        UString errorText = value->toString(exec);
     105        errorText.append(" is ");
     106        errorText.append(error);
     107        return errorText;
     108    }
     109
     110    UString errorText = "Result of expression ";
     111   
     112    if (expressionStart < expressionStop) {
     113        errorText.append('\'');
     114        errorText.append(codeBlock->source->getRange(expressionStart, expressionStop));
     115        errorText.append("' [");
     116        errorText.append(value->toString(exec));
     117        errorText.append("] is ");
     118    } else {
     119        // No range information, so give a few characters of context
     120        const UChar* data = codeBlock->source->data();
     121        int dataLength = codeBlock->source->length();
     122        int start = expressionStart;
     123        int stop = expressionStart;
     124        // Get up to 20 characters of context to the left and right of the divot, clamping to the line.
     125        // then strip whitespace.
     126        while (start > 0 && (expressionStart - start < 20) && data[start - 1] != '\n')
     127            start--;
     128        while (start < (expressionStart - 1) && isStrWhiteSpace(data[start]))
     129            start++;
     130        while (stop < dataLength && (stop - expressionStart < 20) && data[stop] != '\n')
     131            stop++;
     132        while (stop > expressionStart && isStrWhiteSpace(data[stop]))
     133            stop--;
     134        errorText.append("near '...");
     135        errorText.append(codeBlock->source->getRange(start, stop));
     136        errorText.append("...' [");
     137        errorText.append(value->toString(exec));
     138        errorText.append("] is ");
     139    }
     140    errorText.append(error);
     141    errorText.append(".");
     142    return errorText;
     143}
     144
     145JSValue* createInvalidParamError(ExecState* exec, const char* op, JSValue* value, const Instruction* vPC, CodeBlock* codeBlock)
     146{
     147    UString message = "not a valid argument for '";
     148    message.append(op);
     149    message.append("'");
     150   
     151    int startOffset = 0;
     152    int endOffset = 0;
     153    int divotPoint = 0;
     154    int line = codeBlock->expressionRangeForVPC(vPC, divotPoint, startOffset, endOffset);
     155    UString errorMessage = createErrorMessage(exec, codeBlock, line, divotPoint, divotPoint + endOffset, value, message);
     156    JSObject* exception = Error::create(exec, TypeError, errorMessage, line, codeBlock->ownerNode->sourceId(), codeBlock->ownerNode->sourceURL());
     157    exception->putWithAttributes(exec, Identifier(exec, expressionBeginOffsetPropertyName), jsNumber(exec, divotPoint - startOffset), ReadOnly | DontDelete);
     158    exception->putWithAttributes(exec, Identifier(exec, expressionCaretOffsetPropertyName), jsNumber(exec, divotPoint), ReadOnly | DontDelete);
     159    exception->putWithAttributes(exec, Identifier(exec, expressionEndOffsetPropertyName), jsNumber(exec, divotPoint + endOffset), ReadOnly | DontDelete);
     160    return exception;
     161}
     162
     163JSValue* createNotAConstructorError(ExecState* exec, JSValue* value, const Instruction* vPC, CodeBlock* codeBlock)
     164{
     165    int startOffset = 0;
     166    int endOffset = 0;
     167    int divotPoint = 0;
     168    int line = codeBlock->expressionRangeForVPC(vPC, divotPoint, startOffset, endOffset);
     169
     170    // We're in a "new" expression, so we need to skip over the "new.." part
     171    int startPoint = divotPoint - (startOffset ? startOffset - 4 : 0); // -4 for "new "
     172    const UChar* data = codeBlock->source->data();
     173    while (startPoint < divotPoint && isStrWhiteSpace(data[startPoint]))
     174        startPoint++;
     175   
     176    UString errorMessage = createErrorMessage(exec, codeBlock, line, startPoint, divotPoint, value, "not a constructor");
     177    JSObject* exception = Error::create(exec, TypeError, errorMessage, line, codeBlock->ownerNode->sourceId(), codeBlock->ownerNode->sourceURL());
     178    exception->putWithAttributes(exec, Identifier(exec, expressionBeginOffsetPropertyName), jsNumber(exec, divotPoint - startOffset), ReadOnly | DontDelete);
     179    exception->putWithAttributes(exec, Identifier(exec, expressionCaretOffsetPropertyName), jsNumber(exec, divotPoint), ReadOnly | DontDelete);
     180    exception->putWithAttributes(exec, Identifier(exec, expressionEndOffsetPropertyName), jsNumber(exec, divotPoint + endOffset), ReadOnly | DontDelete);
     181    return exception;
     182}
     183
     184JSValue* createNotAFunctionError(ExecState* exec, JSValue* value, const Instruction* vPC, CodeBlock* codeBlock)
     185{
     186    int startOffset = 0;
     187    int endOffset = 0;
     188    int divotPoint = 0;
     189    int line = codeBlock->expressionRangeForVPC(vPC, divotPoint, startOffset, endOffset);
     190    UString errorMessage = createErrorMessage(exec, codeBlock, line, divotPoint - startOffset, divotPoint, value, "not a function");
     191    JSObject* exception = Error::create(exec, TypeError, errorMessage, line, codeBlock->ownerNode->sourceId(), codeBlock->ownerNode->sourceURL());   
     192    exception->putWithAttributes(exec, Identifier(exec, expressionBeginOffsetPropertyName), jsNumber(exec, divotPoint - startOffset), ReadOnly | DontDelete);
     193    exception->putWithAttributes(exec, Identifier(exec, expressionCaretOffsetPropertyName), jsNumber(exec, divotPoint), ReadOnly | DontDelete);
     194    exception->putWithAttributes(exec, Identifier(exec, expressionEndOffsetPropertyName), jsNumber(exec, divotPoint + endOffset), ReadOnly | DontDelete);
     195    return exception;
     196}
     197
     198JSNotAnObjectErrorStub* createNotAnObjectErrorStub(ExecState* exec, bool isNull)
     199{
     200    return new (exec) JSNotAnObjectErrorStub(isNull);
     201}
     202
     203JSObject* createNotAnObjectError(ExecState* exec, JSNotAnObjectErrorStub* error, const Instruction* vPC, CodeBlock* codeBlock)
     204{
     205    int startOffset = 0;
     206    int endOffset = 0;
     207    int divotPoint = 0;
     208    int line = codeBlock->expressionRangeForVPC(vPC, divotPoint, startOffset, endOffset);
     209    UString errorMessage = createErrorMessage(exec, codeBlock, line, divotPoint - startOffset, divotPoint, error->isNull() ? jsNull() : jsUndefined(), "not an object");
     210    JSObject* exception = Error::create(exec, TypeError, errorMessage, line, codeBlock->ownerNode->sourceId(), codeBlock->ownerNode->sourceURL());
     211    exception->putWithAttributes(exec, Identifier(exec, expressionBeginOffsetPropertyName), jsNumber(exec, divotPoint - startOffset), ReadOnly | DontDelete);
     212    exception->putWithAttributes(exec, Identifier(exec, expressionCaretOffsetPropertyName), jsNumber(exec, divotPoint), ReadOnly | DontDelete);
     213    exception->putWithAttributes(exec, Identifier(exec, expressionEndOffsetPropertyName), jsNumber(exec, divotPoint + endOffset), ReadOnly | DontDelete);
     214    return exception;
    116215}
    117216
Note: See TracChangeset for help on using the changeset viewer.