diff --git a/Makefile b/Makefile index 28f828e..2971be6 100644 --- a/Makefile +++ b/Makefile @@ -18,6 +18,7 @@ TEST := -v --run _eval TEST := -v --run Broken TEST := -v --run ParseSuccess TEST := . +TEST := -v --run OttoError test: go test $(TEST) diff --git a/error.go b/error.go index 2368cae..3e07aeb 100644 --- a/error.go +++ b/error.go @@ -8,6 +8,8 @@ import ( type _error struct { Name string Message string + + Line int // Hackish -- line where the error/exception occurred } var messageDetail map[string]string = map[string]string{ @@ -48,6 +50,7 @@ func newError(name string, argumentList... interface{}) _error { return _error{ Name: name, Message: messageFromDescription(description, argumentList...), + Line: -1, } } @@ -86,7 +89,11 @@ func catchPanic(function func()) (err error) { err = errors.New(caught.String()) return case _error: - err = errors.New(caught.String()) + if caught.Line == -1 { + err = errors.New(caught.String()) + } else { + err = errors.New(fmt.Sprintf("%s (line %d)", caught.String(), caught.Line)) + } return case _result: if caught.Kind == resultThrow { diff --git a/evaluate.go b/evaluate.go index f81567a..92f4a16 100644 --- a/evaluate.go +++ b/evaluate.go @@ -20,6 +20,21 @@ func (self *_runtime) evaluateBody(list []_node) Value { } func (self *_runtime) evaluate(node _node) Value { + defer func() { + // This defer is lame (unecessary overhead) + // It would be better to mark the errors at the source + if caught := recover(); caught != nil { + switch caught := caught.(type) { + case _error: + if caught.Line == -1 { + caught.Line = node.position() + } + panic(caught) // Panic the modified _error + } + panic(caught) + } + }() + switch node := node.(type) { case *_variableDeclarationListNode: diff --git a/node.go b/node.go index cd9b141..bee2f23 100644 --- a/node.go +++ b/node.go @@ -10,6 +10,8 @@ import ( type _node interface { Type() _nodeType String() string + setPosition(int) + position() int } type _nodeType int @@ -18,6 +20,19 @@ func (self _nodeType) Type() _nodeType { return self } +type _node_ struct { + _nodeType + Line int // Line in the source +} + +func (self *_node_) setPosition(Line int) { + self.Line = Line +} + +func (self *_node_) position() int { + return self.Line +} + const ( nodeEmpty _nodeType = iota nodeCall diff --git a/node_expression.go b/node_expression.go index 6701c9f..45ee471 100644 --- a/node_expression.go +++ b/node_expression.go @@ -7,6 +7,7 @@ import ( type _arrayNode struct { _nodeType + _node_ nodeList []_node } @@ -26,6 +27,7 @@ func (self *_arrayNode) String() string { type _assignmentNode struct { _nodeType + _node_ Assignment string Operator string Left _node @@ -48,6 +50,7 @@ func (self _assignmentNode) String() string { type _binaryOperationNode struct { _nodeType + _node_ Operator string Left _node Right _node @@ -68,6 +71,7 @@ func (self _binaryOperationNode) String() string { type _callNode struct { _nodeType + _node_ Callee _node ArgumentList []_node } @@ -86,6 +90,7 @@ func (self _callNode) String() string { type _commaNode struct { _nodeType + _node_ Sequence []_node } @@ -102,6 +107,7 @@ func (self _commaNode) String() string { type _comparisonNode struct { _nodeType + _node_ Comparator string Left _node Right _node @@ -122,6 +128,7 @@ func (self _comparisonNode) String() string { type _conditionalNode struct { _nodeType + _node_ Test _node Consequent _node Alternate _node @@ -141,6 +148,7 @@ func (self _conditionalNode) String() string { type _functionNode struct { _nodeType + _node_ _declaration bool ParameterList []string Body []_node @@ -165,6 +173,7 @@ func (self *_functionNode) AddParameter(identifier string) { type _identifierNode struct { _nodeType + _node_ Value string } @@ -181,6 +190,7 @@ func (self *_identifierNode) String() string { type _dotMemberNode struct { _nodeType + _node_ Target _node Member string } @@ -199,6 +209,7 @@ func (self *_dotMemberNode) String() string { type _bracketMemberNode struct { _nodeType + _node_ Target _node Member _node } @@ -217,6 +228,7 @@ func (self *_bracketMemberNode) String() string { type _newNode struct { _nodeType + _node_ Callee _node ArgumentList []_node } @@ -235,6 +247,7 @@ func (self _newNode) String() string { type _objectNode struct { _nodeType + _node_ propertyList []*_objectPropertyNode } @@ -257,6 +270,7 @@ func (self *_objectNode) String() string { type _objectPropertyNode struct { _nodeType + _node_ Key string Value _node } @@ -275,6 +289,7 @@ func (self *_objectPropertyNode) String() string { type _regExpNode struct { _nodeType + _node_ Pattern string Flags string } @@ -293,6 +308,7 @@ func (self *_regExpNode) String() string { type _thisNode struct { _nodeType + _node_ } func newThisNode() *_thisNode { @@ -307,6 +323,7 @@ func (self *_thisNode) String() string { type _unaryOperationNode struct { _nodeType + _node_ Operator string Target _node } @@ -334,6 +351,7 @@ const ( type _valueNode struct { _nodeType + _node_ Value Value Text string Kind _valueNodeType diff --git a/node_statement.go b/node_statement.go index 66f7897..057b0dc 100644 --- a/node_statement.go +++ b/node_statement.go @@ -6,6 +6,7 @@ import ( type _blockNode struct { _nodeType + _node_ Body []_node } @@ -24,6 +25,7 @@ func (self _blockNode) String() string { type _breakNode struct { _nodeType + _node_ Target string } @@ -40,6 +42,7 @@ func (self _breakNode) String() string { type _continueNode struct { _nodeType + _node_ Target string } @@ -56,6 +59,7 @@ func (self _continueNode) String() string { type _doWhileNode struct { _nodeType + _node_ Test _node Body _node _labelSet _labelSet @@ -77,6 +81,7 @@ func (self _doWhileNode) String() string { type _emptyNode struct { _nodeType + _node_ } func newEmptyNode() *_emptyNode { @@ -91,6 +96,7 @@ func (self _emptyNode) String() string { type _forNode struct { _nodeType + _node_ Initial _node Test _node Update _node @@ -122,6 +128,7 @@ func (self _forNode) String() string { type _forInNode struct { _nodeType + _node_ Into _node Source _node Body _node @@ -150,6 +157,7 @@ func (self _forInNode) String() string { type _ifNode struct { _nodeType + _node_ Test _node Consequent _node Alternate _node @@ -180,6 +188,7 @@ func (self _ifNode) String() string { type _programNode struct { _nodeType + _node_ Body []_node VariableList []_declaration FunctionList []_declaration @@ -206,6 +215,7 @@ func (self _programNode) toFunction(parameterList []string) *_functionNode { type _returnNode struct { _nodeType + _node_ Argument _node } @@ -224,6 +234,7 @@ func (self _returnNode) String() string { type _switchNode struct { _nodeType + _node_ Discriminant _node Default int CaseList [](*_caseNode) @@ -247,6 +258,7 @@ func (self _switchNode) String() string { type _caseNode struct { _nodeType + _node_ Test _node Body []_node } @@ -272,6 +284,7 @@ func (self _caseNode) String() string { type _throwNode struct { _nodeType + _node_ Argument _node } @@ -288,6 +301,7 @@ func (self _throwNode) String() string { type _tryCatchNode struct { _nodeType + _node_ Try _node Catch *_catchNode Finally *_blockNode @@ -320,6 +334,7 @@ func (self *_tryCatchNode) AddCatch(identifier string, body *_blockNode) { type _catchNode struct { _nodeType + _node_ Identifier string Body *_blockNode } @@ -338,6 +353,7 @@ func (self _catchNode) String() string { type _variableDeclarationListNode struct { _nodeType + _node_ VariableList []*_variableDeclarationNode } @@ -353,6 +369,7 @@ func (self _variableDeclarationListNode) String() string { type _variableDeclarationNode struct { _nodeType + _node_ Identifier string Operator string Initializer _node @@ -374,6 +391,7 @@ func (self _variableDeclarationNode) String() string { type _whileNode struct { _nodeType + _node_ Test _node Body _node _labelSet _labelSet @@ -395,6 +413,7 @@ func (self _whileNode) String() string { type _withNode struct { _nodeType + _node_ Object _node Body _node } diff --git a/otto_error_test.go b/otto_error_test.go index 384afa4..0b099bd 100644 --- a/otto_error_test.go +++ b/otto_error_test.go @@ -21,4 +21,12 @@ func TestOttoError(t *testing.T) { _, err = ToValue([]byte{}) Is(err, "TypeError: Unable to convert value: [] ([]uint8)") + + _, err = Otto.Run(` + (function(){ + return abcdef.length + })() + `) + Is(err, "ReferenceError: abcdef is not defined (line 2)") + } diff --git a/parse_expression.go b/parse_expression.go index 462560f..6e2c61d 100644 --- a/parse_expression.go +++ b/parse_expression.go @@ -21,7 +21,9 @@ func (self *_parser) ParsePrimaryExpression() _node { return self.ParseFunction(false) case "this": self.Next() - return newThisNode() + node := newThisNode() + self.markNode(node) + return node case "{": return self.ParseObjectLiteral() case "[": @@ -56,7 +58,9 @@ func (self *_parser) ParseObjectProperty() *_objectPropertyNode { self.Expect(":") value := self.ParseAssignmentExpression() - return newObjectPropertyNode(key, value) + node := newObjectPropertyNode(key, value) + self.markNode(node) + return node } func (self *_parser) ParseRegExpLiteral(token _token) *_regExpNode { @@ -74,12 +78,15 @@ func (self *_parser) ParseRegExpLiteral(token _token) *_regExpNode { panic(token.newSyntaxError("Invalid regular expression")) } - return newRegExpNode(pattern_, flags) + node := newRegExpNode(pattern_, flags) + self.markNode(node) + return node } func (self *_parser) ParseObjectLiteral() *_objectNode { node := newObjectNode() + self.markNode(node) self.Expect("{") for !self.Match("}") { @@ -111,7 +118,9 @@ func (self *_parser) ParseArrayLiteral() *_arrayNode { } self.Expect("]") - return newArrayNode(nodeList) + node := newArrayNode(nodeList) + self.markNode(node) + return node } func (self *_parser) ParseArgumentList() (argumentList []_node) { @@ -131,6 +140,7 @@ func (self *_parser) ParseArgumentList() (argumentList []_node) { func (self *_parser) ParseCallExpression(left _node) _node { left = newCallNode(left) + self.markNode(left) left.(*_callNode).ArgumentList = self.ParseArgumentList() return left } @@ -138,19 +148,24 @@ func (self *_parser) ParseCallExpression(left _node) _node { func (self *_parser) ParseDotMember(left _node) _node { self.Expect(".") member := self.ConsumeIdentifier().Value - return newDotMemberNode(left, member) + node := newDotMemberNode(left, member) + self.markNode(node) + return node } func (self *_parser) ParseBracketMember(left _node) _node { self.Expect("[") member := self.ParseExpression() self.Expect("]") - return newBracketMemberNode(left, member) + node := newBracketMemberNode(left, member) + self.markNode(node) + return node } func (self *_parser) ParseNewExpression() _node { self.Expect("new") node := newnewNode(self.ParseLeftHandSideExpression()) + self.markNode(node) if self.Match("(") { node.ArgumentList = self.ParseArgumentList() } @@ -219,7 +234,9 @@ func (self *_parser) ParsePostfixExpression() _node { default: panic(self.History(-1).newSyntaxError("Invalid left-hand side in assignment")) } - return newUnaryOperationNode("=" + self.Consume(), left) + node := newUnaryOperationNode("=" + self.Consume(), left) + self.markNode(node) + return node } return left @@ -232,7 +249,9 @@ func (self *_parser) ParseUnaryExpression() _node { switch token := self.Peek(); token.Kind { case "+", "-", "!", "~": - return newUnaryOperationNode(self.Consume(), self.ParseUnaryExpression()) + node := newUnaryOperationNode(self.Consume(), self.ParseUnaryExpression()) + self.markNode(node) + return node case "++", "--": // Prefix, either ++= or --= operation := self.Consume() left := self.ParseUnaryExpression() @@ -241,9 +260,13 @@ func (self *_parser) ParseUnaryExpression() _node { default: panic(self.History(-1).newSyntaxError("Invalid left-hand side in assignment")) } - return newUnaryOperationNode(operation + "=", left) + node := newUnaryOperationNode(operation + "=", left) + self.markNode(node) + return node case "delete", "void", "typeof": - return newUnaryOperationNode(self.Consume(), self.ParseUnaryExpression()) + node := newUnaryOperationNode(self.Consume(), self.ParseUnaryExpression()) + self.markNode(node) + return node } return self.ParsePostfixExpression() @@ -256,6 +279,7 @@ REPEAT: switch self.Peek().Kind { case "*", "/", "%": left = newBinaryOperationNode(self.Consume(), left, self.ParseUnaryExpression()) + self.markNode(left) goto REPEAT } @@ -269,6 +293,7 @@ REPEAT: switch self.Peek().Kind { case "+", "-": left = newBinaryOperationNode(self.Consume(), left, self.ParseMultiplicativeExpression()) + self.markNode(left) goto REPEAT } @@ -282,6 +307,7 @@ REPEAT: switch self.Peek().Kind { case "<<", ">>", ">>>": left = newBinaryOperationNode(self.Consume(), left, self.ParseAdditiveExpression()) + self.markNode(left) goto REPEAT } return left @@ -295,14 +321,20 @@ func (self *_parser) ParseRelationalExpression() _node { switch self.Peek().Kind { case "<", ">", "<=", ">=": - return newComparisonNode(self.Consume(), left, self.ParseRelationalExpression()) + node := newComparisonNode(self.Consume(), left, self.ParseRelationalExpression()) + self.markNode(node) + return node case "instanceof": - return newBinaryOperationNode(self.Consume(), left, self.ParseRelationalExpression()) + node := newBinaryOperationNode(self.Consume(), left, self.ParseRelationalExpression()) + self.markNode(node) + return node case "in": if !self.Scope().AllowIn { return left } - return newBinaryOperationNode(self.Consume(), left, self.ParseRelationalExpression()) + node := newBinaryOperationNode(self.Consume(), left, self.ParseRelationalExpression()) + self.markNode(node) + return node } @@ -316,6 +348,7 @@ REPEAT: switch self.Peek().Kind { case "==", "!=", "===", "!==": left = newComparisonNode(self.Consume(), left, self.ParseRelationalExpression()) + self.markNode(left) goto REPEAT } @@ -327,6 +360,7 @@ func (self *_parser) ParseBitwiseANDExpression() _node { for self.Match("&") { left = newBinaryOperationNode(self.Consume(), left, self.ParseEqualityExpression()) + self.markNode(left) } return left @@ -337,6 +371,7 @@ func (self *_parser) ParseBitwiseXORExpression() _node { for self.Match("^") { left = newBinaryOperationNode(self.Consume(), left, self.ParseBitwiseANDExpression()) + self.markNode(left) } return left @@ -347,6 +382,7 @@ func (self *_parser) ParseBitwiseORExpression() _node { for self.Match("|") { left = newBinaryOperationNode(self.Consume(), left, self.ParseBitwiseXORExpression()) + self.markNode(left) } return left @@ -357,6 +393,7 @@ func (self *_parser) ParseLogicalANDExpression() _node { for self.Match("&&") { left = newBinaryOperationNode(self.Consume(), left, self.ParseBitwiseORExpression()) + self.markNode(left) } return left @@ -367,6 +404,7 @@ func (self *_parser) ParseLogicalORExpression() _node { for self.Match("||") { left = newBinaryOperationNode(self.Consume(), left, self.ParseLogicalANDExpression()) + self.markNode(left) } return left @@ -378,7 +416,9 @@ func (self *_parser) ParseConditionlExpression() _node { if self.Accept("?") { consequent := self.ParseAssignmentExpression() self.Expect(":"); - return newConditionalNode(left, consequent, self.ParseAssignmentExpression()) + node := newConditionalNode(left, consequent, self.ParseAssignmentExpression()) + self.markNode(node) + return node } return left @@ -393,6 +433,7 @@ func (self *_parser) ParseAssignmentExpression() _node { panic(self.History(-1).newSyntaxError("Invalid left-hand side in assignment")) } left = newAssignmentNode(self.Consume(), left, self.ParseAssignmentExpression()) + self.markNode(left) } return left } @@ -408,7 +449,9 @@ func (self *_parser) ParseExpression() _node { } nodeList = append(nodeList, self.ParseAssignmentExpression()) } - return newCommaNode(nodeList) + node := newCommaNode(nodeList) + self.markNode(node) + return node } return left diff --git a/parse_statement.go b/parse_statement.go index aca35eb..7ffa41b 100644 --- a/parse_statement.go +++ b/parse_statement.go @@ -8,7 +8,9 @@ func (self *_parser) ParseStatement() _node { switch self.Peek().Kind { case ";": self.Next() - return newEmptyNode() + node := newEmptyNode() + self.markNode(node) + return node case "if": return self.ParseIf() case "do": @@ -30,7 +32,9 @@ func (self *_parser) ParseStatement() _node { case "function": self.ParseFunctionDeclaration() // TODO Should be FunctionDeclarationStatement - return newEmptyNode() + node := newEmptyNode() + self.markNode(node) + return node case "switch": return self.ParseSwitch() case "return": @@ -234,6 +238,7 @@ func (self *_parser) parseStatementUntil(stop func() bool) []_node { func (self *_parser) ParseBlock() *_blockNode { node := newBlockNode() + self.markNode(node) self.Expect("{") node.Body = self.parseStatementUntil(func() bool { @@ -250,11 +255,12 @@ func (self *_parser) ParseReturnStatement() _node { panic(self.History(-1).newSyntaxError("Illegal return statement")) } - if self.Match("\n") { - return newReturnNode() - } - node := newReturnNode() + self.markNode(node) + + if self.Match("\n") { + return node + } if !self.Match(";") { if !self.Match("}") && !self.Match("EOF") { @@ -276,6 +282,7 @@ func (self *_parser) ParseThrow() _node { } node := newThrowNode(self.ParseExpression()) + self.markNode(node) self.ConsumeSemicolon() @@ -289,7 +296,8 @@ func (self *_parser) ParseSwitch() _node { discriminant := self.ParseExpression() self.Expect(")") - _switchNode := newSwitchNode(discriminant) + switchNode := newSwitchNode(discriminant) + self.markNode(switchNode) self.Expect("{") @@ -301,17 +309,17 @@ func (self *_parser) ParseSwitch() _node { result := self.ParseCase() if result.Test == nil { - if _switchNode.Default != -1 { + if switchNode.Default != -1 { panic(hereBeDragons("Already saw a default:")) } - _switchNode.Default = i + switchNode.Default = i } - _switchNode.AddCase(result) + switchNode.AddCase(result) } return nil }) - return _switchNode + return switchNode } func (self *_parser) ParseCase() *_caseNode { @@ -319,9 +327,11 @@ func (self *_parser) ParseCase() *_caseNode { var node *_caseNode if self.Accept("default") { node = newDefaultCaseNode() + self.markNode(node) } else { self.Expect("case") node = newCaseNode(self.ParseExpression()) + self.markNode(node) } self.Expect(":") @@ -338,6 +348,7 @@ func (self *_parser) ParseCase() *_caseNode { func (self *_parser) ParseVariable() *_variableDeclarationNode { node := newVariableDeclarationNode(self.ConsumeIdentifier().Value) + self.markNode(node) for _, value := range []string{"=", ":="} { if self.Accept(value) { @@ -354,6 +365,7 @@ func (self *_parser) ParseVariableDeclaration() *_variableDeclarationListNode { self.Expect("var") node := newVariableDeclarationListNode() + self.markNode(node) for { variable := self.ParseVariable() @@ -381,12 +393,13 @@ func (self *_parser) ParseFunction(declare bool) _node { self.Expect("function") - _functionNode := newFunctionNode() + functionNode := newFunctionNode() + self.markNode(functionNode) if self.Match("identifier") { identifier := self.ConsumeIdentifier() if declare { - self.Scope().AddFunction(identifier.Value, _functionNode) + self.Scope().AddFunction(identifier.Value, functionNode) } } @@ -398,9 +411,9 @@ func (self *_parser) ParseFunction(declare bool) _node { self.Expect("(") for !self.Accept(")") { identifier := self.ConsumeIdentifier().Value - _functionNode.AddParameter(identifier) + functionNode.AddParameter(identifier) if identifier == "arguments" { - _functionNode.ArgumentsIsParameter = true + functionNode.ArgumentsIsParameter = true } if !self.Match(")") { self.Expect(",") @@ -411,14 +424,14 @@ func (self *_parser) ParseFunction(declare bool) _node { self.EnterScope() defer self.LeaveScope() self.parseInFunction(func() _node { - _functionNode.Body = self.ParseBlock().Body + functionNode.Body = self.ParseBlock().Body return nil }) - _functionNode.VariableList = self.Scope().VariableList - _functionNode.FunctionList = self.Scope().FunctionList + functionNode.VariableList = self.Scope().VariableList + functionNode.FunctionList = self.Scope().FunctionList } - return _functionNode; + return functionNode; } /*func (self *_parser) ParseFunctionParameterList() []string {*/ @@ -448,6 +461,7 @@ func (self *_parser) parseForIn(into _node) *_forInNode { }) node := newForInNode(into, source, body) + self.markNode(node) node._labelSet[""] = true return node } @@ -473,6 +487,7 @@ func (self *_parser) parseFor(initial _node) *_forNode { }) node := newForNode(initial, test, update, body) + self.markNode(node) node._labelSet[""] = true return node } diff --git a/parser.go b/parser.go index e6548c3..fe36cc3 100644 --- a/parser.go +++ b/parser.go @@ -280,3 +280,8 @@ func (self *_parser) Unexpected(token _token) *_syntaxError { } return token.newSyntaxError("Unexpected token %s", token.Text) } + +func (self *_parser) markNode(node _node) { + node.setPosition(self.lexer.Line) +} + diff --git a/synopsis_test.go b/synopsis_test.go index 98b2547..885eced 100644 --- a/synopsis_test.go +++ b/synopsis_test.go @@ -43,7 +43,7 @@ func TestSynopsis(t *testing.T) { { value, err := Otto.Run("abcdefghijlmnopqrstuvwxyz.length") - Is(err, "ReferenceError: abcdefghijlmnopqrstuvwxyz is not defined") + Is(err, "ReferenceError: abcdefghijlmnopqrstuvwxyz is not defined (line 0)") if err != nil { IsTrue(value.IsUndefined()) }