using robertkrimen/otto, godeps updated
This commit is contained in:
parent
91f9f355b2
commit
8324b683b4
|
@ -47,8 +47,8 @@
|
||||||
"Rev": "ccfcd0245381f0c94c68f50626665eed3c6b726a"
|
"Rev": "ccfcd0245381f0c94c68f50626665eed3c6b726a"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/obscuren/otto",
|
"ImportPath": "github.com/robertkrimen/otto",
|
||||||
"Rev": "cf13cc4228c5e5ce0fe27a7aea90bc10091c4f19"
|
"Rev": "dea31a3d392779af358ec41f77a07fcc7e9d04ba"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/obscuren/qml",
|
"ImportPath": "github.com/obscuren/qml",
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,496 +0,0 @@
|
||||||
/*
|
|
||||||
Package ast declares types representing a JavaScript AST.
|
|
||||||
|
|
||||||
Warning
|
|
||||||
|
|
||||||
The parser and AST interfaces are still works-in-progress (particularly where
|
|
||||||
node types are concerned) and may change in the future.
|
|
||||||
|
|
||||||
*/
|
|
||||||
package ast
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/robertkrimen/otto/file"
|
|
||||||
"github.com/robertkrimen/otto/token"
|
|
||||||
)
|
|
||||||
|
|
||||||
// All nodes implement the Node interface.
|
|
||||||
type Node interface {
|
|
||||||
Idx0() file.Idx // The index of the first character belonging to the node
|
|
||||||
Idx1() file.Idx // The index of the first character immediately after the node
|
|
||||||
}
|
|
||||||
|
|
||||||
// ========== //
|
|
||||||
// Expression //
|
|
||||||
// ========== //
|
|
||||||
|
|
||||||
type (
|
|
||||||
// All expression nodes implement the Expression interface.
|
|
||||||
Expression interface {
|
|
||||||
Node
|
|
||||||
_expressionNode()
|
|
||||||
}
|
|
||||||
|
|
||||||
ArrayLiteral struct {
|
|
||||||
LeftBracket file.Idx
|
|
||||||
RightBracket file.Idx
|
|
||||||
Value []Expression
|
|
||||||
}
|
|
||||||
|
|
||||||
AssignExpression struct {
|
|
||||||
Operator token.Token
|
|
||||||
Left Expression
|
|
||||||
Right Expression
|
|
||||||
}
|
|
||||||
|
|
||||||
BadExpression struct {
|
|
||||||
From file.Idx
|
|
||||||
To file.Idx
|
|
||||||
}
|
|
||||||
|
|
||||||
BinaryExpression struct {
|
|
||||||
Operator token.Token
|
|
||||||
Left Expression
|
|
||||||
Right Expression
|
|
||||||
Comparison bool
|
|
||||||
}
|
|
||||||
|
|
||||||
BooleanLiteral struct {
|
|
||||||
Idx file.Idx
|
|
||||||
Literal string
|
|
||||||
Value bool
|
|
||||||
}
|
|
||||||
|
|
||||||
BracketExpression struct {
|
|
||||||
Left Expression
|
|
||||||
Member Expression
|
|
||||||
LeftBracket file.Idx
|
|
||||||
RightBracket file.Idx
|
|
||||||
}
|
|
||||||
|
|
||||||
CallExpression struct {
|
|
||||||
Callee Expression
|
|
||||||
LeftParenthesis file.Idx
|
|
||||||
ArgumentList []Expression
|
|
||||||
RightParenthesis file.Idx
|
|
||||||
}
|
|
||||||
|
|
||||||
ConditionalExpression struct {
|
|
||||||
Test Expression
|
|
||||||
Consequent Expression
|
|
||||||
Alternate Expression
|
|
||||||
}
|
|
||||||
|
|
||||||
DotExpression struct {
|
|
||||||
Left Expression
|
|
||||||
Identifier Identifier
|
|
||||||
}
|
|
||||||
|
|
||||||
FunctionLiteral struct {
|
|
||||||
Function file.Idx
|
|
||||||
Name *Identifier
|
|
||||||
ParameterList *ParameterList
|
|
||||||
Body Statement
|
|
||||||
Source string
|
|
||||||
|
|
||||||
DeclarationList []Declaration
|
|
||||||
}
|
|
||||||
|
|
||||||
Identifier struct {
|
|
||||||
Name string
|
|
||||||
Idx file.Idx
|
|
||||||
}
|
|
||||||
|
|
||||||
NewExpression struct {
|
|
||||||
New file.Idx
|
|
||||||
Callee Expression
|
|
||||||
LeftParenthesis file.Idx
|
|
||||||
ArgumentList []Expression
|
|
||||||
RightParenthesis file.Idx
|
|
||||||
}
|
|
||||||
|
|
||||||
NullLiteral struct {
|
|
||||||
Idx file.Idx
|
|
||||||
Literal string
|
|
||||||
}
|
|
||||||
|
|
||||||
NumberLiteral struct {
|
|
||||||
Idx file.Idx
|
|
||||||
Literal string
|
|
||||||
Value interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
ObjectLiteral struct {
|
|
||||||
LeftBrace file.Idx
|
|
||||||
RightBrace file.Idx
|
|
||||||
Value []Property
|
|
||||||
}
|
|
||||||
|
|
||||||
ParameterList struct {
|
|
||||||
Opening file.Idx
|
|
||||||
List []*Identifier
|
|
||||||
Closing file.Idx
|
|
||||||
}
|
|
||||||
|
|
||||||
Property struct {
|
|
||||||
Key string
|
|
||||||
Kind string
|
|
||||||
Value Expression
|
|
||||||
}
|
|
||||||
|
|
||||||
RegExpLiteral struct {
|
|
||||||
Idx file.Idx
|
|
||||||
Literal string
|
|
||||||
Pattern string
|
|
||||||
Flags string
|
|
||||||
Value string
|
|
||||||
}
|
|
||||||
|
|
||||||
SequenceExpression struct {
|
|
||||||
Sequence []Expression
|
|
||||||
}
|
|
||||||
|
|
||||||
StringLiteral struct {
|
|
||||||
Idx file.Idx
|
|
||||||
Literal string
|
|
||||||
Value string
|
|
||||||
}
|
|
||||||
|
|
||||||
ThisExpression struct {
|
|
||||||
Idx file.Idx
|
|
||||||
}
|
|
||||||
|
|
||||||
UnaryExpression struct {
|
|
||||||
Operator token.Token
|
|
||||||
Idx file.Idx // If a prefix operation
|
|
||||||
Operand Expression
|
|
||||||
Postfix bool
|
|
||||||
}
|
|
||||||
|
|
||||||
VariableExpression struct {
|
|
||||||
Name string
|
|
||||||
Idx file.Idx
|
|
||||||
Initializer Expression
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// _expressionNode
|
|
||||||
|
|
||||||
func (*ArrayLiteral) _expressionNode() {}
|
|
||||||
func (*AssignExpression) _expressionNode() {}
|
|
||||||
func (*BadExpression) _expressionNode() {}
|
|
||||||
func (*BinaryExpression) _expressionNode() {}
|
|
||||||
func (*BooleanLiteral) _expressionNode() {}
|
|
||||||
func (*BracketExpression) _expressionNode() {}
|
|
||||||
func (*CallExpression) _expressionNode() {}
|
|
||||||
func (*ConditionalExpression) _expressionNode() {}
|
|
||||||
func (*DotExpression) _expressionNode() {}
|
|
||||||
func (*FunctionLiteral) _expressionNode() {}
|
|
||||||
func (*Identifier) _expressionNode() {}
|
|
||||||
func (*NewExpression) _expressionNode() {}
|
|
||||||
func (*NullLiteral) _expressionNode() {}
|
|
||||||
func (*NumberLiteral) _expressionNode() {}
|
|
||||||
func (*ObjectLiteral) _expressionNode() {}
|
|
||||||
func (*RegExpLiteral) _expressionNode() {}
|
|
||||||
func (*SequenceExpression) _expressionNode() {}
|
|
||||||
func (*StringLiteral) _expressionNode() {}
|
|
||||||
func (*ThisExpression) _expressionNode() {}
|
|
||||||
func (*UnaryExpression) _expressionNode() {}
|
|
||||||
func (*VariableExpression) _expressionNode() {}
|
|
||||||
|
|
||||||
// ========= //
|
|
||||||
// Statement //
|
|
||||||
// ========= //
|
|
||||||
|
|
||||||
type (
|
|
||||||
// All statement nodes implement the Statement interface.
|
|
||||||
Statement interface {
|
|
||||||
Node
|
|
||||||
_statementNode()
|
|
||||||
}
|
|
||||||
|
|
||||||
BadStatement struct {
|
|
||||||
From file.Idx
|
|
||||||
To file.Idx
|
|
||||||
}
|
|
||||||
|
|
||||||
BlockStatement struct {
|
|
||||||
LeftBrace file.Idx
|
|
||||||
List []Statement
|
|
||||||
RightBrace file.Idx
|
|
||||||
}
|
|
||||||
|
|
||||||
BranchStatement struct {
|
|
||||||
Idx file.Idx
|
|
||||||
Token token.Token
|
|
||||||
Label *Identifier
|
|
||||||
}
|
|
||||||
|
|
||||||
CaseStatement struct {
|
|
||||||
Case file.Idx
|
|
||||||
Test Expression
|
|
||||||
Consequent []Statement
|
|
||||||
}
|
|
||||||
|
|
||||||
CatchStatement struct {
|
|
||||||
Catch file.Idx
|
|
||||||
Parameter *Identifier
|
|
||||||
Body Statement
|
|
||||||
}
|
|
||||||
|
|
||||||
DebuggerStatement struct {
|
|
||||||
Debugger file.Idx
|
|
||||||
}
|
|
||||||
|
|
||||||
DoWhileStatement struct {
|
|
||||||
Do file.Idx
|
|
||||||
Test Expression
|
|
||||||
Body Statement
|
|
||||||
}
|
|
||||||
|
|
||||||
EmptyStatement struct {
|
|
||||||
Semicolon file.Idx
|
|
||||||
}
|
|
||||||
|
|
||||||
ExpressionStatement struct {
|
|
||||||
Expression Expression
|
|
||||||
}
|
|
||||||
|
|
||||||
ForInStatement struct {
|
|
||||||
For file.Idx
|
|
||||||
Into Expression
|
|
||||||
Source Expression
|
|
||||||
Body Statement
|
|
||||||
}
|
|
||||||
|
|
||||||
ForStatement struct {
|
|
||||||
For file.Idx
|
|
||||||
Initializer Expression
|
|
||||||
Update Expression
|
|
||||||
Test Expression
|
|
||||||
Body Statement
|
|
||||||
}
|
|
||||||
|
|
||||||
IfStatement struct {
|
|
||||||
If file.Idx
|
|
||||||
Test Expression
|
|
||||||
Consequent Statement
|
|
||||||
Alternate Statement
|
|
||||||
}
|
|
||||||
|
|
||||||
LabelledStatement struct {
|
|
||||||
Label *Identifier
|
|
||||||
Colon file.Idx
|
|
||||||
Statement Statement
|
|
||||||
}
|
|
||||||
|
|
||||||
ReturnStatement struct {
|
|
||||||
Return file.Idx
|
|
||||||
Argument Expression
|
|
||||||
}
|
|
||||||
|
|
||||||
SwitchStatement struct {
|
|
||||||
Switch file.Idx
|
|
||||||
Discriminant Expression
|
|
||||||
Default int
|
|
||||||
Body []*CaseStatement
|
|
||||||
}
|
|
||||||
|
|
||||||
ThrowStatement struct {
|
|
||||||
Throw file.Idx
|
|
||||||
Argument Expression
|
|
||||||
}
|
|
||||||
|
|
||||||
TryStatement struct {
|
|
||||||
Try file.Idx
|
|
||||||
Body Statement
|
|
||||||
Catch *CatchStatement
|
|
||||||
Finally Statement
|
|
||||||
}
|
|
||||||
|
|
||||||
VariableStatement struct {
|
|
||||||
Var file.Idx
|
|
||||||
List []Expression
|
|
||||||
}
|
|
||||||
|
|
||||||
WhileStatement struct {
|
|
||||||
While file.Idx
|
|
||||||
Test Expression
|
|
||||||
Body Statement
|
|
||||||
}
|
|
||||||
|
|
||||||
WithStatement struct {
|
|
||||||
With file.Idx
|
|
||||||
Object Expression
|
|
||||||
Body Statement
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// _statementNode
|
|
||||||
|
|
||||||
func (*BadStatement) _statementNode() {}
|
|
||||||
func (*BlockStatement) _statementNode() {}
|
|
||||||
func (*BranchStatement) _statementNode() {}
|
|
||||||
func (*CaseStatement) _statementNode() {}
|
|
||||||
func (*CatchStatement) _statementNode() {}
|
|
||||||
func (*DebuggerStatement) _statementNode() {}
|
|
||||||
func (*DoWhileStatement) _statementNode() {}
|
|
||||||
func (*EmptyStatement) _statementNode() {}
|
|
||||||
func (*ExpressionStatement) _statementNode() {}
|
|
||||||
func (*ForInStatement) _statementNode() {}
|
|
||||||
func (*ForStatement) _statementNode() {}
|
|
||||||
func (*IfStatement) _statementNode() {}
|
|
||||||
func (*LabelledStatement) _statementNode() {}
|
|
||||||
func (*ReturnStatement) _statementNode() {}
|
|
||||||
func (*SwitchStatement) _statementNode() {}
|
|
||||||
func (*ThrowStatement) _statementNode() {}
|
|
||||||
func (*TryStatement) _statementNode() {}
|
|
||||||
func (*VariableStatement) _statementNode() {}
|
|
||||||
func (*WhileStatement) _statementNode() {}
|
|
||||||
func (*WithStatement) _statementNode() {}
|
|
||||||
|
|
||||||
// =========== //
|
|
||||||
// Declaration //
|
|
||||||
// =========== //
|
|
||||||
|
|
||||||
type (
|
|
||||||
// All declaration nodes implement the Declaration interface.
|
|
||||||
Declaration interface {
|
|
||||||
_declarationNode()
|
|
||||||
}
|
|
||||||
|
|
||||||
FunctionDeclaration struct {
|
|
||||||
Function *FunctionLiteral
|
|
||||||
}
|
|
||||||
|
|
||||||
VariableDeclaration struct {
|
|
||||||
Var file.Idx
|
|
||||||
List []*VariableExpression
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// _declarationNode
|
|
||||||
|
|
||||||
func (*FunctionDeclaration) _declarationNode() {}
|
|
||||||
func (*VariableDeclaration) _declarationNode() {}
|
|
||||||
|
|
||||||
// ==== //
|
|
||||||
// Node //
|
|
||||||
// ==== //
|
|
||||||
|
|
||||||
type Program struct {
|
|
||||||
Body []Statement
|
|
||||||
|
|
||||||
DeclarationList []Declaration
|
|
||||||
}
|
|
||||||
|
|
||||||
// ==== //
|
|
||||||
// Idx0 //
|
|
||||||
// ==== //
|
|
||||||
|
|
||||||
func (self *ArrayLiteral) Idx0() file.Idx { return self.LeftBracket }
|
|
||||||
func (self *AssignExpression) Idx0() file.Idx { return self.Left.Idx0() }
|
|
||||||
func (self *BadExpression) Idx0() file.Idx { return self.From }
|
|
||||||
func (self *BinaryExpression) Idx0() file.Idx { return self.Left.Idx0() }
|
|
||||||
func (self *BooleanLiteral) Idx0() file.Idx { return self.Idx }
|
|
||||||
func (self *BracketExpression) Idx0() file.Idx { return self.Left.Idx0() }
|
|
||||||
func (self *CallExpression) Idx0() file.Idx { return self.Callee.Idx0() }
|
|
||||||
func (self *ConditionalExpression) Idx0() file.Idx { return self.Test.Idx0() }
|
|
||||||
func (self *DotExpression) Idx0() file.Idx { return self.Left.Idx0() }
|
|
||||||
func (self *FunctionLiteral) Idx0() file.Idx { return self.Function }
|
|
||||||
func (self *Identifier) Idx0() file.Idx { return self.Idx }
|
|
||||||
func (self *NewExpression) Idx0() file.Idx { return self.New }
|
|
||||||
func (self *NullLiteral) Idx0() file.Idx { return self.Idx }
|
|
||||||
func (self *NumberLiteral) Idx0() file.Idx { return self.Idx }
|
|
||||||
func (self *ObjectLiteral) Idx0() file.Idx { return self.LeftBrace }
|
|
||||||
func (self *RegExpLiteral) Idx0() file.Idx { return self.Idx }
|
|
||||||
func (self *SequenceExpression) Idx0() file.Idx { return self.Sequence[0].Idx0() }
|
|
||||||
func (self *StringLiteral) Idx0() file.Idx { return self.Idx }
|
|
||||||
func (self *ThisExpression) Idx0() file.Idx { return self.Idx }
|
|
||||||
func (self *UnaryExpression) Idx0() file.Idx { return self.Idx }
|
|
||||||
func (self *VariableExpression) Idx0() file.Idx { return self.Idx }
|
|
||||||
|
|
||||||
func (self *BadStatement) Idx0() file.Idx { return self.From }
|
|
||||||
func (self *BlockStatement) Idx0() file.Idx { return self.LeftBrace }
|
|
||||||
func (self *BranchStatement) Idx0() file.Idx { return self.Idx }
|
|
||||||
func (self *CaseStatement) Idx0() file.Idx { return self.Case }
|
|
||||||
func (self *CatchStatement) Idx0() file.Idx { return self.Catch }
|
|
||||||
func (self *DebuggerStatement) Idx0() file.Idx { return self.Debugger }
|
|
||||||
func (self *DoWhileStatement) Idx0() file.Idx { return self.Do }
|
|
||||||
func (self *EmptyStatement) Idx0() file.Idx { return self.Semicolon }
|
|
||||||
func (self *ExpressionStatement) Idx0() file.Idx { return self.Expression.Idx0() }
|
|
||||||
func (self *ForInStatement) Idx0() file.Idx { return self.For }
|
|
||||||
func (self *ForStatement) Idx0() file.Idx { return self.For }
|
|
||||||
func (self *IfStatement) Idx0() file.Idx { return self.If }
|
|
||||||
func (self *LabelledStatement) Idx0() file.Idx { return self.Label.Idx0() }
|
|
||||||
func (self *Program) Idx0() file.Idx { return self.Body[0].Idx0() }
|
|
||||||
func (self *ReturnStatement) Idx0() file.Idx { return self.Return }
|
|
||||||
func (self *SwitchStatement) Idx0() file.Idx { return self.Switch }
|
|
||||||
func (self *ThrowStatement) Idx0() file.Idx { return self.Throw }
|
|
||||||
func (self *TryStatement) Idx0() file.Idx { return self.Try }
|
|
||||||
func (self *VariableStatement) Idx0() file.Idx { return self.Var }
|
|
||||||
func (self *WhileStatement) Idx0() file.Idx { return self.While }
|
|
||||||
func (self *WithStatement) Idx0() file.Idx { return self.With }
|
|
||||||
|
|
||||||
// ==== //
|
|
||||||
// Idx1 //
|
|
||||||
// ==== //
|
|
||||||
|
|
||||||
func (self *ArrayLiteral) Idx1() file.Idx { return self.RightBracket }
|
|
||||||
func (self *AssignExpression) Idx1() file.Idx { return self.Right.Idx1() }
|
|
||||||
func (self *BadExpression) Idx1() file.Idx { return self.To }
|
|
||||||
func (self *BinaryExpression) Idx1() file.Idx { return self.Right.Idx1() }
|
|
||||||
func (self *BooleanLiteral) Idx1() file.Idx { return file.Idx(int(self.Idx) + len(self.Literal)) }
|
|
||||||
func (self *BracketExpression) Idx1() file.Idx { return self.RightBracket + 1 }
|
|
||||||
func (self *CallExpression) Idx1() file.Idx { return self.RightParenthesis + 1 }
|
|
||||||
func (self *ConditionalExpression) Idx1() file.Idx { return self.Test.Idx1() }
|
|
||||||
func (self *DotExpression) Idx1() file.Idx { return self.Identifier.Idx1() }
|
|
||||||
func (self *FunctionLiteral) Idx1() file.Idx { return self.Body.Idx1() }
|
|
||||||
func (self *Identifier) Idx1() file.Idx { return file.Idx(int(self.Idx) + len(self.Name)) }
|
|
||||||
func (self *NewExpression) Idx1() file.Idx { return self.RightParenthesis + 1 }
|
|
||||||
func (self *NullLiteral) Idx1() file.Idx { return file.Idx(int(self.Idx) + 4) } // "null"
|
|
||||||
func (self *NumberLiteral) Idx1() file.Idx { return file.Idx(int(self.Idx) + len(self.Literal)) }
|
|
||||||
func (self *ObjectLiteral) Idx1() file.Idx { return self.RightBrace }
|
|
||||||
func (self *RegExpLiteral) Idx1() file.Idx { return file.Idx(int(self.Idx) + len(self.Literal)) }
|
|
||||||
func (self *SequenceExpression) Idx1() file.Idx { return self.Sequence[0].Idx1() }
|
|
||||||
func (self *StringLiteral) Idx1() file.Idx { return file.Idx(int(self.Idx) + len(self.Literal)) }
|
|
||||||
func (self *ThisExpression) Idx1() file.Idx { return self.Idx }
|
|
||||||
func (self *UnaryExpression) Idx1() file.Idx {
|
|
||||||
if self.Postfix {
|
|
||||||
return self.Operand.Idx1() + 2 // ++ --
|
|
||||||
}
|
|
||||||
return self.Operand.Idx1()
|
|
||||||
}
|
|
||||||
func (self *VariableExpression) Idx1() file.Idx {
|
|
||||||
if self.Initializer == nil {
|
|
||||||
return file.Idx(int(self.Idx) + len(self.Name) + 1)
|
|
||||||
}
|
|
||||||
return self.Initializer.Idx1()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *BadStatement) Idx1() file.Idx { return self.To }
|
|
||||||
func (self *BlockStatement) Idx1() file.Idx { return self.RightBrace + 1 }
|
|
||||||
func (self *BranchStatement) Idx1() file.Idx { return self.Idx }
|
|
||||||
func (self *CaseStatement) Idx1() file.Idx { return self.Consequent[len(self.Consequent)-1].Idx1() }
|
|
||||||
func (self *CatchStatement) Idx1() file.Idx { return self.Body.Idx1() }
|
|
||||||
func (self *DebuggerStatement) Idx1() file.Idx { return self.Debugger + 8 }
|
|
||||||
func (self *DoWhileStatement) Idx1() file.Idx { return self.Test.Idx1() }
|
|
||||||
func (self *EmptyStatement) Idx1() file.Idx { return self.Semicolon + 1 }
|
|
||||||
func (self *ExpressionStatement) Idx1() file.Idx { return self.Expression.Idx1() }
|
|
||||||
func (self *ForInStatement) Idx1() file.Idx { return self.Body.Idx1() }
|
|
||||||
func (self *ForStatement) Idx1() file.Idx { return self.Body.Idx1() }
|
|
||||||
func (self *IfStatement) Idx1() file.Idx {
|
|
||||||
if self.Alternate != nil {
|
|
||||||
return self.Alternate.Idx1()
|
|
||||||
}
|
|
||||||
return self.Consequent.Idx1()
|
|
||||||
}
|
|
||||||
func (self *LabelledStatement) Idx1() file.Idx { return self.Colon + 1 }
|
|
||||||
func (self *Program) Idx1() file.Idx { return self.Body[len(self.Body)-1].Idx1() }
|
|
||||||
func (self *ReturnStatement) Idx1() file.Idx { return self.Return }
|
|
||||||
func (self *SwitchStatement) Idx1() file.Idx { return self.Body[len(self.Body)-1].Idx1() }
|
|
||||||
func (self *ThrowStatement) Idx1() file.Idx { return self.Throw }
|
|
||||||
func (self *TryStatement) Idx1() file.Idx { return self.Try }
|
|
||||||
func (self *VariableStatement) Idx1() file.Idx { return self.List[len(self.List)-1].Idx1() }
|
|
||||||
func (self *WhileStatement) Idx1() file.Idx { return self.Body.Idx1() }
|
|
||||||
func (self *WithStatement) Idx1() file.Idx { return self.Body.Idx1() }
|
|
|
@ -1,85 +0,0 @@
|
||||||
package otto
|
|
||||||
|
|
||||||
func (runtime *_runtime) newEvalError(message Value) *_object {
|
|
||||||
self := runtime.newErrorObject(message)
|
|
||||||
self.prototype = runtime.Global.EvalErrorPrototype
|
|
||||||
return self
|
|
||||||
}
|
|
||||||
|
|
||||||
func builtinEvalError(call FunctionCall) Value {
|
|
||||||
return toValue_object(call.runtime.newEvalError(call.Argument(0)))
|
|
||||||
}
|
|
||||||
|
|
||||||
func builtinNewEvalError(self *_object, _ Value, argumentList []Value) Value {
|
|
||||||
return toValue_object(self.runtime.newEvalError(valueOfArrayIndex(argumentList, 0)))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (runtime *_runtime) newTypeError(message Value) *_object {
|
|
||||||
self := runtime.newErrorObject(message)
|
|
||||||
self.prototype = runtime.Global.TypeErrorPrototype
|
|
||||||
return self
|
|
||||||
}
|
|
||||||
|
|
||||||
func builtinTypeError(call FunctionCall) Value {
|
|
||||||
return toValue_object(call.runtime.newTypeError(call.Argument(0)))
|
|
||||||
}
|
|
||||||
|
|
||||||
func builtinNewTypeError(self *_object, _ Value, argumentList []Value) Value {
|
|
||||||
return toValue_object(self.runtime.newTypeError(valueOfArrayIndex(argumentList, 0)))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (runtime *_runtime) newRangeError(message Value) *_object {
|
|
||||||
self := runtime.newErrorObject(message)
|
|
||||||
self.prototype = runtime.Global.RangeErrorPrototype
|
|
||||||
return self
|
|
||||||
}
|
|
||||||
|
|
||||||
func builtinRangeError(call FunctionCall) Value {
|
|
||||||
return toValue_object(call.runtime.newRangeError(call.Argument(0)))
|
|
||||||
}
|
|
||||||
|
|
||||||
func builtinNewRangeError(self *_object, _ Value, argumentList []Value) Value {
|
|
||||||
return toValue_object(self.runtime.newRangeError(valueOfArrayIndex(argumentList, 0)))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (runtime *_runtime) newURIError(message Value) *_object {
|
|
||||||
self := runtime.newErrorObject(message)
|
|
||||||
self.prototype = runtime.Global.URIErrorPrototype
|
|
||||||
return self
|
|
||||||
}
|
|
||||||
|
|
||||||
func (runtime *_runtime) newReferenceError(message Value) *_object {
|
|
||||||
self := runtime.newErrorObject(message)
|
|
||||||
self.prototype = runtime.Global.ReferenceErrorPrototype
|
|
||||||
return self
|
|
||||||
}
|
|
||||||
|
|
||||||
func builtinReferenceError(call FunctionCall) Value {
|
|
||||||
return toValue_object(call.runtime.newReferenceError(call.Argument(0)))
|
|
||||||
}
|
|
||||||
|
|
||||||
func builtinNewReferenceError(self *_object, _ Value, argumentList []Value) Value {
|
|
||||||
return toValue_object(self.runtime.newReferenceError(valueOfArrayIndex(argumentList, 0)))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (runtime *_runtime) newSyntaxError(message Value) *_object {
|
|
||||||
self := runtime.newErrorObject(message)
|
|
||||||
self.prototype = runtime.Global.SyntaxErrorPrototype
|
|
||||||
return self
|
|
||||||
}
|
|
||||||
|
|
||||||
func builtinSyntaxError(call FunctionCall) Value {
|
|
||||||
return toValue_object(call.runtime.newSyntaxError(call.Argument(0)))
|
|
||||||
}
|
|
||||||
|
|
||||||
func builtinNewSyntaxError(self *_object, _ Value, argumentList []Value) Value {
|
|
||||||
return toValue_object(self.runtime.newSyntaxError(valueOfArrayIndex(argumentList, 0)))
|
|
||||||
}
|
|
||||||
|
|
||||||
func builtinURIError(call FunctionCall) Value {
|
|
||||||
return toValue_object(call.runtime.newURIError(call.Argument(0)))
|
|
||||||
}
|
|
||||||
|
|
||||||
func builtinNewURIError(self *_object, _ Value, argumentList []Value) Value {
|
|
||||||
return toValue_object(self.runtime.newURIError(valueOfArrayIndex(argumentList, 0)))
|
|
||||||
}
|
|
|
@ -1,144 +0,0 @@
|
||||||
package otto
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
type _clone struct {
|
|
||||||
runtime *_runtime
|
|
||||||
stash struct {
|
|
||||||
object map[*_object]*_object
|
|
||||||
objectEnvironment map[*_objectEnvironment]*_objectEnvironment
|
|
||||||
declarativeEnvironment map[*_declarativeEnvironment]*_declarativeEnvironment
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (runtime *_runtime) clone() *_runtime {
|
|
||||||
|
|
||||||
self := &_runtime{}
|
|
||||||
clone := &_clone{
|
|
||||||
runtime: self,
|
|
||||||
}
|
|
||||||
clone.stash.object = make(map[*_object]*_object)
|
|
||||||
clone.stash.objectEnvironment = make(map[*_objectEnvironment]*_objectEnvironment)
|
|
||||||
clone.stash.declarativeEnvironment = make(map[*_declarativeEnvironment]*_declarativeEnvironment)
|
|
||||||
|
|
||||||
globalObject := clone.object(runtime.GlobalObject)
|
|
||||||
self.GlobalEnvironment = self.newObjectEnvironment(globalObject, nil)
|
|
||||||
self.GlobalObject = globalObject
|
|
||||||
self.Global = _global{
|
|
||||||
clone.object(runtime.Global.Object),
|
|
||||||
clone.object(runtime.Global.Function),
|
|
||||||
clone.object(runtime.Global.Array),
|
|
||||||
clone.object(runtime.Global.String),
|
|
||||||
clone.object(runtime.Global.Boolean),
|
|
||||||
clone.object(runtime.Global.Number),
|
|
||||||
clone.object(runtime.Global.Math),
|
|
||||||
clone.object(runtime.Global.Date),
|
|
||||||
clone.object(runtime.Global.RegExp),
|
|
||||||
clone.object(runtime.Global.Error),
|
|
||||||
clone.object(runtime.Global.EvalError),
|
|
||||||
clone.object(runtime.Global.TypeError),
|
|
||||||
clone.object(runtime.Global.RangeError),
|
|
||||||
clone.object(runtime.Global.ReferenceError),
|
|
||||||
clone.object(runtime.Global.SyntaxError),
|
|
||||||
clone.object(runtime.Global.URIError),
|
|
||||||
clone.object(runtime.Global.JSON),
|
|
||||||
|
|
||||||
clone.object(runtime.Global.ObjectPrototype),
|
|
||||||
clone.object(runtime.Global.FunctionPrototype),
|
|
||||||
clone.object(runtime.Global.ArrayPrototype),
|
|
||||||
clone.object(runtime.Global.StringPrototype),
|
|
||||||
clone.object(runtime.Global.BooleanPrototype),
|
|
||||||
clone.object(runtime.Global.NumberPrototype),
|
|
||||||
clone.object(runtime.Global.DatePrototype),
|
|
||||||
clone.object(runtime.Global.RegExpPrototype),
|
|
||||||
clone.object(runtime.Global.ErrorPrototype),
|
|
||||||
clone.object(runtime.Global.EvalErrorPrototype),
|
|
||||||
clone.object(runtime.Global.TypeErrorPrototype),
|
|
||||||
clone.object(runtime.Global.RangeErrorPrototype),
|
|
||||||
clone.object(runtime.Global.ReferenceErrorPrototype),
|
|
||||||
clone.object(runtime.Global.SyntaxErrorPrototype),
|
|
||||||
clone.object(runtime.Global.URIErrorPrototype),
|
|
||||||
}
|
|
||||||
|
|
||||||
self.EnterGlobalExecutionContext()
|
|
||||||
|
|
||||||
self.eval = self.GlobalObject.property["eval"].value.(Value).value.(*_object)
|
|
||||||
self.GlobalObject.prototype = self.Global.ObjectPrototype
|
|
||||||
|
|
||||||
return self
|
|
||||||
}
|
|
||||||
func (clone *_clone) object(self0 *_object) *_object {
|
|
||||||
if self1, exists := clone.stash.object[self0]; exists {
|
|
||||||
return self1
|
|
||||||
}
|
|
||||||
self1 := &_object{}
|
|
||||||
clone.stash.object[self0] = self1
|
|
||||||
return self0.objectClass.clone(self0, self1, clone)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (clone *_clone) declarativeEnvironment(self0 *_declarativeEnvironment) (*_declarativeEnvironment, bool) {
|
|
||||||
if self1, exists := clone.stash.declarativeEnvironment[self0]; exists {
|
|
||||||
return self1, true
|
|
||||||
}
|
|
||||||
self1 := &_declarativeEnvironment{}
|
|
||||||
clone.stash.declarativeEnvironment[self0] = self1
|
|
||||||
return self1, false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (clone *_clone) objectEnvironment(self0 *_objectEnvironment) (*_objectEnvironment, bool) {
|
|
||||||
if self1, exists := clone.stash.objectEnvironment[self0]; exists {
|
|
||||||
return self1, true
|
|
||||||
}
|
|
||||||
self1 := &_objectEnvironment{}
|
|
||||||
clone.stash.objectEnvironment[self0] = self1
|
|
||||||
return self1, false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (clone *_clone) value(self0 Value) Value {
|
|
||||||
self1 := self0
|
|
||||||
switch value := self0.value.(type) {
|
|
||||||
case *_object:
|
|
||||||
self1.value = clone.object(value)
|
|
||||||
}
|
|
||||||
return self1
|
|
||||||
}
|
|
||||||
|
|
||||||
func (clone *_clone) valueArray(self0 []Value) []Value {
|
|
||||||
self1 := make([]Value, len(self0))
|
|
||||||
for index, value := range self0 {
|
|
||||||
self1[index] = clone.value(value)
|
|
||||||
}
|
|
||||||
return self1
|
|
||||||
}
|
|
||||||
|
|
||||||
func (clone *_clone) environment(self0 _environment) _environment {
|
|
||||||
if self0 == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return self0.clone(clone)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (clone *_clone) property(self0 _property) _property {
|
|
||||||
self1 := self0
|
|
||||||
if value, valid := self0.value.(Value); valid {
|
|
||||||
self1.value = clone.value(value)
|
|
||||||
} else {
|
|
||||||
panic(fmt.Errorf("self0.value.(Value) != true"))
|
|
||||||
}
|
|
||||||
return self1
|
|
||||||
}
|
|
||||||
|
|
||||||
func (clone *_clone) declarativeProperty(self0 _declarativeProperty) _declarativeProperty {
|
|
||||||
self1 := self0
|
|
||||||
self1.value = clone.value(self0.value)
|
|
||||||
return self1
|
|
||||||
}
|
|
||||||
|
|
||||||
func (clone *_clone) callFunction(self0 _callFunction) _callFunction {
|
|
||||||
if self0 == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return self0.clone(clone)
|
|
||||||
}
|
|
|
@ -1,46 +0,0 @@
|
||||||
package otto
|
|
||||||
|
|
||||||
// _cmpl_nodeCallFunction
|
|
||||||
type _cmpl_nodeCallFunction struct {
|
|
||||||
node *_nodeFunctionLiteral
|
|
||||||
scopeEnvironment _environment // Can be either Lexical or Variable
|
|
||||||
}
|
|
||||||
|
|
||||||
func new_nodeCallFunction(node *_nodeFunctionLiteral, scopeEnvironment _environment) *_cmpl_nodeCallFunction {
|
|
||||||
self := &_cmpl_nodeCallFunction{
|
|
||||||
node: node,
|
|
||||||
}
|
|
||||||
self.scopeEnvironment = scopeEnvironment
|
|
||||||
return self
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self _cmpl_nodeCallFunction) Dispatch(function *_object, environment *_functionEnvironment, runtime *_runtime, this Value, argumentList []Value, _ bool) Value {
|
|
||||||
return runtime.cmpl_call_nodeFunction(function, environment, self.node, this, argumentList)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self _cmpl_nodeCallFunction) ScopeEnvironment() _environment {
|
|
||||||
return self.scopeEnvironment
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self _cmpl_nodeCallFunction) Source(object *_object) string {
|
|
||||||
return self.node.source
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self0 _cmpl_nodeCallFunction) clone(clone *_clone) _callFunction {
|
|
||||||
return _cmpl_nodeCallFunction{
|
|
||||||
node: self0.node,
|
|
||||||
scopeEnvironment: clone.environment(self0.scopeEnvironment),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---
|
|
||||||
|
|
||||||
func (runtime *_runtime) newNodeFunctionObject(node *_nodeFunctionLiteral, scopeEnvironment _environment) *_object {
|
|
||||||
self := runtime.newClassObject("Function")
|
|
||||||
self.value = _functionObject{
|
|
||||||
call: new_nodeCallFunction(node, scopeEnvironment),
|
|
||||||
construct: defaultConstructFunction,
|
|
||||||
}
|
|
||||||
self.defineProperty("length", toValue_int(len(node.parameterList)), 0000, false)
|
|
||||||
return self
|
|
||||||
}
|
|
|
@ -1,387 +0,0 @@
|
||||||
// This file was AUTOMATICALLY GENERATED by dbg-import (smuggol) from github.com/robertkrimen/dbg
|
|
||||||
|
|
||||||
/*
|
|
||||||
Package dbg is a println/printf/log-debugging utility library.
|
|
||||||
|
|
||||||
import (
|
|
||||||
Dbg "github.com/robertkrimen/dbg"
|
|
||||||
)
|
|
||||||
|
|
||||||
dbg, dbgf := Dbg.New()
|
|
||||||
|
|
||||||
dbg("Emit some debug stuff", []byte{120, 121, 122, 122, 121}, math.Pi)
|
|
||||||
# "2013/01/28 16:50:03 Emit some debug stuff [120 121 122 122 121] 3.141592653589793"
|
|
||||||
|
|
||||||
dbgf("With a %s formatting %.2f", "little", math.Pi)
|
|
||||||
# "2013/01/28 16:51:55 With a little formatting (3.14)"
|
|
||||||
|
|
||||||
dbgf("%/fatal//A fatal debug statement: should not be here")
|
|
||||||
# "A fatal debug statement: should not be here"
|
|
||||||
# ...and then, os.Exit(1)
|
|
||||||
|
|
||||||
dbgf("%/panic//Can also panic %s", "this")
|
|
||||||
# "Can also panic this"
|
|
||||||
# ...as a panic, equivalent to: panic("Can also panic this")
|
|
||||||
|
|
||||||
dbgf("Any %s arguments without a corresponding %%", "extra", "are treated like arguments to dbg()")
|
|
||||||
# "2013/01/28 17:14:40 Any extra arguments (without a corresponding %) are treated like arguments to dbg()"
|
|
||||||
|
|
||||||
dbgf("%d %d", 1, 2, 3, 4, 5)
|
|
||||||
# "2013/01/28 17:16:32 Another example: 1 2 3 4 5"
|
|
||||||
|
|
||||||
dbgf("%@: Include the function name for a little context (via %s)", "%@")
|
|
||||||
# "2013... github.com/robertkrimen/dbg.TestSynopsis: Include the function name for a little context (via %@)"
|
|
||||||
|
|
||||||
By default, dbg uses log (log.Println, log.Printf, log.Panic, etc.) for output.
|
|
||||||
However, you can also provide your own output destination by invoking dbg.New with
|
|
||||||
a customization function:
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
Dbg "github.com/robertkrimen/dbg"
|
|
||||||
"os"
|
|
||||||
)
|
|
||||||
|
|
||||||
# dbg to os.Stderr
|
|
||||||
dbg, dbgf := Dbg.New(func(dbgr *Dbgr) {
|
|
||||||
dbgr.SetOutput(os.Stderr)
|
|
||||||
})
|
|
||||||
|
|
||||||
# A slightly contrived example:
|
|
||||||
var buffer bytes.Buffer
|
|
||||||
dbg, dbgf := New(func(dbgr *Dbgr) {
|
|
||||||
dbgr.SetOutput(&buffer)
|
|
||||||
})
|
|
||||||
|
|
||||||
*/
|
|
||||||
package dbg
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"regexp"
|
|
||||||
"runtime"
|
|
||||||
"strings"
|
|
||||||
"unicode"
|
|
||||||
)
|
|
||||||
|
|
||||||
type _frmt struct {
|
|
||||||
ctl string
|
|
||||||
format string
|
|
||||||
operandCount int
|
|
||||||
panic bool
|
|
||||||
fatal bool
|
|
||||||
check bool
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
ctlTest = regexp.MustCompile(`^\s*%/`)
|
|
||||||
ctlScan = regexp.MustCompile(`%?/(panic|fatal|check)(?:\s|$)`)
|
|
||||||
)
|
|
||||||
|
|
||||||
func operandCount(format string) int {
|
|
||||||
count := 0
|
|
||||||
end := len(format)
|
|
||||||
for at := 0; at < end; {
|
|
||||||
for at < end && format[at] != '%' {
|
|
||||||
at++
|
|
||||||
}
|
|
||||||
at++
|
|
||||||
if at < end {
|
|
||||||
if format[at] != '%' && format[at] != '@' {
|
|
||||||
count++
|
|
||||||
}
|
|
||||||
at++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return count
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseFormat(format string) (frmt _frmt) {
|
|
||||||
if ctlTest.MatchString(format) {
|
|
||||||
format = strings.TrimLeftFunc(format, unicode.IsSpace)
|
|
||||||
index := strings.Index(format, "//")
|
|
||||||
if index != -1 {
|
|
||||||
frmt.ctl = format[0:index]
|
|
||||||
format = format[index+2:] // Skip the second slash via +2 (instead of +1)
|
|
||||||
} else {
|
|
||||||
frmt.ctl = format
|
|
||||||
format = ""
|
|
||||||
}
|
|
||||||
for _, tmp := range ctlScan.FindAllStringSubmatch(frmt.ctl, -1) {
|
|
||||||
for _, value := range tmp[1:] {
|
|
||||||
switch value {
|
|
||||||
case "panic":
|
|
||||||
frmt.panic = true
|
|
||||||
case "fatal":
|
|
||||||
frmt.fatal = true
|
|
||||||
case "check":
|
|
||||||
frmt.check = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
frmt.format = format
|
|
||||||
frmt.operandCount = operandCount(format)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
type Dbgr struct {
|
|
||||||
emit _emit
|
|
||||||
}
|
|
||||||
|
|
||||||
type DbgFunction func(values ...interface{})
|
|
||||||
|
|
||||||
func NewDbgr() *Dbgr {
|
|
||||||
self := &Dbgr{}
|
|
||||||
return self
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
New will create and return a pair of debugging functions. You can customize where
|
|
||||||
they output to by passing in an (optional) customization function:
|
|
||||||
|
|
||||||
import (
|
|
||||||
Dbg "github.com/robertkrimen/dbg"
|
|
||||||
"os"
|
|
||||||
)
|
|
||||||
|
|
||||||
# dbg to os.Stderr
|
|
||||||
dbg, dbgf := Dbg.New(func(dbgr *Dbgr) {
|
|
||||||
dbgr.SetOutput(os.Stderr)
|
|
||||||
})
|
|
||||||
|
|
||||||
*/
|
|
||||||
func New(options ...interface{}) (dbg DbgFunction, dbgf DbgFunction) {
|
|
||||||
dbgr := NewDbgr()
|
|
||||||
if len(options) > 0 {
|
|
||||||
if fn, ok := options[0].(func(*Dbgr)); ok {
|
|
||||||
fn(dbgr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return dbgr.DbgDbgf()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self Dbgr) Dbg(values ...interface{}) {
|
|
||||||
self.getEmit().emit(_frmt{}, "", values...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self Dbgr) Dbgf(values ...interface{}) {
|
|
||||||
self.dbgf(values...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self Dbgr) DbgDbgf() (dbg DbgFunction, dbgf DbgFunction) {
|
|
||||||
dbg = func(vl ...interface{}) {
|
|
||||||
self.Dbg(vl...)
|
|
||||||
}
|
|
||||||
dbgf = func(vl ...interface{}) {
|
|
||||||
self.dbgf(vl...)
|
|
||||||
}
|
|
||||||
return dbg, dbgf // Redundant, but...
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self Dbgr) dbgf(values ...interface{}) {
|
|
||||||
|
|
||||||
var frmt _frmt
|
|
||||||
if len(values) > 0 {
|
|
||||||
tmp := fmt.Sprint(values[0])
|
|
||||||
frmt = parseFormat(tmp)
|
|
||||||
values = values[1:]
|
|
||||||
}
|
|
||||||
|
|
||||||
buffer_f := bytes.Buffer{}
|
|
||||||
format := frmt.format
|
|
||||||
end := len(format)
|
|
||||||
for at := 0; at < end; {
|
|
||||||
last := at
|
|
||||||
for at < end && format[at] != '%' {
|
|
||||||
at++
|
|
||||||
}
|
|
||||||
if at > last {
|
|
||||||
buffer_f.WriteString(format[last:at])
|
|
||||||
}
|
|
||||||
if at >= end {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
// format[at] == '%'
|
|
||||||
at++
|
|
||||||
// format[at] == ?
|
|
||||||
if format[at] == '@' {
|
|
||||||
depth := 2
|
|
||||||
pc, _, _, _ := runtime.Caller(depth)
|
|
||||||
name := runtime.FuncForPC(pc).Name()
|
|
||||||
buffer_f.WriteString(name)
|
|
||||||
} else {
|
|
||||||
buffer_f.WriteString(format[at-1 : at+1])
|
|
||||||
}
|
|
||||||
at++
|
|
||||||
}
|
|
||||||
|
|
||||||
//values_f := append([]interface{}{}, values[0:frmt.operandCount]...)
|
|
||||||
values_f := values[0:frmt.operandCount]
|
|
||||||
values_dbg := values[frmt.operandCount:]
|
|
||||||
if len(values_dbg) > 0 {
|
|
||||||
// Adjust frmt.format:
|
|
||||||
// (%v instead of %s because: frmt.check)
|
|
||||||
{
|
|
||||||
tmp := format
|
|
||||||
if len(tmp) > 0 {
|
|
||||||
if unicode.IsSpace(rune(tmp[len(tmp)-1])) {
|
|
||||||
buffer_f.WriteString("%v")
|
|
||||||
} else {
|
|
||||||
buffer_f.WriteString(" %v")
|
|
||||||
}
|
|
||||||
} else if frmt.check {
|
|
||||||
// Performing a check, so no output
|
|
||||||
} else {
|
|
||||||
buffer_f.WriteString("%v")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Adjust values_f:
|
|
||||||
if !frmt.check {
|
|
||||||
tmp := []string{}
|
|
||||||
for _, value := range values_dbg {
|
|
||||||
tmp = append(tmp, fmt.Sprintf("%v", value))
|
|
||||||
}
|
|
||||||
// First, make a copy of values_f, so we avoid overwriting values_dbg when appending
|
|
||||||
values_f = append([]interface{}{}, values_f...)
|
|
||||||
values_f = append(values_f, strings.Join(tmp, " "))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
format = buffer_f.String()
|
|
||||||
if frmt.check {
|
|
||||||
// We do not actually emit to the log, but panic if
|
|
||||||
// a non-nil value is detected (e.g. a non-nil error)
|
|
||||||
for _, value := range values_dbg {
|
|
||||||
if value != nil {
|
|
||||||
if format == "" {
|
|
||||||
panic(value)
|
|
||||||
} else {
|
|
||||||
panic(fmt.Sprintf(format, append(values_f, value)...))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
self.getEmit().emit(frmt, format, values_f...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Idiot-proof &Dbgr{}, etc.
|
|
||||||
func (self *Dbgr) getEmit() _emit {
|
|
||||||
if self.emit == nil {
|
|
||||||
self.emit = standardEmit()
|
|
||||||
}
|
|
||||||
return self.emit
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetOutput will accept the following as a destination for output:
|
|
||||||
//
|
|
||||||
// *log.Logger Print*/Panic*/Fatal* of the logger
|
|
||||||
// io.Writer -
|
|
||||||
// nil Reset to the default output (os.Stderr)
|
|
||||||
// "log" Print*/Panic*/Fatal* via the "log" package
|
|
||||||
//
|
|
||||||
func (self *Dbgr) SetOutput(output interface{}) {
|
|
||||||
if output == nil {
|
|
||||||
self.emit = standardEmit()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
switch output := output.(type) {
|
|
||||||
case *log.Logger:
|
|
||||||
self.emit = _emitLogger{
|
|
||||||
logger: output,
|
|
||||||
}
|
|
||||||
return
|
|
||||||
case io.Writer:
|
|
||||||
self.emit = _emitWriter{
|
|
||||||
writer: output,
|
|
||||||
}
|
|
||||||
return
|
|
||||||
case string:
|
|
||||||
if output == "log" {
|
|
||||||
self.emit = _emitLog{}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
panic(output)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ======== //
|
|
||||||
// = emit = //
|
|
||||||
// ======== //
|
|
||||||
|
|
||||||
func standardEmit() _emit {
|
|
||||||
return _emitWriter{
|
|
||||||
writer: os.Stderr,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func ln(tmp string) string {
|
|
||||||
length := len(tmp)
|
|
||||||
if length > 0 && tmp[length-1] != '\n' {
|
|
||||||
return tmp + "\n"
|
|
||||||
}
|
|
||||||
return tmp
|
|
||||||
}
|
|
||||||
|
|
||||||
type _emit interface {
|
|
||||||
emit(_frmt, string, ...interface{})
|
|
||||||
}
|
|
||||||
|
|
||||||
type _emitWriter struct {
|
|
||||||
writer io.Writer
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self _emitWriter) emit(frmt _frmt, format string, values ...interface{}) {
|
|
||||||
if format == "" {
|
|
||||||
fmt.Fprintln(self.writer, values...)
|
|
||||||
} else {
|
|
||||||
if frmt.panic {
|
|
||||||
panic(fmt.Sprintf(format, values...))
|
|
||||||
}
|
|
||||||
fmt.Fprintf(self.writer, ln(format), values...)
|
|
||||||
if frmt.fatal {
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type _emitLogger struct {
|
|
||||||
logger *log.Logger
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self _emitLogger) emit(frmt _frmt, format string, values ...interface{}) {
|
|
||||||
if format == "" {
|
|
||||||
self.logger.Println(values...)
|
|
||||||
} else {
|
|
||||||
if frmt.panic {
|
|
||||||
self.logger.Panicf(format, values...)
|
|
||||||
} else if frmt.fatal {
|
|
||||||
self.logger.Fatalf(format, values...)
|
|
||||||
} else {
|
|
||||||
self.logger.Printf(format, values...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type _emitLog struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self _emitLog) emit(frmt _frmt, format string, values ...interface{}) {
|
|
||||||
if format == "" {
|
|
||||||
log.Println(values...)
|
|
||||||
} else {
|
|
||||||
if frmt.panic {
|
|
||||||
log.Panicf(format, values...)
|
|
||||||
} else if frmt.fatal {
|
|
||||||
log.Fatalf(format, values...)
|
|
||||||
} else {
|
|
||||||
log.Printf(format, values...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,280 +0,0 @@
|
||||||
package otto
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
// _environment
|
|
||||||
|
|
||||||
type _environment interface {
|
|
||||||
HasBinding(string) bool
|
|
||||||
|
|
||||||
CreateMutableBinding(string, bool)
|
|
||||||
SetMutableBinding(string, Value, bool)
|
|
||||||
// SetMutableBinding with Lazy CreateMutableBinding(..., true)
|
|
||||||
SetValue(string, Value, bool)
|
|
||||||
|
|
||||||
GetBindingValue(string, bool) Value
|
|
||||||
GetValue(string, bool) Value // GetBindingValue
|
|
||||||
DeleteBinding(string) bool
|
|
||||||
ImplicitThisValue() *_object
|
|
||||||
|
|
||||||
Outer() _environment
|
|
||||||
|
|
||||||
newReference(string, bool) _reference
|
|
||||||
clone(clone *_clone) _environment
|
|
||||||
runtimeOf() *_runtime
|
|
||||||
}
|
|
||||||
|
|
||||||
// _functionEnvironment
|
|
||||||
|
|
||||||
type _functionEnvironment struct {
|
|
||||||
_declarativeEnvironment
|
|
||||||
arguments *_object
|
|
||||||
indexOfArgumentName map[string]string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (runtime *_runtime) newFunctionEnvironment(outer _environment) *_functionEnvironment {
|
|
||||||
return &_functionEnvironment{
|
|
||||||
_declarativeEnvironment: _declarativeEnvironment{
|
|
||||||
runtime: runtime,
|
|
||||||
outer: outer,
|
|
||||||
property: map[string]_declarativeProperty{},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self0 _functionEnvironment) clone(clone *_clone) _environment {
|
|
||||||
return &_functionEnvironment{
|
|
||||||
*(self0._declarativeEnvironment.clone(clone).(*_declarativeEnvironment)),
|
|
||||||
clone.object(self0.arguments),
|
|
||||||
self0.indexOfArgumentName,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self _functionEnvironment) runtimeOf() *_runtime {
|
|
||||||
return self._declarativeEnvironment.runtimeOf()
|
|
||||||
}
|
|
||||||
|
|
||||||
// _objectEnvironment
|
|
||||||
|
|
||||||
type _objectEnvironment struct {
|
|
||||||
runtime *_runtime
|
|
||||||
outer _environment
|
|
||||||
Object *_object
|
|
||||||
ProvideThis bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_objectEnvironment) runtimeOf() *_runtime {
|
|
||||||
return self.runtime
|
|
||||||
}
|
|
||||||
|
|
||||||
func (runtime *_runtime) newObjectEnvironment(object *_object, outer _environment) *_objectEnvironment {
|
|
||||||
if object == nil {
|
|
||||||
object = runtime.newBaseObject()
|
|
||||||
object.class = "environment"
|
|
||||||
}
|
|
||||||
return &_objectEnvironment{
|
|
||||||
runtime: runtime,
|
|
||||||
outer: outer,
|
|
||||||
Object: object,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self0 *_objectEnvironment) clone(clone *_clone) _environment {
|
|
||||||
self1, exists := clone.objectEnvironment(self0)
|
|
||||||
if exists {
|
|
||||||
return self1
|
|
||||||
}
|
|
||||||
*self1 = _objectEnvironment{
|
|
||||||
clone.runtime,
|
|
||||||
clone.environment(self0.outer),
|
|
||||||
clone.object(self0.Object),
|
|
||||||
self0.ProvideThis,
|
|
||||||
}
|
|
||||||
return self1
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_objectEnvironment) HasBinding(name string) bool {
|
|
||||||
return self.Object.hasProperty(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_objectEnvironment) CreateMutableBinding(name string, deletable bool) {
|
|
||||||
if self.Object.hasProperty(name) {
|
|
||||||
panic(hereBeDragons())
|
|
||||||
}
|
|
||||||
mode := _propertyMode(0111)
|
|
||||||
if !deletable {
|
|
||||||
mode = _propertyMode(0110)
|
|
||||||
}
|
|
||||||
// TODO False?
|
|
||||||
self.Object.defineProperty(name, UndefinedValue(), mode, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_objectEnvironment) SetMutableBinding(name string, value Value, strict bool) {
|
|
||||||
self.Object.put(name, value, strict)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_objectEnvironment) SetValue(name string, value Value, throw bool) {
|
|
||||||
if !self.HasBinding(name) {
|
|
||||||
self.CreateMutableBinding(name, true) // Configurable by default
|
|
||||||
}
|
|
||||||
self.SetMutableBinding(name, value, throw)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_objectEnvironment) GetBindingValue(name string, strict bool) Value {
|
|
||||||
if self.Object.hasProperty(name) {
|
|
||||||
return self.Object.get(name)
|
|
||||||
}
|
|
||||||
if strict {
|
|
||||||
panic(newReferenceError("Not Defined", name))
|
|
||||||
}
|
|
||||||
return UndefinedValue()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_objectEnvironment) GetValue(name string, throw bool) Value {
|
|
||||||
return self.GetBindingValue(name, throw)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_objectEnvironment) DeleteBinding(name string) bool {
|
|
||||||
return self.Object.delete(name, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_objectEnvironment) ImplicitThisValue() *_object {
|
|
||||||
if self.ProvideThis {
|
|
||||||
return self.Object
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_objectEnvironment) Outer() _environment {
|
|
||||||
return self.outer
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_objectEnvironment) newReference(name string, strict bool) _reference {
|
|
||||||
return newPropertyReference(self.Object, name, strict)
|
|
||||||
}
|
|
||||||
|
|
||||||
// _declarativeEnvironment
|
|
||||||
|
|
||||||
func (runtime *_runtime) newDeclarativeEnvironment(outer _environment) *_declarativeEnvironment {
|
|
||||||
return &_declarativeEnvironment{
|
|
||||||
runtime: runtime,
|
|
||||||
outer: outer,
|
|
||||||
property: map[string]_declarativeProperty{},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self0 *_declarativeEnvironment) clone(clone *_clone) _environment {
|
|
||||||
self1, exists := clone.declarativeEnvironment(self0)
|
|
||||||
if exists {
|
|
||||||
return self1
|
|
||||||
}
|
|
||||||
property := make(map[string]_declarativeProperty, len(self0.property))
|
|
||||||
for index, value := range self0.property {
|
|
||||||
property[index] = clone.declarativeProperty(value)
|
|
||||||
}
|
|
||||||
*self1 = _declarativeEnvironment{
|
|
||||||
clone.runtime,
|
|
||||||
clone.environment(self0.outer),
|
|
||||||
property,
|
|
||||||
}
|
|
||||||
return self1
|
|
||||||
}
|
|
||||||
|
|
||||||
type _declarativeProperty struct {
|
|
||||||
value Value
|
|
||||||
mutable bool
|
|
||||||
deletable bool
|
|
||||||
readable bool
|
|
||||||
}
|
|
||||||
|
|
||||||
type _declarativeEnvironment struct {
|
|
||||||
runtime *_runtime
|
|
||||||
outer _environment
|
|
||||||
property map[string]_declarativeProperty
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_declarativeEnvironment) HasBinding(name string) bool {
|
|
||||||
_, exists := self.property[name]
|
|
||||||
return exists
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_declarativeEnvironment) runtimeOf() *_runtime {
|
|
||||||
return self.runtime
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_declarativeEnvironment) CreateMutableBinding(name string, deletable bool) {
|
|
||||||
_, exists := self.property[name]
|
|
||||||
if exists {
|
|
||||||
panic(fmt.Errorf("CreateMutableBinding: %s: already exists", name))
|
|
||||||
}
|
|
||||||
self.property[name] = _declarativeProperty{
|
|
||||||
value: UndefinedValue(),
|
|
||||||
mutable: true,
|
|
||||||
deletable: deletable,
|
|
||||||
readable: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_declarativeEnvironment) SetMutableBinding(name string, value Value, strict bool) {
|
|
||||||
property, exists := self.property[name]
|
|
||||||
if !exists {
|
|
||||||
panic(fmt.Errorf("SetMutableBinding: %s: missing", name))
|
|
||||||
}
|
|
||||||
if property.mutable {
|
|
||||||
property.value = value
|
|
||||||
self.property[name] = property
|
|
||||||
} else {
|
|
||||||
typeErrorResult(strict)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_declarativeEnvironment) SetValue(name string, value Value, throw bool) {
|
|
||||||
if !self.HasBinding(name) {
|
|
||||||
self.CreateMutableBinding(name, false) // NOT deletable by default
|
|
||||||
}
|
|
||||||
self.SetMutableBinding(name, value, throw)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_declarativeEnvironment) GetBindingValue(name string, strict bool) Value {
|
|
||||||
property, exists := self.property[name]
|
|
||||||
if !exists {
|
|
||||||
panic(fmt.Errorf("GetBindingValue: %s: missing", name))
|
|
||||||
}
|
|
||||||
if !property.mutable && !property.readable {
|
|
||||||
if strict {
|
|
||||||
panic(newTypeError())
|
|
||||||
}
|
|
||||||
return UndefinedValue()
|
|
||||||
}
|
|
||||||
return property.value
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_declarativeEnvironment) GetValue(name string, throw bool) Value {
|
|
||||||
return self.GetBindingValue(name, throw)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_declarativeEnvironment) DeleteBinding(name string) bool {
|
|
||||||
property, exists := self.property[name]
|
|
||||||
if !exists {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if !property.deletable {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
delete(self.property, name)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_declarativeEnvironment) ImplicitThisValue() *_object {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_declarativeEnvironment) Outer() _environment {
|
|
||||||
return self.outer
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_declarativeEnvironment) newReference(name string, strict bool) _reference {
|
|
||||||
return newEnvironmentReference(self, name, strict, nil)
|
|
||||||
}
|
|
|
@ -1,152 +0,0 @@
|
||||||
package otto
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/robertkrimen/otto/ast"
|
|
||||||
)
|
|
||||||
|
|
||||||
type _exception struct {
|
|
||||||
value interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func newException(value interface{}) *_exception {
|
|
||||||
return &_exception{
|
|
||||||
value: value,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_exception) eject() interface{} {
|
|
||||||
value := self.value
|
|
||||||
self.value = nil // Prevent Go from holding on to the value, whatever it is
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
|
|
||||||
type _error struct {
|
|
||||||
Name string
|
|
||||||
Message string
|
|
||||||
|
|
||||||
Line int // Hackish -- line where the error/exception occurred
|
|
||||||
}
|
|
||||||
|
|
||||||
var messageDetail map[string]string = map[string]string{
|
|
||||||
"notDefined": "%v is not defined",
|
|
||||||
}
|
|
||||||
|
|
||||||
func messageFromDescription(description string, argumentList ...interface{}) string {
|
|
||||||
message := messageDetail[description]
|
|
||||||
if message == "" {
|
|
||||||
message = description
|
|
||||||
}
|
|
||||||
message = fmt.Sprintf(message, argumentList...)
|
|
||||||
return message
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self _error) MessageValue() Value {
|
|
||||||
if self.Message == "" {
|
|
||||||
return UndefinedValue()
|
|
||||||
}
|
|
||||||
return toValue_string(self.Message)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self _error) String() string {
|
|
||||||
if len(self.Name) == 0 {
|
|
||||||
return self.Message
|
|
||||||
}
|
|
||||||
if len(self.Message) == 0 {
|
|
||||||
return self.Name
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("%s: %s", self.Name, self.Message)
|
|
||||||
}
|
|
||||||
|
|
||||||
func newError(name string, argumentList ...interface{}) _error {
|
|
||||||
description := ""
|
|
||||||
var node ast.Node = nil
|
|
||||||
length := len(argumentList)
|
|
||||||
if length > 0 {
|
|
||||||
if node, _ = argumentList[length-1].(ast.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{
|
|
||||||
Name: name,
|
|
||||||
Message: messageFromDescription(description, argumentList...),
|
|
||||||
Line: -1,
|
|
||||||
}
|
|
||||||
//error := _error{
|
|
||||||
// Name: name,
|
|
||||||
// Message: messageFromDescription(description, argumentList...),
|
|
||||||
// Line: -1,
|
|
||||||
//}
|
|
||||||
//if node != nil {
|
|
||||||
// error.Line = ast.position()
|
|
||||||
//}
|
|
||||||
//return error
|
|
||||||
}
|
|
||||||
|
|
||||||
func newReferenceError(argumentList ...interface{}) _error {
|
|
||||||
return newError("ReferenceError", argumentList...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func newTypeError(argumentList ...interface{}) _error {
|
|
||||||
return newError("TypeError", argumentList...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func newRangeError(argumentList ...interface{}) _error {
|
|
||||||
return newError("RangeError", argumentList...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func newSyntaxError(argumentList ...interface{}) _error {
|
|
||||||
return newError("SyntaxError", argumentList...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func newURIError(argumentList ...interface{}) _error {
|
|
||||||
return newError("URIError", argumentList...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func typeErrorResult(throw bool) bool {
|
|
||||||
if throw {
|
|
||||||
panic(newTypeError())
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func catchPanic(function func()) (err error) {
|
|
||||||
// FIXME
|
|
||||||
defer func() {
|
|
||||||
if caught := recover(); caught != nil {
|
|
||||||
if exception, ok := caught.(*_exception); ok {
|
|
||||||
caught = exception.eject()
|
|
||||||
}
|
|
||||||
switch caught := caught.(type) {
|
|
||||||
//case *_syntaxError:
|
|
||||||
// err = errors.New(fmt.Sprintf("%s (line %d)", caught.String(), caught.Line+0))
|
|
||||||
// return
|
|
||||||
case _error:
|
|
||||||
if caught.Line == -1 {
|
|
||||||
err = errors.New(caught.String())
|
|
||||||
} else {
|
|
||||||
// We're 0-based (for now), hence the + 1
|
|
||||||
err = errors.New(fmt.Sprintf("%s (line %d)", caught.String(), caught.Line+1))
|
|
||||||
}
|
|
||||||
return
|
|
||||||
case Value:
|
|
||||||
err = errors.New(toString(caught))
|
|
||||||
return
|
|
||||||
//case string:
|
|
||||||
// if strings.HasPrefix(caught, "SyntaxError:") {
|
|
||||||
// err = errors.New(caught)
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
panic(caught)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
function()
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -1,62 +0,0 @@
|
||||||
package otto
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestError(t *testing.T) {
|
|
||||||
tt(t, func() {
|
|
||||||
test, _ := test()
|
|
||||||
|
|
||||||
test(`
|
|
||||||
[ Error.prototype.name, Error.prototype.message, Error.prototype.hasOwnProperty("message") ];
|
|
||||||
`, "Error,,true")
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestError_instanceof(t *testing.T) {
|
|
||||||
tt(t, func() {
|
|
||||||
test, _ := test()
|
|
||||||
|
|
||||||
test(`(new TypeError()) instanceof Error`, true)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPanicValue(t *testing.T) {
|
|
||||||
tt(t, func() {
|
|
||||||
test, vm := test()
|
|
||||||
|
|
||||||
vm.Set("abc", func(call FunctionCall) Value {
|
|
||||||
value, err := call.Otto.Run(`({ def: 3.14159 })`)
|
|
||||||
is(err, nil)
|
|
||||||
panic(value)
|
|
||||||
})
|
|
||||||
|
|
||||||
test(`
|
|
||||||
try {
|
|
||||||
abc();
|
|
||||||
}
|
|
||||||
catch (err) {
|
|
||||||
error = err;
|
|
||||||
}
|
|
||||||
[ error instanceof Error, error.message, error.def ];
|
|
||||||
`, "false,,3.14159")
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_catchPanic(t *testing.T) {
|
|
||||||
tt(t, func() {
|
|
||||||
vm := New()
|
|
||||||
|
|
||||||
_, err := vm.Run(`
|
|
||||||
A syntax error that
|
|
||||||
does not define
|
|
||||||
var;
|
|
||||||
abc;
|
|
||||||
`)
|
|
||||||
is(err, "!=", nil)
|
|
||||||
|
|
||||||
_, err = vm.Call(`abc.def`, nil)
|
|
||||||
is(err, "!=", nil)
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -1,40 +0,0 @@
|
||||||
package otto
|
|
||||||
|
|
||||||
type _executionContext struct {
|
|
||||||
LexicalEnvironment _environment
|
|
||||||
VariableEnvironment _environment
|
|
||||||
this *_object
|
|
||||||
eval bool // Replace this with kind?
|
|
||||||
}
|
|
||||||
|
|
||||||
func newExecutionContext(lexical _environment, variable _environment, this *_object) *_executionContext {
|
|
||||||
return &_executionContext{
|
|
||||||
LexicalEnvironment: lexical,
|
|
||||||
VariableEnvironment: variable,
|
|
||||||
this: this,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_executionContext) getValue(name string) Value {
|
|
||||||
strict := false
|
|
||||||
return self.LexicalEnvironment.GetValue(name, strict)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_executionContext) setValue(name string, value Value, throw bool) {
|
|
||||||
self.LexicalEnvironment.SetValue(name, value, throw)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_executionContext) newLexicalEnvironment(object *_object) (_environment, *_objectEnvironment) {
|
|
||||||
// Get runtime from the object (for now)
|
|
||||||
runtime := object.runtime
|
|
||||||
previousLexical := self.LexicalEnvironment
|
|
||||||
newLexical := runtime.newObjectEnvironment(object, self.LexicalEnvironment)
|
|
||||||
self.LexicalEnvironment = newLexical
|
|
||||||
return previousLexical, newLexical
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_executionContext) newDeclarativeEnvironment(runtime *_runtime) _environment {
|
|
||||||
previousLexical := self.LexicalEnvironment
|
|
||||||
self.LexicalEnvironment = runtime.newDeclarativeEnvironment(self.LexicalEnvironment)
|
|
||||||
return previousLexical
|
|
||||||
}
|
|
|
@ -1,72 +0,0 @@
|
||||||
# file
|
|
||||||
--
|
|
||||||
import "github.com/robertkrimen/otto/file"
|
|
||||||
|
|
||||||
Package file encapsulates the file abstractions used by the ast & parser.
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
#### type FileSet
|
|
||||||
|
|
||||||
```go
|
|
||||||
type FileSet struct {
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
A FileSet represents a set of source files.
|
|
||||||
|
|
||||||
#### func (*FileSet) AddFile
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (self *FileSet) AddFile(filename, src string) int
|
|
||||||
```
|
|
||||||
AddFile adds a new file with the given filename and src.
|
|
||||||
|
|
||||||
This an internal method, but exported for cross-package use.
|
|
||||||
|
|
||||||
#### func (*FileSet) Position
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (self *FileSet) Position(idx Idx) *Position
|
|
||||||
```
|
|
||||||
Position converts an Idx in the FileSet into a Position.
|
|
||||||
|
|
||||||
#### type Idx
|
|
||||||
|
|
||||||
```go
|
|
||||||
type Idx int
|
|
||||||
```
|
|
||||||
|
|
||||||
Idx is a compact encoding of a source position within a file set. It can be
|
|
||||||
converted into a Position for a more convenient, but much larger,
|
|
||||||
representation.
|
|
||||||
|
|
||||||
#### type Position
|
|
||||||
|
|
||||||
```go
|
|
||||||
type Position struct {
|
|
||||||
Filename string // The filename where the error occurred, if any
|
|
||||||
Offset int // The src offset
|
|
||||||
Line int // The line number, starting at 1
|
|
||||||
Column int // The column number, starting at 1 (The character count)
|
|
||||||
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Position describes an arbitrary source position including the filename, line,
|
|
||||||
and column location.
|
|
||||||
|
|
||||||
#### func (*Position) String
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (self *Position) String() string
|
|
||||||
```
|
|
||||||
String returns a string in one of several forms:
|
|
||||||
|
|
||||||
file:line:column A valid position with filename
|
|
||||||
line:column A valid position without filename
|
|
||||||
file An invalid position with filename
|
|
||||||
- An invalid position without filename
|
|
||||||
|
|
||||||
--
|
|
||||||
**godocdown** http://github.com/robertkrimen/godocdown
|
|
|
@ -1,106 +0,0 @@
|
||||||
// Package file encapsulates the file abstractions used by the ast & parser.
|
|
||||||
//
|
|
||||||
package file
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Idx is a compact encoding of a source position within a file set.
|
|
||||||
// It can be converted into a Position for a more convenient, but much
|
|
||||||
// larger, representation.
|
|
||||||
type Idx int
|
|
||||||
|
|
||||||
// Position describes an arbitrary source position
|
|
||||||
// including the filename, line, and column location.
|
|
||||||
type Position struct {
|
|
||||||
Filename string // The filename where the error occurred, if any
|
|
||||||
Offset int // The src offset
|
|
||||||
Line int // The line number, starting at 1
|
|
||||||
Column int // The column number, starting at 1 (The character count)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// A Position is valid if the line number is > 0.
|
|
||||||
|
|
||||||
func (self *Position) isValid() bool {
|
|
||||||
return self.Line > 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns a string in one of several forms:
|
|
||||||
//
|
|
||||||
// file:line:column A valid position with filename
|
|
||||||
// line:column A valid position without filename
|
|
||||||
// file An invalid position with filename
|
|
||||||
// - An invalid position without filename
|
|
||||||
//
|
|
||||||
func (self *Position) String() string {
|
|
||||||
str := self.Filename
|
|
||||||
if self.isValid() {
|
|
||||||
if str != "" {
|
|
||||||
str += ":"
|
|
||||||
}
|
|
||||||
str += fmt.Sprintf("%d:%d", self.Line, self.Column)
|
|
||||||
}
|
|
||||||
if str == "" {
|
|
||||||
str = "-"
|
|
||||||
}
|
|
||||||
return str
|
|
||||||
}
|
|
||||||
|
|
||||||
// FileSet
|
|
||||||
|
|
||||||
// A FileSet represents a set of source files.
|
|
||||||
type FileSet struct {
|
|
||||||
files []*_file
|
|
||||||
last *_file
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddFile adds a new file with the given filename and src.
|
|
||||||
//
|
|
||||||
// This an internal method, but exported for cross-package use.
|
|
||||||
func (self *FileSet) AddFile(filename, src string) int {
|
|
||||||
base := self.nextBase()
|
|
||||||
file := &_file{
|
|
||||||
filename: filename,
|
|
||||||
src: src,
|
|
||||||
base: base,
|
|
||||||
}
|
|
||||||
self.files = append(self.files, file)
|
|
||||||
self.last = file
|
|
||||||
return base
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *FileSet) nextBase() int {
|
|
||||||
if self.last == nil {
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
return self.last.base + len(self.last.src) + 1
|
|
||||||
}
|
|
||||||
|
|
||||||
// Position converts an Idx in the FileSet into a Position.
|
|
||||||
func (self *FileSet) Position(idx Idx) *Position {
|
|
||||||
position := &Position{}
|
|
||||||
for _, file := range self.files {
|
|
||||||
if idx <= Idx(file.base+len(file.src)) {
|
|
||||||
offset := int(idx) - file.base
|
|
||||||
src := file.src[:offset]
|
|
||||||
position.Filename = file.filename
|
|
||||||
position.Offset = offset
|
|
||||||
position.Line = 1 + strings.Count(src, "\n")
|
|
||||||
if index := strings.LastIndex(src, "\n"); index >= 0 {
|
|
||||||
position.Column = offset - index
|
|
||||||
} else {
|
|
||||||
position.Column = 1 + len(src)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return position
|
|
||||||
}
|
|
||||||
|
|
||||||
type _file struct {
|
|
||||||
filename string
|
|
||||||
src string
|
|
||||||
base int // This will always be 1 or greater
|
|
||||||
}
|
|
|
@ -1,4 +0,0 @@
|
||||||
.PHONY: test
|
|
||||||
|
|
||||||
test:
|
|
||||||
go test
|
|
|
@ -1,190 +0,0 @@
|
||||||
# parser
|
|
||||||
--
|
|
||||||
import "github.com/robertkrimen/otto/parser"
|
|
||||||
|
|
||||||
Package parser implements a parser for JavaScript.
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/robertkrimen/otto/parser"
|
|
||||||
)
|
|
||||||
|
|
||||||
Parse and return an AST
|
|
||||||
|
|
||||||
filename := "" // A filename is optional
|
|
||||||
src := `
|
|
||||||
// Sample xyzzy example
|
|
||||||
(function(){
|
|
||||||
if (3.14159 > 0) {
|
|
||||||
console.log("Hello, World.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var xyzzy = NaN;
|
|
||||||
console.log("Nothing happens.");
|
|
||||||
return xyzzy;
|
|
||||||
})();
|
|
||||||
`
|
|
||||||
|
|
||||||
// Parse some JavaScript, yielding a *ast.Program and/or an ErrorList
|
|
||||||
program, err := parser.ParseFile(nil, filename, src, 0)
|
|
||||||
|
|
||||||
|
|
||||||
### Warning
|
|
||||||
|
|
||||||
The parser and AST interfaces are still works-in-progress (particularly where
|
|
||||||
node types are concerned) and may change in the future.
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
#### func ParseFile
|
|
||||||
|
|
||||||
```go
|
|
||||||
func ParseFile(fileSet *file.FileSet, filename string, src interface{}, mode Mode) (*ast.Program, error)
|
|
||||||
```
|
|
||||||
ParseFile parses the source code of a single JavaScript/ECMAScript source file
|
|
||||||
and returns the corresponding ast.Program node.
|
|
||||||
|
|
||||||
If fileSet == nil, ParseFile parses source without a FileSet. If fileSet != nil,
|
|
||||||
ParseFile first adds filename and src to fileSet.
|
|
||||||
|
|
||||||
The filename argument is optional and is used for labelling errors, etc.
|
|
||||||
|
|
||||||
src may be a string, a byte slice, a bytes.Buffer, or an io.Reader, but it MUST
|
|
||||||
always be in UTF-8.
|
|
||||||
|
|
||||||
// Parse some JavaScript, yielding a *ast.Program and/or an ErrorList
|
|
||||||
program, err := parser.ParseFile(nil, "", `if (abc > 1) {}`, 0)
|
|
||||||
|
|
||||||
#### func ParseFunction
|
|
||||||
|
|
||||||
```go
|
|
||||||
func ParseFunction(parameterList, body string) (*ast.FunctionLiteral, error)
|
|
||||||
```
|
|
||||||
ParseFunction parses a given parameter list and body as a function and returns
|
|
||||||
the corresponding ast.FunctionLiteral node.
|
|
||||||
|
|
||||||
The parameter list, if any, should be a comma-separated list of identifiers.
|
|
||||||
|
|
||||||
#### func ReadSource
|
|
||||||
|
|
||||||
```go
|
|
||||||
func ReadSource(filename string, src interface{}) ([]byte, error)
|
|
||||||
```
|
|
||||||
|
|
||||||
#### func TransformRegExp
|
|
||||||
|
|
||||||
```go
|
|
||||||
func TransformRegExp(pattern string) (string, error)
|
|
||||||
```
|
|
||||||
TransformRegExp transforms a JavaScript pattern into a Go "regexp" pattern.
|
|
||||||
|
|
||||||
re2 (Go) cannot do backtracking, so the presence of a lookahead (?=) (?!) or
|
|
||||||
backreference (\1, \2, ...) will cause an error.
|
|
||||||
|
|
||||||
re2 (Go) has a different definition for \s: [\t\n\f\r ]. The JavaScript
|
|
||||||
definition, on the other hand, also includes \v, Unicode "Separator, Space",
|
|
||||||
etc.
|
|
||||||
|
|
||||||
If the pattern is invalid (not valid even in JavaScript), then this function
|
|
||||||
returns the empty string and an error.
|
|
||||||
|
|
||||||
If the pattern is valid, but incompatible (contains a lookahead or
|
|
||||||
backreference), then this function returns the transformation (a non-empty
|
|
||||||
string) AND an error.
|
|
||||||
|
|
||||||
#### type Error
|
|
||||||
|
|
||||||
```go
|
|
||||||
type Error struct {
|
|
||||||
Position file.Position
|
|
||||||
Message string
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
An Error represents a parsing error. It includes the position where the error
|
|
||||||
occurred and a message/description.
|
|
||||||
|
|
||||||
#### func (Error) Error
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (self Error) Error() string
|
|
||||||
```
|
|
||||||
|
|
||||||
#### type ErrorList
|
|
||||||
|
|
||||||
```go
|
|
||||||
type ErrorList []*Error
|
|
||||||
```
|
|
||||||
|
|
||||||
ErrorList is a list of *Errors.
|
|
||||||
|
|
||||||
#### func (*ErrorList) Add
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (self *ErrorList) Add(position file.Position, msg string)
|
|
||||||
```
|
|
||||||
Add adds an Error with given position and message to an ErrorList.
|
|
||||||
|
|
||||||
#### func (ErrorList) Err
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (self ErrorList) Err() error
|
|
||||||
```
|
|
||||||
Err returns an error equivalent to this ErrorList. If the list is empty, Err
|
|
||||||
returns nil.
|
|
||||||
|
|
||||||
#### func (ErrorList) Error
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (self ErrorList) Error() string
|
|
||||||
```
|
|
||||||
Error implements the Error interface.
|
|
||||||
|
|
||||||
#### func (ErrorList) Len
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (self ErrorList) Len() int
|
|
||||||
```
|
|
||||||
|
|
||||||
#### func (ErrorList) Less
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (self ErrorList) Less(i, j int) bool
|
|
||||||
```
|
|
||||||
|
|
||||||
#### func (*ErrorList) Reset
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (self *ErrorList) Reset()
|
|
||||||
```
|
|
||||||
Reset resets an ErrorList to no errors.
|
|
||||||
|
|
||||||
#### func (ErrorList) Sort
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (self ErrorList) Sort()
|
|
||||||
```
|
|
||||||
|
|
||||||
#### func (ErrorList) Swap
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (self ErrorList) Swap(i, j int)
|
|
||||||
```
|
|
||||||
|
|
||||||
#### type Mode
|
|
||||||
|
|
||||||
```go
|
|
||||||
type Mode uint
|
|
||||||
```
|
|
||||||
|
|
||||||
A Mode value is a set of flags (or 0). They control optional parser
|
|
||||||
functionality.
|
|
||||||
|
|
||||||
```go
|
|
||||||
const (
|
|
||||||
IgnoreRegExpErrors Mode = 1 << iota // Ignore RegExp compatibility errors (allow backtracking)
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
--
|
|
||||||
**godocdown** http://github.com/robertkrimen/godocdown
|
|
|
@ -1,9 +0,0 @@
|
||||||
// This file was AUTOMATICALLY GENERATED by dbg-import (smuggol) for github.com/robertkrimen/dbg
|
|
||||||
|
|
||||||
package parser
|
|
||||||
|
|
||||||
import (
|
|
||||||
Dbg "github.com/robertkrimen/otto/dbg"
|
|
||||||
)
|
|
||||||
|
|
||||||
var dbg, dbgf = Dbg.New()
|
|
|
@ -1,175 +0,0 @@
|
||||||
package parser
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"sort"
|
|
||||||
|
|
||||||
"github.com/robertkrimen/otto/file"
|
|
||||||
"github.com/robertkrimen/otto/token"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
err_UnexpectedToken = "Unexpected token %v"
|
|
||||||
err_UnexpectedEndOfInput = "Unexpected end of input"
|
|
||||||
err_UnexpectedEscape = "Unexpected escape"
|
|
||||||
)
|
|
||||||
|
|
||||||
// UnexpectedNumber: 'Unexpected number',
|
|
||||||
// UnexpectedString: 'Unexpected string',
|
|
||||||
// UnexpectedIdentifier: 'Unexpected identifier',
|
|
||||||
// UnexpectedReserved: 'Unexpected reserved word',
|
|
||||||
// NewlineAfterThrow: 'Illegal newline after throw',
|
|
||||||
// InvalidRegExp: 'Invalid regular expression',
|
|
||||||
// UnterminatedRegExp: 'Invalid regular expression: missing /',
|
|
||||||
// InvalidLHSInAssignment: 'Invalid left-hand side in assignment',
|
|
||||||
// InvalidLHSInForIn: 'Invalid left-hand side in for-in',
|
|
||||||
// MultipleDefaultsInSwitch: 'More than one default clause in switch statement',
|
|
||||||
// NoCatchOrFinally: 'Missing catch or finally after try',
|
|
||||||
// UnknownLabel: 'Undefined label \'%0\'',
|
|
||||||
// Redeclaration: '%0 \'%1\' has already been declared',
|
|
||||||
// IllegalContinue: 'Illegal continue statement',
|
|
||||||
// IllegalBreak: 'Illegal break statement',
|
|
||||||
// IllegalReturn: 'Illegal return statement',
|
|
||||||
// StrictModeWith: 'Strict mode code may not include a with statement',
|
|
||||||
// StrictCatchVariable: 'Catch variable may not be eval or arguments in strict mode',
|
|
||||||
// StrictVarName: 'Variable name may not be eval or arguments in strict mode',
|
|
||||||
// StrictParamName: 'Parameter name eval or arguments is not allowed in strict mode',
|
|
||||||
// StrictParamDupe: 'Strict mode function may not have duplicate parameter names',
|
|
||||||
// StrictFunctionName: 'Function name may not be eval or arguments in strict mode',
|
|
||||||
// StrictOctalLiteral: 'Octal literals are not allowed in strict mode.',
|
|
||||||
// StrictDelete: 'Delete of an unqualified identifier in strict mode.',
|
|
||||||
// StrictDuplicateProperty: 'Duplicate data property in object literal not allowed in strict mode',
|
|
||||||
// AccessorDataProperty: 'Object literal may not have data and accessor property with the same name',
|
|
||||||
// AccessorGetSet: 'Object literal may not have multiple get/set accessors with the same name',
|
|
||||||
// StrictLHSAssignment: 'Assignment to eval or arguments is not allowed in strict mode',
|
|
||||||
// StrictLHSPostfix: 'Postfix increment/decrement may not have eval or arguments operand in strict mode',
|
|
||||||
// StrictLHSPrefix: 'Prefix increment/decrement may not have eval or arguments operand in strict mode',
|
|
||||||
// StrictReservedWord: 'Use of future reserved word in strict mode'
|
|
||||||
|
|
||||||
// A SyntaxError is a description of an ECMAScript syntax error.
|
|
||||||
|
|
||||||
// An Error represents a parsing error. It includes the position where the error occurred and a message/description.
|
|
||||||
type Error struct {
|
|
||||||
Position file.Position
|
|
||||||
Message string
|
|
||||||
}
|
|
||||||
|
|
||||||
// FXIME Should this be "SyntaxError"?
|
|
||||||
|
|
||||||
func (self Error) Error() string {
|
|
||||||
filename := self.Position.Filename
|
|
||||||
if filename == "" {
|
|
||||||
filename = "(anonymous)"
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("%s: Line %d:%d %s",
|
|
||||||
filename,
|
|
||||||
self.Position.Line,
|
|
||||||
self.Position.Column,
|
|
||||||
self.Message,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_parser) error(place interface{}, msg string, msgValues ...interface{}) *Error {
|
|
||||||
idx := file.Idx(0)
|
|
||||||
switch place := place.(type) {
|
|
||||||
case int:
|
|
||||||
idx = self.idxOf(place)
|
|
||||||
case file.Idx:
|
|
||||||
if place == 0 {
|
|
||||||
idx = self.idxOf(self.chrOffset)
|
|
||||||
} else {
|
|
||||||
idx = place
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
panic(fmt.Errorf("error(%T, ...)", place))
|
|
||||||
}
|
|
||||||
|
|
||||||
position := self.position(idx)
|
|
||||||
msg = fmt.Sprintf(msg, msgValues...)
|
|
||||||
self.errors.Add(position, msg)
|
|
||||||
return self.errors[len(self.errors)-1]
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_parser) errorUnexpected(idx file.Idx, chr rune) error {
|
|
||||||
if chr == -1 {
|
|
||||||
return self.error(idx, err_UnexpectedEndOfInput)
|
|
||||||
}
|
|
||||||
return self.error(idx, err_UnexpectedToken, token.ILLEGAL)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_parser) errorUnexpectedToken(tkn token.Token) error {
|
|
||||||
switch tkn {
|
|
||||||
case token.EOF:
|
|
||||||
return self.error(file.Idx(0), err_UnexpectedEndOfInput)
|
|
||||||
}
|
|
||||||
value := tkn.String()
|
|
||||||
switch tkn {
|
|
||||||
case token.BOOLEAN, token.NULL:
|
|
||||||
value = self.literal
|
|
||||||
case token.IDENTIFIER:
|
|
||||||
return self.error(self.idx, "Unexpected identifier")
|
|
||||||
case token.KEYWORD:
|
|
||||||
// TODO Might be a future reserved word
|
|
||||||
return self.error(self.idx, "Unexpected reserved word")
|
|
||||||
case token.NUMBER:
|
|
||||||
return self.error(self.idx, "Unexpected number")
|
|
||||||
case token.STRING:
|
|
||||||
return self.error(self.idx, "Unexpected string")
|
|
||||||
}
|
|
||||||
return self.error(self.idx, err_UnexpectedToken, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ErrorList is a list of *Errors.
|
|
||||||
//
|
|
||||||
type ErrorList []*Error
|
|
||||||
|
|
||||||
// Add adds an Error with given position and message to an ErrorList.
|
|
||||||
func (self *ErrorList) Add(position file.Position, msg string) {
|
|
||||||
*self = append(*self, &Error{position, msg})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset resets an ErrorList to no errors.
|
|
||||||
func (self *ErrorList) Reset() { *self = (*self)[0:0] }
|
|
||||||
|
|
||||||
func (self ErrorList) Len() int { return len(self) }
|
|
||||||
func (self ErrorList) Swap(i, j int) { self[i], self[j] = self[j], self[i] }
|
|
||||||
func (self ErrorList) Less(i, j int) bool {
|
|
||||||
x := &self[i].Position
|
|
||||||
y := &self[j].Position
|
|
||||||
if x.Filename < y.Filename {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if x.Filename == y.Filename {
|
|
||||||
if x.Line < y.Line {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if x.Line == y.Line {
|
|
||||||
return x.Column < y.Column
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self ErrorList) Sort() {
|
|
||||||
sort.Sort(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Error implements the Error interface.
|
|
||||||
func (self ErrorList) Error() string {
|
|
||||||
switch len(self) {
|
|
||||||
case 0:
|
|
||||||
return "no errors"
|
|
||||||
case 1:
|
|
||||||
return self[0].Error()
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("%s (and %d more errors)", self[0].Error(), len(self)-1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Err returns an error equivalent to this ErrorList.
|
|
||||||
// If the list is empty, Err returns nil.
|
|
||||||
func (self ErrorList) Err() error {
|
|
||||||
if len(self) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return self
|
|
||||||
}
|
|
|
@ -1,815 +0,0 @@
|
||||||
package parser
|
|
||||||
|
|
||||||
import (
|
|
||||||
"regexp"
|
|
||||||
|
|
||||||
"github.com/robertkrimen/otto/ast"
|
|
||||||
"github.com/robertkrimen/otto/file"
|
|
||||||
"github.com/robertkrimen/otto/token"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (self *_parser) parseIdentifier() *ast.Identifier {
|
|
||||||
literal := self.literal
|
|
||||||
idx := self.idx
|
|
||||||
self.next()
|
|
||||||
return &ast.Identifier{
|
|
||||||
Name: literal,
|
|
||||||
Idx: idx,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_parser) parsePrimaryExpression() ast.Expression {
|
|
||||||
literal := self.literal
|
|
||||||
idx := self.idx
|
|
||||||
switch self.token {
|
|
||||||
case token.IDENTIFIER:
|
|
||||||
self.next()
|
|
||||||
if len(literal) > 1 {
|
|
||||||
tkn, strict := token.IsKeyword(literal)
|
|
||||||
if tkn == token.KEYWORD {
|
|
||||||
if !strict {
|
|
||||||
self.error(idx, "Unexpected reserved word")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return &ast.Identifier{
|
|
||||||
Name: literal,
|
|
||||||
Idx: idx,
|
|
||||||
}
|
|
||||||
case token.NULL:
|
|
||||||
self.next()
|
|
||||||
return &ast.NullLiteral{
|
|
||||||
Idx: idx,
|
|
||||||
Literal: literal,
|
|
||||||
}
|
|
||||||
case token.BOOLEAN:
|
|
||||||
self.next()
|
|
||||||
value := false
|
|
||||||
switch literal {
|
|
||||||
case "true":
|
|
||||||
value = true
|
|
||||||
case "false":
|
|
||||||
value = false
|
|
||||||
default:
|
|
||||||
self.error(idx, "Illegal boolean literal")
|
|
||||||
}
|
|
||||||
return &ast.BooleanLiteral{
|
|
||||||
Idx: idx,
|
|
||||||
Literal: literal,
|
|
||||||
Value: value,
|
|
||||||
}
|
|
||||||
case token.STRING:
|
|
||||||
self.next()
|
|
||||||
value, err := parseStringLiteral(literal[1 : len(literal)-1])
|
|
||||||
if err != nil {
|
|
||||||
self.error(idx, err.Error())
|
|
||||||
}
|
|
||||||
return &ast.StringLiteral{
|
|
||||||
Idx: idx,
|
|
||||||
Literal: literal,
|
|
||||||
Value: value,
|
|
||||||
}
|
|
||||||
case token.NUMBER:
|
|
||||||
self.next()
|
|
||||||
value, err := parseNumberLiteral(literal)
|
|
||||||
if err != nil {
|
|
||||||
self.error(idx, err.Error())
|
|
||||||
value = 0
|
|
||||||
}
|
|
||||||
return &ast.NumberLiteral{
|
|
||||||
Idx: idx,
|
|
||||||
Literal: literal,
|
|
||||||
Value: value,
|
|
||||||
}
|
|
||||||
case token.SLASH, token.QUOTIENT_ASSIGN:
|
|
||||||
return self.parseRegExpLiteral()
|
|
||||||
case token.LEFT_BRACE:
|
|
||||||
return self.parseObjectLiteral()
|
|
||||||
case token.LEFT_BRACKET:
|
|
||||||
return self.parseArrayLiteral()
|
|
||||||
case token.LEFT_PARENTHESIS:
|
|
||||||
self.expect(token.LEFT_PARENTHESIS)
|
|
||||||
expression := self.parseExpression()
|
|
||||||
self.expect(token.RIGHT_PARENTHESIS)
|
|
||||||
return expression
|
|
||||||
case token.THIS:
|
|
||||||
self.next()
|
|
||||||
return &ast.ThisExpression{
|
|
||||||
Idx: idx,
|
|
||||||
}
|
|
||||||
case token.FUNCTION:
|
|
||||||
return self.parseFunction(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
self.errorUnexpectedToken(self.token)
|
|
||||||
self.nextStatement()
|
|
||||||
return &ast.BadExpression{From: idx, To: self.idx}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_parser) parseRegExpLiteral() *ast.RegExpLiteral {
|
|
||||||
|
|
||||||
offset := self.chrOffset - 1 // Opening slash already gotten
|
|
||||||
if self.token == token.QUOTIENT_ASSIGN {
|
|
||||||
offset -= 1 // =
|
|
||||||
}
|
|
||||||
idx := self.idxOf(offset)
|
|
||||||
|
|
||||||
pattern, err := self.scanString(offset)
|
|
||||||
endOffset := self.chrOffset
|
|
||||||
|
|
||||||
self.next()
|
|
||||||
if err == nil {
|
|
||||||
pattern = pattern[1 : len(pattern)-1]
|
|
||||||
}
|
|
||||||
|
|
||||||
flags := ""
|
|
||||||
if self.token == token.IDENTIFIER { // gim
|
|
||||||
|
|
||||||
flags = self.literal
|
|
||||||
self.next()
|
|
||||||
endOffset = self.chrOffset - 1
|
|
||||||
}
|
|
||||||
|
|
||||||
var value string
|
|
||||||
// TODO 15.10
|
|
||||||
{
|
|
||||||
// Test during parsing that this is a valid regular expression
|
|
||||||
// Sorry, (?=) and (?!) are invalid (for now)
|
|
||||||
pattern, err := TransformRegExp(pattern)
|
|
||||||
if err != nil {
|
|
||||||
if pattern == "" || self.mode&IgnoreRegExpErrors == 0 {
|
|
||||||
self.error(idx, "Invalid regular expression: %s", err.Error())
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
_, err = regexp.Compile(pattern)
|
|
||||||
if err != nil {
|
|
||||||
// We should not get here, ParseRegExp should catch any errors
|
|
||||||
self.error(idx, "Invalid regular expression: %s", err.Error()[22:]) // Skip redundant "parse regexp error"
|
|
||||||
} else {
|
|
||||||
value = pattern
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
literal := self.str[offset:endOffset]
|
|
||||||
|
|
||||||
return &ast.RegExpLiteral{
|
|
||||||
Idx: idx,
|
|
||||||
Literal: literal,
|
|
||||||
Pattern: pattern,
|
|
||||||
Flags: flags,
|
|
||||||
Value: value,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_parser) parseVariableDeclaration(declarationList *[]*ast.VariableExpression) ast.Expression {
|
|
||||||
|
|
||||||
if self.token != token.IDENTIFIER {
|
|
||||||
idx := self.expect(token.IDENTIFIER)
|
|
||||||
self.nextStatement()
|
|
||||||
return &ast.BadExpression{From: idx, To: self.idx}
|
|
||||||
}
|
|
||||||
|
|
||||||
literal := self.literal
|
|
||||||
idx := self.idx
|
|
||||||
self.next()
|
|
||||||
node := &ast.VariableExpression{
|
|
||||||
Name: literal,
|
|
||||||
Idx: idx,
|
|
||||||
}
|
|
||||||
|
|
||||||
if declarationList != nil {
|
|
||||||
*declarationList = append(*declarationList, node)
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.token == token.ASSIGN {
|
|
||||||
self.next()
|
|
||||||
node.Initializer = self.parseAssignmentExpression()
|
|
||||||
}
|
|
||||||
|
|
||||||
return node
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_parser) parseVariableDeclarationList(var_ file.Idx) []ast.Expression {
|
|
||||||
|
|
||||||
var declarationList []*ast.VariableExpression // Avoid bad expressions
|
|
||||||
var list []ast.Expression
|
|
||||||
|
|
||||||
for {
|
|
||||||
list = append(list, self.parseVariableDeclaration(&declarationList))
|
|
||||||
if self.token != token.COMMA {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
self.next()
|
|
||||||
}
|
|
||||||
|
|
||||||
self.scope.declare(&ast.VariableDeclaration{
|
|
||||||
Var: var_,
|
|
||||||
List: declarationList,
|
|
||||||
})
|
|
||||||
|
|
||||||
return list
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_parser) parseObjectPropertyKey() (string, string) {
|
|
||||||
idx, tkn, literal := self.idx, self.token, self.literal
|
|
||||||
value := ""
|
|
||||||
self.next()
|
|
||||||
switch tkn {
|
|
||||||
case token.IDENTIFIER:
|
|
||||||
value = literal
|
|
||||||
case token.NUMBER:
|
|
||||||
var err error
|
|
||||||
_, err = parseNumberLiteral(literal)
|
|
||||||
if err != nil {
|
|
||||||
self.error(idx, err.Error())
|
|
||||||
} else {
|
|
||||||
value = literal
|
|
||||||
}
|
|
||||||
case token.STRING:
|
|
||||||
var err error
|
|
||||||
value, err = parseStringLiteral(literal[1 : len(literal)-1])
|
|
||||||
if err != nil {
|
|
||||||
self.error(idx, err.Error())
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
// null, false, class, etc.
|
|
||||||
if matchIdentifier.MatchString(literal) {
|
|
||||||
value = literal
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return literal, value
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_parser) parseObjectProperty() ast.Property {
|
|
||||||
|
|
||||||
literal, value := self.parseObjectPropertyKey()
|
|
||||||
if literal == "get" && self.token != token.COLON {
|
|
||||||
idx := self.idx
|
|
||||||
_, value := self.parseObjectPropertyKey()
|
|
||||||
parameterList := self.parseFunctionParameterList()
|
|
||||||
|
|
||||||
node := &ast.FunctionLiteral{
|
|
||||||
Function: idx,
|
|
||||||
ParameterList: parameterList,
|
|
||||||
}
|
|
||||||
self.parseFunctionBlock(node)
|
|
||||||
return ast.Property{
|
|
||||||
Key: value,
|
|
||||||
Kind: "get",
|
|
||||||
Value: node,
|
|
||||||
}
|
|
||||||
} else if literal == "set" && self.token != token.COLON {
|
|
||||||
idx := self.idx
|
|
||||||
_, value := self.parseObjectPropertyKey()
|
|
||||||
parameterList := self.parseFunctionParameterList()
|
|
||||||
|
|
||||||
node := &ast.FunctionLiteral{
|
|
||||||
Function: idx,
|
|
||||||
ParameterList: parameterList,
|
|
||||||
}
|
|
||||||
self.parseFunctionBlock(node)
|
|
||||||
return ast.Property{
|
|
||||||
Key: value,
|
|
||||||
Kind: "set",
|
|
||||||
Value: node,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.expect(token.COLON)
|
|
||||||
|
|
||||||
return ast.Property{
|
|
||||||
Key: value,
|
|
||||||
Kind: "value",
|
|
||||||
Value: self.parseAssignmentExpression(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_parser) parseObjectLiteral() ast.Expression {
|
|
||||||
var value []ast.Property
|
|
||||||
idx0 := self.expect(token.LEFT_BRACE)
|
|
||||||
for self.token != token.RIGHT_BRACE && self.token != token.EOF {
|
|
||||||
property := self.parseObjectProperty()
|
|
||||||
value = append(value, property)
|
|
||||||
if self.token == token.COMMA {
|
|
||||||
self.next()
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
idx1 := self.expect(token.RIGHT_BRACE)
|
|
||||||
|
|
||||||
return &ast.ObjectLiteral{
|
|
||||||
LeftBrace: idx0,
|
|
||||||
RightBrace: idx1,
|
|
||||||
Value: value,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_parser) parseArrayLiteral() ast.Expression {
|
|
||||||
|
|
||||||
idx0 := self.expect(token.LEFT_BRACKET)
|
|
||||||
var value []ast.Expression
|
|
||||||
for self.token != token.RIGHT_BRACKET && self.token != token.EOF {
|
|
||||||
if self.token == token.COMMA {
|
|
||||||
self.next()
|
|
||||||
value = append(value, nil)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
value = append(value, self.parseAssignmentExpression())
|
|
||||||
if self.token != token.RIGHT_BRACKET {
|
|
||||||
self.expect(token.COMMA)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
idx1 := self.expect(token.RIGHT_BRACKET)
|
|
||||||
|
|
||||||
return &ast.ArrayLiteral{
|
|
||||||
LeftBracket: idx0,
|
|
||||||
RightBracket: idx1,
|
|
||||||
Value: value,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_parser) parseArgumentList() (argumentList []ast.Expression, idx0, idx1 file.Idx) {
|
|
||||||
idx0 = self.expect(token.LEFT_PARENTHESIS)
|
|
||||||
if self.token != token.RIGHT_PARENTHESIS {
|
|
||||||
for {
|
|
||||||
argumentList = append(argumentList, self.parseAssignmentExpression())
|
|
||||||
if self.token != token.COMMA {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
self.next()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
idx1 = self.expect(token.RIGHT_PARENTHESIS)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_parser) parseCallExpression(left ast.Expression) ast.Expression {
|
|
||||||
argumentList, idx0, idx1 := self.parseArgumentList()
|
|
||||||
return &ast.CallExpression{
|
|
||||||
Callee: left,
|
|
||||||
LeftParenthesis: idx0,
|
|
||||||
ArgumentList: argumentList,
|
|
||||||
RightParenthesis: idx1,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_parser) parseDotMember(left ast.Expression) ast.Expression {
|
|
||||||
period := self.expect(token.PERIOD)
|
|
||||||
|
|
||||||
literal := self.literal
|
|
||||||
idx := self.idx
|
|
||||||
|
|
||||||
if !matchIdentifier.MatchString(literal) {
|
|
||||||
self.expect(token.IDENTIFIER)
|
|
||||||
self.nextStatement()
|
|
||||||
return &ast.BadExpression{From: period, To: self.idx}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.next()
|
|
||||||
|
|
||||||
return &ast.DotExpression{
|
|
||||||
Left: left,
|
|
||||||
Identifier: ast.Identifier{
|
|
||||||
Idx: idx,
|
|
||||||
Name: literal,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_parser) parseBracketMember(left ast.Expression) ast.Expression {
|
|
||||||
idx0 := self.expect(token.LEFT_BRACKET)
|
|
||||||
member := self.parseExpression()
|
|
||||||
idx1 := self.expect(token.RIGHT_BRACKET)
|
|
||||||
return &ast.BracketExpression{
|
|
||||||
LeftBracket: idx0,
|
|
||||||
Left: left,
|
|
||||||
Member: member,
|
|
||||||
RightBracket: idx1,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_parser) parseNewExpression() ast.Expression {
|
|
||||||
idx := self.expect(token.NEW)
|
|
||||||
callee := self.parseLeftHandSideExpression()
|
|
||||||
node := &ast.NewExpression{
|
|
||||||
New: idx,
|
|
||||||
Callee: callee,
|
|
||||||
}
|
|
||||||
if self.token == token.LEFT_PARENTHESIS {
|
|
||||||
argumentList, idx0, idx1 := self.parseArgumentList()
|
|
||||||
node.ArgumentList = argumentList
|
|
||||||
node.LeftParenthesis = idx0
|
|
||||||
node.RightParenthesis = idx1
|
|
||||||
}
|
|
||||||
return node
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_parser) parseLeftHandSideExpression() ast.Expression {
|
|
||||||
|
|
||||||
var left ast.Expression
|
|
||||||
if self.token == token.NEW {
|
|
||||||
left = self.parseNewExpression()
|
|
||||||
} else {
|
|
||||||
left = self.parsePrimaryExpression()
|
|
||||||
}
|
|
||||||
|
|
||||||
for {
|
|
||||||
if self.token == token.PERIOD {
|
|
||||||
left = self.parseDotMember(left)
|
|
||||||
} else if self.token == token.LEFT_BRACE {
|
|
||||||
left = self.parseBracketMember(left)
|
|
||||||
} else {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return left
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_parser) parseLeftHandSideExpressionAllowCall() ast.Expression {
|
|
||||||
|
|
||||||
allowIn := self.scope.allowIn
|
|
||||||
self.scope.allowIn = true
|
|
||||||
defer func() {
|
|
||||||
self.scope.allowIn = allowIn
|
|
||||||
}()
|
|
||||||
|
|
||||||
var left ast.Expression
|
|
||||||
if self.token == token.NEW {
|
|
||||||
left = self.parseNewExpression()
|
|
||||||
} else {
|
|
||||||
left = self.parsePrimaryExpression()
|
|
||||||
}
|
|
||||||
|
|
||||||
for {
|
|
||||||
if self.token == token.PERIOD {
|
|
||||||
left = self.parseDotMember(left)
|
|
||||||
} else if self.token == token.LEFT_BRACKET {
|
|
||||||
left = self.parseBracketMember(left)
|
|
||||||
} else if self.token == token.LEFT_PARENTHESIS {
|
|
||||||
left = self.parseCallExpression(left)
|
|
||||||
} else {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return left
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_parser) parsePostfixExpression() ast.Expression {
|
|
||||||
operand := self.parseLeftHandSideExpressionAllowCall()
|
|
||||||
|
|
||||||
switch self.token {
|
|
||||||
case token.INCREMENT, token.DECREMENT:
|
|
||||||
// Make sure there is no line terminator here
|
|
||||||
if self.implicitSemicolon {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
tkn := self.token
|
|
||||||
idx := self.idx
|
|
||||||
self.next()
|
|
||||||
switch operand.(type) {
|
|
||||||
case *ast.Identifier, *ast.DotExpression, *ast.BracketExpression:
|
|
||||||
default:
|
|
||||||
self.error(idx, "Invalid left-hand side in assignment")
|
|
||||||
self.nextStatement()
|
|
||||||
return &ast.BadExpression{From: idx, To: self.idx}
|
|
||||||
}
|
|
||||||
return &ast.UnaryExpression{
|
|
||||||
Operator: tkn,
|
|
||||||
Idx: idx,
|
|
||||||
Operand: operand,
|
|
||||||
Postfix: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return operand
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_parser) parseUnaryExpression() ast.Expression {
|
|
||||||
|
|
||||||
switch self.token {
|
|
||||||
case token.PLUS, token.MINUS, token.NOT, token.BITWISE_NOT:
|
|
||||||
fallthrough
|
|
||||||
case token.DELETE, token.VOID, token.TYPEOF:
|
|
||||||
tkn := self.token
|
|
||||||
idx := self.idx
|
|
||||||
self.next()
|
|
||||||
return &ast.UnaryExpression{
|
|
||||||
Operator: tkn,
|
|
||||||
Idx: idx,
|
|
||||||
Operand: self.parseUnaryExpression(),
|
|
||||||
}
|
|
||||||
case token.INCREMENT, token.DECREMENT:
|
|
||||||
tkn := self.token
|
|
||||||
idx := self.idx
|
|
||||||
self.next()
|
|
||||||
operand := self.parseUnaryExpression()
|
|
||||||
switch operand.(type) {
|
|
||||||
case *ast.Identifier, *ast.DotExpression, *ast.BracketExpression:
|
|
||||||
default:
|
|
||||||
self.error(idx, "Invalid left-hand side in assignment")
|
|
||||||
self.nextStatement()
|
|
||||||
return &ast.BadExpression{From: idx, To: self.idx}
|
|
||||||
}
|
|
||||||
return &ast.UnaryExpression{
|
|
||||||
Operator: tkn,
|
|
||||||
Idx: idx,
|
|
||||||
Operand: operand,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return self.parsePostfixExpression()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_parser) parseMultiplicativeExpression() ast.Expression {
|
|
||||||
next := self.parseUnaryExpression
|
|
||||||
left := next()
|
|
||||||
|
|
||||||
for self.token == token.MULTIPLY || self.token == token.SLASH ||
|
|
||||||
self.token == token.REMAINDER {
|
|
||||||
tkn := self.token
|
|
||||||
self.next()
|
|
||||||
left = &ast.BinaryExpression{
|
|
||||||
Operator: tkn,
|
|
||||||
Left: left,
|
|
||||||
Right: next(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return left
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_parser) parseAdditiveExpression() ast.Expression {
|
|
||||||
next := self.parseMultiplicativeExpression
|
|
||||||
left := next()
|
|
||||||
|
|
||||||
for self.token == token.PLUS || self.token == token.MINUS {
|
|
||||||
tkn := self.token
|
|
||||||
self.next()
|
|
||||||
left = &ast.BinaryExpression{
|
|
||||||
Operator: tkn,
|
|
||||||
Left: left,
|
|
||||||
Right: next(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return left
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_parser) parseShiftExpression() ast.Expression {
|
|
||||||
next := self.parseAdditiveExpression
|
|
||||||
left := next()
|
|
||||||
|
|
||||||
for self.token == token.SHIFT_LEFT || self.token == token.SHIFT_RIGHT ||
|
|
||||||
self.token == token.UNSIGNED_SHIFT_RIGHT {
|
|
||||||
tkn := self.token
|
|
||||||
self.next()
|
|
||||||
left = &ast.BinaryExpression{
|
|
||||||
Operator: tkn,
|
|
||||||
Left: left,
|
|
||||||
Right: next(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return left
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_parser) parseRelationalExpression() ast.Expression {
|
|
||||||
next := self.parseShiftExpression
|
|
||||||
left := next()
|
|
||||||
|
|
||||||
allowIn := self.scope.allowIn
|
|
||||||
self.scope.allowIn = true
|
|
||||||
defer func() {
|
|
||||||
self.scope.allowIn = allowIn
|
|
||||||
}()
|
|
||||||
|
|
||||||
switch self.token {
|
|
||||||
case token.LESS, token.LESS_OR_EQUAL, token.GREATER, token.GREATER_OR_EQUAL:
|
|
||||||
tkn := self.token
|
|
||||||
self.next()
|
|
||||||
return &ast.BinaryExpression{
|
|
||||||
Operator: tkn,
|
|
||||||
Left: left,
|
|
||||||
Right: self.parseRelationalExpression(),
|
|
||||||
Comparison: true,
|
|
||||||
}
|
|
||||||
case token.INSTANCEOF:
|
|
||||||
tkn := self.token
|
|
||||||
self.next()
|
|
||||||
return &ast.BinaryExpression{
|
|
||||||
Operator: tkn,
|
|
||||||
Left: left,
|
|
||||||
Right: self.parseRelationalExpression(),
|
|
||||||
}
|
|
||||||
case token.IN:
|
|
||||||
if !allowIn {
|
|
||||||
return left
|
|
||||||
}
|
|
||||||
tkn := self.token
|
|
||||||
self.next()
|
|
||||||
return &ast.BinaryExpression{
|
|
||||||
Operator: tkn,
|
|
||||||
Left: left,
|
|
||||||
Right: self.parseRelationalExpression(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return left
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_parser) parseEqualityExpression() ast.Expression {
|
|
||||||
next := self.parseRelationalExpression
|
|
||||||
left := next()
|
|
||||||
|
|
||||||
for self.token == token.EQUAL || self.token == token.NOT_EQUAL ||
|
|
||||||
self.token == token.STRICT_EQUAL || self.token == token.STRICT_NOT_EQUAL {
|
|
||||||
tkn := self.token
|
|
||||||
self.next()
|
|
||||||
left = &ast.BinaryExpression{
|
|
||||||
Operator: tkn,
|
|
||||||
Left: left,
|
|
||||||
Right: next(),
|
|
||||||
Comparison: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return left
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_parser) parseBitwiseAndExpression() ast.Expression {
|
|
||||||
next := self.parseEqualityExpression
|
|
||||||
left := next()
|
|
||||||
|
|
||||||
for self.token == token.AND {
|
|
||||||
tkn := self.token
|
|
||||||
self.next()
|
|
||||||
left = &ast.BinaryExpression{
|
|
||||||
Operator: tkn,
|
|
||||||
Left: left,
|
|
||||||
Right: next(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return left
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_parser) parseBitwiseExclusiveOrExpression() ast.Expression {
|
|
||||||
next := self.parseBitwiseAndExpression
|
|
||||||
left := next()
|
|
||||||
|
|
||||||
for self.token == token.EXCLUSIVE_OR {
|
|
||||||
tkn := self.token
|
|
||||||
self.next()
|
|
||||||
left = &ast.BinaryExpression{
|
|
||||||
Operator: tkn,
|
|
||||||
Left: left,
|
|
||||||
Right: next(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return left
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_parser) parseBitwiseOrExpression() ast.Expression {
|
|
||||||
next := self.parseBitwiseExclusiveOrExpression
|
|
||||||
left := next()
|
|
||||||
|
|
||||||
for self.token == token.OR {
|
|
||||||
tkn := self.token
|
|
||||||
self.next()
|
|
||||||
left = &ast.BinaryExpression{
|
|
||||||
Operator: tkn,
|
|
||||||
Left: left,
|
|
||||||
Right: next(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return left
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_parser) parseLogicalAndExpression() ast.Expression {
|
|
||||||
next := self.parseBitwiseOrExpression
|
|
||||||
left := next()
|
|
||||||
|
|
||||||
for self.token == token.LOGICAL_AND {
|
|
||||||
tkn := self.token
|
|
||||||
self.next()
|
|
||||||
left = &ast.BinaryExpression{
|
|
||||||
Operator: tkn,
|
|
||||||
Left: left,
|
|
||||||
Right: next(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return left
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_parser) parseLogicalOrExpression() ast.Expression {
|
|
||||||
next := self.parseLogicalAndExpression
|
|
||||||
left := next()
|
|
||||||
|
|
||||||
for self.token == token.LOGICAL_OR {
|
|
||||||
tkn := self.token
|
|
||||||
self.next()
|
|
||||||
left = &ast.BinaryExpression{
|
|
||||||
Operator: tkn,
|
|
||||||
Left: left,
|
|
||||||
Right: next(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return left
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_parser) parseConditionlExpression() ast.Expression {
|
|
||||||
left := self.parseLogicalOrExpression()
|
|
||||||
|
|
||||||
if self.token == token.QUESTION_MARK {
|
|
||||||
self.next()
|
|
||||||
consequent := self.parseAssignmentExpression()
|
|
||||||
self.expect(token.COLON)
|
|
||||||
return &ast.ConditionalExpression{
|
|
||||||
Test: left,
|
|
||||||
Consequent: consequent,
|
|
||||||
Alternate: self.parseAssignmentExpression(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return left
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_parser) parseAssignmentExpression() ast.Expression {
|
|
||||||
left := self.parseConditionlExpression()
|
|
||||||
var operator token.Token
|
|
||||||
switch self.token {
|
|
||||||
case token.ASSIGN:
|
|
||||||
operator = self.token
|
|
||||||
case token.ADD_ASSIGN:
|
|
||||||
operator = token.PLUS
|
|
||||||
case token.SUBTRACT_ASSIGN:
|
|
||||||
operator = token.MINUS
|
|
||||||
case token.MULTIPLY_ASSIGN:
|
|
||||||
operator = token.MULTIPLY
|
|
||||||
case token.QUOTIENT_ASSIGN:
|
|
||||||
operator = token.SLASH
|
|
||||||
case token.REMAINDER_ASSIGN:
|
|
||||||
operator = token.REMAINDER
|
|
||||||
case token.AND_ASSIGN:
|
|
||||||
operator = token.AND
|
|
||||||
case token.AND_NOT_ASSIGN:
|
|
||||||
operator = token.AND_NOT
|
|
||||||
case token.OR_ASSIGN:
|
|
||||||
operator = token.OR
|
|
||||||
case token.EXCLUSIVE_OR_ASSIGN:
|
|
||||||
operator = token.EXCLUSIVE_OR
|
|
||||||
case token.SHIFT_LEFT_ASSIGN:
|
|
||||||
operator = token.SHIFT_LEFT
|
|
||||||
case token.SHIFT_RIGHT_ASSIGN:
|
|
||||||
operator = token.SHIFT_RIGHT
|
|
||||||
case token.UNSIGNED_SHIFT_RIGHT_ASSIGN:
|
|
||||||
operator = token.UNSIGNED_SHIFT_RIGHT
|
|
||||||
}
|
|
||||||
|
|
||||||
if operator != 0 {
|
|
||||||
idx := self.idx
|
|
||||||
self.next()
|
|
||||||
switch left.(type) {
|
|
||||||
case *ast.Identifier, *ast.DotExpression, *ast.BracketExpression:
|
|
||||||
default:
|
|
||||||
self.error(left.Idx0(), "Invalid left-hand side in assignment")
|
|
||||||
self.nextStatement()
|
|
||||||
return &ast.BadExpression{From: idx, To: self.idx}
|
|
||||||
}
|
|
||||||
return &ast.AssignExpression{
|
|
||||||
Left: left,
|
|
||||||
Operator: operator,
|
|
||||||
Right: self.parseAssignmentExpression(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return left
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_parser) parseExpression() ast.Expression {
|
|
||||||
next := self.parseAssignmentExpression
|
|
||||||
left := next()
|
|
||||||
|
|
||||||
if self.token == token.COMMA {
|
|
||||||
sequence := []ast.Expression{left}
|
|
||||||
for {
|
|
||||||
if self.token != token.COMMA {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
self.next()
|
|
||||||
sequence = append(sequence, next())
|
|
||||||
}
|
|
||||||
return &ast.SequenceExpression{
|
|
||||||
Sequence: sequence,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return left
|
|
||||||
}
|
|
|
@ -1,819 +0,0 @@
|
||||||
package parser
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"regexp"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"unicode"
|
|
||||||
"unicode/utf8"
|
|
||||||
|
|
||||||
"github.com/robertkrimen/otto/file"
|
|
||||||
"github.com/robertkrimen/otto/token"
|
|
||||||
)
|
|
||||||
|
|
||||||
type _chr struct {
|
|
||||||
value rune
|
|
||||||
width int
|
|
||||||
}
|
|
||||||
|
|
||||||
var matchIdentifier = regexp.MustCompile(`^[$_\p{L}][$_\p{L}\d}]*$`)
|
|
||||||
|
|
||||||
func isDecimalDigit(chr rune) bool {
|
|
||||||
return '0' <= chr && chr <= '9'
|
|
||||||
}
|
|
||||||
|
|
||||||
func digitValue(chr rune) int {
|
|
||||||
switch {
|
|
||||||
case '0' <= chr && chr <= '9':
|
|
||||||
return int(chr - '0')
|
|
||||||
case 'a' <= chr && chr <= 'f':
|
|
||||||
return int(chr - 'a' + 10)
|
|
||||||
case 'A' <= chr && chr <= 'F':
|
|
||||||
return int(chr - 'A' + 10)
|
|
||||||
}
|
|
||||||
return 16 // Larger than any legal digit value
|
|
||||||
}
|
|
||||||
|
|
||||||
func isDigit(chr rune, base int) bool {
|
|
||||||
return digitValue(chr) < base
|
|
||||||
}
|
|
||||||
|
|
||||||
func isIdentifierStart(chr rune) bool {
|
|
||||||
return chr == '$' || chr == '_' || chr == '\\' ||
|
|
||||||
'a' <= chr && chr <= 'z' || 'A' <= chr && chr <= 'Z' ||
|
|
||||||
chr >= utf8.RuneSelf && unicode.IsLetter(chr)
|
|
||||||
}
|
|
||||||
|
|
||||||
func isIdentifierPart(chr rune) bool {
|
|
||||||
return chr == '$' || chr == '_' || chr == '\\' ||
|
|
||||||
'a' <= chr && chr <= 'z' || 'A' <= chr && chr <= 'Z' ||
|
|
||||||
'0' <= chr && chr <= '9' ||
|
|
||||||
chr >= utf8.RuneSelf && (unicode.IsLetter(chr) || unicode.IsDigit(chr))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_parser) scanIdentifier() (string, error) {
|
|
||||||
offset := self.chrOffset
|
|
||||||
parse := false
|
|
||||||
for isIdentifierPart(self.chr) {
|
|
||||||
if self.chr == '\\' {
|
|
||||||
distance := self.chrOffset - offset
|
|
||||||
self.read()
|
|
||||||
if self.chr != 'u' {
|
|
||||||
return "", fmt.Errorf("Invalid identifier escape character: %c (%s)", self.chr, string(self.chr))
|
|
||||||
}
|
|
||||||
parse = true
|
|
||||||
var value rune
|
|
||||||
for j := 0; j < 4; j++ {
|
|
||||||
self.read()
|
|
||||||
decimal, ok := hex2decimal(byte(self.chr))
|
|
||||||
if !ok {
|
|
||||||
return "", fmt.Errorf("Invalid identifier escape character: %c (%s)", self.chr, string(self.chr))
|
|
||||||
}
|
|
||||||
value = value<<4 | decimal
|
|
||||||
}
|
|
||||||
if value == '\\' {
|
|
||||||
return "", fmt.Errorf("Invalid identifier escape value: %c (%s)", value, string(value))
|
|
||||||
} else if distance == 0 {
|
|
||||||
if !isIdentifierStart(value) {
|
|
||||||
return "", fmt.Errorf("Invalid identifier escape value: %c (%s)", value, string(value))
|
|
||||||
}
|
|
||||||
} else if distance > 0 {
|
|
||||||
if !isIdentifierPart(value) {
|
|
||||||
return "", fmt.Errorf("Invalid identifier escape value: %c (%s)", value, string(value))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.read()
|
|
||||||
}
|
|
||||||
literal := string(self.str[offset:self.chrOffset])
|
|
||||||
if parse {
|
|
||||||
return parseStringLiteral(literal)
|
|
||||||
}
|
|
||||||
return literal, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// 7.2
|
|
||||||
func isLineWhiteSpace(chr rune) bool {
|
|
||||||
switch chr {
|
|
||||||
case '\u0009', '\u000b', '\u000c', '\u0020', '\u00a0', '\ufeff':
|
|
||||||
return true
|
|
||||||
case '\u000a', '\u000d', '\u2028', '\u2029':
|
|
||||||
return false
|
|
||||||
case '\u0085':
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return unicode.IsSpace(chr)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 7.3
|
|
||||||
func isLineTerminator(chr rune) bool {
|
|
||||||
switch chr {
|
|
||||||
case '\u000a', '\u000d', '\u2028', '\u2029':
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_parser) scan() (tkn token.Token, literal string, idx file.Idx) {
|
|
||||||
|
|
||||||
self.implicitSemicolon = false
|
|
||||||
|
|
||||||
for {
|
|
||||||
self.skipWhiteSpace()
|
|
||||||
|
|
||||||
idx = self.idxOf(self.chrOffset)
|
|
||||||
insertSemicolon := false
|
|
||||||
|
|
||||||
switch chr := self.chr; {
|
|
||||||
case isIdentifierStart(chr):
|
|
||||||
var err error
|
|
||||||
literal, err = self.scanIdentifier()
|
|
||||||
if err != nil {
|
|
||||||
tkn = token.ILLEGAL
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if len(literal) > 1 {
|
|
||||||
// Keywords are longer than 1 character, avoid lookup otherwise
|
|
||||||
var strict bool
|
|
||||||
tkn, strict = token.IsKeyword(literal)
|
|
||||||
|
|
||||||
switch tkn {
|
|
||||||
|
|
||||||
case 0: // Not a keyword
|
|
||||||
if literal == "true" || literal == "false" {
|
|
||||||
self.insertSemicolon = true
|
|
||||||
tkn = token.BOOLEAN
|
|
||||||
return
|
|
||||||
} else if literal == "null" {
|
|
||||||
self.insertSemicolon = true
|
|
||||||
tkn = token.NULL
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
case token.KEYWORD:
|
|
||||||
tkn = token.KEYWORD
|
|
||||||
if strict {
|
|
||||||
// TODO If strict and in strict mode, then this is not a break
|
|
||||||
break
|
|
||||||
}
|
|
||||||
return
|
|
||||||
|
|
||||||
case
|
|
||||||
token.THIS,
|
|
||||||
token.BREAK,
|
|
||||||
token.THROW, // A newline after a throw is not allowed, but we need to detect it
|
|
||||||
token.RETURN,
|
|
||||||
token.CONTINUE,
|
|
||||||
token.DEBUGGER:
|
|
||||||
self.insertSemicolon = true
|
|
||||||
return
|
|
||||||
|
|
||||||
default:
|
|
||||||
return
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.insertSemicolon = true
|
|
||||||
tkn = token.IDENTIFIER
|
|
||||||
return
|
|
||||||
case '0' <= chr && chr <= '9':
|
|
||||||
self.insertSemicolon = true
|
|
||||||
tkn, literal = self.scanNumericLiteral(false)
|
|
||||||
return
|
|
||||||
default:
|
|
||||||
self.read()
|
|
||||||
switch chr {
|
|
||||||
case -1:
|
|
||||||
if self.insertSemicolon {
|
|
||||||
self.insertSemicolon = false
|
|
||||||
self.implicitSemicolon = true
|
|
||||||
}
|
|
||||||
tkn = token.EOF
|
|
||||||
case '\r', '\n', '\u2028', '\u2029':
|
|
||||||
self.insertSemicolon = false
|
|
||||||
self.implicitSemicolon = true
|
|
||||||
continue
|
|
||||||
case ':':
|
|
||||||
tkn = token.COLON
|
|
||||||
case '.':
|
|
||||||
if digitValue(self.chr) < 10 {
|
|
||||||
insertSemicolon = true
|
|
||||||
tkn, literal = self.scanNumericLiteral(true)
|
|
||||||
} else {
|
|
||||||
tkn = token.PERIOD
|
|
||||||
}
|
|
||||||
case ',':
|
|
||||||
tkn = token.COMMA
|
|
||||||
case ';':
|
|
||||||
tkn = token.SEMICOLON
|
|
||||||
case '(':
|
|
||||||
tkn = token.LEFT_PARENTHESIS
|
|
||||||
case ')':
|
|
||||||
tkn = token.RIGHT_PARENTHESIS
|
|
||||||
insertSemicolon = true
|
|
||||||
case '[':
|
|
||||||
tkn = token.LEFT_BRACKET
|
|
||||||
case ']':
|
|
||||||
tkn = token.RIGHT_BRACKET
|
|
||||||
insertSemicolon = true
|
|
||||||
case '{':
|
|
||||||
tkn = token.LEFT_BRACE
|
|
||||||
case '}':
|
|
||||||
tkn = token.RIGHT_BRACE
|
|
||||||
insertSemicolon = true
|
|
||||||
case '+':
|
|
||||||
tkn = self.switch3(token.PLUS, token.ADD_ASSIGN, '+', token.INCREMENT)
|
|
||||||
if tkn == token.INCREMENT {
|
|
||||||
insertSemicolon = true
|
|
||||||
}
|
|
||||||
case '-':
|
|
||||||
tkn = self.switch3(token.MINUS, token.SUBTRACT_ASSIGN, '-', token.DECREMENT)
|
|
||||||
if tkn == token.DECREMENT {
|
|
||||||
insertSemicolon = true
|
|
||||||
}
|
|
||||||
case '*':
|
|
||||||
tkn = self.switch2(token.MULTIPLY, token.MULTIPLY_ASSIGN)
|
|
||||||
case '/':
|
|
||||||
if self.chr == '/' {
|
|
||||||
self.skipSingleLineComment()
|
|
||||||
continue
|
|
||||||
} else if self.chr == '*' {
|
|
||||||
self.skipMultiLineComment()
|
|
||||||
continue
|
|
||||||
} else {
|
|
||||||
// Could be division, could be RegExp literal
|
|
||||||
tkn = self.switch2(token.SLASH, token.QUOTIENT_ASSIGN)
|
|
||||||
insertSemicolon = true
|
|
||||||
}
|
|
||||||
case '%':
|
|
||||||
tkn = self.switch2(token.REMAINDER, token.REMAINDER_ASSIGN)
|
|
||||||
case '^':
|
|
||||||
tkn = self.switch2(token.EXCLUSIVE_OR, token.EXCLUSIVE_OR_ASSIGN)
|
|
||||||
case '<':
|
|
||||||
tkn = self.switch4(token.LESS, token.LESS_OR_EQUAL, '<', token.SHIFT_LEFT, token.SHIFT_LEFT_ASSIGN)
|
|
||||||
case '>':
|
|
||||||
tkn = self.switch6(token.GREATER, token.GREATER_OR_EQUAL, '>', token.SHIFT_RIGHT, token.SHIFT_RIGHT_ASSIGN, '>', token.UNSIGNED_SHIFT_RIGHT, token.UNSIGNED_SHIFT_RIGHT_ASSIGN)
|
|
||||||
case '=':
|
|
||||||
tkn = self.switch2(token.ASSIGN, token.EQUAL)
|
|
||||||
if tkn == token.EQUAL && self.chr == '=' {
|
|
||||||
self.read()
|
|
||||||
tkn = token.STRICT_EQUAL
|
|
||||||
}
|
|
||||||
case '!':
|
|
||||||
tkn = self.switch2(token.NOT, token.NOT_EQUAL)
|
|
||||||
if tkn == token.NOT_EQUAL && self.chr == '=' {
|
|
||||||
self.read()
|
|
||||||
tkn = token.STRICT_NOT_EQUAL
|
|
||||||
}
|
|
||||||
case '&':
|
|
||||||
if self.chr == '^' {
|
|
||||||
self.read()
|
|
||||||
tkn = self.switch2(token.AND_NOT, token.AND_NOT_ASSIGN)
|
|
||||||
} else {
|
|
||||||
tkn = self.switch3(token.AND, token.AND_ASSIGN, '&', token.LOGICAL_AND)
|
|
||||||
}
|
|
||||||
case '|':
|
|
||||||
tkn = self.switch3(token.OR, token.OR_ASSIGN, '|', token.LOGICAL_OR)
|
|
||||||
case '~':
|
|
||||||
tkn = token.BITWISE_NOT
|
|
||||||
case '?':
|
|
||||||
tkn = token.QUESTION_MARK
|
|
||||||
case '"', '\'':
|
|
||||||
insertSemicolon = true
|
|
||||||
tkn = token.STRING
|
|
||||||
var err error
|
|
||||||
literal, err = self.scanString(self.chrOffset - 1)
|
|
||||||
if err != nil {
|
|
||||||
tkn = token.ILLEGAL
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
self.errorUnexpected(idx, chr)
|
|
||||||
tkn = token.ILLEGAL
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.insertSemicolon = insertSemicolon
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_parser) switch2(tkn0, tkn1 token.Token) token.Token {
|
|
||||||
if self.chr == '=' {
|
|
||||||
self.read()
|
|
||||||
return tkn1
|
|
||||||
}
|
|
||||||
return tkn0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_parser) switch3(tkn0, tkn1 token.Token, chr2 rune, tkn2 token.Token) token.Token {
|
|
||||||
if self.chr == '=' {
|
|
||||||
self.read()
|
|
||||||
return tkn1
|
|
||||||
}
|
|
||||||
if self.chr == chr2 {
|
|
||||||
self.read()
|
|
||||||
return tkn2
|
|
||||||
}
|
|
||||||
return tkn0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_parser) switch4(tkn0, tkn1 token.Token, chr2 rune, tkn2, tkn3 token.Token) token.Token {
|
|
||||||
if self.chr == '=' {
|
|
||||||
self.read()
|
|
||||||
return tkn1
|
|
||||||
}
|
|
||||||
if self.chr == chr2 {
|
|
||||||
self.read()
|
|
||||||
if self.chr == '=' {
|
|
||||||
self.read()
|
|
||||||
return tkn3
|
|
||||||
}
|
|
||||||
return tkn2
|
|
||||||
}
|
|
||||||
return tkn0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_parser) switch6(tkn0, tkn1 token.Token, chr2 rune, tkn2, tkn3 token.Token, chr3 rune, tkn4, tkn5 token.Token) token.Token {
|
|
||||||
if self.chr == '=' {
|
|
||||||
self.read()
|
|
||||||
return tkn1
|
|
||||||
}
|
|
||||||
if self.chr == chr2 {
|
|
||||||
self.read()
|
|
||||||
if self.chr == '=' {
|
|
||||||
self.read()
|
|
||||||
return tkn3
|
|
||||||
}
|
|
||||||
if self.chr == chr3 {
|
|
||||||
self.read()
|
|
||||||
if self.chr == '=' {
|
|
||||||
self.read()
|
|
||||||
return tkn5
|
|
||||||
}
|
|
||||||
return tkn4
|
|
||||||
}
|
|
||||||
return tkn2
|
|
||||||
}
|
|
||||||
return tkn0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_parser) chrAt(index int) _chr {
|
|
||||||
value, width := utf8.DecodeRuneInString(self.str[index:])
|
|
||||||
return _chr{
|
|
||||||
value: value,
|
|
||||||
width: width,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_parser) _peek() rune {
|
|
||||||
if self.offset+1 < self.length {
|
|
||||||
return rune(self.str[self.offset+1])
|
|
||||||
}
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_parser) read() {
|
|
||||||
if self.offset < self.length {
|
|
||||||
self.chrOffset = self.offset
|
|
||||||
chr, width := rune(self.str[self.offset]), 1
|
|
||||||
if chr >= utf8.RuneSelf { // !ASCII
|
|
||||||
chr, width = utf8.DecodeRuneInString(self.str[self.offset:])
|
|
||||||
if chr == utf8.RuneError && width == 1 {
|
|
||||||
self.error(self.chrOffset, "Invalid UTF-8 character")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.offset += width
|
|
||||||
self.chr = chr
|
|
||||||
} else {
|
|
||||||
self.chrOffset = self.length
|
|
||||||
self.chr = -1 // EOF
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is here since the functions are so similar
|
|
||||||
func (self *_RegExp_parser) read() {
|
|
||||||
if self.offset < self.length {
|
|
||||||
self.chrOffset = self.offset
|
|
||||||
chr, width := rune(self.str[self.offset]), 1
|
|
||||||
if chr >= utf8.RuneSelf { // !ASCII
|
|
||||||
chr, width = utf8.DecodeRuneInString(self.str[self.offset:])
|
|
||||||
if chr == utf8.RuneError && width == 1 {
|
|
||||||
self.error(self.chrOffset, "Invalid UTF-8 character")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.offset += width
|
|
||||||
self.chr = chr
|
|
||||||
} else {
|
|
||||||
self.chrOffset = self.length
|
|
||||||
self.chr = -1 // EOF
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_parser) skipSingleLineComment() {
|
|
||||||
for self.chr != -1 {
|
|
||||||
self.read()
|
|
||||||
if isLineTerminator(self.chr) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_parser) skipMultiLineComment() {
|
|
||||||
self.read()
|
|
||||||
for self.chr >= 0 {
|
|
||||||
chr := self.chr
|
|
||||||
self.read()
|
|
||||||
if chr == '*' && self.chr == '/' {
|
|
||||||
self.read()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.errorUnexpected(0, self.chr)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_parser) skipWhiteSpace() {
|
|
||||||
for {
|
|
||||||
switch self.chr {
|
|
||||||
case ' ', '\t', '\f', '\v', '\u00a0', '\ufeff':
|
|
||||||
self.read()
|
|
||||||
continue
|
|
||||||
case '\r':
|
|
||||||
if self._peek() == '\n' {
|
|
||||||
self.read()
|
|
||||||
}
|
|
||||||
fallthrough
|
|
||||||
case '\u2028', '\u2029', '\n':
|
|
||||||
if self.insertSemicolon {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
self.read()
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if self.chr >= utf8.RuneSelf {
|
|
||||||
if unicode.IsSpace(self.chr) {
|
|
||||||
self.read()
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_parser) skipLineWhiteSpace() {
|
|
||||||
for isLineWhiteSpace(self.chr) {
|
|
||||||
self.read()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_parser) scanMantissa(base int) {
|
|
||||||
for digitValue(self.chr) < base {
|
|
||||||
self.read()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_parser) scanEscape(quote rune) {
|
|
||||||
|
|
||||||
var length, base uint32
|
|
||||||
switch self.chr {
|
|
||||||
//case '0', '1', '2', '3', '4', '5', '6', '7':
|
|
||||||
// Octal:
|
|
||||||
// length, base, limit = 3, 8, 255
|
|
||||||
case 'a', 'b', 'f', 'n', 'r', 't', 'v', '\\', '"', '\'', '0':
|
|
||||||
self.read()
|
|
||||||
return
|
|
||||||
case '\r', '\n', '\u2028', '\u2029':
|
|
||||||
self.scanNewline()
|
|
||||||
return
|
|
||||||
case 'x':
|
|
||||||
self.read()
|
|
||||||
length, base = 2, 16
|
|
||||||
case 'u':
|
|
||||||
self.read()
|
|
||||||
length, base = 4, 16
|
|
||||||
default:
|
|
||||||
self.read() // Always make progress
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var value uint32
|
|
||||||
for ; length > 0 && self.chr != quote && self.chr >= 0; length-- {
|
|
||||||
digit := uint32(digitValue(self.chr))
|
|
||||||
if digit >= base {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
value = value*base + digit
|
|
||||||
self.read()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_parser) scanString(offset int) (string, error) {
|
|
||||||
// " ' /
|
|
||||||
quote := rune(self.str[offset])
|
|
||||||
|
|
||||||
for self.chr != quote {
|
|
||||||
chr := self.chr
|
|
||||||
if chr == '\n' || chr == '\r' || chr == '\u2028' || chr == '\u2029' || chr < 0 {
|
|
||||||
goto newline
|
|
||||||
}
|
|
||||||
self.read()
|
|
||||||
if chr == '\\' {
|
|
||||||
if quote == '/' {
|
|
||||||
if self.chr == '\n' || self.chr == '\r' || self.chr == '\u2028' || self.chr == '\u2029' || self.chr < 0 {
|
|
||||||
goto newline
|
|
||||||
}
|
|
||||||
self.read()
|
|
||||||
} else {
|
|
||||||
self.scanEscape(quote)
|
|
||||||
}
|
|
||||||
} else if chr == '[' && quote == '/' {
|
|
||||||
// Allow a slash (/) in a bracket character class ([...])
|
|
||||||
// TODO Fix this, this is hacky...
|
|
||||||
quote = -1
|
|
||||||
} else if chr == ']' && quote == -1 {
|
|
||||||
quote = '/'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// " ' /
|
|
||||||
self.read()
|
|
||||||
|
|
||||||
return string(self.str[offset:self.chrOffset]), nil
|
|
||||||
|
|
||||||
newline:
|
|
||||||
self.scanNewline()
|
|
||||||
err := "String not terminated"
|
|
||||||
if quote == '/' {
|
|
||||||
err = "Invalid regular expression: missing /"
|
|
||||||
self.error(self.idxOf(offset), err)
|
|
||||||
}
|
|
||||||
return "", errors.New(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_parser) scanNewline() {
|
|
||||||
if self.chr == '\r' {
|
|
||||||
self.read()
|
|
||||||
if self.chr != '\n' {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.read()
|
|
||||||
}
|
|
||||||
|
|
||||||
func hex2decimal(chr byte) (value rune, ok bool) {
|
|
||||||
{
|
|
||||||
chr := rune(chr)
|
|
||||||
switch {
|
|
||||||
case '0' <= chr && chr <= '9':
|
|
||||||
return chr - '0', true
|
|
||||||
case 'a' <= chr && chr <= 'f':
|
|
||||||
return chr - 'a' + 10, true
|
|
||||||
case 'A' <= chr && chr <= 'F':
|
|
||||||
return chr - 'A' + 10, true
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseNumberLiteral(literal string) (value interface{}, err error) {
|
|
||||||
// TODO Is Uint okay? What about -MAX_UINT
|
|
||||||
value, err = strconv.ParseInt(literal, 0, 64)
|
|
||||||
if err == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
parseIntErr := err // Save this first error, just in case
|
|
||||||
|
|
||||||
value, err = strconv.ParseFloat(literal, 64)
|
|
||||||
if err == nil {
|
|
||||||
return
|
|
||||||
} else if err.(*strconv.NumError).Err == strconv.ErrRange {
|
|
||||||
// Infinity, etc.
|
|
||||||
return value, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
err = parseIntErr
|
|
||||||
|
|
||||||
if err.(*strconv.NumError).Err == strconv.ErrRange {
|
|
||||||
if len(literal) > 2 && literal[0] == '0' && (literal[1] == 'X' || literal[1] == 'x') {
|
|
||||||
// Could just be a very large number (e.g. 0x8000000000000000)
|
|
||||||
var value float64
|
|
||||||
literal = literal[2:]
|
|
||||||
for _, chr := range literal {
|
|
||||||
digit := digitValue(chr)
|
|
||||||
if digit >= 16 {
|
|
||||||
goto error
|
|
||||||
}
|
|
||||||
value = value*16 + float64(digit)
|
|
||||||
}
|
|
||||||
return value, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
error:
|
|
||||||
return nil, errors.New("Illegal numeric literal")
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseStringLiteral(literal string) (string, error) {
|
|
||||||
// Best case scenario...
|
|
||||||
if literal == "" {
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Slightly less-best case scenario...
|
|
||||||
if !strings.ContainsRune(literal, '\\') {
|
|
||||||
return literal, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
str := literal
|
|
||||||
buffer := bytes.NewBuffer(make([]byte, 0, 3*len(literal)/2))
|
|
||||||
|
|
||||||
for len(str) > 0 {
|
|
||||||
switch chr := str[0]; {
|
|
||||||
// We do not explicitly handle the case of the quote
|
|
||||||
// value, which can be: " ' /
|
|
||||||
// This assumes we're already passed a partially well-formed literal
|
|
||||||
case chr >= utf8.RuneSelf:
|
|
||||||
chr, size := utf8.DecodeRuneInString(str)
|
|
||||||
buffer.WriteRune(chr)
|
|
||||||
str = str[size:]
|
|
||||||
continue
|
|
||||||
case chr != '\\':
|
|
||||||
buffer.WriteByte(chr)
|
|
||||||
str = str[1:]
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(str) <= 1 {
|
|
||||||
panic("len(str) <= 1")
|
|
||||||
}
|
|
||||||
chr := str[1]
|
|
||||||
var value rune
|
|
||||||
if chr >= utf8.RuneSelf {
|
|
||||||
str = str[1:]
|
|
||||||
var size int
|
|
||||||
value, size = utf8.DecodeRuneInString(str)
|
|
||||||
str = str[size:] // \ + <character>
|
|
||||||
} else {
|
|
||||||
str = str[2:] // \<character>
|
|
||||||
switch chr {
|
|
||||||
case 'b':
|
|
||||||
value = '\b'
|
|
||||||
case 'f':
|
|
||||||
value = '\f'
|
|
||||||
case 'n':
|
|
||||||
value = '\n'
|
|
||||||
case 'r':
|
|
||||||
value = '\r'
|
|
||||||
case 't':
|
|
||||||
value = '\t'
|
|
||||||
case 'v':
|
|
||||||
value = '\v'
|
|
||||||
case 'x', 'u':
|
|
||||||
size := 0
|
|
||||||
switch chr {
|
|
||||||
case 'x':
|
|
||||||
size = 2
|
|
||||||
case 'u':
|
|
||||||
size = 4
|
|
||||||
}
|
|
||||||
if len(str) < size {
|
|
||||||
return "", fmt.Errorf("invalid escape: \\%s: len(%q) != %d", string(chr), str, size)
|
|
||||||
}
|
|
||||||
for j := 0; j < size; j++ {
|
|
||||||
decimal, ok := hex2decimal(str[j])
|
|
||||||
if !ok {
|
|
||||||
return "", fmt.Errorf("invalid escape: \\%s: %q", string(chr), str[:size])
|
|
||||||
}
|
|
||||||
value = value<<4 | decimal
|
|
||||||
}
|
|
||||||
str = str[size:]
|
|
||||||
if chr == 'x' {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if value > utf8.MaxRune {
|
|
||||||
panic("value > utf8.MaxRune")
|
|
||||||
}
|
|
||||||
case '0':
|
|
||||||
if len(str) == 0 || '0' > str[0] || str[0] > '7' {
|
|
||||||
value = 0
|
|
||||||
break
|
|
||||||
}
|
|
||||||
fallthrough
|
|
||||||
case '1', '2', '3', '4', '5', '6', '7':
|
|
||||||
// TODO strict
|
|
||||||
value = rune(chr) - '0'
|
|
||||||
j := 0
|
|
||||||
for ; j < 2; j++ {
|
|
||||||
if len(str) < j+1 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
chr := str[j]
|
|
||||||
if '0' > chr || chr > '7' {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
decimal := rune(str[j]) - '0'
|
|
||||||
value = (value << 3) | decimal
|
|
||||||
}
|
|
||||||
str = str[j:]
|
|
||||||
case '\\':
|
|
||||||
value = '\\'
|
|
||||||
case '\'', '"':
|
|
||||||
value = rune(chr)
|
|
||||||
case '\r':
|
|
||||||
if len(str) > 0 {
|
|
||||||
if str[0] == '\n' {
|
|
||||||
str = str[1:]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fallthrough
|
|
||||||
case '\n':
|
|
||||||
continue
|
|
||||||
default:
|
|
||||||
value = rune(chr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
buffer.WriteRune(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
return buffer.String(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_parser) scanNumericLiteral(decimalPoint bool) (token.Token, string) {
|
|
||||||
|
|
||||||
offset := self.chrOffset
|
|
||||||
tkn := token.NUMBER
|
|
||||||
|
|
||||||
if decimalPoint {
|
|
||||||
offset--
|
|
||||||
self.scanMantissa(10)
|
|
||||||
goto exponent
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.chr == '0' {
|
|
||||||
offset := self.chrOffset
|
|
||||||
self.read()
|
|
||||||
if self.chr == 'x' || self.chr == 'X' {
|
|
||||||
// Hexadecimal
|
|
||||||
self.read()
|
|
||||||
if isDigit(self.chr, 16) {
|
|
||||||
self.read()
|
|
||||||
} else {
|
|
||||||
return token.ILLEGAL, self.str[offset:self.chrOffset]
|
|
||||||
}
|
|
||||||
self.scanMantissa(16)
|
|
||||||
|
|
||||||
if self.chrOffset-offset <= 2 {
|
|
||||||
// Only "0x" or "0X"
|
|
||||||
self.error(0, "Illegal hexadecimal number")
|
|
||||||
}
|
|
||||||
|
|
||||||
goto hexadecimal
|
|
||||||
} else if self.chr == '.' {
|
|
||||||
// Float
|
|
||||||
goto float
|
|
||||||
} else {
|
|
||||||
// Octal, Float
|
|
||||||
if self.chr == 'e' || self.chr == 'E' {
|
|
||||||
goto exponent
|
|
||||||
}
|
|
||||||
self.scanMantissa(8)
|
|
||||||
if self.chr == '8' || self.chr == '9' {
|
|
||||||
return token.ILLEGAL, self.str[offset:self.chrOffset]
|
|
||||||
}
|
|
||||||
goto octal
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.scanMantissa(10)
|
|
||||||
|
|
||||||
float:
|
|
||||||
if self.chr == '.' {
|
|
||||||
self.read()
|
|
||||||
self.scanMantissa(10)
|
|
||||||
}
|
|
||||||
|
|
||||||
exponent:
|
|
||||||
if self.chr == 'e' || self.chr == 'E' {
|
|
||||||
self.read()
|
|
||||||
if self.chr == '-' || self.chr == '+' {
|
|
||||||
self.read()
|
|
||||||
}
|
|
||||||
if isDecimalDigit(self.chr) {
|
|
||||||
self.read()
|
|
||||||
self.scanMantissa(10)
|
|
||||||
} else {
|
|
||||||
return token.ILLEGAL, self.str[offset:self.chrOffset]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
hexadecimal:
|
|
||||||
octal:
|
|
||||||
if isIdentifierStart(self.chr) || isDecimalDigit(self.chr) {
|
|
||||||
return token.ILLEGAL, self.str[offset:self.chrOffset]
|
|
||||||
}
|
|
||||||
|
|
||||||
return tkn, self.str[offset:self.chrOffset]
|
|
||||||
}
|
|
|
@ -1,380 +0,0 @@
|
||||||
package parser
|
|
||||||
|
|
||||||
import (
|
|
||||||
"../terst"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/robertkrimen/otto/file"
|
|
||||||
"github.com/robertkrimen/otto/token"
|
|
||||||
)
|
|
||||||
|
|
||||||
var tt = terst.Terst
|
|
||||||
var is = terst.Is
|
|
||||||
|
|
||||||
func TestLexer(t *testing.T) {
|
|
||||||
tt(t, func() {
|
|
||||||
setup := func(src string) *_parser {
|
|
||||||
parser := newParser("", src)
|
|
||||||
return parser
|
|
||||||
}
|
|
||||||
|
|
||||||
test := func(src string, test ...interface{}) {
|
|
||||||
parser := setup(src)
|
|
||||||
for len(test) > 0 {
|
|
||||||
tkn, literal, idx := parser.scan()
|
|
||||||
if len(test) > 0 {
|
|
||||||
is(tkn, test[0].(token.Token))
|
|
||||||
test = test[1:]
|
|
||||||
}
|
|
||||||
if len(test) > 0 {
|
|
||||||
is(literal, test[0].(string))
|
|
||||||
test = test[1:]
|
|
||||||
}
|
|
||||||
if len(test) > 0 {
|
|
||||||
// FIXME terst, Fix this so that cast to file.Idx is not necessary?
|
|
||||||
is(idx, file.Idx(test[0].(int)))
|
|
||||||
test = test[1:]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
test("",
|
|
||||||
token.EOF, "", 1,
|
|
||||||
)
|
|
||||||
|
|
||||||
test("1",
|
|
||||||
token.NUMBER, "1", 1,
|
|
||||||
token.EOF, "", 2,
|
|
||||||
)
|
|
||||||
|
|
||||||
test(".0",
|
|
||||||
token.NUMBER, ".0", 1,
|
|
||||||
token.EOF, "", 3,
|
|
||||||
)
|
|
||||||
|
|
||||||
test("abc",
|
|
||||||
token.IDENTIFIER, "abc", 1,
|
|
||||||
token.EOF, "", 4,
|
|
||||||
)
|
|
||||||
|
|
||||||
test("abc(1)",
|
|
||||||
token.IDENTIFIER, "abc", 1,
|
|
||||||
token.LEFT_PARENTHESIS, "", 4,
|
|
||||||
token.NUMBER, "1", 5,
|
|
||||||
token.RIGHT_PARENTHESIS, "", 6,
|
|
||||||
token.EOF, "", 7,
|
|
||||||
)
|
|
||||||
|
|
||||||
test(".",
|
|
||||||
token.PERIOD, "", 1,
|
|
||||||
token.EOF, "", 2,
|
|
||||||
)
|
|
||||||
|
|
||||||
test("===.",
|
|
||||||
token.STRICT_EQUAL, "", 1,
|
|
||||||
token.PERIOD, "", 4,
|
|
||||||
token.EOF, "", 5,
|
|
||||||
)
|
|
||||||
|
|
||||||
test(">>>=.0",
|
|
||||||
token.UNSIGNED_SHIFT_RIGHT_ASSIGN, "", 1,
|
|
||||||
token.NUMBER, ".0", 5,
|
|
||||||
token.EOF, "", 7,
|
|
||||||
)
|
|
||||||
|
|
||||||
test(">>>=0.0.",
|
|
||||||
token.UNSIGNED_SHIFT_RIGHT_ASSIGN, "", 1,
|
|
||||||
token.NUMBER, "0.0", 5,
|
|
||||||
token.PERIOD, "", 8,
|
|
||||||
token.EOF, "", 9,
|
|
||||||
)
|
|
||||||
|
|
||||||
test("\"abc\"",
|
|
||||||
token.STRING, "\"abc\"", 1,
|
|
||||||
token.EOF, "", 6,
|
|
||||||
)
|
|
||||||
|
|
||||||
test("abc = //",
|
|
||||||
token.IDENTIFIER, "abc", 1,
|
|
||||||
token.ASSIGN, "", 5,
|
|
||||||
token.EOF, "", 9,
|
|
||||||
)
|
|
||||||
|
|
||||||
test("abc = 1 / 2",
|
|
||||||
token.IDENTIFIER, "abc", 1,
|
|
||||||
token.ASSIGN, "", 5,
|
|
||||||
token.NUMBER, "1", 7,
|
|
||||||
token.SLASH, "", 9,
|
|
||||||
token.NUMBER, "2", 11,
|
|
||||||
token.EOF, "", 12,
|
|
||||||
)
|
|
||||||
|
|
||||||
test("xyzzy = 'Nothing happens.'",
|
|
||||||
token.IDENTIFIER, "xyzzy", 1,
|
|
||||||
token.ASSIGN, "", 7,
|
|
||||||
token.STRING, "'Nothing happens.'", 9,
|
|
||||||
token.EOF, "", 27,
|
|
||||||
)
|
|
||||||
|
|
||||||
test("abc = !false",
|
|
||||||
token.IDENTIFIER, "abc", 1,
|
|
||||||
token.ASSIGN, "", 5,
|
|
||||||
token.NOT, "", 7,
|
|
||||||
token.BOOLEAN, "false", 8,
|
|
||||||
token.EOF, "", 13,
|
|
||||||
)
|
|
||||||
|
|
||||||
test("abc = !!true",
|
|
||||||
token.IDENTIFIER, "abc", 1,
|
|
||||||
token.ASSIGN, "", 5,
|
|
||||||
token.NOT, "", 7,
|
|
||||||
token.NOT, "", 8,
|
|
||||||
token.BOOLEAN, "true", 9,
|
|
||||||
token.EOF, "", 13,
|
|
||||||
)
|
|
||||||
|
|
||||||
test("abc *= 1",
|
|
||||||
token.IDENTIFIER, "abc", 1,
|
|
||||||
token.MULTIPLY_ASSIGN, "", 5,
|
|
||||||
token.NUMBER, "1", 8,
|
|
||||||
token.EOF, "", 9,
|
|
||||||
)
|
|
||||||
|
|
||||||
test("if 1 else",
|
|
||||||
token.IF, "if", 1,
|
|
||||||
token.NUMBER, "1", 4,
|
|
||||||
token.ELSE, "else", 6,
|
|
||||||
token.EOF, "", 10,
|
|
||||||
)
|
|
||||||
|
|
||||||
test("null",
|
|
||||||
token.NULL, "null", 1,
|
|
||||||
token.EOF, "", 5,
|
|
||||||
)
|
|
||||||
|
|
||||||
test(`"\u007a\x79\u000a\x78"`,
|
|
||||||
token.STRING, "\"\\u007a\\x79\\u000a\\x78\"", 1,
|
|
||||||
token.EOF, "", 23,
|
|
||||||
)
|
|
||||||
|
|
||||||
test(`"[First line \
|
|
||||||
Second line \
|
|
||||||
Third line\
|
|
||||||
. ]"
|
|
||||||
`,
|
|
||||||
token.STRING, "\"[First line \\\nSecond line \\\n Third line\\\n. ]\"", 1,
|
|
||||||
token.EOF, "", 53,
|
|
||||||
)
|
|
||||||
|
|
||||||
test("/",
|
|
||||||
token.SLASH, "", 1,
|
|
||||||
token.EOF, "", 2,
|
|
||||||
)
|
|
||||||
|
|
||||||
test("var abc = \"abc\uFFFFabc\"",
|
|
||||||
token.VAR, "var", 1,
|
|
||||||
token.IDENTIFIER, "abc", 5,
|
|
||||||
token.ASSIGN, "", 9,
|
|
||||||
token.STRING, "\"abc\uFFFFabc\"", 11,
|
|
||||||
token.EOF, "", 22,
|
|
||||||
)
|
|
||||||
|
|
||||||
test(`'\t' === '\r'`,
|
|
||||||
token.STRING, "'\\t'", 1,
|
|
||||||
token.STRICT_EQUAL, "", 6,
|
|
||||||
token.STRING, "'\\r'", 10,
|
|
||||||
token.EOF, "", 14,
|
|
||||||
)
|
|
||||||
|
|
||||||
test(`var \u0024 = 1`,
|
|
||||||
token.VAR, "var", 1,
|
|
||||||
token.IDENTIFIER, "$", 5,
|
|
||||||
token.ASSIGN, "", 12,
|
|
||||||
token.NUMBER, "1", 14,
|
|
||||||
token.EOF, "", 15,
|
|
||||||
)
|
|
||||||
|
|
||||||
test("10e10000",
|
|
||||||
token.NUMBER, "10e10000", 1,
|
|
||||||
token.EOF, "", 9,
|
|
||||||
)
|
|
||||||
|
|
||||||
test(`var if var class`,
|
|
||||||
token.VAR, "var", 1,
|
|
||||||
token.IF, "if", 5,
|
|
||||||
token.VAR, "var", 8,
|
|
||||||
token.KEYWORD, "class", 12,
|
|
||||||
token.EOF, "", 17,
|
|
||||||
)
|
|
||||||
|
|
||||||
test(`-0`,
|
|
||||||
token.MINUS, "", 1,
|
|
||||||
token.NUMBER, "0", 2,
|
|
||||||
token.EOF, "", 3,
|
|
||||||
)
|
|
||||||
|
|
||||||
test(`.01`,
|
|
||||||
token.NUMBER, ".01", 1,
|
|
||||||
token.EOF, "", 4,
|
|
||||||
)
|
|
||||||
|
|
||||||
test(`.01e+2`,
|
|
||||||
token.NUMBER, ".01e+2", 1,
|
|
||||||
token.EOF, "", 7,
|
|
||||||
)
|
|
||||||
|
|
||||||
test(";",
|
|
||||||
token.SEMICOLON, "", 1,
|
|
||||||
token.EOF, "", 2,
|
|
||||||
)
|
|
||||||
|
|
||||||
test(";;",
|
|
||||||
token.SEMICOLON, "", 1,
|
|
||||||
token.SEMICOLON, "", 2,
|
|
||||||
token.EOF, "", 3,
|
|
||||||
)
|
|
||||||
|
|
||||||
test("//",
|
|
||||||
token.EOF, "", 3,
|
|
||||||
)
|
|
||||||
|
|
||||||
test(";;//",
|
|
||||||
token.SEMICOLON, "", 1,
|
|
||||||
token.SEMICOLON, "", 2,
|
|
||||||
token.EOF, "", 5,
|
|
||||||
)
|
|
||||||
|
|
||||||
test("1",
|
|
||||||
token.NUMBER, "1", 1,
|
|
||||||
)
|
|
||||||
|
|
||||||
test("12 123",
|
|
||||||
token.NUMBER, "12", 1,
|
|
||||||
token.NUMBER, "123", 4,
|
|
||||||
)
|
|
||||||
|
|
||||||
test("1.2 12.3",
|
|
||||||
token.NUMBER, "1.2", 1,
|
|
||||||
token.NUMBER, "12.3", 5,
|
|
||||||
)
|
|
||||||
|
|
||||||
test("/ /=",
|
|
||||||
token.SLASH, "", 1,
|
|
||||||
token.QUOTIENT_ASSIGN, "", 3,
|
|
||||||
)
|
|
||||||
|
|
||||||
test(`"abc"`,
|
|
||||||
token.STRING, `"abc"`, 1,
|
|
||||||
)
|
|
||||||
|
|
||||||
test(`'abc'`,
|
|
||||||
token.STRING, `'abc'`, 1,
|
|
||||||
)
|
|
||||||
|
|
||||||
test("++",
|
|
||||||
token.INCREMENT, "", 1,
|
|
||||||
)
|
|
||||||
|
|
||||||
test(">",
|
|
||||||
token.GREATER, "", 1,
|
|
||||||
)
|
|
||||||
|
|
||||||
test(">=",
|
|
||||||
token.GREATER_OR_EQUAL, "", 1,
|
|
||||||
)
|
|
||||||
|
|
||||||
test(">>",
|
|
||||||
token.SHIFT_RIGHT, "", 1,
|
|
||||||
)
|
|
||||||
|
|
||||||
test(">>=",
|
|
||||||
token.SHIFT_RIGHT_ASSIGN, "", 1,
|
|
||||||
)
|
|
||||||
|
|
||||||
test(">>>",
|
|
||||||
token.UNSIGNED_SHIFT_RIGHT, "", 1,
|
|
||||||
)
|
|
||||||
|
|
||||||
test(">>>=",
|
|
||||||
token.UNSIGNED_SHIFT_RIGHT_ASSIGN, "", 1,
|
|
||||||
)
|
|
||||||
|
|
||||||
test("1 \"abc\"",
|
|
||||||
token.NUMBER, "1", 1,
|
|
||||||
token.STRING, "\"abc\"", 3,
|
|
||||||
)
|
|
||||||
|
|
||||||
test(",",
|
|
||||||
token.COMMA, "", 1,
|
|
||||||
)
|
|
||||||
|
|
||||||
test("1, \"abc\"",
|
|
||||||
token.NUMBER, "1", 1,
|
|
||||||
token.COMMA, "", 2,
|
|
||||||
token.STRING, "\"abc\"", 4,
|
|
||||||
)
|
|
||||||
|
|
||||||
test("new abc(1, 3.14159);",
|
|
||||||
token.NEW, "new", 1,
|
|
||||||
token.IDENTIFIER, "abc", 5,
|
|
||||||
token.LEFT_PARENTHESIS, "", 8,
|
|
||||||
token.NUMBER, "1", 9,
|
|
||||||
token.COMMA, "", 10,
|
|
||||||
token.NUMBER, "3.14159", 12,
|
|
||||||
token.RIGHT_PARENTHESIS, "", 19,
|
|
||||||
token.SEMICOLON, "", 20,
|
|
||||||
)
|
|
||||||
|
|
||||||
test("1 == \"1\"",
|
|
||||||
token.NUMBER, "1", 1,
|
|
||||||
token.EQUAL, "", 3,
|
|
||||||
token.STRING, "\"1\"", 6,
|
|
||||||
)
|
|
||||||
|
|
||||||
test("1\n[]\n",
|
|
||||||
token.NUMBER, "1", 1,
|
|
||||||
token.LEFT_BRACKET, "", 3,
|
|
||||||
token.RIGHT_BRACKET, "", 4,
|
|
||||||
)
|
|
||||||
|
|
||||||
test("1\ufeff[]\ufeff",
|
|
||||||
token.NUMBER, "1", 1,
|
|
||||||
token.LEFT_BRACKET, "", 5,
|
|
||||||
token.RIGHT_BRACKET, "", 6,
|
|
||||||
)
|
|
||||||
|
|
||||||
// ILLEGAL
|
|
||||||
|
|
||||||
test(`3ea`,
|
|
||||||
token.ILLEGAL, "3e", 1,
|
|
||||||
token.IDENTIFIER, "a", 3,
|
|
||||||
token.EOF, "", 4,
|
|
||||||
)
|
|
||||||
|
|
||||||
test(`3in`,
|
|
||||||
token.ILLEGAL, "3", 1,
|
|
||||||
token.IN, "in", 2,
|
|
||||||
token.EOF, "", 4,
|
|
||||||
)
|
|
||||||
|
|
||||||
test("\"Hello\nWorld\"",
|
|
||||||
token.ILLEGAL, "", 1,
|
|
||||||
token.IDENTIFIER, "World", 8,
|
|
||||||
token.ILLEGAL, "", 13,
|
|
||||||
token.EOF, "", 14,
|
|
||||||
)
|
|
||||||
|
|
||||||
test("\u203f = 10",
|
|
||||||
token.ILLEGAL, "", 1,
|
|
||||||
token.ASSIGN, "", 5,
|
|
||||||
token.NUMBER, "10", 7,
|
|
||||||
token.EOF, "", 9,
|
|
||||||
)
|
|
||||||
|
|
||||||
test(`"\x0G"`,
|
|
||||||
token.STRING, "\"\\x0G\"", 1,
|
|
||||||
token.EOF, "", 7,
|
|
||||||
)
|
|
||||||
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -1,930 +0,0 @@
|
||||||
package parser
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"reflect"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/robertkrimen/otto/ast"
|
|
||||||
)
|
|
||||||
|
|
||||||
func marshal(name string, children ...interface{}) interface{} {
|
|
||||||
if len(children) == 1 {
|
|
||||||
if name == "" {
|
|
||||||
return testMarshalNode(children[0])
|
|
||||||
}
|
|
||||||
return map[string]interface{}{
|
|
||||||
name: children[0],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
map_ := map[string]interface{}{}
|
|
||||||
length := len(children) / 2
|
|
||||||
for i := 0; i < length; i++ {
|
|
||||||
name := children[i*2].(string)
|
|
||||||
value := children[i*2+1]
|
|
||||||
map_[name] = value
|
|
||||||
}
|
|
||||||
if name == "" {
|
|
||||||
return map_
|
|
||||||
}
|
|
||||||
return map[string]interface{}{
|
|
||||||
name: map_,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func testMarshalNode(node interface{}) interface{} {
|
|
||||||
switch node := node.(type) {
|
|
||||||
|
|
||||||
// Expression
|
|
||||||
|
|
||||||
case *ast.ArrayLiteral:
|
|
||||||
return marshal("Array", testMarshalNode(node.Value))
|
|
||||||
|
|
||||||
case *ast.AssignExpression:
|
|
||||||
return marshal("Assign",
|
|
||||||
"Left", testMarshalNode(node.Left),
|
|
||||||
"Right", testMarshalNode(node.Right),
|
|
||||||
)
|
|
||||||
|
|
||||||
case *ast.BinaryExpression:
|
|
||||||
return marshal("BinaryExpression",
|
|
||||||
"Operator", node.Operator.String(),
|
|
||||||
"Left", testMarshalNode(node.Left),
|
|
||||||
"Right", testMarshalNode(node.Right),
|
|
||||||
)
|
|
||||||
|
|
||||||
case *ast.BooleanLiteral:
|
|
||||||
return marshal("Literal", node.Value)
|
|
||||||
|
|
||||||
case *ast.CallExpression:
|
|
||||||
return marshal("Call",
|
|
||||||
"Callee", testMarshalNode(node.Callee),
|
|
||||||
"ArgumentList", testMarshalNode(node.ArgumentList),
|
|
||||||
)
|
|
||||||
|
|
||||||
case *ast.ConditionalExpression:
|
|
||||||
return marshal("Conditional",
|
|
||||||
"Test", testMarshalNode(node.Test),
|
|
||||||
"Consequent", testMarshalNode(node.Consequent),
|
|
||||||
"Alternate", testMarshalNode(node.Alternate),
|
|
||||||
)
|
|
||||||
|
|
||||||
case *ast.DotExpression:
|
|
||||||
return marshal("Dot",
|
|
||||||
"Left", testMarshalNode(node.Left),
|
|
||||||
"Member", node.Identifier.Name,
|
|
||||||
)
|
|
||||||
|
|
||||||
case *ast.NewExpression:
|
|
||||||
return marshal("New",
|
|
||||||
"Callee", testMarshalNode(node.Callee),
|
|
||||||
"ArgumentList", testMarshalNode(node.ArgumentList),
|
|
||||||
)
|
|
||||||
|
|
||||||
case *ast.NullLiteral:
|
|
||||||
return marshal("Literal", nil)
|
|
||||||
|
|
||||||
case *ast.NumberLiteral:
|
|
||||||
return marshal("Literal", node.Value)
|
|
||||||
|
|
||||||
case *ast.ObjectLiteral:
|
|
||||||
return marshal("Object", testMarshalNode(node.Value))
|
|
||||||
|
|
||||||
case *ast.RegExpLiteral:
|
|
||||||
return marshal("Literal", node.Literal)
|
|
||||||
|
|
||||||
case *ast.StringLiteral:
|
|
||||||
return marshal("Literal", node.Literal)
|
|
||||||
|
|
||||||
case *ast.VariableExpression:
|
|
||||||
return []interface{}{node.Name, testMarshalNode(node.Initializer)}
|
|
||||||
|
|
||||||
// Statement
|
|
||||||
|
|
||||||
case *ast.Program:
|
|
||||||
return testMarshalNode(node.Body)
|
|
||||||
|
|
||||||
case *ast.BlockStatement:
|
|
||||||
return marshal("BlockStatement", testMarshalNode(node.List))
|
|
||||||
|
|
||||||
case *ast.EmptyStatement:
|
|
||||||
return "EmptyStatement"
|
|
||||||
|
|
||||||
case *ast.ExpressionStatement:
|
|
||||||
return testMarshalNode(node.Expression)
|
|
||||||
|
|
||||||
case *ast.ForInStatement:
|
|
||||||
return marshal("ForIn",
|
|
||||||
"Into", marshal("", node.Into),
|
|
||||||
"Source", marshal("", node.Source),
|
|
||||||
"Body", marshal("", node.Body),
|
|
||||||
)
|
|
||||||
|
|
||||||
case *ast.FunctionLiteral:
|
|
||||||
return marshal("Function", testMarshalNode(node.Body))
|
|
||||||
|
|
||||||
case *ast.Identifier:
|
|
||||||
return marshal("Identifier", node.Name)
|
|
||||||
|
|
||||||
case *ast.IfStatement:
|
|
||||||
if_ := marshal("",
|
|
||||||
"Test", testMarshalNode(node.Test),
|
|
||||||
"Consequent", testMarshalNode(node.Consequent),
|
|
||||||
).(map[string]interface{})
|
|
||||||
if node.Alternate != nil {
|
|
||||||
if_["Alternate"] = testMarshalNode(node.Alternate)
|
|
||||||
}
|
|
||||||
return marshal("If", if_)
|
|
||||||
|
|
||||||
case *ast.LabelledStatement:
|
|
||||||
return marshal("Label",
|
|
||||||
"Name", node.Label.Name,
|
|
||||||
"Statement", testMarshalNode(node.Statement),
|
|
||||||
)
|
|
||||||
case ast.Property:
|
|
||||||
return marshal("",
|
|
||||||
"Key", node.Key,
|
|
||||||
"Value", testMarshalNode(node.Value),
|
|
||||||
)
|
|
||||||
|
|
||||||
case *ast.ReturnStatement:
|
|
||||||
return marshal("Return", testMarshalNode(node.Argument))
|
|
||||||
|
|
||||||
case *ast.SequenceExpression:
|
|
||||||
return marshal("Sequence", testMarshalNode(node.Sequence))
|
|
||||||
|
|
||||||
case *ast.ThrowStatement:
|
|
||||||
return marshal("Throw", testMarshalNode(node.Argument))
|
|
||||||
|
|
||||||
case *ast.VariableStatement:
|
|
||||||
return marshal("Var", testMarshalNode(node.List))
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
value := reflect.ValueOf(node)
|
|
||||||
if value.Kind() == reflect.Slice {
|
|
||||||
tmp0 := []interface{}{}
|
|
||||||
for index := 0; index < value.Len(); index++ {
|
|
||||||
tmp0 = append(tmp0, testMarshalNode(value.Index(index).Interface()))
|
|
||||||
}
|
|
||||||
return tmp0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if node != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, "testMarshalNode(%T)\n", node)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func testMarshal(node interface{}) string {
|
|
||||||
value, err := json.Marshal(testMarshalNode(node))
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return string(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParserAST(t *testing.T) {
|
|
||||||
tt(t, func() {
|
|
||||||
|
|
||||||
test := func(inputOutput string) {
|
|
||||||
match := matchBeforeAfterSeparator.FindStringIndex(inputOutput)
|
|
||||||
input := strings.TrimSpace(inputOutput[0:match[0]])
|
|
||||||
wantOutput := strings.TrimSpace(inputOutput[match[1]:])
|
|
||||||
_, program, err := testParse(input)
|
|
||||||
is(err, nil)
|
|
||||||
haveOutput := testMarshal(program)
|
|
||||||
tmp0, tmp1 := bytes.Buffer{}, bytes.Buffer{}
|
|
||||||
json.Indent(&tmp0, []byte(haveOutput), "\t\t", " ")
|
|
||||||
json.Indent(&tmp1, []byte(wantOutput), "\t\t", " ")
|
|
||||||
is("\n\t\t"+tmp0.String(), "\n\t\t"+tmp1.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
test(`
|
|
||||||
---
|
|
||||||
[]
|
|
||||||
`)
|
|
||||||
|
|
||||||
test(`
|
|
||||||
;
|
|
||||||
---
|
|
||||||
[
|
|
||||||
"EmptyStatement"
|
|
||||||
]
|
|
||||||
`)
|
|
||||||
|
|
||||||
test(`
|
|
||||||
;;;
|
|
||||||
---
|
|
||||||
[
|
|
||||||
"EmptyStatement",
|
|
||||||
"EmptyStatement",
|
|
||||||
"EmptyStatement"
|
|
||||||
]
|
|
||||||
`)
|
|
||||||
|
|
||||||
test(`
|
|
||||||
1; true; abc; "abc"; null;
|
|
||||||
---
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"Literal": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Literal": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Identifier": "abc"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Literal": "\"abc\""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Literal": null
|
|
||||||
}
|
|
||||||
]
|
|
||||||
`)
|
|
||||||
|
|
||||||
test(`
|
|
||||||
{ 1; null; 3.14159; ; }
|
|
||||||
---
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"BlockStatement": [
|
|
||||||
{
|
|
||||||
"Literal": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Literal": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Literal": 3.14159
|
|
||||||
},
|
|
||||||
"EmptyStatement"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
`)
|
|
||||||
|
|
||||||
test(`
|
|
||||||
new abc();
|
|
||||||
---
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"New": {
|
|
||||||
"ArgumentList": [],
|
|
||||||
"Callee": {
|
|
||||||
"Identifier": "abc"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
`)
|
|
||||||
|
|
||||||
test(`
|
|
||||||
new abc(1, 3.14159)
|
|
||||||
---
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"New": {
|
|
||||||
"ArgumentList": [
|
|
||||||
{
|
|
||||||
"Literal": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Literal": 3.14159
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"Callee": {
|
|
||||||
"Identifier": "abc"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
`)
|
|
||||||
|
|
||||||
test(`
|
|
||||||
true ? false : true
|
|
||||||
---
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"Conditional": {
|
|
||||||
"Alternate": {
|
|
||||||
"Literal": true
|
|
||||||
},
|
|
||||||
"Consequent": {
|
|
||||||
"Literal": false
|
|
||||||
},
|
|
||||||
"Test": {
|
|
||||||
"Literal": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
`)
|
|
||||||
|
|
||||||
test(`
|
|
||||||
true || false
|
|
||||||
---
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"BinaryExpression": {
|
|
||||||
"Left": {
|
|
||||||
"Literal": true
|
|
||||||
},
|
|
||||||
"Operator": "||",
|
|
||||||
"Right": {
|
|
||||||
"Literal": false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
`)
|
|
||||||
|
|
||||||
test(`
|
|
||||||
0 + { abc: true }
|
|
||||||
---
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"BinaryExpression": {
|
|
||||||
"Left": {
|
|
||||||
"Literal": 0
|
|
||||||
},
|
|
||||||
"Operator": "+",
|
|
||||||
"Right": {
|
|
||||||
"Object": [
|
|
||||||
{
|
|
||||||
"Key": "abc",
|
|
||||||
"Value": {
|
|
||||||
"Literal": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
`)
|
|
||||||
|
|
||||||
test(`
|
|
||||||
1 == "1"
|
|
||||||
---
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"BinaryExpression": {
|
|
||||||
"Left": {
|
|
||||||
"Literal": 1
|
|
||||||
},
|
|
||||||
"Operator": "==",
|
|
||||||
"Right": {
|
|
||||||
"Literal": "\"1\""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
`)
|
|
||||||
|
|
||||||
test(`
|
|
||||||
abc(1)
|
|
||||||
---
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"Call": {
|
|
||||||
"ArgumentList": [
|
|
||||||
{
|
|
||||||
"Literal": 1
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"Callee": {
|
|
||||||
"Identifier": "abc"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
`)
|
|
||||||
|
|
||||||
test(`
|
|
||||||
Math.pow(3, 2)
|
|
||||||
---
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"Call": {
|
|
||||||
"ArgumentList": [
|
|
||||||
{
|
|
||||||
"Literal": 3
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Literal": 2
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"Callee": {
|
|
||||||
"Dot": {
|
|
||||||
"Left": {
|
|
||||||
"Identifier": "Math"
|
|
||||||
},
|
|
||||||
"Member": "pow"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
`)
|
|
||||||
|
|
||||||
test(`
|
|
||||||
1, 2, 3
|
|
||||||
---
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"Sequence": [
|
|
||||||
{
|
|
||||||
"Literal": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Literal": 2
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Literal": 3
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
`)
|
|
||||||
|
|
||||||
test(`
|
|
||||||
/ abc / gim;
|
|
||||||
---
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"Literal": "/ abc / gim"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
`)
|
|
||||||
|
|
||||||
test(`
|
|
||||||
if (0)
|
|
||||||
1;
|
|
||||||
---
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"If": {
|
|
||||||
"Consequent": {
|
|
||||||
"Literal": 1
|
|
||||||
},
|
|
||||||
"Test": {
|
|
||||||
"Literal": 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
`)
|
|
||||||
|
|
||||||
test(`
|
|
||||||
0+function(){
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
---
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"BinaryExpression": {
|
|
||||||
"Left": {
|
|
||||||
"Literal": 0
|
|
||||||
},
|
|
||||||
"Operator": "+",
|
|
||||||
"Right": {
|
|
||||||
"Function": {
|
|
||||||
"BlockStatement": [
|
|
||||||
{
|
|
||||||
"Return": null
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
`)
|
|
||||||
|
|
||||||
test(`
|
|
||||||
xyzzy // Ignore it
|
|
||||||
// Ignore this
|
|
||||||
// And this
|
|
||||||
/* And all..
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
... of this!
|
|
||||||
*/
|
|
||||||
"Nothing happens."
|
|
||||||
// And finally this
|
|
||||||
---
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"Identifier": "xyzzy"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Literal": "\"Nothing happens.\""
|
|
||||||
}
|
|
||||||
]
|
|
||||||
`)
|
|
||||||
|
|
||||||
test(`
|
|
||||||
((x & (x = 1)) !== 0)
|
|
||||||
---
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"BinaryExpression": {
|
|
||||||
"Left": {
|
|
||||||
"BinaryExpression": {
|
|
||||||
"Left": {
|
|
||||||
"Identifier": "x"
|
|
||||||
},
|
|
||||||
"Operator": "\u0026",
|
|
||||||
"Right": {
|
|
||||||
"Assign": {
|
|
||||||
"Left": {
|
|
||||||
"Identifier": "x"
|
|
||||||
},
|
|
||||||
"Right": {
|
|
||||||
"Literal": 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Operator": "!==",
|
|
||||||
"Right": {
|
|
||||||
"Literal": 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
`)
|
|
||||||
|
|
||||||
test(`
|
|
||||||
{ abc: 'def' }
|
|
||||||
---
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"BlockStatement": [
|
|
||||||
{
|
|
||||||
"Label": {
|
|
||||||
"Name": "abc",
|
|
||||||
"Statement": {
|
|
||||||
"Literal": "'def'"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
`)
|
|
||||||
|
|
||||||
test(`
|
|
||||||
// This is not an object, this is a string literal with a label!
|
|
||||||
({ abc: 'def' })
|
|
||||||
---
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"Object": [
|
|
||||||
{
|
|
||||||
"Key": "abc",
|
|
||||||
"Value": {
|
|
||||||
"Literal": "'def'"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
`)
|
|
||||||
|
|
||||||
test(`
|
|
||||||
[,]
|
|
||||||
---
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"Array": [
|
|
||||||
null
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
`)
|
|
||||||
|
|
||||||
test(`
|
|
||||||
[,,]
|
|
||||||
---
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"Array": [
|
|
||||||
null,
|
|
||||||
null
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
`)
|
|
||||||
|
|
||||||
test(`
|
|
||||||
({ get abc() {} })
|
|
||||||
---
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"Object": [
|
|
||||||
{
|
|
||||||
"Key": "abc",
|
|
||||||
"Value": {
|
|
||||||
"Function": {
|
|
||||||
"BlockStatement": []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
`)
|
|
||||||
|
|
||||||
test(`
|
|
||||||
/abc/.source
|
|
||||||
---
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"Dot": {
|
|
||||||
"Left": {
|
|
||||||
"Literal": "/abc/"
|
|
||||||
},
|
|
||||||
"Member": "source"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
`)
|
|
||||||
|
|
||||||
test(`
|
|
||||||
xyzzy
|
|
||||||
|
|
||||||
throw new TypeError("Nothing happens.")
|
|
||||||
---
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"Identifier": "xyzzy"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Throw": {
|
|
||||||
"New": {
|
|
||||||
"ArgumentList": [
|
|
||||||
{
|
|
||||||
"Literal": "\"Nothing happens.\""
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"Callee": {
|
|
||||||
"Identifier": "TypeError"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
`)
|
|
||||||
|
|
||||||
// When run, this will call a type error to be thrown
|
|
||||||
// This is essentially the same as:
|
|
||||||
//
|
|
||||||
// var abc = 1(function(){})()
|
|
||||||
//
|
|
||||||
test(`
|
|
||||||
var abc = 1
|
|
||||||
(function(){
|
|
||||||
})()
|
|
||||||
---
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"Var": [
|
|
||||||
[
|
|
||||||
"abc",
|
|
||||||
{
|
|
||||||
"Call": {
|
|
||||||
"ArgumentList": [],
|
|
||||||
"Callee": {
|
|
||||||
"Call": {
|
|
||||||
"ArgumentList": [
|
|
||||||
{
|
|
||||||
"Function": {
|
|
||||||
"BlockStatement": []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"Callee": {
|
|
||||||
"Literal": 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
`)
|
|
||||||
|
|
||||||
test(`
|
|
||||||
"use strict"
|
|
||||||
---
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"Literal": "\"use strict\""
|
|
||||||
}
|
|
||||||
]
|
|
||||||
`)
|
|
||||||
|
|
||||||
test(`
|
|
||||||
"use strict"
|
|
||||||
abc = 1 + 2 + 11
|
|
||||||
---
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"Literal": "\"use strict\""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Assign": {
|
|
||||||
"Left": {
|
|
||||||
"Identifier": "abc"
|
|
||||||
},
|
|
||||||
"Right": {
|
|
||||||
"BinaryExpression": {
|
|
||||||
"Left": {
|
|
||||||
"BinaryExpression": {
|
|
||||||
"Left": {
|
|
||||||
"Literal": 1
|
|
||||||
},
|
|
||||||
"Operator": "+",
|
|
||||||
"Right": {
|
|
||||||
"Literal": 2
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Operator": "+",
|
|
||||||
"Right": {
|
|
||||||
"Literal": 11
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
`)
|
|
||||||
|
|
||||||
test(`
|
|
||||||
abc = function() { 'use strict' }
|
|
||||||
---
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"Assign": {
|
|
||||||
"Left": {
|
|
||||||
"Identifier": "abc"
|
|
||||||
},
|
|
||||||
"Right": {
|
|
||||||
"Function": {
|
|
||||||
"BlockStatement": [
|
|
||||||
{
|
|
||||||
"Literal": "'use strict'"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
`)
|
|
||||||
|
|
||||||
test(`
|
|
||||||
for (var abc in def) {
|
|
||||||
}
|
|
||||||
---
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"ForIn": {
|
|
||||||
"Body": {
|
|
||||||
"BlockStatement": []
|
|
||||||
},
|
|
||||||
"Into": [
|
|
||||||
"abc",
|
|
||||||
null
|
|
||||||
],
|
|
||||||
"Source": {
|
|
||||||
"Identifier": "def"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
`)
|
|
||||||
|
|
||||||
test(`
|
|
||||||
abc = {
|
|
||||||
'"': "'",
|
|
||||||
"'": '"',
|
|
||||||
}
|
|
||||||
---
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"Assign": {
|
|
||||||
"Left": {
|
|
||||||
"Identifier": "abc"
|
|
||||||
},
|
|
||||||
"Right": {
|
|
||||||
"Object": [
|
|
||||||
{
|
|
||||||
"Key": "\"",
|
|
||||||
"Value": {
|
|
||||||
"Literal": "\"'\""
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Key": "'",
|
|
||||||
"Value": {
|
|
||||||
"Literal": "'\"'"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
`)
|
|
||||||
|
|
||||||
return
|
|
||||||
|
|
||||||
test(`
|
|
||||||
if (!abc && abc.jkl(def) && abc[0] === +abc[0] && abc.length < ghi) {
|
|
||||||
}
|
|
||||||
---
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"If": {
|
|
||||||
"Consequent": {
|
|
||||||
"BlockStatement": []
|
|
||||||
},
|
|
||||||
"Test": {
|
|
||||||
"BinaryExpression": {
|
|
||||||
"Left": {
|
|
||||||
"BinaryExpression": {
|
|
||||||
"Left": {
|
|
||||||
"BinaryExpression": {
|
|
||||||
"Left": null,
|
|
||||||
"Operator": "\u0026\u0026",
|
|
||||||
"Right": {
|
|
||||||
"Call": {
|
|
||||||
"ArgumentList": [
|
|
||||||
{
|
|
||||||
"Identifier": "def"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"Callee": {
|
|
||||||
"Dot": {
|
|
||||||
"Left": {
|
|
||||||
"Identifier": "abc"
|
|
||||||
},
|
|
||||||
"Member": "jkl"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Operator": "\u0026\u0026",
|
|
||||||
"Right": {
|
|
||||||
"BinaryExpression": {
|
|
||||||
"Left": null,
|
|
||||||
"Operator": "===",
|
|
||||||
"Right": null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Operator": "\u0026\u0026",
|
|
||||||
"Right": {
|
|
||||||
"BinaryExpression": {
|
|
||||||
"Left": {
|
|
||||||
"Dot": {
|
|
||||||
"Left": {
|
|
||||||
"Identifier": "abc"
|
|
||||||
},
|
|
||||||
"Member": "length"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Operator": "\u003c",
|
|
||||||
"Right": {
|
|
||||||
"Identifier": "ghi"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
`)
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -1,270 +0,0 @@
|
||||||
/*
|
|
||||||
Package parser implements a parser for JavaScript.
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/robertkrimen/otto/parser"
|
|
||||||
)
|
|
||||||
|
|
||||||
Parse and return an AST
|
|
||||||
|
|
||||||
filename := "" // A filename is optional
|
|
||||||
src := `
|
|
||||||
// Sample xyzzy example
|
|
||||||
(function(){
|
|
||||||
if (3.14159 > 0) {
|
|
||||||
console.log("Hello, World.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var xyzzy = NaN;
|
|
||||||
console.log("Nothing happens.");
|
|
||||||
return xyzzy;
|
|
||||||
})();
|
|
||||||
`
|
|
||||||
|
|
||||||
// Parse some JavaScript, yielding a *ast.Program and/or an ErrorList
|
|
||||||
program, err := parser.ParseFile(nil, filename, src, 0)
|
|
||||||
|
|
||||||
Warning
|
|
||||||
|
|
||||||
The parser and AST interfaces are still works-in-progress (particularly where
|
|
||||||
node types are concerned) and may change in the future.
|
|
||||||
|
|
||||||
*/
|
|
||||||
package parser
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"errors"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
|
|
||||||
"github.com/robertkrimen/otto/ast"
|
|
||||||
"github.com/robertkrimen/otto/file"
|
|
||||||
"github.com/robertkrimen/otto/token"
|
|
||||||
)
|
|
||||||
|
|
||||||
// A Mode value is a set of flags (or 0). They control optional parser functionality.
|
|
||||||
type Mode uint
|
|
||||||
|
|
||||||
const (
|
|
||||||
IgnoreRegExpErrors Mode = 1 << iota // Ignore RegExp compatibility errors (allow backtracking)
|
|
||||||
)
|
|
||||||
|
|
||||||
type _parser struct {
|
|
||||||
filename string
|
|
||||||
str string
|
|
||||||
length int
|
|
||||||
base int
|
|
||||||
|
|
||||||
chr rune // The current character
|
|
||||||
chrOffset int // The offset of current character
|
|
||||||
offset int // The offset after current character (may be greater than 1)
|
|
||||||
|
|
||||||
idx file.Idx // The index of token
|
|
||||||
token token.Token // The token
|
|
||||||
literal string // The literal of the token, if any
|
|
||||||
|
|
||||||
scope *_scope
|
|
||||||
insertSemicolon bool // If we see a newline, then insert an implicit semicolon
|
|
||||||
implicitSemicolon bool // An implicit semicolon exists
|
|
||||||
|
|
||||||
errors ErrorList
|
|
||||||
|
|
||||||
recover struct {
|
|
||||||
// Scratch when trying to seek to the next statement, etc.
|
|
||||||
idx file.Idx
|
|
||||||
count int
|
|
||||||
}
|
|
||||||
|
|
||||||
mode Mode
|
|
||||||
}
|
|
||||||
|
|
||||||
func _newParser(filename, src string, base int) *_parser {
|
|
||||||
return &_parser{
|
|
||||||
chr: ' ', // This is set so we can start scanning by skipping whitespace
|
|
||||||
str: src,
|
|
||||||
length: len(src),
|
|
||||||
base: base,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func newParser(filename, src string) *_parser {
|
|
||||||
return _newParser(filename, src, 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func ReadSource(filename string, src interface{}) ([]byte, error) {
|
|
||||||
if src != nil {
|
|
||||||
switch src := src.(type) {
|
|
||||||
case string:
|
|
||||||
return []byte(src), nil
|
|
||||||
case []byte:
|
|
||||||
return src, nil
|
|
||||||
case *bytes.Buffer:
|
|
||||||
if src != nil {
|
|
||||||
return src.Bytes(), nil
|
|
||||||
}
|
|
||||||
case io.Reader:
|
|
||||||
var bfr bytes.Buffer
|
|
||||||
if _, err := io.Copy(&bfr, src); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return bfr.Bytes(), nil
|
|
||||||
}
|
|
||||||
return nil, errors.New("invalid source")
|
|
||||||
}
|
|
||||||
return ioutil.ReadFile(filename)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseFile parses the source code of a single JavaScript/ECMAScript source file and returns
|
|
||||||
// the corresponding ast.Program node.
|
|
||||||
//
|
|
||||||
// If fileSet == nil, ParseFile parses source without a FileSet.
|
|
||||||
// If fileSet != nil, ParseFile first adds filename and src to fileSet.
|
|
||||||
//
|
|
||||||
// The filename argument is optional and is used for labelling errors, etc.
|
|
||||||
//
|
|
||||||
// src may be a string, a byte slice, a bytes.Buffer, or an io.Reader, but it MUST always be in UTF-8.
|
|
||||||
//
|
|
||||||
// // Parse some JavaScript, yielding a *ast.Program and/or an ErrorList
|
|
||||||
// program, err := parser.ParseFile(nil, "", `if (abc > 1) {}`, 0)
|
|
||||||
//
|
|
||||||
func ParseFile(fileSet *file.FileSet, filename string, src interface{}, mode Mode) (*ast.Program, error) {
|
|
||||||
str, err := ReadSource(filename, src)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
{
|
|
||||||
str := string(str)
|
|
||||||
|
|
||||||
base := 1
|
|
||||||
if fileSet != nil {
|
|
||||||
base = fileSet.AddFile(filename, str)
|
|
||||||
}
|
|
||||||
|
|
||||||
parser := _newParser(filename, str, base)
|
|
||||||
parser.mode = mode
|
|
||||||
return parser.parse()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseFunction parses a given parameter list and body as a function and returns the
|
|
||||||
// corresponding ast.FunctionLiteral node.
|
|
||||||
//
|
|
||||||
// The parameter list, if any, should be a comma-separated list of identifiers.
|
|
||||||
//
|
|
||||||
func ParseFunction(parameterList, body string) (*ast.FunctionLiteral, error) {
|
|
||||||
|
|
||||||
src := "(function(" + parameterList + ") {\n" + body + "\n})"
|
|
||||||
|
|
||||||
parser := _newParser("", src, 1)
|
|
||||||
program, err := parser.parse()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.FunctionLiteral), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_parser) slice(idx0, idx1 file.Idx) string {
|
|
||||||
from := int(idx0) - self.base
|
|
||||||
to := int(idx1) - self.base
|
|
||||||
if from >= 0 && to <= len(self.str) {
|
|
||||||
return self.str[from:to]
|
|
||||||
}
|
|
||||||
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_parser) parse() (*ast.Program, error) {
|
|
||||||
self.next()
|
|
||||||
program := self.parseProgram()
|
|
||||||
if false {
|
|
||||||
self.errors.Sort()
|
|
||||||
}
|
|
||||||
return program, self.errors.Err()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_parser) next() {
|
|
||||||
self.token, self.literal, self.idx = self.scan()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_parser) optionalSemicolon() {
|
|
||||||
if self.token == token.SEMICOLON {
|
|
||||||
self.next()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.implicitSemicolon {
|
|
||||||
self.implicitSemicolon = false
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.token != token.EOF && self.token != token.RIGHT_BRACE {
|
|
||||||
self.expect(token.SEMICOLON)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_parser) semicolon() {
|
|
||||||
if self.token != token.RIGHT_PARENTHESIS && self.token != token.RIGHT_BRACE {
|
|
||||||
if self.implicitSemicolon {
|
|
||||||
self.implicitSemicolon = false
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
self.expect(token.SEMICOLON)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_parser) idxOf(offset int) file.Idx {
|
|
||||||
return file.Idx(self.base + offset)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_parser) expect(value token.Token) file.Idx {
|
|
||||||
idx := self.idx
|
|
||||||
if self.token != value {
|
|
||||||
self.errorUnexpectedToken(self.token)
|
|
||||||
}
|
|
||||||
self.next()
|
|
||||||
return idx
|
|
||||||
}
|
|
||||||
|
|
||||||
func lineCount(str string) (int, int) {
|
|
||||||
line, last := 0, -1
|
|
||||||
pair := false
|
|
||||||
for index, chr := range str {
|
|
||||||
switch chr {
|
|
||||||
case '\r':
|
|
||||||
line += 1
|
|
||||||
last = index
|
|
||||||
pair = true
|
|
||||||
continue
|
|
||||||
case '\n':
|
|
||||||
if !pair {
|
|
||||||
line += 1
|
|
||||||
}
|
|
||||||
last = index
|
|
||||||
case '\u2028', '\u2029':
|
|
||||||
line += 1
|
|
||||||
last = index + 2
|
|
||||||
}
|
|
||||||
pair = false
|
|
||||||
}
|
|
||||||
return line, last
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_parser) position(idx file.Idx) file.Position {
|
|
||||||
position := file.Position{}
|
|
||||||
offset := int(idx) - self.base
|
|
||||||
str := self.str[:offset]
|
|
||||||
position.Filename = self.filename
|
|
||||||
line, last := lineCount(str)
|
|
||||||
position.Line = 1 + line
|
|
||||||
if last >= 0 {
|
|
||||||
position.Column = offset - last
|
|
||||||
} else {
|
|
||||||
position.Column = 1 + len(str)
|
|
||||||
}
|
|
||||||
|
|
||||||
return position
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,358 +0,0 @@
|
||||||
package parser
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"strconv"
|
|
||||||
)
|
|
||||||
|
|
||||||
type _RegExp_parser struct {
|
|
||||||
str string
|
|
||||||
length int
|
|
||||||
|
|
||||||
chr rune // The current character
|
|
||||||
chrOffset int // The offset of current character
|
|
||||||
offset int // The offset after current character (may be greater than 1)
|
|
||||||
|
|
||||||
errors []error
|
|
||||||
invalid bool // The input is an invalid JavaScript RegExp
|
|
||||||
|
|
||||||
goRegexp *bytes.Buffer
|
|
||||||
}
|
|
||||||
|
|
||||||
// TransformRegExp transforms a JavaScript pattern into a Go "regexp" pattern.
|
|
||||||
//
|
|
||||||
// re2 (Go) cannot do backtracking, so the presence of a lookahead (?=) (?!) or
|
|
||||||
// backreference (\1, \2, ...) will cause an error.
|
|
||||||
//
|
|
||||||
// re2 (Go) has a different definition for \s: [\t\n\f\r ].
|
|
||||||
// The JavaScript definition, on the other hand, also includes \v, Unicode "Separator, Space", etc.
|
|
||||||
//
|
|
||||||
// If the pattern is invalid (not valid even in JavaScript), then this function
|
|
||||||
// returns the empty string and an error.
|
|
||||||
//
|
|
||||||
// If the pattern is valid, but incompatible (contains a lookahead or backreference),
|
|
||||||
// then this function returns the transformation (a non-empty string) AND an error.
|
|
||||||
func TransformRegExp(pattern string) (string, error) {
|
|
||||||
|
|
||||||
if pattern == "" {
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO If without \, if without (?=, (?!, then another shortcut
|
|
||||||
|
|
||||||
parser := _RegExp_parser{
|
|
||||||
str: pattern,
|
|
||||||
length: len(pattern),
|
|
||||||
goRegexp: bytes.NewBuffer(make([]byte, 0, 3*len(pattern)/2)),
|
|
||||||
}
|
|
||||||
parser.read() // Pull in the first character
|
|
||||||
parser.scan()
|
|
||||||
var err error
|
|
||||||
if len(parser.errors) > 0 {
|
|
||||||
err = parser.errors[0]
|
|
||||||
}
|
|
||||||
if parser.invalid {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Might not be re2 compatible, but is still a valid JavaScript RegExp
|
|
||||||
return parser.goRegexp.String(), err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_RegExp_parser) scan() {
|
|
||||||
for self.chr != -1 {
|
|
||||||
switch self.chr {
|
|
||||||
case '\\':
|
|
||||||
self.read()
|
|
||||||
self.scanEscape(false)
|
|
||||||
case '(':
|
|
||||||
self.pass()
|
|
||||||
self.scanGroup()
|
|
||||||
case '[':
|
|
||||||
self.pass()
|
|
||||||
self.scanBracket()
|
|
||||||
case ')':
|
|
||||||
self.error(-1, "Unmatched ')'")
|
|
||||||
self.invalid = true
|
|
||||||
self.pass()
|
|
||||||
default:
|
|
||||||
self.pass()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// (...)
|
|
||||||
func (self *_RegExp_parser) scanGroup() {
|
|
||||||
str := self.str[self.chrOffset:]
|
|
||||||
if len(str) > 1 { // A possibility of (?= or (?!
|
|
||||||
if str[0] == '?' {
|
|
||||||
if str[1] == '=' || str[1] == '!' {
|
|
||||||
self.error(-1, "re2: Invalid (%s) <lookahead>", self.str[self.chrOffset:self.chrOffset+2])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for self.chr != -1 && self.chr != ')' {
|
|
||||||
switch self.chr {
|
|
||||||
case '\\':
|
|
||||||
self.read()
|
|
||||||
self.scanEscape(false)
|
|
||||||
case '(':
|
|
||||||
self.pass()
|
|
||||||
self.scanGroup()
|
|
||||||
case '[':
|
|
||||||
self.pass()
|
|
||||||
self.scanBracket()
|
|
||||||
default:
|
|
||||||
self.pass()
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if self.chr != ')' {
|
|
||||||
self.error(-1, "Unterminated group")
|
|
||||||
self.invalid = true
|
|
||||||
return
|
|
||||||
}
|
|
||||||
self.pass()
|
|
||||||
}
|
|
||||||
|
|
||||||
// [...]
|
|
||||||
func (self *_RegExp_parser) scanBracket() {
|
|
||||||
for self.chr != -1 {
|
|
||||||
if self.chr == ']' {
|
|
||||||
break
|
|
||||||
} else if self.chr == '\\' {
|
|
||||||
self.read()
|
|
||||||
self.scanEscape(true)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
self.pass()
|
|
||||||
}
|
|
||||||
if self.chr != ']' {
|
|
||||||
self.error(-1, "Unterminated character class")
|
|
||||||
self.invalid = true
|
|
||||||
return
|
|
||||||
}
|
|
||||||
self.pass()
|
|
||||||
}
|
|
||||||
|
|
||||||
// \...
|
|
||||||
func (self *_RegExp_parser) scanEscape(inClass bool) {
|
|
||||||
offset := self.chrOffset
|
|
||||||
|
|
||||||
var length, base uint32
|
|
||||||
switch self.chr {
|
|
||||||
|
|
||||||
case '0', '1', '2', '3', '4', '5', '6', '7':
|
|
||||||
var value int64
|
|
||||||
size := 0
|
|
||||||
for {
|
|
||||||
digit := int64(digitValue(self.chr))
|
|
||||||
if digit >= 8 {
|
|
||||||
// Not a valid digit
|
|
||||||
break
|
|
||||||
}
|
|
||||||
value = value*8 + digit
|
|
||||||
self.read()
|
|
||||||
size += 1
|
|
||||||
}
|
|
||||||
if size == 1 { // The number of characters read
|
|
||||||
_, err := self.goRegexp.Write([]byte{'\\', byte(value) + '0'})
|
|
||||||
if err != nil {
|
|
||||||
self.errors = append(self.errors, err)
|
|
||||||
}
|
|
||||||
if value != 0 {
|
|
||||||
// An invalid backreference
|
|
||||||
self.error(-1, "re2: Invalid \\%d <backreference>", value)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
tmp := []byte{'\\', 'x', '0', 0}
|
|
||||||
if value >= 16 {
|
|
||||||
tmp = tmp[0:2]
|
|
||||||
} else {
|
|
||||||
tmp = tmp[0:3]
|
|
||||||
}
|
|
||||||
tmp = strconv.AppendInt(tmp, value, 16)
|
|
||||||
_, err := self.goRegexp.Write(tmp)
|
|
||||||
if err != nil {
|
|
||||||
self.errors = append(self.errors, err)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
|
|
||||||
case '8', '9':
|
|
||||||
size := 0
|
|
||||||
for {
|
|
||||||
digit := digitValue(self.chr)
|
|
||||||
if digit >= 10 {
|
|
||||||
// Not a valid digit
|
|
||||||
break
|
|
||||||
}
|
|
||||||
self.read()
|
|
||||||
size += 1
|
|
||||||
}
|
|
||||||
err := self.goRegexp.WriteByte('\\')
|
|
||||||
if err != nil {
|
|
||||||
self.errors = append(self.errors, err)
|
|
||||||
}
|
|
||||||
_, err = self.goRegexp.WriteString(self.str[offset:self.chrOffset])
|
|
||||||
if err != nil {
|
|
||||||
self.errors = append(self.errors, err)
|
|
||||||
}
|
|
||||||
self.error(-1, "re2: Invalid \\%s <backreference>", self.str[offset:self.chrOffset])
|
|
||||||
return
|
|
||||||
|
|
||||||
case 'x':
|
|
||||||
self.read()
|
|
||||||
length, base = 2, 16
|
|
||||||
|
|
||||||
case 'u':
|
|
||||||
self.read()
|
|
||||||
length, base = 4, 16
|
|
||||||
|
|
||||||
case 'b':
|
|
||||||
if inClass {
|
|
||||||
_, err := self.goRegexp.Write([]byte{'\\', 'x', '0', '8'})
|
|
||||||
if err != nil {
|
|
||||||
self.errors = append(self.errors, err)
|
|
||||||
}
|
|
||||||
self.read()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
fallthrough
|
|
||||||
|
|
||||||
case 'B':
|
|
||||||
fallthrough
|
|
||||||
|
|
||||||
case 'd', 'D', 's', 'S', 'w', 'W':
|
|
||||||
// This is slightly broken, because ECMAScript
|
|
||||||
// includes \v in \s, \S, while re2 does not
|
|
||||||
fallthrough
|
|
||||||
|
|
||||||
case '\\':
|
|
||||||
fallthrough
|
|
||||||
|
|
||||||
case 'f', 'n', 'r', 't', 'v':
|
|
||||||
err := self.goRegexp.WriteByte('\\')
|
|
||||||
if err != nil {
|
|
||||||
self.errors = append(self.errors, err)
|
|
||||||
}
|
|
||||||
self.pass()
|
|
||||||
return
|
|
||||||
|
|
||||||
case 'c':
|
|
||||||
self.read()
|
|
||||||
var value int64
|
|
||||||
if 'a' <= self.chr && self.chr <= 'z' {
|
|
||||||
value = int64(self.chr) - 'a' + 1
|
|
||||||
} else if 'A' <= self.chr && self.chr <= 'Z' {
|
|
||||||
value = int64(self.chr) - 'A' + 1
|
|
||||||
} else {
|
|
||||||
err := self.goRegexp.WriteByte('c')
|
|
||||||
if err != nil {
|
|
||||||
self.errors = append(self.errors, err)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
tmp := []byte{'\\', 'x', '0', 0}
|
|
||||||
if value >= 16 {
|
|
||||||
tmp = tmp[0:2]
|
|
||||||
} else {
|
|
||||||
tmp = tmp[0:3]
|
|
||||||
}
|
|
||||||
tmp = strconv.AppendInt(tmp, value, 16)
|
|
||||||
_, err := self.goRegexp.Write(tmp)
|
|
||||||
if err != nil {
|
|
||||||
self.errors = append(self.errors, err)
|
|
||||||
}
|
|
||||||
self.read()
|
|
||||||
return
|
|
||||||
|
|
||||||
default:
|
|
||||||
// $ is an identifier character, so we have to have
|
|
||||||
// a special case for it here
|
|
||||||
if self.chr == '$' || !isIdentifierPart(self.chr) {
|
|
||||||
// A non-identifier character needs escaping
|
|
||||||
err := self.goRegexp.WriteByte('\\')
|
|
||||||
if err != nil {
|
|
||||||
self.errors = append(self.errors, err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Unescape the character for re2
|
|
||||||
}
|
|
||||||
self.pass()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise, we're a \u.... or \x...
|
|
||||||
valueOffset := self.chrOffset
|
|
||||||
|
|
||||||
var value uint32
|
|
||||||
{
|
|
||||||
length := length
|
|
||||||
for ; length > 0; length-- {
|
|
||||||
digit := uint32(digitValue(self.chr))
|
|
||||||
if digit >= base {
|
|
||||||
// Not a valid digit
|
|
||||||
goto skip
|
|
||||||
}
|
|
||||||
value = value*base + digit
|
|
||||||
self.read()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if length == 4 {
|
|
||||||
_, err := self.goRegexp.Write([]byte{
|
|
||||||
'\\',
|
|
||||||
'x',
|
|
||||||
'{',
|
|
||||||
self.str[valueOffset+0],
|
|
||||||
self.str[valueOffset+1],
|
|
||||||
self.str[valueOffset+2],
|
|
||||||
self.str[valueOffset+3],
|
|
||||||
'}',
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
self.errors = append(self.errors, err)
|
|
||||||
}
|
|
||||||
} else if length == 2 {
|
|
||||||
_, err := self.goRegexp.Write([]byte{
|
|
||||||
'\\',
|
|
||||||
'x',
|
|
||||||
self.str[valueOffset+0],
|
|
||||||
self.str[valueOffset+1],
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
self.errors = append(self.errors, err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Should never, ever get here...
|
|
||||||
self.error(-1, "re2: Illegal branch in scanEscape")
|
|
||||||
goto skip
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
|
|
||||||
skip:
|
|
||||||
_, err := self.goRegexp.WriteString(self.str[offset:self.chrOffset])
|
|
||||||
if err != nil {
|
|
||||||
self.errors = append(self.errors, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_RegExp_parser) pass() {
|
|
||||||
if self.chr != -1 {
|
|
||||||
_, err := self.goRegexp.WriteRune(self.chr)
|
|
||||||
if err != nil {
|
|
||||||
self.errors = append(self.errors, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.read()
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO Better error reporting, use the offset, etc.
|
|
||||||
func (self *_RegExp_parser) error(offset int, msg string, msgValues ...interface{}) error {
|
|
||||||
err := fmt.Errorf(msg, msgValues...)
|
|
||||||
self.errors = append(self.errors, err)
|
|
||||||
return err
|
|
||||||
}
|
|
|
@ -1,149 +0,0 @@
|
||||||
package parser
|
|
||||||
|
|
||||||
import (
|
|
||||||
"regexp"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestRegExp(t *testing.T) {
|
|
||||||
tt(t, func() {
|
|
||||||
{
|
|
||||||
// err
|
|
||||||
test := func(input string, expect interface{}) {
|
|
||||||
_, err := TransformRegExp(input)
|
|
||||||
is(err, expect)
|
|
||||||
}
|
|
||||||
|
|
||||||
test("[", "Unterminated character class")
|
|
||||||
|
|
||||||
test("(", "Unterminated group")
|
|
||||||
|
|
||||||
test("(?=)", "re2: Invalid (?=) <lookahead>")
|
|
||||||
|
|
||||||
test("(?=)", "re2: Invalid (?=) <lookahead>")
|
|
||||||
|
|
||||||
test("(?!)", "re2: Invalid (?!) <lookahead>")
|
|
||||||
|
|
||||||
// An error anyway
|
|
||||||
test("(?=", "re2: Invalid (?=) <lookahead>")
|
|
||||||
|
|
||||||
test("\\1", "re2: Invalid \\1 <backreference>")
|
|
||||||
|
|
||||||
test("\\90", "re2: Invalid \\90 <backreference>")
|
|
||||||
|
|
||||||
test("\\9123456789", "re2: Invalid \\9123456789 <backreference>")
|
|
||||||
|
|
||||||
test("\\(?=)", "Unmatched ')'")
|
|
||||||
|
|
||||||
test(")", "Unmatched ')'")
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
// err
|
|
||||||
test := func(input, expect string, expectErr interface{}) {
|
|
||||||
output, err := TransformRegExp(input)
|
|
||||||
is(output, expect)
|
|
||||||
is(err, expectErr)
|
|
||||||
}
|
|
||||||
|
|
||||||
test("(?!)", "(?!)", "re2: Invalid (?!) <lookahead>")
|
|
||||||
|
|
||||||
test(")", "", "Unmatched ')'")
|
|
||||||
|
|
||||||
test("(?!))", "", "re2: Invalid (?!) <lookahead>")
|
|
||||||
|
|
||||||
test("\\0", "\\0", nil)
|
|
||||||
|
|
||||||
test("\\1", "\\1", "re2: Invalid \\1 <backreference>")
|
|
||||||
|
|
||||||
test("\\9123456789", "\\9123456789", "re2: Invalid \\9123456789 <backreference>")
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
// err
|
|
||||||
test := func(input string, expect string) {
|
|
||||||
result, err := TransformRegExp(input)
|
|
||||||
is(err, nil)
|
|
||||||
if is(result, expect) {
|
|
||||||
_, err := regexp.Compile(result)
|
|
||||||
if !is(err, nil) {
|
|
||||||
t.Log(result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
test("", "")
|
|
||||||
|
|
||||||
test("abc", "abc")
|
|
||||||
|
|
||||||
test(`\abc`, `abc`)
|
|
||||||
|
|
||||||
test(`\a\b\c`, `a\bc`)
|
|
||||||
|
|
||||||
test(`\x`, `x`)
|
|
||||||
|
|
||||||
test(`\c`, `c`)
|
|
||||||
|
|
||||||
test(`\cA`, `\x01`)
|
|
||||||
|
|
||||||
test(`\cz`, `\x1a`)
|
|
||||||
|
|
||||||
test(`\ca`, `\x01`)
|
|
||||||
|
|
||||||
test(`\cj`, `\x0a`)
|
|
||||||
|
|
||||||
test(`\ck`, `\x0b`)
|
|
||||||
|
|
||||||
test(`\+`, `\+`)
|
|
||||||
|
|
||||||
test(`[\b]`, `[\x08]`)
|
|
||||||
|
|
||||||
test(`\u0z01\x\undefined`, `u0z01xundefined`)
|
|
||||||
|
|
||||||
test(`\\|'|\r|\n|\t|\u2028|\u2029`, `\\|'|\r|\n|\t|\x{2028}|\x{2029}`)
|
|
||||||
|
|
||||||
test("]", "]")
|
|
||||||
|
|
||||||
test("}", "}")
|
|
||||||
|
|
||||||
test("%", "%")
|
|
||||||
|
|
||||||
test("(%)", "(%)")
|
|
||||||
|
|
||||||
test("(?:[%\\s])", "(?:[%\\s])")
|
|
||||||
|
|
||||||
test("[[]", "[[]")
|
|
||||||
|
|
||||||
test("\\101", "\\x41")
|
|
||||||
|
|
||||||
test("\\51", "\\x29")
|
|
||||||
|
|
||||||
test("\\051", "\\x29")
|
|
||||||
|
|
||||||
test("\\175", "\\x7d")
|
|
||||||
|
|
||||||
test("\\04", "\\x04")
|
|
||||||
|
|
||||||
test(`<%([\s\S]+?)%>`, `<%([\s\S]+?)%>`)
|
|
||||||
|
|
||||||
test(`(.)^`, "(.)^")
|
|
||||||
|
|
||||||
test(`<%-([\s\S]+?)%>|<%=([\s\S]+?)%>|<%([\s\S]+?)%>|$`, `<%-([\s\S]+?)%>|<%=([\s\S]+?)%>|<%([\s\S]+?)%>|$`)
|
|
||||||
|
|
||||||
test(`\$`, `\$`)
|
|
||||||
|
|
||||||
test(`[G-b]`, `[G-b]`)
|
|
||||||
|
|
||||||
test(`[G-b\0]`, `[G-b\0]`)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTransformRegExp(t *testing.T) {
|
|
||||||
tt(t, func() {
|
|
||||||
pattern, err := TransformRegExp(`\s+abc\s+`)
|
|
||||||
is(err, nil)
|
|
||||||
is(pattern, `\s+abc\s+`)
|
|
||||||
is(regexp.MustCompile(pattern).MatchString("\t abc def"), true)
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -1,44 +0,0 @@
|
||||||
package parser
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/robertkrimen/otto/ast"
|
|
||||||
)
|
|
||||||
|
|
||||||
type _scope struct {
|
|
||||||
outer *_scope
|
|
||||||
allowIn bool
|
|
||||||
inIteration bool
|
|
||||||
inSwitch bool
|
|
||||||
inFunction bool
|
|
||||||
declarationList []ast.Declaration
|
|
||||||
|
|
||||||
labels []string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_parser) openScope() {
|
|
||||||
self.scope = &_scope{
|
|
||||||
outer: self.scope,
|
|
||||||
allowIn: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_parser) closeScope() {
|
|
||||||
self.scope = self.scope.outer
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_scope) declare(declaration ast.Declaration) {
|
|
||||||
self.declarationList = append(self.declarationList, declaration)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_scope) hasLabel(name string) bool {
|
|
||||||
for _, label := range self.labels {
|
|
||||||
if label == name {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if self.outer != nil && !self.inFunction {
|
|
||||||
// Crossing a function boundary to look for a label is verboten
|
|
||||||
return self.outer.hasLabel(name)
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
|
@ -1,662 +0,0 @@
|
||||||
package parser
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/robertkrimen/otto/ast"
|
|
||||||
"github.com/robertkrimen/otto/token"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (self *_parser) parseBlockStatement() *ast.BlockStatement {
|
|
||||||
node := &ast.BlockStatement{}
|
|
||||||
node.LeftBrace = self.expect(token.LEFT_BRACE)
|
|
||||||
node.List = self.parseStatementList()
|
|
||||||
node.RightBrace = self.expect(token.RIGHT_BRACE)
|
|
||||||
|
|
||||||
return node
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_parser) parseEmptyStatement() ast.Statement {
|
|
||||||
idx := self.expect(token.SEMICOLON)
|
|
||||||
return &ast.EmptyStatement{Semicolon: idx}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_parser) parseStatementList() (list []ast.Statement) {
|
|
||||||
for self.token != token.RIGHT_BRACE && self.token != token.EOF {
|
|
||||||
list = append(list, self.parseStatement())
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_parser) parseStatement() ast.Statement {
|
|
||||||
|
|
||||||
if self.token == token.EOF {
|
|
||||||
self.errorUnexpectedToken(self.token)
|
|
||||||
return &ast.BadStatement{From: self.idx, To: self.idx + 1}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch self.token {
|
|
||||||
case token.SEMICOLON:
|
|
||||||
return self.parseEmptyStatement()
|
|
||||||
case token.LEFT_BRACE:
|
|
||||||
return self.parseBlockStatement()
|
|
||||||
case token.IF:
|
|
||||||
return self.parseIfStatement()
|
|
||||||
case token.DO:
|
|
||||||
return self.parseDoWhileStatement()
|
|
||||||
case token.WHILE:
|
|
||||||
return self.parseWhileStatement()
|
|
||||||
case token.FOR:
|
|
||||||
return self.parseForOrForInStatement()
|
|
||||||
case token.BREAK:
|
|
||||||
return self.parseBreakStatement()
|
|
||||||
case token.CONTINUE:
|
|
||||||
return self.parseContinueStatement()
|
|
||||||
case token.DEBUGGER:
|
|
||||||
return self.parseDebuggerStatement()
|
|
||||||
case token.WITH:
|
|
||||||
return self.parseWithStatement()
|
|
||||||
case token.VAR:
|
|
||||||
return self.parseVariableStatement()
|
|
||||||
case token.FUNCTION:
|
|
||||||
self.parseFunction(true)
|
|
||||||
// FIXME
|
|
||||||
return &ast.EmptyStatement{}
|
|
||||||
case token.SWITCH:
|
|
||||||
return self.parseSwitchStatement()
|
|
||||||
case token.RETURN:
|
|
||||||
return self.parseReturnStatement()
|
|
||||||
case token.THROW:
|
|
||||||
return self.parseThrowStatement()
|
|
||||||
case token.TRY:
|
|
||||||
return self.parseTryStatement()
|
|
||||||
}
|
|
||||||
|
|
||||||
expression := self.parseExpression()
|
|
||||||
|
|
||||||
if identifier, isIdentifier := expression.(*ast.Identifier); isIdentifier && self.token == token.COLON {
|
|
||||||
// LabelledStatement
|
|
||||||
colon := self.idx
|
|
||||||
self.next() // :
|
|
||||||
label := identifier.Name
|
|
||||||
for _, value := range self.scope.labels {
|
|
||||||
if label == value {
|
|
||||||
self.error(identifier.Idx0(), "Label '%s' already exists", label)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.scope.labels = append(self.scope.labels, label) // Push the label
|
|
||||||
statement := self.parseStatement()
|
|
||||||
self.scope.labels = self.scope.labels[:len(self.scope.labels)-1] // Pop the label
|
|
||||||
return &ast.LabelledStatement{
|
|
||||||
Label: identifier,
|
|
||||||
Colon: colon,
|
|
||||||
Statement: statement,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.optionalSemicolon()
|
|
||||||
|
|
||||||
return &ast.ExpressionStatement{
|
|
||||||
Expression: expression,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_parser) parseTryStatement() ast.Statement {
|
|
||||||
|
|
||||||
node := &ast.TryStatement{
|
|
||||||
Try: self.expect(token.TRY),
|
|
||||||
Body: self.parseBlockStatement(),
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.token == token.CATCH {
|
|
||||||
catch := self.idx
|
|
||||||
self.next()
|
|
||||||
self.expect(token.LEFT_PARENTHESIS)
|
|
||||||
if self.token != token.IDENTIFIER {
|
|
||||||
self.expect(token.IDENTIFIER)
|
|
||||||
self.nextStatement()
|
|
||||||
return &ast.BadStatement{From: catch, To: self.idx}
|
|
||||||
} else {
|
|
||||||
identifier := self.parseIdentifier()
|
|
||||||
self.expect(token.RIGHT_PARENTHESIS)
|
|
||||||
node.Catch = &ast.CatchStatement{
|
|
||||||
Catch: catch,
|
|
||||||
Parameter: identifier,
|
|
||||||
Body: self.parseBlockStatement(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.token == token.FINALLY {
|
|
||||||
self.next()
|
|
||||||
node.Finally = self.parseBlockStatement()
|
|
||||||
}
|
|
||||||
|
|
||||||
if node.Catch == nil && node.Finally == nil {
|
|
||||||
self.error(node.Try, "Missing catch or finally after try")
|
|
||||||
return &ast.BadStatement{From: node.Try, To: node.Body.Idx1()}
|
|
||||||
}
|
|
||||||
|
|
||||||
return node
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_parser) parseFunctionParameterList() *ast.ParameterList {
|
|
||||||
opening := self.expect(token.LEFT_PARENTHESIS)
|
|
||||||
var list []*ast.Identifier
|
|
||||||
for self.token != token.RIGHT_PARENTHESIS && self.token != token.EOF {
|
|
||||||
if self.token != token.IDENTIFIER {
|
|
||||||
self.expect(token.IDENTIFIER)
|
|
||||||
} else {
|
|
||||||
list = append(list, self.parseIdentifier())
|
|
||||||
}
|
|
||||||
if self.token != token.RIGHT_PARENTHESIS {
|
|
||||||
self.expect(token.COMMA)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
closing := self.expect(token.RIGHT_PARENTHESIS)
|
|
||||||
|
|
||||||
return &ast.ParameterList{
|
|
||||||
Opening: opening,
|
|
||||||
List: list,
|
|
||||||
Closing: closing,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_parser) parseParameterList() (list []string) {
|
|
||||||
for self.token != token.EOF {
|
|
||||||
if self.token != token.IDENTIFIER {
|
|
||||||
self.expect(token.IDENTIFIER)
|
|
||||||
}
|
|
||||||
list = append(list, self.literal)
|
|
||||||
self.next()
|
|
||||||
if self.token != token.EOF {
|
|
||||||
self.expect(token.COMMA)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_parser) parseFunction(declaration bool) *ast.FunctionLiteral {
|
|
||||||
|
|
||||||
node := &ast.FunctionLiteral{
|
|
||||||
Function: self.expect(token.FUNCTION),
|
|
||||||
}
|
|
||||||
|
|
||||||
var name *ast.Identifier
|
|
||||||
if self.token == token.IDENTIFIER {
|
|
||||||
name = self.parseIdentifier()
|
|
||||||
if declaration {
|
|
||||||
self.scope.declare(&ast.FunctionDeclaration{
|
|
||||||
Function: node,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
} else if declaration {
|
|
||||||
// Use expect error handling
|
|
||||||
self.expect(token.IDENTIFIER)
|
|
||||||
}
|
|
||||||
node.Name = name
|
|
||||||
node.ParameterList = self.parseFunctionParameterList()
|
|
||||||
self.parseFunctionBlock(node)
|
|
||||||
node.Source = self.slice(node.Idx0(), node.Idx1())
|
|
||||||
|
|
||||||
return node
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_parser) parseFunctionBlock(node *ast.FunctionLiteral) {
|
|
||||||
{
|
|
||||||
self.openScope()
|
|
||||||
inFunction := self.scope.inFunction
|
|
||||||
self.scope.inFunction = true
|
|
||||||
defer func() {
|
|
||||||
self.scope.inFunction = inFunction
|
|
||||||
self.closeScope()
|
|
||||||
}()
|
|
||||||
node.Body = self.parseBlockStatement()
|
|
||||||
node.DeclarationList = self.scope.declarationList
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_parser) parseDebuggerStatement() ast.Statement {
|
|
||||||
idx := self.expect(token.DEBUGGER)
|
|
||||||
|
|
||||||
node := &ast.DebuggerStatement{
|
|
||||||
Debugger: idx,
|
|
||||||
}
|
|
||||||
|
|
||||||
self.semicolon()
|
|
||||||
|
|
||||||
return node
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_parser) parseReturnStatement() ast.Statement {
|
|
||||||
idx := self.expect(token.RETURN)
|
|
||||||
|
|
||||||
if !self.scope.inFunction {
|
|
||||||
self.error(idx, "Illegal return statement")
|
|
||||||
self.nextStatement()
|
|
||||||
return &ast.BadStatement{From: idx, To: self.idx}
|
|
||||||
}
|
|
||||||
|
|
||||||
node := &ast.ReturnStatement{
|
|
||||||
Return: idx,
|
|
||||||
}
|
|
||||||
|
|
||||||
if !self.implicitSemicolon && self.token != token.SEMICOLON && self.token != token.RIGHT_BRACE && self.token != token.EOF {
|
|
||||||
node.Argument = self.parseExpression()
|
|
||||||
}
|
|
||||||
|
|
||||||
self.semicolon()
|
|
||||||
|
|
||||||
return node
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_parser) parseThrowStatement() ast.Statement {
|
|
||||||
idx := self.expect(token.THROW)
|
|
||||||
|
|
||||||
if self.implicitSemicolon {
|
|
||||||
if self.chr == -1 { // Hackish
|
|
||||||
self.error(idx, "Unexpected end of input")
|
|
||||||
} else {
|
|
||||||
self.error(idx, "Illegal newline after throw")
|
|
||||||
}
|
|
||||||
self.nextStatement()
|
|
||||||
return &ast.BadStatement{From: idx, To: self.idx}
|
|
||||||
}
|
|
||||||
|
|
||||||
node := &ast.ThrowStatement{
|
|
||||||
Argument: self.parseExpression(),
|
|
||||||
}
|
|
||||||
|
|
||||||
self.semicolon()
|
|
||||||
|
|
||||||
return node
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_parser) parseSwitchStatement() ast.Statement {
|
|
||||||
self.expect(token.SWITCH)
|
|
||||||
self.expect(token.LEFT_PARENTHESIS)
|
|
||||||
node := &ast.SwitchStatement{
|
|
||||||
Discriminant: self.parseExpression(),
|
|
||||||
Default: -1,
|
|
||||||
}
|
|
||||||
self.expect(token.RIGHT_PARENTHESIS)
|
|
||||||
|
|
||||||
self.expect(token.LEFT_BRACE)
|
|
||||||
|
|
||||||
inSwitch := self.scope.inSwitch
|
|
||||||
self.scope.inSwitch = true
|
|
||||||
defer func() {
|
|
||||||
self.scope.inSwitch = inSwitch
|
|
||||||
}()
|
|
||||||
|
|
||||||
for index := 0; self.token != token.EOF; index++ {
|
|
||||||
if self.token == token.RIGHT_BRACE {
|
|
||||||
self.next()
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
clause := self.parseCaseStatement()
|
|
||||||
if clause.Test == nil {
|
|
||||||
if node.Default != -1 {
|
|
||||||
self.error(clause.Case, "Already saw a default in switch")
|
|
||||||
}
|
|
||||||
node.Default = index
|
|
||||||
}
|
|
||||||
node.Body = append(node.Body, clause)
|
|
||||||
}
|
|
||||||
|
|
||||||
return node
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_parser) parseWithStatement() ast.Statement {
|
|
||||||
self.expect(token.WITH)
|
|
||||||
self.expect(token.LEFT_PARENTHESIS)
|
|
||||||
node := &ast.WithStatement{
|
|
||||||
Object: self.parseExpression(),
|
|
||||||
}
|
|
||||||
self.expect(token.RIGHT_PARENTHESIS)
|
|
||||||
|
|
||||||
node.Body = self.parseStatement()
|
|
||||||
|
|
||||||
return node
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_parser) parseCaseStatement() *ast.CaseStatement {
|
|
||||||
|
|
||||||
node := &ast.CaseStatement{
|
|
||||||
Case: self.idx,
|
|
||||||
}
|
|
||||||
if self.token == token.DEFAULT {
|
|
||||||
self.next()
|
|
||||||
} else {
|
|
||||||
self.expect(token.CASE)
|
|
||||||
node.Test = self.parseExpression()
|
|
||||||
}
|
|
||||||
self.expect(token.COLON)
|
|
||||||
|
|
||||||
for {
|
|
||||||
if self.token == token.EOF ||
|
|
||||||
self.token == token.RIGHT_BRACE ||
|
|
||||||
self.token == token.CASE ||
|
|
||||||
self.token == token.DEFAULT {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
node.Consequent = append(node.Consequent, self.parseStatement())
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return node
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_parser) parseIterationStatement() ast.Statement {
|
|
||||||
inIteration := self.scope.inIteration
|
|
||||||
self.scope.inIteration = true
|
|
||||||
defer func() {
|
|
||||||
self.scope.inIteration = inIteration
|
|
||||||
}()
|
|
||||||
return self.parseStatement()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_parser) parseForIn(into ast.Expression) *ast.ForInStatement {
|
|
||||||
|
|
||||||
// Already have consumed "<into> in"
|
|
||||||
|
|
||||||
source := self.parseExpression()
|
|
||||||
self.expect(token.RIGHT_PARENTHESIS)
|
|
||||||
|
|
||||||
return &ast.ForInStatement{
|
|
||||||
Into: into,
|
|
||||||
Source: source,
|
|
||||||
Body: self.parseIterationStatement(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_parser) parseFor(initializer ast.Expression) *ast.ForStatement {
|
|
||||||
|
|
||||||
// Already have consumed "<initializer> ;"
|
|
||||||
|
|
||||||
var test, update ast.Expression
|
|
||||||
|
|
||||||
if self.token != token.SEMICOLON {
|
|
||||||
test = self.parseExpression()
|
|
||||||
}
|
|
||||||
self.expect(token.SEMICOLON)
|
|
||||||
|
|
||||||
if self.token != token.RIGHT_PARENTHESIS {
|
|
||||||
update = self.parseExpression()
|
|
||||||
}
|
|
||||||
self.expect(token.RIGHT_PARENTHESIS)
|
|
||||||
|
|
||||||
return &ast.ForStatement{
|
|
||||||
Initializer: initializer,
|
|
||||||
Test: test,
|
|
||||||
Update: update,
|
|
||||||
Body: self.parseIterationStatement(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_parser) parseForOrForInStatement() ast.Statement {
|
|
||||||
idx := self.expect(token.FOR)
|
|
||||||
self.expect(token.LEFT_PARENTHESIS)
|
|
||||||
|
|
||||||
var left []ast.Expression
|
|
||||||
|
|
||||||
forIn := false
|
|
||||||
if self.token != token.SEMICOLON {
|
|
||||||
|
|
||||||
allowIn := self.scope.allowIn
|
|
||||||
self.scope.allowIn = false
|
|
||||||
if self.token == token.VAR {
|
|
||||||
var_ := self.idx
|
|
||||||
self.next()
|
|
||||||
list := self.parseVariableDeclarationList(var_)
|
|
||||||
if len(list) == 1 && self.token == token.IN {
|
|
||||||
self.next() // in
|
|
||||||
forIn = true
|
|
||||||
left = []ast.Expression{list[0]} // There is only one declaration
|
|
||||||
} else {
|
|
||||||
left = list
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
left = append(left, self.parseExpression())
|
|
||||||
if self.token == token.IN {
|
|
||||||
self.next()
|
|
||||||
forIn = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.scope.allowIn = allowIn
|
|
||||||
}
|
|
||||||
|
|
||||||
if forIn {
|
|
||||||
switch left[0].(type) {
|
|
||||||
case *ast.Identifier, *ast.DotExpression, *ast.BracketExpression, *ast.VariableExpression:
|
|
||||||
// These are all acceptable
|
|
||||||
default:
|
|
||||||
self.error(idx, "Invalid left-hand side in for-in")
|
|
||||||
self.nextStatement()
|
|
||||||
return &ast.BadStatement{From: idx, To: self.idx}
|
|
||||||
}
|
|
||||||
return self.parseForIn(left[0])
|
|
||||||
}
|
|
||||||
|
|
||||||
self.expect(token.SEMICOLON)
|
|
||||||
return self.parseFor(&ast.SequenceExpression{Sequence: left})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_parser) parseVariableStatement() *ast.VariableStatement {
|
|
||||||
|
|
||||||
idx := self.expect(token.VAR)
|
|
||||||
|
|
||||||
list := self.parseVariableDeclarationList(idx)
|
|
||||||
self.semicolon()
|
|
||||||
|
|
||||||
return &ast.VariableStatement{
|
|
||||||
Var: idx,
|
|
||||||
List: list,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_parser) parseDoWhileStatement() ast.Statement {
|
|
||||||
inIteration := self.scope.inIteration
|
|
||||||
self.scope.inIteration = true
|
|
||||||
defer func() {
|
|
||||||
self.scope.inIteration = inIteration
|
|
||||||
}()
|
|
||||||
|
|
||||||
self.expect(token.DO)
|
|
||||||
node := &ast.DoWhileStatement{}
|
|
||||||
if self.token == token.LEFT_BRACE {
|
|
||||||
node.Body = self.parseBlockStatement()
|
|
||||||
} else {
|
|
||||||
node.Body = self.parseStatement()
|
|
||||||
}
|
|
||||||
|
|
||||||
self.expect(token.WHILE)
|
|
||||||
self.expect(token.LEFT_PARENTHESIS)
|
|
||||||
node.Test = self.parseExpression()
|
|
||||||
self.expect(token.RIGHT_PARENTHESIS)
|
|
||||||
|
|
||||||
return node
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_parser) parseWhileStatement() ast.Statement {
|
|
||||||
self.expect(token.WHILE)
|
|
||||||
self.expect(token.LEFT_PARENTHESIS)
|
|
||||||
node := &ast.WhileStatement{
|
|
||||||
Test: self.parseExpression(),
|
|
||||||
}
|
|
||||||
self.expect(token.RIGHT_PARENTHESIS)
|
|
||||||
node.Body = self.parseIterationStatement()
|
|
||||||
|
|
||||||
return node
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_parser) parseIfStatement() ast.Statement {
|
|
||||||
self.expect(token.IF)
|
|
||||||
self.expect(token.LEFT_PARENTHESIS)
|
|
||||||
node := &ast.IfStatement{
|
|
||||||
Test: self.parseExpression(),
|
|
||||||
}
|
|
||||||
self.expect(token.RIGHT_PARENTHESIS)
|
|
||||||
|
|
||||||
if self.token == token.LEFT_BRACE {
|
|
||||||
node.Consequent = self.parseBlockStatement()
|
|
||||||
} else {
|
|
||||||
node.Consequent = self.parseStatement()
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.token == token.ELSE {
|
|
||||||
self.next()
|
|
||||||
node.Alternate = self.parseStatement()
|
|
||||||
}
|
|
||||||
|
|
||||||
return node
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_parser) parseSourceElement() ast.Statement {
|
|
||||||
return self.parseStatement()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_parser) parseSourceElements() []ast.Statement {
|
|
||||||
body := []ast.Statement(nil)
|
|
||||||
|
|
||||||
for {
|
|
||||||
if self.token != token.STRING {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
body = append(body, self.parseSourceElement())
|
|
||||||
}
|
|
||||||
|
|
||||||
for self.token != token.EOF {
|
|
||||||
body = append(body, self.parseSourceElement())
|
|
||||||
}
|
|
||||||
|
|
||||||
return body
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_parser) parseProgram() *ast.Program {
|
|
||||||
self.openScope()
|
|
||||||
defer self.closeScope()
|
|
||||||
return &ast.Program{
|
|
||||||
Body: self.parseSourceElements(),
|
|
||||||
DeclarationList: self.scope.declarationList,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_parser) parseBreakStatement() ast.Statement {
|
|
||||||
idx := self.expect(token.BREAK)
|
|
||||||
semicolon := self.implicitSemicolon
|
|
||||||
if self.token == token.SEMICOLON {
|
|
||||||
semicolon = true
|
|
||||||
self.next()
|
|
||||||
}
|
|
||||||
|
|
||||||
if semicolon || self.token == token.RIGHT_BRACE {
|
|
||||||
self.implicitSemicolon = false
|
|
||||||
if !self.scope.inIteration && !self.scope.inSwitch {
|
|
||||||
goto illegal
|
|
||||||
}
|
|
||||||
return &ast.BranchStatement{
|
|
||||||
Idx: idx,
|
|
||||||
Token: token.BREAK,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.token == token.IDENTIFIER {
|
|
||||||
identifier := self.parseIdentifier()
|
|
||||||
if !self.scope.hasLabel(identifier.Name) {
|
|
||||||
self.error(idx, "Undefined label '%s'", identifier.Name)
|
|
||||||
return &ast.BadStatement{From: idx, To: identifier.Idx1()}
|
|
||||||
}
|
|
||||||
self.semicolon()
|
|
||||||
return &ast.BranchStatement{
|
|
||||||
Idx: idx,
|
|
||||||
Token: token.BREAK,
|
|
||||||
Label: identifier,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.expect(token.IDENTIFIER)
|
|
||||||
|
|
||||||
illegal:
|
|
||||||
self.error(idx, "Illegal break statement")
|
|
||||||
self.nextStatement()
|
|
||||||
return &ast.BadStatement{From: idx, To: self.idx}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_parser) parseContinueStatement() ast.Statement {
|
|
||||||
idx := self.expect(token.CONTINUE)
|
|
||||||
semicolon := self.implicitSemicolon
|
|
||||||
if self.token == token.SEMICOLON {
|
|
||||||
semicolon = true
|
|
||||||
self.next()
|
|
||||||
}
|
|
||||||
|
|
||||||
if semicolon || self.token == token.RIGHT_BRACE {
|
|
||||||
self.implicitSemicolon = false
|
|
||||||
if !self.scope.inIteration {
|
|
||||||
goto illegal
|
|
||||||
}
|
|
||||||
return &ast.BranchStatement{
|
|
||||||
Idx: idx,
|
|
||||||
Token: token.CONTINUE,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.token == token.IDENTIFIER {
|
|
||||||
identifier := self.parseIdentifier()
|
|
||||||
if !self.scope.hasLabel(identifier.Name) {
|
|
||||||
self.error(idx, "Undefined label '%s'", identifier.Name)
|
|
||||||
return &ast.BadStatement{From: idx, To: identifier.Idx1()}
|
|
||||||
}
|
|
||||||
if !self.scope.inIteration {
|
|
||||||
goto illegal
|
|
||||||
}
|
|
||||||
self.semicolon()
|
|
||||||
return &ast.BranchStatement{
|
|
||||||
Idx: idx,
|
|
||||||
Token: token.CONTINUE,
|
|
||||||
Label: identifier,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.expect(token.IDENTIFIER)
|
|
||||||
|
|
||||||
illegal:
|
|
||||||
self.error(idx, "Illegal continue statement")
|
|
||||||
self.nextStatement()
|
|
||||||
return &ast.BadStatement{From: idx, To: self.idx}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find the next statement after an error (recover)
|
|
||||||
func (self *_parser) nextStatement() {
|
|
||||||
for {
|
|
||||||
switch self.token {
|
|
||||||
case token.BREAK, token.CONTINUE,
|
|
||||||
token.FOR, token.IF, token.RETURN, token.SWITCH,
|
|
||||||
token.VAR, token.DO, token.TRY, token.WITH,
|
|
||||||
token.WHILE, token.THROW, token.CATCH, token.FINALLY:
|
|
||||||
// Return only if parser made some progress since last
|
|
||||||
// sync or if it has not reached 10 next calls without
|
|
||||||
// progress. Otherwise consume at least one token to
|
|
||||||
// avoid an endless parser loop
|
|
||||||
if self.idx == self.recover.idx && self.recover.count < 10 {
|
|
||||||
self.recover.count++
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if self.idx > self.recover.idx {
|
|
||||||
self.recover.idx = self.idx
|
|
||||||
self.recover.count = 0
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// Reaching here indicates a parser bug, likely an
|
|
||||||
// incorrect token list in this function, but it only
|
|
||||||
// leads to skipping of possibly correct code if a
|
|
||||||
// previous error is present, and thus is preferred
|
|
||||||
// over a non-terminating parse.
|
|
||||||
case token.EOF:
|
|
||||||
return
|
|
||||||
}
|
|
||||||
self.next()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,51 +0,0 @@
|
||||||
# registry
|
|
||||||
--
|
|
||||||
import "github.com/robertkrimen/otto/registry"
|
|
||||||
|
|
||||||
Package registry is an expirmental package to facillitate altering the otto
|
|
||||||
runtime via import.
|
|
||||||
|
|
||||||
This interface can change at any time.
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
#### func Apply
|
|
||||||
|
|
||||||
```go
|
|
||||||
func Apply(callback func(Entry))
|
|
||||||
```
|
|
||||||
|
|
||||||
#### type Entry
|
|
||||||
|
|
||||||
```go
|
|
||||||
type Entry struct {
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
#### func Register
|
|
||||||
|
|
||||||
```go
|
|
||||||
func Register(source func() string) *Entry
|
|
||||||
```
|
|
||||||
|
|
||||||
#### func (*Entry) Disable
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (self *Entry) Disable()
|
|
||||||
```
|
|
||||||
|
|
||||||
#### func (*Entry) Enable
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (self *Entry) Enable()
|
|
||||||
```
|
|
||||||
|
|
||||||
#### func (Entry) Source
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (self Entry) Source() string
|
|
||||||
```
|
|
||||||
|
|
||||||
--
|
|
||||||
**godocdown** http://github.com/robertkrimen/godocdown
|
|
|
@ -1,47 +0,0 @@
|
||||||
/*
|
|
||||||
Package registry is an expirmental package to facillitate altering the otto runtime via import.
|
|
||||||
|
|
||||||
This interface can change at any time.
|
|
||||||
*/
|
|
||||||
package registry
|
|
||||||
|
|
||||||
var registry []*Entry = make([]*Entry, 0)
|
|
||||||
|
|
||||||
type Entry struct {
|
|
||||||
active bool
|
|
||||||
source func() string
|
|
||||||
}
|
|
||||||
|
|
||||||
func newEntry(source func() string) *Entry {
|
|
||||||
return &Entry{
|
|
||||||
active: true,
|
|
||||||
source: source,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *Entry) Enable() {
|
|
||||||
self.active = true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *Entry) Disable() {
|
|
||||||
self.active = false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self Entry) Source() string {
|
|
||||||
return self.source()
|
|
||||||
}
|
|
||||||
|
|
||||||
func Apply(callback func(Entry)) {
|
|
||||||
for _, entry := range registry {
|
|
||||||
if !entry.active {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
callback(*entry)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Register(source func() string) *Entry {
|
|
||||||
entry := newEntry(source)
|
|
||||||
registry = append(registry, entry)
|
|
||||||
return entry
|
|
||||||
}
|
|
|
@ -1,2 +0,0 @@
|
||||||
token_const.go: tokenfmt
|
|
||||||
./$^ | gofmt > $@
|
|
|
@ -1,171 +0,0 @@
|
||||||
# token
|
|
||||||
--
|
|
||||||
import "github.com/robertkrimen/otto/token"
|
|
||||||
|
|
||||||
Package token defines constants representing the lexical tokens of JavaScript
|
|
||||||
(ECMA5).
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
```go
|
|
||||||
const (
|
|
||||||
ILLEGAL
|
|
||||||
EOF
|
|
||||||
COMMENT
|
|
||||||
KEYWORD
|
|
||||||
|
|
||||||
STRING
|
|
||||||
BOOLEAN
|
|
||||||
NULL
|
|
||||||
NUMBER
|
|
||||||
IDENTIFIER
|
|
||||||
|
|
||||||
PLUS // +
|
|
||||||
MINUS // -
|
|
||||||
MULTIPLY // *
|
|
||||||
SLASH // /
|
|
||||||
REMAINDER // %
|
|
||||||
|
|
||||||
AND // &
|
|
||||||
OR // |
|
|
||||||
EXCLUSIVE_OR // ^
|
|
||||||
SHIFT_LEFT // <<
|
|
||||||
SHIFT_RIGHT // >>
|
|
||||||
UNSIGNED_SHIFT_RIGHT // >>>
|
|
||||||
AND_NOT // &^
|
|
||||||
|
|
||||||
ADD_ASSIGN // +=
|
|
||||||
SUBTRACT_ASSIGN // -=
|
|
||||||
MULTIPLY_ASSIGN // *=
|
|
||||||
QUOTIENT_ASSIGN // /=
|
|
||||||
REMAINDER_ASSIGN // %=
|
|
||||||
|
|
||||||
AND_ASSIGN // &=
|
|
||||||
OR_ASSIGN // |=
|
|
||||||
EXCLUSIVE_OR_ASSIGN // ^=
|
|
||||||
SHIFT_LEFT_ASSIGN // <<=
|
|
||||||
SHIFT_RIGHT_ASSIGN // >>=
|
|
||||||
UNSIGNED_SHIFT_RIGHT_ASSIGN // >>>=
|
|
||||||
AND_NOT_ASSIGN // &^=
|
|
||||||
|
|
||||||
LOGICAL_AND // &&
|
|
||||||
LOGICAL_OR // ||
|
|
||||||
INCREMENT // ++
|
|
||||||
DECREMENT // --
|
|
||||||
|
|
||||||
EQUAL // ==
|
|
||||||
STRICT_EQUAL // ===
|
|
||||||
LESS // <
|
|
||||||
GREATER // >
|
|
||||||
ASSIGN // =
|
|
||||||
NOT // !
|
|
||||||
|
|
||||||
BITWISE_NOT // ~
|
|
||||||
|
|
||||||
NOT_EQUAL // !=
|
|
||||||
STRICT_NOT_EQUAL // !==
|
|
||||||
LESS_OR_EQUAL // <=
|
|
||||||
GREATER_OR_EQUAL // <=
|
|
||||||
|
|
||||||
LEFT_PARENTHESIS // (
|
|
||||||
LEFT_BRACKET // [
|
|
||||||
LEFT_BRACE // {
|
|
||||||
COMMA // ,
|
|
||||||
PERIOD // .
|
|
||||||
|
|
||||||
RIGHT_PARENTHESIS // )
|
|
||||||
RIGHT_BRACKET // ]
|
|
||||||
RIGHT_BRACE // }
|
|
||||||
SEMICOLON // ;
|
|
||||||
COLON // :
|
|
||||||
QUESTION_MARK // ?
|
|
||||||
|
|
||||||
IF
|
|
||||||
IN
|
|
||||||
DO
|
|
||||||
|
|
||||||
VAR
|
|
||||||
FOR
|
|
||||||
NEW
|
|
||||||
TRY
|
|
||||||
|
|
||||||
THIS
|
|
||||||
ELSE
|
|
||||||
CASE
|
|
||||||
VOID
|
|
||||||
WITH
|
|
||||||
|
|
||||||
WHILE
|
|
||||||
BREAK
|
|
||||||
CATCH
|
|
||||||
THROW
|
|
||||||
|
|
||||||
RETURN
|
|
||||||
TYPEOF
|
|
||||||
DELETE
|
|
||||||
SWITCH
|
|
||||||
|
|
||||||
DEFAULT
|
|
||||||
FINALLY
|
|
||||||
|
|
||||||
FUNCTION
|
|
||||||
CONTINUE
|
|
||||||
DEBUGGER
|
|
||||||
|
|
||||||
INSTANCEOF
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
#### type Token
|
|
||||||
|
|
||||||
```go
|
|
||||||
type Token int
|
|
||||||
```
|
|
||||||
|
|
||||||
Token is the set of lexical tokens in JavaScript (ECMA5).
|
|
||||||
|
|
||||||
#### func IsKeyword
|
|
||||||
|
|
||||||
```go
|
|
||||||
func IsKeyword(literal string) (Token, bool)
|
|
||||||
```
|
|
||||||
IsKeyword returns the keyword token if literal is a keyword, a KEYWORD token if
|
|
||||||
the literal is a future keyword (const, let, class, super, ...), or 0 if the
|
|
||||||
literal is not a keyword.
|
|
||||||
|
|
||||||
If the literal is a keyword, IsKeyword returns a second value indicating if the
|
|
||||||
literal is considered a future keyword in strict-mode only.
|
|
||||||
|
|
||||||
7.6.1.2 Future Reserved Words:
|
|
||||||
|
|
||||||
const
|
|
||||||
class
|
|
||||||
enum
|
|
||||||
export
|
|
||||||
extends
|
|
||||||
import
|
|
||||||
super
|
|
||||||
|
|
||||||
7.6.1.2 Future Reserved Words (strict):
|
|
||||||
|
|
||||||
implements
|
|
||||||
interface
|
|
||||||
let
|
|
||||||
package
|
|
||||||
private
|
|
||||||
protected
|
|
||||||
public
|
|
||||||
static
|
|
||||||
|
|
||||||
#### func (Token) String
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (tkn Token) String() string
|
|
||||||
```
|
|
||||||
String returns the string corresponding to the token. For operators, delimiters,
|
|
||||||
and keywords the string is the actual token string (e.g., for the token PLUS,
|
|
||||||
the String() is "+"). For all other tokens the string corresponds to the token
|
|
||||||
name (e.g. for the token IDENTIFIER, the string is "IDENTIFIER").
|
|
||||||
|
|
||||||
--
|
|
||||||
**godocdown** http://github.com/robertkrimen/godocdown
|
|
|
@ -1,116 +0,0 @@
|
||||||
// Package token defines constants representing the lexical tokens of JavaScript (ECMA5).
|
|
||||||
package token
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strconv"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Token is the set of lexical tokens in JavaScript (ECMA5).
|
|
||||||
type Token int
|
|
||||||
|
|
||||||
// String returns the string corresponding to the token.
|
|
||||||
// For operators, delimiters, and keywords the string is the actual
|
|
||||||
// token string (e.g., for the token PLUS, the String() is
|
|
||||||
// "+"). For all other tokens the string corresponds to the token
|
|
||||||
// name (e.g. for the token IDENTIFIER, the string is "IDENTIFIER").
|
|
||||||
//
|
|
||||||
func (tkn Token) String() string {
|
|
||||||
if 0 == tkn {
|
|
||||||
return "UNKNOWN"
|
|
||||||
}
|
|
||||||
if tkn < Token(len(token2string)) {
|
|
||||||
return token2string[tkn]
|
|
||||||
}
|
|
||||||
return "token(" + strconv.Itoa(int(tkn)) + ")"
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is not used for anything
|
|
||||||
func (tkn Token) precedence(in bool) int {
|
|
||||||
|
|
||||||
switch tkn {
|
|
||||||
case LOGICAL_OR:
|
|
||||||
return 1
|
|
||||||
|
|
||||||
case LOGICAL_AND:
|
|
||||||
return 2
|
|
||||||
|
|
||||||
case OR, OR_ASSIGN:
|
|
||||||
return 3
|
|
||||||
|
|
||||||
case EXCLUSIVE_OR:
|
|
||||||
return 4
|
|
||||||
|
|
||||||
case AND, AND_ASSIGN, AND_NOT, AND_NOT_ASSIGN:
|
|
||||||
return 5
|
|
||||||
|
|
||||||
case EQUAL,
|
|
||||||
NOT_EQUAL,
|
|
||||||
STRICT_EQUAL,
|
|
||||||
STRICT_NOT_EQUAL:
|
|
||||||
return 6
|
|
||||||
|
|
||||||
case LESS, GREATER, LESS_OR_EQUAL, GREATER_OR_EQUAL, INSTANCEOF:
|
|
||||||
return 7
|
|
||||||
|
|
||||||
case IN:
|
|
||||||
if in {
|
|
||||||
return 7
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
|
|
||||||
case SHIFT_LEFT, SHIFT_RIGHT, UNSIGNED_SHIFT_RIGHT:
|
|
||||||
fallthrough
|
|
||||||
case SHIFT_LEFT_ASSIGN, SHIFT_RIGHT_ASSIGN, UNSIGNED_SHIFT_RIGHT_ASSIGN:
|
|
||||||
return 8
|
|
||||||
|
|
||||||
case PLUS, MINUS, ADD_ASSIGN, SUBTRACT_ASSIGN:
|
|
||||||
return 9
|
|
||||||
|
|
||||||
case MULTIPLY, SLASH, REMAINDER, MULTIPLY_ASSIGN, QUOTIENT_ASSIGN, REMAINDER_ASSIGN:
|
|
||||||
return 11
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
type _keyword struct {
|
|
||||||
token Token
|
|
||||||
futureKeyword bool
|
|
||||||
strict bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsKeyword returns the keyword token if literal is a keyword, a KEYWORD token
|
|
||||||
// if the literal is a future keyword (const, let, class, super, ...), or 0 if the literal is not a keyword.
|
|
||||||
//
|
|
||||||
// If the literal is a keyword, IsKeyword returns a second value indicating if the literal
|
|
||||||
// is considered a future keyword in strict-mode only.
|
|
||||||
//
|
|
||||||
// 7.6.1.2 Future Reserved Words:
|
|
||||||
//
|
|
||||||
// const
|
|
||||||
// class
|
|
||||||
// enum
|
|
||||||
// export
|
|
||||||
// extends
|
|
||||||
// import
|
|
||||||
// super
|
|
||||||
//
|
|
||||||
// 7.6.1.2 Future Reserved Words (strict):
|
|
||||||
//
|
|
||||||
// implements
|
|
||||||
// interface
|
|
||||||
// let
|
|
||||||
// package
|
|
||||||
// private
|
|
||||||
// protected
|
|
||||||
// public
|
|
||||||
// static
|
|
||||||
//
|
|
||||||
func IsKeyword(literal string) (Token, bool) {
|
|
||||||
if keyword, exists := keywordTable[literal]; exists {
|
|
||||||
if keyword.futureKeyword {
|
|
||||||
return KEYWORD, keyword.strict
|
|
||||||
}
|
|
||||||
return keyword.token, false
|
|
||||||
}
|
|
||||||
return 0, false
|
|
||||||
}
|
|
|
@ -1,349 +0,0 @@
|
||||||
package token
|
|
||||||
|
|
||||||
const (
|
|
||||||
_ Token = iota
|
|
||||||
|
|
||||||
ILLEGAL
|
|
||||||
EOF
|
|
||||||
COMMENT
|
|
||||||
KEYWORD
|
|
||||||
|
|
||||||
STRING
|
|
||||||
BOOLEAN
|
|
||||||
NULL
|
|
||||||
NUMBER
|
|
||||||
IDENTIFIER
|
|
||||||
|
|
||||||
PLUS // +
|
|
||||||
MINUS // -
|
|
||||||
MULTIPLY // *
|
|
||||||
SLASH // /
|
|
||||||
REMAINDER // %
|
|
||||||
|
|
||||||
AND // &
|
|
||||||
OR // |
|
|
||||||
EXCLUSIVE_OR // ^
|
|
||||||
SHIFT_LEFT // <<
|
|
||||||
SHIFT_RIGHT // >>
|
|
||||||
UNSIGNED_SHIFT_RIGHT // >>>
|
|
||||||
AND_NOT // &^
|
|
||||||
|
|
||||||
ADD_ASSIGN // +=
|
|
||||||
SUBTRACT_ASSIGN // -=
|
|
||||||
MULTIPLY_ASSIGN // *=
|
|
||||||
QUOTIENT_ASSIGN // /=
|
|
||||||
REMAINDER_ASSIGN // %=
|
|
||||||
|
|
||||||
AND_ASSIGN // &=
|
|
||||||
OR_ASSIGN // |=
|
|
||||||
EXCLUSIVE_OR_ASSIGN // ^=
|
|
||||||
SHIFT_LEFT_ASSIGN // <<=
|
|
||||||
SHIFT_RIGHT_ASSIGN // >>=
|
|
||||||
UNSIGNED_SHIFT_RIGHT_ASSIGN // >>>=
|
|
||||||
AND_NOT_ASSIGN // &^=
|
|
||||||
|
|
||||||
LOGICAL_AND // &&
|
|
||||||
LOGICAL_OR // ||
|
|
||||||
INCREMENT // ++
|
|
||||||
DECREMENT // --
|
|
||||||
|
|
||||||
EQUAL // ==
|
|
||||||
STRICT_EQUAL // ===
|
|
||||||
LESS // <
|
|
||||||
GREATER // >
|
|
||||||
ASSIGN // =
|
|
||||||
NOT // !
|
|
||||||
|
|
||||||
BITWISE_NOT // ~
|
|
||||||
|
|
||||||
NOT_EQUAL // !=
|
|
||||||
STRICT_NOT_EQUAL // !==
|
|
||||||
LESS_OR_EQUAL // <=
|
|
||||||
GREATER_OR_EQUAL // <=
|
|
||||||
|
|
||||||
LEFT_PARENTHESIS // (
|
|
||||||
LEFT_BRACKET // [
|
|
||||||
LEFT_BRACE // {
|
|
||||||
COMMA // ,
|
|
||||||
PERIOD // .
|
|
||||||
|
|
||||||
RIGHT_PARENTHESIS // )
|
|
||||||
RIGHT_BRACKET // ]
|
|
||||||
RIGHT_BRACE // }
|
|
||||||
SEMICOLON // ;
|
|
||||||
COLON // :
|
|
||||||
QUESTION_MARK // ?
|
|
||||||
|
|
||||||
firstKeyword
|
|
||||||
IF
|
|
||||||
IN
|
|
||||||
DO
|
|
||||||
|
|
||||||
VAR
|
|
||||||
FOR
|
|
||||||
NEW
|
|
||||||
TRY
|
|
||||||
|
|
||||||
THIS
|
|
||||||
ELSE
|
|
||||||
CASE
|
|
||||||
VOID
|
|
||||||
WITH
|
|
||||||
|
|
||||||
WHILE
|
|
||||||
BREAK
|
|
||||||
CATCH
|
|
||||||
THROW
|
|
||||||
|
|
||||||
RETURN
|
|
||||||
TYPEOF
|
|
||||||
DELETE
|
|
||||||
SWITCH
|
|
||||||
|
|
||||||
DEFAULT
|
|
||||||
FINALLY
|
|
||||||
|
|
||||||
FUNCTION
|
|
||||||
CONTINUE
|
|
||||||
DEBUGGER
|
|
||||||
|
|
||||||
INSTANCEOF
|
|
||||||
lastKeyword
|
|
||||||
)
|
|
||||||
|
|
||||||
var token2string = [...]string{
|
|
||||||
ILLEGAL: "ILLEGAL",
|
|
||||||
EOF: "EOF",
|
|
||||||
COMMENT: "COMMENT",
|
|
||||||
KEYWORD: "KEYWORD",
|
|
||||||
STRING: "STRING",
|
|
||||||
BOOLEAN: "BOOLEAN",
|
|
||||||
NULL: "NULL",
|
|
||||||
NUMBER: "NUMBER",
|
|
||||||
IDENTIFIER: "IDENTIFIER",
|
|
||||||
PLUS: "+",
|
|
||||||
MINUS: "-",
|
|
||||||
MULTIPLY: "*",
|
|
||||||
SLASH: "/",
|
|
||||||
REMAINDER: "%",
|
|
||||||
AND: "&",
|
|
||||||
OR: "|",
|
|
||||||
EXCLUSIVE_OR: "^",
|
|
||||||
SHIFT_LEFT: "<<",
|
|
||||||
SHIFT_RIGHT: ">>",
|
|
||||||
UNSIGNED_SHIFT_RIGHT: ">>>",
|
|
||||||
AND_NOT: "&^",
|
|
||||||
ADD_ASSIGN: "+=",
|
|
||||||
SUBTRACT_ASSIGN: "-=",
|
|
||||||
MULTIPLY_ASSIGN: "*=",
|
|
||||||
QUOTIENT_ASSIGN: "/=",
|
|
||||||
REMAINDER_ASSIGN: "%=",
|
|
||||||
AND_ASSIGN: "&=",
|
|
||||||
OR_ASSIGN: "|=",
|
|
||||||
EXCLUSIVE_OR_ASSIGN: "^=",
|
|
||||||
SHIFT_LEFT_ASSIGN: "<<=",
|
|
||||||
SHIFT_RIGHT_ASSIGN: ">>=",
|
|
||||||
UNSIGNED_SHIFT_RIGHT_ASSIGN: ">>>=",
|
|
||||||
AND_NOT_ASSIGN: "&^=",
|
|
||||||
LOGICAL_AND: "&&",
|
|
||||||
LOGICAL_OR: "||",
|
|
||||||
INCREMENT: "++",
|
|
||||||
DECREMENT: "--",
|
|
||||||
EQUAL: "==",
|
|
||||||
STRICT_EQUAL: "===",
|
|
||||||
LESS: "<",
|
|
||||||
GREATER: ">",
|
|
||||||
ASSIGN: "=",
|
|
||||||
NOT: "!",
|
|
||||||
BITWISE_NOT: "~",
|
|
||||||
NOT_EQUAL: "!=",
|
|
||||||
STRICT_NOT_EQUAL: "!==",
|
|
||||||
LESS_OR_EQUAL: "<=",
|
|
||||||
GREATER_OR_EQUAL: "<=",
|
|
||||||
LEFT_PARENTHESIS: "(",
|
|
||||||
LEFT_BRACKET: "[",
|
|
||||||
LEFT_BRACE: "{",
|
|
||||||
COMMA: ",",
|
|
||||||
PERIOD: ".",
|
|
||||||
RIGHT_PARENTHESIS: ")",
|
|
||||||
RIGHT_BRACKET: "]",
|
|
||||||
RIGHT_BRACE: "}",
|
|
||||||
SEMICOLON: ";",
|
|
||||||
COLON: ":",
|
|
||||||
QUESTION_MARK: "?",
|
|
||||||
IF: "if",
|
|
||||||
IN: "in",
|
|
||||||
DO: "do",
|
|
||||||
VAR: "var",
|
|
||||||
FOR: "for",
|
|
||||||
NEW: "new",
|
|
||||||
TRY: "try",
|
|
||||||
THIS: "this",
|
|
||||||
ELSE: "else",
|
|
||||||
CASE: "case",
|
|
||||||
VOID: "void",
|
|
||||||
WITH: "with",
|
|
||||||
WHILE: "while",
|
|
||||||
BREAK: "break",
|
|
||||||
CATCH: "catch",
|
|
||||||
THROW: "throw",
|
|
||||||
RETURN: "return",
|
|
||||||
TYPEOF: "typeof",
|
|
||||||
DELETE: "delete",
|
|
||||||
SWITCH: "switch",
|
|
||||||
DEFAULT: "default",
|
|
||||||
FINALLY: "finally",
|
|
||||||
FUNCTION: "function",
|
|
||||||
CONTINUE: "continue",
|
|
||||||
DEBUGGER: "debugger",
|
|
||||||
INSTANCEOF: "instanceof",
|
|
||||||
}
|
|
||||||
|
|
||||||
var keywordTable = map[string]_keyword{
|
|
||||||
"if": _keyword{
|
|
||||||
token: IF,
|
|
||||||
},
|
|
||||||
"in": _keyword{
|
|
||||||
token: IN,
|
|
||||||
},
|
|
||||||
"do": _keyword{
|
|
||||||
token: DO,
|
|
||||||
},
|
|
||||||
"var": _keyword{
|
|
||||||
token: VAR,
|
|
||||||
},
|
|
||||||
"for": _keyword{
|
|
||||||
token: FOR,
|
|
||||||
},
|
|
||||||
"new": _keyword{
|
|
||||||
token: NEW,
|
|
||||||
},
|
|
||||||
"try": _keyword{
|
|
||||||
token: TRY,
|
|
||||||
},
|
|
||||||
"this": _keyword{
|
|
||||||
token: THIS,
|
|
||||||
},
|
|
||||||
"else": _keyword{
|
|
||||||
token: ELSE,
|
|
||||||
},
|
|
||||||
"case": _keyword{
|
|
||||||
token: CASE,
|
|
||||||
},
|
|
||||||
"void": _keyword{
|
|
||||||
token: VOID,
|
|
||||||
},
|
|
||||||
"with": _keyword{
|
|
||||||
token: WITH,
|
|
||||||
},
|
|
||||||
"while": _keyword{
|
|
||||||
token: WHILE,
|
|
||||||
},
|
|
||||||
"break": _keyword{
|
|
||||||
token: BREAK,
|
|
||||||
},
|
|
||||||
"catch": _keyword{
|
|
||||||
token: CATCH,
|
|
||||||
},
|
|
||||||
"throw": _keyword{
|
|
||||||
token: THROW,
|
|
||||||
},
|
|
||||||
"return": _keyword{
|
|
||||||
token: RETURN,
|
|
||||||
},
|
|
||||||
"typeof": _keyword{
|
|
||||||
token: TYPEOF,
|
|
||||||
},
|
|
||||||
"delete": _keyword{
|
|
||||||
token: DELETE,
|
|
||||||
},
|
|
||||||
"switch": _keyword{
|
|
||||||
token: SWITCH,
|
|
||||||
},
|
|
||||||
"default": _keyword{
|
|
||||||
token: DEFAULT,
|
|
||||||
},
|
|
||||||
"finally": _keyword{
|
|
||||||
token: FINALLY,
|
|
||||||
},
|
|
||||||
"function": _keyword{
|
|
||||||
token: FUNCTION,
|
|
||||||
},
|
|
||||||
"continue": _keyword{
|
|
||||||
token: CONTINUE,
|
|
||||||
},
|
|
||||||
"debugger": _keyword{
|
|
||||||
token: DEBUGGER,
|
|
||||||
},
|
|
||||||
"instanceof": _keyword{
|
|
||||||
token: INSTANCEOF,
|
|
||||||
},
|
|
||||||
"const": _keyword{
|
|
||||||
token: KEYWORD,
|
|
||||||
futureKeyword: true,
|
|
||||||
},
|
|
||||||
"class": _keyword{
|
|
||||||
token: KEYWORD,
|
|
||||||
futureKeyword: true,
|
|
||||||
},
|
|
||||||
"enum": _keyword{
|
|
||||||
token: KEYWORD,
|
|
||||||
futureKeyword: true,
|
|
||||||
},
|
|
||||||
"export": _keyword{
|
|
||||||
token: KEYWORD,
|
|
||||||
futureKeyword: true,
|
|
||||||
},
|
|
||||||
"extends": _keyword{
|
|
||||||
token: KEYWORD,
|
|
||||||
futureKeyword: true,
|
|
||||||
},
|
|
||||||
"import": _keyword{
|
|
||||||
token: KEYWORD,
|
|
||||||
futureKeyword: true,
|
|
||||||
},
|
|
||||||
"super": _keyword{
|
|
||||||
token: KEYWORD,
|
|
||||||
futureKeyword: true,
|
|
||||||
},
|
|
||||||
"implements": _keyword{
|
|
||||||
token: KEYWORD,
|
|
||||||
futureKeyword: true,
|
|
||||||
strict: true,
|
|
||||||
},
|
|
||||||
"interface": _keyword{
|
|
||||||
token: KEYWORD,
|
|
||||||
futureKeyword: true,
|
|
||||||
strict: true,
|
|
||||||
},
|
|
||||||
"let": _keyword{
|
|
||||||
token: KEYWORD,
|
|
||||||
futureKeyword: true,
|
|
||||||
strict: true,
|
|
||||||
},
|
|
||||||
"package": _keyword{
|
|
||||||
token: KEYWORD,
|
|
||||||
futureKeyword: true,
|
|
||||||
strict: true,
|
|
||||||
},
|
|
||||||
"private": _keyword{
|
|
||||||
token: KEYWORD,
|
|
||||||
futureKeyword: true,
|
|
||||||
strict: true,
|
|
||||||
},
|
|
||||||
"protected": _keyword{
|
|
||||||
token: KEYWORD,
|
|
||||||
futureKeyword: true,
|
|
||||||
strict: true,
|
|
||||||
},
|
|
||||||
"public": _keyword{
|
|
||||||
token: KEYWORD,
|
|
||||||
futureKeyword: true,
|
|
||||||
strict: true,
|
|
||||||
},
|
|
||||||
"static": _keyword{
|
|
||||||
token: KEYWORD,
|
|
||||||
futureKeyword: true,
|
|
||||||
strict: true,
|
|
||||||
},
|
|
||||||
}
|
|
|
@ -1,222 +0,0 @@
|
||||||
#!/usr/bin/env perl
|
|
||||||
|
|
||||||
use strict;
|
|
||||||
use warnings;
|
|
||||||
|
|
||||||
my (%token, @order, @keywords);
|
|
||||||
|
|
||||||
{
|
|
||||||
my $keywords;
|
|
||||||
my @const;
|
|
||||||
push @const, <<_END_;
|
|
||||||
package token
|
|
||||||
|
|
||||||
const(
|
|
||||||
_ Token = iota
|
|
||||||
_END_
|
|
||||||
|
|
||||||
for (split m/\n/, <<_END_) {
|
|
||||||
ILLEGAL
|
|
||||||
EOF
|
|
||||||
COMMENT
|
|
||||||
KEYWORD
|
|
||||||
|
|
||||||
STRING
|
|
||||||
BOOLEAN
|
|
||||||
NULL
|
|
||||||
NUMBER
|
|
||||||
IDENTIFIER
|
|
||||||
|
|
||||||
PLUS +
|
|
||||||
MINUS -
|
|
||||||
MULTIPLY *
|
|
||||||
SLASH /
|
|
||||||
REMAINDER %
|
|
||||||
|
|
||||||
AND &
|
|
||||||
OR |
|
|
||||||
EXCLUSIVE_OR ^
|
|
||||||
SHIFT_LEFT <<
|
|
||||||
SHIFT_RIGHT >>
|
|
||||||
UNSIGNED_SHIFT_RIGHT >>>
|
|
||||||
AND_NOT &^
|
|
||||||
|
|
||||||
ADD_ASSIGN +=
|
|
||||||
SUBTRACT_ASSIGN -=
|
|
||||||
MULTIPLY_ASSIGN *=
|
|
||||||
QUOTIENT_ASSIGN /=
|
|
||||||
REMAINDER_ASSIGN %=
|
|
||||||
|
|
||||||
AND_ASSIGN &=
|
|
||||||
OR_ASSIGN |=
|
|
||||||
EXCLUSIVE_OR_ASSIGN ^=
|
|
||||||
SHIFT_LEFT_ASSIGN <<=
|
|
||||||
SHIFT_RIGHT_ASSIGN >>=
|
|
||||||
UNSIGNED_SHIFT_RIGHT_ASSIGN >>>=
|
|
||||||
AND_NOT_ASSIGN &^=
|
|
||||||
|
|
||||||
LOGICAL_AND &&
|
|
||||||
LOGICAL_OR ||
|
|
||||||
INCREMENT ++
|
|
||||||
DECREMENT --
|
|
||||||
|
|
||||||
EQUAL ==
|
|
||||||
STRICT_EQUAL ===
|
|
||||||
LESS <
|
|
||||||
GREATER >
|
|
||||||
ASSIGN =
|
|
||||||
NOT !
|
|
||||||
|
|
||||||
BITWISE_NOT ~
|
|
||||||
|
|
||||||
NOT_EQUAL !=
|
|
||||||
STRICT_NOT_EQUAL !==
|
|
||||||
LESS_OR_EQUAL <=
|
|
||||||
GREATER_OR_EQUAL <=
|
|
||||||
|
|
||||||
LEFT_PARENTHESIS (
|
|
||||||
LEFT_BRACKET [
|
|
||||||
LEFT_BRACE {
|
|
||||||
COMMA ,
|
|
||||||
PERIOD .
|
|
||||||
|
|
||||||
RIGHT_PARENTHESIS )
|
|
||||||
RIGHT_BRACKET ]
|
|
||||||
RIGHT_BRACE }
|
|
||||||
SEMICOLON ;
|
|
||||||
COLON :
|
|
||||||
QUESTION_MARK ?
|
|
||||||
|
|
||||||
firstKeyword
|
|
||||||
IF
|
|
||||||
IN
|
|
||||||
DO
|
|
||||||
|
|
||||||
VAR
|
|
||||||
FOR
|
|
||||||
NEW
|
|
||||||
TRY
|
|
||||||
|
|
||||||
THIS
|
|
||||||
ELSE
|
|
||||||
CASE
|
|
||||||
VOID
|
|
||||||
WITH
|
|
||||||
|
|
||||||
WHILE
|
|
||||||
BREAK
|
|
||||||
CATCH
|
|
||||||
THROW
|
|
||||||
|
|
||||||
RETURN
|
|
||||||
TYPEOF
|
|
||||||
DELETE
|
|
||||||
SWITCH
|
|
||||||
|
|
||||||
DEFAULT
|
|
||||||
FINALLY
|
|
||||||
|
|
||||||
FUNCTION
|
|
||||||
CONTINUE
|
|
||||||
DEBUGGER
|
|
||||||
|
|
||||||
INSTANCEOF
|
|
||||||
lastKeyword
|
|
||||||
_END_
|
|
||||||
chomp;
|
|
||||||
|
|
||||||
next if m/^\s*#/;
|
|
||||||
|
|
||||||
my ($name, $symbol) = m/(\w+)\s*(\S+)?/;
|
|
||||||
|
|
||||||
if (defined $symbol) {
|
|
||||||
push @order, $name;
|
|
||||||
push @const, "$name // $symbol";
|
|
||||||
$token{$name} = $symbol;
|
|
||||||
} elsif (defined $name) {
|
|
||||||
$keywords ||= $name eq 'firstKeyword';
|
|
||||||
|
|
||||||
push @const, $name;
|
|
||||||
#$const[-1] .= " Token = iota" if 2 == @const;
|
|
||||||
if ($name =~ m/^([A-Z]+)/) {
|
|
||||||
push @keywords, $name if $keywords;
|
|
||||||
push @order, $name;
|
|
||||||
if ($token{SEMICOLON}) {
|
|
||||||
$token{$name} = lc $1;
|
|
||||||
} else {
|
|
||||||
$token{$name} = $name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
push @const, "";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
push @const, ")";
|
|
||||||
print join "\n", @const, "";
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
print <<_END_;
|
|
||||||
|
|
||||||
var token2string = [...]string{
|
|
||||||
_END_
|
|
||||||
for my $name (@order) {
|
|
||||||
print "$name: \"$token{$name}\",\n";
|
|
||||||
}
|
|
||||||
print <<_END_;
|
|
||||||
}
|
|
||||||
_END_
|
|
||||||
|
|
||||||
print <<_END_;
|
|
||||||
|
|
||||||
var keywordTable = map[string]_keyword{
|
|
||||||
_END_
|
|
||||||
for my $name (@keywords) {
|
|
||||||
print <<_END_
|
|
||||||
"@{[ lc $name ]}": _keyword{
|
|
||||||
token: $name,
|
|
||||||
},
|
|
||||||
_END_
|
|
||||||
}
|
|
||||||
|
|
||||||
for my $name (qw/
|
|
||||||
const
|
|
||||||
class
|
|
||||||
enum
|
|
||||||
export
|
|
||||||
extends
|
|
||||||
import
|
|
||||||
super
|
|
||||||
/) {
|
|
||||||
print <<_END_
|
|
||||||
"$name": _keyword{
|
|
||||||
token: KEYWORD,
|
|
||||||
futureKeyword: true,
|
|
||||||
},
|
|
||||||
_END_
|
|
||||||
}
|
|
||||||
|
|
||||||
for my $name (qw/
|
|
||||||
implements
|
|
||||||
interface
|
|
||||||
let
|
|
||||||
package
|
|
||||||
private
|
|
||||||
protected
|
|
||||||
public
|
|
||||||
static
|
|
||||||
/) {
|
|
||||||
print <<_END_
|
|
||||||
"$name": _keyword{
|
|
||||||
token: KEYWORD,
|
|
||||||
futureKeyword: true,
|
|
||||||
strict: true,
|
|
||||||
},
|
|
||||||
_END_
|
|
||||||
}
|
|
||||||
|
|
||||||
print <<_END_;
|
|
||||||
}
|
|
||||||
_END_
|
|
||||||
}
|
|
|
@ -1,9 +0,0 @@
|
||||||
package otto
|
|
||||||
|
|
||||||
func (runtime *_runtime) newErrorObject(message Value) *_object {
|
|
||||||
self := runtime.newClassObject("Error")
|
|
||||||
if message.IsDefined() {
|
|
||||||
self.defineProperty("message", toValue_string(toString(message)), 0111, false)
|
|
||||||
}
|
|
||||||
return self
|
|
||||||
}
|
|
|
@ -1,276 +0,0 @@
|
||||||
package otto
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
type _functionObject struct {
|
|
||||||
call _callFunction
|
|
||||||
construct _constructFunction
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self _functionObject) source(object *_object) string {
|
|
||||||
return self.call.Source(object)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self0 _functionObject) clone(clone *_clone) _functionObject {
|
|
||||||
return _functionObject{
|
|
||||||
clone.callFunction(self0.call),
|
|
||||||
self0.construct,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (runtime *_runtime) newNativeFunctionObject(name string, native _nativeFunction, length int) *_object {
|
|
||||||
self := runtime.newClassObject("Function")
|
|
||||||
self.value = _functionObject{
|
|
||||||
call: newNativeCallFunction(native),
|
|
||||||
construct: defaultConstructFunction,
|
|
||||||
}
|
|
||||||
self.defineProperty("length", toValue_int(length), 0000, false)
|
|
||||||
return self
|
|
||||||
}
|
|
||||||
|
|
||||||
func (runtime *_runtime) newBoundFunctionObject(target *_object, this Value, argumentList []Value) *_object {
|
|
||||||
self := runtime.newClassObject("Function")
|
|
||||||
self.value = _functionObject{
|
|
||||||
call: newBoundCallFunction(target, this, argumentList),
|
|
||||||
construct: newBoundConstructFunction(target),
|
|
||||||
}
|
|
||||||
length := int(toInt32(target.get("length")))
|
|
||||||
length -= len(argumentList)
|
|
||||||
if length < 0 {
|
|
||||||
length = 0
|
|
||||||
}
|
|
||||||
self.defineProperty("length", toValue_int(length), 0000, false)
|
|
||||||
self.defineProperty("caller", UndefinedValue(), 0000, false) // TODO Should throw a TypeError
|
|
||||||
self.defineProperty("arguments", UndefinedValue(), 0000, false) // TODO Should throw a TypeError
|
|
||||||
return self
|
|
||||||
}
|
|
||||||
|
|
||||||
func (runtime *_runtime) newBoundFunction(target *_object, this Value, argumentList []Value) *_object {
|
|
||||||
self := runtime.newBoundFunctionObject(target, this, argumentList)
|
|
||||||
self.prototype = runtime.Global.FunctionPrototype
|
|
||||||
prototype := runtime.newObject()
|
|
||||||
self.defineProperty("prototype", toValue_object(prototype), 0100, false)
|
|
||||||
prototype.defineProperty("constructor", toValue_object(self), 0100, false)
|
|
||||||
return self
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_object) functionValue() _functionObject {
|
|
||||||
value, _ := self.value.(_functionObject)
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_object) Call(this Value, argumentList ...interface{}) Value {
|
|
||||||
if self.functionValue().call == nil {
|
|
||||||
panic(newTypeError("%v is not a function", toValue_object(self)))
|
|
||||||
}
|
|
||||||
return self.runtime.Call(self, this, self.runtime.toValueArray(argumentList...), false)
|
|
||||||
// ... -> runtime -> self.Function.Call.Dispatch -> ...
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_object) Construct(this Value, argumentList ...interface{}) Value {
|
|
||||||
function := self.functionValue()
|
|
||||||
if function.call == nil {
|
|
||||||
panic(newTypeError("%v is not a function", toValue_object(self)))
|
|
||||||
}
|
|
||||||
if function.construct == nil {
|
|
||||||
panic(newTypeError("%v is not a constructor", toValue_object(self)))
|
|
||||||
}
|
|
||||||
return function.construct(self, this, self.runtime.toValueArray(argumentList...))
|
|
||||||
}
|
|
||||||
|
|
||||||
func defaultConstructFunction(self *_object, this Value, argumentList []Value) Value {
|
|
||||||
newObject := self.runtime.newObject()
|
|
||||||
newObject.class = "Object"
|
|
||||||
prototypeValue := self.get("prototype")
|
|
||||||
if !prototypeValue.IsObject() {
|
|
||||||
prototypeValue = toValue_object(self.runtime.Global.ObjectPrototype)
|
|
||||||
}
|
|
||||||
newObject.prototype = prototypeValue._object()
|
|
||||||
newObjectValue := toValue_object(newObject)
|
|
||||||
result := self.Call(newObjectValue, argumentList)
|
|
||||||
if result.IsObject() {
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
return newObjectValue
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_object) callGet(this Value) Value {
|
|
||||||
return self.runtime.Call(self, this, []Value(nil), false)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_object) callSet(this Value, value Value) {
|
|
||||||
self.runtime.Call(self, this, []Value{value}, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 15.3.5.3
|
|
||||||
func (self *_object) HasInstance(of Value) bool {
|
|
||||||
if self.functionValue().call == nil {
|
|
||||||
// We should not have a HasInstance method
|
|
||||||
panic(newTypeError())
|
|
||||||
}
|
|
||||||
if !of.IsObject() {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
prototype := self.get("prototype")
|
|
||||||
if !prototype.IsObject() {
|
|
||||||
panic(newTypeError())
|
|
||||||
}
|
|
||||||
prototypeObject := prototype._object()
|
|
||||||
|
|
||||||
value := of._object().prototype
|
|
||||||
for value != nil {
|
|
||||||
if value == prototypeObject {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
value = value.prototype
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
type _nativeFunction func(FunctionCall) Value
|
|
||||||
|
|
||||||
// _constructFunction
|
|
||||||
type _constructFunction func(*_object, Value, []Value) Value
|
|
||||||
|
|
||||||
// _callFunction
|
|
||||||
type _callFunction interface {
|
|
||||||
Dispatch(*_object, *_functionEnvironment, *_runtime, Value, []Value, bool) Value
|
|
||||||
Source(*_object) string
|
|
||||||
ScopeEnvironment() _environment
|
|
||||||
clone(clone *_clone) _callFunction
|
|
||||||
}
|
|
||||||
|
|
||||||
// _nativeCallFunction
|
|
||||||
type _nativeCallFunction struct {
|
|
||||||
name string
|
|
||||||
function _nativeFunction
|
|
||||||
}
|
|
||||||
|
|
||||||
func newNativeCallFunction(native _nativeFunction) _nativeCallFunction {
|
|
||||||
return _nativeCallFunction{"", native}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self _nativeCallFunction) Dispatch(_ *_object, _ *_functionEnvironment, runtime *_runtime, this Value, argumentList []Value, evalHint bool) Value {
|
|
||||||
return self.function(FunctionCall{
|
|
||||||
runtime: runtime,
|
|
||||||
evalHint: evalHint,
|
|
||||||
|
|
||||||
This: this,
|
|
||||||
ArgumentList: argumentList,
|
|
||||||
Otto: runtime.Otto,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self _nativeCallFunction) ScopeEnvironment() _environment {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self _nativeCallFunction) Source(*_object) string {
|
|
||||||
return fmt.Sprintf("function %s() { [native code] }", self.name)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self0 _nativeCallFunction) clone(clone *_clone) _callFunction {
|
|
||||||
return self0
|
|
||||||
}
|
|
||||||
|
|
||||||
// _boundCallFunction
|
|
||||||
type _boundCallFunction struct {
|
|
||||||
target *_object
|
|
||||||
this Value
|
|
||||||
argumentList []Value
|
|
||||||
}
|
|
||||||
|
|
||||||
func newBoundCallFunction(target *_object, this Value, argumentList []Value) *_boundCallFunction {
|
|
||||||
self := &_boundCallFunction{
|
|
||||||
target: target,
|
|
||||||
this: this,
|
|
||||||
argumentList: argumentList,
|
|
||||||
}
|
|
||||||
return self
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self _boundCallFunction) Dispatch(_ *_object, _ *_functionEnvironment, runtime *_runtime, this Value, argumentList []Value, _ bool) Value {
|
|
||||||
argumentList = append(self.argumentList, argumentList...)
|
|
||||||
return runtime.Call(self.target, self.this, argumentList, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self _boundCallFunction) ScopeEnvironment() _environment {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self _boundCallFunction) Source(*_object) string {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self0 _boundCallFunction) clone(clone *_clone) _callFunction {
|
|
||||||
return _boundCallFunction{
|
|
||||||
target: clone.object(self0.target),
|
|
||||||
this: clone.value(self0.this),
|
|
||||||
argumentList: clone.valueArray(self0.argumentList),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func newBoundConstructFunction(target *_object) _constructFunction {
|
|
||||||
// This is not exactly as described in 15.3.4.5.2, we let [[Call]] supply the
|
|
||||||
// bound arguments, etc.
|
|
||||||
return func(self *_object, this Value, argumentList []Value) Value {
|
|
||||||
switch value := target.value.(type) {
|
|
||||||
case _functionObject:
|
|
||||||
return value.construct(self, this, argumentList)
|
|
||||||
}
|
|
||||||
panic(newTypeError())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// FunctionCall{}
|
|
||||||
|
|
||||||
// FunctionCall is an encapsulation of a JavaScript function call.
|
|
||||||
type FunctionCall struct {
|
|
||||||
runtime *_runtime
|
|
||||||
_thisObject *_object
|
|
||||||
evalHint bool
|
|
||||||
|
|
||||||
This Value
|
|
||||||
ArgumentList []Value
|
|
||||||
Otto *Otto
|
|
||||||
}
|
|
||||||
|
|
||||||
// Argument will return the value of the argument at the given index.
|
|
||||||
//
|
|
||||||
// If no such argument exists, undefined is returned.
|
|
||||||
func (self FunctionCall) Argument(index int) Value {
|
|
||||||
return valueOfArrayIndex(self.ArgumentList, index)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self FunctionCall) getArgument(index int) (Value, bool) {
|
|
||||||
return getValueOfArrayIndex(self.ArgumentList, index)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self FunctionCall) slice(index int) []Value {
|
|
||||||
if index < len(self.ArgumentList) {
|
|
||||||
return self.ArgumentList[index:]
|
|
||||||
}
|
|
||||||
return []Value{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *FunctionCall) thisObject() *_object {
|
|
||||||
if self._thisObject == nil {
|
|
||||||
this := self.runtime.GetValue(self.This) // FIXME Is this right?
|
|
||||||
self._thisObject = self.runtime.toObject(this)
|
|
||||||
}
|
|
||||||
return self._thisObject
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *FunctionCall) thisClassObject(class string) *_object {
|
|
||||||
thisObject := self.thisObject()
|
|
||||||
if thisObject.class != class {
|
|
||||||
panic(newTypeError())
|
|
||||||
}
|
|
||||||
return self._thisObject
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self FunctionCall) toObject(value Value) *_object {
|
|
||||||
return self.runtime.toObject(value)
|
|
||||||
}
|
|
|
@ -1,157 +0,0 @@
|
||||||
package otto
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/robertkrimen/otto/ast"
|
|
||||||
)
|
|
||||||
|
|
||||||
type _reference interface {
|
|
||||||
GetBase() interface{} // GetBase
|
|
||||||
GetName() string // GetReferencedName
|
|
||||||
IsStrict() bool // IsStrictReference
|
|
||||||
IsUnresolvable() bool // IsUnresolvableReference
|
|
||||||
IsPropertyReference() bool // IsPropertyReference
|
|
||||||
GetValue() Value // GetValue
|
|
||||||
PutValue(Value) bool // PutValue
|
|
||||||
Delete() bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reference
|
|
||||||
|
|
||||||
type _referenceDefault struct {
|
|
||||||
name string
|
|
||||||
strict bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self _referenceDefault) GetName() string {
|
|
||||||
return self.name
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self _referenceDefault) IsStrict() bool {
|
|
||||||
return self.strict
|
|
||||||
}
|
|
||||||
|
|
||||||
// PropertyReference
|
|
||||||
|
|
||||||
type _propertyReference struct {
|
|
||||||
_referenceDefault
|
|
||||||
Base *_object
|
|
||||||
}
|
|
||||||
|
|
||||||
func newPropertyReference(base *_object, name string, strict bool) *_propertyReference {
|
|
||||||
return &_propertyReference{
|
|
||||||
Base: base,
|
|
||||||
_referenceDefault: _referenceDefault{
|
|
||||||
name: name,
|
|
||||||
strict: strict,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_propertyReference) GetBase() interface{} {
|
|
||||||
return self.Base
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_propertyReference) IsUnresolvable() bool {
|
|
||||||
return self.Base == nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_propertyReference) IsPropertyReference() bool {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_propertyReference) GetValue() Value {
|
|
||||||
if self.Base == nil {
|
|
||||||
panic(newReferenceError("notDefined", self.name))
|
|
||||||
}
|
|
||||||
return self.Base.get(self.name)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_propertyReference) PutValue(value Value) bool {
|
|
||||||
if self.Base == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
self.Base.put(self.name, value, self.IsStrict())
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_propertyReference) Delete() bool {
|
|
||||||
if self.Base == nil {
|
|
||||||
// TODO Throw an error if strict
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return self.Base.delete(self.name, self.IsStrict())
|
|
||||||
}
|
|
||||||
|
|
||||||
// ArgumentReference
|
|
||||||
|
|
||||||
func newArgumentReference(base *_object, name string, strict bool) *_propertyReference {
|
|
||||||
if base == nil {
|
|
||||||
panic(hereBeDragons())
|
|
||||||
}
|
|
||||||
return newPropertyReference(base, name, strict)
|
|
||||||
}
|
|
||||||
|
|
||||||
type _environmentReference struct {
|
|
||||||
_referenceDefault
|
|
||||||
Base _environment
|
|
||||||
node ast.Node
|
|
||||||
}
|
|
||||||
|
|
||||||
func newEnvironmentReference(base _environment, name string, strict bool, node ast.Node) *_environmentReference {
|
|
||||||
return &_environmentReference{
|
|
||||||
Base: base,
|
|
||||||
_referenceDefault: _referenceDefault{
|
|
||||||
name: name,
|
|
||||||
strict: strict,
|
|
||||||
},
|
|
||||||
node: node,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_environmentReference) GetBase() interface{} {
|
|
||||||
return self.Base
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_environmentReference) IsUnresolvable() bool {
|
|
||||||
return self.Base == nil // The base (an environment) will never be nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_environmentReference) IsPropertyReference() bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_environmentReference) GetValue() Value {
|
|
||||||
if self.Base == nil {
|
|
||||||
// This should never be reached, but just in case
|
|
||||||
}
|
|
||||||
return self.Base.GetValue(self.name, self.IsStrict())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_environmentReference) PutValue(value Value) bool {
|
|
||||||
if self.Base == nil {
|
|
||||||
// This should never be reached, but just in case
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
self.Base.SetValue(self.name, value, self.IsStrict())
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_environmentReference) Delete() bool {
|
|
||||||
if self.Base == nil {
|
|
||||||
// This should never be reached, but just in case
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return self.Base.DeleteBinding(self.name)
|
|
||||||
}
|
|
||||||
|
|
||||||
// getIdentifierReference
|
|
||||||
|
|
||||||
func getIdentifierReference(environment _environment, name string, strict bool) _reference {
|
|
||||||
if environment == nil {
|
|
||||||
return newPropertyReference(nil, name, strict)
|
|
||||||
}
|
|
||||||
if environment.HasBinding(name) {
|
|
||||||
return environment.newReference(name, strict)
|
|
||||||
}
|
|
||||||
return getIdentifierReference(environment.Outer(), name, strict)
|
|
||||||
}
|
|
|
@ -6,8 +6,8 @@ TESTS := \
|
||||||
~
|
~
|
||||||
|
|
||||||
TEST := -v --run
|
TEST := -v --run
|
||||||
TEST := -v --run Test\($(subst $(eval) ,\|,$(TESTS))\)
|
|
||||||
TEST := -v
|
TEST := -v
|
||||||
|
TEST := -v --run Test\($(subst $(eval) ,\|,$(TESTS))\)
|
||||||
TEST := .
|
TEST := .
|
||||||
|
|
||||||
test: parser inline.go
|
test: parser inline.go
|
|
@ -60,7 +60,7 @@ Set a Go function
|
||||||
|
|
||||||
vm.Set("sayHello", func(call otto.FunctionCall) otto.Value {
|
vm.Set("sayHello", func(call otto.FunctionCall) otto.Value {
|
||||||
fmt.Printf("Hello, %s.\n", call.Argument(0).String())
|
fmt.Printf("Hello, %s.\n", call.Argument(0).String())
|
||||||
return otto.UndefinedValue()
|
return otto.Value{}
|
||||||
})
|
})
|
||||||
|
|
||||||
Set a Go function that returns something useful
|
Set a Go function that returns something useful
|
||||||
|
@ -139,7 +139,6 @@ For more information: http://github.com/robertkrimen/otto/tree/master/underscore
|
||||||
The following are some limitations with otto:
|
The following are some limitations with otto:
|
||||||
|
|
||||||
* "use strict" will parse, but does nothing.
|
* "use strict" will parse, but does nothing.
|
||||||
* Error reporting needs to be improved.
|
|
||||||
* The regular expression engine (re2/regexp) is not fully compatible with the ECMA5 specification.
|
* The regular expression engine (re2/regexp) is not fully compatible with the ECMA5 specification.
|
||||||
|
|
||||||
|
|
||||||
|
@ -205,16 +204,18 @@ the interrupt channel to do this:
|
||||||
}
|
}
|
||||||
fmt.Fprintf(os.Stderr, "Ran code successfully: %v\n", duration)
|
fmt.Fprintf(os.Stderr, "Ran code successfully: %v\n", duration)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
vm := otto.New()
|
vm := otto.New()
|
||||||
vm.Interrupt = make(chan func())
|
vm.Interrupt = make(chan func(), 1) // The buffer prevents blocking
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
time.Sleep(2 * time.Second) // Stop after two seconds
|
time.Sleep(2 * time.Second) // Stop after two seconds
|
||||||
vm.Interrupt <- func() {
|
vm.Interrupt <- func() {
|
||||||
panic(halt)
|
panic(halt)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
vm.Run(unsafe) // Here be dragons (risky code)
|
vm.Run(unsafe) // Here be dragons (risky code)
|
||||||
vm.Interrupt = nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Where is setTimeout/setInterval?
|
Where is setTimeout/setInterval?
|
||||||
|
@ -242,6 +243,36 @@ Here is some more discussion of the issue:
|
||||||
var ErrVersion = errors.New("version mismatch")
|
var ErrVersion = errors.New("version mismatch")
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### type Error
|
||||||
|
|
||||||
|
```go
|
||||||
|
type Error struct {
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
An Error represents a runtime error, e.g. a TypeError, a ReferenceError, etc.
|
||||||
|
|
||||||
|
#### func (Error) Error
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (err Error) Error() string
|
||||||
|
```
|
||||||
|
Error returns a description of the error
|
||||||
|
|
||||||
|
TypeError: 'def' is not a function
|
||||||
|
|
||||||
|
#### func (Error) String
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (err Error) String() string
|
||||||
|
```
|
||||||
|
String returns a description of the error and a trace of where the error
|
||||||
|
occurred.
|
||||||
|
|
||||||
|
TypeError: 'def' is not a function
|
||||||
|
at xyz (<anonymous>:3:9)
|
||||||
|
at <anonymous>:7:1/
|
||||||
|
|
||||||
#### type FunctionCall
|
#### type FunctionCall
|
||||||
|
|
||||||
```go
|
```go
|
||||||
|
@ -416,16 +447,16 @@ error if there was a problem during compilation.
|
||||||
#### func (*Otto) Copy
|
#### func (*Otto) Copy
|
||||||
|
|
||||||
```go
|
```go
|
||||||
func (self *Otto) Copy() *Otto
|
func (in *Otto) Copy() *Otto
|
||||||
```
|
```
|
||||||
Copy will create a copy/clone of the runtime.
|
Copy will create a copy/clone of the runtime.
|
||||||
|
|
||||||
Copy is useful for saving some processing time when creating many similar
|
Copy is useful for saving some time when creating many similar runtimes.
|
||||||
runtimes.
|
|
||||||
|
|
||||||
This implementation is alpha-ish, and works by introspecting every part of the
|
This method works by walking the original runtime and cloning each object,
|
||||||
runtime and reallocating and then relinking everything back together. Please
|
scope, stash, etc. into a new runtime.
|
||||||
report if you notice any inadvertent sharing of data between copies.
|
|
||||||
|
Be on the lookout for memory leaks or inadvertent sharing of resources.
|
||||||
|
|
||||||
#### func (Otto) Get
|
#### func (Otto) Get
|
||||||
|
|
||||||
|
@ -562,12 +593,10 @@ NullValue will return a Value representing null.
|
||||||
func ToValue(value interface{}) (Value, error)
|
func ToValue(value interface{}) (Value, error)
|
||||||
```
|
```
|
||||||
ToValue will convert an interface{} value to a value digestible by
|
ToValue will convert an interface{} value to a value digestible by
|
||||||
otto/JavaScript This function will not work for advanced types (struct, map,
|
otto/JavaScript
|
||||||
slice/array, etc.) and you probably should not use it.
|
|
||||||
|
|
||||||
ToValue may be deprecated and removed in the near future.
|
This function will not work for advanced types (struct, map, slice/array, etc.)
|
||||||
|
and you should use Otto.ToValue instead.
|
||||||
Try Otto.ToValue for a replacement.
|
|
||||||
|
|
||||||
#### func TrueValue
|
#### func TrueValue
|
||||||
|
|
||||||
|
@ -630,15 +659,13 @@ func (self Value) Export() (interface{}, error)
|
||||||
Export will attempt to convert the value to a Go representation and return it
|
Export will attempt to convert the value to a Go representation and return it
|
||||||
via an interface{} kind.
|
via an interface{} kind.
|
||||||
|
|
||||||
WARNING: The interface function will be changing soon to:
|
Export returns an error, but it will always be nil. It is present for backwards
|
||||||
|
compatibility.
|
||||||
|
|
||||||
Export() interface{}
|
If a reasonable conversion is not possible, then the original value is returned.
|
||||||
|
|
||||||
If a reasonable conversion is not possible, then the original result is
|
undefined -> nil (FIXME?: Should be Value{})
|
||||||
returned.
|
null -> nil
|
||||||
|
|
||||||
undefined -> otto.Value (UndefinedValue())
|
|
||||||
null -> interface{}(nil)
|
|
||||||
boolean -> bool
|
boolean -> bool
|
||||||
number -> A number type (int, float32, uint64, ...)
|
number -> A number type (int, float32, uint64, ...)
|
||||||
string -> string
|
string -> string
|
|
@ -55,7 +55,7 @@ func Test_issue13(t *testing.T) {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
is(result.toString(), "Xyzzy,42,def,ghi")
|
is(result.string(), "Xyzzy,42,def,ghi")
|
||||||
|
|
||||||
anything := struct {
|
anything := struct {
|
||||||
Abc interface{}
|
Abc interface{}
|
||||||
|
@ -231,12 +231,12 @@ func Test_issue24(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
vm.Set("abc", testStruct{Abc: true, Ghi: "Nothing happens."})
|
vm.Set("abc", _abcStruct{Abc: true, Ghi: "Nothing happens."})
|
||||||
value, err := vm.Get("abc")
|
value, err := vm.Get("abc")
|
||||||
is(err, nil)
|
is(err, nil)
|
||||||
export, _ := value.Export()
|
export, _ := value.Export()
|
||||||
{
|
{
|
||||||
value, valid := export.(testStruct)
|
value, valid := export.(_abcStruct)
|
||||||
is(valid, true)
|
is(valid, true)
|
||||||
|
|
||||||
is(value.Abc, true)
|
is(value.Abc, true)
|
||||||
|
@ -245,12 +245,12 @@ func Test_issue24(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
vm.Set("abc", &testStruct{Abc: true, Ghi: "Nothing happens."})
|
vm.Set("abc", &_abcStruct{Abc: true, Ghi: "Nothing happens."})
|
||||||
value, err := vm.Get("abc")
|
value, err := vm.Get("abc")
|
||||||
is(err, nil)
|
is(err, nil)
|
||||||
export, _ := value.Export()
|
export, _ := value.Export()
|
||||||
{
|
{
|
||||||
value, valid := export.(*testStruct)
|
value, valid := export.(*_abcStruct)
|
||||||
is(valid, true)
|
is(valid, true)
|
||||||
|
|
||||||
is(value.Abc, true)
|
is(value.Abc, true)
|
||||||
|
@ -313,10 +313,22 @@ func Test_issue64(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_issue73(t *testing.T) {
|
||||||
|
tt(t, func() {
|
||||||
|
test, vm := test()
|
||||||
|
|
||||||
|
vm.Set("abc", [4]int{3, 2, 1, 0})
|
||||||
|
|
||||||
|
test(`
|
||||||
|
var def = [ 0, 1, 2, 3 ];
|
||||||
|
JSON.stringify(def) + JSON.stringify(abc);
|
||||||
|
`, "[0,1,2,3][3,2,1,0]")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func Test_7_3_1(t *testing.T) {
|
func Test_7_3_1(t *testing.T) {
|
||||||
tt(t, func() {
|
tt(t, func() {
|
||||||
test(`
|
test(`
|
||||||
|
|
||||||
eval("var test7_3_1\u2028abc = 66;");
|
eval("var test7_3_1\u2028abc = 66;");
|
||||||
[ abc, typeof test7_3_1 ];
|
[ abc, typeof test7_3_1 ];
|
||||||
`, "66,undefined")
|
`, "66,undefined")
|
||||||
|
@ -430,13 +442,13 @@ def"
|
||||||
test(`
|
test(`
|
||||||
var abc = 0;
|
var abc = 0;
|
||||||
do {
|
do {
|
||||||
if(typeof(def) === "function"){
|
if(typeof(def) === "function"){
|
||||||
abc = -1;
|
abc = -1;
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
abc = 1;
|
abc = 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} while(function def(){});
|
} while(function def(){});
|
||||||
abc;
|
abc;
|
||||||
`, 1)
|
`, 1)
|
||||||
|
@ -502,3 +514,104 @@ def"
|
||||||
`, "1,0,\t abc def,\t abc ")
|
`, "1,0,\t abc def,\t abc ")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_issue79(t *testing.T) {
|
||||||
|
tt(t, func() {
|
||||||
|
test, vm := test()
|
||||||
|
|
||||||
|
vm.Set("abc", []_abcStruct{
|
||||||
|
{
|
||||||
|
Ghi: "一",
|
||||||
|
Def: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Def: 3,
|
||||||
|
Ghi: "三",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Def: 2,
|
||||||
|
Ghi: "二",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Def: 4,
|
||||||
|
Ghi: "四",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
test(`
|
||||||
|
abc.sort(function(a,b){ return b.Def-a.Def });
|
||||||
|
def = [];
|
||||||
|
for (i = 0; i < abc.length; i++) {
|
||||||
|
def.push(abc[i].String())
|
||||||
|
}
|
||||||
|
def;
|
||||||
|
`, "四,三,二,一")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_issue80(t *testing.T) {
|
||||||
|
tt(t, func() {
|
||||||
|
test, _ := test()
|
||||||
|
|
||||||
|
test(`
|
||||||
|
JSON.stringify([
|
||||||
|
1401868959,
|
||||||
|
14018689591,
|
||||||
|
140186895901,
|
||||||
|
1401868959001,
|
||||||
|
14018689590001,
|
||||||
|
140186895900001,
|
||||||
|
1401868959000001,
|
||||||
|
1401868959000001.5,
|
||||||
|
14018689590000001,
|
||||||
|
140186895900000001,
|
||||||
|
1401868959000000001,
|
||||||
|
14018689590000000001,
|
||||||
|
140186895900000000001,
|
||||||
|
140186895900000000001.5
|
||||||
|
]);
|
||||||
|
`, "[1401868959,14018689591,140186895901,1401868959001,14018689590001,140186895900001,1401868959000001,1.4018689590000015e+15,14018689590000001,140186895900000001,1401868959000000001,1.401868959e+19,1.401868959e+20,1.401868959e+20]")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_issue87(t *testing.T) {
|
||||||
|
tt(t, func() {
|
||||||
|
test, vm := test()
|
||||||
|
|
||||||
|
test(`
|
||||||
|
var def = 0;
|
||||||
|
abc: {
|
||||||
|
for (;;) {
|
||||||
|
def = !1;
|
||||||
|
break abc;
|
||||||
|
}
|
||||||
|
def = !0;
|
||||||
|
}
|
||||||
|
def;
|
||||||
|
`, false)
|
||||||
|
|
||||||
|
_, err := vm.Run(`
|
||||||
|
/*
|
||||||
|
CryptoJS v3.1.2
|
||||||
|
code.google.com/p/crypto-js
|
||||||
|
(c) 2009-2013 by Jeff Mott. All rights reserved.
|
||||||
|
code.google.com/p/crypto-js/wiki/License
|
||||||
|
*/
|
||||||
|
var CryptoJS=CryptoJS||function(h,s){var f={},g=f.lib={},q=function(){},m=g.Base={extend:function(a){q.prototype=this;var c=new q;a&&c.mixIn(a);c.hasOwnProperty("init")||(c.init=function(){c.$super.init.apply(this,arguments)});c.init.prototype=c;c.$super=this;return c},create:function(){var a=this.extend();a.init.apply(a,arguments);return a},init:function(){},mixIn:function(a){for(var c in a)a.hasOwnProperty(c)&&(this[c]=a[c]);a.hasOwnProperty("toString")&&(this.toString=a.toString)},clone:function(){return this.init.prototype.extend(this)}},
|
||||||
|
r=g.WordArray=m.extend({init:function(a,c){a=this.words=a||[];this.sigBytes=c!=s?c:4*a.length},toString:function(a){return(a||k).stringify(this)},concat:function(a){var c=this.words,d=a.words,b=this.sigBytes;a=a.sigBytes;this.clamp();if(b%4)for(var e=0;e<a;e++)c[b+e>>>2]|=(d[e>>>2]>>>24-8*(e%4)&255)<<24-8*((b+e)%4);else if(65535<d.length)for(e=0;e<a;e+=4)c[b+e>>>2]=d[e>>>2];else c.push.apply(c,d);this.sigBytes+=a;return this},clamp:function(){var a=this.words,c=this.sigBytes;a[c>>>2]&=4294967295<<
|
||||||
|
32-8*(c%4);a.length=h.ceil(c/4)},clone:function(){var a=m.clone.call(this);a.words=this.words.slice(0);return a},random:function(a){for(var c=[],d=0;d<a;d+=4)c.push(4294967296*h.random()|0);return new r.init(c,a)}}),l=f.enc={},k=l.Hex={stringify:function(a){var c=a.words;a=a.sigBytes;for(var d=[],b=0;b<a;b++){var e=c[b>>>2]>>>24-8*(b%4)&255;d.push((e>>>4).toString(16));d.push((e&15).toString(16))}return d.join("")},parse:function(a){for(var c=a.length,d=[],b=0;b<c;b+=2)d[b>>>3]|=parseInt(a.substr(b,
|
||||||
|
2),16)<<24-4*(b%8);return new r.init(d,c/2)}},n=l.Latin1={stringify:function(a){var c=a.words;a=a.sigBytes;for(var d=[],b=0;b<a;b++)d.push(String.fromCharCode(c[b>>>2]>>>24-8*(b%4)&255));return d.join("")},parse:function(a){for(var c=a.length,d=[],b=0;b<c;b++)d[b>>>2]|=(a.charCodeAt(b)&255)<<24-8*(b%4);return new r.init(d,c)}},j=l.Utf8={stringify:function(a){try{return decodeURIComponent(escape(n.stringify(a)))}catch(c){throw Error("Malformed UTF-8 data");}},parse:function(a){return n.parse(unescape(encodeURIComponent(a)))}},
|
||||||
|
u=g.BufferedBlockAlgorithm=m.extend({reset:function(){this._data=new r.init;this._nDataBytes=0},_append:function(a){"string"==typeof a&&(a=j.parse(a));this._data.concat(a);this._nDataBytes+=a.sigBytes},_process:function(a){var c=this._data,d=c.words,b=c.sigBytes,e=this.blockSize,f=b/(4*e),f=a?h.ceil(f):h.max((f|0)-this._minBufferSize,0);a=f*e;b=h.min(4*a,b);if(a){for(var g=0;g<a;g+=e)this._doProcessBlock(d,g);g=d.splice(0,a);c.sigBytes-=b}return new r.init(g,b)},clone:function(){var a=m.clone.call(this);
|
||||||
|
a._data=this._data.clone();return a},_minBufferSize:0});g.Hasher=u.extend({cfg:m.extend(),init:function(a){this.cfg=this.cfg.extend(a);this.reset()},reset:function(){u.reset.call(this);this._doReset()},update:function(a){this._append(a);this._process();return this},finalize:function(a){a&&this._append(a);return this._doFinalize()},blockSize:16,_createHelper:function(a){return function(c,d){return(new a.init(d)).finalize(c)}},_createHmacHelper:function(a){return function(c,d){return(new t.HMAC.init(a,
|
||||||
|
d)).finalize(c)}}});var t=f.algo={};return f}(Math);
|
||||||
|
(function(h){for(var s=CryptoJS,f=s.lib,g=f.WordArray,q=f.Hasher,f=s.algo,m=[],r=[],l=function(a){return 4294967296*(a-(a|0))|0},k=2,n=0;64>n;){var j;a:{j=k;for(var u=h.sqrt(j),t=2;t<=u;t++)if(!(j%t)){j=!1;break a}j=!0}j&&(8>n&&(m[n]=l(h.pow(k,0.5))),r[n]=l(h.pow(k,1/3)),n++);k++}var a=[],f=f.SHA256=q.extend({_doReset:function(){this._hash=new g.init(m.slice(0))},_doProcessBlock:function(c,d){for(var b=this._hash.words,e=b[0],f=b[1],g=b[2],j=b[3],h=b[4],m=b[5],n=b[6],q=b[7],p=0;64>p;p++){if(16>p)a[p]=
|
||||||
|
c[d+p]|0;else{var k=a[p-15],l=a[p-2];a[p]=((k<<25|k>>>7)^(k<<14|k>>>18)^k>>>3)+a[p-7]+((l<<15|l>>>17)^(l<<13|l>>>19)^l>>>10)+a[p-16]}k=q+((h<<26|h>>>6)^(h<<21|h>>>11)^(h<<7|h>>>25))+(h&m^~h&n)+r[p]+a[p];l=((e<<30|e>>>2)^(e<<19|e>>>13)^(e<<10|e>>>22))+(e&f^e&g^f&g);q=n;n=m;m=h;h=j+k|0;j=g;g=f;f=e;e=k+l|0}b[0]=b[0]+e|0;b[1]=b[1]+f|0;b[2]=b[2]+g|0;b[3]=b[3]+j|0;b[4]=b[4]+h|0;b[5]=b[5]+m|0;b[6]=b[6]+n|0;b[7]=b[7]+q|0},_doFinalize:function(){var a=this._data,d=a.words,b=8*this._nDataBytes,e=8*a.sigBytes;
|
||||||
|
d[e>>>5]|=128<<24-e%32;d[(e+64>>>9<<4)+14]=h.floor(b/4294967296);d[(e+64>>>9<<4)+15]=b;a.sigBytes=4*d.length;this._process();return this._hash},clone:function(){var a=q.clone.call(this);a._hash=this._hash.clone();return a}});s.SHA256=q._createHelper(f);s.HmacSHA256=q._createHmacHelper(f)})(Math);
|
||||||
|
(function(){var h=CryptoJS,s=h.enc.Utf8;h.algo.HMAC=h.lib.Base.extend({init:function(f,g){f=this._hasher=new f.init;"string"==typeof g&&(g=s.parse(g));var h=f.blockSize,m=4*h;g.sigBytes>m&&(g=f.finalize(g));g.clamp();for(var r=this._oKey=g.clone(),l=this._iKey=g.clone(),k=r.words,n=l.words,j=0;j<h;j++)k[j]^=1549556828,n[j]^=909522486;r.sigBytes=l.sigBytes=m;this.reset()},reset:function(){var f=this._hasher;f.reset();f.update(this._iKey)},update:function(f){this._hasher.update(f);return this},finalize:function(f){var g=
|
||||||
|
this._hasher;f=g.finalize(f);g.reset();return g.finalize(this._oKey.clone().concat(f))}})})();
|
||||||
|
`)
|
||||||
|
is(err, nil)
|
||||||
|
|
||||||
|
test(`CryptoJS.HmacSHA256("Message", "secret");`, "aa747c502a898200f9e4fa21bac68136f886a0e27aec70ba06daf2e2a5cb5597")
|
||||||
|
})
|
||||||
|
}
|
|
@ -2,7 +2,6 @@ package otto
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
|
||||||
"math"
|
"math"
|
||||||
"net/url"
|
"net/url"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
@ -19,25 +18,26 @@ func builtinGlobal_eval(call FunctionCall) Value {
|
||||||
return src
|
return src
|
||||||
}
|
}
|
||||||
runtime := call.runtime
|
runtime := call.runtime
|
||||||
program := runtime.cmpl_parseOrThrow(toString(src))
|
program := runtime.cmpl_parseOrThrow(src.string())
|
||||||
if call.evalHint {
|
if !call.eval {
|
||||||
runtime.EnterEvalExecutionContext(call)
|
// Not a direct call to eval, so we enter the global ExecutionContext
|
||||||
defer runtime.LeaveExecutionContext()
|
runtime.enterGlobalScope()
|
||||||
|
defer runtime.leaveScope()
|
||||||
}
|
}
|
||||||
returnValue := runtime.cmpl_evaluate_nodeProgram(program)
|
returnValue := runtime.cmpl_evaluate_nodeProgram(program, true)
|
||||||
if returnValue.isEmpty() {
|
if returnValue.isEmpty() {
|
||||||
return UndefinedValue()
|
return Value{}
|
||||||
}
|
}
|
||||||
return returnValue
|
return returnValue
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinGlobal_isNaN(call FunctionCall) Value {
|
func builtinGlobal_isNaN(call FunctionCall) Value {
|
||||||
value := toFloat(call.Argument(0))
|
value := call.Argument(0).float64()
|
||||||
return toValue_bool(math.IsNaN(value))
|
return toValue_bool(math.IsNaN(value))
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinGlobal_isFinite(call FunctionCall) Value {
|
func builtinGlobal_isFinite(call FunctionCall) Value {
|
||||||
value := toFloat(call.Argument(0))
|
value := call.Argument(0).float64()
|
||||||
return toValue_bool(!math.IsNaN(value) && !math.IsInf(value, 0))
|
return toValue_bool(!math.IsNaN(value) && !math.IsInf(value, 0))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,7 +70,7 @@ func digitValue(chr rune) int {
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinGlobal_parseInt(call FunctionCall) Value {
|
func builtinGlobal_parseInt(call FunctionCall) Value {
|
||||||
input := strings.TrimSpace(toString(call.Argument(0)))
|
input := strings.TrimSpace(call.Argument(0).string())
|
||||||
if len(input) == 0 {
|
if len(input) == 0 {
|
||||||
return NaNValue()
|
return NaNValue()
|
||||||
}
|
}
|
||||||
|
@ -153,7 +153,7 @@ var parseFloat_matchValid = regexp.MustCompile(`[0-9eE\+\-\.]|Infinity`)
|
||||||
|
|
||||||
func builtinGlobal_parseFloat(call FunctionCall) Value {
|
func builtinGlobal_parseFloat(call FunctionCall) Value {
|
||||||
// Caveat emptor: This implementation does NOT match the specification
|
// Caveat emptor: This implementation does NOT match the specification
|
||||||
input := strings.TrimSpace(toString(call.Argument(0)))
|
input := strings.TrimSpace(call.Argument(0).string())
|
||||||
if parseFloat_matchBadSpecial.MatchString(input) {
|
if parseFloat_matchBadSpecial.MatchString(input) {
|
||||||
return NaNValue()
|
return NaNValue()
|
||||||
}
|
}
|
||||||
|
@ -185,7 +185,7 @@ func _builtinGlobal_encodeURI(call FunctionCall, escape *regexp.Regexp) Value {
|
||||||
case []uint16:
|
case []uint16:
|
||||||
input = vl
|
input = vl
|
||||||
default:
|
default:
|
||||||
input = utf16.Encode([]rune(toString(value)))
|
input = utf16.Encode([]rune(value.string()))
|
||||||
}
|
}
|
||||||
if len(input) == 0 {
|
if len(input) == 0 {
|
||||||
return toValue_string("")
|
return toValue_string("")
|
||||||
|
@ -197,18 +197,17 @@ func _builtinGlobal_encodeURI(call FunctionCall, escape *regexp.Regexp) Value {
|
||||||
value := input[index]
|
value := input[index]
|
||||||
decode := utf16.Decode(input[index : index+1])
|
decode := utf16.Decode(input[index : index+1])
|
||||||
if value >= 0xDC00 && value <= 0xDFFF {
|
if value >= 0xDC00 && value <= 0xDFFF {
|
||||||
panic(newURIError("URI malformed"))
|
panic(call.runtime.panicURIError("URI malformed"))
|
||||||
}
|
}
|
||||||
if value >= 0xD800 && value <= 0xDBFF {
|
if value >= 0xD800 && value <= 0xDBFF {
|
||||||
index += 1
|
index += 1
|
||||||
if index >= length {
|
if index >= length {
|
||||||
panic(newURIError("URI malformed"))
|
panic(call.runtime.panicURIError("URI malformed"))
|
||||||
}
|
}
|
||||||
// input = ..., value, value1, ...
|
// input = ..., value, value1, ...
|
||||||
value = value
|
|
||||||
value1 := input[index]
|
value1 := input[index]
|
||||||
if value1 < 0xDC00 || value1 > 0xDFFF {
|
if value1 < 0xDC00 || value1 > 0xDFFF {
|
||||||
panic(newURIError("URI malformed"))
|
panic(call.runtime.panicURIError("URI malformed"))
|
||||||
}
|
}
|
||||||
decode = []rune{((rune(value) - 0xD800) * 0x400) + (rune(value1) - 0xDC00) + 0x10000}
|
decode = []rune{((rune(value) - 0xD800) * 0x400) + (rune(value1) - 0xDC00) + 0x10000}
|
||||||
}
|
}
|
||||||
|
@ -257,17 +256,17 @@ func _decodeURI(input string, reserve bool) (string, bool) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinGlobal_decodeURI(call FunctionCall) Value {
|
func builtinGlobal_decodeURI(call FunctionCall) Value {
|
||||||
output, err := _decodeURI(toString(call.Argument(0)), true)
|
output, err := _decodeURI(call.Argument(0).string(), true)
|
||||||
if err {
|
if err {
|
||||||
panic(newURIError("URI malformed"))
|
panic(call.runtime.panicURIError("URI malformed"))
|
||||||
}
|
}
|
||||||
return toValue_string(output)
|
return toValue_string(output)
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinGlobal_decodeURIComponent(call FunctionCall) Value {
|
func builtinGlobal_decodeURIComponent(call FunctionCall) Value {
|
||||||
output, err := _decodeURI(toString(call.Argument(0)), false)
|
output, err := _decodeURI(call.Argument(0).string(), false)
|
||||||
if err {
|
if err {
|
||||||
panic(newURIError("URI malformed"))
|
panic(call.runtime.panicURIError("URI malformed"))
|
||||||
}
|
}
|
||||||
return toValue_string(output)
|
return toValue_string(output)
|
||||||
}
|
}
|
||||||
|
@ -346,48 +345,9 @@ func builtin_unescape(input string) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinGlobal_escape(call FunctionCall) Value {
|
func builtinGlobal_escape(call FunctionCall) Value {
|
||||||
return toValue_string(builtin_escape(toString(call.Argument(0))))
|
return toValue_string(builtin_escape(call.Argument(0).string()))
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinGlobal_unescape(call FunctionCall) Value {
|
func builtinGlobal_unescape(call FunctionCall) Value {
|
||||||
return toValue_string(builtin_unescape(toString(call.Argument(0))))
|
return toValue_string(builtin_unescape(call.Argument(0).string()))
|
||||||
}
|
|
||||||
|
|
||||||
// Error
|
|
||||||
|
|
||||||
func builtinError(call FunctionCall) Value {
|
|
||||||
return toValue_object(call.runtime.newError("", call.Argument(0)))
|
|
||||||
}
|
|
||||||
|
|
||||||
func builtinNewError(self *_object, _ Value, argumentList []Value) Value {
|
|
||||||
return toValue_object(self.runtime.newError("", valueOfArrayIndex(argumentList, 0)))
|
|
||||||
}
|
|
||||||
|
|
||||||
func builtinError_toString(call FunctionCall) Value {
|
|
||||||
thisObject := call.thisObject()
|
|
||||||
if thisObject == nil {
|
|
||||||
panic(newTypeError())
|
|
||||||
}
|
|
||||||
|
|
||||||
name := "Error"
|
|
||||||
nameValue := thisObject.get("name")
|
|
||||||
if nameValue.IsDefined() {
|
|
||||||
name = toString(nameValue)
|
|
||||||
}
|
|
||||||
|
|
||||||
message := ""
|
|
||||||
messageValue := thisObject.get("message")
|
|
||||||
if messageValue.IsDefined() {
|
|
||||||
message = toString(messageValue)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(name) == 0 {
|
|
||||||
return toValue_string(message)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(message) == 0 {
|
|
||||||
return toValue_string(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
return toValue_string(fmt.Sprintf("%s: %s", name, message))
|
|
||||||
}
|
}
|
|
@ -11,7 +11,7 @@ func builtinArray(call FunctionCall) Value {
|
||||||
return toValue_object(builtinNewArrayNative(call.runtime, call.ArgumentList))
|
return toValue_object(builtinNewArrayNative(call.runtime, call.ArgumentList))
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinNewArray(self *_object, _ Value, argumentList []Value) Value {
|
func builtinNewArray(self *_object, argumentList []Value) Value {
|
||||||
return toValue_object(builtinNewArrayNative(self.runtime, argumentList))
|
return toValue_object(builtinNewArrayNative(self.runtime, argumentList))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ func builtinNewArrayNative(runtime *_runtime, argumentList []Value) *_object {
|
||||||
if len(argumentList) == 1 {
|
if len(argumentList) == 1 {
|
||||||
firstArgument := argumentList[0]
|
firstArgument := argumentList[0]
|
||||||
if firstArgument.IsNumber() {
|
if firstArgument.IsNumber() {
|
||||||
return runtime.newArray(arrayUint32(firstArgument))
|
return runtime.newArray(arrayUint32(runtime, firstArgument))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return runtime.newArrayOf(argumentList)
|
return runtime.newArrayOf(argumentList)
|
||||||
|
@ -30,7 +30,7 @@ func builtinArray_toString(call FunctionCall) Value {
|
||||||
join := thisObject.get("join")
|
join := thisObject.get("join")
|
||||||
if join.isCallable() {
|
if join.isCallable() {
|
||||||
join := join._object()
|
join := join._object()
|
||||||
return join.Call(call.This, call.ArgumentList)
|
return join.call(call.This, call.ArgumentList, false, nativeFrame)
|
||||||
}
|
}
|
||||||
return builtinObject_toString(call)
|
return builtinObject_toString(call)
|
||||||
}
|
}
|
||||||
|
@ -46,15 +46,15 @@ func builtinArray_toLocaleString(call FunctionCall) Value {
|
||||||
for index := int64(0); index < length; index += 1 {
|
for index := int64(0); index < length; index += 1 {
|
||||||
value := thisObject.get(arrayIndexToString(index))
|
value := thisObject.get(arrayIndexToString(index))
|
||||||
stringValue := ""
|
stringValue := ""
|
||||||
switch value._valueType {
|
switch value.kind {
|
||||||
case valueEmpty, valueUndefined, valueNull:
|
case valueEmpty, valueUndefined, valueNull:
|
||||||
default:
|
default:
|
||||||
object := call.runtime.toObject(value)
|
object := call.runtime.toObject(value)
|
||||||
toLocaleString := object.get("toLocaleString")
|
toLocaleString := object.get("toLocaleString")
|
||||||
if !toLocaleString.isCallable() {
|
if !toLocaleString.isCallable() {
|
||||||
panic(newTypeError())
|
panic(call.runtime.panicTypeError())
|
||||||
}
|
}
|
||||||
stringValue = toLocaleString.call(toValue_object(object)).toString()
|
stringValue = toLocaleString.call(call.runtime, toValue_object(object)).string()
|
||||||
}
|
}
|
||||||
stringList = append(stringList, stringValue)
|
stringList = append(stringList, stringValue)
|
||||||
}
|
}
|
||||||
|
@ -66,11 +66,11 @@ func builtinArray_concat(call FunctionCall) Value {
|
||||||
valueArray := []Value{}
|
valueArray := []Value{}
|
||||||
source := append([]Value{toValue_object(thisObject)}, call.ArgumentList...)
|
source := append([]Value{toValue_object(thisObject)}, call.ArgumentList...)
|
||||||
for _, item := range source {
|
for _, item := range source {
|
||||||
switch item._valueType {
|
switch item.kind {
|
||||||
case valueObject:
|
case valueObject:
|
||||||
object := item._object()
|
object := item._object()
|
||||||
if isArray(object) {
|
if isArray(object) {
|
||||||
length := toInteger(object.get("length")).value
|
length := object.get("length").number().int64
|
||||||
for index := int64(0); index < length; index += 1 {
|
for index := int64(0); index < length; index += 1 {
|
||||||
name := strconv.FormatInt(index, 10)
|
name := strconv.FormatInt(index, 10)
|
||||||
if object.hasProperty(name) {
|
if object.hasProperty(name) {
|
||||||
|
@ -94,7 +94,7 @@ func builtinArray_shift(call FunctionCall) Value {
|
||||||
length := int64(toUint32(thisObject.get("length")))
|
length := int64(toUint32(thisObject.get("length")))
|
||||||
if 0 == length {
|
if 0 == length {
|
||||||
thisObject.put("length", toValue_int64(0), true)
|
thisObject.put("length", toValue_int64(0), true)
|
||||||
return UndefinedValue()
|
return Value{}
|
||||||
}
|
}
|
||||||
first := thisObject.get("0")
|
first := thisObject.get("0")
|
||||||
for index := int64(1); index < length; index++ {
|
for index := int64(1); index < length; index++ {
|
||||||
|
@ -130,7 +130,7 @@ func builtinArray_pop(call FunctionCall) Value {
|
||||||
length := int64(toUint32(thisObject.get("length")))
|
length := int64(toUint32(thisObject.get("length")))
|
||||||
if 0 == length {
|
if 0 == length {
|
||||||
thisObject.put("length", toValue_uint32(0), true)
|
thisObject.put("length", toValue_uint32(0), true)
|
||||||
return UndefinedValue()
|
return Value{}
|
||||||
}
|
}
|
||||||
last := thisObject.get(arrayIndexToString(length - 1))
|
last := thisObject.get(arrayIndexToString(length - 1))
|
||||||
thisObject.delete(arrayIndexToString(length-1), true)
|
thisObject.delete(arrayIndexToString(length-1), true)
|
||||||
|
@ -143,7 +143,7 @@ func builtinArray_join(call FunctionCall) Value {
|
||||||
{
|
{
|
||||||
argument := call.Argument(0)
|
argument := call.Argument(0)
|
||||||
if argument.IsDefined() {
|
if argument.IsDefined() {
|
||||||
separator = toString(argument)
|
separator = argument.string()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
thisObject := call.thisObject()
|
thisObject := call.thisObject()
|
||||||
|
@ -155,10 +155,10 @@ func builtinArray_join(call FunctionCall) Value {
|
||||||
for index := int64(0); index < length; index += 1 {
|
for index := int64(0); index < length; index += 1 {
|
||||||
value := thisObject.get(arrayIndexToString(index))
|
value := thisObject.get(arrayIndexToString(index))
|
||||||
stringValue := ""
|
stringValue := ""
|
||||||
switch value._valueType {
|
switch value.kind {
|
||||||
case valueEmpty, valueUndefined, valueNull:
|
case valueEmpty, valueUndefined, valueNull:
|
||||||
default:
|
default:
|
||||||
stringValue = toString(value)
|
stringValue = value.string()
|
||||||
}
|
}
|
||||||
stringList = append(stringList, stringValue)
|
stringList = append(stringList, stringValue)
|
||||||
}
|
}
|
||||||
|
@ -370,8 +370,8 @@ func sortCompare(thisObject *_object, index0, index1 uint, compare *_object) int
|
||||||
}
|
}
|
||||||
|
|
||||||
if compare == nil {
|
if compare == nil {
|
||||||
j.value = toString(x)
|
j.value = x.string()
|
||||||
k.value = toString(y)
|
k.value = y.string()
|
||||||
|
|
||||||
if j.value == k.value {
|
if j.value == k.value {
|
||||||
return 0
|
return 0
|
||||||
|
@ -382,7 +382,7 @@ func sortCompare(thisObject *_object, index0, index1 uint, compare *_object) int
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
return int(toInt32(compare.Call(UndefinedValue(), []Value{x, y})))
|
return int(toInt32(compare.call(Value{}, []Value{x, y}, false, nativeFrame)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func arraySortSwap(thisObject *_object, index0, index1 uint) {
|
func arraySortSwap(thisObject *_object, index0, index1 uint) {
|
||||||
|
@ -447,7 +447,7 @@ func builtinArray_sort(call FunctionCall) Value {
|
||||||
compare := compareValue._object()
|
compare := compareValue._object()
|
||||||
if compareValue.IsUndefined() {
|
if compareValue.IsUndefined() {
|
||||||
} else if !compareValue.isCallable() {
|
} else if !compareValue.isCallable() {
|
||||||
panic(newTypeError())
|
panic(call.runtime.panicTypeError())
|
||||||
}
|
}
|
||||||
if length > 1 {
|
if length > 1 {
|
||||||
arraySortQuickSort(thisObject, 0, length-1, compare)
|
arraySortQuickSort(thisObject, 0, length-1, compare)
|
||||||
|
@ -464,7 +464,7 @@ func builtinArray_indexOf(call FunctionCall) Value {
|
||||||
if length := int64(toUint32(thisObject.get("length"))); length > 0 {
|
if length := int64(toUint32(thisObject.get("length"))); length > 0 {
|
||||||
index := int64(0)
|
index := int64(0)
|
||||||
if len(call.ArgumentList) > 1 {
|
if len(call.ArgumentList) > 1 {
|
||||||
index = toInteger(call.Argument(1)).value
|
index = call.Argument(1).number().int64
|
||||||
}
|
}
|
||||||
if index < 0 {
|
if index < 0 {
|
||||||
if index += length; index < 0 {
|
if index += length; index < 0 {
|
||||||
|
@ -492,7 +492,7 @@ func builtinArray_lastIndexOf(call FunctionCall) Value {
|
||||||
length := int64(toUint32(thisObject.get("length")))
|
length := int64(toUint32(thisObject.get("length")))
|
||||||
index := length - 1
|
index := length - 1
|
||||||
if len(call.ArgumentList) > 1 {
|
if len(call.ArgumentList) > 1 {
|
||||||
index = toInteger(call.Argument(1)).value
|
index = call.Argument(1).number().int64
|
||||||
}
|
}
|
||||||
if 0 > index {
|
if 0 > index {
|
||||||
index += length
|
index += length
|
||||||
|
@ -523,15 +523,15 @@ func builtinArray_every(call FunctionCall) Value {
|
||||||
callThis := call.Argument(1)
|
callThis := call.Argument(1)
|
||||||
for index := int64(0); index < length; index++ {
|
for index := int64(0); index < length; index++ {
|
||||||
if key := arrayIndexToString(index); thisObject.hasProperty(key) {
|
if key := arrayIndexToString(index); thisObject.hasProperty(key) {
|
||||||
if value := thisObject.get(key); iterator.call(callThis, value, toValue_int64(index), this).isTrue() {
|
if value := thisObject.get(key); iterator.call(call.runtime, callThis, value, toValue_int64(index), this).bool() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
return FalseValue()
|
return falseValue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return TrueValue()
|
return trueValue
|
||||||
}
|
}
|
||||||
panic(newTypeError())
|
panic(call.runtime.panicTypeError())
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinArray_some(call FunctionCall) Value {
|
func builtinArray_some(call FunctionCall) Value {
|
||||||
|
@ -542,14 +542,14 @@ func builtinArray_some(call FunctionCall) Value {
|
||||||
callThis := call.Argument(1)
|
callThis := call.Argument(1)
|
||||||
for index := int64(0); index < length; index++ {
|
for index := int64(0); index < length; index++ {
|
||||||
if key := arrayIndexToString(index); thisObject.hasProperty(key) {
|
if key := arrayIndexToString(index); thisObject.hasProperty(key) {
|
||||||
if value := thisObject.get(key); iterator.call(callThis, value, toValue_int64(index), this).isTrue() {
|
if value := thisObject.get(key); iterator.call(call.runtime, callThis, value, toValue_int64(index), this).bool() {
|
||||||
return TrueValue()
|
return trueValue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return FalseValue()
|
return falseValue
|
||||||
}
|
}
|
||||||
panic(newTypeError())
|
panic(call.runtime.panicTypeError())
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinArray_forEach(call FunctionCall) Value {
|
func builtinArray_forEach(call FunctionCall) Value {
|
||||||
|
@ -560,12 +560,12 @@ func builtinArray_forEach(call FunctionCall) Value {
|
||||||
callThis := call.Argument(1)
|
callThis := call.Argument(1)
|
||||||
for index := int64(0); index < length; index++ {
|
for index := int64(0); index < length; index++ {
|
||||||
if key := arrayIndexToString(index); thisObject.hasProperty(key) {
|
if key := arrayIndexToString(index); thisObject.hasProperty(key) {
|
||||||
iterator.call(callThis, thisObject.get(key), toValue_int64(index), this)
|
iterator.call(call.runtime, callThis, thisObject.get(key), toValue_int64(index), this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return UndefinedValue()
|
return Value{}
|
||||||
}
|
}
|
||||||
panic(newTypeError())
|
panic(call.runtime.panicTypeError())
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinArray_map(call FunctionCall) Value {
|
func builtinArray_map(call FunctionCall) Value {
|
||||||
|
@ -577,14 +577,14 @@ func builtinArray_map(call FunctionCall) Value {
|
||||||
values := make([]Value, length)
|
values := make([]Value, length)
|
||||||
for index := int64(0); index < length; index++ {
|
for index := int64(0); index < length; index++ {
|
||||||
if key := arrayIndexToString(index); thisObject.hasProperty(key) {
|
if key := arrayIndexToString(index); thisObject.hasProperty(key) {
|
||||||
values[index] = iterator.call(callThis, thisObject.get(key), index, this)
|
values[index] = iterator.call(call.runtime, callThis, thisObject.get(key), index, this)
|
||||||
} else {
|
} else {
|
||||||
values[index] = UndefinedValue()
|
values[index] = Value{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return toValue_object(call.runtime.newArrayOf(values))
|
return toValue_object(call.runtime.newArrayOf(values))
|
||||||
}
|
}
|
||||||
panic(newTypeError())
|
panic(call.runtime.panicTypeError())
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinArray_filter(call FunctionCall) Value {
|
func builtinArray_filter(call FunctionCall) Value {
|
||||||
|
@ -597,14 +597,14 @@ func builtinArray_filter(call FunctionCall) Value {
|
||||||
for index := int64(0); index < length; index++ {
|
for index := int64(0); index < length; index++ {
|
||||||
if key := arrayIndexToString(index); thisObject.hasProperty(key) {
|
if key := arrayIndexToString(index); thisObject.hasProperty(key) {
|
||||||
value := thisObject.get(key)
|
value := thisObject.get(key)
|
||||||
if iterator.call(callThis, value, index, this).isTrue() {
|
if iterator.call(call.runtime, callThis, value, index, this).bool() {
|
||||||
values = append(values, value)
|
values = append(values, value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return toValue_object(call.runtime.newArrayOf(values))
|
return toValue_object(call.runtime.newArrayOf(values))
|
||||||
}
|
}
|
||||||
panic(newTypeError())
|
panic(call.runtime.panicTypeError())
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinArray_reduce(call FunctionCall) Value {
|
func builtinArray_reduce(call FunctionCall) Value {
|
||||||
|
@ -630,13 +630,13 @@ func builtinArray_reduce(call FunctionCall) Value {
|
||||||
}
|
}
|
||||||
for ; index < length; index++ {
|
for ; index < length; index++ {
|
||||||
if key := arrayIndexToString(index); thisObject.hasProperty(key) {
|
if key := arrayIndexToString(index); thisObject.hasProperty(key) {
|
||||||
accumulator = iterator.call(UndefinedValue(), accumulator, thisObject.get(key), key, this)
|
accumulator = iterator.call(call.runtime, Value{}, accumulator, thisObject.get(key), key, this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return accumulator
|
return accumulator
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
panic(newTypeError())
|
panic(call.runtime.panicTypeError())
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinArray_reduceRight(call FunctionCall) Value {
|
func builtinArray_reduceRight(call FunctionCall) Value {
|
||||||
|
@ -662,11 +662,11 @@ func builtinArray_reduceRight(call FunctionCall) Value {
|
||||||
}
|
}
|
||||||
for ; index >= 0; index-- {
|
for ; index >= 0; index-- {
|
||||||
if key := arrayIndexToString(index); thisObject.hasProperty(key) {
|
if key := arrayIndexToString(index); thisObject.hasProperty(key) {
|
||||||
accumulator = iterator.call(UndefinedValue(), accumulator, thisObject.get(key), key, this)
|
accumulator = iterator.call(call.runtime, Value{}, accumulator, thisObject.get(key), key, this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return accumulator
|
return accumulator
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
panic(newTypeError())
|
panic(call.runtime.panicTypeError())
|
||||||
}
|
}
|
|
@ -3,10 +3,10 @@ package otto
|
||||||
// Boolean
|
// Boolean
|
||||||
|
|
||||||
func builtinBoolean(call FunctionCall) Value {
|
func builtinBoolean(call FunctionCall) Value {
|
||||||
return toValue_bool(toBoolean(call.Argument(0)))
|
return toValue_bool(call.Argument(0).bool())
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinNewBoolean(self *_object, _ Value, argumentList []Value) Value {
|
func builtinNewBoolean(self *_object, argumentList []Value) Value {
|
||||||
return toValue_object(self.runtime.newBoolean(valueOfArrayIndex(argumentList, 0)))
|
return toValue_object(self.runtime.newBoolean(valueOfArrayIndex(argumentList, 0)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@ func builtinBoolean_toString(call FunctionCall) Value {
|
||||||
// Will throw a TypeError if ThisObject is not a Boolean
|
// Will throw a TypeError if ThisObject is not a Boolean
|
||||||
value = call.thisClassObject("Boolean").primitiveValue()
|
value = call.thisClassObject("Boolean").primitiveValue()
|
||||||
}
|
}
|
||||||
return toValue_string(toString(value))
|
return toValue_string(value.string())
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinBoolean_valueOf(call FunctionCall) Value {
|
func builtinBoolean_valueOf(call FunctionCall) Value {
|
|
@ -21,12 +21,12 @@ func builtinDate(call FunctionCall) Value {
|
||||||
return toValue_string(date.Time().Format(builtinDate_goDateTimeLayout))
|
return toValue_string(date.Time().Format(builtinDate_goDateTimeLayout))
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinNewDate(self *_object, _ Value, argumentList []Value) Value {
|
func builtinNewDate(self *_object, argumentList []Value) Value {
|
||||||
return toValue_object(self.runtime.newDate(newDateTime(argumentList, Time.Local)))
|
return toValue_object(self.runtime.newDate(newDateTime(argumentList, Time.Local)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinDate_toString(call FunctionCall) Value {
|
func builtinDate_toString(call FunctionCall) Value {
|
||||||
date := dateObjectOf(call.thisObject())
|
date := dateObjectOf(call.runtime, call.thisObject())
|
||||||
if date.isNaN {
|
if date.isNaN {
|
||||||
return toValue_string("Invalid Date")
|
return toValue_string("Invalid Date")
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,7 @@ func builtinDate_toString(call FunctionCall) Value {
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinDate_toDateString(call FunctionCall) Value {
|
func builtinDate_toDateString(call FunctionCall) Value {
|
||||||
date := dateObjectOf(call.thisObject())
|
date := dateObjectOf(call.runtime, call.thisObject())
|
||||||
if date.isNaN {
|
if date.isNaN {
|
||||||
return toValue_string("Invalid Date")
|
return toValue_string("Invalid Date")
|
||||||
}
|
}
|
||||||
|
@ -42,7 +42,7 @@ func builtinDate_toDateString(call FunctionCall) Value {
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinDate_toTimeString(call FunctionCall) Value {
|
func builtinDate_toTimeString(call FunctionCall) Value {
|
||||||
date := dateObjectOf(call.thisObject())
|
date := dateObjectOf(call.runtime, call.thisObject())
|
||||||
if date.isNaN {
|
if date.isNaN {
|
||||||
return toValue_string("Invalid Date")
|
return toValue_string("Invalid Date")
|
||||||
}
|
}
|
||||||
|
@ -50,7 +50,7 @@ func builtinDate_toTimeString(call FunctionCall) Value {
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinDate_toUTCString(call FunctionCall) Value {
|
func builtinDate_toUTCString(call FunctionCall) Value {
|
||||||
date := dateObjectOf(call.thisObject())
|
date := dateObjectOf(call.runtime, call.thisObject())
|
||||||
if date.isNaN {
|
if date.isNaN {
|
||||||
return toValue_string("Invalid Date")
|
return toValue_string("Invalid Date")
|
||||||
}
|
}
|
||||||
|
@ -58,7 +58,7 @@ func builtinDate_toUTCString(call FunctionCall) Value {
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinDate_toISOString(call FunctionCall) Value {
|
func builtinDate_toISOString(call FunctionCall) Value {
|
||||||
date := dateObjectOf(call.thisObject())
|
date := dateObjectOf(call.runtime, call.thisObject())
|
||||||
if date.isNaN {
|
if date.isNaN {
|
||||||
return toValue_string("Invalid Date")
|
return toValue_string("Invalid Date")
|
||||||
}
|
}
|
||||||
|
@ -69,20 +69,21 @@ func builtinDate_toJSON(call FunctionCall) Value {
|
||||||
object := call.thisObject()
|
object := call.thisObject()
|
||||||
value := object.DefaultValue(defaultValueHintNumber) // FIXME object.primitiveNumberValue
|
value := object.DefaultValue(defaultValueHintNumber) // FIXME object.primitiveNumberValue
|
||||||
{ // FIXME value.isFinite
|
{ // FIXME value.isFinite
|
||||||
value := toFloat(value)
|
value := value.float64()
|
||||||
if math.IsNaN(value) || math.IsInf(value, 0) {
|
if math.IsNaN(value) || math.IsInf(value, 0) {
|
||||||
return NullValue()
|
return nullValue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
toISOString := object.get("toISOString")
|
toISOString := object.get("toISOString")
|
||||||
if !toISOString.isCallable() {
|
if !toISOString.isCallable() {
|
||||||
panic(newTypeError())
|
// FIXME
|
||||||
|
panic(call.runtime.panicTypeError())
|
||||||
}
|
}
|
||||||
return toISOString.call(toValue_object(object), []Value{})
|
return toISOString.call(call.runtime, toValue_object(object), []Value{})
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinDate_toGMTString(call FunctionCall) Value {
|
func builtinDate_toGMTString(call FunctionCall) Value {
|
||||||
date := dateObjectOf(call.thisObject())
|
date := dateObjectOf(call.runtime, call.thisObject())
|
||||||
if date.isNaN {
|
if date.isNaN {
|
||||||
return toValue_string("Invalid Date")
|
return toValue_string("Invalid Date")
|
||||||
}
|
}
|
||||||
|
@ -90,7 +91,7 @@ func builtinDate_toGMTString(call FunctionCall) Value {
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinDate_getTime(call FunctionCall) Value {
|
func builtinDate_getTime(call FunctionCall) Value {
|
||||||
date := dateObjectOf(call.thisObject())
|
date := dateObjectOf(call.runtime, call.thisObject())
|
||||||
if date.isNaN {
|
if date.isNaN {
|
||||||
return NaNValue()
|
return NaNValue()
|
||||||
}
|
}
|
||||||
|
@ -101,15 +102,15 @@ func builtinDate_getTime(call FunctionCall) Value {
|
||||||
|
|
||||||
func builtinDate_setTime(call FunctionCall) Value {
|
func builtinDate_setTime(call FunctionCall) Value {
|
||||||
object := call.thisObject()
|
object := call.thisObject()
|
||||||
date := dateObjectOf(object)
|
date := dateObjectOf(call.runtime, call.thisObject())
|
||||||
date.Set(toFloat(call.Argument(0)))
|
date.Set(call.Argument(0).float64())
|
||||||
object.value = date
|
object.value = date
|
||||||
return date.Value()
|
return date.Value()
|
||||||
}
|
}
|
||||||
|
|
||||||
func _builtinDate_beforeSet(call FunctionCall, argumentLimit int, timeLocal bool) (*_object, *_dateObject, *_ecmaTime, []int) {
|
func _builtinDate_beforeSet(call FunctionCall, argumentLimit int, timeLocal bool) (*_object, *_dateObject, *_ecmaTime, []int) {
|
||||||
object := call.thisObject()
|
object := call.thisObject()
|
||||||
date := dateObjectOf(object)
|
date := dateObjectOf(call.runtime, call.thisObject())
|
||||||
if date.isNaN {
|
if date.isNaN {
|
||||||
return nil, nil, nil, nil
|
return nil, nil, nil, nil
|
||||||
}
|
}
|
||||||
|
@ -126,16 +127,14 @@ func _builtinDate_beforeSet(call FunctionCall, argumentLimit int, timeLocal bool
|
||||||
valueList := make([]int, argumentLimit)
|
valueList := make([]int, argumentLimit)
|
||||||
for index := 0; index < argumentLimit; index++ {
|
for index := 0; index < argumentLimit; index++ {
|
||||||
value := call.ArgumentList[index]
|
value := call.ArgumentList[index]
|
||||||
if value.IsNaN() {
|
nm := value.number()
|
||||||
|
switch nm.kind {
|
||||||
|
case numberInteger, numberFloat:
|
||||||
|
default:
|
||||||
object.value = invalidDateObject
|
object.value = invalidDateObject
|
||||||
return nil, nil, nil, nil
|
return nil, nil, nil, nil
|
||||||
}
|
}
|
||||||
integer := toInteger(value)
|
valueList[index] = int(nm.int64)
|
||||||
if !integer.valid() {
|
|
||||||
object.value = invalidDateObject
|
|
||||||
return nil, nil, nil, nil
|
|
||||||
}
|
|
||||||
valueList[index] = int(integer.value)
|
|
||||||
}
|
}
|
||||||
baseTime := date.Time()
|
baseTime := date.Time()
|
||||||
if timeLocal {
|
if timeLocal {
|
||||||
|
@ -146,7 +145,7 @@ func _builtinDate_beforeSet(call FunctionCall, argumentLimit int, timeLocal bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinDate_parse(call FunctionCall) Value {
|
func builtinDate_parse(call FunctionCall) Value {
|
||||||
date := toString(call.Argument(0))
|
date := call.Argument(0).string()
|
||||||
return toValue_float64(dateParse(date))
|
return toValue_float64(dateParse(date))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -161,7 +160,7 @@ func builtinDate_now(call FunctionCall) Value {
|
||||||
|
|
||||||
// This is a placeholder
|
// This is a placeholder
|
||||||
func builtinDate_toLocaleString(call FunctionCall) Value {
|
func builtinDate_toLocaleString(call FunctionCall) Value {
|
||||||
date := dateObjectOf(call.thisObject())
|
date := dateObjectOf(call.runtime, call.thisObject())
|
||||||
if date.isNaN {
|
if date.isNaN {
|
||||||
return toValue_string("Invalid Date")
|
return toValue_string("Invalid Date")
|
||||||
}
|
}
|
||||||
|
@ -170,7 +169,7 @@ func builtinDate_toLocaleString(call FunctionCall) Value {
|
||||||
|
|
||||||
// This is a placeholder
|
// This is a placeholder
|
||||||
func builtinDate_toLocaleDateString(call FunctionCall) Value {
|
func builtinDate_toLocaleDateString(call FunctionCall) Value {
|
||||||
date := dateObjectOf(call.thisObject())
|
date := dateObjectOf(call.runtime, call.thisObject())
|
||||||
if date.isNaN {
|
if date.isNaN {
|
||||||
return toValue_string("Invalid Date")
|
return toValue_string("Invalid Date")
|
||||||
}
|
}
|
||||||
|
@ -179,7 +178,7 @@ func builtinDate_toLocaleDateString(call FunctionCall) Value {
|
||||||
|
|
||||||
// This is a placeholder
|
// This is a placeholder
|
||||||
func builtinDate_toLocaleTimeString(call FunctionCall) Value {
|
func builtinDate_toLocaleTimeString(call FunctionCall) Value {
|
||||||
date := dateObjectOf(call.thisObject())
|
date := dateObjectOf(call.runtime, call.thisObject())
|
||||||
if date.isNaN {
|
if date.isNaN {
|
||||||
return toValue_string("Invalid Date")
|
return toValue_string("Invalid Date")
|
||||||
}
|
}
|
||||||
|
@ -187,7 +186,7 @@ func builtinDate_toLocaleTimeString(call FunctionCall) Value {
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinDate_valueOf(call FunctionCall) Value {
|
func builtinDate_valueOf(call FunctionCall) Value {
|
||||||
date := dateObjectOf(call.thisObject())
|
date := dateObjectOf(call.runtime, call.thisObject())
|
||||||
if date.isNaN {
|
if date.isNaN {
|
||||||
return NaNValue()
|
return NaNValue()
|
||||||
}
|
}
|
||||||
|
@ -197,7 +196,7 @@ func builtinDate_valueOf(call FunctionCall) Value {
|
||||||
func builtinDate_getYear(call FunctionCall) Value {
|
func builtinDate_getYear(call FunctionCall) Value {
|
||||||
// Will throw a TypeError is ThisObject is nil or
|
// Will throw a TypeError is ThisObject is nil or
|
||||||
// does not have Class of "Date"
|
// does not have Class of "Date"
|
||||||
date := dateObjectOf(call.thisObject())
|
date := dateObjectOf(call.runtime, call.thisObject())
|
||||||
if date.isNaN {
|
if date.isNaN {
|
||||||
return NaNValue()
|
return NaNValue()
|
||||||
}
|
}
|
||||||
|
@ -207,7 +206,7 @@ func builtinDate_getYear(call FunctionCall) Value {
|
||||||
func builtinDate_getFullYear(call FunctionCall) Value {
|
func builtinDate_getFullYear(call FunctionCall) Value {
|
||||||
// Will throw a TypeError is ThisObject is nil or
|
// Will throw a TypeError is ThisObject is nil or
|
||||||
// does not have Class of "Date"
|
// does not have Class of "Date"
|
||||||
date := dateObjectOf(call.thisObject())
|
date := dateObjectOf(call.runtime, call.thisObject())
|
||||||
if date.isNaN {
|
if date.isNaN {
|
||||||
return NaNValue()
|
return NaNValue()
|
||||||
}
|
}
|
||||||
|
@ -215,7 +214,7 @@ func builtinDate_getFullYear(call FunctionCall) Value {
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinDate_getUTCFullYear(call FunctionCall) Value {
|
func builtinDate_getUTCFullYear(call FunctionCall) Value {
|
||||||
date := dateObjectOf(call.thisObject())
|
date := dateObjectOf(call.runtime, call.thisObject())
|
||||||
if date.isNaN {
|
if date.isNaN {
|
||||||
return NaNValue()
|
return NaNValue()
|
||||||
}
|
}
|
||||||
|
@ -223,7 +222,7 @@ func builtinDate_getUTCFullYear(call FunctionCall) Value {
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinDate_getMonth(call FunctionCall) Value {
|
func builtinDate_getMonth(call FunctionCall) Value {
|
||||||
date := dateObjectOf(call.thisObject())
|
date := dateObjectOf(call.runtime, call.thisObject())
|
||||||
if date.isNaN {
|
if date.isNaN {
|
||||||
return NaNValue()
|
return NaNValue()
|
||||||
}
|
}
|
||||||
|
@ -231,7 +230,7 @@ func builtinDate_getMonth(call FunctionCall) Value {
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinDate_getUTCMonth(call FunctionCall) Value {
|
func builtinDate_getUTCMonth(call FunctionCall) Value {
|
||||||
date := dateObjectOf(call.thisObject())
|
date := dateObjectOf(call.runtime, call.thisObject())
|
||||||
if date.isNaN {
|
if date.isNaN {
|
||||||
return NaNValue()
|
return NaNValue()
|
||||||
}
|
}
|
||||||
|
@ -239,7 +238,7 @@ func builtinDate_getUTCMonth(call FunctionCall) Value {
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinDate_getDate(call FunctionCall) Value {
|
func builtinDate_getDate(call FunctionCall) Value {
|
||||||
date := dateObjectOf(call.thisObject())
|
date := dateObjectOf(call.runtime, call.thisObject())
|
||||||
if date.isNaN {
|
if date.isNaN {
|
||||||
return NaNValue()
|
return NaNValue()
|
||||||
}
|
}
|
||||||
|
@ -247,7 +246,7 @@ func builtinDate_getDate(call FunctionCall) Value {
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinDate_getUTCDate(call FunctionCall) Value {
|
func builtinDate_getUTCDate(call FunctionCall) Value {
|
||||||
date := dateObjectOf(call.thisObject())
|
date := dateObjectOf(call.runtime, call.thisObject())
|
||||||
if date.isNaN {
|
if date.isNaN {
|
||||||
return NaNValue()
|
return NaNValue()
|
||||||
}
|
}
|
||||||
|
@ -256,7 +255,7 @@ func builtinDate_getUTCDate(call FunctionCall) Value {
|
||||||
|
|
||||||
func builtinDate_getDay(call FunctionCall) Value {
|
func builtinDate_getDay(call FunctionCall) Value {
|
||||||
// Actually day of the week
|
// Actually day of the week
|
||||||
date := dateObjectOf(call.thisObject())
|
date := dateObjectOf(call.runtime, call.thisObject())
|
||||||
if date.isNaN {
|
if date.isNaN {
|
||||||
return NaNValue()
|
return NaNValue()
|
||||||
}
|
}
|
||||||
|
@ -264,7 +263,7 @@ func builtinDate_getDay(call FunctionCall) Value {
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinDate_getUTCDay(call FunctionCall) Value {
|
func builtinDate_getUTCDay(call FunctionCall) Value {
|
||||||
date := dateObjectOf(call.thisObject())
|
date := dateObjectOf(call.runtime, call.thisObject())
|
||||||
if date.isNaN {
|
if date.isNaN {
|
||||||
return NaNValue()
|
return NaNValue()
|
||||||
}
|
}
|
||||||
|
@ -272,7 +271,7 @@ func builtinDate_getUTCDay(call FunctionCall) Value {
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinDate_getHours(call FunctionCall) Value {
|
func builtinDate_getHours(call FunctionCall) Value {
|
||||||
date := dateObjectOf(call.thisObject())
|
date := dateObjectOf(call.runtime, call.thisObject())
|
||||||
if date.isNaN {
|
if date.isNaN {
|
||||||
return NaNValue()
|
return NaNValue()
|
||||||
}
|
}
|
||||||
|
@ -280,7 +279,7 @@ func builtinDate_getHours(call FunctionCall) Value {
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinDate_getUTCHours(call FunctionCall) Value {
|
func builtinDate_getUTCHours(call FunctionCall) Value {
|
||||||
date := dateObjectOf(call.thisObject())
|
date := dateObjectOf(call.runtime, call.thisObject())
|
||||||
if date.isNaN {
|
if date.isNaN {
|
||||||
return NaNValue()
|
return NaNValue()
|
||||||
}
|
}
|
||||||
|
@ -288,7 +287,7 @@ func builtinDate_getUTCHours(call FunctionCall) Value {
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinDate_getMinutes(call FunctionCall) Value {
|
func builtinDate_getMinutes(call FunctionCall) Value {
|
||||||
date := dateObjectOf(call.thisObject())
|
date := dateObjectOf(call.runtime, call.thisObject())
|
||||||
if date.isNaN {
|
if date.isNaN {
|
||||||
return NaNValue()
|
return NaNValue()
|
||||||
}
|
}
|
||||||
|
@ -296,7 +295,7 @@ func builtinDate_getMinutes(call FunctionCall) Value {
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinDate_getUTCMinutes(call FunctionCall) Value {
|
func builtinDate_getUTCMinutes(call FunctionCall) Value {
|
||||||
date := dateObjectOf(call.thisObject())
|
date := dateObjectOf(call.runtime, call.thisObject())
|
||||||
if date.isNaN {
|
if date.isNaN {
|
||||||
return NaNValue()
|
return NaNValue()
|
||||||
}
|
}
|
||||||
|
@ -304,7 +303,7 @@ func builtinDate_getUTCMinutes(call FunctionCall) Value {
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinDate_getSeconds(call FunctionCall) Value {
|
func builtinDate_getSeconds(call FunctionCall) Value {
|
||||||
date := dateObjectOf(call.thisObject())
|
date := dateObjectOf(call.runtime, call.thisObject())
|
||||||
if date.isNaN {
|
if date.isNaN {
|
||||||
return NaNValue()
|
return NaNValue()
|
||||||
}
|
}
|
||||||
|
@ -312,7 +311,7 @@ func builtinDate_getSeconds(call FunctionCall) Value {
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinDate_getUTCSeconds(call FunctionCall) Value {
|
func builtinDate_getUTCSeconds(call FunctionCall) Value {
|
||||||
date := dateObjectOf(call.thisObject())
|
date := dateObjectOf(call.runtime, call.thisObject())
|
||||||
if date.isNaN {
|
if date.isNaN {
|
||||||
return NaNValue()
|
return NaNValue()
|
||||||
}
|
}
|
||||||
|
@ -320,7 +319,7 @@ func builtinDate_getUTCSeconds(call FunctionCall) Value {
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinDate_getMilliseconds(call FunctionCall) Value {
|
func builtinDate_getMilliseconds(call FunctionCall) Value {
|
||||||
date := dateObjectOf(call.thisObject())
|
date := dateObjectOf(call.runtime, call.thisObject())
|
||||||
if date.isNaN {
|
if date.isNaN {
|
||||||
return NaNValue()
|
return NaNValue()
|
||||||
}
|
}
|
||||||
|
@ -328,7 +327,7 @@ func builtinDate_getMilliseconds(call FunctionCall) Value {
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinDate_getUTCMilliseconds(call FunctionCall) Value {
|
func builtinDate_getUTCMilliseconds(call FunctionCall) Value {
|
||||||
date := dateObjectOf(call.thisObject())
|
date := dateObjectOf(call.runtime, call.thisObject())
|
||||||
if date.isNaN {
|
if date.isNaN {
|
||||||
return NaNValue()
|
return NaNValue()
|
||||||
}
|
}
|
||||||
|
@ -336,7 +335,7 @@ func builtinDate_getUTCMilliseconds(call FunctionCall) Value {
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinDate_getTimezoneOffset(call FunctionCall) Value {
|
func builtinDate_getTimezoneOffset(call FunctionCall) Value {
|
||||||
date := dateObjectOf(call.thisObject())
|
date := dateObjectOf(call.runtime, call.thisObject())
|
||||||
if date.isNaN {
|
if date.isNaN {
|
||||||
return NaNValue()
|
return NaNValue()
|
||||||
}
|
}
|
126
Godeps/_workspace/src/github.com/robertkrimen/otto/builtin_error.go
generated
vendored
Normal file
126
Godeps/_workspace/src/github.com/robertkrimen/otto/builtin_error.go
generated
vendored
Normal file
|
@ -0,0 +1,126 @@
|
||||||
|
package otto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
func builtinError(call FunctionCall) Value {
|
||||||
|
return toValue_object(call.runtime.newError("", call.Argument(0)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinNewError(self *_object, argumentList []Value) Value {
|
||||||
|
return toValue_object(self.runtime.newError("", valueOfArrayIndex(argumentList, 0)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinError_toString(call FunctionCall) Value {
|
||||||
|
thisObject := call.thisObject()
|
||||||
|
if thisObject == nil {
|
||||||
|
panic(call.runtime.panicTypeError())
|
||||||
|
}
|
||||||
|
|
||||||
|
name := "Error"
|
||||||
|
nameValue := thisObject.get("name")
|
||||||
|
if nameValue.IsDefined() {
|
||||||
|
name = nameValue.string()
|
||||||
|
}
|
||||||
|
|
||||||
|
message := ""
|
||||||
|
messageValue := thisObject.get("message")
|
||||||
|
if messageValue.IsDefined() {
|
||||||
|
message = messageValue.string()
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(name) == 0 {
|
||||||
|
return toValue_string(message)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(message) == 0 {
|
||||||
|
return toValue_string(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
return toValue_string(fmt.Sprintf("%s: %s", name, message))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (runtime *_runtime) newEvalError(message Value) *_object {
|
||||||
|
self := runtime.newErrorObject("EvalError", message)
|
||||||
|
self.prototype = runtime.global.EvalErrorPrototype
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinEvalError(call FunctionCall) Value {
|
||||||
|
return toValue_object(call.runtime.newEvalError(call.Argument(0)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinNewEvalError(self *_object, argumentList []Value) Value {
|
||||||
|
return toValue_object(self.runtime.newEvalError(valueOfArrayIndex(argumentList, 0)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (runtime *_runtime) newTypeError(message Value) *_object {
|
||||||
|
self := runtime.newErrorObject("TypeError", message)
|
||||||
|
self.prototype = runtime.global.TypeErrorPrototype
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinTypeError(call FunctionCall) Value {
|
||||||
|
return toValue_object(call.runtime.newTypeError(call.Argument(0)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinNewTypeError(self *_object, argumentList []Value) Value {
|
||||||
|
return toValue_object(self.runtime.newTypeError(valueOfArrayIndex(argumentList, 0)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (runtime *_runtime) newRangeError(message Value) *_object {
|
||||||
|
self := runtime.newErrorObject("RangeError", message)
|
||||||
|
self.prototype = runtime.global.RangeErrorPrototype
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinRangeError(call FunctionCall) Value {
|
||||||
|
return toValue_object(call.runtime.newRangeError(call.Argument(0)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinNewRangeError(self *_object, argumentList []Value) Value {
|
||||||
|
return toValue_object(self.runtime.newRangeError(valueOfArrayIndex(argumentList, 0)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (runtime *_runtime) newURIError(message Value) *_object {
|
||||||
|
self := runtime.newErrorObject("URIError", message)
|
||||||
|
self.prototype = runtime.global.URIErrorPrototype
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
func (runtime *_runtime) newReferenceError(message Value) *_object {
|
||||||
|
self := runtime.newErrorObject("ReferenceError", message)
|
||||||
|
self.prototype = runtime.global.ReferenceErrorPrototype
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinReferenceError(call FunctionCall) Value {
|
||||||
|
return toValue_object(call.runtime.newReferenceError(call.Argument(0)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinNewReferenceError(self *_object, argumentList []Value) Value {
|
||||||
|
return toValue_object(self.runtime.newReferenceError(valueOfArrayIndex(argumentList, 0)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (runtime *_runtime) newSyntaxError(message Value) *_object {
|
||||||
|
self := runtime.newErrorObject("SyntaxError", message)
|
||||||
|
self.prototype = runtime.global.SyntaxErrorPrototype
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinSyntaxError(call FunctionCall) Value {
|
||||||
|
return toValue_object(call.runtime.newSyntaxError(call.Argument(0)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinNewSyntaxError(self *_object, argumentList []Value) Value {
|
||||||
|
return toValue_object(self.runtime.newSyntaxError(valueOfArrayIndex(argumentList, 0)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinURIError(call FunctionCall) Value {
|
||||||
|
return toValue_object(call.runtime.newURIError(call.Argument(0)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinNewURIError(self *_object, argumentList []Value) Value {
|
||||||
|
return toValue_object(self.runtime.newURIError(valueOfArrayIndex(argumentList, 0)))
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
package otto
|
package otto
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"unicode"
|
"unicode"
|
||||||
|
@ -14,14 +15,14 @@ func builtinFunction(call FunctionCall) Value {
|
||||||
return toValue_object(builtinNewFunctionNative(call.runtime, call.ArgumentList))
|
return toValue_object(builtinNewFunctionNative(call.runtime, call.ArgumentList))
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinNewFunction(self *_object, _ Value, argumentList []Value) Value {
|
func builtinNewFunction(self *_object, argumentList []Value) Value {
|
||||||
return toValue_object(builtinNewFunctionNative(self.runtime, argumentList))
|
return toValue_object(builtinNewFunctionNative(self.runtime, argumentList))
|
||||||
}
|
}
|
||||||
|
|
||||||
func argumentList2parameterList(argumentList []Value) []string {
|
func argumentList2parameterList(argumentList []Value) []string {
|
||||||
parameterList := make([]string, 0, len(argumentList))
|
parameterList := make([]string, 0, len(argumentList))
|
||||||
for _, value := range argumentList {
|
for _, value := range argumentList {
|
||||||
tmp := strings.FieldsFunc(toString(value), func(chr rune) bool {
|
tmp := strings.FieldsFunc(value.string(), func(chr rune) bool {
|
||||||
return chr == ',' || unicode.IsSpace(chr)
|
return chr == ',' || unicode.IsSpace(chr)
|
||||||
})
|
})
|
||||||
parameterList = append(parameterList, tmp...)
|
parameterList = append(parameterList, tmp...)
|
||||||
|
@ -37,40 +38,51 @@ func builtinNewFunctionNative(runtime *_runtime, argumentList []Value) *_object
|
||||||
if count > 0 {
|
if count > 0 {
|
||||||
tmp := make([]string, 0, count-1)
|
tmp := make([]string, 0, count-1)
|
||||||
for _, value := range argumentList[0 : count-1] {
|
for _, value := range argumentList[0 : count-1] {
|
||||||
tmp = append(tmp, toString(value))
|
tmp = append(tmp, value.string())
|
||||||
}
|
}
|
||||||
parameterList = strings.Join(tmp, ",")
|
parameterList = strings.Join(tmp, ",")
|
||||||
body = toString(argumentList[count-1])
|
body = argumentList[count-1].string()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME
|
||||||
function, err := parser.ParseFunction(parameterList, body)
|
function, err := parser.ParseFunction(parameterList, body)
|
||||||
runtime.parseThrow(err) // Will panic/throw appropriately
|
runtime.parseThrow(err) // Will panic/throw appropriately
|
||||||
cmpl_function := parseExpression(function)
|
cmpl := _compiler{}
|
||||||
|
cmpl_function := cmpl.parseExpression(function)
|
||||||
|
|
||||||
return runtime.newNodeFunction(cmpl_function.(*_nodeFunctionLiteral), runtime.GlobalEnvironment)
|
return runtime.newNodeFunction(cmpl_function.(*_nodeFunctionLiteral), runtime.globalStash)
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinFunction_toString(call FunctionCall) Value {
|
func builtinFunction_toString(call FunctionCall) Value {
|
||||||
object := call.thisClassObject("Function") // Should throw a TypeError unless Function
|
object := call.thisClassObject("Function") // Should throw a TypeError unless Function
|
||||||
return toValue_string(object.value.(_functionObject).source(object))
|
switch fn := object.value.(type) {
|
||||||
|
case _nativeFunctionObject:
|
||||||
|
return toValue_string(fmt.Sprintf("function %s() { [native code] }", fn.name))
|
||||||
|
case _nodeFunctionObject:
|
||||||
|
return toValue_string(fn.node.source)
|
||||||
|
case _bindFunctionObject:
|
||||||
|
return toValue_string("function () { [native code] }")
|
||||||
|
}
|
||||||
|
|
||||||
|
panic(call.runtime.panicTypeError("Function.toString()"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinFunction_apply(call FunctionCall) Value {
|
func builtinFunction_apply(call FunctionCall) Value {
|
||||||
if !call.This.isCallable() {
|
if !call.This.isCallable() {
|
||||||
panic(newTypeError())
|
panic(call.runtime.panicTypeError())
|
||||||
}
|
}
|
||||||
this := call.Argument(0)
|
this := call.Argument(0)
|
||||||
if this.IsUndefined() {
|
if this.IsUndefined() {
|
||||||
// FIXME Not ECMA5
|
// FIXME Not ECMA5
|
||||||
this = toValue_object(call.runtime.GlobalObject)
|
this = toValue_object(call.runtime.globalObject)
|
||||||
}
|
}
|
||||||
argumentList := call.Argument(1)
|
argumentList := call.Argument(1)
|
||||||
switch argumentList._valueType {
|
switch argumentList.kind {
|
||||||
case valueUndefined, valueNull:
|
case valueUndefined, valueNull:
|
||||||
return call.thisObject().Call(this, []Value{})
|
return call.thisObject().call(this, nil, false, nativeFrame)
|
||||||
case valueObject:
|
case valueObject:
|
||||||
default:
|
default:
|
||||||
panic(newTypeError())
|
panic(call.runtime.panicTypeError())
|
||||||
}
|
}
|
||||||
|
|
||||||
arrayObject := argumentList._object()
|
arrayObject := argumentList._object()
|
||||||
|
@ -80,29 +92,29 @@ func builtinFunction_apply(call FunctionCall) Value {
|
||||||
for index := int64(0); index < length; index++ {
|
for index := int64(0); index < length; index++ {
|
||||||
valueArray[index] = arrayObject.get(arrayIndexToString(index))
|
valueArray[index] = arrayObject.get(arrayIndexToString(index))
|
||||||
}
|
}
|
||||||
return thisObject.Call(this, valueArray)
|
return thisObject.call(this, valueArray, false, nativeFrame)
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinFunction_call(call FunctionCall) Value {
|
func builtinFunction_call(call FunctionCall) Value {
|
||||||
if !call.This.isCallable() {
|
if !call.This.isCallable() {
|
||||||
panic(newTypeError())
|
panic(call.runtime.panicTypeError())
|
||||||
}
|
}
|
||||||
thisObject := call.thisObject()
|
thisObject := call.thisObject()
|
||||||
this := call.Argument(0)
|
this := call.Argument(0)
|
||||||
if this.IsUndefined() {
|
if this.IsUndefined() {
|
||||||
// FIXME Not ECMA5
|
// FIXME Not ECMA5
|
||||||
this = toValue_object(call.runtime.GlobalObject)
|
this = toValue_object(call.runtime.globalObject)
|
||||||
}
|
}
|
||||||
if len(call.ArgumentList) >= 1 {
|
if len(call.ArgumentList) >= 1 {
|
||||||
return thisObject.Call(this, call.ArgumentList[1:])
|
return thisObject.call(this, call.ArgumentList[1:], false, nativeFrame)
|
||||||
}
|
}
|
||||||
return thisObject.Call(this, []Value{})
|
return thisObject.call(this, nil, false, nativeFrame)
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinFunction_bind(call FunctionCall) Value {
|
func builtinFunction_bind(call FunctionCall) Value {
|
||||||
target := call.This
|
target := call.This
|
||||||
if !target.isCallable() {
|
if !target.isCallable() {
|
||||||
panic(newTypeError())
|
panic(call.runtime.panicTypeError())
|
||||||
}
|
}
|
||||||
targetObject := target._object()
|
targetObject := target._object()
|
||||||
|
|
||||||
|
@ -110,7 +122,7 @@ func builtinFunction_bind(call FunctionCall) Value {
|
||||||
argumentList := call.slice(1)
|
argumentList := call.slice(1)
|
||||||
if this.IsUndefined() {
|
if this.IsUndefined() {
|
||||||
// FIXME Do this elsewhere?
|
// FIXME Do this elsewhere?
|
||||||
this = toValue_object(call.runtime.GlobalObject)
|
this = toValue_object(call.runtime.globalObject)
|
||||||
}
|
}
|
||||||
|
|
||||||
return toValue_object(call.runtime.newBoundFunction(targetObject, this, argumentList))
|
return toValue_object(call.runtime.newBoundFunction(targetObject, this, argumentList))
|
|
@ -3,7 +3,7 @@ package otto
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"math"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -23,13 +23,13 @@ func builtinJSON_parse(call FunctionCall) Value {
|
||||||
}
|
}
|
||||||
|
|
||||||
var root interface{}
|
var root interface{}
|
||||||
err := json.Unmarshal([]byte(toString(call.Argument(0))), &root)
|
err := json.Unmarshal([]byte(call.Argument(0).string()), &root)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(newSyntaxError(err.Error()))
|
panic(call.runtime.panicSyntaxError(err.Error()))
|
||||||
}
|
}
|
||||||
value, exists := builtinJSON_parseWalk(ctx, root)
|
value, exists := builtinJSON_parseWalk(ctx, root)
|
||||||
if !exists {
|
if !exists {
|
||||||
value = UndefinedValue()
|
value = Value{}
|
||||||
}
|
}
|
||||||
if revive {
|
if revive {
|
||||||
root := ctx.call.runtime.newObject()
|
root := ctx.call.runtime.newObject()
|
||||||
|
@ -65,13 +65,13 @@ func builtinJSON_reviveWalk(ctx _builtinJSON_parseContext, holder *_object, name
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ctx.reviver.call(toValue_object(holder), name, value)
|
return ctx.reviver.call(ctx.call.runtime, toValue_object(holder), name, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinJSON_parseWalk(ctx _builtinJSON_parseContext, rawValue interface{}) (Value, bool) {
|
func builtinJSON_parseWalk(ctx _builtinJSON_parseContext, rawValue interface{}) (Value, bool) {
|
||||||
switch value := rawValue.(type) {
|
switch value := rawValue.(type) {
|
||||||
case nil:
|
case nil:
|
||||||
return NullValue(), true
|
return nullValue, true
|
||||||
case bool:
|
case bool:
|
||||||
return toValue_bool(value), true
|
return toValue_bool(value), true
|
||||||
case string:
|
case string:
|
||||||
|
@ -120,7 +120,7 @@ func builtinJSON_stringify(call FunctionCall) Value {
|
||||||
length = 0
|
length = 0
|
||||||
for index, _ := range propertyList {
|
for index, _ := range propertyList {
|
||||||
value := replacer.get(arrayIndexToString(int64(index)))
|
value := replacer.get(arrayIndexToString(int64(index)))
|
||||||
switch value._valueType {
|
switch value.kind {
|
||||||
case valueObject:
|
case valueObject:
|
||||||
switch value.value.(*_object).class {
|
switch value.value.(*_object).class {
|
||||||
case "String":
|
case "String":
|
||||||
|
@ -133,7 +133,7 @@ func builtinJSON_stringify(call FunctionCall) Value {
|
||||||
default:
|
default:
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
name := toString(value)
|
name := value.string()
|
||||||
if seen[name] {
|
if seen[name] {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -148,24 +148,24 @@ func builtinJSON_stringify(call FunctionCall) Value {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if spaceValue, exists := call.getArgument(2); exists {
|
if spaceValue, exists := call.getArgument(2); exists {
|
||||||
if spaceValue._valueType == valueObject {
|
if spaceValue.kind == valueObject {
|
||||||
switch spaceValue.value.(*_object).class {
|
switch spaceValue.value.(*_object).class {
|
||||||
case "String":
|
case "String":
|
||||||
spaceValue = toValue_string(toString(spaceValue))
|
spaceValue = toValue_string(spaceValue.string())
|
||||||
case "Number":
|
case "Number":
|
||||||
spaceValue = toNumber(spaceValue)
|
spaceValue = spaceValue.numberValue()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
switch spaceValue._valueType {
|
switch spaceValue.kind {
|
||||||
case valueString:
|
case valueString:
|
||||||
value := toString(spaceValue)
|
value := spaceValue.string()
|
||||||
if len(value) > 10 {
|
if len(value) > 10 {
|
||||||
ctx.gap = value[0:10]
|
ctx.gap = value[0:10]
|
||||||
} else {
|
} else {
|
||||||
ctx.gap = value
|
ctx.gap = value
|
||||||
}
|
}
|
||||||
case valueNumber:
|
case valueNumber:
|
||||||
value := toInteger(spaceValue).value
|
value := spaceValue.number().int64
|
||||||
if value > 10 {
|
if value > 10 {
|
||||||
value = 10
|
value = 10
|
||||||
} else if value < 0 {
|
} else if value < 0 {
|
||||||
|
@ -178,11 +178,11 @@ func builtinJSON_stringify(call FunctionCall) Value {
|
||||||
holder.put("", call.Argument(0), false)
|
holder.put("", call.Argument(0), false)
|
||||||
value, exists := builtinJSON_stringifyWalk(ctx, "", holder)
|
value, exists := builtinJSON_stringifyWalk(ctx, "", holder)
|
||||||
if !exists {
|
if !exists {
|
||||||
return UndefinedValue()
|
return Value{}
|
||||||
}
|
}
|
||||||
valueJSON, err := json.Marshal(value)
|
valueJSON, err := json.Marshal(value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(newTypeError(err.Error()))
|
panic(call.runtime.panicTypeError(err.Error()))
|
||||||
}
|
}
|
||||||
if ctx.gap != "" {
|
if ctx.gap != "" {
|
||||||
valueJSON1 := bytes.Buffer{}
|
valueJSON1 := bytes.Buffer{}
|
||||||
|
@ -198,7 +198,7 @@ func builtinJSON_stringifyWalk(ctx _builtinJSON_stringifyContext, key string, ho
|
||||||
if value.IsObject() {
|
if value.IsObject() {
|
||||||
object := value._object()
|
object := value._object()
|
||||||
if toJSON := object.get("toJSON"); toJSON.IsFunction() {
|
if toJSON := object.get("toJSON"); toJSON.IsFunction() {
|
||||||
value = toJSON.call(value, key)
|
value = toJSON.call(ctx.call.runtime, value, key)
|
||||||
} else {
|
} else {
|
||||||
// If the object is a GoStruct or something that implements json.Marshaler
|
// If the object is a GoStruct or something that implements json.Marshaler
|
||||||
if object.objectClass.marshalJSON != nil {
|
if object.objectClass.marshalJSON != nil {
|
||||||
|
@ -211,31 +211,35 @@ func builtinJSON_stringifyWalk(ctx _builtinJSON_stringifyContext, key string, ho
|
||||||
}
|
}
|
||||||
|
|
||||||
if ctx.replacerFunction != nil {
|
if ctx.replacerFunction != nil {
|
||||||
value = (*ctx.replacerFunction).call(toValue_object(holder), key, value)
|
value = (*ctx.replacerFunction).call(ctx.call.runtime, toValue_object(holder), key, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
if value._valueType == valueObject {
|
if value.kind == valueObject {
|
||||||
switch value.value.(*_object).class {
|
switch value.value.(*_object).class {
|
||||||
case "Boolean":
|
case "Boolean":
|
||||||
value = value._object().value.(Value)
|
value = value._object().value.(Value)
|
||||||
case "String":
|
case "String":
|
||||||
value = toValue_string(toString(value))
|
value = toValue_string(value.string())
|
||||||
case "Number":
|
case "Number":
|
||||||
value = toNumber(value)
|
value = value.numberValue()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
switch value._valueType {
|
switch value.kind {
|
||||||
case valueBoolean:
|
case valueBoolean:
|
||||||
return toBoolean(value), true
|
return value.bool(), true
|
||||||
case valueString:
|
case valueString:
|
||||||
return toString(value), true
|
return value.string(), true
|
||||||
case valueNumber:
|
case valueNumber:
|
||||||
value := toFloat(value)
|
integer := value.number()
|
||||||
if math.IsNaN(value) || math.IsInf(value, 0) {
|
switch integer.kind {
|
||||||
|
case numberInteger:
|
||||||
|
return integer.int64, true
|
||||||
|
case numberFloat:
|
||||||
|
return integer.float64, true
|
||||||
|
default:
|
||||||
return nil, true
|
return nil, true
|
||||||
}
|
}
|
||||||
return value, true
|
|
||||||
case valueNull:
|
case valueNull:
|
||||||
return nil, true
|
return nil, true
|
||||||
case valueObject:
|
case valueObject:
|
||||||
|
@ -243,14 +247,24 @@ func builtinJSON_stringifyWalk(ctx _builtinJSON_stringifyContext, key string, ho
|
||||||
if value := value._object(); nil != value {
|
if value := value._object(); nil != value {
|
||||||
for _, object := range ctx.stack {
|
for _, object := range ctx.stack {
|
||||||
if holder == object {
|
if holder == object {
|
||||||
panic(newTypeError("Converting circular structure to JSON"))
|
panic(ctx.call.runtime.panicTypeError("Converting circular structure to JSON"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ctx.stack = append(ctx.stack, value)
|
ctx.stack = append(ctx.stack, value)
|
||||||
defer func() { ctx.stack = ctx.stack[:len(ctx.stack)-1] }()
|
defer func() { ctx.stack = ctx.stack[:len(ctx.stack)-1] }()
|
||||||
}
|
}
|
||||||
if isArray(holder) {
|
if isArray(holder) {
|
||||||
length := holder.get("length").value.(uint32)
|
var length uint32
|
||||||
|
switch value := holder.get("length").value.(type) {
|
||||||
|
case uint32:
|
||||||
|
length = value
|
||||||
|
case int:
|
||||||
|
if value >= 0 {
|
||||||
|
length = uint32(value)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
panic(ctx.call.runtime.panicTypeError(fmt.Sprintf("JSON.stringify: invalid length: %v (%[1]T)", value)))
|
||||||
|
}
|
||||||
array := make([]interface{}, length)
|
array := make([]interface{}, length)
|
||||||
for index, _ := range array {
|
for index, _ := range array {
|
||||||
name := arrayIndexToString(int64(index))
|
name := arrayIndexToString(int64(index))
|
|
@ -8,31 +8,31 @@ import (
|
||||||
// Math
|
// Math
|
||||||
|
|
||||||
func builtinMath_abs(call FunctionCall) Value {
|
func builtinMath_abs(call FunctionCall) Value {
|
||||||
number := toFloat(call.Argument(0))
|
number := call.Argument(0).float64()
|
||||||
return toValue_float64(math.Abs(number))
|
return toValue_float64(math.Abs(number))
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinMath_acos(call FunctionCall) Value {
|
func builtinMath_acos(call FunctionCall) Value {
|
||||||
number := toFloat(call.Argument(0))
|
number := call.Argument(0).float64()
|
||||||
return toValue_float64(math.Acos(number))
|
return toValue_float64(math.Acos(number))
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinMath_asin(call FunctionCall) Value {
|
func builtinMath_asin(call FunctionCall) Value {
|
||||||
number := toFloat(call.Argument(0))
|
number := call.Argument(0).float64()
|
||||||
return toValue_float64(math.Asin(number))
|
return toValue_float64(math.Asin(number))
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinMath_atan(call FunctionCall) Value {
|
func builtinMath_atan(call FunctionCall) Value {
|
||||||
number := toFloat(call.Argument(0))
|
number := call.Argument(0).float64()
|
||||||
return toValue_float64(math.Atan(number))
|
return toValue_float64(math.Atan(number))
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinMath_atan2(call FunctionCall) Value {
|
func builtinMath_atan2(call FunctionCall) Value {
|
||||||
y := toFloat(call.Argument(0))
|
y := call.Argument(0).float64()
|
||||||
if math.IsNaN(y) {
|
if math.IsNaN(y) {
|
||||||
return NaNValue()
|
return NaNValue()
|
||||||
}
|
}
|
||||||
x := toFloat(call.Argument(1))
|
x := call.Argument(1).float64()
|
||||||
if math.IsNaN(x) {
|
if math.IsNaN(x) {
|
||||||
return NaNValue()
|
return NaNValue()
|
||||||
}
|
}
|
||||||
|
@ -40,27 +40,27 @@ func builtinMath_atan2(call FunctionCall) Value {
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinMath_cos(call FunctionCall) Value {
|
func builtinMath_cos(call FunctionCall) Value {
|
||||||
number := toFloat(call.Argument(0))
|
number := call.Argument(0).float64()
|
||||||
return toValue_float64(math.Cos(number))
|
return toValue_float64(math.Cos(number))
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinMath_ceil(call FunctionCall) Value {
|
func builtinMath_ceil(call FunctionCall) Value {
|
||||||
number := toFloat(call.Argument(0))
|
number := call.Argument(0).float64()
|
||||||
return toValue_float64(math.Ceil(number))
|
return toValue_float64(math.Ceil(number))
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinMath_exp(call FunctionCall) Value {
|
func builtinMath_exp(call FunctionCall) Value {
|
||||||
number := toFloat(call.Argument(0))
|
number := call.Argument(0).float64()
|
||||||
return toValue_float64(math.Exp(number))
|
return toValue_float64(math.Exp(number))
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinMath_floor(call FunctionCall) Value {
|
func builtinMath_floor(call FunctionCall) Value {
|
||||||
number := toFloat(call.Argument(0))
|
number := call.Argument(0).float64()
|
||||||
return toValue_float64(math.Floor(number))
|
return toValue_float64(math.Floor(number))
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinMath_log(call FunctionCall) Value {
|
func builtinMath_log(call FunctionCall) Value {
|
||||||
number := toFloat(call.Argument(0))
|
number := call.Argument(0).float64()
|
||||||
return toValue_float64(math.Log(number))
|
return toValue_float64(math.Log(number))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,14 +69,14 @@ func builtinMath_max(call FunctionCall) Value {
|
||||||
case 0:
|
case 0:
|
||||||
return negativeInfinityValue()
|
return negativeInfinityValue()
|
||||||
case 1:
|
case 1:
|
||||||
return toValue_float64(toFloat(call.ArgumentList[0]))
|
return toValue_float64(call.ArgumentList[0].float64())
|
||||||
}
|
}
|
||||||
result := toFloat(call.ArgumentList[0])
|
result := call.ArgumentList[0].float64()
|
||||||
if math.IsNaN(result) {
|
if math.IsNaN(result) {
|
||||||
return NaNValue()
|
return NaNValue()
|
||||||
}
|
}
|
||||||
for _, value := range call.ArgumentList[1:] {
|
for _, value := range call.ArgumentList[1:] {
|
||||||
value := toFloat(value)
|
value := value.float64()
|
||||||
if math.IsNaN(value) {
|
if math.IsNaN(value) {
|
||||||
return NaNValue()
|
return NaNValue()
|
||||||
}
|
}
|
||||||
|
@ -90,14 +90,14 @@ func builtinMath_min(call FunctionCall) Value {
|
||||||
case 0:
|
case 0:
|
||||||
return positiveInfinityValue()
|
return positiveInfinityValue()
|
||||||
case 1:
|
case 1:
|
||||||
return toValue_float64(toFloat(call.ArgumentList[0]))
|
return toValue_float64(call.ArgumentList[0].float64())
|
||||||
}
|
}
|
||||||
result := toFloat(call.ArgumentList[0])
|
result := call.ArgumentList[0].float64()
|
||||||
if math.IsNaN(result) {
|
if math.IsNaN(result) {
|
||||||
return NaNValue()
|
return NaNValue()
|
||||||
}
|
}
|
||||||
for _, value := range call.ArgumentList[1:] {
|
for _, value := range call.ArgumentList[1:] {
|
||||||
value := toFloat(value)
|
value := value.float64()
|
||||||
if math.IsNaN(value) {
|
if math.IsNaN(value) {
|
||||||
return NaNValue()
|
return NaNValue()
|
||||||
}
|
}
|
||||||
|
@ -108,8 +108,8 @@ func builtinMath_min(call FunctionCall) Value {
|
||||||
|
|
||||||
func builtinMath_pow(call FunctionCall) Value {
|
func builtinMath_pow(call FunctionCall) Value {
|
||||||
// TODO Make sure this works according to the specification (15.8.2.13)
|
// TODO Make sure this works according to the specification (15.8.2.13)
|
||||||
x := toFloat(call.Argument(0))
|
x := call.Argument(0).float64()
|
||||||
y := toFloat(call.Argument(1))
|
y := call.Argument(1).float64()
|
||||||
if math.Abs(x) == 1 && math.IsInf(y, 0) {
|
if math.Abs(x) == 1 && math.IsInf(y, 0) {
|
||||||
return NaNValue()
|
return NaNValue()
|
||||||
}
|
}
|
||||||
|
@ -121,7 +121,7 @@ func builtinMath_random(call FunctionCall) Value {
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinMath_round(call FunctionCall) Value {
|
func builtinMath_round(call FunctionCall) Value {
|
||||||
number := toFloat(call.Argument(0))
|
number := call.Argument(0).float64()
|
||||||
value := math.Floor(number + 0.5)
|
value := math.Floor(number + 0.5)
|
||||||
if value == 0 {
|
if value == 0 {
|
||||||
value = math.Copysign(0, number)
|
value = math.Copysign(0, number)
|
||||||
|
@ -130,16 +130,16 @@ func builtinMath_round(call FunctionCall) Value {
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinMath_sin(call FunctionCall) Value {
|
func builtinMath_sin(call FunctionCall) Value {
|
||||||
number := toFloat(call.Argument(0))
|
number := call.Argument(0).float64()
|
||||||
return toValue_float64(math.Sin(number))
|
return toValue_float64(math.Sin(number))
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinMath_sqrt(call FunctionCall) Value {
|
func builtinMath_sqrt(call FunctionCall) Value {
|
||||||
number := toFloat(call.Argument(0))
|
number := call.Argument(0).float64()
|
||||||
return toValue_float64(math.Sqrt(number))
|
return toValue_float64(math.Sqrt(number))
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinMath_tan(call FunctionCall) Value {
|
func builtinMath_tan(call FunctionCall) Value {
|
||||||
number := toFloat(call.Argument(0))
|
number := call.Argument(0).float64()
|
||||||
return toValue_float64(math.Tan(number))
|
return toValue_float64(math.Tan(number))
|
||||||
}
|
}
|
|
@ -9,7 +9,7 @@ import (
|
||||||
|
|
||||||
func numberValueFromNumberArgumentList(argumentList []Value) Value {
|
func numberValueFromNumberArgumentList(argumentList []Value) Value {
|
||||||
if len(argumentList) > 0 {
|
if len(argumentList) > 0 {
|
||||||
return toNumber(argumentList[0])
|
return argumentList[0].numberValue()
|
||||||
}
|
}
|
||||||
return toValue_int(0)
|
return toValue_int(0)
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@ func builtinNumber(call FunctionCall) Value {
|
||||||
return numberValueFromNumberArgumentList(call.ArgumentList)
|
return numberValueFromNumberArgumentList(call.ArgumentList)
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinNewNumber(self *_object, _ Value, argumentList []Value) Value {
|
func builtinNewNumber(self *_object, argumentList []Value) Value {
|
||||||
return toValue_object(self.runtime.newNumber(numberValueFromNumberArgumentList(argumentList)))
|
return toValue_object(self.runtime.newNumber(numberValueFromNumberArgumentList(argumentList)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,12 +30,12 @@ func builtinNumber_toString(call FunctionCall) Value {
|
||||||
if radixArgument.IsDefined() {
|
if radixArgument.IsDefined() {
|
||||||
integer := toIntegerFloat(radixArgument)
|
integer := toIntegerFloat(radixArgument)
|
||||||
if integer < 2 || integer > 36 {
|
if integer < 2 || integer > 36 {
|
||||||
panic(newRangeError("RangeError: toString() radix must be between 2 and 36"))
|
panic(call.runtime.panicRangeError("RangeError: toString() radix must be between 2 and 36"))
|
||||||
}
|
}
|
||||||
radix = int(integer)
|
radix = int(integer)
|
||||||
}
|
}
|
||||||
if radix == 10 {
|
if radix == 10 {
|
||||||
return toValue_string(toString(value))
|
return toValue_string(value.string())
|
||||||
}
|
}
|
||||||
return toValue_string(numberToStringRadix(value, radix))
|
return toValue_string(numberToStringRadix(value, radix))
|
||||||
}
|
}
|
||||||
|
@ -47,16 +47,16 @@ func builtinNumber_valueOf(call FunctionCall) Value {
|
||||||
func builtinNumber_toFixed(call FunctionCall) Value {
|
func builtinNumber_toFixed(call FunctionCall) Value {
|
||||||
precision := toIntegerFloat(call.Argument(0))
|
precision := toIntegerFloat(call.Argument(0))
|
||||||
if 20 < precision || 0 > precision {
|
if 20 < precision || 0 > precision {
|
||||||
panic(newRangeError("toFixed() precision must be between 0 and 20"))
|
panic(call.runtime.panicRangeError("toFixed() precision must be between 0 and 20"))
|
||||||
}
|
}
|
||||||
if call.This.IsNaN() {
|
if call.This.IsNaN() {
|
||||||
return toValue_string("NaN")
|
return toValue_string("NaN")
|
||||||
}
|
}
|
||||||
value := toFloat(call.This)
|
value := call.This.float64()
|
||||||
if math.Abs(value) >= 1e21 {
|
if math.Abs(value) >= 1e21 {
|
||||||
return toValue_string(floatToString(value, 64))
|
return toValue_string(floatToString(value, 64))
|
||||||
}
|
}
|
||||||
return toValue_string(strconv.FormatFloat(toFloat(call.This), 'f', int(precision), 64))
|
return toValue_string(strconv.FormatFloat(call.This.float64(), 'f', int(precision), 64))
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinNumber_toExponential(call FunctionCall) Value {
|
func builtinNumber_toExponential(call FunctionCall) Value {
|
||||||
|
@ -67,10 +67,10 @@ func builtinNumber_toExponential(call FunctionCall) Value {
|
||||||
if value := call.Argument(0); value.IsDefined() {
|
if value := call.Argument(0); value.IsDefined() {
|
||||||
precision = toIntegerFloat(value)
|
precision = toIntegerFloat(value)
|
||||||
if 0 > precision {
|
if 0 > precision {
|
||||||
panic(newRangeError("RangeError: toExponential() precision must be greater than 0"))
|
panic(call.runtime.panicRangeError("RangeError: toString() radix must be between 2 and 36"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return toValue_string(strconv.FormatFloat(toFloat(call.This), 'e', int(precision), 64))
|
return toValue_string(strconv.FormatFloat(call.This.float64(), 'e', int(precision), 64))
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinNumber_toPrecision(call FunctionCall) Value {
|
func builtinNumber_toPrecision(call FunctionCall) Value {
|
||||||
|
@ -79,13 +79,13 @@ func builtinNumber_toPrecision(call FunctionCall) Value {
|
||||||
}
|
}
|
||||||
value := call.Argument(0)
|
value := call.Argument(0)
|
||||||
if value.IsUndefined() {
|
if value.IsUndefined() {
|
||||||
return toValue_string(toString(call.This))
|
return toValue_string(call.This.string())
|
||||||
}
|
}
|
||||||
precision := toIntegerFloat(value)
|
precision := toIntegerFloat(value)
|
||||||
if 1 > precision {
|
if 1 > precision {
|
||||||
panic(newRangeError("RangeError: toPrecision() precision must be greater than 1"))
|
panic(call.runtime.panicRangeError("RangeError: toPrecision() precision must be greater than 1"))
|
||||||
}
|
}
|
||||||
return toValue_string(strconv.FormatFloat(toFloat(call.This), 'g', int(precision), 64))
|
return toValue_string(strconv.FormatFloat(call.This.float64(), 'g', int(precision), 64))
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinNumber_toLocaleString(call FunctionCall) Value {
|
func builtinNumber_toLocaleString(call FunctionCall) Value {
|
|
@ -8,7 +8,7 @@ import (
|
||||||
|
|
||||||
func builtinObject(call FunctionCall) Value {
|
func builtinObject(call FunctionCall) Value {
|
||||||
value := call.Argument(0)
|
value := call.Argument(0)
|
||||||
switch value._valueType {
|
switch value.kind {
|
||||||
case valueUndefined, valueNull:
|
case valueUndefined, valueNull:
|
||||||
return toValue_object(call.runtime.newObject())
|
return toValue_object(call.runtime.newObject())
|
||||||
}
|
}
|
||||||
|
@ -16,9 +16,9 @@ func builtinObject(call FunctionCall) Value {
|
||||||
return toValue_object(call.runtime.toObject(value))
|
return toValue_object(call.runtime.toObject(value))
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinNewObject(self *_object, _ Value, argumentList []Value) Value {
|
func builtinNewObject(self *_object, argumentList []Value) Value {
|
||||||
value := valueOfArrayIndex(argumentList, 0)
|
value := valueOfArrayIndex(argumentList, 0)
|
||||||
switch value._valueType {
|
switch value.kind {
|
||||||
case valueNull, valueUndefined:
|
case valueNull, valueUndefined:
|
||||||
case valueNumber, valueString, valueBoolean:
|
case valueNumber, valueString, valueBoolean:
|
||||||
return toValue_object(self.runtime.toObject(value))
|
return toValue_object(self.runtime.toObject(value))
|
||||||
|
@ -34,7 +34,7 @@ func builtinObject_valueOf(call FunctionCall) Value {
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinObject_hasOwnProperty(call FunctionCall) Value {
|
func builtinObject_hasOwnProperty(call FunctionCall) Value {
|
||||||
propertyName := toString(call.Argument(0))
|
propertyName := call.Argument(0).string()
|
||||||
thisObject := call.thisObject()
|
thisObject := call.thisObject()
|
||||||
return toValue_bool(thisObject.hasOwnProperty(propertyName))
|
return toValue_bool(thisObject.hasOwnProperty(propertyName))
|
||||||
}
|
}
|
||||||
|
@ -42,27 +42,27 @@ func builtinObject_hasOwnProperty(call FunctionCall) Value {
|
||||||
func builtinObject_isPrototypeOf(call FunctionCall) Value {
|
func builtinObject_isPrototypeOf(call FunctionCall) Value {
|
||||||
value := call.Argument(0)
|
value := call.Argument(0)
|
||||||
if !value.IsObject() {
|
if !value.IsObject() {
|
||||||
return FalseValue()
|
return falseValue
|
||||||
}
|
}
|
||||||
prototype := call.toObject(value).prototype
|
prototype := call.toObject(value).prototype
|
||||||
thisObject := call.thisObject()
|
thisObject := call.thisObject()
|
||||||
for prototype != nil {
|
for prototype != nil {
|
||||||
if thisObject == prototype {
|
if thisObject == prototype {
|
||||||
return TrueValue()
|
return trueValue
|
||||||
}
|
}
|
||||||
prototype = prototype.prototype
|
prototype = prototype.prototype
|
||||||
}
|
}
|
||||||
return FalseValue()
|
return falseValue
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinObject_propertyIsEnumerable(call FunctionCall) Value {
|
func builtinObject_propertyIsEnumerable(call FunctionCall) Value {
|
||||||
propertyName := toString(call.Argument(0))
|
propertyName := call.Argument(0).string()
|
||||||
thisObject := call.thisObject()
|
thisObject := call.thisObject()
|
||||||
property := thisObject.getOwnProperty(propertyName)
|
property := thisObject.getOwnProperty(propertyName)
|
||||||
if property != nil && property.enumerable() {
|
if property != nil && property.enumerable() {
|
||||||
return TrueValue()
|
return trueValue
|
||||||
}
|
}
|
||||||
return FalseValue()
|
return falseValue
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinObject_toString(call FunctionCall) Value {
|
func builtinObject_toString(call FunctionCall) Value {
|
||||||
|
@ -80,20 +80,20 @@ func builtinObject_toString(call FunctionCall) Value {
|
||||||
func builtinObject_toLocaleString(call FunctionCall) Value {
|
func builtinObject_toLocaleString(call FunctionCall) Value {
|
||||||
toString := call.thisObject().get("toString")
|
toString := call.thisObject().get("toString")
|
||||||
if !toString.isCallable() {
|
if !toString.isCallable() {
|
||||||
panic(newTypeError())
|
panic(call.runtime.panicTypeError())
|
||||||
}
|
}
|
||||||
return toString.call(call.This)
|
return toString.call(call.runtime, call.This)
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinObject_getPrototypeOf(call FunctionCall) Value {
|
func builtinObject_getPrototypeOf(call FunctionCall) Value {
|
||||||
objectValue := call.Argument(0)
|
objectValue := call.Argument(0)
|
||||||
object := objectValue._object()
|
object := objectValue._object()
|
||||||
if object == nil {
|
if object == nil {
|
||||||
panic(newTypeError())
|
panic(call.runtime.panicTypeError())
|
||||||
}
|
}
|
||||||
|
|
||||||
if object.prototype == nil {
|
if object.prototype == nil {
|
||||||
return NullValue()
|
return nullValue
|
||||||
}
|
}
|
||||||
|
|
||||||
return toValue_object(object.prototype)
|
return toValue_object(object.prototype)
|
||||||
|
@ -103,13 +103,13 @@ func builtinObject_getOwnPropertyDescriptor(call FunctionCall) Value {
|
||||||
objectValue := call.Argument(0)
|
objectValue := call.Argument(0)
|
||||||
object := objectValue._object()
|
object := objectValue._object()
|
||||||
if object == nil {
|
if object == nil {
|
||||||
panic(newTypeError())
|
panic(call.runtime.panicTypeError())
|
||||||
}
|
}
|
||||||
|
|
||||||
name := toString(call.Argument(1))
|
name := call.Argument(1).string()
|
||||||
descriptor := object.getOwnProperty(name)
|
descriptor := object.getOwnProperty(name)
|
||||||
if descriptor == nil {
|
if descriptor == nil {
|
||||||
return UndefinedValue()
|
return Value{}
|
||||||
}
|
}
|
||||||
return toValue_object(call.runtime.fromPropertyDescriptor(*descriptor))
|
return toValue_object(call.runtime.fromPropertyDescriptor(*descriptor))
|
||||||
}
|
}
|
||||||
|
@ -118,10 +118,10 @@ func builtinObject_defineProperty(call FunctionCall) Value {
|
||||||
objectValue := call.Argument(0)
|
objectValue := call.Argument(0)
|
||||||
object := objectValue._object()
|
object := objectValue._object()
|
||||||
if object == nil {
|
if object == nil {
|
||||||
panic(newTypeError())
|
panic(call.runtime.panicTypeError())
|
||||||
}
|
}
|
||||||
name := toString(call.Argument(1))
|
name := call.Argument(1).string()
|
||||||
descriptor := toPropertyDescriptor(call.Argument(2))
|
descriptor := toPropertyDescriptor(call.runtime, call.Argument(2))
|
||||||
object.defineOwnProperty(name, descriptor, true)
|
object.defineOwnProperty(name, descriptor, true)
|
||||||
return objectValue
|
return objectValue
|
||||||
}
|
}
|
||||||
|
@ -130,12 +130,12 @@ func builtinObject_defineProperties(call FunctionCall) Value {
|
||||||
objectValue := call.Argument(0)
|
objectValue := call.Argument(0)
|
||||||
object := objectValue._object()
|
object := objectValue._object()
|
||||||
if object == nil {
|
if object == nil {
|
||||||
panic(newTypeError())
|
panic(call.runtime.panicTypeError())
|
||||||
}
|
}
|
||||||
|
|
||||||
properties := call.runtime.toObject(call.Argument(1))
|
properties := call.runtime.toObject(call.Argument(1))
|
||||||
properties.enumerate(false, func(name string) bool {
|
properties.enumerate(false, func(name string) bool {
|
||||||
descriptor := toPropertyDescriptor(properties.get(name))
|
descriptor := toPropertyDescriptor(call.runtime, properties.get(name))
|
||||||
object.defineOwnProperty(name, descriptor, true)
|
object.defineOwnProperty(name, descriptor, true)
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
|
@ -146,7 +146,7 @@ func builtinObject_defineProperties(call FunctionCall) Value {
|
||||||
func builtinObject_create(call FunctionCall) Value {
|
func builtinObject_create(call FunctionCall) Value {
|
||||||
prototypeValue := call.Argument(0)
|
prototypeValue := call.Argument(0)
|
||||||
if !prototypeValue.IsNull() && !prototypeValue.IsObject() {
|
if !prototypeValue.IsNull() && !prototypeValue.IsObject() {
|
||||||
panic(newTypeError())
|
panic(call.runtime.panicTypeError())
|
||||||
}
|
}
|
||||||
|
|
||||||
object := call.runtime.newObject()
|
object := call.runtime.newObject()
|
||||||
|
@ -156,7 +156,7 @@ func builtinObject_create(call FunctionCall) Value {
|
||||||
if propertiesValue.IsDefined() {
|
if propertiesValue.IsDefined() {
|
||||||
properties := call.runtime.toObject(propertiesValue)
|
properties := call.runtime.toObject(propertiesValue)
|
||||||
properties.enumerate(false, func(name string) bool {
|
properties.enumerate(false, func(name string) bool {
|
||||||
descriptor := toPropertyDescriptor(properties.get(name))
|
descriptor := toPropertyDescriptor(call.runtime, properties.get(name))
|
||||||
object.defineOwnProperty(name, descriptor, true)
|
object.defineOwnProperty(name, descriptor, true)
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
|
@ -170,7 +170,7 @@ func builtinObject_isExtensible(call FunctionCall) Value {
|
||||||
if object := object._object(); object != nil {
|
if object := object._object(); object != nil {
|
||||||
return toValue_bool(object.extensible)
|
return toValue_bool(object.extensible)
|
||||||
}
|
}
|
||||||
panic(newTypeError())
|
panic(call.runtime.panicTypeError())
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinObject_preventExtensions(call FunctionCall) Value {
|
func builtinObject_preventExtensions(call FunctionCall) Value {
|
||||||
|
@ -178,7 +178,7 @@ func builtinObject_preventExtensions(call FunctionCall) Value {
|
||||||
if object := object._object(); object != nil {
|
if object := object._object(); object != nil {
|
||||||
object.extensible = false
|
object.extensible = false
|
||||||
} else {
|
} else {
|
||||||
panic(newTypeError())
|
panic(call.runtime.panicTypeError())
|
||||||
}
|
}
|
||||||
return object
|
return object
|
||||||
}
|
}
|
||||||
|
@ -199,7 +199,7 @@ func builtinObject_isSealed(call FunctionCall) Value {
|
||||||
})
|
})
|
||||||
return toValue_bool(result)
|
return toValue_bool(result)
|
||||||
}
|
}
|
||||||
panic(newTypeError())
|
panic(call.runtime.panicTypeError())
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinObject_seal(call FunctionCall) Value {
|
func builtinObject_seal(call FunctionCall) Value {
|
||||||
|
@ -214,7 +214,7 @@ func builtinObject_seal(call FunctionCall) Value {
|
||||||
})
|
})
|
||||||
object.extensible = false
|
object.extensible = false
|
||||||
} else {
|
} else {
|
||||||
panic(newTypeError())
|
panic(call.runtime.panicTypeError())
|
||||||
}
|
}
|
||||||
return object
|
return object
|
||||||
}
|
}
|
||||||
|
@ -235,7 +235,7 @@ func builtinObject_isFrozen(call FunctionCall) Value {
|
||||||
})
|
})
|
||||||
return toValue_bool(result)
|
return toValue_bool(result)
|
||||||
}
|
}
|
||||||
panic(newTypeError())
|
panic(call.runtime.panicTypeError())
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinObject_freeze(call FunctionCall) Value {
|
func builtinObject_freeze(call FunctionCall) Value {
|
||||||
|
@ -259,7 +259,7 @@ func builtinObject_freeze(call FunctionCall) Value {
|
||||||
})
|
})
|
||||||
object.extensible = false
|
object.extensible = false
|
||||||
} else {
|
} else {
|
||||||
panic(newTypeError())
|
panic(call.runtime.panicTypeError())
|
||||||
}
|
}
|
||||||
return object
|
return object
|
||||||
}
|
}
|
||||||
|
@ -272,7 +272,7 @@ func builtinObject_keys(call FunctionCall) Value {
|
||||||
})
|
})
|
||||||
return toValue_object(call.runtime.newArrayOf(keys))
|
return toValue_object(call.runtime.newArrayOf(keys))
|
||||||
}
|
}
|
||||||
panic(newTypeError())
|
panic(call.runtime.panicTypeError())
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinObject_getOwnPropertyNames(call FunctionCall) Value {
|
func builtinObject_getOwnPropertyNames(call FunctionCall) Value {
|
||||||
|
@ -285,5 +285,5 @@ func builtinObject_getOwnPropertyNames(call FunctionCall) Value {
|
||||||
})
|
})
|
||||||
return toValue_object(call.runtime.newArrayOf(propertyNames))
|
return toValue_object(call.runtime.newArrayOf(propertyNames))
|
||||||
}
|
}
|
||||||
panic(newTypeError())
|
panic(call.runtime.panicTypeError())
|
||||||
}
|
}
|
|
@ -17,7 +17,7 @@ func builtinRegExp(call FunctionCall) Value {
|
||||||
return toValue_object(call.runtime.newRegExp(pattern, flags))
|
return toValue_object(call.runtime.newRegExp(pattern, flags))
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinNewRegExp(self *_object, _ Value, argumentList []Value) Value {
|
func builtinNewRegExp(self *_object, argumentList []Value) Value {
|
||||||
return toValue_object(self.runtime.newRegExp(
|
return toValue_object(self.runtime.newRegExp(
|
||||||
valueOfArrayIndex(argumentList, 0),
|
valueOfArrayIndex(argumentList, 0),
|
||||||
valueOfArrayIndex(argumentList, 1),
|
valueOfArrayIndex(argumentList, 1),
|
||||||
|
@ -26,15 +26,15 @@ func builtinNewRegExp(self *_object, _ Value, argumentList []Value) Value {
|
||||||
|
|
||||||
func builtinRegExp_toString(call FunctionCall) Value {
|
func builtinRegExp_toString(call FunctionCall) Value {
|
||||||
thisObject := call.thisObject()
|
thisObject := call.thisObject()
|
||||||
source := toString(thisObject.get("source"))
|
source := thisObject.get("source").string()
|
||||||
flags := []byte{}
|
flags := []byte{}
|
||||||
if toBoolean(thisObject.get("global")) {
|
if thisObject.get("global").bool() {
|
||||||
flags = append(flags, 'g')
|
flags = append(flags, 'g')
|
||||||
}
|
}
|
||||||
if toBoolean(thisObject.get("ignoreCase")) {
|
if thisObject.get("ignoreCase").bool() {
|
||||||
flags = append(flags, 'i')
|
flags = append(flags, 'i')
|
||||||
}
|
}
|
||||||
if toBoolean(thisObject.get("multiline")) {
|
if thisObject.get("multiline").bool() {
|
||||||
flags = append(flags, 'm')
|
flags = append(flags, 'm')
|
||||||
}
|
}
|
||||||
return toValue_string(fmt.Sprintf("/%s/%s", source, flags))
|
return toValue_string(fmt.Sprintf("/%s/%s", source, flags))
|
||||||
|
@ -42,17 +42,17 @@ func builtinRegExp_toString(call FunctionCall) Value {
|
||||||
|
|
||||||
func builtinRegExp_exec(call FunctionCall) Value {
|
func builtinRegExp_exec(call FunctionCall) Value {
|
||||||
thisObject := call.thisObject()
|
thisObject := call.thisObject()
|
||||||
target := toString(call.Argument(0))
|
target := call.Argument(0).string()
|
||||||
match, result := execRegExp(thisObject, target)
|
match, result := execRegExp(thisObject, target)
|
||||||
if !match {
|
if !match {
|
||||||
return NullValue()
|
return nullValue
|
||||||
}
|
}
|
||||||
return toValue_object(execResultToArray(call.runtime, target, result))
|
return toValue_object(execResultToArray(call.runtime, target, result))
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinRegExp_test(call FunctionCall) Value {
|
func builtinRegExp_test(call FunctionCall) Value {
|
||||||
thisObject := call.thisObject()
|
thisObject := call.thisObject()
|
||||||
target := toString(call.Argument(0))
|
target := call.Argument(0).string()
|
||||||
match, _ := execRegExp(thisObject, target)
|
match, _ := execRegExp(thisObject, target)
|
||||||
return toValue_bool(match)
|
return toValue_bool(match)
|
||||||
}
|
}
|
||||||
|
@ -61,5 +61,5 @@ func builtinRegExp_compile(call FunctionCall) Value {
|
||||||
// This (useless) function is deprecated, but is here to provide some
|
// This (useless) function is deprecated, but is here to provide some
|
||||||
// semblance of compatibility.
|
// semblance of compatibility.
|
||||||
// Caveat emptor: it may not be around for long.
|
// Caveat emptor: it may not be around for long.
|
||||||
return UndefinedValue()
|
return Value{}
|
||||||
}
|
}
|
|
@ -12,7 +12,7 @@ import (
|
||||||
|
|
||||||
func stringValueFromStringArgumentList(argumentList []Value) Value {
|
func stringValueFromStringArgumentList(argumentList []Value) Value {
|
||||||
if len(argumentList) > 0 {
|
if len(argumentList) > 0 {
|
||||||
return toValue_string(toString(argumentList[0]))
|
return toValue_string(argumentList[0].string())
|
||||||
}
|
}
|
||||||
return toValue_string("")
|
return toValue_string("")
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ func builtinString(call FunctionCall) Value {
|
||||||
return stringValueFromStringArgumentList(call.ArgumentList)
|
return stringValueFromStringArgumentList(call.ArgumentList)
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinNewString(self *_object, _ Value, argumentList []Value) Value {
|
func builtinNewString(self *_object, argumentList []Value) Value {
|
||||||
return toValue_object(self.runtime.newString(stringValueFromStringArgumentList(argumentList)))
|
return toValue_object(self.runtime.newString(stringValueFromStringArgumentList(argumentList)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,8 +41,8 @@ func builtinString_fromCharCode(call FunctionCall) Value {
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinString_charAt(call FunctionCall) Value {
|
func builtinString_charAt(call FunctionCall) Value {
|
||||||
checkObjectCoercible(call.This)
|
checkObjectCoercible(call.runtime, call.This)
|
||||||
idx := int(toInteger(call.Argument(0)).value)
|
idx := int(call.Argument(0).number().int64)
|
||||||
chr := stringAt(call.This._object().stringValue(), idx)
|
chr := stringAt(call.This._object().stringValue(), idx)
|
||||||
if chr == utf8.RuneError {
|
if chr == utf8.RuneError {
|
||||||
return toValue_string("")
|
return toValue_string("")
|
||||||
|
@ -51,8 +51,8 @@ func builtinString_charAt(call FunctionCall) Value {
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinString_charCodeAt(call FunctionCall) Value {
|
func builtinString_charCodeAt(call FunctionCall) Value {
|
||||||
checkObjectCoercible(call.This)
|
checkObjectCoercible(call.runtime, call.This)
|
||||||
idx := int(toInteger(call.Argument(0)).value)
|
idx := int(call.Argument(0).number().int64)
|
||||||
chr := stringAt(call.This._object().stringValue(), idx)
|
chr := stringAt(call.This._object().stringValue(), idx)
|
||||||
if chr == utf8.RuneError {
|
if chr == utf8.RuneError {
|
||||||
return NaNValue()
|
return NaNValue()
|
||||||
|
@ -61,19 +61,19 @@ func builtinString_charCodeAt(call FunctionCall) Value {
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinString_concat(call FunctionCall) Value {
|
func builtinString_concat(call FunctionCall) Value {
|
||||||
checkObjectCoercible(call.This)
|
checkObjectCoercible(call.runtime, call.This)
|
||||||
var value bytes.Buffer
|
var value bytes.Buffer
|
||||||
value.WriteString(toString(call.This))
|
value.WriteString(call.This.string())
|
||||||
for _, item := range call.ArgumentList {
|
for _, item := range call.ArgumentList {
|
||||||
value.WriteString(toString(item))
|
value.WriteString(item.string())
|
||||||
}
|
}
|
||||||
return toValue_string(value.String())
|
return toValue_string(value.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinString_indexOf(call FunctionCall) Value {
|
func builtinString_indexOf(call FunctionCall) Value {
|
||||||
checkObjectCoercible(call.This)
|
checkObjectCoercible(call.runtime, call.This)
|
||||||
value := toString(call.This)
|
value := call.This.string()
|
||||||
target := toString(call.Argument(0))
|
target := call.Argument(0).string()
|
||||||
if 2 > len(call.ArgumentList) {
|
if 2 > len(call.ArgumentList) {
|
||||||
return toValue_int(strings.Index(value, target))
|
return toValue_int(strings.Index(value, target))
|
||||||
}
|
}
|
||||||
|
@ -94,9 +94,9 @@ func builtinString_indexOf(call FunctionCall) Value {
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinString_lastIndexOf(call FunctionCall) Value {
|
func builtinString_lastIndexOf(call FunctionCall) Value {
|
||||||
checkObjectCoercible(call.This)
|
checkObjectCoercible(call.runtime, call.This)
|
||||||
value := toString(call.This)
|
value := call.This.string()
|
||||||
target := toString(call.Argument(0))
|
target := call.Argument(0).string()
|
||||||
if 2 > len(call.ArgumentList) || call.ArgumentList[1].IsUndefined() {
|
if 2 > len(call.ArgumentList) || call.ArgumentList[1].IsUndefined() {
|
||||||
return toValue_int(strings.LastIndex(value, target))
|
return toValue_int(strings.LastIndex(value, target))
|
||||||
}
|
}
|
||||||
|
@ -104,15 +104,15 @@ func builtinString_lastIndexOf(call FunctionCall) Value {
|
||||||
if length == 0 {
|
if length == 0 {
|
||||||
return toValue_int(strings.LastIndex(value, target))
|
return toValue_int(strings.LastIndex(value, target))
|
||||||
}
|
}
|
||||||
start := toInteger(call.ArgumentList[1])
|
start := call.ArgumentList[1].number()
|
||||||
if !start.valid() {
|
if start.kind == numberInfinity { // FIXME
|
||||||
// startNumber is infinity, so start is the end of string (start = length)
|
// startNumber is infinity, so start is the end of string (start = length)
|
||||||
return toValue_int(strings.LastIndex(value, target))
|
return toValue_int(strings.LastIndex(value, target))
|
||||||
}
|
}
|
||||||
if 0 > start.value {
|
if 0 > start.int64 {
|
||||||
start.value = 0
|
start.int64 = 0
|
||||||
}
|
}
|
||||||
end := int(start.value) + len(target)
|
end := int(start.int64) + len(target)
|
||||||
if end > length {
|
if end > length {
|
||||||
end = length
|
end = length
|
||||||
}
|
}
|
||||||
|
@ -120,18 +120,18 @@ func builtinString_lastIndexOf(call FunctionCall) Value {
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinString_match(call FunctionCall) Value {
|
func builtinString_match(call FunctionCall) Value {
|
||||||
checkObjectCoercible(call.This)
|
checkObjectCoercible(call.runtime, call.This)
|
||||||
target := toString(call.This)
|
target := call.This.string()
|
||||||
matcherValue := call.Argument(0)
|
matcherValue := call.Argument(0)
|
||||||
matcher := matcherValue._object()
|
matcher := matcherValue._object()
|
||||||
if !matcherValue.IsObject() || matcher.class != "RegExp" {
|
if !matcherValue.IsObject() || matcher.class != "RegExp" {
|
||||||
matcher = call.runtime.newRegExp(matcherValue, UndefinedValue())
|
matcher = call.runtime.newRegExp(matcherValue, Value{})
|
||||||
}
|
}
|
||||||
global := toBoolean(matcher.get("global"))
|
global := matcher.get("global").bool()
|
||||||
if !global {
|
if !global {
|
||||||
match, result := execRegExp(matcher, target)
|
match, result := execRegExp(matcher, target)
|
||||||
if !match {
|
if !match {
|
||||||
return NullValue()
|
return nullValue
|
||||||
}
|
}
|
||||||
return toValue_object(execResultToArray(call.runtime, target, result))
|
return toValue_object(execResultToArray(call.runtime, target, result))
|
||||||
}
|
}
|
||||||
|
@ -141,7 +141,7 @@ func builtinString_match(call FunctionCall) Value {
|
||||||
matchCount := len(result)
|
matchCount := len(result)
|
||||||
if result == nil {
|
if result == nil {
|
||||||
matcher.put("lastIndex", toValue_int(0), true)
|
matcher.put("lastIndex", toValue_int(0), true)
|
||||||
return UndefinedValue() // !match
|
return Value{} // !match
|
||||||
}
|
}
|
||||||
matchCount = len(result)
|
matchCount = len(result)
|
||||||
valueArray := make([]Value, matchCount)
|
valueArray := make([]Value, matchCount)
|
||||||
|
@ -189,8 +189,8 @@ func builtinString_findAndReplaceString(input []byte, lastIndex int, match []int
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinString_replace(call FunctionCall) Value {
|
func builtinString_replace(call FunctionCall) Value {
|
||||||
checkObjectCoercible(call.This)
|
checkObjectCoercible(call.runtime, call.This)
|
||||||
target := []byte(toString(call.This))
|
target := []byte(call.This.string())
|
||||||
searchValue := call.Argument(0)
|
searchValue := call.Argument(0)
|
||||||
searchObject := searchValue._object()
|
searchObject := searchValue._object()
|
||||||
|
|
||||||
|
@ -205,7 +205,7 @@ func builtinString_replace(call FunctionCall) Value {
|
||||||
find = -1
|
find = -1
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
search = regexp.MustCompile(regexp.QuoteMeta(toString(searchValue)))
|
search = regexp.MustCompile(regexp.QuoteMeta(searchValue.string()))
|
||||||
}
|
}
|
||||||
|
|
||||||
found := search.FindAllSubmatchIndex(target, find)
|
found := search.FindAllSubmatchIndex(target, find)
|
||||||
|
@ -232,18 +232,18 @@ func builtinString_replace(call FunctionCall) Value {
|
||||||
if match[offset] != -1 {
|
if match[offset] != -1 {
|
||||||
argumentList[index] = toValue_string(target[match[offset]:match[offset+1]])
|
argumentList[index] = toValue_string(target[match[offset]:match[offset+1]])
|
||||||
} else {
|
} else {
|
||||||
argumentList[index] = UndefinedValue()
|
argumentList[index] = Value{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
argumentList[matchCount+0] = toValue_int(match[0])
|
argumentList[matchCount+0] = toValue_int(match[0])
|
||||||
argumentList[matchCount+1] = toValue_string(target)
|
argumentList[matchCount+1] = toValue_string(target)
|
||||||
replacement := toString(replace.Call(UndefinedValue(), argumentList))
|
replacement := replace.call(Value{}, argumentList, false, nativeFrame).string()
|
||||||
result = append(result, []byte(replacement)...)
|
result = append(result, []byte(replacement)...)
|
||||||
lastIndex = match[1]
|
lastIndex = match[1]
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
replace := []byte(toString(replaceValue))
|
replace := []byte(replaceValue.string())
|
||||||
for _, match := range found {
|
for _, match := range found {
|
||||||
result = builtinString_findAndReplaceString(result, lastIndex, match, target, replace)
|
result = builtinString_findAndReplaceString(result, lastIndex, match, target, replace)
|
||||||
lastIndex = match[1]
|
lastIndex = match[1]
|
||||||
|
@ -260,17 +260,15 @@ func builtinString_replace(call FunctionCall) Value {
|
||||||
|
|
||||||
return toValue_string(string(result))
|
return toValue_string(string(result))
|
||||||
}
|
}
|
||||||
|
|
||||||
return UndefinedValue()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinString_search(call FunctionCall) Value {
|
func builtinString_search(call FunctionCall) Value {
|
||||||
checkObjectCoercible(call.This)
|
checkObjectCoercible(call.runtime, call.This)
|
||||||
target := toString(call.This)
|
target := call.This.string()
|
||||||
searchValue := call.Argument(0)
|
searchValue := call.Argument(0)
|
||||||
search := searchValue._object()
|
search := searchValue._object()
|
||||||
if !searchValue.IsObject() || search.class != "RegExp" {
|
if !searchValue.IsObject() || search.class != "RegExp" {
|
||||||
search = call.runtime.newRegExp(searchValue, UndefinedValue())
|
search = call.runtime.newRegExp(searchValue, Value{})
|
||||||
}
|
}
|
||||||
result := search.regExpValue().regularExpression.FindStringIndex(target)
|
result := search.regExpValue().regularExpression.FindStringIndex(target)
|
||||||
if result == nil {
|
if result == nil {
|
||||||
|
@ -291,8 +289,8 @@ func stringSplitMatch(target string, targetLength int64, index uint, search stri
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinString_split(call FunctionCall) Value {
|
func builtinString_split(call FunctionCall) Value {
|
||||||
checkObjectCoercible(call.This)
|
checkObjectCoercible(call.runtime, call.This)
|
||||||
target := toString(call.This)
|
target := call.This.string()
|
||||||
|
|
||||||
separatorValue := call.Argument(0)
|
separatorValue := call.Argument(0)
|
||||||
limitValue := call.Argument(1)
|
limitValue := call.Argument(1)
|
||||||
|
@ -343,7 +341,7 @@ func builtinString_split(call FunctionCall) Value {
|
||||||
captureCount := len(match) / 2
|
captureCount := len(match) / 2
|
||||||
for index := 1; index < captureCount; index++ {
|
for index := 1; index < captureCount; index++ {
|
||||||
offset := index * 2
|
offset := index * 2
|
||||||
value := UndefinedValue()
|
value := Value{}
|
||||||
if match[offset] != -1 {
|
if match[offset] != -1 {
|
||||||
value = toValue_string(target[match[offset]:match[offset+1]])
|
value = toValue_string(target[match[offset]:match[offset+1]])
|
||||||
}
|
}
|
||||||
|
@ -367,7 +365,7 @@ func builtinString_split(call FunctionCall) Value {
|
||||||
return toValue_object(call.runtime.newArrayOf(valueArray))
|
return toValue_object(call.runtime.newArrayOf(valueArray))
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
separator := toString(separatorValue)
|
separator := separatorValue.string()
|
||||||
|
|
||||||
splitLimit := limit
|
splitLimit := limit
|
||||||
excess := false
|
excess := false
|
||||||
|
@ -389,13 +387,11 @@ func builtinString_split(call FunctionCall) Value {
|
||||||
|
|
||||||
return toValue_object(call.runtime.newArrayOf(valueArray))
|
return toValue_object(call.runtime.newArrayOf(valueArray))
|
||||||
}
|
}
|
||||||
|
|
||||||
return UndefinedValue()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinString_slice(call FunctionCall) Value {
|
func builtinString_slice(call FunctionCall) Value {
|
||||||
checkObjectCoercible(call.This)
|
checkObjectCoercible(call.runtime, call.This)
|
||||||
target := toString(call.This)
|
target := call.This.string()
|
||||||
|
|
||||||
length := int64(len(target))
|
length := int64(len(target))
|
||||||
start, end := rangeStartEnd(call.ArgumentList, length, false)
|
start, end := rangeStartEnd(call.ArgumentList, length, false)
|
||||||
|
@ -406,8 +402,8 @@ func builtinString_slice(call FunctionCall) Value {
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinString_substring(call FunctionCall) Value {
|
func builtinString_substring(call FunctionCall) Value {
|
||||||
checkObjectCoercible(call.This)
|
checkObjectCoercible(call.runtime, call.This)
|
||||||
target := toString(call.This)
|
target := call.This.string()
|
||||||
|
|
||||||
length := int64(len(target))
|
length := int64(len(target))
|
||||||
start, end := rangeStartEnd(call.ArgumentList, length, true)
|
start, end := rangeStartEnd(call.ArgumentList, length, true)
|
||||||
|
@ -418,7 +414,7 @@ func builtinString_substring(call FunctionCall) Value {
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinString_substr(call FunctionCall) Value {
|
func builtinString_substr(call FunctionCall) Value {
|
||||||
target := toString(call.This)
|
target := call.This.string()
|
||||||
|
|
||||||
size := int64(len(target))
|
size := int64(len(target))
|
||||||
start, length := rangeStartLength(call.ArgumentList, size)
|
start, length := rangeStartLength(call.ArgumentList, size)
|
||||||
|
@ -443,42 +439,42 @@ func builtinString_substr(call FunctionCall) Value {
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinString_toLowerCase(call FunctionCall) Value {
|
func builtinString_toLowerCase(call FunctionCall) Value {
|
||||||
checkObjectCoercible(call.This)
|
checkObjectCoercible(call.runtime, call.This)
|
||||||
return toValue_string(strings.ToLower(toString(call.This)))
|
return toValue_string(strings.ToLower(call.This.string()))
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinString_toUpperCase(call FunctionCall) Value {
|
func builtinString_toUpperCase(call FunctionCall) Value {
|
||||||
checkObjectCoercible(call.This)
|
checkObjectCoercible(call.runtime, call.This)
|
||||||
return toValue_string(strings.ToUpper(toString(call.This)))
|
return toValue_string(strings.ToUpper(call.This.string()))
|
||||||
}
|
}
|
||||||
|
|
||||||
// 7.2 Table 2 — Whitespace Characters & 7.3 Table 3 - Line Terminator Characters
|
// 7.2 Table 2 — Whitespace Characters & 7.3 Table 3 - Line Terminator Characters
|
||||||
const builtinString_trim_whitespace = "\u0009\u000A\u000B\u000C\u000D\u0020\u00A0\u1680\u180E\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF"
|
const builtinString_trim_whitespace = "\u0009\u000A\u000B\u000C\u000D\u0020\u00A0\u1680\u180E\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF"
|
||||||
|
|
||||||
func builtinString_trim(call FunctionCall) Value {
|
func builtinString_trim(call FunctionCall) Value {
|
||||||
checkObjectCoercible(call.This)
|
checkObjectCoercible(call.runtime, call.This)
|
||||||
return toValue(strings.Trim(toString(call.This),
|
return toValue(strings.Trim(call.This.string(),
|
||||||
builtinString_trim_whitespace))
|
builtinString_trim_whitespace))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mozilla extension, not ECMAScript 5
|
// Mozilla extension, not ECMAScript 5
|
||||||
func builtinString_trimLeft(call FunctionCall) Value {
|
func builtinString_trimLeft(call FunctionCall) Value {
|
||||||
checkObjectCoercible(call.This)
|
checkObjectCoercible(call.runtime, call.This)
|
||||||
return toValue(strings.TrimLeft(toString(call.This),
|
return toValue(strings.TrimLeft(call.This.string(),
|
||||||
builtinString_trim_whitespace))
|
builtinString_trim_whitespace))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mozilla extension, not ECMAScript 5
|
// Mozilla extension, not ECMAScript 5
|
||||||
func builtinString_trimRight(call FunctionCall) Value {
|
func builtinString_trimRight(call FunctionCall) Value {
|
||||||
checkObjectCoercible(call.This)
|
checkObjectCoercible(call.runtime, call.This)
|
||||||
return toValue(strings.TrimRight(toString(call.This),
|
return toValue(strings.TrimRight(call.This.string(),
|
||||||
builtinString_trim_whitespace))
|
builtinString_trim_whitespace))
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinString_localeCompare(call FunctionCall) Value {
|
func builtinString_localeCompare(call FunctionCall) Value {
|
||||||
checkObjectCoercible(call.This)
|
checkObjectCoercible(call.runtime, call.This)
|
||||||
this := toString(call.This)
|
this := call.This.string()
|
||||||
that := toString(call.Argument(0))
|
that := call.Argument(0).string()
|
||||||
if this < that {
|
if this < that {
|
||||||
return toValue_int(-1)
|
return toValue_int(-1)
|
||||||
} else if this == that {
|
} else if this == that {
|
||||||
|
@ -491,7 +487,7 @@ func builtinString_localeCompare(call FunctionCall) Value {
|
||||||
An alternate version of String.trim
|
An alternate version of String.trim
|
||||||
func builtinString_trim(call FunctionCall) Value {
|
func builtinString_trim(call FunctionCall) Value {
|
||||||
checkObjectCoercible(call.This)
|
checkObjectCoercible(call.This)
|
||||||
return toValue_string(strings.TrimFunc(toString(call.This), isWhiteSpaceOrLineTerminator))
|
return toValue_string(strings.TrimFunc(call.string(.This), isWhiteSpaceOrLineTerminator))
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -0,0 +1,155 @@
|
||||||
|
package otto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
type _clone struct {
|
||||||
|
runtime *_runtime
|
||||||
|
_object map[*_object]*_object
|
||||||
|
_objectStash map[*_objectStash]*_objectStash
|
||||||
|
_dclStash map[*_dclStash]*_dclStash
|
||||||
|
_fnStash map[*_fnStash]*_fnStash
|
||||||
|
}
|
||||||
|
|
||||||
|
func (in *_runtime) clone() *_runtime {
|
||||||
|
|
||||||
|
in.lck.Lock()
|
||||||
|
defer in.lck.Unlock()
|
||||||
|
|
||||||
|
out := &_runtime{}
|
||||||
|
clone := _clone{
|
||||||
|
runtime: out,
|
||||||
|
_object: make(map[*_object]*_object),
|
||||||
|
_objectStash: make(map[*_objectStash]*_objectStash),
|
||||||
|
_dclStash: make(map[*_dclStash]*_dclStash),
|
||||||
|
_fnStash: make(map[*_fnStash]*_fnStash),
|
||||||
|
}
|
||||||
|
|
||||||
|
globalObject := clone.object(in.globalObject)
|
||||||
|
out.globalStash = out.newObjectStash(globalObject, nil)
|
||||||
|
out.globalObject = globalObject
|
||||||
|
out.global = _global{
|
||||||
|
clone.object(in.global.Object),
|
||||||
|
clone.object(in.global.Function),
|
||||||
|
clone.object(in.global.Array),
|
||||||
|
clone.object(in.global.String),
|
||||||
|
clone.object(in.global.Boolean),
|
||||||
|
clone.object(in.global.Number),
|
||||||
|
clone.object(in.global.Math),
|
||||||
|
clone.object(in.global.Date),
|
||||||
|
clone.object(in.global.RegExp),
|
||||||
|
clone.object(in.global.Error),
|
||||||
|
clone.object(in.global.EvalError),
|
||||||
|
clone.object(in.global.TypeError),
|
||||||
|
clone.object(in.global.RangeError),
|
||||||
|
clone.object(in.global.ReferenceError),
|
||||||
|
clone.object(in.global.SyntaxError),
|
||||||
|
clone.object(in.global.URIError),
|
||||||
|
clone.object(in.global.JSON),
|
||||||
|
|
||||||
|
clone.object(in.global.ObjectPrototype),
|
||||||
|
clone.object(in.global.FunctionPrototype),
|
||||||
|
clone.object(in.global.ArrayPrototype),
|
||||||
|
clone.object(in.global.StringPrototype),
|
||||||
|
clone.object(in.global.BooleanPrototype),
|
||||||
|
clone.object(in.global.NumberPrototype),
|
||||||
|
clone.object(in.global.DatePrototype),
|
||||||
|
clone.object(in.global.RegExpPrototype),
|
||||||
|
clone.object(in.global.ErrorPrototype),
|
||||||
|
clone.object(in.global.EvalErrorPrototype),
|
||||||
|
clone.object(in.global.TypeErrorPrototype),
|
||||||
|
clone.object(in.global.RangeErrorPrototype),
|
||||||
|
clone.object(in.global.ReferenceErrorPrototype),
|
||||||
|
clone.object(in.global.SyntaxErrorPrototype),
|
||||||
|
clone.object(in.global.URIErrorPrototype),
|
||||||
|
}
|
||||||
|
|
||||||
|
out.eval = out.globalObject.property["eval"].value.(Value).value.(*_object)
|
||||||
|
out.globalObject.prototype = out.global.ObjectPrototype
|
||||||
|
|
||||||
|
// Not sure if this is necessary, but give some help to the GC
|
||||||
|
clone.runtime = nil
|
||||||
|
clone._object = nil
|
||||||
|
clone._objectStash = nil
|
||||||
|
clone._dclStash = nil
|
||||||
|
clone._fnStash = nil
|
||||||
|
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func (clone *_clone) object(in *_object) *_object {
|
||||||
|
if out, exists := clone._object[in]; exists {
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
out := &_object{}
|
||||||
|
clone._object[in] = out
|
||||||
|
return in.objectClass.clone(in, out, clone)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (clone *_clone) dclStash(in *_dclStash) (*_dclStash, bool) {
|
||||||
|
if out, exists := clone._dclStash[in]; exists {
|
||||||
|
return out, true
|
||||||
|
}
|
||||||
|
out := &_dclStash{}
|
||||||
|
clone._dclStash[in] = out
|
||||||
|
return out, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (clone *_clone) objectStash(in *_objectStash) (*_objectStash, bool) {
|
||||||
|
if out, exists := clone._objectStash[in]; exists {
|
||||||
|
return out, true
|
||||||
|
}
|
||||||
|
out := &_objectStash{}
|
||||||
|
clone._objectStash[in] = out
|
||||||
|
return out, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (clone *_clone) fnStash(in *_fnStash) (*_fnStash, bool) {
|
||||||
|
if out, exists := clone._fnStash[in]; exists {
|
||||||
|
return out, true
|
||||||
|
}
|
||||||
|
out := &_fnStash{}
|
||||||
|
clone._fnStash[in] = out
|
||||||
|
return out, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (clone *_clone) value(in Value) Value {
|
||||||
|
out := in
|
||||||
|
switch value := in.value.(type) {
|
||||||
|
case *_object:
|
||||||
|
out.value = clone.object(value)
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func (clone *_clone) valueArray(in []Value) []Value {
|
||||||
|
out := make([]Value, len(in))
|
||||||
|
for index, value := range in {
|
||||||
|
out[index] = clone.value(value)
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func (clone *_clone) stash(in _stash) _stash {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return in.clone(clone)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (clone *_clone) property(in _property) _property {
|
||||||
|
out := in
|
||||||
|
if value, valid := in.value.(Value); valid {
|
||||||
|
out.value = clone.value(value)
|
||||||
|
} else {
|
||||||
|
panic(fmt.Errorf("in.value.(Value) != true"))
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func (clone *_clone) dclProperty(in _dclProperty) _dclProperty {
|
||||||
|
out := in
|
||||||
|
out.value = clone.value(in.value)
|
||||||
|
return out
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
package otto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/robertkrimen/otto/ast"
|
||||||
|
"github.com/robertkrimen/otto/file"
|
||||||
|
)
|
||||||
|
|
||||||
|
type _file struct {
|
||||||
|
name string
|
||||||
|
src string
|
||||||
|
base int // This will always be 1 or greater
|
||||||
|
}
|
||||||
|
|
||||||
|
type _compiler struct {
|
||||||
|
file *file.File
|
||||||
|
program *ast.Program
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cmpl *_compiler) parse() *_nodeProgram {
|
||||||
|
if cmpl.program != nil {
|
||||||
|
cmpl.file = cmpl.program.File
|
||||||
|
}
|
||||||
|
return cmpl._parse(cmpl.program)
|
||||||
|
}
|
|
@ -4,13 +4,20 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (self *_runtime) cmpl_evaluate_nodeProgram(node *_nodeProgram) Value {
|
func (self *_runtime) cmpl_evaluate_nodeProgram(node *_nodeProgram, eval bool) Value {
|
||||||
|
if !eval {
|
||||||
|
self.enterGlobalScope()
|
||||||
|
defer func() {
|
||||||
|
self.leaveScope()
|
||||||
|
}()
|
||||||
|
}
|
||||||
self.cmpl_functionDeclaration(node.functionList)
|
self.cmpl_functionDeclaration(node.functionList)
|
||||||
self.cmpl_variableDeclaration(node.varList)
|
self.cmpl_variableDeclaration(node.varList)
|
||||||
|
self.scope.frame.file = node.file
|
||||||
return self.cmpl_evaluate_nodeStatementList(node.body)
|
return self.cmpl_evaluate_nodeStatementList(node.body)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *_runtime) cmpl_call_nodeFunction(function *_object, environment *_functionEnvironment, node *_nodeFunctionLiteral, this Value, argumentList []Value) Value {
|
func (self *_runtime) cmpl_call_nodeFunction(function *_object, stash *_fnStash, node *_nodeFunctionLiteral, this Value, argumentList []Value) Value {
|
||||||
|
|
||||||
indexOfParameterName := make([]string, len(argumentList))
|
indexOfParameterName := make([]string, len(argumentList))
|
||||||
// function(abc, def, ghi)
|
// function(abc, def, ghi)
|
||||||
|
@ -24,19 +31,21 @@ func (self *_runtime) cmpl_call_nodeFunction(function *_object, environment *_fu
|
||||||
if name == "arguments" {
|
if name == "arguments" {
|
||||||
argumentsFound = true
|
argumentsFound = true
|
||||||
}
|
}
|
||||||
value := UndefinedValue()
|
value := Value{}
|
||||||
if index < len(argumentList) {
|
if index < len(argumentList) {
|
||||||
value = argumentList[index]
|
value = argumentList[index]
|
||||||
indexOfParameterName[index] = name
|
indexOfParameterName[index] = name
|
||||||
}
|
}
|
||||||
self.localSet(name, value)
|
// strict = false
|
||||||
|
self.scope.lexical.setValue(name, value, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !argumentsFound {
|
if !argumentsFound {
|
||||||
arguments := self.newArgumentsObject(indexOfParameterName, environment, len(argumentList))
|
arguments := self.newArgumentsObject(indexOfParameterName, stash, len(argumentList))
|
||||||
arguments.defineProperty("callee", toValue_object(function), 0101, false)
|
arguments.defineProperty("callee", toValue_object(function), 0101, false)
|
||||||
environment.arguments = arguments
|
stash.arguments = arguments
|
||||||
self.localSet("arguments", toValue_object(arguments))
|
// strict = false
|
||||||
|
self.scope.lexical.setValue("arguments", toValue_object(arguments), false)
|
||||||
for index, _ := range argumentList {
|
for index, _ := range argumentList {
|
||||||
if index < len(node.parameterList) {
|
if index < len(node.parameterList) {
|
||||||
continue
|
continue
|
||||||
|
@ -50,38 +59,38 @@ func (self *_runtime) cmpl_call_nodeFunction(function *_object, environment *_fu
|
||||||
self.cmpl_variableDeclaration(node.varList)
|
self.cmpl_variableDeclaration(node.varList)
|
||||||
|
|
||||||
result := self.cmpl_evaluate_nodeStatement(node.body)
|
result := self.cmpl_evaluate_nodeStatement(node.body)
|
||||||
if result.isResult() {
|
if result.kind == valueResult {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
return UndefinedValue()
|
return Value{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *_runtime) cmpl_functionDeclaration(list []*_nodeFunctionLiteral) {
|
func (self *_runtime) cmpl_functionDeclaration(list []*_nodeFunctionLiteral) {
|
||||||
executionContext := self._executionContext(0)
|
executionContext := self.scope
|
||||||
eval := executionContext.eval
|
eval := executionContext.eval
|
||||||
environment := executionContext.VariableEnvironment
|
stash := executionContext.variable
|
||||||
|
|
||||||
for _, function := range list {
|
for _, function := range list {
|
||||||
name := function.name
|
name := function.name
|
||||||
value := self.cmpl_evaluate_nodeExpression(function)
|
value := self.cmpl_evaluate_nodeExpression(function)
|
||||||
if !environment.HasBinding(name) {
|
if !stash.hasBinding(name) {
|
||||||
environment.CreateMutableBinding(name, eval == true)
|
stash.createBinding(name, eval == true, value)
|
||||||
|
} else {
|
||||||
|
// TODO 10.5.5.e
|
||||||
|
stash.setBinding(name, value, false) // TODO strict
|
||||||
}
|
}
|
||||||
// TODO 10.5.5.e
|
|
||||||
environment.SetMutableBinding(name, value, false) // TODO strict
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *_runtime) cmpl_variableDeclaration(list []string) {
|
func (self *_runtime) cmpl_variableDeclaration(list []string) {
|
||||||
executionContext := self._executionContext(0)
|
executionContext := self.scope
|
||||||
eval := executionContext.eval
|
eval := executionContext.eval
|
||||||
environment := executionContext.VariableEnvironment
|
stash := executionContext.variable
|
||||||
|
|
||||||
for _, name := range list {
|
for _, name := range list {
|
||||||
if !environment.HasBinding(name) {
|
if !stash.hasBinding(name) {
|
||||||
environment.CreateMutableBinding(name, eval == true)
|
stash.createBinding(name, eval == true, Value{}) // TODO strict?
|
||||||
environment.SetMutableBinding(name, UndefinedValue(), false) // TODO strict
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -13,10 +13,10 @@ func (self *_runtime) cmpl_evaluate_nodeExpression(node _nodeExpression) Value {
|
||||||
// If the Interrupt channel is nil, then
|
// If the Interrupt channel is nil, then
|
||||||
// we avoid runtime.Gosched() overhead (if any)
|
// we avoid runtime.Gosched() overhead (if any)
|
||||||
// FIXME: Test this
|
// FIXME: Test this
|
||||||
if self.Otto.Interrupt != nil {
|
if self.otto.Interrupt != nil {
|
||||||
runtime.Gosched()
|
runtime.Gosched()
|
||||||
select {
|
select {
|
||||||
case value := <-self.Otto.Interrupt:
|
case value := <-self.otto.Interrupt:
|
||||||
value()
|
value()
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
|
@ -50,15 +50,14 @@ func (self *_runtime) cmpl_evaluate_nodeExpression(node _nodeExpression) Value {
|
||||||
return self.cmpl_evaluate_nodeDotExpression(node)
|
return self.cmpl_evaluate_nodeDotExpression(node)
|
||||||
|
|
||||||
case *_nodeFunctionLiteral:
|
case *_nodeFunctionLiteral:
|
||||||
var local = self.LexicalEnvironment()
|
var local = self.scope.lexical
|
||||||
if node.name != "" {
|
if node.name != "" {
|
||||||
local = self.newDeclarativeEnvironment(local)
|
local = self.newDeclarationStash(local)
|
||||||
}
|
}
|
||||||
|
|
||||||
value := toValue_object(self.newNodeFunction(node, local))
|
value := toValue_object(self.newNodeFunction(node, local))
|
||||||
if node.name != "" {
|
if node.name != "" {
|
||||||
local.CreateMutableBinding(node.name, false)
|
local.createBinding(node.name, false, value)
|
||||||
local.SetMutableBinding(node.name, value, false)
|
|
||||||
}
|
}
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
@ -67,7 +66,7 @@ func (self *_runtime) cmpl_evaluate_nodeExpression(node _nodeExpression) Value {
|
||||||
// TODO Should be true or false (strictness) depending on context
|
// TODO Should be true or false (strictness) depending on context
|
||||||
// getIdentifierReference should not return nil, but we check anyway and panic
|
// getIdentifierReference should not return nil, but we check anyway and panic
|
||||||
// so as not to propagate the nil into something else
|
// so as not to propagate the nil into something else
|
||||||
reference := getIdentifierReference(self.LexicalEnvironment(), name, false)
|
reference := getIdentifierReference(self, self.scope.lexical, name, false, _at(node.idx))
|
||||||
if reference == nil {
|
if reference == nil {
|
||||||
// Should never get here!
|
// Should never get here!
|
||||||
panic(hereBeDragons("referenceError == nil: " + name))
|
panic(hereBeDragons("referenceError == nil: " + name))
|
||||||
|
@ -90,7 +89,7 @@ func (self *_runtime) cmpl_evaluate_nodeExpression(node _nodeExpression) Value {
|
||||||
return self.cmpl_evaluate_nodeSequenceExpression(node)
|
return self.cmpl_evaluate_nodeSequenceExpression(node)
|
||||||
|
|
||||||
case *_nodeThisExpression:
|
case *_nodeThisExpression:
|
||||||
return toValue_object(self._executionContext(0).this)
|
return toValue_object(self.scope.this)
|
||||||
|
|
||||||
case *_nodeUnaryExpression:
|
case *_nodeUnaryExpression:
|
||||||
return self.cmpl_evaluate_nodeUnaryExpression(node)
|
return self.cmpl_evaluate_nodeUnaryExpression(node)
|
||||||
|
@ -108,9 +107,9 @@ func (self *_runtime) cmpl_evaluate_nodeArrayLiteral(node *_nodeArrayLiteral) Va
|
||||||
|
|
||||||
for _, node := range node.value {
|
for _, node := range node.value {
|
||||||
if node == nil {
|
if node == nil {
|
||||||
valueArray = append(valueArray, Value{})
|
valueArray = append(valueArray, emptyValue)
|
||||||
} else {
|
} else {
|
||||||
valueArray = append(valueArray, self.GetValue(self.cmpl_evaluate_nodeExpression(node)))
|
valueArray = append(valueArray, self.cmpl_evaluate_nodeExpression(node).resolve())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,14 +122,14 @@ func (self *_runtime) cmpl_evaluate_nodeAssignExpression(node *_nodeAssignExpres
|
||||||
|
|
||||||
left := self.cmpl_evaluate_nodeExpression(node.left)
|
left := self.cmpl_evaluate_nodeExpression(node.left)
|
||||||
right := self.cmpl_evaluate_nodeExpression(node.right)
|
right := self.cmpl_evaluate_nodeExpression(node.right)
|
||||||
rightValue := self.GetValue(right)
|
rightValue := right.resolve()
|
||||||
|
|
||||||
result := rightValue
|
result := rightValue
|
||||||
if node.operator != token.ASSIGN {
|
if node.operator != token.ASSIGN {
|
||||||
result = self.calculateBinaryExpression(node.operator, left, rightValue)
|
result = self.calculateBinaryExpression(node.operator, left, rightValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.PutValue(left.reference(), result)
|
self.putValue(left.reference(), result)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
@ -138,22 +137,22 @@ func (self *_runtime) cmpl_evaluate_nodeAssignExpression(node *_nodeAssignExpres
|
||||||
func (self *_runtime) cmpl_evaluate_nodeBinaryExpression(node *_nodeBinaryExpression) Value {
|
func (self *_runtime) cmpl_evaluate_nodeBinaryExpression(node *_nodeBinaryExpression) Value {
|
||||||
|
|
||||||
left := self.cmpl_evaluate_nodeExpression(node.left)
|
left := self.cmpl_evaluate_nodeExpression(node.left)
|
||||||
leftValue := self.GetValue(left)
|
leftValue := left.resolve()
|
||||||
|
|
||||||
switch node.operator {
|
switch node.operator {
|
||||||
// Logical
|
// Logical
|
||||||
case token.LOGICAL_AND:
|
case token.LOGICAL_AND:
|
||||||
if !toBoolean(leftValue) {
|
if !leftValue.bool() {
|
||||||
return leftValue
|
return leftValue
|
||||||
}
|
}
|
||||||
right := self.cmpl_evaluate_nodeExpression(node.right)
|
right := self.cmpl_evaluate_nodeExpression(node.right)
|
||||||
return self.GetValue(right)
|
return right.resolve()
|
||||||
case token.LOGICAL_OR:
|
case token.LOGICAL_OR:
|
||||||
if toBoolean(leftValue) {
|
if leftValue.bool() {
|
||||||
return leftValue
|
return leftValue
|
||||||
}
|
}
|
||||||
right := self.cmpl_evaluate_nodeExpression(node.right)
|
right := self.cmpl_evaluate_nodeExpression(node.right)
|
||||||
return self.GetValue(right)
|
return right.resolve()
|
||||||
}
|
}
|
||||||
|
|
||||||
return self.calculateBinaryExpression(node.operator, leftValue, self.cmpl_evaluate_nodeExpression(node.right))
|
return self.calculateBinaryExpression(node.operator, leftValue, self.cmpl_evaluate_nodeExpression(node.right))
|
||||||
|
@ -161,57 +160,90 @@ func (self *_runtime) cmpl_evaluate_nodeBinaryExpression(node *_nodeBinaryExpres
|
||||||
|
|
||||||
func (self *_runtime) cmpl_evaluate_nodeBinaryExpression_comparison(node *_nodeBinaryExpression) Value {
|
func (self *_runtime) cmpl_evaluate_nodeBinaryExpression_comparison(node *_nodeBinaryExpression) Value {
|
||||||
|
|
||||||
left := self.GetValue(self.cmpl_evaluate_nodeExpression(node.left))
|
left := self.cmpl_evaluate_nodeExpression(node.left).resolve()
|
||||||
right := self.GetValue(self.cmpl_evaluate_nodeExpression(node.right))
|
right := self.cmpl_evaluate_nodeExpression(node.right).resolve()
|
||||||
|
|
||||||
return toValue_bool(self.calculateComparison(node.operator, left, right))
|
return toValue_bool(self.calculateComparison(node.operator, left, right))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *_runtime) cmpl_evaluate_nodeBracketExpression(node *_nodeBracketExpression) Value {
|
func (self *_runtime) cmpl_evaluate_nodeBracketExpression(node *_nodeBracketExpression) Value {
|
||||||
target := self.cmpl_evaluate_nodeExpression(node.left)
|
target := self.cmpl_evaluate_nodeExpression(node.left)
|
||||||
targetValue := self.GetValue(target)
|
targetValue := target.resolve()
|
||||||
member := self.cmpl_evaluate_nodeExpression(node.member)
|
member := self.cmpl_evaluate_nodeExpression(node.member)
|
||||||
memberValue := self.GetValue(member)
|
memberValue := member.resolve()
|
||||||
|
|
||||||
// TODO Pass in base value as-is, and defer toObject till later?
|
// TODO Pass in base value as-is, and defer toObject till later?
|
||||||
return toValue(newPropertyReference(self.toObject(targetValue), toString(memberValue), false))
|
return toValue(newPropertyReference(self, self.toObject(targetValue), memberValue.string(), false, _at(node.idx)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *_runtime) cmpl_evaluate_nodeCallExpression(node *_nodeCallExpression, withArgumentList []interface{}) Value {
|
func (self *_runtime) cmpl_evaluate_nodeCallExpression(node *_nodeCallExpression, withArgumentList []interface{}) Value {
|
||||||
|
rt := self
|
||||||
|
this := Value{}
|
||||||
callee := self.cmpl_evaluate_nodeExpression(node.callee)
|
callee := self.cmpl_evaluate_nodeExpression(node.callee)
|
||||||
calleeValue := self.GetValue(callee)
|
|
||||||
argumentList := []Value{}
|
argumentList := []Value{}
|
||||||
if withArgumentList != nil {
|
if withArgumentList != nil {
|
||||||
argumentList = self.toValueArray(withArgumentList...)
|
argumentList = self.toValueArray(withArgumentList...)
|
||||||
} else {
|
} else {
|
||||||
for _, argumentNode := range node.argumentList {
|
for _, argumentNode := range node.argumentList {
|
||||||
argumentList = append(argumentList, self.GetValue(self.cmpl_evaluate_nodeExpression(argumentNode)))
|
argumentList = append(argumentList, self.cmpl_evaluate_nodeExpression(argumentNode).resolve())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this := UndefinedValue()
|
|
||||||
calleeReference := callee.reference()
|
rf := callee.reference()
|
||||||
evalHint := false
|
vl := callee.resolve()
|
||||||
if calleeReference != nil {
|
|
||||||
if calleeReference.IsPropertyReference() {
|
eval := false // Whether this call is a (candidate for) direct call to eval
|
||||||
calleeObject := calleeReference.GetBase().(*_object)
|
name := ""
|
||||||
this = toValue_object(calleeObject)
|
if rf != nil {
|
||||||
} else {
|
switch rf := rf.(type) {
|
||||||
// TODO ImplictThisValue
|
case *_propertyReference:
|
||||||
}
|
name = rf.name
|
||||||
if calleeReference.GetName() == "eval" {
|
object := rf.base
|
||||||
evalHint = true // Possible direct eval
|
this = toValue_object(object)
|
||||||
|
eval = rf.name == "eval" // Possible direct eval
|
||||||
|
case *_stashReference:
|
||||||
|
// TODO ImplicitThisValue
|
||||||
|
name = rf.name
|
||||||
|
eval = rf.name == "eval" // Possible direct eval
|
||||||
|
default:
|
||||||
|
// FIXME?
|
||||||
|
panic(rt.panicTypeError("Here be dragons"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !calleeValue.IsFunction() {
|
|
||||||
panic(newTypeError("%v is not a function", calleeValue))
|
at := _at(-1)
|
||||||
|
switch callee := node.callee.(type) {
|
||||||
|
case *_nodeIdentifier:
|
||||||
|
at = _at(callee.idx)
|
||||||
|
case *_nodeDotExpression:
|
||||||
|
at = _at(callee.idx)
|
||||||
|
case *_nodeBracketExpression:
|
||||||
|
at = _at(callee.idx)
|
||||||
}
|
}
|
||||||
return self.Call(calleeValue._object(), this, argumentList, evalHint)
|
|
||||||
|
frame := _frame{
|
||||||
|
callee: name,
|
||||||
|
file: self.scope.frame.file,
|
||||||
|
}
|
||||||
|
|
||||||
|
if !vl.IsFunction() {
|
||||||
|
if name == "" {
|
||||||
|
// FIXME Maybe typeof?
|
||||||
|
panic(rt.panicTypeError("%v is not a function", vl, at))
|
||||||
|
}
|
||||||
|
panic(rt.panicTypeError("'%s' is not a function", name, at))
|
||||||
|
}
|
||||||
|
|
||||||
|
self.scope.frame.offset = int(at)
|
||||||
|
|
||||||
|
return vl._object().call(this, argumentList, eval, frame)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *_runtime) cmpl_evaluate_nodeConditionalExpression(node *_nodeConditionalExpression) Value {
|
func (self *_runtime) cmpl_evaluate_nodeConditionalExpression(node *_nodeConditionalExpression) Value {
|
||||||
test := self.cmpl_evaluate_nodeExpression(node.test)
|
test := self.cmpl_evaluate_nodeExpression(node.test)
|
||||||
testValue := self.GetValue(test)
|
testValue := test.resolve()
|
||||||
if toBoolean(testValue) {
|
if testValue.bool() {
|
||||||
return self.cmpl_evaluate_nodeExpression(node.consequent)
|
return self.cmpl_evaluate_nodeExpression(node.consequent)
|
||||||
}
|
}
|
||||||
return self.cmpl_evaluate_nodeExpression(node.alternate)
|
return self.cmpl_evaluate_nodeExpression(node.alternate)
|
||||||
|
@ -219,27 +251,60 @@ func (self *_runtime) cmpl_evaluate_nodeConditionalExpression(node *_nodeConditi
|
||||||
|
|
||||||
func (self *_runtime) cmpl_evaluate_nodeDotExpression(node *_nodeDotExpression) Value {
|
func (self *_runtime) cmpl_evaluate_nodeDotExpression(node *_nodeDotExpression) Value {
|
||||||
target := self.cmpl_evaluate_nodeExpression(node.left)
|
target := self.cmpl_evaluate_nodeExpression(node.left)
|
||||||
targetValue := self.GetValue(target)
|
targetValue := target.resolve()
|
||||||
// TODO Pass in base value as-is, and defer toObject till later?
|
// TODO Pass in base value as-is, and defer toObject till later?
|
||||||
object, err := self.objectCoerce(targetValue)
|
object, err := self.objectCoerce(targetValue)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(newTypeError(fmt.Sprintf("Cannot access member '%s' of %s", node.identifier, err.Error())))
|
panic(self.panicTypeError("Cannot access member '%s' of %s", node.identifier, err.Error()))
|
||||||
}
|
}
|
||||||
return toValue(newPropertyReference(object, node.identifier, false))
|
return toValue(newPropertyReference(self, object, node.identifier, false, _at(node.idx)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *_runtime) cmpl_evaluate_nodeNewExpression(node *_nodeNewExpression) Value {
|
func (self *_runtime) cmpl_evaluate_nodeNewExpression(node *_nodeNewExpression) Value {
|
||||||
|
rt := self
|
||||||
callee := self.cmpl_evaluate_nodeExpression(node.callee)
|
callee := self.cmpl_evaluate_nodeExpression(node.callee)
|
||||||
calleeValue := self.GetValue(callee)
|
|
||||||
argumentList := []Value{}
|
argumentList := []Value{}
|
||||||
for _, argumentNode := range node.argumentList {
|
for _, argumentNode := range node.argumentList {
|
||||||
argumentList = append(argumentList, self.GetValue(self.cmpl_evaluate_nodeExpression(argumentNode)))
|
argumentList = append(argumentList, self.cmpl_evaluate_nodeExpression(argumentNode).resolve())
|
||||||
}
|
}
|
||||||
this := UndefinedValue()
|
|
||||||
if !calleeValue.IsFunction() {
|
rf := callee.reference()
|
||||||
panic(newTypeError("%v is not a function", calleeValue))
|
vl := callee.resolve()
|
||||||
|
|
||||||
|
name := ""
|
||||||
|
if rf != nil {
|
||||||
|
switch rf := rf.(type) {
|
||||||
|
case *_propertyReference:
|
||||||
|
name = rf.name
|
||||||
|
case *_stashReference:
|
||||||
|
name = rf.name
|
||||||
|
default:
|
||||||
|
panic(rt.panicTypeError("Here be dragons"))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return calleeValue._object().Construct(this, argumentList)
|
|
||||||
|
at := _at(-1)
|
||||||
|
switch callee := node.callee.(type) {
|
||||||
|
case *_nodeIdentifier:
|
||||||
|
at = _at(callee.idx)
|
||||||
|
case *_nodeDotExpression:
|
||||||
|
at = _at(callee.idx)
|
||||||
|
case *_nodeBracketExpression:
|
||||||
|
at = _at(callee.idx)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !vl.IsFunction() {
|
||||||
|
if name == "" {
|
||||||
|
// FIXME Maybe typeof?
|
||||||
|
panic(rt.panicTypeError("%v is not a function", vl, at))
|
||||||
|
}
|
||||||
|
panic(rt.panicTypeError("'%s' is not a function", name, at))
|
||||||
|
}
|
||||||
|
|
||||||
|
self.scope.frame.offset = int(at)
|
||||||
|
|
||||||
|
return vl._object().construct(argumentList)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *_runtime) cmpl_evaluate_nodeObjectLiteral(node *_nodeObjectLiteral) Value {
|
func (self *_runtime) cmpl_evaluate_nodeObjectLiteral(node *_nodeObjectLiteral) Value {
|
||||||
|
@ -249,15 +314,15 @@ func (self *_runtime) cmpl_evaluate_nodeObjectLiteral(node *_nodeObjectLiteral)
|
||||||
for _, property := range node.value {
|
for _, property := range node.value {
|
||||||
switch property.kind {
|
switch property.kind {
|
||||||
case "value":
|
case "value":
|
||||||
result.defineProperty(property.key, self.GetValue(self.cmpl_evaluate_nodeExpression(property.value)), 0111, false)
|
result.defineProperty(property.key, self.cmpl_evaluate_nodeExpression(property.value).resolve(), 0111, false)
|
||||||
case "get":
|
case "get":
|
||||||
getter := self.newNodeFunction(property.value.(*_nodeFunctionLiteral), self.LexicalEnvironment())
|
getter := self.newNodeFunction(property.value.(*_nodeFunctionLiteral), self.scope.lexical)
|
||||||
descriptor := _property{}
|
descriptor := _property{}
|
||||||
descriptor.mode = 0211
|
descriptor.mode = 0211
|
||||||
descriptor.value = _propertyGetSet{getter, nil}
|
descriptor.value = _propertyGetSet{getter, nil}
|
||||||
result.defineOwnProperty(property.key, descriptor, false)
|
result.defineOwnProperty(property.key, descriptor, false)
|
||||||
case "set":
|
case "set":
|
||||||
setter := self.newNodeFunction(property.value.(*_nodeFunctionLiteral), self.LexicalEnvironment())
|
setter := self.newNodeFunction(property.value.(*_nodeFunctionLiteral), self.scope.lexical)
|
||||||
descriptor := _property{}
|
descriptor := _property{}
|
||||||
descriptor.mode = 0211
|
descriptor.mode = 0211
|
||||||
descriptor.value = _propertyGetSet{nil, setter}
|
descriptor.value = _propertyGetSet{nil, setter}
|
||||||
|
@ -274,7 +339,7 @@ func (self *_runtime) cmpl_evaluate_nodeSequenceExpression(node *_nodeSequenceEx
|
||||||
var result Value
|
var result Value
|
||||||
for _, node := range node.sequence {
|
for _, node := range node.sequence {
|
||||||
result = self.cmpl_evaluate_nodeExpression(node)
|
result = self.cmpl_evaluate_nodeExpression(node)
|
||||||
result = self.GetValue(result)
|
result = result.resolve()
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
@ -284,31 +349,31 @@ func (self *_runtime) cmpl_evaluate_nodeUnaryExpression(node *_nodeUnaryExpressi
|
||||||
target := self.cmpl_evaluate_nodeExpression(node.operand)
|
target := self.cmpl_evaluate_nodeExpression(node.operand)
|
||||||
switch node.operator {
|
switch node.operator {
|
||||||
case token.TYPEOF, token.DELETE:
|
case token.TYPEOF, token.DELETE:
|
||||||
if target._valueType == valueReference && target.reference().IsUnresolvable() {
|
if target.kind == valueReference && target.reference().invalid() {
|
||||||
if node.operator == token.TYPEOF {
|
if node.operator == token.TYPEOF {
|
||||||
return toValue_string("undefined")
|
return toValue_string("undefined")
|
||||||
}
|
}
|
||||||
return TrueValue()
|
return trueValue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
switch node.operator {
|
switch node.operator {
|
||||||
case token.NOT:
|
case token.NOT:
|
||||||
targetValue := self.GetValue(target)
|
targetValue := target.resolve()
|
||||||
if targetValue.toBoolean() {
|
if targetValue.bool() {
|
||||||
return FalseValue()
|
return falseValue
|
||||||
}
|
}
|
||||||
return TrueValue()
|
return trueValue
|
||||||
case token.BITWISE_NOT:
|
case token.BITWISE_NOT:
|
||||||
targetValue := self.GetValue(target)
|
targetValue := target.resolve()
|
||||||
integerValue := toInt32(targetValue)
|
integerValue := toInt32(targetValue)
|
||||||
return toValue_int32(^integerValue)
|
return toValue_int32(^integerValue)
|
||||||
case token.PLUS:
|
case token.PLUS:
|
||||||
targetValue := self.GetValue(target)
|
targetValue := target.resolve()
|
||||||
return toValue_float64(targetValue.toFloat())
|
return toValue_float64(targetValue.float64())
|
||||||
case token.MINUS:
|
case token.MINUS:
|
||||||
targetValue := self.GetValue(target)
|
targetValue := target.resolve()
|
||||||
value := targetValue.toFloat()
|
value := targetValue.float64()
|
||||||
// TODO Test this
|
// TODO Test this
|
||||||
sign := float64(-1)
|
sign := float64(-1)
|
||||||
if math.Signbit(value) {
|
if math.Signbit(value) {
|
||||||
|
@ -316,45 +381,45 @@ func (self *_runtime) cmpl_evaluate_nodeUnaryExpression(node *_nodeUnaryExpressi
|
||||||
}
|
}
|
||||||
return toValue_float64(math.Copysign(value, sign))
|
return toValue_float64(math.Copysign(value, sign))
|
||||||
case token.INCREMENT:
|
case token.INCREMENT:
|
||||||
targetValue := self.GetValue(target)
|
targetValue := target.resolve()
|
||||||
if node.postfix {
|
if node.postfix {
|
||||||
// Postfix++
|
// Postfix++
|
||||||
oldValue := targetValue.toFloat()
|
oldValue := targetValue.float64()
|
||||||
newValue := toValue_float64(+1 + oldValue)
|
newValue := toValue_float64(+1 + oldValue)
|
||||||
self.PutValue(target.reference(), newValue)
|
self.putValue(target.reference(), newValue)
|
||||||
return toValue_float64(oldValue)
|
return toValue_float64(oldValue)
|
||||||
} else {
|
} else {
|
||||||
// ++Prefix
|
// ++Prefix
|
||||||
newValue := toValue_float64(+1 + targetValue.toFloat())
|
newValue := toValue_float64(+1 + targetValue.float64())
|
||||||
self.PutValue(target.reference(), newValue)
|
self.putValue(target.reference(), newValue)
|
||||||
return newValue
|
return newValue
|
||||||
}
|
}
|
||||||
case token.DECREMENT:
|
case token.DECREMENT:
|
||||||
targetValue := self.GetValue(target)
|
targetValue := target.resolve()
|
||||||
if node.postfix {
|
if node.postfix {
|
||||||
// Postfix--
|
// Postfix--
|
||||||
oldValue := targetValue.toFloat()
|
oldValue := targetValue.float64()
|
||||||
newValue := toValue_float64(-1 + oldValue)
|
newValue := toValue_float64(-1 + oldValue)
|
||||||
self.PutValue(target.reference(), newValue)
|
self.putValue(target.reference(), newValue)
|
||||||
return toValue_float64(oldValue)
|
return toValue_float64(oldValue)
|
||||||
} else {
|
} else {
|
||||||
// --Prefix
|
// --Prefix
|
||||||
newValue := toValue_float64(-1 + targetValue.toFloat())
|
newValue := toValue_float64(-1 + targetValue.float64())
|
||||||
self.PutValue(target.reference(), newValue)
|
self.putValue(target.reference(), newValue)
|
||||||
return newValue
|
return newValue
|
||||||
}
|
}
|
||||||
case token.VOID:
|
case token.VOID:
|
||||||
self.GetValue(target) // FIXME Side effect?
|
target.resolve() // FIXME Side effect?
|
||||||
return UndefinedValue()
|
return Value{}
|
||||||
case token.DELETE:
|
case token.DELETE:
|
||||||
reference := target.reference()
|
reference := target.reference()
|
||||||
if reference == nil {
|
if reference == nil {
|
||||||
return TrueValue()
|
return trueValue
|
||||||
}
|
}
|
||||||
return toValue_bool(target.reference().Delete())
|
return toValue_bool(target.reference().delete())
|
||||||
case token.TYPEOF:
|
case token.TYPEOF:
|
||||||
targetValue := self.GetValue(target)
|
targetValue := target.resolve()
|
||||||
switch targetValue._valueType {
|
switch targetValue.kind {
|
||||||
case valueUndefined:
|
case valueUndefined:
|
||||||
return toValue_string("undefined")
|
return toValue_string("undefined")
|
||||||
case valueNull:
|
case valueNull:
|
||||||
|
@ -366,7 +431,7 @@ func (self *_runtime) cmpl_evaluate_nodeUnaryExpression(node *_nodeUnaryExpressi
|
||||||
case valueString:
|
case valueString:
|
||||||
return toValue_string("string")
|
return toValue_string("string")
|
||||||
case valueObject:
|
case valueObject:
|
||||||
if targetValue._object().functionValue().call != nil {
|
if targetValue._object().isCall() {
|
||||||
return toValue_string("function")
|
return toValue_string("function")
|
||||||
}
|
}
|
||||||
return toValue_string("object")
|
return toValue_string("object")
|
||||||
|
@ -381,11 +446,11 @@ func (self *_runtime) cmpl_evaluate_nodeUnaryExpression(node *_nodeUnaryExpressi
|
||||||
func (self *_runtime) cmpl_evaluate_nodeVariableExpression(node *_nodeVariableExpression) Value {
|
func (self *_runtime) cmpl_evaluate_nodeVariableExpression(node *_nodeVariableExpression) Value {
|
||||||
if node.initializer != nil {
|
if node.initializer != nil {
|
||||||
// FIXME If reference is nil
|
// FIXME If reference is nil
|
||||||
left := getIdentifierReference(self.LexicalEnvironment(), node.name, false)
|
left := getIdentifierReference(self, self.scope.lexical, node.name, false, _at(node.idx))
|
||||||
right := self.cmpl_evaluate_nodeExpression(node.initializer)
|
right := self.cmpl_evaluate_nodeExpression(node.initializer)
|
||||||
rightValue := self.GetValue(right)
|
rightValue := right.resolve()
|
||||||
|
|
||||||
self.PutValue(left, rightValue)
|
self.putValue(left, rightValue)
|
||||||
}
|
}
|
||||||
return toValue_string(node.name)
|
return toValue_string(node.name)
|
||||||
}
|
}
|
|
@ -12,10 +12,10 @@ func (self *_runtime) cmpl_evaluate_nodeStatement(node _nodeStatement) Value {
|
||||||
// If the Interrupt channel is nil, then
|
// If the Interrupt channel is nil, then
|
||||||
// we avoid runtime.Gosched() overhead (if any)
|
// we avoid runtime.Gosched() overhead (if any)
|
||||||
// FIXME: Test this
|
// FIXME: Test this
|
||||||
if self.Otto.Interrupt != nil {
|
if self.otto.Interrupt != nil {
|
||||||
runtime.Gosched()
|
runtime.Gosched()
|
||||||
select {
|
select {
|
||||||
case value := <-self.Otto.Interrupt:
|
case value := <-self.otto.Interrupt:
|
||||||
value()
|
value()
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
|
@ -24,8 +24,18 @@ func (self *_runtime) cmpl_evaluate_nodeStatement(node _nodeStatement) Value {
|
||||||
switch node := node.(type) {
|
switch node := node.(type) {
|
||||||
|
|
||||||
case *_nodeBlockStatement:
|
case *_nodeBlockStatement:
|
||||||
// FIXME If result is break, then return the empty value?
|
labels := self.labels
|
||||||
return self.cmpl_evaluate_nodeStatementList(node.list)
|
self.labels = nil
|
||||||
|
|
||||||
|
value := self.cmpl_evaluate_nodeStatementList(node.list)
|
||||||
|
switch value.kind {
|
||||||
|
case valueResult:
|
||||||
|
switch value.evaluateBreak(labels) {
|
||||||
|
case resultBreak:
|
||||||
|
return emptyValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
|
||||||
case *_nodeBranchStatement:
|
case *_nodeBranchStatement:
|
||||||
target := node.label
|
target := node.label
|
||||||
|
@ -37,13 +47,13 @@ func (self *_runtime) cmpl_evaluate_nodeStatement(node _nodeStatement) Value {
|
||||||
}
|
}
|
||||||
|
|
||||||
case *_nodeDebuggerStatement:
|
case *_nodeDebuggerStatement:
|
||||||
return Value{} // Nothing happens.
|
return emptyValue // Nothing happens.
|
||||||
|
|
||||||
case *_nodeDoWhileStatement:
|
case *_nodeDoWhileStatement:
|
||||||
return self.cmpl_evaluate_nodeDoWhileStatement(node)
|
return self.cmpl_evaluate_nodeDoWhileStatement(node)
|
||||||
|
|
||||||
case *_nodeEmptyStatement:
|
case *_nodeEmptyStatement:
|
||||||
return Value{}
|
return emptyValue
|
||||||
|
|
||||||
case *_nodeExpressionStatement:
|
case *_nodeExpressionStatement:
|
||||||
return self.cmpl_evaluate_nodeExpression(node.expression)
|
return self.cmpl_evaluate_nodeExpression(node.expression)
|
||||||
|
@ -70,15 +80,15 @@ func (self *_runtime) cmpl_evaluate_nodeStatement(node _nodeStatement) Value {
|
||||||
|
|
||||||
case *_nodeReturnStatement:
|
case *_nodeReturnStatement:
|
||||||
if node.argument != nil {
|
if node.argument != nil {
|
||||||
return toValue(newReturnResult(self.GetValue(self.cmpl_evaluate_nodeExpression(node.argument))))
|
return toValue(newReturnResult(self.cmpl_evaluate_nodeExpression(node.argument).resolve()))
|
||||||
}
|
}
|
||||||
return toValue(newReturnResult(UndefinedValue()))
|
return toValue(newReturnResult(Value{}))
|
||||||
|
|
||||||
case *_nodeSwitchStatement:
|
case *_nodeSwitchStatement:
|
||||||
return self.cmpl_evaluate_nodeSwitchStatement(node)
|
return self.cmpl_evaluate_nodeSwitchStatement(node)
|
||||||
|
|
||||||
case *_nodeThrowStatement:
|
case *_nodeThrowStatement:
|
||||||
value := self.GetValue(self.cmpl_evaluate_nodeExpression(node.argument))
|
value := self.cmpl_evaluate_nodeExpression(node.argument).resolve()
|
||||||
panic(newException(value))
|
panic(newException(value))
|
||||||
|
|
||||||
case *_nodeTryStatement:
|
case *_nodeTryStatement:
|
||||||
|
@ -89,7 +99,7 @@ func (self *_runtime) cmpl_evaluate_nodeStatement(node _nodeStatement) Value {
|
||||||
for _, variable := range node.list {
|
for _, variable := range node.list {
|
||||||
self.cmpl_evaluate_nodeVariableExpression(variable.(*_nodeVariableExpression))
|
self.cmpl_evaluate_nodeVariableExpression(variable.(*_nodeVariableExpression))
|
||||||
}
|
}
|
||||||
return Value{}
|
return emptyValue
|
||||||
|
|
||||||
case *_nodeWhileStatement:
|
case *_nodeWhileStatement:
|
||||||
return self.cmpl_evaluate_nodeWhileStatement(node)
|
return self.cmpl_evaluate_nodeWhileStatement(node)
|
||||||
|
@ -106,17 +116,17 @@ func (self *_runtime) cmpl_evaluate_nodeStatementList(list []_nodeStatement) Val
|
||||||
var result Value
|
var result Value
|
||||||
for _, node := range list {
|
for _, node := range list {
|
||||||
value := self.cmpl_evaluate_nodeStatement(node)
|
value := self.cmpl_evaluate_nodeStatement(node)
|
||||||
switch value._valueType {
|
switch value.kind {
|
||||||
case valueResult:
|
case valueResult:
|
||||||
return value
|
return value
|
||||||
case valueEmpty:
|
case valueEmpty:
|
||||||
default:
|
default:
|
||||||
// We have GetValue here to (for example) trigger a
|
// We have getValue here to (for example) trigger a
|
||||||
// ReferenceError (of the not defined variety)
|
// ReferenceError (of the not defined variety)
|
||||||
// Not sure if this is the best way to error out early
|
// Not sure if this is the best way to error out early
|
||||||
// for such errors or if there is a better way
|
// for such errors or if there is a better way
|
||||||
// TODO Do we still need this?
|
// TODO Do we still need this?
|
||||||
result = self.GetValue(value)
|
result = value.resolve()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
|
@ -129,12 +139,12 @@ func (self *_runtime) cmpl_evaluate_nodeDoWhileStatement(node *_nodeDoWhileState
|
||||||
|
|
||||||
test := node.test
|
test := node.test
|
||||||
|
|
||||||
result := Value{}
|
result := emptyValue
|
||||||
resultBreak:
|
resultBreak:
|
||||||
for {
|
for {
|
||||||
for _, node := range node.body {
|
for _, node := range node.body {
|
||||||
value := self.cmpl_evaluate_nodeStatement(node)
|
value := self.cmpl_evaluate_nodeStatement(node)
|
||||||
switch value._valueType {
|
switch value.kind {
|
||||||
case valueResult:
|
case valueResult:
|
||||||
switch value.evaluateBreakContinue(labels) {
|
switch value.evaluateBreakContinue(labels) {
|
||||||
case resultReturn:
|
case resultReturn:
|
||||||
|
@ -150,7 +160,7 @@ resultBreak:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
resultContinue:
|
resultContinue:
|
||||||
if !self.GetValue(self.cmpl_evaluate_nodeExpression(test)).isTrue() {
|
if !self.cmpl_evaluate_nodeExpression(test).resolve().bool() {
|
||||||
// Stahp: do ... while (false)
|
// Stahp: do ... while (false)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -164,11 +174,11 @@ func (self *_runtime) cmpl_evaluate_nodeForInStatement(node *_nodeForInStatement
|
||||||
self.labels = nil
|
self.labels = nil
|
||||||
|
|
||||||
source := self.cmpl_evaluate_nodeExpression(node.source)
|
source := self.cmpl_evaluate_nodeExpression(node.source)
|
||||||
sourceValue := self.GetValue(source)
|
sourceValue := source.resolve()
|
||||||
|
|
||||||
switch sourceValue._valueType {
|
switch sourceValue.kind {
|
||||||
case valueUndefined, valueNull:
|
case valueUndefined, valueNull:
|
||||||
return emptyValue()
|
return emptyValue
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceObject := self.toObject(sourceValue)
|
sourceObject := self.toObject(sourceValue)
|
||||||
|
@ -176,22 +186,22 @@ func (self *_runtime) cmpl_evaluate_nodeForInStatement(node *_nodeForInStatement
|
||||||
into := node.into
|
into := node.into
|
||||||
body := node.body
|
body := node.body
|
||||||
|
|
||||||
result := Value{}
|
result := emptyValue
|
||||||
object := sourceObject
|
object := sourceObject
|
||||||
for object != nil {
|
for object != nil {
|
||||||
enumerateValue := Value{}
|
enumerateValue := emptyValue
|
||||||
object.enumerate(false, func(name string) bool {
|
object.enumerate(false, func(name string) bool {
|
||||||
into := self.cmpl_evaluate_nodeExpression(into)
|
into := self.cmpl_evaluate_nodeExpression(into)
|
||||||
// In the case of: for (var abc in def) ...
|
// In the case of: for (var abc in def) ...
|
||||||
if into.reference() == nil {
|
if into.reference() == nil {
|
||||||
identifier := toString(into)
|
identifier := into.string()
|
||||||
// TODO Should be true or false (strictness) depending on context
|
// TODO Should be true or false (strictness) depending on context
|
||||||
into = toValue(getIdentifierReference(self.LexicalEnvironment(), identifier, false))
|
into = toValue(getIdentifierReference(self, self.scope.lexical, identifier, false, -1))
|
||||||
}
|
}
|
||||||
self.PutValue(into.reference(), toValue_string(name))
|
self.putValue(into.reference(), toValue_string(name))
|
||||||
for _, node := range body {
|
for _, node := range body {
|
||||||
value := self.cmpl_evaluate_nodeStatement(node)
|
value := self.cmpl_evaluate_nodeStatement(node)
|
||||||
switch value._valueType {
|
switch value.kind {
|
||||||
case valueResult:
|
case valueResult:
|
||||||
switch value.evaluateBreakContinue(labels) {
|
switch value.evaluateBreakContinue(labels) {
|
||||||
case resultReturn:
|
case resultReturn:
|
||||||
|
@ -233,22 +243,22 @@ func (self *_runtime) cmpl_evaluate_nodeForStatement(node *_nodeForStatement) Va
|
||||||
|
|
||||||
if initializer != nil {
|
if initializer != nil {
|
||||||
initialResult := self.cmpl_evaluate_nodeExpression(initializer)
|
initialResult := self.cmpl_evaluate_nodeExpression(initializer)
|
||||||
self.GetValue(initialResult) // Side-effect trigger
|
initialResult.resolve() // Side-effect trigger
|
||||||
}
|
}
|
||||||
|
|
||||||
result := Value{}
|
result := emptyValue
|
||||||
resultBreak:
|
resultBreak:
|
||||||
for {
|
for {
|
||||||
if test != nil {
|
if test != nil {
|
||||||
testResult := self.cmpl_evaluate_nodeExpression(test)
|
testResult := self.cmpl_evaluate_nodeExpression(test)
|
||||||
testResultValue := self.GetValue(testResult)
|
testResultValue := testResult.resolve()
|
||||||
if toBoolean(testResultValue) == false {
|
if testResultValue.bool() == false {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, node := range body {
|
for _, node := range body {
|
||||||
value := self.cmpl_evaluate_nodeStatement(node)
|
value := self.cmpl_evaluate_nodeStatement(node)
|
||||||
switch value._valueType {
|
switch value.kind {
|
||||||
case valueResult:
|
case valueResult:
|
||||||
switch value.evaluateBreakContinue(labels) {
|
switch value.evaluateBreakContinue(labels) {
|
||||||
case resultReturn:
|
case resultReturn:
|
||||||
|
@ -266,7 +276,7 @@ resultBreak:
|
||||||
resultContinue:
|
resultContinue:
|
||||||
if update != nil {
|
if update != nil {
|
||||||
updateResult := self.cmpl_evaluate_nodeExpression(update)
|
updateResult := self.cmpl_evaluate_nodeExpression(update)
|
||||||
self.GetValue(updateResult) // Side-effect trigger
|
updateResult.resolve() // Side-effect trigger
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
|
@ -274,14 +284,14 @@ resultBreak:
|
||||||
|
|
||||||
func (self *_runtime) cmpl_evaluate_nodeIfStatement(node *_nodeIfStatement) Value {
|
func (self *_runtime) cmpl_evaluate_nodeIfStatement(node *_nodeIfStatement) Value {
|
||||||
test := self.cmpl_evaluate_nodeExpression(node.test)
|
test := self.cmpl_evaluate_nodeExpression(node.test)
|
||||||
testValue := self.GetValue(test)
|
testValue := test.resolve()
|
||||||
if toBoolean(testValue) {
|
if testValue.bool() {
|
||||||
return self.cmpl_evaluate_nodeStatement(node.consequent)
|
return self.cmpl_evaluate_nodeStatement(node.consequent)
|
||||||
} else if node.alternate != nil {
|
} else if node.alternate != nil {
|
||||||
return self.cmpl_evaluate_nodeStatement(node.alternate)
|
return self.cmpl_evaluate_nodeStatement(node.alternate)
|
||||||
}
|
}
|
||||||
|
|
||||||
return Value{}
|
return emptyValue
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *_runtime) cmpl_evaluate_nodeSwitchStatement(node *_nodeSwitchStatement) Value {
|
func (self *_runtime) cmpl_evaluate_nodeSwitchStatement(node *_nodeSwitchStatement) Value {
|
||||||
|
@ -302,18 +312,18 @@ func (self *_runtime) cmpl_evaluate_nodeSwitchStatement(node *_nodeSwitchStateme
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
result := Value{}
|
result := emptyValue
|
||||||
if target != -1 {
|
if target != -1 {
|
||||||
for _, clause := range node.body[target:] {
|
for _, clause := range node.body[target:] {
|
||||||
for _, statement := range clause.consequent {
|
for _, statement := range clause.consequent {
|
||||||
value := self.cmpl_evaluate_nodeStatement(statement)
|
value := self.cmpl_evaluate_nodeStatement(statement)
|
||||||
switch value._valueType {
|
switch value.kind {
|
||||||
case valueResult:
|
case valueResult:
|
||||||
switch value.evaluateBreak(labels) {
|
switch value.evaluateBreak(labels) {
|
||||||
case resultReturn:
|
case resultReturn:
|
||||||
return value
|
return value
|
||||||
case resultBreak:
|
case resultBreak:
|
||||||
return Value{}
|
return emptyValue
|
||||||
}
|
}
|
||||||
case valueEmpty:
|
case valueEmpty:
|
||||||
default:
|
default:
|
||||||
|
@ -332,14 +342,15 @@ func (self *_runtime) cmpl_evaluate_nodeTryStatement(node *_nodeTryStatement) Va
|
||||||
})
|
})
|
||||||
|
|
||||||
if exception && node.catch != nil {
|
if exception && node.catch != nil {
|
||||||
|
outer := self.scope.lexical
|
||||||
lexicalEnvironment := self._executionContext(0).newDeclarativeEnvironment(self)
|
self.scope.lexical = self.newDeclarationStash(outer)
|
||||||
defer func() {
|
defer func() {
|
||||||
self._executionContext(0).LexicalEnvironment = lexicalEnvironment
|
self.scope.lexical = outer
|
||||||
}()
|
}()
|
||||||
// TODO If necessary, convert TypeError<runtime> => TypeError
|
// TODO If necessary, convert TypeError<runtime> => TypeError
|
||||||
// That, is, such errors can be thrown despite not being JavaScript "native"
|
// That, is, such errors can be thrown despite not being JavaScript "native"
|
||||||
self.localSet(node.catch.parameter, tryCatchValue)
|
// strict = false
|
||||||
|
self.scope.lexical.setValue(node.catch.parameter, tryCatchValue, false)
|
||||||
|
|
||||||
// FIXME node.CatchParameter
|
// FIXME node.CatchParameter
|
||||||
// FIXME node.Catch
|
// FIXME node.Catch
|
||||||
|
@ -350,7 +361,7 @@ func (self *_runtime) cmpl_evaluate_nodeTryStatement(node *_nodeTryStatement) Va
|
||||||
|
|
||||||
if node.finally != nil {
|
if node.finally != nil {
|
||||||
finallyValue := self.cmpl_evaluate_nodeStatement(node.finally)
|
finallyValue := self.cmpl_evaluate_nodeStatement(node.finally)
|
||||||
if finallyValue.isResult() {
|
if finallyValue.kind == valueResult {
|
||||||
return finallyValue
|
return finallyValue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -369,16 +380,16 @@ func (self *_runtime) cmpl_evaluate_nodeWhileStatement(node *_nodeWhileStatement
|
||||||
labels := append(self.labels, "")
|
labels := append(self.labels, "")
|
||||||
self.labels = nil
|
self.labels = nil
|
||||||
|
|
||||||
result := Value{}
|
result := emptyValue
|
||||||
resultBreakContinue:
|
resultBreakContinue:
|
||||||
for {
|
for {
|
||||||
if !self.GetValue(self.cmpl_evaluate_nodeExpression(test)).isTrue() {
|
if !self.cmpl_evaluate_nodeExpression(test).resolve().bool() {
|
||||||
// Stahp: while (false) ...
|
// Stahp: while (false) ...
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
for _, node := range body {
|
for _, node := range body {
|
||||||
value := self.cmpl_evaluate_nodeStatement(node)
|
value := self.cmpl_evaluate_nodeStatement(node)
|
||||||
switch value._valueType {
|
switch value.kind {
|
||||||
case valueResult:
|
case valueResult:
|
||||||
switch value.evaluateBreakContinue(labels) {
|
switch value.evaluateBreakContinue(labels) {
|
||||||
case resultReturn:
|
case resultReturn:
|
||||||
|
@ -399,11 +410,11 @@ resultBreakContinue:
|
||||||
|
|
||||||
func (self *_runtime) cmpl_evaluate_nodeWithStatement(node *_nodeWithStatement) Value {
|
func (self *_runtime) cmpl_evaluate_nodeWithStatement(node *_nodeWithStatement) Value {
|
||||||
object := self.cmpl_evaluate_nodeExpression(node.object)
|
object := self.cmpl_evaluate_nodeExpression(node.object)
|
||||||
objectValue := self.GetValue(object)
|
outer := self.scope.lexical
|
||||||
previousLexicalEnvironment, lexicalEnvironment := self._executionContext(0).newLexicalEnvironment(self.toObject(objectValue))
|
lexical := self.newObjectStash(self.toObject(object.resolve()), outer)
|
||||||
lexicalEnvironment.ProvideThis = true
|
self.scope.lexical = lexical
|
||||||
defer func() {
|
defer func() {
|
||||||
self._executionContext(0).LexicalEnvironment = previousLexicalEnvironment
|
self.scope.lexical = outer
|
||||||
}()
|
}()
|
||||||
|
|
||||||
return self.cmpl_evaluate_nodeStatement(node.body)
|
return self.cmpl_evaluate_nodeStatement(node.body)
|
|
@ -5,165 +5,170 @@ import (
|
||||||
"regexp"
|
"regexp"
|
||||||
|
|
||||||
"github.com/robertkrimen/otto/ast"
|
"github.com/robertkrimen/otto/ast"
|
||||||
|
"github.com/robertkrimen/otto/file"
|
||||||
"github.com/robertkrimen/otto/token"
|
"github.com/robertkrimen/otto/token"
|
||||||
)
|
)
|
||||||
|
|
||||||
var trueLiteral = &_nodeLiteral{value: toValue_bool(true)}
|
var trueLiteral = &_nodeLiteral{value: toValue_bool(true)}
|
||||||
var falseLiteral = &_nodeLiteral{value: toValue_bool(false)}
|
var falseLiteral = &_nodeLiteral{value: toValue_bool(false)}
|
||||||
var nullLiteral = &_nodeLiteral{value: NullValue()}
|
var nullLiteral = &_nodeLiteral{value: nullValue}
|
||||||
var emptyStatement = &_nodeEmptyStatement{}
|
var emptyStatement = &_nodeEmptyStatement{}
|
||||||
|
|
||||||
func parseExpression(x ast.Expression) _nodeExpression {
|
func (cmpl *_compiler) parseExpression(in ast.Expression) _nodeExpression {
|
||||||
if x == nil {
|
if in == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
switch x := x.(type) {
|
switch in := in.(type) {
|
||||||
|
|
||||||
case *ast.ArrayLiteral:
|
case *ast.ArrayLiteral:
|
||||||
y := &_nodeArrayLiteral{
|
out := &_nodeArrayLiteral{
|
||||||
value: make([]_nodeExpression, len(x.Value)),
|
value: make([]_nodeExpression, len(in.Value)),
|
||||||
}
|
}
|
||||||
for i, value := range x.Value {
|
for i, value := range in.Value {
|
||||||
y.value[i] = parseExpression(value)
|
out.value[i] = cmpl.parseExpression(value)
|
||||||
}
|
}
|
||||||
return y
|
return out
|
||||||
|
|
||||||
case *ast.AssignExpression:
|
case *ast.AssignExpression:
|
||||||
return &_nodeAssignExpression{
|
return &_nodeAssignExpression{
|
||||||
operator: x.Operator,
|
operator: in.Operator,
|
||||||
left: parseExpression(x.Left),
|
left: cmpl.parseExpression(in.Left),
|
||||||
right: parseExpression(x.Right),
|
right: cmpl.parseExpression(in.Right),
|
||||||
}
|
}
|
||||||
|
|
||||||
case *ast.BinaryExpression:
|
case *ast.BinaryExpression:
|
||||||
return &_nodeBinaryExpression{
|
return &_nodeBinaryExpression{
|
||||||
operator: x.Operator,
|
operator: in.Operator,
|
||||||
left: parseExpression(x.Left),
|
left: cmpl.parseExpression(in.Left),
|
||||||
right: parseExpression(x.Right),
|
right: cmpl.parseExpression(in.Right),
|
||||||
comparison: x.Comparison,
|
comparison: in.Comparison,
|
||||||
}
|
}
|
||||||
|
|
||||||
case *ast.BooleanLiteral:
|
case *ast.BooleanLiteral:
|
||||||
if x.Value {
|
if in.Value {
|
||||||
return trueLiteral
|
return trueLiteral
|
||||||
}
|
}
|
||||||
return falseLiteral
|
return falseLiteral
|
||||||
|
|
||||||
case *ast.BracketExpression:
|
case *ast.BracketExpression:
|
||||||
return &_nodeBracketExpression{
|
return &_nodeBracketExpression{
|
||||||
left: parseExpression(x.Left),
|
idx: in.Left.Idx0(),
|
||||||
member: parseExpression(x.Member),
|
left: cmpl.parseExpression(in.Left),
|
||||||
|
member: cmpl.parseExpression(in.Member),
|
||||||
}
|
}
|
||||||
|
|
||||||
case *ast.CallExpression:
|
case *ast.CallExpression:
|
||||||
y := &_nodeCallExpression{
|
out := &_nodeCallExpression{
|
||||||
callee: parseExpression(x.Callee),
|
callee: cmpl.parseExpression(in.Callee),
|
||||||
argumentList: make([]_nodeExpression, len(x.ArgumentList)),
|
argumentList: make([]_nodeExpression, len(in.ArgumentList)),
|
||||||
}
|
}
|
||||||
for i, value := range x.ArgumentList {
|
for i, value := range in.ArgumentList {
|
||||||
y.argumentList[i] = parseExpression(value)
|
out.argumentList[i] = cmpl.parseExpression(value)
|
||||||
}
|
}
|
||||||
return y
|
return out
|
||||||
|
|
||||||
case *ast.ConditionalExpression:
|
case *ast.ConditionalExpression:
|
||||||
return &_nodeConditionalExpression{
|
return &_nodeConditionalExpression{
|
||||||
test: parseExpression(x.Test),
|
test: cmpl.parseExpression(in.Test),
|
||||||
consequent: parseExpression(x.Consequent),
|
consequent: cmpl.parseExpression(in.Consequent),
|
||||||
alternate: parseExpression(x.Alternate),
|
alternate: cmpl.parseExpression(in.Alternate),
|
||||||
}
|
}
|
||||||
|
|
||||||
case *ast.DotExpression:
|
case *ast.DotExpression:
|
||||||
return &_nodeDotExpression{
|
return &_nodeDotExpression{
|
||||||
left: parseExpression(x.Left),
|
idx: in.Left.Idx0(),
|
||||||
identifier: x.Identifier.Name,
|
left: cmpl.parseExpression(in.Left),
|
||||||
|
identifier: in.Identifier.Name,
|
||||||
}
|
}
|
||||||
|
|
||||||
case *ast.FunctionLiteral:
|
case *ast.FunctionLiteral:
|
||||||
name := ""
|
name := ""
|
||||||
if x.Name != nil {
|
if in.Name != nil {
|
||||||
name = x.Name.Name
|
name = in.Name.Name
|
||||||
}
|
}
|
||||||
y := &_nodeFunctionLiteral{
|
out := &_nodeFunctionLiteral{
|
||||||
name: name,
|
name: name,
|
||||||
body: parseStatement(x.Body),
|
body: cmpl.parseStatement(in.Body),
|
||||||
source: x.Source,
|
source: in.Source,
|
||||||
|
file: cmpl.file,
|
||||||
}
|
}
|
||||||
if x.ParameterList != nil {
|
if in.ParameterList != nil {
|
||||||
list := x.ParameterList.List
|
list := in.ParameterList.List
|
||||||
y.parameterList = make([]string, len(list))
|
out.parameterList = make([]string, len(list))
|
||||||
for i, value := range list {
|
for i, value := range list {
|
||||||
y.parameterList[i] = value.Name
|
out.parameterList[i] = value.Name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, value := range x.DeclarationList {
|
for _, value := range in.DeclarationList {
|
||||||
switch value := value.(type) {
|
switch value := value.(type) {
|
||||||
case *ast.FunctionDeclaration:
|
case *ast.FunctionDeclaration:
|
||||||
y.functionList = append(y.functionList, parseExpression(value.Function).(*_nodeFunctionLiteral))
|
out.functionList = append(out.functionList, cmpl.parseExpression(value.Function).(*_nodeFunctionLiteral))
|
||||||
case *ast.VariableDeclaration:
|
case *ast.VariableDeclaration:
|
||||||
for _, value := range value.List {
|
for _, value := range value.List {
|
||||||
y.varList = append(y.varList, value.Name)
|
out.varList = append(out.varList, value.Name)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
panic(fmt.Errorf("Here be dragons: parseProgram.declaration(%T)", value))
|
panic(fmt.Errorf("Here be dragons: parseProgram.declaration(%T)", value))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return y
|
return out
|
||||||
|
|
||||||
case *ast.Identifier:
|
case *ast.Identifier:
|
||||||
return &_nodeIdentifier{
|
return &_nodeIdentifier{
|
||||||
name: x.Name,
|
idx: in.Idx,
|
||||||
|
name: in.Name,
|
||||||
}
|
}
|
||||||
|
|
||||||
case *ast.NewExpression:
|
case *ast.NewExpression:
|
||||||
y := &_nodeNewExpression{
|
out := &_nodeNewExpression{
|
||||||
callee: parseExpression(x.Callee),
|
callee: cmpl.parseExpression(in.Callee),
|
||||||
argumentList: make([]_nodeExpression, len(x.ArgumentList)),
|
argumentList: make([]_nodeExpression, len(in.ArgumentList)),
|
||||||
}
|
}
|
||||||
for i, value := range x.ArgumentList {
|
for i, value := range in.ArgumentList {
|
||||||
y.argumentList[i] = parseExpression(value)
|
out.argumentList[i] = cmpl.parseExpression(value)
|
||||||
}
|
}
|
||||||
return y
|
return out
|
||||||
|
|
||||||
case *ast.NullLiteral:
|
case *ast.NullLiteral:
|
||||||
return nullLiteral
|
return nullLiteral
|
||||||
|
|
||||||
case *ast.NumberLiteral:
|
case *ast.NumberLiteral:
|
||||||
return &_nodeLiteral{
|
return &_nodeLiteral{
|
||||||
value: toValue(x.Value),
|
value: toValue(in.Value),
|
||||||
}
|
}
|
||||||
|
|
||||||
case *ast.ObjectLiteral:
|
case *ast.ObjectLiteral:
|
||||||
y := &_nodeObjectLiteral{
|
out := &_nodeObjectLiteral{
|
||||||
value: make([]_nodeProperty, len(x.Value)),
|
value: make([]_nodeProperty, len(in.Value)),
|
||||||
}
|
}
|
||||||
for i, value := range x.Value {
|
for i, value := range in.Value {
|
||||||
y.value[i] = _nodeProperty{
|
out.value[i] = _nodeProperty{
|
||||||
key: value.Key,
|
key: value.Key,
|
||||||
kind: value.Kind,
|
kind: value.Kind,
|
||||||
value: parseExpression(value.Value),
|
value: cmpl.parseExpression(value.Value),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return y
|
return out
|
||||||
|
|
||||||
case *ast.RegExpLiteral:
|
case *ast.RegExpLiteral:
|
||||||
return &_nodeRegExpLiteral{
|
return &_nodeRegExpLiteral{
|
||||||
flags: x.Flags,
|
flags: in.Flags,
|
||||||
pattern: x.Pattern,
|
pattern: in.Pattern,
|
||||||
}
|
}
|
||||||
|
|
||||||
case *ast.SequenceExpression:
|
case *ast.SequenceExpression:
|
||||||
y := &_nodeSequenceExpression{
|
out := &_nodeSequenceExpression{
|
||||||
sequence: make([]_nodeExpression, len(x.Sequence)),
|
sequence: make([]_nodeExpression, len(in.Sequence)),
|
||||||
}
|
}
|
||||||
for i, value := range x.Sequence {
|
for i, value := range in.Sequence {
|
||||||
y.sequence[i] = parseExpression(value)
|
out.sequence[i] = cmpl.parseExpression(value)
|
||||||
}
|
}
|
||||||
return y
|
return out
|
||||||
|
|
||||||
case *ast.StringLiteral:
|
case *ast.StringLiteral:
|
||||||
return &_nodeLiteral{
|
return &_nodeLiteral{
|
||||||
value: toValue_string(x.Value),
|
value: toValue_string(in.Value),
|
||||||
}
|
}
|
||||||
|
|
||||||
case *ast.ThisExpression:
|
case *ast.ThisExpression:
|
||||||
|
@ -171,203 +176,211 @@ func parseExpression(x ast.Expression) _nodeExpression {
|
||||||
|
|
||||||
case *ast.UnaryExpression:
|
case *ast.UnaryExpression:
|
||||||
return &_nodeUnaryExpression{
|
return &_nodeUnaryExpression{
|
||||||
operator: x.Operator,
|
operator: in.Operator,
|
||||||
operand: parseExpression(x.Operand),
|
operand: cmpl.parseExpression(in.Operand),
|
||||||
postfix: x.Postfix,
|
postfix: in.Postfix,
|
||||||
}
|
}
|
||||||
|
|
||||||
case *ast.VariableExpression:
|
case *ast.VariableExpression:
|
||||||
return &_nodeVariableExpression{
|
return &_nodeVariableExpression{
|
||||||
name: x.Name,
|
idx: in.Idx0(),
|
||||||
initializer: parseExpression(x.Initializer),
|
name: in.Name,
|
||||||
|
initializer: cmpl.parseExpression(in.Initializer),
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
panic(fmt.Errorf("Here be dragons: parseExpression(%T)", x))
|
panic(fmt.Errorf("Here be dragons: cmpl.parseExpression(%T)", in))
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseStatement(x ast.Statement) _nodeStatement {
|
func (cmpl *_compiler) parseStatement(in ast.Statement) _nodeStatement {
|
||||||
if x == nil {
|
if in == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
switch x := x.(type) {
|
switch in := in.(type) {
|
||||||
|
|
||||||
case *ast.BlockStatement:
|
case *ast.BlockStatement:
|
||||||
y := &_nodeBlockStatement{
|
out := &_nodeBlockStatement{
|
||||||
list: make([]_nodeStatement, len(x.List)),
|
list: make([]_nodeStatement, len(in.List)),
|
||||||
}
|
}
|
||||||
for i, value := range x.List {
|
for i, value := range in.List {
|
||||||
y.list[i] = parseStatement(value)
|
out.list[i] = cmpl.parseStatement(value)
|
||||||
}
|
}
|
||||||
return y
|
return out
|
||||||
|
|
||||||
case *ast.BranchStatement:
|
case *ast.BranchStatement:
|
||||||
y := &_nodeBranchStatement{
|
out := &_nodeBranchStatement{
|
||||||
branch: x.Token,
|
branch: in.Token,
|
||||||
}
|
}
|
||||||
if x.Label != nil {
|
if in.Label != nil {
|
||||||
y.label = x.Label.Name
|
out.label = in.Label.Name
|
||||||
}
|
}
|
||||||
return y
|
return out
|
||||||
|
|
||||||
case *ast.DebuggerStatement:
|
case *ast.DebuggerStatement:
|
||||||
return &_nodeDebuggerStatement{}
|
return &_nodeDebuggerStatement{}
|
||||||
|
|
||||||
case *ast.DoWhileStatement:
|
case *ast.DoWhileStatement:
|
||||||
y := &_nodeDoWhileStatement{
|
out := &_nodeDoWhileStatement{
|
||||||
test: parseExpression(x.Test),
|
test: cmpl.parseExpression(in.Test),
|
||||||
}
|
}
|
||||||
body := parseStatement(x.Body)
|
body := cmpl.parseStatement(in.Body)
|
||||||
if block, ok := body.(*_nodeBlockStatement); ok {
|
if block, ok := body.(*_nodeBlockStatement); ok {
|
||||||
y.body = block.list
|
out.body = block.list
|
||||||
} else {
|
} else {
|
||||||
y.body = append(y.body, body)
|
out.body = append(out.body, body)
|
||||||
}
|
}
|
||||||
return y
|
return out
|
||||||
|
|
||||||
case *ast.EmptyStatement:
|
case *ast.EmptyStatement:
|
||||||
return emptyStatement
|
return emptyStatement
|
||||||
|
|
||||||
case *ast.ExpressionStatement:
|
case *ast.ExpressionStatement:
|
||||||
return &_nodeExpressionStatement{
|
return &_nodeExpressionStatement{
|
||||||
expression: parseExpression(x.Expression),
|
expression: cmpl.parseExpression(in.Expression),
|
||||||
}
|
}
|
||||||
|
|
||||||
case *ast.ForInStatement:
|
case *ast.ForInStatement:
|
||||||
y := &_nodeForInStatement{
|
out := &_nodeForInStatement{
|
||||||
into: parseExpression(x.Into),
|
into: cmpl.parseExpression(in.Into),
|
||||||
source: parseExpression(x.Source),
|
source: cmpl.parseExpression(in.Source),
|
||||||
}
|
}
|
||||||
body := parseStatement(x.Body)
|
body := cmpl.parseStatement(in.Body)
|
||||||
if block, ok := body.(*_nodeBlockStatement); ok {
|
if block, ok := body.(*_nodeBlockStatement); ok {
|
||||||
y.body = block.list
|
out.body = block.list
|
||||||
} else {
|
} else {
|
||||||
y.body = append(y.body, body)
|
out.body = append(out.body, body)
|
||||||
}
|
}
|
||||||
return y
|
return out
|
||||||
|
|
||||||
case *ast.ForStatement:
|
case *ast.ForStatement:
|
||||||
y := &_nodeForStatement{
|
out := &_nodeForStatement{
|
||||||
initializer: parseExpression(x.Initializer),
|
initializer: cmpl.parseExpression(in.Initializer),
|
||||||
update: parseExpression(x.Update),
|
update: cmpl.parseExpression(in.Update),
|
||||||
test: parseExpression(x.Test),
|
test: cmpl.parseExpression(in.Test),
|
||||||
}
|
}
|
||||||
body := parseStatement(x.Body)
|
body := cmpl.parseStatement(in.Body)
|
||||||
if block, ok := body.(*_nodeBlockStatement); ok {
|
if block, ok := body.(*_nodeBlockStatement); ok {
|
||||||
y.body = block.list
|
out.body = block.list
|
||||||
} else {
|
} else {
|
||||||
y.body = append(y.body, body)
|
out.body = append(out.body, body)
|
||||||
}
|
}
|
||||||
return y
|
return out
|
||||||
|
|
||||||
case *ast.IfStatement:
|
case *ast.IfStatement:
|
||||||
return &_nodeIfStatement{
|
return &_nodeIfStatement{
|
||||||
test: parseExpression(x.Test),
|
test: cmpl.parseExpression(in.Test),
|
||||||
consequent: parseStatement(x.Consequent),
|
consequent: cmpl.parseStatement(in.Consequent),
|
||||||
alternate: parseStatement(x.Alternate),
|
alternate: cmpl.parseStatement(in.Alternate),
|
||||||
}
|
}
|
||||||
|
|
||||||
case *ast.LabelledStatement:
|
case *ast.LabelledStatement:
|
||||||
return &_nodeLabelledStatement{
|
return &_nodeLabelledStatement{
|
||||||
label: x.Label.Name,
|
label: in.Label.Name,
|
||||||
statement: parseStatement(x.Statement),
|
statement: cmpl.parseStatement(in.Statement),
|
||||||
}
|
}
|
||||||
|
|
||||||
case *ast.ReturnStatement:
|
case *ast.ReturnStatement:
|
||||||
return &_nodeReturnStatement{
|
return &_nodeReturnStatement{
|
||||||
argument: parseExpression(x.Argument),
|
argument: cmpl.parseExpression(in.Argument),
|
||||||
}
|
}
|
||||||
|
|
||||||
case *ast.SwitchStatement:
|
case *ast.SwitchStatement:
|
||||||
y := &_nodeSwitchStatement{
|
out := &_nodeSwitchStatement{
|
||||||
discriminant: parseExpression(x.Discriminant),
|
discriminant: cmpl.parseExpression(in.Discriminant),
|
||||||
default_: x.Default,
|
default_: in.Default,
|
||||||
body: make([]*_nodeCaseStatement, len(x.Body)),
|
body: make([]*_nodeCaseStatement, len(in.Body)),
|
||||||
}
|
}
|
||||||
for i, p := range x.Body {
|
for i, clause := range in.Body {
|
||||||
q := &_nodeCaseStatement{
|
out.body[i] = &_nodeCaseStatement{
|
||||||
test: parseExpression(p.Test),
|
test: cmpl.parseExpression(clause.Test),
|
||||||
consequent: make([]_nodeStatement, len(p.Consequent)),
|
consequent: make([]_nodeStatement, len(clause.Consequent)),
|
||||||
}
|
}
|
||||||
for j, value := range p.Consequent {
|
for j, value := range clause.Consequent {
|
||||||
q.consequent[j] = parseStatement(value)
|
out.body[i].consequent[j] = cmpl.parseStatement(value)
|
||||||
}
|
}
|
||||||
y.body[i] = q
|
|
||||||
}
|
}
|
||||||
return y
|
return out
|
||||||
|
|
||||||
case *ast.ThrowStatement:
|
case *ast.ThrowStatement:
|
||||||
return &_nodeThrowStatement{
|
return &_nodeThrowStatement{
|
||||||
argument: parseExpression(x.Argument),
|
argument: cmpl.parseExpression(in.Argument),
|
||||||
}
|
}
|
||||||
|
|
||||||
case *ast.TryStatement:
|
case *ast.TryStatement:
|
||||||
y := &_nodeTryStatement{
|
out := &_nodeTryStatement{
|
||||||
body: parseStatement(x.Body),
|
body: cmpl.parseStatement(in.Body),
|
||||||
finally: parseStatement(x.Finally),
|
finally: cmpl.parseStatement(in.Finally),
|
||||||
}
|
}
|
||||||
if x.Catch != nil {
|
if in.Catch != nil {
|
||||||
y.catch = &_nodeCatchStatement{
|
out.catch = &_nodeCatchStatement{
|
||||||
parameter: x.Catch.Parameter.Name,
|
parameter: in.Catch.Parameter.Name,
|
||||||
body: parseStatement(x.Catch.Body),
|
body: cmpl.parseStatement(in.Catch.Body),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return y
|
return out
|
||||||
|
|
||||||
case *ast.VariableStatement:
|
case *ast.VariableStatement:
|
||||||
y := &_nodeVariableStatement{
|
out := &_nodeVariableStatement{
|
||||||
list: make([]_nodeExpression, len(x.List)),
|
list: make([]_nodeExpression, len(in.List)),
|
||||||
}
|
}
|
||||||
for i, value := range x.List {
|
for i, value := range in.List {
|
||||||
y.list[i] = parseExpression(value)
|
out.list[i] = cmpl.parseExpression(value)
|
||||||
}
|
}
|
||||||
return y
|
return out
|
||||||
|
|
||||||
case *ast.WhileStatement:
|
case *ast.WhileStatement:
|
||||||
y := &_nodeWhileStatement{
|
out := &_nodeWhileStatement{
|
||||||
test: parseExpression(x.Test),
|
test: cmpl.parseExpression(in.Test),
|
||||||
}
|
}
|
||||||
body := parseStatement(x.Body)
|
body := cmpl.parseStatement(in.Body)
|
||||||
if block, ok := body.(*_nodeBlockStatement); ok {
|
if block, ok := body.(*_nodeBlockStatement); ok {
|
||||||
y.body = block.list
|
out.body = block.list
|
||||||
} else {
|
} else {
|
||||||
y.body = append(y.body, body)
|
out.body = append(out.body, body)
|
||||||
}
|
}
|
||||||
return y
|
return out
|
||||||
|
|
||||||
case *ast.WithStatement:
|
case *ast.WithStatement:
|
||||||
return &_nodeWithStatement{
|
return &_nodeWithStatement{
|
||||||
object: parseExpression(x.Object),
|
object: cmpl.parseExpression(in.Object),
|
||||||
body: parseStatement(x.Body),
|
body: cmpl.parseStatement(in.Body),
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
panic(fmt.Errorf("Here be dragons: parseStatement(%T)", x))
|
panic(fmt.Errorf("Here be dragons: cmpl.parseStatement(%T)", in))
|
||||||
}
|
}
|
||||||
|
|
||||||
func cmpl_parse(x *ast.Program) *_nodeProgram {
|
func cmpl_parse(in *ast.Program) *_nodeProgram {
|
||||||
y := &_nodeProgram{
|
cmpl := _compiler{
|
||||||
body: make([]_nodeStatement, len(x.Body)),
|
program: in,
|
||||||
}
|
}
|
||||||
for i, value := range x.Body {
|
return cmpl.parse()
|
||||||
y.body[i] = parseStatement(value)
|
}
|
||||||
|
|
||||||
|
func (cmpl *_compiler) _parse(in *ast.Program) *_nodeProgram {
|
||||||
|
out := &_nodeProgram{
|
||||||
|
body: make([]_nodeStatement, len(in.Body)),
|
||||||
|
file: in.File,
|
||||||
}
|
}
|
||||||
for _, value := range x.DeclarationList {
|
for i, value := range in.Body {
|
||||||
|
out.body[i] = cmpl.parseStatement(value)
|
||||||
|
}
|
||||||
|
for _, value := range in.DeclarationList {
|
||||||
switch value := value.(type) {
|
switch value := value.(type) {
|
||||||
case *ast.FunctionDeclaration:
|
case *ast.FunctionDeclaration:
|
||||||
y.functionList = append(y.functionList, parseExpression(value.Function).(*_nodeFunctionLiteral))
|
out.functionList = append(out.functionList, cmpl.parseExpression(value.Function).(*_nodeFunctionLiteral))
|
||||||
case *ast.VariableDeclaration:
|
case *ast.VariableDeclaration:
|
||||||
for _, value := range value.List {
|
for _, value := range value.List {
|
||||||
y.varList = append(y.varList, value.Name)
|
out.varList = append(out.varList, value.Name)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
panic(fmt.Errorf("Here be dragons: parseProgram.DeclarationList(%T)", value))
|
panic(fmt.Errorf("Here be dragons: cmpl.parseProgram.DeclarationList(%T)", value))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return y
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
type _nodeProgram struct {
|
type _nodeProgram struct {
|
||||||
|
@ -377,6 +390,8 @@ type _nodeProgram struct {
|
||||||
functionList []*_nodeFunctionLiteral
|
functionList []*_nodeFunctionLiteral
|
||||||
|
|
||||||
variableList []_nodeDeclaration
|
variableList []_nodeDeclaration
|
||||||
|
|
||||||
|
file *file.File
|
||||||
}
|
}
|
||||||
|
|
||||||
type _nodeDeclaration struct {
|
type _nodeDeclaration struct {
|
||||||
|
@ -411,6 +426,7 @@ type (
|
||||||
}
|
}
|
||||||
|
|
||||||
_nodeBracketExpression struct {
|
_nodeBracketExpression struct {
|
||||||
|
idx file.Idx
|
||||||
left _nodeExpression
|
left _nodeExpression
|
||||||
member _nodeExpression
|
member _nodeExpression
|
||||||
}
|
}
|
||||||
|
@ -427,6 +443,7 @@ type (
|
||||||
}
|
}
|
||||||
|
|
||||||
_nodeDotExpression struct {
|
_nodeDotExpression struct {
|
||||||
|
idx file.Idx
|
||||||
left _nodeExpression
|
left _nodeExpression
|
||||||
identifier string
|
identifier string
|
||||||
}
|
}
|
||||||
|
@ -438,9 +455,11 @@ type (
|
||||||
parameterList []string
|
parameterList []string
|
||||||
varList []string
|
varList []string
|
||||||
functionList []*_nodeFunctionLiteral
|
functionList []*_nodeFunctionLiteral
|
||||||
|
file *file.File
|
||||||
}
|
}
|
||||||
|
|
||||||
_nodeIdentifier struct {
|
_nodeIdentifier struct {
|
||||||
|
idx file.Idx
|
||||||
name string
|
name string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -483,6 +502,7 @@ type (
|
||||||
}
|
}
|
||||||
|
|
||||||
_nodeVariableExpression struct {
|
_nodeVariableExpression struct {
|
||||||
|
idx file.Idx
|
||||||
name string
|
name string
|
||||||
initializer _nodeExpression
|
initializer _nodeExpression
|
||||||
}
|
}
|
|
@ -15,7 +15,7 @@ func Test_cmpl(t *testing.T) {
|
||||||
is(err, nil)
|
is(err, nil)
|
||||||
{
|
{
|
||||||
program := cmpl_parse(program)
|
program := cmpl_parse(program)
|
||||||
value := vm.runtime.cmpl_evaluate_nodeProgram(program)
|
value := vm.runtime.cmpl_evaluate_nodeProgram(program, false)
|
||||||
if len(expect) > 0 {
|
if len(expect) > 0 {
|
||||||
is(value, expect[0])
|
is(value, expect[0])
|
||||||
}
|
}
|
|
@ -16,33 +16,33 @@ func formatForConsole(argumentList []Value) string {
|
||||||
|
|
||||||
func builtinConsole_log(call FunctionCall) Value {
|
func builtinConsole_log(call FunctionCall) Value {
|
||||||
fmt.Fprintln(os.Stdout, formatForConsole(call.ArgumentList))
|
fmt.Fprintln(os.Stdout, formatForConsole(call.ArgumentList))
|
||||||
return UndefinedValue()
|
return Value{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinConsole_error(call FunctionCall) Value {
|
func builtinConsole_error(call FunctionCall) Value {
|
||||||
fmt.Fprintln(os.Stdout, formatForConsole(call.ArgumentList))
|
fmt.Fprintln(os.Stdout, formatForConsole(call.ArgumentList))
|
||||||
return UndefinedValue()
|
return Value{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Nothing happens.
|
// Nothing happens.
|
||||||
func builtinConsole_dir(call FunctionCall) Value {
|
func builtinConsole_dir(call FunctionCall) Value {
|
||||||
return UndefinedValue()
|
return Value{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinConsole_time(call FunctionCall) Value {
|
func builtinConsole_time(call FunctionCall) Value {
|
||||||
return UndefinedValue()
|
return Value{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinConsole_timeEnd(call FunctionCall) Value {
|
func builtinConsole_timeEnd(call FunctionCall) Value {
|
||||||
return UndefinedValue()
|
return Value{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinConsole_trace(call FunctionCall) Value {
|
func builtinConsole_trace(call FunctionCall) Value {
|
||||||
return UndefinedValue()
|
return Value{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinConsole_assert(call FunctionCall) Value {
|
func builtinConsole_assert(call FunctionCall) Value {
|
||||||
return UndefinedValue()
|
return Value{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (runtime *_runtime) newConsole() *_object {
|
func (runtime *_runtime) newConsole() *_object {
|
|
@ -246,7 +246,10 @@ func TestDate_new(t *testing.T) {
|
||||||
// This is probably incorrect, due to differences in Go date/time handling
|
// This is probably incorrect, due to differences in Go date/time handling
|
||||||
// versus ECMA date/time handling, but we'll leave this here for
|
// versus ECMA date/time handling, but we'll leave this here for
|
||||||
// future reference
|
// future reference
|
||||||
return
|
|
||||||
|
if true {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
tt(t, func() {
|
tt(t, func() {
|
||||||
test, _ := test()
|
test, _ := test()
|
|
@ -64,7 +64,7 @@ func ExampleSynopsis() {
|
||||||
// 16
|
// 16
|
||||||
// 16
|
// 16
|
||||||
// undefined
|
// undefined
|
||||||
// ReferenceError: abcdefghijlmnopqrstuvwxyz is not defined
|
// ReferenceError: 'abcdefghijlmnopqrstuvwxyz' is not defined
|
||||||
// Hello, Xyzzy.
|
// Hello, Xyzzy.
|
||||||
// Hello, undefined.
|
// Hello, undefined.
|
||||||
// 4
|
// 4
|
|
@ -0,0 +1,245 @@
|
||||||
|
package otto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/robertkrimen/otto/file"
|
||||||
|
)
|
||||||
|
|
||||||
|
type _exception struct {
|
||||||
|
value interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newException(value interface{}) *_exception {
|
||||||
|
return &_exception{
|
||||||
|
value: value,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_exception) eject() interface{} {
|
||||||
|
value := self.value
|
||||||
|
self.value = nil // Prevent Go from holding on to the value, whatever it is
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
type _error struct {
|
||||||
|
name string
|
||||||
|
message string
|
||||||
|
trace []_frame
|
||||||
|
|
||||||
|
offset int
|
||||||
|
}
|
||||||
|
|
||||||
|
type _frame struct {
|
||||||
|
file *file.File
|
||||||
|
offset int
|
||||||
|
callee string
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
nativeFrame = _frame{}
|
||||||
|
)
|
||||||
|
|
||||||
|
type _at int
|
||||||
|
|
||||||
|
func (fr _frame) location() string {
|
||||||
|
if fr.file == nil {
|
||||||
|
return "<unknown>"
|
||||||
|
}
|
||||||
|
path := fr.file.Name()
|
||||||
|
line, column := _position(fr.file, fr.offset)
|
||||||
|
|
||||||
|
if path == "" {
|
||||||
|
path = "<anonymous>"
|
||||||
|
}
|
||||||
|
|
||||||
|
str := fmt.Sprintf("%s:%d:%d", path, line, column)
|
||||||
|
|
||||||
|
if fr.callee != "" {
|
||||||
|
str = fmt.Sprintf("%s (%s)", fr.callee, str)
|
||||||
|
}
|
||||||
|
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
|
||||||
|
func _position(file *file.File, offset int) (line, column int) {
|
||||||
|
{
|
||||||
|
offset := offset - file.Base()
|
||||||
|
if offset < 0 {
|
||||||
|
return -offset, -1
|
||||||
|
}
|
||||||
|
|
||||||
|
src := file.Source()
|
||||||
|
if offset >= len(src) {
|
||||||
|
return -offset, -len(src)
|
||||||
|
}
|
||||||
|
src = src[:offset]
|
||||||
|
|
||||||
|
line := 1 + strings.Count(src, "\n")
|
||||||
|
column := 0
|
||||||
|
if index := strings.LastIndex(src, "\n"); index >= 0 {
|
||||||
|
column = offset - index
|
||||||
|
} else {
|
||||||
|
column = 1 + len(src)
|
||||||
|
}
|
||||||
|
return line, column
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// An Error represents a runtime error, e.g. a TypeError, a ReferenceError, etc.
|
||||||
|
type Error struct {
|
||||||
|
_error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error returns a description of the error
|
||||||
|
//
|
||||||
|
// TypeError: 'def' is not a function
|
||||||
|
//
|
||||||
|
func (err Error) Error() string {
|
||||||
|
if len(err.name) == 0 {
|
||||||
|
return err.message
|
||||||
|
}
|
||||||
|
if len(err.message) == 0 {
|
||||||
|
return err.name
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s: %s", err.name, err.message)
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns a description of the error and a trace of where the
|
||||||
|
// error occurred.
|
||||||
|
//
|
||||||
|
// TypeError: 'def' is not a function
|
||||||
|
// at xyz (<anonymous>:3:9)
|
||||||
|
// at <anonymous>:7:1/
|
||||||
|
//
|
||||||
|
func (err Error) String() string {
|
||||||
|
str := err.Error() + "\n"
|
||||||
|
for _, frame := range err.trace {
|
||||||
|
str += " at " + frame.location() + "\n"
|
||||||
|
}
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err _error) describe(format string, in ...interface{}) string {
|
||||||
|
return fmt.Sprintf(format, in...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self _error) messageValue() Value {
|
||||||
|
if self.message == "" {
|
||||||
|
return Value{}
|
||||||
|
}
|
||||||
|
return toValue_string(self.message)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rt *_runtime) typeErrorResult(throw bool) bool {
|
||||||
|
if throw {
|
||||||
|
panic(rt.panicTypeError())
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func newError(rt *_runtime, name string, in ...interface{}) _error {
|
||||||
|
err := _error{
|
||||||
|
name: name,
|
||||||
|
offset: -1,
|
||||||
|
}
|
||||||
|
description := ""
|
||||||
|
length := len(in)
|
||||||
|
|
||||||
|
if rt != nil {
|
||||||
|
scope := rt.scope
|
||||||
|
frame := scope.frame
|
||||||
|
if length > 0 {
|
||||||
|
if at, ok := in[length-1].(_at); ok {
|
||||||
|
in = in[0 : length-1]
|
||||||
|
if scope != nil {
|
||||||
|
frame.offset = int(at)
|
||||||
|
}
|
||||||
|
length -= 1
|
||||||
|
}
|
||||||
|
if length > 0 {
|
||||||
|
description, in = in[0].(string), in[1:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
limit := 10
|
||||||
|
err.trace = append(err.trace, frame)
|
||||||
|
if scope != nil {
|
||||||
|
for limit > 0 {
|
||||||
|
scope = scope.outer
|
||||||
|
if scope == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if scope.frame.offset >= 0 {
|
||||||
|
err.trace = append(err.trace, scope.frame)
|
||||||
|
}
|
||||||
|
limit--
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if length > 0 {
|
||||||
|
description, in = in[0].(string), in[1:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err.message = err.describe(description, in...)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rt *_runtime) panicTypeError(argumentList ...interface{}) *_exception {
|
||||||
|
return &_exception{
|
||||||
|
value: newError(rt, "TypeError", argumentList...),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rt *_runtime) panicReferenceError(argumentList ...interface{}) *_exception {
|
||||||
|
return &_exception{
|
||||||
|
value: newError(rt, "ReferenceError", argumentList...),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rt *_runtime) panicURIError(argumentList ...interface{}) *_exception {
|
||||||
|
return &_exception{
|
||||||
|
value: newError(rt, "URIError", argumentList...),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rt *_runtime) panicSyntaxError(argumentList ...interface{}) *_exception {
|
||||||
|
return &_exception{
|
||||||
|
value: newError(rt, "SyntaxError", argumentList...),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rt *_runtime) panicRangeError(argumentList ...interface{}) *_exception {
|
||||||
|
return &_exception{
|
||||||
|
value: newError(rt, "RangeError", argumentList...),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func catchPanic(function func()) (err error) {
|
||||||
|
defer func() {
|
||||||
|
if caught := recover(); caught != nil {
|
||||||
|
if exception, ok := caught.(*_exception); ok {
|
||||||
|
caught = exception.eject()
|
||||||
|
}
|
||||||
|
switch caught := caught.(type) {
|
||||||
|
case _error:
|
||||||
|
err = &Error{caught}
|
||||||
|
return
|
||||||
|
case Value:
|
||||||
|
if vl := caught._object(); vl != nil {
|
||||||
|
switch vl := vl.value.(type) {
|
||||||
|
case _error:
|
||||||
|
err = &Error{vl}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err = errors.New(caught.string())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
panic(caught)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
function()
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,192 @@
|
||||||
|
package otto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestError(t *testing.T) {
|
||||||
|
tt(t, func() {
|
||||||
|
test, _ := test()
|
||||||
|
|
||||||
|
test(`
|
||||||
|
[ Error.prototype.name, Error.prototype.message, Error.prototype.hasOwnProperty("message") ];
|
||||||
|
`, "Error,,true")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestError_instanceof(t *testing.T) {
|
||||||
|
tt(t, func() {
|
||||||
|
test, _ := test()
|
||||||
|
|
||||||
|
test(`(new TypeError()) instanceof Error`, true)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPanicValue(t *testing.T) {
|
||||||
|
tt(t, func() {
|
||||||
|
test, vm := test()
|
||||||
|
|
||||||
|
vm.Set("abc", func(call FunctionCall) Value {
|
||||||
|
value, err := call.Otto.Run(`({ def: 3.14159 })`)
|
||||||
|
is(err, nil)
|
||||||
|
panic(value)
|
||||||
|
})
|
||||||
|
|
||||||
|
test(`
|
||||||
|
try {
|
||||||
|
abc();
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
error = err;
|
||||||
|
}
|
||||||
|
[ error instanceof Error, error.message, error.def ];
|
||||||
|
`, "false,,3.14159")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_catchPanic(t *testing.T) {
|
||||||
|
tt(t, func() {
|
||||||
|
vm := New()
|
||||||
|
|
||||||
|
_, err := vm.Run(`
|
||||||
|
A syntax error that
|
||||||
|
does not define
|
||||||
|
var;
|
||||||
|
abc;
|
||||||
|
`)
|
||||||
|
is(err, "!=", nil)
|
||||||
|
|
||||||
|
_, err = vm.Call(`abc.def`, nil)
|
||||||
|
is(err, "!=", nil)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestErrorContext(t *testing.T) {
|
||||||
|
tt(t, func() {
|
||||||
|
vm := New()
|
||||||
|
|
||||||
|
_, err := vm.Run(`
|
||||||
|
undefined();
|
||||||
|
`)
|
||||||
|
{
|
||||||
|
err := err.(*Error)
|
||||||
|
is(err.message, "'undefined' is not a function")
|
||||||
|
is(len(err.trace), 1)
|
||||||
|
is(err.trace[0].location(), "<anonymous>:2:13")
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = vm.Run(`
|
||||||
|
({}).abc();
|
||||||
|
`)
|
||||||
|
{
|
||||||
|
err := err.(*Error)
|
||||||
|
is(err.message, "'abc' is not a function")
|
||||||
|
is(len(err.trace), 1)
|
||||||
|
is(err.trace[0].location(), "<anonymous>:2:14")
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = vm.Run(`
|
||||||
|
("abc").abc();
|
||||||
|
`)
|
||||||
|
{
|
||||||
|
err := err.(*Error)
|
||||||
|
is(err.message, "'abc' is not a function")
|
||||||
|
is(len(err.trace), 1)
|
||||||
|
is(err.trace[0].location(), "<anonymous>:2:14")
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = vm.Run(`
|
||||||
|
var ghi = "ghi";
|
||||||
|
ghi();
|
||||||
|
`)
|
||||||
|
{
|
||||||
|
err := err.(*Error)
|
||||||
|
is(err.message, "'ghi' is not a function")
|
||||||
|
is(len(err.trace), 1)
|
||||||
|
is(err.trace[0].location(), "<anonymous>:3:13")
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = vm.Run(`
|
||||||
|
function def() {
|
||||||
|
undefined();
|
||||||
|
}
|
||||||
|
function abc() {
|
||||||
|
def();
|
||||||
|
}
|
||||||
|
abc();
|
||||||
|
`)
|
||||||
|
{
|
||||||
|
err := err.(*Error)
|
||||||
|
is(err.message, "'undefined' is not a function")
|
||||||
|
is(len(err.trace), 3)
|
||||||
|
is(err.trace[0].location(), "def (<anonymous>:3:17)")
|
||||||
|
is(err.trace[1].location(), "abc (<anonymous>:6:17)")
|
||||||
|
is(err.trace[2].location(), "<anonymous>:8:13")
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = vm.Run(`
|
||||||
|
function abc() {
|
||||||
|
xyz();
|
||||||
|
}
|
||||||
|
abc();
|
||||||
|
`)
|
||||||
|
{
|
||||||
|
err := err.(*Error)
|
||||||
|
is(err.message, "'xyz' is not defined")
|
||||||
|
is(len(err.trace), 2)
|
||||||
|
is(err.trace[0].location(), "abc (<anonymous>:3:17)")
|
||||||
|
is(err.trace[1].location(), "<anonymous>:5:13")
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = vm.Run(`
|
||||||
|
mno + 1;
|
||||||
|
`)
|
||||||
|
{
|
||||||
|
err := err.(*Error)
|
||||||
|
is(err.message, "'mno' is not defined")
|
||||||
|
is(len(err.trace), 1)
|
||||||
|
is(err.trace[0].location(), "<anonymous>:2:13")
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = vm.Run(`
|
||||||
|
eval("xyz();");
|
||||||
|
`)
|
||||||
|
{
|
||||||
|
err := err.(*Error)
|
||||||
|
is(err.message, "'xyz' is not defined")
|
||||||
|
is(len(err.trace), 1)
|
||||||
|
is(err.trace[0].location(), "<anonymous>:1:1")
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = vm.Run(`
|
||||||
|
xyzzy = "Nothing happens."
|
||||||
|
eval("xyzzy();");
|
||||||
|
`)
|
||||||
|
{
|
||||||
|
err := err.(*Error)
|
||||||
|
is(err.message, "'xyzzy' is not a function")
|
||||||
|
is(len(err.trace), 1)
|
||||||
|
is(err.trace[0].location(), "<anonymous>:1:1")
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = vm.Run(`
|
||||||
|
throw Error("xyzzy");
|
||||||
|
`)
|
||||||
|
{
|
||||||
|
err := err.(*Error)
|
||||||
|
is(err.message, "xyzzy")
|
||||||
|
is(len(err.trace), 1)
|
||||||
|
is(err.trace[0].location(), "<anonymous>:2:19")
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = vm.Run(`
|
||||||
|
throw new Error("xyzzy");
|
||||||
|
`)
|
||||||
|
{
|
||||||
|
err := err.(*Error)
|
||||||
|
is(err.message, "xyzzy")
|
||||||
|
is(len(err.trace), 1)
|
||||||
|
is(err.trace[0].location(), "<anonymous>:2:23")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
|
@ -10,7 +10,7 @@ import (
|
||||||
|
|
||||||
func (self *_runtime) evaluateMultiply(left float64, right float64) Value {
|
func (self *_runtime) evaluateMultiply(left float64, right float64) Value {
|
||||||
// TODO 11.5.1
|
// TODO 11.5.1
|
||||||
return UndefinedValue()
|
return Value{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *_runtime) evaluateDivide(left float64, right float64) Value {
|
func (self *_runtime) evaluateDivide(left float64, right float64) Value {
|
||||||
|
@ -49,98 +49,98 @@ func (self *_runtime) evaluateDivide(left float64, right float64) Value {
|
||||||
|
|
||||||
func (self *_runtime) evaluateModulo(left float64, right float64) Value {
|
func (self *_runtime) evaluateModulo(left float64, right float64) Value {
|
||||||
// TODO 11.5.3
|
// TODO 11.5.3
|
||||||
return UndefinedValue()
|
return Value{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *_runtime) calculateBinaryExpression(operator token.Token, left Value, right Value) Value {
|
func (self *_runtime) calculateBinaryExpression(operator token.Token, left Value, right Value) Value {
|
||||||
|
|
||||||
leftValue := self.GetValue(left)
|
leftValue := left.resolve()
|
||||||
|
|
||||||
switch operator {
|
switch operator {
|
||||||
|
|
||||||
// Additive
|
// Additive
|
||||||
case token.PLUS:
|
case token.PLUS:
|
||||||
leftValue = toPrimitive(leftValue)
|
leftValue = toPrimitive(leftValue)
|
||||||
rightValue := self.GetValue(right)
|
rightValue := right.resolve()
|
||||||
rightValue = toPrimitive(rightValue)
|
rightValue = toPrimitive(rightValue)
|
||||||
|
|
||||||
if leftValue.IsString() || rightValue.IsString() {
|
if leftValue.IsString() || rightValue.IsString() {
|
||||||
return toValue_string(strings.Join([]string{leftValue.toString(), rightValue.toString()}, ""))
|
return toValue_string(strings.Join([]string{leftValue.string(), rightValue.string()}, ""))
|
||||||
} else {
|
} else {
|
||||||
return toValue_float64(leftValue.toFloat() + rightValue.toFloat())
|
return toValue_float64(leftValue.float64() + rightValue.float64())
|
||||||
}
|
}
|
||||||
case token.MINUS:
|
case token.MINUS:
|
||||||
rightValue := self.GetValue(right)
|
rightValue := right.resolve()
|
||||||
return toValue_float64(leftValue.toFloat() - rightValue.toFloat())
|
return toValue_float64(leftValue.float64() - rightValue.float64())
|
||||||
|
|
||||||
// Multiplicative
|
// Multiplicative
|
||||||
case token.MULTIPLY:
|
case token.MULTIPLY:
|
||||||
rightValue := self.GetValue(right)
|
rightValue := right.resolve()
|
||||||
return toValue_float64(leftValue.toFloat() * rightValue.toFloat())
|
return toValue_float64(leftValue.float64() * rightValue.float64())
|
||||||
case token.SLASH:
|
case token.SLASH:
|
||||||
rightValue := self.GetValue(right)
|
rightValue := right.resolve()
|
||||||
return self.evaluateDivide(leftValue.toFloat(), rightValue.toFloat())
|
return self.evaluateDivide(leftValue.float64(), rightValue.float64())
|
||||||
case token.REMAINDER:
|
case token.REMAINDER:
|
||||||
rightValue := self.GetValue(right)
|
rightValue := right.resolve()
|
||||||
return toValue_float64(math.Mod(leftValue.toFloat(), rightValue.toFloat()))
|
return toValue_float64(math.Mod(leftValue.float64(), rightValue.float64()))
|
||||||
|
|
||||||
// Logical
|
// Logical
|
||||||
case token.LOGICAL_AND:
|
case token.LOGICAL_AND:
|
||||||
left := toBoolean(leftValue)
|
left := leftValue.bool()
|
||||||
if !left {
|
if !left {
|
||||||
return FalseValue()
|
return falseValue
|
||||||
}
|
}
|
||||||
return toValue_bool(toBoolean(self.GetValue(right)))
|
return toValue_bool(right.resolve().bool())
|
||||||
case token.LOGICAL_OR:
|
case token.LOGICAL_OR:
|
||||||
left := toBoolean(leftValue)
|
left := leftValue.bool()
|
||||||
if left {
|
if left {
|
||||||
return TrueValue()
|
return trueValue
|
||||||
}
|
}
|
||||||
return toValue_bool(toBoolean(self.GetValue(right)))
|
return toValue_bool(right.resolve().bool())
|
||||||
|
|
||||||
// Bitwise
|
// Bitwise
|
||||||
case token.AND:
|
case token.AND:
|
||||||
rightValue := self.GetValue(right)
|
rightValue := right.resolve()
|
||||||
return toValue_int32(toInt32(leftValue) & toInt32(rightValue))
|
return toValue_int32(toInt32(leftValue) & toInt32(rightValue))
|
||||||
case token.OR:
|
case token.OR:
|
||||||
rightValue := self.GetValue(right)
|
rightValue := right.resolve()
|
||||||
return toValue_int32(toInt32(leftValue) | toInt32(rightValue))
|
return toValue_int32(toInt32(leftValue) | toInt32(rightValue))
|
||||||
case token.EXCLUSIVE_OR:
|
case token.EXCLUSIVE_OR:
|
||||||
rightValue := self.GetValue(right)
|
rightValue := right.resolve()
|
||||||
return toValue_int32(toInt32(leftValue) ^ toInt32(rightValue))
|
return toValue_int32(toInt32(leftValue) ^ toInt32(rightValue))
|
||||||
|
|
||||||
// Shift
|
// Shift
|
||||||
// (Masking of 0x1f is to restrict the shift to a maximum of 31 places)
|
// (Masking of 0x1f is to restrict the shift to a maximum of 31 places)
|
||||||
case token.SHIFT_LEFT:
|
case token.SHIFT_LEFT:
|
||||||
rightValue := self.GetValue(right)
|
rightValue := right.resolve()
|
||||||
return toValue_int32(toInt32(leftValue) << (toUint32(rightValue) & 0x1f))
|
return toValue_int32(toInt32(leftValue) << (toUint32(rightValue) & 0x1f))
|
||||||
case token.SHIFT_RIGHT:
|
case token.SHIFT_RIGHT:
|
||||||
rightValue := self.GetValue(right)
|
rightValue := right.resolve()
|
||||||
return toValue_int32(toInt32(leftValue) >> (toUint32(rightValue) & 0x1f))
|
return toValue_int32(toInt32(leftValue) >> (toUint32(rightValue) & 0x1f))
|
||||||
case token.UNSIGNED_SHIFT_RIGHT:
|
case token.UNSIGNED_SHIFT_RIGHT:
|
||||||
rightValue := self.GetValue(right)
|
rightValue := right.resolve()
|
||||||
// Shifting an unsigned integer is a logical shift
|
// Shifting an unsigned integer is a logical shift
|
||||||
return toValue_uint32(toUint32(leftValue) >> (toUint32(rightValue) & 0x1f))
|
return toValue_uint32(toUint32(leftValue) >> (toUint32(rightValue) & 0x1f))
|
||||||
|
|
||||||
case token.INSTANCEOF:
|
case token.INSTANCEOF:
|
||||||
rightValue := self.GetValue(right)
|
rightValue := right.resolve()
|
||||||
if !rightValue.IsObject() {
|
if !rightValue.IsObject() {
|
||||||
panic(newTypeError("Expecting a function in instanceof check, but got: %v", rightValue))
|
panic(self.panicTypeError("Expecting a function in instanceof check, but got: %v", rightValue))
|
||||||
}
|
}
|
||||||
return toValue_bool(rightValue._object().HasInstance(leftValue))
|
return toValue_bool(rightValue._object().hasInstance(leftValue))
|
||||||
|
|
||||||
case token.IN:
|
case token.IN:
|
||||||
rightValue := self.GetValue(right)
|
rightValue := right.resolve()
|
||||||
if !rightValue.IsObject() {
|
if !rightValue.IsObject() {
|
||||||
panic(newTypeError())
|
panic(self.panicTypeError())
|
||||||
}
|
}
|
||||||
return toValue_bool(rightValue._object().hasProperty(toString(leftValue)))
|
return toValue_bool(rightValue._object().hasProperty(leftValue.string()))
|
||||||
}
|
}
|
||||||
|
|
||||||
panic(hereBeDragons(operator))
|
panic(hereBeDragons(operator))
|
||||||
}
|
}
|
||||||
|
|
||||||
func valueKindDispatchKey(left _valueType, right _valueType) int {
|
func valueKindDispatchKey(left _valueKind, right _valueKind) int {
|
||||||
return (int(left) << 2) + int(right)
|
return (int(left) << 2) + int(right)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -150,10 +150,10 @@ func makeEqualDispatch() map[int](func(Value, Value) bool) {
|
||||||
key := valueKindDispatchKey
|
key := valueKindDispatchKey
|
||||||
return map[int](func(Value, Value) bool){
|
return map[int](func(Value, Value) bool){
|
||||||
|
|
||||||
key(valueNumber, valueObject): func(x Value, y Value) bool { return x.toFloat() == y.toFloat() },
|
key(valueNumber, valueObject): func(x Value, y Value) bool { return x.float64() == y.float64() },
|
||||||
key(valueString, valueObject): func(x Value, y Value) bool { return x.toFloat() == y.toFloat() },
|
key(valueString, valueObject): func(x Value, y Value) bool { return x.float64() == y.float64() },
|
||||||
key(valueObject, valueNumber): func(x Value, y Value) bool { return x.toFloat() == y.toFloat() },
|
key(valueObject, valueNumber): func(x Value, y Value) bool { return x.float64() == y.float64() },
|
||||||
key(valueObject, valueString): func(x Value, y Value) bool { return x.toFloat() == y.toFloat() },
|
key(valueObject, valueString): func(x Value, y Value) bool { return x.float64() == y.float64() },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -167,7 +167,7 @@ const (
|
||||||
|
|
||||||
func calculateLessThan(left Value, right Value, leftFirst bool) _lessThanResult {
|
func calculateLessThan(left Value, right Value, leftFirst bool) _lessThanResult {
|
||||||
|
|
||||||
x := UndefinedValue()
|
x := Value{}
|
||||||
y := x
|
y := x
|
||||||
|
|
||||||
if leftFirst {
|
if leftFirst {
|
||||||
|
@ -179,14 +179,14 @@ func calculateLessThan(left Value, right Value, leftFirst bool) _lessThanResult
|
||||||
}
|
}
|
||||||
|
|
||||||
result := false
|
result := false
|
||||||
if x._valueType != valueString || y._valueType != valueString {
|
if x.kind != valueString || y.kind != valueString {
|
||||||
x, y := x.toFloat(), y.toFloat()
|
x, y := x.float64(), y.float64()
|
||||||
if math.IsNaN(x) || math.IsNaN(y) {
|
if math.IsNaN(x) || math.IsNaN(y) {
|
||||||
return lessThanUndefined
|
return lessThanUndefined
|
||||||
}
|
}
|
||||||
result = x < y
|
result = x < y
|
||||||
} else {
|
} else {
|
||||||
x, y := x.toString(), y.toString()
|
x, y := x.string(), y.string()
|
||||||
result = x < y
|
result = x < y
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -197,6 +197,7 @@ func calculateLessThan(left Value, right Value, leftFirst bool) _lessThanResult
|
||||||
return lessThanFalse
|
return lessThanFalse
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME Probably a map is not the most efficient way to do this
|
||||||
var lessThanTable [4](map[_lessThanResult]bool) = [4](map[_lessThanResult]bool){
|
var lessThanTable [4](map[_lessThanResult]bool) = [4](map[_lessThanResult]bool){
|
||||||
// <
|
// <
|
||||||
map[_lessThanResult]bool{
|
map[_lessThanResult]bool{
|
||||||
|
@ -231,8 +232,8 @@ func (self *_runtime) calculateComparison(comparator token.Token, left Value, ri
|
||||||
|
|
||||||
// FIXME Use strictEqualityComparison?
|
// FIXME Use strictEqualityComparison?
|
||||||
// TODO This might be redundant now (with regards to evaluateComparison)
|
// TODO This might be redundant now (with regards to evaluateComparison)
|
||||||
x := self.GetValue(left)
|
x := left.resolve()
|
||||||
y := self.GetValue(right)
|
y := right.resolve()
|
||||||
|
|
||||||
kindEqualKind := false
|
kindEqualKind := false
|
||||||
result := true
|
result := true
|
||||||
|
@ -251,7 +252,7 @@ func (self *_runtime) calculateComparison(comparator token.Token, left Value, ri
|
||||||
negate = true
|
negate = true
|
||||||
fallthrough
|
fallthrough
|
||||||
case token.STRICT_EQUAL:
|
case token.STRICT_EQUAL:
|
||||||
if x._valueType != y._valueType {
|
if x.kind != y.kind {
|
||||||
result = false
|
result = false
|
||||||
} else {
|
} else {
|
||||||
kindEqualKind = true
|
kindEqualKind = true
|
||||||
|
@ -260,21 +261,21 @@ func (self *_runtime) calculateComparison(comparator token.Token, left Value, ri
|
||||||
negate = true
|
negate = true
|
||||||
fallthrough
|
fallthrough
|
||||||
case token.EQUAL:
|
case token.EQUAL:
|
||||||
if x._valueType == y._valueType {
|
if x.kind == y.kind {
|
||||||
kindEqualKind = true
|
kindEqualKind = true
|
||||||
} else if x._valueType <= valueUndefined && y._valueType <= valueUndefined {
|
} else if x.kind <= valueNull && y.kind <= valueNull {
|
||||||
result = true
|
result = true
|
||||||
} else if x._valueType <= valueUndefined || y._valueType <= valueUndefined {
|
} else if x.kind <= valueNull || y.kind <= valueNull {
|
||||||
result = false
|
result = false
|
||||||
} else if x._valueType <= valueString && y._valueType <= valueString {
|
} else if x.kind <= valueString && y.kind <= valueString {
|
||||||
result = x.toFloat() == y.toFloat()
|
result = x.float64() == y.float64()
|
||||||
} else if x._valueType == valueBoolean {
|
} else if x.kind == valueBoolean {
|
||||||
result = self.calculateComparison(token.EQUAL, toValue_float64(x.toFloat()), y)
|
result = self.calculateComparison(token.EQUAL, toValue_float64(x.float64()), y)
|
||||||
} else if y._valueType == valueBoolean {
|
} else if y.kind == valueBoolean {
|
||||||
result = self.calculateComparison(token.EQUAL, x, toValue_float64(y.toFloat()))
|
result = self.calculateComparison(token.EQUAL, x, toValue_float64(y.float64()))
|
||||||
} else if x._valueType == valueObject {
|
} else if x.kind == valueObject {
|
||||||
result = self.calculateComparison(token.EQUAL, toPrimitive(x), y)
|
result = self.calculateComparison(token.EQUAL, toPrimitive(x), y)
|
||||||
} else if y._valueType == valueObject {
|
} else if y.kind == valueObject {
|
||||||
result = self.calculateComparison(token.EQUAL, x, toPrimitive(y))
|
result = self.calculateComparison(token.EQUAL, x, toPrimitive(y))
|
||||||
} else {
|
} else {
|
||||||
panic(hereBeDragons("Unable to test for equality: %v ==? %v", x, y))
|
panic(hereBeDragons("Unable to test for equality: %v ==? %v", x, y))
|
||||||
|
@ -284,21 +285,21 @@ func (self *_runtime) calculateComparison(comparator token.Token, left Value, ri
|
||||||
}
|
}
|
||||||
|
|
||||||
if kindEqualKind {
|
if kindEqualKind {
|
||||||
switch x._valueType {
|
switch x.kind {
|
||||||
case valueUndefined, valueNull:
|
case valueUndefined, valueNull:
|
||||||
result = true
|
result = true
|
||||||
case valueNumber:
|
case valueNumber:
|
||||||
x := x.toFloat()
|
x := x.float64()
|
||||||
y := y.toFloat()
|
y := y.float64()
|
||||||
if math.IsNaN(x) || math.IsNaN(y) {
|
if math.IsNaN(x) || math.IsNaN(y) {
|
||||||
result = false
|
result = false
|
||||||
} else {
|
} else {
|
||||||
result = x == y
|
result = x == y
|
||||||
}
|
}
|
||||||
case valueString:
|
case valueString:
|
||||||
result = x.toString() == y.toString()
|
result = x.string() == y.string()
|
||||||
case valueBoolean:
|
case valueBoolean:
|
||||||
result = x.toBoolean() == y.toBoolean()
|
result = x.bool() == y.bool()
|
||||||
case valueObject:
|
case valueObject:
|
||||||
result = x._object() == y._object()
|
result = x._object() == y._object()
|
||||||
default:
|
default:
|
||||||
|
@ -313,5 +314,5 @@ func (self *_runtime) calculateComparison(comparator token.Token, left Value, ri
|
||||||
return result
|
return result
|
||||||
|
|
||||||
ERROR:
|
ERROR:
|
||||||
panic(hereBeDragons("%v (%v) %s %v (%v)", x, x._valueType, comparator, y, y._valueType))
|
panic(hereBeDragons("%v (%v) %s %v (%v)", x, x.kind, comparator, y, y.kind))
|
||||||
}
|
}
|
|
@ -229,7 +229,7 @@ func TestFunction_bind(t *testing.T) {
|
||||||
|
|
||||||
test(`raise:
|
test(`raise:
|
||||||
Math.bind();
|
Math.bind();
|
||||||
`, "TypeError: undefined is not a function")
|
`, "TypeError: 'bind' is not a function")
|
||||||
|
|
||||||
test(`
|
test(`
|
||||||
function construct(fn, arguments) {
|
function construct(fn, arguments) {
|
||||||
|
@ -251,6 +251,14 @@ func TestFunction_bind(t *testing.T) {
|
||||||
var result = new newFn();
|
var result = new newFn();
|
||||||
[ result.hasOwnProperty("abc"), result.hasOwnProperty("def"), result.abc, result.def ];
|
[ result.hasOwnProperty("abc"), result.hasOwnProperty("def"), result.abc, result.def ];
|
||||||
`, "true,true,abc,true")
|
`, "true,true,abc,true")
|
||||||
|
|
||||||
|
test(`
|
||||||
|
abc = function(){
|
||||||
|
return "abc";
|
||||||
|
};
|
||||||
|
def = abc.bind();
|
||||||
|
def.toString();
|
||||||
|
`, "function () { [native code] }")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,28 +7,28 @@ import (
|
||||||
|
|
||||||
var (
|
var (
|
||||||
prototypeValueObject = interface{}(nil)
|
prototypeValueObject = interface{}(nil)
|
||||||
prototypeValueFunction = _functionObject{
|
prototypeValueFunction = _nativeFunctionObject{
|
||||||
call: _nativeCallFunction{"", func(_ FunctionCall) Value {
|
call: func(_ FunctionCall) Value {
|
||||||
return UndefinedValue()
|
return Value{}
|
||||||
}},
|
},
|
||||||
}
|
}
|
||||||
prototypeValueString = _stringASCII("")
|
prototypeValueString = _stringASCII("")
|
||||||
// TODO Make this just false?
|
// TODO Make this just false?
|
||||||
prototypeValueBoolean = Value{
|
prototypeValueBoolean = Value{
|
||||||
_valueType: valueBoolean,
|
kind: valueBoolean,
|
||||||
value: false,
|
value: false,
|
||||||
}
|
}
|
||||||
prototypeValueNumber = Value{
|
prototypeValueNumber = Value{
|
||||||
_valueType: valueNumber,
|
kind: valueNumber,
|
||||||
value: 0,
|
value: 0,
|
||||||
}
|
}
|
||||||
prototypeValueDate = _dateObject{
|
prototypeValueDate = _dateObject{
|
||||||
epoch: 0,
|
epoch: 0,
|
||||||
isNaN: false,
|
isNaN: false,
|
||||||
time: time.Unix(0, 0).UTC(),
|
time: time.Unix(0, 0).UTC(),
|
||||||
value: Value{
|
value: Value{
|
||||||
_valueType: valueNumber,
|
kind: valueNumber,
|
||||||
value: 0,
|
value: 0,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
prototypeValueRegExp = _regExpObject{
|
prototypeValueRegExp = _regExpObject{
|
||||||
|
@ -45,16 +45,13 @@ func newContext() *_runtime {
|
||||||
|
|
||||||
self := &_runtime{}
|
self := &_runtime{}
|
||||||
|
|
||||||
self.GlobalEnvironment = self.newObjectEnvironment(nil, nil)
|
self.globalStash = self.newObjectStash(nil, nil)
|
||||||
self.GlobalObject = self.GlobalEnvironment.Object
|
self.globalObject = self.globalStash.object
|
||||||
|
|
||||||
self.EnterGlobalExecutionContext()
|
|
||||||
|
|
||||||
_newContext(self)
|
_newContext(self)
|
||||||
|
|
||||||
self.eval = self.GlobalObject.property["eval"].value.(Value).value.(*_object)
|
self.eval = self.globalObject.property["eval"].value.(Value).value.(*_object)
|
||||||
self.GlobalObject.prototype = self.Global.ObjectPrototype
|
self.globalObject.prototype = self.global.ObjectPrototype
|
||||||
//self.parser = ast.NewParser()
|
|
||||||
|
|
||||||
return self
|
return self
|
||||||
}
|
}
|
||||||
|
@ -94,13 +91,13 @@ func (self *_object) hasPrimitive() bool {
|
||||||
|
|
||||||
func (runtime *_runtime) newObject() *_object {
|
func (runtime *_runtime) newObject() *_object {
|
||||||
self := runtime.newClassObject("Object")
|
self := runtime.newClassObject("Object")
|
||||||
self.prototype = runtime.Global.ObjectPrototype
|
self.prototype = runtime.global.ObjectPrototype
|
||||||
return self
|
return self
|
||||||
}
|
}
|
||||||
|
|
||||||
func (runtime *_runtime) newArray(length uint32) *_object {
|
func (runtime *_runtime) newArray(length uint32) *_object {
|
||||||
self := runtime.newArrayObject(length)
|
self := runtime.newArrayObject(length)
|
||||||
self.prototype = runtime.Global.ArrayPrototype
|
self.prototype = runtime.global.ArrayPrototype
|
||||||
return self
|
return self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,19 +114,19 @@ func (runtime *_runtime) newArrayOf(valueArray []Value) *_object {
|
||||||
|
|
||||||
func (runtime *_runtime) newString(value Value) *_object {
|
func (runtime *_runtime) newString(value Value) *_object {
|
||||||
self := runtime.newStringObject(value)
|
self := runtime.newStringObject(value)
|
||||||
self.prototype = runtime.Global.StringPrototype
|
self.prototype = runtime.global.StringPrototype
|
||||||
return self
|
return self
|
||||||
}
|
}
|
||||||
|
|
||||||
func (runtime *_runtime) newBoolean(value Value) *_object {
|
func (runtime *_runtime) newBoolean(value Value) *_object {
|
||||||
self := runtime.newBooleanObject(value)
|
self := runtime.newBooleanObject(value)
|
||||||
self.prototype = runtime.Global.BooleanPrototype
|
self.prototype = runtime.global.BooleanPrototype
|
||||||
return self
|
return self
|
||||||
}
|
}
|
||||||
|
|
||||||
func (runtime *_runtime) newNumber(value Value) *_object {
|
func (runtime *_runtime) newNumber(value Value) *_object {
|
||||||
self := runtime.newNumberObject(value)
|
self := runtime.newNumberObject(value)
|
||||||
self.prototype = runtime.Global.NumberPrototype
|
self.prototype = runtime.global.NumberPrototype
|
||||||
return self
|
return self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -139,17 +136,17 @@ func (runtime *_runtime) newRegExp(patternValue Value, flagsValue Value) *_objec
|
||||||
flags := ""
|
flags := ""
|
||||||
if object := patternValue._object(); object != nil && object.class == "RegExp" {
|
if object := patternValue._object(); object != nil && object.class == "RegExp" {
|
||||||
if flagsValue.IsDefined() {
|
if flagsValue.IsDefined() {
|
||||||
panic(newTypeError("Cannot supply flags when constructing one RegExp from another"))
|
panic(runtime.panicTypeError("Cannot supply flags when constructing one RegExp from another"))
|
||||||
}
|
}
|
||||||
regExp := object.regExpValue()
|
regExp := object.regExpValue()
|
||||||
pattern = regExp.source
|
pattern = regExp.source
|
||||||
flags = regExp.flags
|
flags = regExp.flags
|
||||||
} else {
|
} else {
|
||||||
if patternValue.IsDefined() {
|
if patternValue.IsDefined() {
|
||||||
pattern = toString(patternValue)
|
pattern = patternValue.string()
|
||||||
}
|
}
|
||||||
if flagsValue.IsDefined() {
|
if flagsValue.IsDefined() {
|
||||||
flags = toString(flagsValue)
|
flags = flagsValue.string()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -158,14 +155,14 @@ func (runtime *_runtime) newRegExp(patternValue Value, flagsValue Value) *_objec
|
||||||
|
|
||||||
func (runtime *_runtime) _newRegExp(pattern string, flags string) *_object {
|
func (runtime *_runtime) _newRegExp(pattern string, flags string) *_object {
|
||||||
self := runtime.newRegExpObject(pattern, flags)
|
self := runtime.newRegExpObject(pattern, flags)
|
||||||
self.prototype = runtime.Global.RegExpPrototype
|
self.prototype = runtime.global.RegExpPrototype
|
||||||
return self
|
return self
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO Should (probably) be one argument, right? This is redundant
|
// TODO Should (probably) be one argument, right? This is redundant
|
||||||
func (runtime *_runtime) newDate(epoch float64) *_object {
|
func (runtime *_runtime) newDate(epoch float64) *_object {
|
||||||
self := runtime.newDateObject(epoch)
|
self := runtime.newDateObject(epoch)
|
||||||
self.prototype = runtime.Global.DatePrototype
|
self.prototype = runtime.global.DatePrototype
|
||||||
return self
|
return self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -186,8 +183,8 @@ func (runtime *_runtime) newError(name string, message Value) *_object {
|
||||||
return runtime.newURIError(message)
|
return runtime.newURIError(message)
|
||||||
}
|
}
|
||||||
|
|
||||||
self = runtime.newErrorObject(message)
|
self = runtime.newErrorObject(name, message)
|
||||||
self.prototype = runtime.Global.ErrorPrototype
|
self.prototype = runtime.global.ErrorPrototype
|
||||||
if name != "" {
|
if name != "" {
|
||||||
self.defineProperty("name", toValue_string(name), 0111, false)
|
self.defineProperty("name", toValue_string(name), 0111, false)
|
||||||
}
|
}
|
||||||
|
@ -196,19 +193,29 @@ func (runtime *_runtime) newError(name string, message Value) *_object {
|
||||||
|
|
||||||
func (runtime *_runtime) newNativeFunction(name string, _nativeFunction _nativeFunction) *_object {
|
func (runtime *_runtime) newNativeFunction(name string, _nativeFunction _nativeFunction) *_object {
|
||||||
self := runtime.newNativeFunctionObject(name, _nativeFunction, 0)
|
self := runtime.newNativeFunctionObject(name, _nativeFunction, 0)
|
||||||
self.prototype = runtime.Global.FunctionPrototype
|
self.prototype = runtime.global.FunctionPrototype
|
||||||
prototype := runtime.newObject()
|
prototype := runtime.newObject()
|
||||||
self.defineProperty("prototype", toValue_object(prototype), 0100, false)
|
self.defineProperty("prototype", toValue_object(prototype), 0100, false)
|
||||||
prototype.defineProperty("constructor", toValue_object(self), 0100, false)
|
prototype.defineProperty("constructor", toValue_object(self), 0100, false)
|
||||||
return self
|
return self
|
||||||
}
|
}
|
||||||
|
|
||||||
func (runtime *_runtime) newNodeFunction(node *_nodeFunctionLiteral, scopeEnvironment _environment) *_object {
|
func (runtime *_runtime) newNodeFunction(node *_nodeFunctionLiteral, scopeEnvironment _stash) *_object {
|
||||||
// TODO Implement 13.2 fully
|
// TODO Implement 13.2 fully
|
||||||
self := runtime.newNodeFunctionObject(node, scopeEnvironment)
|
self := runtime.newNodeFunctionObject(node, scopeEnvironment)
|
||||||
self.prototype = runtime.Global.FunctionPrototype
|
self.prototype = runtime.global.FunctionPrototype
|
||||||
prototype := runtime.newObject()
|
prototype := runtime.newObject()
|
||||||
self.defineProperty("prototype", toValue_object(prototype), 0100, false)
|
self.defineProperty("prototype", toValue_object(prototype), 0100, false)
|
||||||
prototype.defineProperty("constructor", toValue_object(self), 0101, false)
|
prototype.defineProperty("constructor", toValue_object(self), 0101, false)
|
||||||
return self
|
return self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME Only in one place...
|
||||||
|
func (runtime *_runtime) newBoundFunction(target *_object, this Value, argumentList []Value) *_object {
|
||||||
|
self := runtime.newBoundFunctionObject(target, this, argumentList)
|
||||||
|
self.prototype = runtime.global.FunctionPrototype
|
||||||
|
prototype := runtime.newObject()
|
||||||
|
self.defineProperty("prototype", toValue_object(prototype), 0100, false)
|
||||||
|
prototype.defineProperty("constructor", toValue_object(self), 0100, false)
|
||||||
|
return self
|
||||||
|
}
|
|
@ -31,35 +31,38 @@ func TestGlobal(t *testing.T) {
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
|
||||||
value := runtime.localGet("Object")._object().Call(UndefinedValue(), []Value{toValue(runtime.newObject())})
|
// FIXME enterGlobalScope
|
||||||
is(value.IsObject(), true)
|
if false {
|
||||||
is(value, "[object Object]")
|
value := runtime.scope.lexical.getBinding("Object", false)._object().call(UndefinedValue(), []Value{toValue(runtime.newObject())}, false, nativeFrame)
|
||||||
is(value._object().prototype == runtime.Global.ObjectPrototype, true)
|
is(value.IsObject(), true)
|
||||||
is(value._object().prototype == runtime.Global.Object.get("prototype")._object(), true)
|
is(value, "[object Object]")
|
||||||
is(value._object().get("toString"), "function toString() { [native code] }")
|
is(value._object().prototype == runtime.global.ObjectPrototype, true)
|
||||||
is(call(value.Object(), "hasOwnProperty", "hasOwnProperty"), false)
|
is(value._object().prototype == runtime.global.Object.get("prototype")._object(), true)
|
||||||
|
is(value._object().get("toString"), "function toString() { [native code] }")
|
||||||
|
is(call(value.Object(), "hasOwnProperty", "hasOwnProperty"), false)
|
||||||
|
|
||||||
is(call(value._object().get("toString")._object().prototype, "toString"), "function () { [native code] }") // TODO Is this right?
|
is(call(value._object().get("toString")._object().prototype, "toString"), "function () { [native code] }") // TODO Is this right?
|
||||||
is(value._object().get("toString")._object().get("toString"), "function toString() { [native code] }")
|
is(value._object().get("toString")._object().get("toString"), "function toString() { [native code] }")
|
||||||
is(value._object().get("toString")._object().get("toString")._object(), "function toString() { [native code] }")
|
is(value._object().get("toString")._object().get("toString")._object(), "function toString() { [native code] }")
|
||||||
|
|
||||||
is(call(value._object(), "propertyIsEnumerable", "isPrototypeOf"), false)
|
is(call(value._object(), "propertyIsEnumerable", "isPrototypeOf"), false)
|
||||||
value._object().put("xyzzy", toValue_string("Nothing happens."), false)
|
value._object().put("xyzzy", toValue_string("Nothing happens."), false)
|
||||||
is(call(value, "propertyIsEnumerable", "isPrototypeOf"), false)
|
is(call(value, "propertyIsEnumerable", "isPrototypeOf"), false)
|
||||||
is(call(value, "propertyIsEnumerable", "xyzzy"), true)
|
is(call(value, "propertyIsEnumerable", "xyzzy"), true)
|
||||||
is(value._object().get("xyzzy"), "Nothing happens.")
|
is(value._object().get("xyzzy"), "Nothing happens.")
|
||||||
|
|
||||||
is(call(runtime.localGet("Object"), "isPrototypeOf", value), false)
|
is(call(runtime.scope.lexical.getBinding("Object", false), "isPrototypeOf", value), false)
|
||||||
is(call(runtime.localGet("Object")._object().get("prototype"), "isPrototypeOf", value), true)
|
is(call(runtime.scope.lexical.getBinding("Object", false)._object().get("prototype"), "isPrototypeOf", value), true)
|
||||||
is(call(runtime.localGet("Function"), "isPrototypeOf", value), false)
|
is(call(runtime.scope.lexical.getBinding("Function", false), "isPrototypeOf", value), false)
|
||||||
|
|
||||||
is(runtime.newObject().prototype == runtime.Global.Object.get("prototype")._object(), true)
|
is(runtime.newObject().prototype == runtime.global.Object.get("prototype")._object(), true)
|
||||||
|
|
||||||
abc := runtime.newBoolean(toValue_bool(true))
|
abc := runtime.newBoolean(toValue_bool(true))
|
||||||
is(toValue_object(abc), "true") // TODO Call primitive?
|
is(toValue_object(abc), "true") // TODO Call primitive?
|
||||||
|
|
||||||
def := runtime.localGet("Boolean")._object().Construct(UndefinedValue(), []Value{})
|
//def := runtime.localGet("Boolean")._object().Construct(UndefinedValue(), []Value{})
|
||||||
is(def, "false") // TODO Call primitive?
|
//is(def, "false") // TODO Call primitive?
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
test(`new Number().constructor == Number`, true)
|
test(`new Number().constructor == Number`, true)
|
|
@ -43,7 +43,7 @@ for (qw/int int8 int16 int32 int64 uint uint8 uint16 uint32 uint64 float32 float
|
||||||
|
|
||||||
func toValue_$_(value $_) Value {
|
func toValue_$_(value $_) Value {
|
||||||
return Value{
|
return Value{
|
||||||
_valueType: valueNumber,
|
kind: valueNumber,
|
||||||
value: value,
|
value: value,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -54,28 +54,28 @@ $fmt->print(<<_END_);
|
||||||
|
|
||||||
func toValue_string(value string) Value {
|
func toValue_string(value string) Value {
|
||||||
return Value{
|
return Value{
|
||||||
_valueType: valueString,
|
kind: valueString,
|
||||||
value: value,
|
value: value,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func toValue_string16(value []uint16) Value {
|
func toValue_string16(value []uint16) Value {
|
||||||
return Value{
|
return Value{
|
||||||
_valueType: valueString,
|
kind: valueString,
|
||||||
value: value,
|
value: value,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func toValue_bool(value bool) Value {
|
func toValue_bool(value bool) Value {
|
||||||
return Value{
|
return Value{
|
||||||
_valueType: valueBoolean,
|
kind: valueBoolean,
|
||||||
value: value,
|
value: value,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func toValue_object(value *_object) Value {
|
func toValue_object(value *_object) Value {
|
||||||
return Value{
|
return Value{
|
||||||
_valueType: valueObject,
|
kind: valueObject,
|
||||||
value: value,
|
value: value,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -642,9 +642,9 @@ sub newContext {
|
||||||
my $propertyOrder = $self->propertyOrder(@propertyMap);
|
my $propertyOrder = $self->propertyOrder(@propertyMap);
|
||||||
$propertyOrder =~ s/^propertyOrder: //;
|
$propertyOrder =~ s/^propertyOrder: //;
|
||||||
return
|
return
|
||||||
"runtime.GlobalObject.property =",
|
"runtime.globalObject.property =",
|
||||||
@propertyMap,
|
@propertyMap,
|
||||||
"runtime.GlobalObject.propertyOrder =",
|
"runtime.globalObject.propertyOrder =",
|
||||||
$propertyOrder,
|
$propertyOrder,
|
||||||
;
|
;
|
||||||
}),
|
}),
|
||||||
|
@ -666,7 +666,7 @@ sub block {
|
||||||
while (@input) {
|
while (@input) {
|
||||||
local $_ = shift @input;
|
local $_ = shift @input;
|
||||||
if (m/^\./) {
|
if (m/^\./) {
|
||||||
$_ = "runtime.Global$_";
|
$_ = "runtime.global$_";
|
||||||
}
|
}
|
||||||
if (m/ :?=$/) {
|
if (m/ :?=$/) {
|
||||||
$_ .= shift @input;
|
$_ .= shift @input;
|
||||||
|
@ -712,7 +712,7 @@ sub globalDeclare {
|
||||||
my @got;
|
my @got;
|
||||||
while (@_) {
|
while (@_) {
|
||||||
my $name = shift;
|
my $name = shift;
|
||||||
push @got, $self->property($name, $self->objectValue("runtime.Global.$name"), "0101"),
|
push @got, $self->property($name, $self->objectValue("runtime.global.$name"), "0101"),
|
||||||
}
|
}
|
||||||
return @got;
|
return @got;
|
||||||
}
|
}
|
||||||
|
@ -743,7 +743,7 @@ sub globalObject {
|
||||||
runtime: runtime,
|
runtime: runtime,
|
||||||
class: "$name",
|
class: "$name",
|
||||||
objectClass: _classObject,
|
objectClass: _classObject,
|
||||||
prototype: runtime.Global.ObjectPrototype,
|
prototype: runtime.global.ObjectPrototype,
|
||||||
extensible: true,
|
extensible: true,
|
||||||
$propertyMap
|
$propertyMap
|
||||||
}
|
}
|
||||||
|
@ -757,7 +757,7 @@ sub globalFunction {
|
||||||
|
|
||||||
my $builtin = "builtin${name}";
|
my $builtin = "builtin${name}";
|
||||||
my $builtinNew = "builtinNew${name}";
|
my $builtinNew = "builtinNew${name}";
|
||||||
my $prototype = "runtime.Global.${name}Prototype";
|
my $prototype = "runtime.global.${name}Prototype";
|
||||||
my $propertyMap = "";
|
my $propertyMap = "";
|
||||||
unshift @_,
|
unshift @_,
|
||||||
$self->property("length", $self->numberValue($length), "0"),
|
$self->property("length", $self->numberValue($length), "0"),
|
||||||
|
@ -772,7 +772,7 @@ sub globalFunction {
|
||||||
|
|
||||||
push @postblock, $self->statement(
|
push @postblock, $self->statement(
|
||||||
"$prototype.property[\"constructor\"] =",
|
"$prototype.property[\"constructor\"] =",
|
||||||
$self->property(undef, $self->objectValue("runtime.Global.${name}"), "0101"),
|
$self->property(undef, $self->objectValue("runtime.global.${name}"), "0101"),
|
||||||
);
|
);
|
||||||
|
|
||||||
return trim <<_END_;
|
return trim <<_END_;
|
||||||
|
@ -780,9 +780,9 @@ sub globalFunction {
|
||||||
runtime: runtime,
|
runtime: runtime,
|
||||||
class: "Function",
|
class: "Function",
|
||||||
objectClass: _classObject,
|
objectClass: _classObject,
|
||||||
prototype: runtime.Global.FunctionPrototype,
|
prototype: runtime.global.FunctionPrototype,
|
||||||
extensible: true,
|
extensible: true,
|
||||||
value: @{[ $self->functionOf($self->nativeCallFunction($name, $builtin), $builtinNew) ]},
|
value: @{[ $self->nativeFunctionOf($name, $builtin, $builtinNew) ]},
|
||||||
$propertyMap
|
$propertyMap
|
||||||
}
|
}
|
||||||
_END_
|
_END_
|
||||||
|
@ -813,7 +813,7 @@ sub globalPrototype {
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($prototype =~ m/^\./) {
|
if ($prototype =~ m/^\./) {
|
||||||
$prototype = "runtime.Global$prototype";
|
$prototype = "runtime.global$prototype";
|
||||||
}
|
}
|
||||||
|
|
||||||
my $propertyMap = "";
|
my $propertyMap = "";
|
||||||
|
@ -869,11 +869,11 @@ sub newFunction {
|
||||||
runtime: runtime,
|
runtime: runtime,
|
||||||
class: "Function",
|
class: "Function",
|
||||||
objectClass: _classObject,
|
objectClass: _classObject,
|
||||||
prototype: runtime.Global.FunctionPrototype,
|
prototype: runtime.global.FunctionPrototype,
|
||||||
extensible: true,
|
extensible: true,
|
||||||
property: @{[ join "\n", $self->propertyMap(@propertyMap) ]},
|
property: @{[ join "\n", $self->propertyMap(@propertyMap) ]},
|
||||||
$propertyOrder
|
$propertyOrder
|
||||||
value: @{[ $self->functionOf($self->nativeCallFunction($name, $func)) ]},
|
value: @{[ $self->nativeFunctionOf($name, $func) ]},
|
||||||
}
|
}
|
||||||
_END_
|
_END_
|
||||||
);
|
);
|
||||||
|
@ -892,7 +892,7 @@ sub newObject {
|
||||||
runtime: runtime,
|
runtime: runtime,
|
||||||
class: "Object",
|
class: "Object",
|
||||||
objectClass: _classObject,
|
objectClass: _classObject,
|
||||||
prototype: runtime.Global.ObjectPrototype,
|
prototype: runtime.global.ObjectPrototype,
|
||||||
extensible: true,
|
extensible: true,
|
||||||
property: $propertyMap,
|
property: $propertyMap,
|
||||||
$propertyOrder,
|
$propertyOrder,
|
||||||
|
@ -917,7 +917,7 @@ sub newPrototypeObject {
|
||||||
runtime: runtime,
|
runtime: runtime,
|
||||||
class: "$class",
|
class: "$class",
|
||||||
objectClass: $objectClass,
|
objectClass: $objectClass,
|
||||||
prototype: runtime.Global.ObjectPrototype,
|
prototype: runtime.global.ObjectPrototype,
|
||||||
extensible: true,
|
extensible: true,
|
||||||
property: $propertyMap,
|
property: $propertyMap,
|
||||||
$propertyOrder,
|
$propertyOrder,
|
||||||
|
@ -959,6 +959,26 @@ _functionObject{
|
||||||
_END_
|
_END_
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub nativeFunctionOf {
|
||||||
|
my $self = shift;
|
||||||
|
my $name = shift;
|
||||||
|
my $call = shift;
|
||||||
|
my $construct = shift;
|
||||||
|
if ($construct) {
|
||||||
|
$construct = "construct: $construct,";
|
||||||
|
} else {
|
||||||
|
$construct = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
return trim <<_END_
|
||||||
|
_nativeFunctionObject{
|
||||||
|
name: "$name",
|
||||||
|
call: $call,
|
||||||
|
$construct
|
||||||
|
}
|
||||||
|
_END_
|
||||||
|
}
|
||||||
|
|
||||||
sub nameProperty {
|
sub nameProperty {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
my $name = shift;
|
my $name = shift;
|
||||||
|
@ -977,7 +997,7 @@ sub numberValue {
|
||||||
my $value = shift;
|
my $value = shift;
|
||||||
return trim <<_END_;
|
return trim <<_END_;
|
||||||
Value{
|
Value{
|
||||||
_valueType: valueNumber,
|
kind: valueNumber,
|
||||||
value: $value,
|
value: $value,
|
||||||
}
|
}
|
||||||
_END_
|
_END_
|
||||||
|
@ -1028,7 +1048,7 @@ sub objectValue {
|
||||||
my $value = shift;
|
my $value = shift;
|
||||||
return trim <<_END_
|
return trim <<_END_
|
||||||
Value{
|
Value{
|
||||||
_valueType: valueObject,
|
kind: valueObject,
|
||||||
value: $value,
|
value: $value,
|
||||||
}
|
}
|
||||||
_END_
|
_END_
|
||||||
|
@ -1039,7 +1059,7 @@ sub stringValue {
|
||||||
my $value = shift;
|
my $value = shift;
|
||||||
return trim <<_END_
|
return trim <<_END_
|
||||||
Value{
|
Value{
|
||||||
_valueType: valueString,
|
kind: valueString,
|
||||||
value: "$value",
|
value: "$value",
|
||||||
}
|
}
|
||||||
_END_
|
_END_
|
||||||
|
@ -1050,7 +1070,7 @@ sub booleanValue {
|
||||||
my $value = shift;
|
my $value = shift;
|
||||||
return trim <<_END_
|
return trim <<_END_
|
||||||
Value{
|
Value{
|
||||||
_valueType: valueBoolean,
|
kind: valueBoolean,
|
||||||
value: $value,
|
value: $value,
|
||||||
}
|
}
|
||||||
_END_
|
_END_
|
||||||
|
@ -1060,7 +1080,7 @@ sub undefinedValue {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
return trim <<_END_
|
return trim <<_END_
|
||||||
Value{
|
Value{
|
||||||
_valueType: valueUndefined,
|
kind: valueUndefined,
|
||||||
}
|
}
|
||||||
_END_
|
_END_
|
||||||
}
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -144,15 +144,13 @@ func TestNumber_toLocaleString(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_toInteger(t *testing.T) {
|
func TestValue_number(t *testing.T) {
|
||||||
tt(t, func() {
|
tt(t, func() {
|
||||||
integer := toInteger(toValue(0.0))
|
nm := toValue(0.0).number()
|
||||||
is(integer.valid(), true)
|
is(nm.kind, numberInteger)
|
||||||
is(integer.exact(), true)
|
|
||||||
|
|
||||||
integer = toInteger(toValue(3.14159))
|
nm = toValue(3.14159).number()
|
||||||
is(integer.valid(), true)
|
is(nm.kind, numberFloat)
|
||||||
is(integer.exact(), false)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -85,20 +85,20 @@ func (self *_object) DefaultValue(hint _defaultValueHint) Value {
|
||||||
}
|
}
|
||||||
for _, methodName := range methodSequence {
|
for _, methodName := range methodSequence {
|
||||||
method := self.get(methodName)
|
method := self.get(methodName)
|
||||||
|
// FIXME This is redundant...
|
||||||
if method.isCallable() {
|
if method.isCallable() {
|
||||||
result := method._object().Call(toValue_object(self))
|
result := method._object().call(toValue_object(self), nil, false, nativeFrame)
|
||||||
if result.IsPrimitive() {
|
if result.IsPrimitive() {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
panic(newTypeError())
|
panic(self.runtime.panicTypeError())
|
||||||
return UndefinedValue()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *_object) String() string {
|
func (self *_object) String() string {
|
||||||
return toString(self.DefaultValue(defaultValueHintString))
|
return self.DefaultValue(defaultValueHintString).string()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *_object) defineProperty(name string, value Value, mode _propertyMode, throw bool) bool {
|
func (self *_object) defineProperty(name string, value Value, mode _propertyMode, throw bool) bool {
|
||||||
|
@ -130,7 +130,7 @@ func (self *_object) _read(name string) (_property, bool) {
|
||||||
|
|
||||||
func (self *_object) _write(name string, value interface{}, mode _propertyMode) {
|
func (self *_object) _write(name string, value interface{}, mode _propertyMode) {
|
||||||
if value == nil {
|
if value == nil {
|
||||||
value = UndefinedValue()
|
value = Value{}
|
||||||
}
|
}
|
||||||
_, exists := self.property[name]
|
_, exists := self.property[name]
|
||||||
self.property[name] = _property{value, mode}
|
self.property[name] = _property{value, mode}
|
|
@ -193,7 +193,7 @@ func objectGet(self *_object, name string) Value {
|
||||||
if property != nil {
|
if property != nil {
|
||||||
return property.get(self)
|
return property.get(self)
|
||||||
}
|
}
|
||||||
return UndefinedValue()
|
return Value{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 8.12.4
|
// 8.12.4
|
||||||
|
@ -214,7 +214,7 @@ func _objectCanPut(self *_object, name string) (canPut bool, property *_property
|
||||||
canPut = setter != nil
|
canPut = setter != nil
|
||||||
return
|
return
|
||||||
default:
|
default:
|
||||||
panic(newTypeError())
|
panic(self.runtime.panicTypeError())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -238,10 +238,8 @@ func _objectCanPut(self *_object, name string) (canPut bool, property *_property
|
||||||
canPut = setter != nil
|
canPut = setter != nil
|
||||||
return
|
return
|
||||||
default:
|
default:
|
||||||
panic(newTypeError())
|
panic(self.runtime.panicTypeError())
|
||||||
}
|
}
|
||||||
|
|
||||||
return false, nil, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 8.12.5
|
// 8.12.5
|
||||||
|
@ -258,9 +256,9 @@ func objectPut(self *_object, name string, value Value, throw bool) {
|
||||||
// incompatible canPut routine
|
// incompatible canPut routine
|
||||||
canPut, property, setter := _objectCanPut(self, name)
|
canPut, property, setter := _objectCanPut(self, name)
|
||||||
if !canPut {
|
if !canPut {
|
||||||
typeErrorResult(throw)
|
self.runtime.typeErrorResult(throw)
|
||||||
} else if setter != nil {
|
} else if setter != nil {
|
||||||
setter.callSet(toValue(self), value)
|
setter.call(toValue(self), []Value{value}, false, nativeFrame)
|
||||||
} else if property != nil {
|
} else if property != nil {
|
||||||
property.value = value
|
property.value = value
|
||||||
self.defineOwnProperty(name, *property, throw)
|
self.defineOwnProperty(name, *property, throw)
|
||||||
|
@ -274,7 +272,7 @@ func objectPut(self *_object, name string, value Value, throw bool) {
|
||||||
//
|
//
|
||||||
// Right now, code should never get here, see above
|
// Right now, code should never get here, see above
|
||||||
if !self.canPut(name) {
|
if !self.canPut(name) {
|
||||||
typeErrorResult(throw)
|
self.runtime.typeErrorResult(throw)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -283,7 +281,7 @@ func objectPut(self *_object, name string, value Value, throw bool) {
|
||||||
property = self.getProperty(name)
|
property = self.getProperty(name)
|
||||||
if property != nil {
|
if property != nil {
|
||||||
if getSet, isAccessor := property.value.(_propertyGetSet); isAccessor {
|
if getSet, isAccessor := property.value.(_propertyGetSet); isAccessor {
|
||||||
getSet[1].callSet(toValue(self), value)
|
getSet[1].call(toValue(self), []Value{value}, false, nativeFrame)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -295,14 +293,14 @@ func objectPut(self *_object, name string, value Value, throw bool) {
|
||||||
self.defineOwnProperty(name, *property, throw)
|
self.defineOwnProperty(name, *property, throw)
|
||||||
case _propertyGetSet:
|
case _propertyGetSet:
|
||||||
if propertyValue[1] != nil {
|
if propertyValue[1] != nil {
|
||||||
propertyValue[1].callSet(toValue(self), value)
|
propertyValue[1].call(toValue(self), []Value{value}, false, nativeFrame)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if throw {
|
if throw {
|
||||||
panic(newTypeError())
|
panic(self.runtime.panicTypeError())
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
panic(newTypeError())
|
panic(self.runtime.panicTypeError())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -442,7 +440,7 @@ func objectDefineOwnProperty(self *_object, name string, descriptor _property, t
|
||||||
}
|
}
|
||||||
Reject:
|
Reject:
|
||||||
if throw {
|
if throw {
|
||||||
panic(newTypeError())
|
panic(self.runtime.panicTypeError())
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -456,29 +454,40 @@ func objectDelete(self *_object, name string, throw bool) bool {
|
||||||
self._delete(name)
|
self._delete(name)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return typeErrorResult(throw)
|
return self.runtime.typeErrorResult(throw)
|
||||||
}
|
}
|
||||||
|
|
||||||
func objectClone(self0 *_object, self1 *_object, clone *_clone) *_object {
|
func objectClone(in *_object, out *_object, clone *_clone) *_object {
|
||||||
*self1 = *self0
|
*out = *in
|
||||||
|
|
||||||
self1.runtime = clone.runtime
|
out.runtime = clone.runtime
|
||||||
if self1.prototype != nil {
|
if out.prototype != nil {
|
||||||
self1.prototype = clone.object(self0.prototype)
|
out.prototype = clone.object(in.prototype)
|
||||||
}
|
}
|
||||||
self1.property = make(map[string]_property, len(self0.property))
|
out.property = make(map[string]_property, len(in.property))
|
||||||
self1.propertyOrder = make([]string, len(self0.propertyOrder))
|
out.propertyOrder = make([]string, len(in.propertyOrder))
|
||||||
copy(self1.propertyOrder, self0.propertyOrder)
|
copy(out.propertyOrder, in.propertyOrder)
|
||||||
for index, property := range self0.property {
|
for index, property := range in.property {
|
||||||
self1.property[index] = clone.property(property)
|
out.property[index] = clone.property(property)
|
||||||
}
|
}
|
||||||
|
|
||||||
switch value := self0.value.(type) {
|
switch value := in.value.(type) {
|
||||||
case _functionObject:
|
case _nativeFunctionObject:
|
||||||
self1.value = value.clone(clone)
|
out.value = value
|
||||||
|
case _bindFunctionObject:
|
||||||
|
out.value = _bindFunctionObject{
|
||||||
|
target: clone.object(value.target),
|
||||||
|
this: clone.value(value.this),
|
||||||
|
argumentList: clone.valueArray(value.argumentList),
|
||||||
|
}
|
||||||
|
case _nodeFunctionObject:
|
||||||
|
out.value = _nodeFunctionObject{
|
||||||
|
node: value.node,
|
||||||
|
stash: clone.stash(value.stash),
|
||||||
|
}
|
||||||
case _argumentsObject:
|
case _argumentsObject:
|
||||||
self1.value = value.clone(clone)
|
out.value = value.clone(clone)
|
||||||
}
|
}
|
||||||
|
|
||||||
return self1
|
return out
|
||||||
}
|
}
|
|
@ -57,7 +57,7 @@ Set a Go function
|
||||||
|
|
||||||
vm.Set("sayHello", func(call otto.FunctionCall) otto.Value {
|
vm.Set("sayHello", func(call otto.FunctionCall) otto.Value {
|
||||||
fmt.Printf("Hello, %s.\n", call.Argument(0).String())
|
fmt.Printf("Hello, %s.\n", call.Argument(0).String())
|
||||||
return otto.UndefinedValue()
|
return otto.Value{}
|
||||||
})
|
})
|
||||||
|
|
||||||
Set a Go function that returns something useful
|
Set a Go function that returns something useful
|
||||||
|
@ -131,7 +131,6 @@ Caveat Emptor
|
||||||
The following are some limitations with otto:
|
The following are some limitations with otto:
|
||||||
|
|
||||||
* "use strict" will parse, but does nothing.
|
* "use strict" will parse, but does nothing.
|
||||||
* Error reporting needs to be improved.
|
|
||||||
* The regular expression engine (re2/regexp) is not fully compatible with the ECMA5 specification.
|
* The regular expression engine (re2/regexp) is not fully compatible with the ECMA5 specification.
|
||||||
|
|
||||||
Regular Expression Incompatibility
|
Regular Expression Incompatibility
|
||||||
|
@ -190,16 +189,18 @@ If you want to stop long running executions (like third-party code), you can use
|
||||||
}
|
}
|
||||||
fmt.Fprintf(os.Stderr, "Ran code successfully: %v\n", duration)
|
fmt.Fprintf(os.Stderr, "Ran code successfully: %v\n", duration)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
vm := otto.New()
|
vm := otto.New()
|
||||||
vm.Interrupt = make(chan func())
|
vm.Interrupt = make(chan func(), 1) // The buffer prevents blocking
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
time.Sleep(2 * time.Second) // Stop after two seconds
|
time.Sleep(2 * time.Second) // Stop after two seconds
|
||||||
vm.Interrupt <- func() {
|
vm.Interrupt <- func() {
|
||||||
panic(halt)
|
panic(halt)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
vm.Run(unsafe) // Here be dragons (risky code)
|
vm.Run(unsafe) // Here be dragons (risky code)
|
||||||
vm.Interrupt = nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Where is setTimeout/setInterval?
|
Where is setTimeout/setInterval?
|
||||||
|
@ -242,7 +243,7 @@ func New() *Otto {
|
||||||
self := &Otto{
|
self := &Otto{
|
||||||
runtime: newContext(),
|
runtime: newContext(),
|
||||||
}
|
}
|
||||||
self.runtime.Otto = self
|
self.runtime.otto = self
|
||||||
self.Set("console", self.runtime.newConsole())
|
self.Set("console", self.runtime.newConsole())
|
||||||
|
|
||||||
registry.Apply(func(entry registry.Entry) {
|
registry.Apply(func(entry registry.Entry) {
|
||||||
|
@ -256,7 +257,7 @@ func (otto *Otto) clone() *Otto {
|
||||||
self := &Otto{
|
self := &Otto{
|
||||||
runtime: otto.runtime.clone(),
|
runtime: otto.runtime.clone(),
|
||||||
}
|
}
|
||||||
self.runtime.Otto = self
|
self.runtime.otto = self
|
||||||
return self
|
return self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -272,7 +273,7 @@ func (otto *Otto) clone() *Otto {
|
||||||
//
|
//
|
||||||
func Run(src interface{}) (*Otto, Value, error) {
|
func Run(src interface{}) (*Otto, Value, error) {
|
||||||
otto := New()
|
otto := New()
|
||||||
value, err := otto.Run(src)
|
value, err := otto.Run(src) // This already does safety checking
|
||||||
return otto, value, err
|
return otto, value, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -288,7 +289,11 @@ func Run(src interface{}) (*Otto, Value, error) {
|
||||||
// src may also be a Program, but if the AST has been modified, then runtime behavior is undefined.
|
// src may also be a Program, but if the AST has been modified, then runtime behavior is undefined.
|
||||||
//
|
//
|
||||||
func (self Otto) Run(src interface{}) (Value, error) {
|
func (self Otto) Run(src interface{}) (Value, error) {
|
||||||
return self.runtime.cmpl_run(src)
|
value, err := self.runtime.cmpl_run(src)
|
||||||
|
if !value.safe() {
|
||||||
|
value = Value{}
|
||||||
|
}
|
||||||
|
return value, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the value of the top-level binding of the given name.
|
// Get the value of the top-level binding of the given name.
|
||||||
|
@ -296,15 +301,18 @@ func (self Otto) Run(src interface{}) (Value, error) {
|
||||||
// If there is an error (like the binding does not exist), then the value
|
// If there is an error (like the binding does not exist), then the value
|
||||||
// will be undefined.
|
// will be undefined.
|
||||||
func (self Otto) Get(name string) (Value, error) {
|
func (self Otto) Get(name string) (Value, error) {
|
||||||
value := UndefinedValue()
|
value := Value{}
|
||||||
err := catchPanic(func() {
|
err := catchPanic(func() {
|
||||||
value = self.getValue(name)
|
value = self.getValue(name)
|
||||||
})
|
})
|
||||||
|
if !value.safe() {
|
||||||
|
value = Value{}
|
||||||
|
}
|
||||||
return value, err
|
return value, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self Otto) getValue(name string) Value {
|
func (self Otto) getValue(name string) Value {
|
||||||
return self.runtime.GlobalEnvironment.GetValue(name, false)
|
return self.runtime.globalStash.getBinding(name, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the top-level binding of the given name to the given value.
|
// Set the top-level binding of the given name to the given value.
|
||||||
|
@ -330,7 +338,7 @@ func (self Otto) Set(name string, value interface{}) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self Otto) setValue(name string, value Value) {
|
func (self Otto) setValue(name string, value Value) {
|
||||||
self.runtime.GlobalEnvironment.SetValue(name, value, false)
|
self.runtime.globalStash.setValue(name, value, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call the given JavaScript with a given this and arguments.
|
// Call the given JavaScript with a given this and arguments.
|
||||||
|
@ -355,7 +363,7 @@ func (self Otto) setValue(name string, value Value) {
|
||||||
//
|
//
|
||||||
func (self Otto) Call(source string, this interface{}, argumentList ...interface{}) (Value, error) {
|
func (self Otto) Call(source string, this interface{}, argumentList ...interface{}) (Value, error) {
|
||||||
|
|
||||||
thisValue := UndefinedValue()
|
thisValue := Value{}
|
||||||
|
|
||||||
construct := false
|
construct := false
|
||||||
if strings.HasPrefix(source, "new ") {
|
if strings.HasPrefix(source, "new ") {
|
||||||
|
@ -363,6 +371,12 @@ func (self Otto) Call(source string, this interface{}, argumentList ...interface
|
||||||
construct = true
|
construct = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME enterGlobalScope
|
||||||
|
self.runtime.enterGlobalScope()
|
||||||
|
defer func() {
|
||||||
|
self.runtime.leaveScope()
|
||||||
|
}()
|
||||||
|
|
||||||
if !construct && this == nil {
|
if !construct && this == nil {
|
||||||
program, err := self.runtime.cmpl_parse("", source+"()")
|
program, err := self.runtime.cmpl_parse("", source+"()")
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
@ -373,7 +387,7 @@ func (self Otto) Call(source string, this interface{}, argumentList ...interface
|
||||||
value = self.runtime.cmpl_evaluate_nodeCallExpression(node, argumentList)
|
value = self.runtime.cmpl_evaluate_nodeCallExpression(node, argumentList)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return UndefinedValue(), err
|
return Value{}, err
|
||||||
}
|
}
|
||||||
return value, nil
|
return value, nil
|
||||||
}
|
}
|
||||||
|
@ -382,7 +396,7 @@ func (self Otto) Call(source string, this interface{}, argumentList ...interface
|
||||||
} else {
|
} else {
|
||||||
value, err := self.ToValue(this)
|
value, err := self.ToValue(this)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return UndefinedValue(), err
|
return Value{}, err
|
||||||
}
|
}
|
||||||
thisValue = value
|
thisValue = value
|
||||||
}
|
}
|
||||||
|
@ -392,20 +406,20 @@ func (self Otto) Call(source string, this interface{}, argumentList ...interface
|
||||||
|
|
||||||
fn, err := self.Run(source)
|
fn, err := self.Run(source)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return UndefinedValue(), err
|
return Value{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if construct {
|
if construct {
|
||||||
result, err := fn.constructSafe(this, argumentList...)
|
result, err := fn.constructSafe(self.runtime, this, argumentList...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return UndefinedValue(), err
|
return Value{}, err
|
||||||
}
|
}
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
result, err := fn.Call(this, argumentList...)
|
result, err := fn.Call(this, argumentList...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return UndefinedValue(), err
|
return Value{}, err
|
||||||
}
|
}
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
@ -441,23 +455,23 @@ func (self Otto) Object(source string) (*Object, error) {
|
||||||
|
|
||||||
// ToValue will convert an interface{} value to a value digestible by otto/JavaScript.
|
// ToValue will convert an interface{} value to a value digestible by otto/JavaScript.
|
||||||
func (self Otto) ToValue(value interface{}) (Value, error) {
|
func (self Otto) ToValue(value interface{}) (Value, error) {
|
||||||
return self.runtime.ToValue(value)
|
return self.runtime.safeToValue(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy will create a copy/clone of the runtime.
|
// Copy will create a copy/clone of the runtime.
|
||||||
//
|
//
|
||||||
// Copy is useful for saving some processing time when creating many similar
|
// Copy is useful for saving some time when creating many similar runtimes.
|
||||||
// runtimes.
|
|
||||||
//
|
//
|
||||||
// This implementation is alpha-ish, and works by introspecting every part of the runtime
|
// This method works by walking the original runtime and cloning each object, scope, stash,
|
||||||
// and reallocating and then relinking everything back together. Please report if you
|
// etc. into a new runtime.
|
||||||
// notice any inadvertent sharing of data between copies.
|
//
|
||||||
func (self *Otto) Copy() *Otto {
|
// Be on the lookout for memory leaks or inadvertent sharing of resources.
|
||||||
otto := &Otto{
|
func (in *Otto) Copy() *Otto {
|
||||||
runtime: self.runtime.clone(),
|
out := &Otto{
|
||||||
|
runtime: in.runtime.clone(),
|
||||||
}
|
}
|
||||||
otto.runtime.Otto = otto
|
out.runtime.otto = out
|
||||||
return otto
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
// Object{}
|
// Object{}
|
||||||
|
@ -495,7 +509,7 @@ func (self Object) Call(name string, argumentList ...interface{}) (Value, error)
|
||||||
|
|
||||||
function, err := self.Get(name)
|
function, err := self.Get(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return UndefinedValue(), err
|
return Value{}, err
|
||||||
}
|
}
|
||||||
return function.Call(self.Value(), argumentList...)
|
return function.Call(self.Value(), argumentList...)
|
||||||
}
|
}
|
||||||
|
@ -507,10 +521,13 @@ func (self Object) Value() Value {
|
||||||
|
|
||||||
// Get the value of the property with the given name.
|
// Get the value of the property with the given name.
|
||||||
func (self Object) Get(name string) (Value, error) {
|
func (self Object) Get(name string) (Value, error) {
|
||||||
value := UndefinedValue()
|
value := Value{}
|
||||||
err := catchPanic(func() {
|
err := catchPanic(func() {
|
||||||
value = self.object.get(name)
|
value = self.object.get(name)
|
||||||
})
|
})
|
||||||
|
if !value.safe() {
|
||||||
|
value = Value{}
|
||||||
|
}
|
||||||
return value, err
|
return value, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -520,7 +537,7 @@ func (self Object) Get(name string) (Value, error) {
|
||||||
// or there is an error during conversion of the given value.
|
// or there is an error during conversion of the given value.
|
||||||
func (self Object) Set(name string, value interface{}) error {
|
func (self Object) Set(name string, value interface{}) error {
|
||||||
{
|
{
|
||||||
value, err := self.object.runtime.ToValue(value)
|
value, err := self.object.runtime.safeToValue(value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
|
@ -37,7 +37,12 @@ func main() {
|
||||||
return err
|
return err
|
||||||
}()
|
}()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
switch err := err.(type) {
|
||||||
|
case *otto.Error:
|
||||||
|
fmt.Print(err.String())
|
||||||
|
default:
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
os.Exit(64)
|
os.Exit(64)
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -67,13 +67,13 @@ func getValueOfArrayIndex(array []Value, index int) (Value, bool) {
|
||||||
return value, true
|
return value, true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return UndefinedValue(), false
|
return Value{}, false
|
||||||
}
|
}
|
||||||
|
|
||||||
// A range index can be anything from 0 up to length. It is NOT safe to use as an index
|
// A range index can be anything from 0 up to length. It is NOT safe to use as an index
|
||||||
// to an array, but is useful for slicing and in some ECMA algorithms.
|
// to an array, but is useful for slicing and in some ECMA algorithms.
|
||||||
func valueToRangeIndex(indexValue Value, length int64, negativeIsZero bool) int64 {
|
func valueToRangeIndex(indexValue Value, length int64, negativeIsZero bool) int64 {
|
||||||
index := toInteger(indexValue).value
|
index := indexValue.number().int64
|
||||||
if negativeIsZero {
|
if negativeIsZero {
|
||||||
if index < 0 {
|
if index < 0 {
|
||||||
index = 0
|
index = 0
|
||||||
|
@ -129,7 +129,7 @@ func rangeStartLength(source []Value, size int64) (start, length int64) {
|
||||||
lengthValue := valueOfArrayIndex(source, 1)
|
lengthValue := valueOfArrayIndex(source, 1)
|
||||||
if !lengthValue.IsUndefined() {
|
if !lengthValue.IsUndefined() {
|
||||||
// Which it is not, so get the value as an array index
|
// Which it is not, so get the value as an array index
|
||||||
length = toInteger(lengthValue).value
|
length = lengthValue.number().int64
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
|
@ -18,14 +18,14 @@ func TestOttoError(t *testing.T) {
|
||||||
is(err, "TypeError: Nothing happens.")
|
is(err, "TypeError: Nothing happens.")
|
||||||
|
|
||||||
_, err = ToValue([]byte{})
|
_, err = ToValue([]byte{})
|
||||||
is(err, "TypeError: Invalid value (slice): Missing runtime: [] ([]uint8)")
|
is(err, "TypeError: invalid value (slice): missing runtime: [] ([]uint8)")
|
||||||
|
|
||||||
_, err = vm.Run(`
|
_, err = vm.Run(`
|
||||||
(function(){
|
(function(){
|
||||||
return abcdef.length
|
return abcdef.length
|
||||||
})()
|
})()
|
||||||
`)
|
`)
|
||||||
is(err, "ReferenceError: abcdef is not defined")
|
is(err, "ReferenceError: 'abcdef' is not defined")
|
||||||
|
|
||||||
_, err = vm.Run(`
|
_, err = vm.Run(`
|
||||||
function start() {
|
function start() {
|
||||||
|
@ -35,14 +35,14 @@ func TestOttoError(t *testing.T) {
|
||||||
|
|
||||||
xyzzy()
|
xyzzy()
|
||||||
`)
|
`)
|
||||||
is(err, "ReferenceError: xyzzy is not defined")
|
is(err, "ReferenceError: 'xyzzy' is not defined")
|
||||||
|
|
||||||
_, err = vm.Run(`
|
_, err = vm.Run(`
|
||||||
// Just a comment
|
// Just a comment
|
||||||
|
|
||||||
xyzzy
|
xyzzy
|
||||||
`)
|
`)
|
||||||
is(err, "ReferenceError: xyzzy is not defined")
|
is(err, "ReferenceError: 'xyzzy' is not defined")
|
||||||
|
|
||||||
})
|
})
|
||||||
}
|
}
|
|
@ -649,7 +649,7 @@ func Test_PrimitiveValueObjectValue(t *testing.T) {
|
||||||
test, _ := test()
|
test, _ := test()
|
||||||
|
|
||||||
Number11 := test(`new Number(11)`)
|
Number11 := test(`new Number(11)`)
|
||||||
is(toFloat(Number11), 11)
|
is(Number11.float64(), 11)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -700,24 +700,22 @@ func Test_evalDirectIndirect(t *testing.T) {
|
||||||
tt(t, func() {
|
tt(t, func() {
|
||||||
test, _ := test()
|
test, _ := test()
|
||||||
|
|
||||||
|
// (function () {return this;}()).abc = "global";
|
||||||
test(`
|
test(`
|
||||||
var abc = "global";
|
var abc = "global";
|
||||||
(function(){
|
(function(){
|
||||||
try {
|
try {
|
||||||
var _eval = eval;
|
var _eval = eval;
|
||||||
var abc = "function";
|
var abc = "function";
|
||||||
if (
|
return [
|
||||||
_eval("\'global\' === abc") === true && // eval (Indirect)
|
_eval("\'global\' === abc"), // eval (Indirect)
|
||||||
eval("\'function\' === abc") === true // eval (Direct)
|
eval("\'function\' === abc"), // eval (Direct)
|
||||||
) {
|
];
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
} finally {
|
} finally {
|
||||||
delete this.abc;
|
delete this.abc;
|
||||||
}
|
}
|
||||||
})()
|
})();
|
||||||
`, true)
|
`, "true,true")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -766,7 +764,7 @@ func TestShouldError(t *testing.T) {
|
||||||
test(`raise:
|
test(`raise:
|
||||||
xyzzy
|
xyzzy
|
||||||
throw new TypeError("Nothing happens.")
|
throw new TypeError("Nothing happens.")
|
||||||
`, "ReferenceError: xyzzy is not defined")
|
`, "ReferenceError: 'xyzzy' is not defined")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -868,8 +866,8 @@ func TestDotMember(t *testing.T) {
|
||||||
func Test_stringToFloat(t *testing.T) {
|
func Test_stringToFloat(t *testing.T) {
|
||||||
tt(t, func() {
|
tt(t, func() {
|
||||||
|
|
||||||
is(stringToFloat("10e10000"), _Infinity)
|
is(parseNumber("10e10000"), _Infinity)
|
||||||
is(stringToFloat("10e10_."), _NaN)
|
is(parseNumber("10e10_."), _NaN)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1014,7 +1012,9 @@ func TestOttoCall_throw(t *testing.T) {
|
||||||
// Looks like this has been broken for a while... what
|
// Looks like this has been broken for a while... what
|
||||||
// behavior do we want here?
|
// behavior do we want here?
|
||||||
|
|
||||||
return
|
if true {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
tt(t, func() {
|
tt(t, func() {
|
||||||
test, vm := test()
|
test, vm := test()
|
||||||
|
@ -1024,7 +1024,7 @@ func TestOttoCall_throw(t *testing.T) {
|
||||||
call.Otto.Call(`throw eval`, nil, "({ def: 3.14159 })")
|
call.Otto.Call(`throw eval`, nil, "({ def: 3.14159 })")
|
||||||
}
|
}
|
||||||
call.Otto.Call(`throw Error`, nil, "abcdef")
|
call.Otto.Call(`throw Error`, nil, "abcdef")
|
||||||
return UndefinedValue()
|
return Value{}
|
||||||
})
|
})
|
||||||
// TODO try { abc(); } catch (err) { error = err }
|
// TODO try { abc(); } catch (err) { error = err }
|
||||||
// Possible unrelated error case:
|
// Possible unrelated error case:
|
||||||
|
@ -1099,6 +1099,54 @@ func TestOttoCopy(t *testing.T) {
|
||||||
`)
|
`)
|
||||||
is(err, nil)
|
is(err, nil)
|
||||||
is(value, "Xyzzy0[object Object]")
|
is(value, "Xyzzy0[object Object]")
|
||||||
|
|
||||||
|
{
|
||||||
|
vm0 := New()
|
||||||
|
vm0.Run(`
|
||||||
|
var global = (function () {return this;}())
|
||||||
|
var abc = 0;
|
||||||
|
var vm = "vm0";
|
||||||
|
|
||||||
|
var def = (function(){
|
||||||
|
var jkl = 0;
|
||||||
|
var abc = function() {
|
||||||
|
global.abc += 1;
|
||||||
|
jkl += 1;
|
||||||
|
return 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
return function() {
|
||||||
|
return [ vm, global.abc, jkl, abc() ];
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
`)
|
||||||
|
|
||||||
|
value, err := vm0.Run(`
|
||||||
|
def();
|
||||||
|
`)
|
||||||
|
is(err, nil)
|
||||||
|
is(value, "vm0,0,0,1")
|
||||||
|
|
||||||
|
vm1 := vm0.Copy()
|
||||||
|
vm1.Set("vm", "vm1")
|
||||||
|
value, err = vm1.Run(`
|
||||||
|
def();
|
||||||
|
`)
|
||||||
|
is(err, nil)
|
||||||
|
is(value, "vm1,1,1,1")
|
||||||
|
|
||||||
|
value, err = vm0.Run(`
|
||||||
|
def();
|
||||||
|
`)
|
||||||
|
is(err, nil)
|
||||||
|
is(value, "vm0,1,1,1")
|
||||||
|
|
||||||
|
value, err = vm1.Run(`
|
||||||
|
def();
|
||||||
|
`)
|
||||||
|
is(err, nil)
|
||||||
|
is(value, "vm1,2,2,1")
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1109,11 +1157,11 @@ func TestOttoCall_clone(t *testing.T) {
|
||||||
|
|
||||||
{
|
{
|
||||||
// FIXME terst, Check how this comparison is done
|
// FIXME terst, Check how this comparison is done
|
||||||
is(rt.Global.Array.prototype, rt.Global.FunctionPrototype)
|
is(rt.global.Array.prototype, rt.global.FunctionPrototype)
|
||||||
is(rt.Global.ArrayPrototype, "!=", nil)
|
is(rt.global.ArrayPrototype, "!=", nil)
|
||||||
is(rt.Global.Array.runtime, rt)
|
is(rt.global.Array.runtime, rt)
|
||||||
is(rt.Global.Array.prototype.runtime, rt)
|
is(rt.global.Array.prototype.runtime, rt)
|
||||||
is(rt.Global.Array.get("prototype")._object().runtime, rt)
|
is(rt.global.Array.get("prototype")._object().runtime, rt)
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -1128,14 +1176,14 @@ func TestOttoCall_clone(t *testing.T) {
|
||||||
is(value, "1,2,3")
|
is(value, "1,2,3")
|
||||||
object := value._object()
|
object := value._object()
|
||||||
is(object, "!=", nil)
|
is(object, "!=", nil)
|
||||||
is(object.prototype, rt.Global.ArrayPrototype)
|
is(object.prototype, rt.global.ArrayPrototype)
|
||||||
|
|
||||||
value, err = vm.Run(`Array.prototype`)
|
value, err = vm.Run(`Array.prototype`)
|
||||||
is(err, nil)
|
is(err, nil)
|
||||||
object = value._object()
|
object = value._object()
|
||||||
is(object.runtime, rt)
|
is(object.runtime, rt)
|
||||||
is(object, "!=", nil)
|
is(object, "!=", nil)
|
||||||
is(object, rt.Global.ArrayPrototype)
|
is(object, rt.global.ArrayPrototype)
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
|
@ -85,10 +85,10 @@ func (self _property) get(this *_object) Value {
|
||||||
return value
|
return value
|
||||||
case _propertyGetSet:
|
case _propertyGetSet:
|
||||||
if value[0] != nil {
|
if value[0] != nil {
|
||||||
return value[0].callGet(toValue(this))
|
return value[0].call(toValue(this), nil, false, nativeFrame)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return UndefinedValue()
|
return Value{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self _property) isAccessorDescriptor() bool {
|
func (self _property) isAccessorDescriptor() bool {
|
||||||
|
@ -115,16 +115,16 @@ func (self _property) isEmpty() bool {
|
||||||
// _enumerableValue, _enumerableTrue, _enumerableFalse?
|
// _enumerableValue, _enumerableTrue, _enumerableFalse?
|
||||||
// .enumerableValue() .enumerableExists()
|
// .enumerableValue() .enumerableExists()
|
||||||
|
|
||||||
func toPropertyDescriptor(value Value) (descriptor _property) {
|
func toPropertyDescriptor(rt *_runtime, value Value) (descriptor _property) {
|
||||||
objectDescriptor := value._object()
|
objectDescriptor := value._object()
|
||||||
if objectDescriptor == nil {
|
if objectDescriptor == nil {
|
||||||
panic(newTypeError())
|
panic(rt.panicTypeError())
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
descriptor.mode = modeSetMask // Initially nothing is set
|
descriptor.mode = modeSetMask // Initially nothing is set
|
||||||
if objectDescriptor.hasProperty("enumerable") {
|
if objectDescriptor.hasProperty("enumerable") {
|
||||||
if objectDescriptor.get("enumerable").toBoolean() {
|
if objectDescriptor.get("enumerable").bool() {
|
||||||
descriptor.enumerateOn()
|
descriptor.enumerateOn()
|
||||||
} else {
|
} else {
|
||||||
descriptor.enumerateOff()
|
descriptor.enumerateOff()
|
||||||
|
@ -132,7 +132,7 @@ func toPropertyDescriptor(value Value) (descriptor _property) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if objectDescriptor.hasProperty("configurable") {
|
if objectDescriptor.hasProperty("configurable") {
|
||||||
if objectDescriptor.get("configurable").toBoolean() {
|
if objectDescriptor.get("configurable").bool() {
|
||||||
descriptor.configureOn()
|
descriptor.configureOn()
|
||||||
} else {
|
} else {
|
||||||
descriptor.configureOff()
|
descriptor.configureOff()
|
||||||
|
@ -140,7 +140,7 @@ func toPropertyDescriptor(value Value) (descriptor _property) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if objectDescriptor.hasProperty("writable") {
|
if objectDescriptor.hasProperty("writable") {
|
||||||
if objectDescriptor.get("writable").toBoolean() {
|
if objectDescriptor.get("writable").bool() {
|
||||||
descriptor.writeOn()
|
descriptor.writeOn()
|
||||||
} else {
|
} else {
|
||||||
descriptor.writeOff()
|
descriptor.writeOff()
|
||||||
|
@ -155,7 +155,7 @@ func toPropertyDescriptor(value Value) (descriptor _property) {
|
||||||
value := objectDescriptor.get("get")
|
value := objectDescriptor.get("get")
|
||||||
if value.IsDefined() {
|
if value.IsDefined() {
|
||||||
if !value.isCallable() {
|
if !value.isCallable() {
|
||||||
panic(newTypeError())
|
panic(rt.panicTypeError())
|
||||||
}
|
}
|
||||||
getter = value._object()
|
getter = value._object()
|
||||||
getterSetter = true
|
getterSetter = true
|
||||||
|
@ -169,7 +169,7 @@ func toPropertyDescriptor(value Value) (descriptor _property) {
|
||||||
value := objectDescriptor.get("set")
|
value := objectDescriptor.get("set")
|
||||||
if value.IsDefined() {
|
if value.IsDefined() {
|
||||||
if !value.isCallable() {
|
if !value.isCallable() {
|
||||||
panic(newTypeError())
|
panic(rt.panicTypeError())
|
||||||
}
|
}
|
||||||
setter = value._object()
|
setter = value._object()
|
||||||
getterSetter = true
|
getterSetter = true
|
||||||
|
@ -181,14 +181,14 @@ func toPropertyDescriptor(value Value) (descriptor _property) {
|
||||||
|
|
||||||
if getterSetter {
|
if getterSetter {
|
||||||
if descriptor.writeSet() {
|
if descriptor.writeSet() {
|
||||||
panic(newTypeError())
|
panic(rt.panicTypeError())
|
||||||
}
|
}
|
||||||
descriptor.value = _propertyGetSet{getter, setter}
|
descriptor.value = _propertyGetSet{getter, setter}
|
||||||
}
|
}
|
||||||
|
|
||||||
if objectDescriptor.hasProperty("value") {
|
if objectDescriptor.hasProperty("value") {
|
||||||
if getterSetter {
|
if getterSetter {
|
||||||
panic(newTypeError())
|
panic(rt.panicTypeError())
|
||||||
}
|
}
|
||||||
descriptor.value = objectDescriptor.get("value")
|
descriptor.value = objectDescriptor.get("value")
|
||||||
}
|
}
|
||||||
|
@ -203,11 +203,11 @@ func (self *_runtime) fromPropertyDescriptor(descriptor _property) *_object {
|
||||||
object.defineProperty("writable", toValue_bool(descriptor.writable()), 0111, false)
|
object.defineProperty("writable", toValue_bool(descriptor.writable()), 0111, false)
|
||||||
} else if descriptor.isAccessorDescriptor() {
|
} else if descriptor.isAccessorDescriptor() {
|
||||||
getSet := descriptor.value.(_propertyGetSet)
|
getSet := descriptor.value.(_propertyGetSet)
|
||||||
get := UndefinedValue()
|
get := Value{}
|
||||||
if getSet[0] != nil {
|
if getSet[0] != nil {
|
||||||
get = toValue_object(getSet[0])
|
get = toValue_object(getSet[0])
|
||||||
}
|
}
|
||||||
set := UndefinedValue()
|
set := Value{}
|
||||||
if getSet[1] != nil {
|
if getSet[1] != nil {
|
||||||
set = toValue_object(getSet[1])
|
set = toValue_object(getSet[1])
|
||||||
}
|
}
|
|
@ -6,43 +6,63 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
type testStruct struct {
|
type _abcStruct struct {
|
||||||
Abc bool
|
Abc bool
|
||||||
Def int
|
Def int
|
||||||
Ghi string
|
Ghi string
|
||||||
Jkl interface{}
|
Jkl interface{}
|
||||||
|
Mno _mnoStruct
|
||||||
|
Pqr map[string]int8
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *testStruct) FuncPointerReciever() string {
|
func (abc _abcStruct) String() string {
|
||||||
|
return abc.Ghi
|
||||||
|
}
|
||||||
|
|
||||||
|
func (abc *_abcStruct) FuncPointer() string {
|
||||||
return "abc"
|
return "abc"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t testStruct) FuncNoArgsNoRet() {
|
func (abc _abcStruct) Func() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t testStruct) FuncNoArgs() string {
|
func (abc _abcStruct) FuncReturn1() string {
|
||||||
return "abc"
|
return "abc"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t testStruct) FuncNoArgsMultRet() (string, error) {
|
func (abc _abcStruct) FuncReturn2() (string, error) {
|
||||||
return "def", nil
|
return "def", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t testStruct) FuncOneArgs(a string) string {
|
func (abc _abcStruct) Func1Return1(a string) string {
|
||||||
return a
|
return a
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t testStruct) FuncMultArgs(a, b string) string {
|
func (abc _abcStruct) Func2Return1(x, y string) string {
|
||||||
return a + b
|
return x + y
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t testStruct) FuncVarArgs(as ...string) int {
|
func (abc _abcStruct) FuncEllipsis(xyz ...string) int {
|
||||||
return len(as)
|
return len(xyz)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (abc _abcStruct) FuncReturnStruct() _mnoStruct {
|
||||||
|
return _mnoStruct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type _mnoStruct struct {
|
||||||
|
Ghi string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mno _mnoStruct) Func() string {
|
||||||
|
return "mno"
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestReflect(t *testing.T) {
|
func TestReflect(t *testing.T) {
|
||||||
return
|
if true {
|
||||||
|
return
|
||||||
|
}
|
||||||
tt(t, func() {
|
tt(t, func() {
|
||||||
// Testing dbgf
|
// Testing dbgf
|
||||||
// These should panic
|
// These should panic
|
||||||
|
@ -55,15 +75,11 @@ func Test_reflectStruct(t *testing.T) {
|
||||||
tt(t, func() {
|
tt(t, func() {
|
||||||
test, vm := test()
|
test, vm := test()
|
||||||
|
|
||||||
// testStruct
|
// _abcStruct
|
||||||
{
|
{
|
||||||
abc := &testStruct{}
|
abc := &_abcStruct{}
|
||||||
vm.Set("abc", abc)
|
vm.Set("abc", abc)
|
||||||
|
|
||||||
test(`
|
|
||||||
abc.FuncPointerReciever();
|
|
||||||
`, "abc")
|
|
||||||
|
|
||||||
test(`
|
test(`
|
||||||
[ abc.Abc, abc.Ghi ];
|
[ abc.Abc, abc.Ghi ];
|
||||||
`, "false,")
|
`, "false,")
|
||||||
|
@ -75,7 +91,7 @@ func Test_reflectStruct(t *testing.T) {
|
||||||
[ abc.Abc, abc.Ghi ];
|
[ abc.Abc, abc.Ghi ];
|
||||||
`, "true,Nothing happens.")
|
`, "true,Nothing happens.")
|
||||||
|
|
||||||
*abc = testStruct{}
|
*abc = _abcStruct{}
|
||||||
|
|
||||||
test(`
|
test(`
|
||||||
[ abc.Abc, abc.Ghi ];
|
[ abc.Abc, abc.Ghi ];
|
||||||
|
@ -109,24 +125,40 @@ func Test_reflectStruct(t *testing.T) {
|
||||||
is(abc.Def, 451)
|
is(abc.Def, 451)
|
||||||
|
|
||||||
test(`
|
test(`
|
||||||
abc.FuncNoArgsNoRet();
|
abc.FuncPointer();
|
||||||
|
`, "abc")
|
||||||
|
|
||||||
|
test(`
|
||||||
|
abc.Func();
|
||||||
`, "undefined")
|
`, "undefined")
|
||||||
|
|
||||||
test(`
|
test(`
|
||||||
abc.FuncNoArgs();
|
abc.FuncReturn1();
|
||||||
`, "abc")
|
`, "abc")
|
||||||
|
|
||||||
test(`
|
test(`
|
||||||
abc.FuncOneArgs("abc");
|
abc.Func1Return1("abc");
|
||||||
`, "abc")
|
`, "abc")
|
||||||
|
|
||||||
test(`
|
test(`
|
||||||
abc.FuncMultArgs("abc", "def");
|
abc.Func2Return1("abc", "def");
|
||||||
`, "abcdef")
|
`, "abcdef")
|
||||||
|
|
||||||
test(`
|
test(`
|
||||||
abc.FuncVarArgs("abc", "def", "ghi");
|
abc.FuncEllipsis("abc", "def", "ghi");
|
||||||
`, 3)
|
`, 3)
|
||||||
|
|
||||||
test(`raise:
|
test(`raise:
|
||||||
abc.FuncNoArgsMultRet();
|
abc.FuncReturn2();
|
||||||
`, "TypeError")
|
`, "TypeError")
|
||||||
|
|
||||||
|
test(`
|
||||||
|
abc.FuncReturnStruct();
|
||||||
|
`, "[object Object]")
|
||||||
|
|
||||||
|
test(`
|
||||||
|
abc.FuncReturnStruct().Func();
|
||||||
|
`, "mno")
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -387,7 +419,7 @@ func Test_reflectMapInterface(t *testing.T) {
|
||||||
"jkl": "jkl",
|
"jkl": "jkl",
|
||||||
}
|
}
|
||||||
vm.Set("abc", abc)
|
vm.Set("abc", abc)
|
||||||
vm.Set("mno", &testStruct{})
|
vm.Set("mno", &_abcStruct{})
|
||||||
|
|
||||||
test(`
|
test(`
|
||||||
abc.xyz = "pqr";
|
abc.xyz = "pqr";
|
||||||
|
@ -402,10 +434,50 @@ func Test_reflectMapInterface(t *testing.T) {
|
||||||
is(abc["xyz"], "pqr")
|
is(abc["xyz"], "pqr")
|
||||||
is(abc["ghi"], "[object Object]")
|
is(abc["ghi"], "[object Object]")
|
||||||
is(abc["jkl"], float64(3.14159))
|
is(abc["jkl"], float64(3.14159))
|
||||||
mno, valid := abc["mno"].(*testStruct)
|
mno, valid := abc["mno"].(*_abcStruct)
|
||||||
is(valid, true)
|
is(valid, true)
|
||||||
is(mno.Abc, true)
|
is(mno.Abc, true)
|
||||||
is(mno.Ghi, "Something happens.")
|
is(mno.Ghi, "Something happens.")
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPassthrough(t *testing.T) {
|
||||||
|
tt(t, func() {
|
||||||
|
test, vm := test()
|
||||||
|
|
||||||
|
{
|
||||||
|
abc := &_abcStruct{
|
||||||
|
Mno: _mnoStruct{
|
||||||
|
Ghi: "<Mno.Ghi>",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
vm.Set("abc", abc)
|
||||||
|
|
||||||
|
test(`
|
||||||
|
abc.Mno.Ghi;
|
||||||
|
`, "<Mno.Ghi>")
|
||||||
|
|
||||||
|
vm.Set("pqr", map[string]int8{
|
||||||
|
"xyzzy": 0,
|
||||||
|
"Nothing happens.": 1,
|
||||||
|
})
|
||||||
|
|
||||||
|
test(`
|
||||||
|
abc.Ghi = "abc";
|
||||||
|
abc.Pqr = pqr;
|
||||||
|
abc.Pqr["Nothing happens."];
|
||||||
|
`, 1)
|
||||||
|
|
||||||
|
mno := _mnoStruct{
|
||||||
|
Ghi: "<mno.Ghi>",
|
||||||
|
}
|
||||||
|
vm.Set("mno", mno)
|
||||||
|
|
||||||
|
test(`
|
||||||
|
abc.Mno = mno;
|
||||||
|
abc.Mno.Ghi;
|
||||||
|
`, "<mno.Ghi>")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
|
@ -160,7 +160,10 @@ func TestRegExp_toString(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRegExp_zaacbbbcac(t *testing.T) {
|
func TestRegExp_zaacbbbcac(t *testing.T) {
|
||||||
return
|
if true {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
tt(t, func() {
|
tt(t, func() {
|
||||||
test, _ := test()
|
test, _ := test()
|
||||||
|
|
|
@ -22,9 +22,9 @@ func newReturnResult(value Value) _result {
|
||||||
}
|
}
|
||||||
|
|
||||||
func newContinueResult(target string) _result {
|
func newContinueResult(target string) _result {
|
||||||
return _result{resultContinue, emptyValue(), target}
|
return _result{resultContinue, emptyValue, target}
|
||||||
}
|
}
|
||||||
|
|
||||||
func newBreakResult(target string) _result {
|
func newBreakResult(target string) _result {
|
||||||
return _result{resultBreak, emptyValue(), target}
|
return _result{resultBreak, emptyValue, target}
|
||||||
}
|
}
|
|
@ -3,6 +3,7 @@ package otto
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/robertkrimen/otto/ast"
|
"github.com/robertkrimen/otto/ast"
|
||||||
"github.com/robertkrimen/otto/parser"
|
"github.com/robertkrimen/otto/parser"
|
||||||
|
@ -45,100 +46,54 @@ type _global struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type _runtime struct {
|
type _runtime struct {
|
||||||
Stack [](*_executionContext)
|
global _global
|
||||||
|
globalObject *_object
|
||||||
GlobalObject *_object
|
globalStash *_objectStash
|
||||||
GlobalEnvironment *_objectEnvironment
|
scope *_scope
|
||||||
|
otto *Otto
|
||||||
Global _global
|
eval *_object // The builtin eval, for determine indirect versus direct invocation
|
||||||
|
|
||||||
eval *_object // The builtin eval, for determine indirect versus direct invocation
|
|
||||||
|
|
||||||
Otto *Otto
|
|
||||||
|
|
||||||
labels []string // FIXME
|
labels []string // FIXME
|
||||||
|
lck sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *_runtime) EnterGlobalExecutionContext() {
|
func (self *_runtime) enterScope(scope *_scope) {
|
||||||
self.EnterExecutionContext(newExecutionContext(self.GlobalEnvironment, self.GlobalEnvironment, self.GlobalObject))
|
scope.outer = self.scope
|
||||||
|
self.scope = scope
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *_runtime) EnterExecutionContext(scope *_executionContext) {
|
func (self *_runtime) leaveScope() {
|
||||||
self.Stack = append(self.Stack, scope)
|
self.scope = self.scope.outer
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *_runtime) LeaveExecutionContext() {
|
// FIXME This is used in two places (cloning)
|
||||||
self.Stack = self.Stack[:len(self.Stack)-1]
|
func (self *_runtime) enterGlobalScope() {
|
||||||
|
self.enterScope(newScope(self.globalStash, self.globalStash, self.globalObject))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *_runtime) _executionContext(depth int) *_executionContext {
|
func (self *_runtime) enterFunctionScope(outer _stash, this Value) *_fnStash {
|
||||||
if depth == 0 {
|
if outer == nil {
|
||||||
return self.Stack[len(self.Stack)-1]
|
outer = self.globalStash
|
||||||
}
|
}
|
||||||
if len(self.Stack)-1+depth >= 0 {
|
stash := self.newFunctionStash(outer)
|
||||||
return self.Stack[len(self.Stack)-1+depth]
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_runtime) EnterFunctionExecutionContext(function *_object, this Value) *_functionEnvironment {
|
|
||||||
scopeEnvironment := function.functionValue().call.ScopeEnvironment()
|
|
||||||
if scopeEnvironment == nil {
|
|
||||||
scopeEnvironment = self.GlobalEnvironment
|
|
||||||
}
|
|
||||||
environment := self.newFunctionEnvironment(scopeEnvironment)
|
|
||||||
var thisObject *_object
|
var thisObject *_object
|
||||||
switch this._valueType {
|
switch this.kind {
|
||||||
case valueUndefined, valueNull:
|
case valueUndefined, valueNull:
|
||||||
thisObject = self.GlobalObject
|
thisObject = self.globalObject
|
||||||
default:
|
default:
|
||||||
thisObject = self.toObject(this)
|
thisObject = self.toObject(this)
|
||||||
}
|
}
|
||||||
self.EnterExecutionContext(newExecutionContext(environment, environment, thisObject))
|
self.enterScope(newScope(stash, stash, thisObject))
|
||||||
return environment
|
return stash
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *_runtime) EnterEvalExecutionContext(call FunctionCall) {
|
func (self *_runtime) putValue(reference _reference, value Value) {
|
||||||
// Skip the current function lexical/variable environment, which is of the function execution context call
|
name := reference.putValue(value)
|
||||||
// to eval (the global execution context). Instead, execute in the context of where the eval was called,
|
if name != "" {
|
||||||
// which is essentially dynamic scoping
|
// Why? -- If reference.base == nil
|
||||||
parent := self._executionContext(-1)
|
// strict = false
|
||||||
new := newExecutionContext(parent.LexicalEnvironment, parent.VariableEnvironment, parent.this)
|
self.globalObject.defineProperty(name, value, 0111, false)
|
||||||
// FIXME Make passing through of self.GlobalObject more general? Whenever newExecutionContext is passed a nil object?
|
|
||||||
new.eval = true
|
|
||||||
self.EnterExecutionContext(new)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_runtime) GetValue(value Value) Value {
|
|
||||||
if value.isReference() {
|
|
||||||
return value.reference().GetValue()
|
|
||||||
}
|
}
|
||||||
return value
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_runtime) PutValue(reference _reference, value Value) {
|
|
||||||
if !reference.PutValue(value) {
|
|
||||||
// Why? -- If reference.Base == nil
|
|
||||||
strict := false
|
|
||||||
self.GlobalObject.defineProperty(reference.GetName(), value, 0111, strict)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_runtime) Call(function *_object, this Value, argumentList []Value, evalHint bool) Value {
|
|
||||||
// Pass eval boolean through to EnterFunctionExecutionContext for further testing
|
|
||||||
_functionEnvironment := self.EnterFunctionExecutionContext(function, this)
|
|
||||||
defer func() {
|
|
||||||
self.LeaveExecutionContext()
|
|
||||||
}()
|
|
||||||
|
|
||||||
if evalHint {
|
|
||||||
evalHint = function == self.eval // If evalHint is true, then it IS a direct eval
|
|
||||||
}
|
|
||||||
callValue := function.functionValue().call.Dispatch(function, _functionEnvironment, self, this, argumentList, evalHint)
|
|
||||||
if value, valid := callValue.value.(_result); valid {
|
|
||||||
return value.value
|
|
||||||
}
|
|
||||||
return callValue
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *_runtime) tryCatchEvaluate(inner func() Value) (tryValue Value, exception bool) {
|
func (self *_runtime) tryCatchEvaluate(inner func() Value) (tryValue Value, exception bool) {
|
||||||
|
@ -155,10 +110,7 @@ func (self *_runtime) tryCatchEvaluate(inner func() Value) (tryValue Value, exce
|
||||||
switch caught := caught.(type) {
|
switch caught := caught.(type) {
|
||||||
case _error:
|
case _error:
|
||||||
exception = true
|
exception = true
|
||||||
tryValue = toValue_object(self.newError(caught.Name, caught.MessageValue()))
|
tryValue = toValue_object(self.newError(caught.name, caught.messageValue()))
|
||||||
//case *_syntaxError:
|
|
||||||
// exception = true
|
|
||||||
// tryValue = toValue_object(self.newError("SyntaxError", toValue_string(caught.Message)))
|
|
||||||
case Value:
|
case Value:
|
||||||
exception = true
|
exception = true
|
||||||
tryValue = caught
|
tryValue = caught
|
||||||
|
@ -172,30 +124,12 @@ func (self *_runtime) tryCatchEvaluate(inner func() Value) (tryValue Value, exce
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// _executionContext Proxy
|
|
||||||
|
|
||||||
func (self *_runtime) localGet(name string) Value {
|
|
||||||
return self._executionContext(0).getValue(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_runtime) localSet(name string, value Value) {
|
|
||||||
self._executionContext(0).setValue(name, value, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_runtime) VariableEnvironment() _environment {
|
|
||||||
return self._executionContext(0).VariableEnvironment
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *_runtime) LexicalEnvironment() _environment {
|
|
||||||
return self._executionContext(0).LexicalEnvironment
|
|
||||||
}
|
|
||||||
|
|
||||||
// toObject
|
// toObject
|
||||||
|
|
||||||
func (self *_runtime) toObject(value Value) *_object {
|
func (self *_runtime) toObject(value Value) *_object {
|
||||||
switch value._valueType {
|
switch value.kind {
|
||||||
case valueEmpty, valueUndefined, valueNull:
|
case valueEmpty, valueUndefined, valueNull:
|
||||||
panic(newTypeError())
|
panic(self.panicTypeError())
|
||||||
case valueBoolean:
|
case valueBoolean:
|
||||||
return self.newBoolean(value)
|
return self.newBoolean(value)
|
||||||
case valueString:
|
case valueString:
|
||||||
|
@ -205,11 +139,11 @@ func (self *_runtime) toObject(value Value) *_object {
|
||||||
case valueObject:
|
case valueObject:
|
||||||
return value._object()
|
return value._object()
|
||||||
}
|
}
|
||||||
panic(newTypeError())
|
panic(self.panicTypeError())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *_runtime) objectCoerce(value Value) (*_object, error) {
|
func (self *_runtime) objectCoerce(value Value) (*_object, error) {
|
||||||
switch value._valueType {
|
switch value.kind {
|
||||||
case valueUndefined:
|
case valueUndefined:
|
||||||
return nil, errors.New("undefined")
|
return nil, errors.New("undefined")
|
||||||
case valueNull:
|
case valueNull:
|
||||||
|
@ -223,20 +157,20 @@ func (self *_runtime) objectCoerce(value Value) (*_object, error) {
|
||||||
case valueObject:
|
case valueObject:
|
||||||
return value._object(), nil
|
return value._object(), nil
|
||||||
}
|
}
|
||||||
panic(newTypeError())
|
panic(self.panicTypeError())
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkObjectCoercible(value Value) {
|
func checkObjectCoercible(rt *_runtime, value Value) {
|
||||||
isObject, mustCoerce := testObjectCoercible(value)
|
isObject, mustCoerce := testObjectCoercible(value)
|
||||||
if !isObject && !mustCoerce {
|
if !isObject && !mustCoerce {
|
||||||
panic(newTypeError())
|
panic(rt.panicTypeError())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// testObjectCoercible
|
// testObjectCoercible
|
||||||
|
|
||||||
func testObjectCoercible(value Value) (isObject bool, mustCoerce bool) {
|
func testObjectCoercible(value Value) (isObject bool, mustCoerce bool) {
|
||||||
switch value._valueType {
|
switch value.kind {
|
||||||
case valueReference, valueEmpty, valueNull, valueUndefined:
|
case valueReference, valueEmpty, valueNull, valueUndefined:
|
||||||
return false, false
|
return false, false
|
||||||
case valueNumber, valueString, valueBoolean:
|
case valueNumber, valueString, valueBoolean:
|
||||||
|
@ -249,8 +183,8 @@ func testObjectCoercible(value Value) (isObject bool, mustCoerce bool) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *_runtime) ToValue(value interface{}) (Value, error) {
|
func (self *_runtime) safeToValue(value interface{}) (Value, error) {
|
||||||
result := UndefinedValue()
|
result := Value{}
|
||||||
err := catchPanic(func() {
|
err := catchPanic(func() {
|
||||||
result = self.toValue(value)
|
result = self.toValue(value)
|
||||||
})
|
})
|
||||||
|
@ -267,7 +201,8 @@ func (self *_runtime) toValue(value interface{}) Value {
|
||||||
return toValue_object(self.newNativeFunction("", value))
|
return toValue_object(self.newNativeFunction("", value))
|
||||||
case Object, *Object, _object, *_object:
|
case Object, *Object, _object, *_object:
|
||||||
// Nothing happens.
|
// Nothing happens.
|
||||||
// FIXME
|
// FIXME We should really figure out what can come here.
|
||||||
|
// This catch-all is ugly.
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
value := reflect.ValueOf(value)
|
value := reflect.ValueOf(value)
|
||||||
|
@ -280,19 +215,21 @@ func (self *_runtime) toValue(value interface{}) Value {
|
||||||
return toValue_object(self.newGoArray(value))
|
return toValue_object(self.newGoArray(value))
|
||||||
}
|
}
|
||||||
case reflect.Func:
|
case reflect.Func:
|
||||||
|
// TODO Maybe cache this?
|
||||||
return toValue_object(self.newNativeFunction("", func(call FunctionCall) Value {
|
return toValue_object(self.newNativeFunction("", func(call FunctionCall) Value {
|
||||||
args := make([]reflect.Value, len(call.ArgumentList))
|
in := make([]reflect.Value, len(call.ArgumentList))
|
||||||
for i, a := range call.ArgumentList {
|
for i, value := range call.ArgumentList {
|
||||||
args[i] = reflect.ValueOf(a.export())
|
in[i] = reflect.ValueOf(value.export())
|
||||||
}
|
}
|
||||||
|
|
||||||
retvals := value.Call(args)
|
out := value.Call(in)
|
||||||
if len(retvals) > 1 {
|
if len(out) == 1 {
|
||||||
panic(newTypeError())
|
return self.toValue(out[0].Interface())
|
||||||
} else if len(retvals) == 1 {
|
} else if len(out) == 0 {
|
||||||
return toValue(retvals[0].Interface())
|
return Value{}
|
||||||
}
|
}
|
||||||
return UndefinedValue()
|
|
||||||
|
panic(call.runtime.panicTypeError())
|
||||||
}))
|
}))
|
||||||
case reflect.Struct:
|
case reflect.Struct:
|
||||||
return toValue_object(self.newGoStructObject(value))
|
return toValue_object(self.newGoStructObject(value))
|
||||||
|
@ -310,13 +247,13 @@ func (self *_runtime) toValue(value interface{}) Value {
|
||||||
|
|
||||||
func (runtime *_runtime) newGoSlice(value reflect.Value) *_object {
|
func (runtime *_runtime) newGoSlice(value reflect.Value) *_object {
|
||||||
self := runtime.newGoSliceObject(value)
|
self := runtime.newGoSliceObject(value)
|
||||||
self.prototype = runtime.Global.ArrayPrototype
|
self.prototype = runtime.global.ArrayPrototype
|
||||||
return self
|
return self
|
||||||
}
|
}
|
||||||
|
|
||||||
func (runtime *_runtime) newGoArray(value reflect.Value) *_object {
|
func (runtime *_runtime) newGoArray(value reflect.Value) *_object {
|
||||||
self := runtime.newGoArrayObject(value)
|
self := runtime.newGoArrayObject(value)
|
||||||
self.prototype = runtime.Global.ArrayPrototype
|
self.prototype = runtime.global.ArrayPrototype
|
||||||
return self
|
return self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -344,7 +281,7 @@ func (self *_runtime) parseSource(src interface{}) (*_nodeProgram, *ast.Program,
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *_runtime) cmpl_run(src interface{}) (Value, error) {
|
func (self *_runtime) cmpl_run(src interface{}) (Value, error) {
|
||||||
result := UndefinedValue()
|
result := Value{}
|
||||||
cmpl_program, program, err := self.parseSource(src)
|
cmpl_program, program, err := self.parseSource(src)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return result, err
|
return result, err
|
||||||
|
@ -353,13 +290,13 @@ func (self *_runtime) cmpl_run(src interface{}) (Value, error) {
|
||||||
cmpl_program = cmpl_parse(program)
|
cmpl_program = cmpl_parse(program)
|
||||||
}
|
}
|
||||||
err = catchPanic(func() {
|
err = catchPanic(func() {
|
||||||
result = self.cmpl_evaluate_nodeProgram(cmpl_program)
|
result = self.cmpl_evaluate_nodeProgram(cmpl_program, false)
|
||||||
})
|
})
|
||||||
switch result._valueType {
|
switch result.kind {
|
||||||
case valueEmpty:
|
case valueEmpty:
|
||||||
result = UndefinedValue()
|
result = Value{}
|
||||||
case valueReference:
|
case valueReference:
|
||||||
result = self.GetValue(result)
|
result = result.resolve()
|
||||||
}
|
}
|
||||||
return result, err
|
return result, err
|
||||||
}
|
}
|
||||||
|
@ -373,12 +310,12 @@ func (self *_runtime) parseThrow(err error) {
|
||||||
{
|
{
|
||||||
err := err[0]
|
err := err[0]
|
||||||
if err.Message == "Invalid left-hand side in assignment" {
|
if err.Message == "Invalid left-hand side in assignment" {
|
||||||
panic(newReferenceError(err.Message))
|
panic(self.panicReferenceError(err.Message))
|
||||||
}
|
}
|
||||||
panic(newSyntaxError(err.Message))
|
panic(self.panicSyntaxError(err.Message))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
panic(newSyntaxError(err.Error()))
|
panic(self.panicSyntaxError(err.Error()))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *_runtime) parseOrThrow(source string) *ast.Program {
|
func (self *_runtime) parseOrThrow(source string) *ast.Program {
|
|
@ -359,7 +359,7 @@ func TestComparison(t *testing.T) {
|
||||||
|
|
||||||
test("1 == 'Hello, World.'", false)
|
test("1 == 'Hello, World.'", false)
|
||||||
|
|
||||||
is(stringToFloat("-1"), -1)
|
is(parseNumber("-1"), -1)
|
||||||
|
|
||||||
test("0+Object", "0function Object() { [native code] }")
|
test("0+Object", "0function Object() { [native code] }")
|
||||||
})
|
})
|
|
@ -0,0 +1,34 @@
|
||||||
|
package otto
|
||||||
|
|
||||||
|
// _scope:
|
||||||
|
// entryFile
|
||||||
|
// entryIdx
|
||||||
|
// top?
|
||||||
|
// outer => nil
|
||||||
|
|
||||||
|
// _stash:
|
||||||
|
// lexical
|
||||||
|
// variable
|
||||||
|
//
|
||||||
|
// _thisStash (ObjectEnvironment)
|
||||||
|
// _fnStash
|
||||||
|
// _dclStash
|
||||||
|
|
||||||
|
// An ECMA-262 ExecutionContext
|
||||||
|
type _scope struct {
|
||||||
|
lexical _stash
|
||||||
|
variable _stash
|
||||||
|
this *_object
|
||||||
|
eval bool // Replace this with kind?
|
||||||
|
outer *_scope
|
||||||
|
|
||||||
|
frame _frame
|
||||||
|
}
|
||||||
|
|
||||||
|
func newScope(lexical _stash, variable _stash, this *_object) *_scope {
|
||||||
|
return &_scope{
|
||||||
|
lexical: lexical,
|
||||||
|
variable: variable,
|
||||||
|
this: this,
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,8 +6,6 @@ import (
|
||||||
|
|
||||||
func TestScript(t *testing.T) {
|
func TestScript(t *testing.T) {
|
||||||
tt(t, func() {
|
tt(t, func() {
|
||||||
return
|
|
||||||
|
|
||||||
vm := New()
|
vm := New()
|
||||||
|
|
||||||
script, err := vm.Compile("xyzzy", `var abc; if (!abc) abc = 0; abc += 2; abc;`)
|
script, err := vm.Compile("xyzzy", `var abc; if (!abc) abc = 0; abc += 2; abc;`)
|
||||||
|
@ -20,6 +18,10 @@ func TestScript(t *testing.T) {
|
||||||
is(err, nil)
|
is(err, nil)
|
||||||
is(value, 2)
|
is(value, 2)
|
||||||
|
|
||||||
|
if true {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
tmp, err := script.marshalBinary()
|
tmp, err := script.marshalBinary()
|
||||||
is(err, nil)
|
is(err, nil)
|
||||||
is(len(tmp), 1228)
|
is(len(tmp), 1228)
|
|
@ -0,0 +1,275 @@
|
||||||
|
package otto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ======
|
||||||
|
// _stash
|
||||||
|
// ======
|
||||||
|
|
||||||
|
type _stash interface {
|
||||||
|
hasBinding(string) bool //
|
||||||
|
createBinding(string, bool, Value) // CreateMutableBinding
|
||||||
|
setBinding(string, Value, bool) // SetMutableBinding
|
||||||
|
getBinding(string, bool) Value // GetBindingValue
|
||||||
|
deleteBinding(string) bool //
|
||||||
|
setValue(string, Value, bool) // createBinding + setBinding
|
||||||
|
|
||||||
|
outer() _stash
|
||||||
|
runtime() *_runtime
|
||||||
|
|
||||||
|
newReference(string, bool, _at) _reference
|
||||||
|
|
||||||
|
clone(clone *_clone) _stash
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==========
|
||||||
|
// _objectStash
|
||||||
|
// ==========
|
||||||
|
|
||||||
|
type _objectStash struct {
|
||||||
|
_runtime *_runtime
|
||||||
|
_outer _stash
|
||||||
|
object *_object
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_objectStash) runtime() *_runtime {
|
||||||
|
return self._runtime
|
||||||
|
}
|
||||||
|
|
||||||
|
func (runtime *_runtime) newObjectStash(object *_object, outer _stash) *_objectStash {
|
||||||
|
if object == nil {
|
||||||
|
object = runtime.newBaseObject()
|
||||||
|
object.class = "environment"
|
||||||
|
}
|
||||||
|
return &_objectStash{
|
||||||
|
_runtime: runtime,
|
||||||
|
_outer: outer,
|
||||||
|
object: object,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (in *_objectStash) clone(clone *_clone) _stash {
|
||||||
|
out, exists := clone.objectStash(in)
|
||||||
|
if exists {
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
*out = _objectStash{
|
||||||
|
clone.runtime,
|
||||||
|
clone.stash(in._outer),
|
||||||
|
clone.object(in.object),
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_objectStash) hasBinding(name string) bool {
|
||||||
|
return self.object.hasProperty(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_objectStash) createBinding(name string, deletable bool, value Value) {
|
||||||
|
if self.object.hasProperty(name) {
|
||||||
|
panic(hereBeDragons())
|
||||||
|
}
|
||||||
|
mode := _propertyMode(0111)
|
||||||
|
if !deletable {
|
||||||
|
mode = _propertyMode(0110)
|
||||||
|
}
|
||||||
|
// TODO False?
|
||||||
|
self.object.defineProperty(name, value, mode, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_objectStash) setBinding(name string, value Value, strict bool) {
|
||||||
|
self.object.put(name, value, strict)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_objectStash) setValue(name string, value Value, throw bool) {
|
||||||
|
if !self.hasBinding(name) {
|
||||||
|
self.createBinding(name, true, value) // Configurable by default
|
||||||
|
} else {
|
||||||
|
self.setBinding(name, value, throw)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_objectStash) getBinding(name string, throw bool) Value {
|
||||||
|
if self.object.hasProperty(name) {
|
||||||
|
return self.object.get(name)
|
||||||
|
}
|
||||||
|
if throw { // strict?
|
||||||
|
panic(self._runtime.panicReferenceError("Not Defined", name))
|
||||||
|
}
|
||||||
|
return Value{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_objectStash) deleteBinding(name string) bool {
|
||||||
|
return self.object.delete(name, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_objectStash) outer() _stash {
|
||||||
|
return self._outer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_objectStash) newReference(name string, strict bool, at _at) _reference {
|
||||||
|
return newPropertyReference(self._runtime, self.object, name, strict, at)
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========
|
||||||
|
// _dclStash
|
||||||
|
// =========
|
||||||
|
|
||||||
|
type _dclStash struct {
|
||||||
|
_runtime *_runtime
|
||||||
|
_outer _stash
|
||||||
|
property map[string]_dclProperty
|
||||||
|
}
|
||||||
|
|
||||||
|
type _dclProperty struct {
|
||||||
|
value Value
|
||||||
|
mutable bool
|
||||||
|
deletable bool
|
||||||
|
readable bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (runtime *_runtime) newDeclarationStash(outer _stash) *_dclStash {
|
||||||
|
return &_dclStash{
|
||||||
|
_runtime: runtime,
|
||||||
|
_outer: outer,
|
||||||
|
property: map[string]_dclProperty{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (in *_dclStash) clone(clone *_clone) _stash {
|
||||||
|
out, exists := clone.dclStash(in)
|
||||||
|
if exists {
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
property := make(map[string]_dclProperty, len(in.property))
|
||||||
|
for index, value := range in.property {
|
||||||
|
property[index] = clone.dclProperty(value)
|
||||||
|
}
|
||||||
|
*out = _dclStash{
|
||||||
|
clone.runtime,
|
||||||
|
clone.stash(in._outer),
|
||||||
|
property,
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_dclStash) hasBinding(name string) bool {
|
||||||
|
_, exists := self.property[name]
|
||||||
|
return exists
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_dclStash) runtime() *_runtime {
|
||||||
|
return self._runtime
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_dclStash) createBinding(name string, deletable bool, value Value) {
|
||||||
|
_, exists := self.property[name]
|
||||||
|
if exists {
|
||||||
|
panic(fmt.Errorf("createBinding: %s: already exists", name))
|
||||||
|
}
|
||||||
|
self.property[name] = _dclProperty{
|
||||||
|
value: value,
|
||||||
|
mutable: true,
|
||||||
|
deletable: deletable,
|
||||||
|
readable: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_dclStash) setBinding(name string, value Value, strict bool) {
|
||||||
|
property, exists := self.property[name]
|
||||||
|
if !exists {
|
||||||
|
panic(fmt.Errorf("setBinding: %s: missing", name))
|
||||||
|
}
|
||||||
|
if property.mutable {
|
||||||
|
property.value = value
|
||||||
|
self.property[name] = property
|
||||||
|
} else {
|
||||||
|
self._runtime.typeErrorResult(strict)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_dclStash) setValue(name string, value Value, throw bool) {
|
||||||
|
if !self.hasBinding(name) {
|
||||||
|
self.createBinding(name, false, value) // NOT deletable by default
|
||||||
|
} else {
|
||||||
|
self.setBinding(name, value, throw)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME This is called a __lot__
|
||||||
|
func (self *_dclStash) getBinding(name string, throw bool) Value {
|
||||||
|
property, exists := self.property[name]
|
||||||
|
if !exists {
|
||||||
|
panic(fmt.Errorf("getBinding: %s: missing", name))
|
||||||
|
}
|
||||||
|
if !property.mutable && !property.readable {
|
||||||
|
if throw { // strict?
|
||||||
|
panic(self._runtime.panicTypeError())
|
||||||
|
}
|
||||||
|
return Value{}
|
||||||
|
}
|
||||||
|
return property.value
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_dclStash) deleteBinding(name string) bool {
|
||||||
|
property, exists := self.property[name]
|
||||||
|
if !exists {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if !property.deletable {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
delete(self.property, name)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_dclStash) outer() _stash {
|
||||||
|
return self._outer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_dclStash) newReference(name string, strict bool, _ _at) _reference {
|
||||||
|
return &_stashReference{
|
||||||
|
name: name,
|
||||||
|
base: self,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========
|
||||||
|
// _fnStash
|
||||||
|
// ========
|
||||||
|
|
||||||
|
type _fnStash struct {
|
||||||
|
_dclStash
|
||||||
|
arguments *_object
|
||||||
|
indexOfArgumentName map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (runtime *_runtime) newFunctionStash(outer _stash) *_fnStash {
|
||||||
|
return &_fnStash{
|
||||||
|
_dclStash: _dclStash{
|
||||||
|
_runtime: runtime,
|
||||||
|
_outer: outer,
|
||||||
|
property: map[string]_dclProperty{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (in *_fnStash) clone(clone *_clone) _stash {
|
||||||
|
out, exists := clone.fnStash(in)
|
||||||
|
if exists {
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
dclStash := in._dclStash.clone(clone).(*_dclStash)
|
||||||
|
index := make(map[string]string, len(in.indexOfArgumentName))
|
||||||
|
for name, value := range in.indexOfArgumentName {
|
||||||
|
index[name] = value
|
||||||
|
}
|
||||||
|
*out = _fnStash{
|
||||||
|
_dclStash: *dclStash,
|
||||||
|
arguments: clone.object(in.arguments),
|
||||||
|
indexOfArgumentName: index,
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue