mirror of
https://github.com/robertkrimen/otto
synced 2025-10-05 19:19:10 +08:00
Partially fix ReferenceError origin reporting
This commit is contained in:
parent
326264ae3e
commit
ade6a169be
1
DESIGN.markdown
Normal file
1
DESIGN.markdown
Normal file
|
@ -0,0 +1 @@
|
|||
* Designate the filename of "anonymous" source code by the hash (md5/sha1, etc.)
|
6
Makefile
6
Makefile
|
@ -1,4 +1,4 @@
|
|||
.PHONY: test assets todo fixme otto run test-all README
|
||||
.PHONY: test assets todo fixme otto run test-all release
|
||||
|
||||
export TERST_BASE=$(PWD)
|
||||
|
||||
|
@ -42,5 +42,5 @@ run:
|
|||
test-all:
|
||||
go test .
|
||||
|
||||
README:
|
||||
godocdown > README.md
|
||||
release:
|
||||
godocdown > README.markdown
|
||||
|
|
|
@ -83,13 +83,13 @@ func (self Object) Call(this Value, argumentList ...interface{}) (Value, error)
|
|||
Call the object as a function with the given this value and argument list and
|
||||
return the result of invocation. It is essentially equivalent to:
|
||||
|
||||
self.apply(thisValue, argumentList)
|
||||
self.apply(thisValue, argumentList)
|
||||
|
||||
An undefined value and an error will result if:
|
||||
|
||||
1. There is an error during conversion of the argument list
|
||||
2. The object is not actually a function
|
||||
3. An (uncaught) exception is thrown
|
||||
1. There is an error during conversion of the argument list
|
||||
2. The object is not actually a function
|
||||
3. An (uncaught) exception is thrown
|
||||
|
||||
#### func (Object) Class
|
||||
|
||||
|
@ -100,14 +100,14 @@ Class will return the class string of the object.
|
|||
|
||||
The return value will (generally) be one of:
|
||||
|
||||
Object
|
||||
Function
|
||||
Array
|
||||
String
|
||||
Number
|
||||
Boolean
|
||||
Date
|
||||
RegExp
|
||||
Object
|
||||
Function
|
||||
Array
|
||||
String
|
||||
Number
|
||||
Boolean
|
||||
Date
|
||||
RegExp
|
||||
|
||||
#### func (Object) Get
|
||||
|
||||
|
@ -123,8 +123,8 @@ func (self Object) Set(name string, value interface{}) error
|
|||
```
|
||||
Set the property of the given name to the given value.
|
||||
|
||||
An error will result if the setting the property triggers an exception (i.e. read-only),
|
||||
or there is an error during conversion of the given value.
|
||||
An error will result if the setting the property triggers an exception (i.e.
|
||||
read-only), or there is an error during conversion of the given value.
|
||||
|
||||
#### type Otto
|
||||
|
||||
|
@ -134,7 +134,8 @@ type Otto struct {
|
|||
}
|
||||
```
|
||||
|
||||
Otto is the representation of the JavaScript runtime. Each instance of Otto has a self-contained namespace.
|
||||
Otto is the representation of the JavaScript runtime. Each instance of Otto has
|
||||
a self-contained namespace.
|
||||
|
||||
#### func New
|
||||
|
||||
|
@ -148,9 +149,8 @@ New will allocate a new JavaScript runtime
|
|||
```go
|
||||
func Run(source string) (*Otto, Value, error)
|
||||
```
|
||||
Run will allocate a new JavaScript runtime, run the given source
|
||||
on the allocated runtime, and return the runtime, resulting value, and
|
||||
error (if any).
|
||||
Run will allocate a new JavaScript runtime, run the given source on the
|
||||
allocated runtime, and return the runtime, resulting value, and error (if any).
|
||||
|
||||
#### func (Otto) Get
|
||||
|
||||
|
@ -159,8 +159,8 @@ func (self Otto) Get(name string) (Value, error)
|
|||
```
|
||||
Get the value of the top-level binding of the given name.
|
||||
|
||||
If there is an error (like the binding not existing), then the value
|
||||
will be undefined.
|
||||
If there is an error (like the binding not existing), then the value will be
|
||||
undefined.
|
||||
|
||||
#### func (Otto) Object
|
||||
|
||||
|
@ -171,29 +171,30 @@ Object will run the given source and return the result as an object.
|
|||
|
||||
For example, accessing an existing object:
|
||||
|
||||
object, _ := Otto.Object(`Number`)
|
||||
object, _ := Otto.Object(`Number`)
|
||||
|
||||
Or, creating a new object:
|
||||
|
||||
object, _ := Otto.Object(`{ xyzzy: "Nothing happens." }`)
|
||||
object, _ := Otto.Object(`{ xyzzy: "Nothing happens." }`)
|
||||
|
||||
Or, creating and assigning an object:
|
||||
|
||||
object, _ := Otto.Object(`xyzzy = {}`)
|
||||
object.Set("volume", 11)
|
||||
object, _ := Otto.Object(`xyzzy = {}`)
|
||||
object.Set("volume", 11)
|
||||
|
||||
If there is an error (like the source does not result in an object), then
|
||||
nil and an error is returned.
|
||||
If there is an error (like the source does not result in an object), then nil
|
||||
and an error is returned.
|
||||
|
||||
#### func (Otto) Run
|
||||
|
||||
```go
|
||||
func (self Otto) Run(source string) (Value, error)
|
||||
```
|
||||
Run will run the given source (parsing it first), returning the resulting value and error (if any)
|
||||
Run will run the given source (parsing it first), returning the resulting value
|
||||
and error (if any)
|
||||
|
||||
If the runtime is unable to parse the source, then this function will return undefined and the parse error (nothing
|
||||
will be evaluated in this case).
|
||||
If the runtime is unable to parse the source, then this function will return
|
||||
undefined and the parse error (nothing will be evaluated in this case).
|
||||
|
||||
#### func (Otto) Set
|
||||
|
||||
|
@ -202,11 +203,11 @@ func (self Otto) Set(name string, value interface{}) error
|
|||
```
|
||||
Set the top-level binding of the given name to the given value.
|
||||
|
||||
Set will automatically apply ToValue to the given value in order
|
||||
to convert it to a JavaScript value (type Value).
|
||||
Set will automatically apply ToValue to the given value in order to convert it
|
||||
to a JavaScript value (type Value).
|
||||
|
||||
If there is an error (like the binding being read-only, or the ToValue conversion
|
||||
failing), then an error is returned.
|
||||
If there is an error (like the binding being read-only, or the ToValue
|
||||
conversion failing), then an error is returned.
|
||||
|
||||
If the top-level binding does not exist, it will be created.
|
||||
|
||||
|
@ -229,7 +230,7 @@ FalseValue will return a value represting false.
|
|||
|
||||
It is equivalent to:
|
||||
|
||||
ToValue(false)
|
||||
ToValue(false)
|
||||
|
||||
#### func NaNValue
|
||||
|
||||
|
@ -240,7 +241,7 @@ NaNValue will return a value representing NaN.
|
|||
|
||||
It is equivalent to:
|
||||
|
||||
ToValue(math.NaN())
|
||||
ToValue(math.NaN())
|
||||
|
||||
#### func NullValue
|
||||
|
||||
|
@ -264,7 +265,7 @@ TrueValue will return a value represting true.
|
|||
|
||||
It is equivalent to:
|
||||
|
||||
ToValue(true)
|
||||
ToValue(true)
|
||||
|
||||
#### func UndefinedValue
|
||||
|
||||
|
@ -281,31 +282,32 @@ func (value Value) Call(this Value, argumentList ...interface{}) (Value, error)
|
|||
Call the value as a function with the given this value and argument list and
|
||||
return the result of invocation. It is essentially equivalent to:
|
||||
|
||||
value.apply(thisValue, argumentList)
|
||||
value.apply(thisValue, argumentList)
|
||||
|
||||
An undefined value and an error will result if:
|
||||
|
||||
1. There is an error during conversion of the argument list
|
||||
2. The value is not actually a function
|
||||
3. An (uncaught) exception is thrown
|
||||
1. There is an error during conversion of the argument list
|
||||
2. The value is not actually a function
|
||||
3. An (uncaught) exception is thrown
|
||||
|
||||
#### func (Value) Class
|
||||
|
||||
```go
|
||||
func (value Value) Class() string
|
||||
```
|
||||
Class will return the class string of the value or the empty string if value is not an object.
|
||||
Class will return the class string of the value or the empty string if value is
|
||||
not an object.
|
||||
|
||||
The return value will (generally) be one of:
|
||||
|
||||
Object
|
||||
Function
|
||||
Array
|
||||
String
|
||||
Number
|
||||
Boolean
|
||||
Date
|
||||
RegExp
|
||||
Object
|
||||
Function
|
||||
Array
|
||||
String
|
||||
Number
|
||||
Boolean
|
||||
Date
|
||||
RegExp
|
||||
|
||||
#### func (Value) IsBoolean
|
||||
|
||||
|
@ -384,7 +386,8 @@ func (value Value) Object() *Object
|
|||
```
|
||||
Object will return the object of the value, or nil if value is not an object.
|
||||
|
||||
This method will not do any implicit conversion. For example, calling this method on a string primitive value will not return a String object.
|
||||
This method will not do any implicit conversion. For example, calling this
|
||||
method on a string primitive value will not return a String object.
|
||||
|
||||
#### func (Value) String
|
||||
|
||||
|
@ -402,13 +405,14 @@ func (value Value) ToBoolean() (bool, error)
|
|||
```
|
||||
ToBoolean will convert the value to a boolean (bool).
|
||||
|
||||
ToValue(0).ToBoolean() => false
|
||||
ToValue("").ToBoolean() => false
|
||||
ToValue(true).ToBoolean() => true
|
||||
ToValue(1).ToBoolean() => true
|
||||
ToValue("Nothing happens").ToBoolean() => true
|
||||
ToValue(0).ToBoolean() => false
|
||||
ToValue("").ToBoolean() => false
|
||||
ToValue(true).ToBoolean() => true
|
||||
ToValue(1).ToBoolean() => true
|
||||
ToValue("Nothing happens").ToBoolean() => true
|
||||
|
||||
If there is an error during the conversion process (like an uncaught exception), then the result will be false and an error.
|
||||
If there is an error during the conversion process (like an uncaught exception),
|
||||
then the result will be false and an error.
|
||||
|
||||
#### func (Value) ToFloat
|
||||
|
||||
|
@ -417,11 +421,12 @@ func (value Value) ToFloat() (float64, error)
|
|||
```
|
||||
ToFloat will convert the value to a number (float64).
|
||||
|
||||
ToValue(0).ToFloat() => 0.
|
||||
ToValue(1.1).ToFloat() => 1.1
|
||||
ToValue("11").ToFloat() => 11.
|
||||
ToValue(0).ToFloat() => 0.
|
||||
ToValue(1.1).ToFloat() => 1.1
|
||||
ToValue("11").ToFloat() => 11.
|
||||
|
||||
If there is an error during the conversion process (like an uncaught exception), then the result will be 0 and an error.
|
||||
If there is an error during the conversion process (like an uncaught exception),
|
||||
then the result will be 0 and an error.
|
||||
|
||||
#### func (Value) ToInteger
|
||||
|
||||
|
@ -430,11 +435,12 @@ func (value Value) ToInteger() (int64, error)
|
|||
```
|
||||
ToInteger will convert the value to a number (int64).
|
||||
|
||||
ToValue(0).ToInteger() => 0
|
||||
ToValue(1.1).ToInteger() => 1
|
||||
ToValue("11").ToInteger() => 11
|
||||
ToValue(0).ToInteger() => 0
|
||||
ToValue(1.1).ToInteger() => 1
|
||||
ToValue("11").ToInteger() => 11
|
||||
|
||||
If there is an error during the conversion process (like an uncaught exception), then the result will be 0 and an error.
|
||||
If there is an error during the conversion process (like an uncaught exception),
|
||||
then the result will be 0 and an error.
|
||||
|
||||
#### func (Value) ToString
|
||||
|
||||
|
@ -443,12 +449,13 @@ func (value Value) ToString() (string, error)
|
|||
```
|
||||
ToString will convert the value to a string (string).
|
||||
|
||||
ToValue(0).ToString() => "0"
|
||||
ToValue(false).ToString() => "false"
|
||||
ToValue(1.1).ToString() => "1.1"
|
||||
ToValue("11").ToString() => "11"
|
||||
ToValue('Nothing happens.').ToString() => "Nothing happens."
|
||||
ToValue(0).ToString() => "0"
|
||||
ToValue(false).ToString() => "false"
|
||||
ToValue(1.1).ToString() => "1.1"
|
||||
ToValue("11").ToString() => "11"
|
||||
ToValue('Nothing happens.').ToString() => "Nothing happens."
|
||||
|
||||
If there is an error during the conversion process (like an uncaught exception), then the result will be the empty string ("") and an error.
|
||||
If there is an error during the conversion process (like an uncaught exception),
|
||||
then the result will be the empty string ("") and an error.
|
||||
|
||||
|
|
@ -114,7 +114,7 @@ func (self *_objectEnvironment) ImplicitThisValue() *_object {
|
|||
|
||||
func getIdentifierReference(environment _environment, name string, strict bool) _reference {
|
||||
if environment == nil {
|
||||
return newObjectReference(nil, name, strict)
|
||||
return newObjectReference(nil, name, strict, nil)
|
||||
}
|
||||
if environment.HasBinding(name) {
|
||||
return environment.newReference(name, strict)
|
||||
|
@ -140,7 +140,7 @@ func (self *_objectEnvironment) newDeclarativeEnvironment() _environment {
|
|||
}
|
||||
|
||||
func (self *_objectEnvironment) newReference(name string, strict bool) _reference {
|
||||
return newObjectReference(self.Object, name, strict)
|
||||
return newObjectReference(self.Object, name, strict, nil)
|
||||
}
|
||||
|
||||
func (self *_objectEnvironment) GetReference(name string) _reference {
|
||||
|
|
18
error.go
18
error.go
|
@ -44,14 +44,26 @@ func (self _error) String() string {
|
|||
|
||||
func newError(name string, argumentList... interface{}) _error {
|
||||
description := ""
|
||||
if len(argumentList) > 0 {
|
||||
description, argumentList = argumentList[0].(string), argumentList[1:]
|
||||
var node _node = nil
|
||||
length := len(argumentList)
|
||||
if length > 0 {
|
||||
if node, _ = argumentList[length-1].(_node); node != nil || argumentList[length-1] == nil {
|
||||
argumentList = argumentList[0:length-1]
|
||||
length -= 1
|
||||
}
|
||||
if length > 0 {
|
||||
description, argumentList = argumentList[0].(string), argumentList[1:]
|
||||
}
|
||||
}
|
||||
return _error{
|
||||
error := _error{
|
||||
Name: name,
|
||||
Message: messageFromDescription(description, argumentList...),
|
||||
Line: -1,
|
||||
}
|
||||
if node != nil {
|
||||
error.Line = node.position()
|
||||
}
|
||||
return error
|
||||
}
|
||||
|
||||
func newReferenceError(argumentList... interface{}) _error {
|
||||
|
|
|
@ -507,7 +507,7 @@ func (self *_runtime) evaluateFunction(node *_functionNode) Value {
|
|||
func (self *_runtime) evaluateDotMember(node *_dotMemberNode) Value {
|
||||
target := self.evaluate(node.Target)
|
||||
targetValue := self.GetValue(target)
|
||||
return toValue(newObjectReference(self.toObject(targetValue), node.Member, false))
|
||||
return toValue(newObjectReference(self.toObject(targetValue), node.Member, false, node))
|
||||
}
|
||||
|
||||
func (self *_runtime) evaluateBracketMember(node *_bracketMemberNode) Value {
|
||||
|
@ -516,12 +516,14 @@ func (self *_runtime) evaluateBracketMember(node *_bracketMemberNode) Value {
|
|||
member := self.evaluate(node.Member)
|
||||
memberValue := self.GetValue(member)
|
||||
|
||||
return toValue(newObjectReference(self.toObject(targetValue), toString(memberValue), false))
|
||||
return toValue(newObjectReference(self.toObject(targetValue), toString(memberValue), false, node))
|
||||
}
|
||||
|
||||
func (self *_runtime) evaluateIdentifier(node *_identifierNode) Value {
|
||||
name := node.Value
|
||||
// TODO Should be true or false (strictness) depending on context
|
||||
// TODO Associate the node with reference... how?
|
||||
// TODO Can/Will getIdentifierReference ever return nil?
|
||||
reference := getIdentifierReference(self.LexicalEnvironment(), name, false)
|
||||
if reference == nil {
|
||||
panic("referenceError: " + name)
|
||||
|
|
|
@ -29,4 +29,21 @@ func TestOttoError(t *testing.T) {
|
|||
`)
|
||||
Is(err, "ReferenceError: abcdef is not defined (line 2)")
|
||||
|
||||
_, err = Otto.Run(`
|
||||
function start() {
|
||||
}
|
||||
|
||||
start()
|
||||
|
||||
xyzzy()
|
||||
`)
|
||||
Is(err, "ReferenceError: xyzzy is not defined (line 6)")
|
||||
|
||||
_, err = Otto.Run(`
|
||||
// Just a comment
|
||||
|
||||
xyzzy
|
||||
`)
|
||||
Is(err, "ReferenceError: xyzzy is not defined (line 3)")
|
||||
|
||||
}
|
||||
|
|
20
parser.go
20
parser.go
|
@ -162,7 +162,9 @@ func (self *_parser) matchAssignment() bool {
|
|||
}
|
||||
|
||||
func (self *_parser) ConsumeNull() *_valueNode {
|
||||
return newNullNode(self.Next().Text)
|
||||
node := newNullNode(self.Next().Text)
|
||||
self.markNode(node)
|
||||
return node
|
||||
}
|
||||
|
||||
func (self *_parser) ConsumeIdentifier() *_identifierNode {
|
||||
|
@ -170,19 +172,27 @@ func (self *_parser) ConsumeIdentifier() *_identifierNode {
|
|||
if token.Kind != "identifier" {
|
||||
panic(token.newSyntaxError("Unexpected token %s", token.Kind))
|
||||
}
|
||||
return newIdentifierNode(token.Text)
|
||||
node := newIdentifierNode(token.Text)
|
||||
self.markNode(node)
|
||||
return node
|
||||
}
|
||||
|
||||
func (self *_parser) ConsumeString() *_valueNode {
|
||||
return newStringNode(self.Next().Text)
|
||||
node := newStringNode(self.Next().Text)
|
||||
self.markNode(node)
|
||||
return node
|
||||
}
|
||||
|
||||
func (self *_parser) ConsumeBoolean() *_valueNode {
|
||||
return newBooleanNode(self.Next().Text)
|
||||
node := newBooleanNode(self.Next().Text)
|
||||
self.markNode(node)
|
||||
return node
|
||||
}
|
||||
|
||||
func (self *_parser) ConsumeNumber() *_valueNode {
|
||||
return newNumberNode(self.Next().Text)
|
||||
node := newNumberNode(self.Next().Text)
|
||||
self.markNode(node)
|
||||
return node
|
||||
}
|
||||
|
||||
func (self *_parser) ConsumeSemicolon() {
|
||||
|
|
|
@ -64,15 +64,17 @@ func (self *_argumentReference) PutValue(value Value) bool {
|
|||
type _objectReference struct {
|
||||
_referenceBase
|
||||
Base *_object
|
||||
node _node
|
||||
}
|
||||
|
||||
func newObjectReference(base *_object, name string, strict bool) *_objectReference {
|
||||
func newObjectReference(base *_object, name string, strict bool, node _node) *_objectReference {
|
||||
return &_objectReference{
|
||||
Base: base,
|
||||
_referenceBase: _referenceBase{
|
||||
name: name,
|
||||
strict: strict,
|
||||
},
|
||||
node: node,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -82,7 +84,7 @@ func (self *_objectReference) GetBase() *_object {
|
|||
|
||||
func (self *_objectReference) GetValue() Value {
|
||||
if self.Base == nil {
|
||||
panic(newReferenceError("notDefined", self.name))
|
||||
panic(newReferenceError("notDefined", self.name, self.node))
|
||||
}
|
||||
return self.Base.GetValue(self.name)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user