diff --git a/.github/workflows/test-lint.yml b/.github/workflows/test-lint.yml index f56b0261..505282de 100644 --- a/.github/workflows/test-lint.yml +++ b/.github/workflows/test-lint.yml @@ -8,16 +8,16 @@ jobs: go-test-lint: strategy: matrix: - go: [1.19, 1.18] - golangcli: [v1.50.1] + go: [1.21, 1.22] + golangcli: [v1.57.2] os: [ubuntu-latest] runs-on: ${{ matrix.os }} steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Set up Go - uses: actions/setup-go@v3 + uses: actions/setup-go@v5 with: go-version: ${{ matrix.go }} cache: true @@ -30,10 +30,10 @@ jobs: git --no-pager diff && [[ 0 -eq $(git status --porcelain | wc -l) ]] - name: Go Lint - uses: golangci/golangci-lint-action@v3 + uses: golangci/golangci-lint-action@v4 with: - version: ${{ matrix.golangci }} - args: "--out-${NO_FUTURE}format colored-line-number" + version: ${{ matrix.golangcli }} + args: --out-format=colored-line-number skip-pkg-cache: true skip-build-cache: true diff --git a/.golangci.yml b/.golangci.yml index 733922ab..32c76ed9 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,14 +1,12 @@ run: - deadline: 6m - skip-dirs: - - terst - skip-files: - - dbg/dbg.go - - token/token_const.go + timeout: 6m linters-settings: govet: - check-shadowing: false + settings: + shadow: + strict: true + enable-all: true goconst: min-len: 2 min-occurrences: 4 @@ -48,7 +46,31 @@ linters: - exhaustruct - nosnakecase - dupword + - structcheck + - deadcode + - golint + - varcheck + - ifshort + - interfacer + - maligned + # Just causes noise + - depguard + # Go 1.22+ only + - copyloopvar + - intrange issues: exclude-use-default: false max-same-issues: 0 + exclude: + - Deferring unsafe method "Close" on type "io\.ReadCloser" + exclude-dirs: + - terst + exclude-files: + - dbg/dbg.go + - token/token_const.go + exclude-rules: + # Field alignment in tests isn't a performance issue. + - text: fieldalignment + path: _test\.go + diff --git a/ast/comments.go b/ast/comments.go index 425aa217..81dc594e 100644 --- a/ast/comments.go +++ b/ast/comments.go @@ -38,8 +38,8 @@ const ( // Comment contains the data of the comment. type Comment struct { - Begin file.Idx Text string + Begin file.Idx Position CommentPosition } diff --git a/ast/comments_test.go b/ast/comments_test.go index bc4bbda3..0b1b676e 100644 --- a/ast/comments_test.go +++ b/ast/comments_test.go @@ -8,7 +8,7 @@ import ( func TestCommentMap(t *testing.T) { statement := &EmptyStatement{file.Idx(1)} - comment := &Comment{1, "test", LEADING} + comment := &Comment{Begin: 1, Text: "test", Position: LEADING} cm := CommentMap{} cm.AddComment(statement, comment) @@ -29,7 +29,7 @@ func TestCommentMap(t *testing.T) { func TestCommentMap_move(t *testing.T) { statement1 := &EmptyStatement{file.Idx(1)} statement2 := &EmptyStatement{file.Idx(2)} - comment := &Comment{1, "test", LEADING} + comment := &Comment{Begin: 1, Text: "test", Position: LEADING} cm := CommentMap{} cm.AddComment(statement1, comment) diff --git a/ast/node.go b/ast/node.go index 7cc23443..ae9e63b8 100644 --- a/ast/node.go +++ b/ast/node.go @@ -24,9 +24,9 @@ type Expression interface { // ArrayLiteral represents an array literal. type ArrayLiteral struct { + Value []Expression LeftBracket file.Idx RightBracket file.Idx - Value []Expression } // Idx0 implements Node. @@ -36,7 +36,7 @@ func (al *ArrayLiteral) Idx0() file.Idx { // Idx1 implements Node. func (al *ArrayLiteral) Idx1() file.Idx { - return al.RightBracket + return al.RightBracket + 1 } // expression implements Expression. @@ -44,9 +44,9 @@ func (*ArrayLiteral) expression() {} // AssignExpression represents an assignment expression. type AssignExpression struct { - Operator token.Token Left Expression Right Expression + Operator token.Token } // Idx0 implements Node. @@ -83,9 +83,9 @@ func (*BadExpression) expression() {} // BinaryExpression represents a binary expression. type BinaryExpression struct { - Operator token.Token Left Expression Right Expression + Operator token.Token Comparison bool } @@ -104,8 +104,8 @@ func (*BinaryExpression) expression() {} // BooleanLiteral represents a boolean expression. type BooleanLiteral struct { - Idx file.Idx Literal string + Idx file.Idx Value bool } @@ -146,8 +146,8 @@ func (*BracketExpression) expression() {} // CallExpression represents a call expression. type CallExpression struct { Callee Expression - LeftParenthesis file.Idx ArgumentList []Expression + LeftParenthesis file.Idx RightParenthesis file.Idx } @@ -178,7 +178,7 @@ func (ce *ConditionalExpression) Idx0() file.Idx { // Idx1 implements Node. func (ce *ConditionalExpression) Idx1() file.Idx { - return ce.Test.Idx1() + return ce.Alternate.Idx1() } // expression implements Expression. @@ -224,13 +224,12 @@ func (*EmptyExpression) expression() {} // FunctionLiteral represents a function literal. type FunctionLiteral struct { - Function file.Idx - Name *Identifier - ParameterList *ParameterList - Body Statement - Source string - + Body Statement + Name *Identifier + ParameterList *ParameterList + Source string DeclarationList []Declaration + Function file.Idx } // Idx0 implements Node. @@ -267,10 +266,10 @@ func (*Identifier) expression() {} // NewExpression represents a new expression. type NewExpression struct { - New file.Idx Callee Expression - LeftParenthesis file.Idx ArgumentList []Expression + New file.Idx + LeftParenthesis file.Idx RightParenthesis file.Idx } @@ -292,8 +291,8 @@ func (*NewExpression) expression() {} // NullLiteral represents a null literal. type NullLiteral struct { - Idx file.Idx Literal string + Idx file.Idx } // Idx0 implements Node. @@ -311,9 +310,9 @@ func (*NullLiteral) expression() {} // NumberLiteral represents a number literal. type NumberLiteral struct { - Idx file.Idx - Literal string Value interface{} + Literal string + Idx file.Idx } // Idx0 implements Node. @@ -331,9 +330,9 @@ func (*NumberLiteral) expression() {} // ObjectLiteral represents an object literal. type ObjectLiteral struct { + Value []Property LeftBrace file.Idx RightBrace file.Idx - Value []Property } // Idx0 implements Node. @@ -343,7 +342,7 @@ func (ol *ObjectLiteral) Idx0() file.Idx { // Idx1 implements Node. func (ol *ObjectLiteral) Idx1() file.Idx { - return ol.RightBrace + return ol.RightBrace + 1 } // expression implements Expression. @@ -351,25 +350,25 @@ func (*ObjectLiteral) expression() {} // ParameterList represents a parameter list. type ParameterList struct { - Opening file.Idx List []*Identifier + Opening file.Idx Closing file.Idx } // Property represents a property. type Property struct { + Value Expression Key string Kind string - Value Expression } // RegExpLiteral represents a regular expression literal. type RegExpLiteral struct { - Idx file.Idx Literal string Pattern string Flags string Value string + Idx file.Idx } // Idx0 implements Node. @@ -397,7 +396,7 @@ func (se *SequenceExpression) Idx0() file.Idx { // Idx1 implements Node. func (se *SequenceExpression) Idx1() file.Idx { - return se.Sequence[0].Idx1() + return se.Sequence[len(se.Sequence)-1].Idx1() } // expression implements Expression. @@ -405,9 +404,9 @@ func (*SequenceExpression) expression() {} // StringLiteral represents a string literal. type StringLiteral struct { - Idx file.Idx Literal string Value string + Idx file.Idx } // Idx0 implements Node. @@ -443,14 +442,17 @@ func (*ThisExpression) expression() {} // UnaryExpression represents a unary expression. type UnaryExpression struct { - Operator token.Token - Idx file.Idx // If a prefix operation Operand Expression + Operator token.Token + Idx file.Idx Postfix bool } // Idx0 implements Node. func (ue *UnaryExpression) Idx0() file.Idx { + if ue.Postfix { + return ue.Operand.Idx0() + } return ue.Idx } @@ -467,9 +469,9 @@ func (*UnaryExpression) expression() {} // VariableExpression represents a variable expression. type VariableExpression struct { + Initializer Expression Name string Idx file.Idx - Initializer Expression } // Idx0 implements Node. @@ -480,7 +482,7 @@ func (ve *VariableExpression) Idx0() file.Idx { // Idx1 implements Node. func (ve *VariableExpression) Idx1() file.Idx { if ve.Initializer == nil { - return file.Idx(int(ve.Idx) + len(ve.Name) + 1) + return file.Idx(int(ve.Idx) + len(ve.Name)) } return ve.Initializer.Idx1() } @@ -515,8 +517,8 @@ func (*BadStatement) statement() {} // BlockStatement represents a block statement. type BlockStatement struct { - LeftBrace file.Idx List []Statement + LeftBrace file.Idx RightBrace file.Idx } @@ -535,14 +537,9 @@ func (*BlockStatement) statement() {} // BranchStatement represents a branch statement. type BranchStatement struct { + Label *Identifier Idx file.Idx Token token.Token - Label *Identifier -} - -// Idx1 implements Node. -func (bs *BranchStatement) Idx1() file.Idx { - return bs.Idx } // Idx0 implements Node. @@ -550,14 +547,22 @@ func (bs *BranchStatement) Idx0() file.Idx { return bs.Idx } +// Idx1 implements Node. +func (bs *BranchStatement) Idx1() file.Idx { + if bs.Label == nil { + return file.Idx(int(bs.Idx) + len(bs.Token.String())) + } + return bs.Label.Idx1() +} + // expression implements Statement. func (*BranchStatement) statement() {} // CaseStatement represents a case statement. type CaseStatement struct { - Case file.Idx Test Expression Consequent []Statement + Case file.Idx } // Idx0 implements Node. @@ -575,9 +580,9 @@ func (*CaseStatement) statement() {} // CatchStatement represents a catch statement. type CatchStatement struct { - Catch file.Idx - Parameter *Identifier Body Statement + Parameter *Identifier + Catch file.Idx } // Idx0 implements Node. @@ -613,9 +618,10 @@ func (*DebuggerStatement) statement() {} // DoWhileStatement represents a do while statement. type DoWhileStatement struct { - Do file.Idx - Test Expression - Body Statement + Test Expression + Body Statement + Do file.Idx + RightParenthesis file.Idx } // Idx0 implements Node. @@ -625,7 +631,7 @@ func (dws *DoWhileStatement) Idx0() file.Idx { // Idx1 implements Node. func (dws *DoWhileStatement) Idx1() file.Idx { - return dws.Test.Idx1() + return dws.RightParenthesis + 1 } // expression implements Statement. @@ -669,10 +675,10 @@ func (*ExpressionStatement) statement() {} // ForInStatement represents a for in statement. type ForInStatement struct { - For file.Idx Into Expression Source Expression Body Statement + For file.Idx } // Idx0 implements Node. @@ -690,11 +696,11 @@ func (*ForInStatement) statement() {} // ForStatement represents a for statement. type ForStatement struct { - For file.Idx Initializer Expression Update Expression Test Expression Body Statement + For file.Idx } // Idx0 implements Node. @@ -730,10 +736,10 @@ func (*FunctionStatement) statement() {} // IfStatement represents a if statement. type IfStatement struct { - If file.Idx Test Expression Consequent Statement Alternate Statement + If file.Idx } // Idx0 implements Node. @@ -754,9 +760,9 @@ func (*IfStatement) statement() {} // LabelledStatement represents a labelled statement. type LabelledStatement struct { + Statement Statement Label *Identifier Colon file.Idx - Statement Statement } // Idx0 implements Node. @@ -766,7 +772,7 @@ func (ls *LabelledStatement) Idx0() file.Idx { // Idx1 implements Node. func (ls *LabelledStatement) Idx1() file.Idx { - return ls.Colon + 1 + return ls.Statement.Idx1() } // expression implements Statement. @@ -774,8 +780,8 @@ func (*LabelledStatement) statement() {} // ReturnStatement represents a return statement. type ReturnStatement struct { - Return file.Idx Argument Expression + Return file.Idx } // Idx0 implements Node. @@ -785,7 +791,10 @@ func (rs *ReturnStatement) Idx0() file.Idx { // Idx1 implements Node. func (rs *ReturnStatement) Idx1() file.Idx { - return rs.Return + if rs.Argument != nil { + return rs.Argument.Idx1() + } + return rs.Return + 6 } // expression implements Statement. @@ -793,10 +802,11 @@ func (*ReturnStatement) statement() {} // SwitchStatement represents a switch statement. type SwitchStatement struct { - Switch file.Idx Discriminant Expression - Default int Body []*CaseStatement + Switch file.Idx + Default int + RightBrace file.Idx } // Idx0 implements Node. @@ -806,7 +816,7 @@ func (ss *SwitchStatement) Idx0() file.Idx { // Idx1 implements Node. func (ss *SwitchStatement) Idx1() file.Idx { - return ss.Body[len(ss.Body)-1].Idx1() + return ss.RightBrace + 1 } // expression implements Statement. @@ -814,8 +824,8 @@ func (*SwitchStatement) statement() {} // ThrowStatement represents a throw statement. type ThrowStatement struct { - Throw file.Idx Argument Expression + Throw file.Idx } // Idx0 implements Node. @@ -825,7 +835,7 @@ func (ts *ThrowStatement) Idx0() file.Idx { // Idx1 implements Node. func (ts *ThrowStatement) Idx1() file.Idx { - return ts.Throw + return ts.Argument.Idx1() } // expression implements Statement. @@ -833,10 +843,10 @@ func (*ThrowStatement) statement() {} // TryStatement represents a try statement. type TryStatement struct { - Try file.Idx Body Statement - Catch *CatchStatement Finally Statement + Catch *CatchStatement + Try file.Idx } // Idx0 implements Node. @@ -846,7 +856,10 @@ func (ts *TryStatement) Idx0() file.Idx { // Idx1 implements Node. func (ts *TryStatement) Idx1() file.Idx { - return ts.Try + if ts.Finally != nil { + return ts.Finally.Idx1() + } + return ts.Catch.Idx1() } // expression implements Statement. @@ -854,8 +867,8 @@ func (*TryStatement) statement() {} // VariableStatement represents a variable statement. type VariableStatement struct { - Var file.Idx List []Expression + Var file.Idx } // Idx0 implements Node. @@ -873,9 +886,9 @@ func (*VariableStatement) statement() {} // WhileStatement represents a while statement. type WhileStatement struct { - While file.Idx Test Expression Body Statement + While file.Idx } // Idx0 implements Node. @@ -893,9 +906,9 @@ func (*WhileStatement) statement() {} // WithStatement represents a with statement. type WithStatement struct { - With file.Idx Object Expression Body Statement + With file.Idx } // Idx0 implements Node. @@ -925,8 +938,8 @@ func (*FunctionDeclaration) declaration() {} // VariableDeclaration represents a variable declaration. type VariableDeclaration struct { - Var file.Idx List []*VariableExpression + Var file.Idx } // declaration implements Declaration. @@ -934,13 +947,10 @@ func (*VariableDeclaration) declaration() {} // Program represents a full program. type Program struct { - Body []Statement - + File *file.File + Comments CommentMap + Body []Statement DeclarationList []Declaration - - File *file.File - - Comments CommentMap } // Idx0 implements Node. diff --git a/ast/walk.go b/ast/walk.go index 521445b8..44229302 100644 --- a/ast/walk.go +++ b/ast/walk.go @@ -38,6 +38,7 @@ func Walk(v Visitor, n Node) { Walk(v, n.Right) } case *BadExpression: + case *BadStatement: case *BinaryExpression: if n != nil { Walk(v, n.Left) diff --git a/ast/walk_test.go b/ast/walk_test.go index 1748c67d..faa4b561 100644 --- a/ast/walk_test.go +++ b/ast/walk_test.go @@ -10,10 +10,10 @@ import ( ) type walker struct { - stack []ast.Node + seen map[ast.Node]struct{} source string + stack []ast.Node shift file.Idx - seen map[ast.Node]struct{} duplicate int newExpressionIdx1 file.Idx } @@ -96,8 +96,8 @@ func TestVisitorRewrite(t *testing.T) { } // test` require.Equal(t, xformed, w.source) - require.Len(t, w.stack, 0) - require.Equal(t, w.duplicate, 0) + require.Empty(t, w.stack) + require.Zero(t, w.duplicate) } func Test_issue261(t *testing.T) { @@ -131,8 +131,27 @@ func Test_issue261(t *testing.T) { ast.Walk(w, prog) require.Equal(t, tt.want, w.newExpressionIdx1) - require.Len(t, w.stack, 0) - require.Equal(t, w.duplicate, 0) + require.Empty(t, w.stack) + require.Zero(t, w.duplicate) }) } } + +func TestBadStatement(t *testing.T) { + source := ` + var abc; + break; do { + } while(true); +` + program, err := parser.ParseFile(nil, "", source, 0) + require.ErrorContains(t, err, "Illegal break statement") + + w := &walker{ + source: source, + seen: make(map[ast.Node]struct{}), + } + + require.NotPanics(t, func() { + ast.Walk(w, program) + }) +} diff --git a/builtin.go b/builtin.go index 3e63dca6..bfb15621 100644 --- a/builtin.go +++ b/builtin.go @@ -147,11 +147,11 @@ func builtinGlobalParseFloat(call FunctionCall) Value { value, err := strconv.ParseFloat(input, 64) if err != nil { for end := len(input); end > 0; end-- { - input := input[0:end] - if !parseFloatMatchValid.MatchString(input) { + val := input[0:end] + if !parseFloatMatchValid.MatchString(val) { return NaNValue() } - value, err = strconv.ParseFloat(input, 64) + value, err = strconv.ParseFloat(val, 64) if err == nil { break } @@ -200,8 +200,7 @@ func encodeDecodeURI(call FunctionCall, escape *regexp.Regexp) Value { } index++ size := utf8.EncodeRune(encode, decode[0]) - encode := encode[0:size] - output = append(output, encode...) + output = append(output, encode[0:size]...) } bytes := escape.ReplaceAllFunc(output, func(target []byte) []byte { diff --git a/builtin_array.go b/builtin_array.go index a82ed012..3ca88db4 100644 --- a/builtin_array.go +++ b/builtin_array.go @@ -339,9 +339,9 @@ func builtinArrayReverse(call FunctionCall) Value { func sortCompare(thisObject *object, index0, index1 uint, compare *object) int { j := struct { name string + value string exists bool defined bool - value string }{} k := j j.name = arrayIndexToString(int64(index0)) diff --git a/builtin_date.go b/builtin_date.go index 2d1d601a..82332405 100644 --- a/builtin_date.go +++ b/builtin_date.go @@ -2,7 +2,7 @@ package otto import ( "math" - Time "time" + "time" ) // Date @@ -10,7 +10,7 @@ import ( const ( // TODO Be like V8? // builtinDateDateTimeLayout = "Mon Jan 2 2006 15:04:05 GMT-0700 (MST)". - builtinDateDateTimeLayout = Time.RFC1123 // "Mon, 02 Jan 2006 15:04:05 MST" + builtinDateDateTimeLayout = time.RFC1123 // "Mon, 02 Jan 2006 15:04:05 MST" builtinDateDateLayout = "Mon, 02 Jan 2006" builtinDateTimeLayout = "15:04:05 MST" ) @@ -18,16 +18,16 @@ const ( // utcTimeZone is the time zone used for UTC calculations. // It is GMT not UTC as that's what Javascript does because toUTCString is // actually an alias to toGMTString. -var utcTimeZone = Time.FixedZone("GMT", 0) +var utcTimeZone = time.FixedZone("GMT", 0) func builtinDate(call FunctionCall) Value { date := &dateObject{} - date.Set(newDateTime([]Value{}, Time.Local)) + date.Set(newDateTime([]Value{}, time.Local)) //nolint:gosmopolitan return stringValue(date.Time().Format(builtinDateDateTimeLayout)) } func builtinNewDate(obj *object, argumentList []Value) Value { - return objectValue(obj.runtime.newDate(newDateTime(argumentList, Time.Local))) + return objectValue(obj.runtime.newDate(newDateTime(argumentList, time.Local))) //nolint:gosmopolitan } func builtinDateToString(call FunctionCall) Value { @@ -35,7 +35,7 @@ func builtinDateToString(call FunctionCall) Value { if date.isNaN { return stringValue("Invalid Date") } - return stringValue(date.Time().Local().Format(builtinDateDateTimeLayout)) + return stringValue(date.Time().Local().Format(builtinDateDateTimeLayout)) //nolint:gosmopolitan } func builtinDateToDateString(call FunctionCall) Value { @@ -43,7 +43,7 @@ func builtinDateToDateString(call FunctionCall) Value { if date.isNaN { return stringValue("Invalid Date") } - return stringValue(date.Time().Local().Format(builtinDateDateLayout)) + return stringValue(date.Time().Local().Format(builtinDateDateLayout)) //nolint:gosmopolitan } func builtinDateToTimeString(call FunctionCall) Value { @@ -51,7 +51,7 @@ func builtinDateToTimeString(call FunctionCall) Value { if date.isNaN { return stringValue("Invalid Date") } - return stringValue(date.Time().Local().Format(builtinDateTimeLayout)) + return stringValue(date.Time().Local().Format(builtinDateTimeLayout)) //nolint:gosmopolitan } func builtinDateToUTCString(call FunctionCall) Value { @@ -142,7 +142,7 @@ func builtinDateBeforeSet(call FunctionCall, argumentLimit int, timeLocal bool) } baseTime := date.Time() if timeLocal { - baseTime = baseTime.Local() + baseTime = baseTime.Local() //nolint:gosmopolitan } ecmaTime := newEcmaTime(baseTime) return obj, &date, &ecmaTime, valueList @@ -154,7 +154,7 @@ func builtinDateParse(call FunctionCall) Value { } func builtinDateUTC(call FunctionCall) Value { - return float64Value(newDateTime(call.ArgumentList, Time.UTC)) + return float64Value(newDateTime(call.ArgumentList, time.UTC)) } func builtinDateNow(call FunctionCall) Value { @@ -168,7 +168,7 @@ func builtinDateToLocaleString(call FunctionCall) Value { if date.isNaN { return stringValue("Invalid Date") } - return stringValue(date.Time().Local().Format("2006-01-02 15:04:05")) + return stringValue(date.Time().Local().Format("2006-01-02 15:04:05")) //nolint:gosmopolitan } // This is a placeholder. @@ -177,7 +177,7 @@ func builtinDateToLocaleDateString(call FunctionCall) Value { if date.isNaN { return stringValue("Invalid Date") } - return stringValue(date.Time().Local().Format("2006-01-02")) + return stringValue(date.Time().Local().Format("2006-01-02")) //nolint:gosmopolitan } // This is a placeholder. @@ -186,7 +186,7 @@ func builtinDateToLocaleTimeString(call FunctionCall) Value { if date.isNaN { return stringValue("Invalid Date") } - return stringValue(date.Time().Local().Format("15:04:05")) + return stringValue(date.Time().Local().Format("15:04:05")) //nolint:gosmopolitan } func builtinDateValueOf(call FunctionCall) Value { @@ -204,7 +204,7 @@ func builtinDateGetYear(call FunctionCall) Value { if date.isNaN { return NaNValue() } - return intValue(date.Time().Local().Year() - 1900) + return intValue(date.Time().Local().Year() - 1900) //nolint:gosmopolitan } func builtinDateGetFullYear(call FunctionCall) Value { @@ -214,7 +214,7 @@ func builtinDateGetFullYear(call FunctionCall) Value { if date.isNaN { return NaNValue() } - return intValue(date.Time().Local().Year()) + return intValue(date.Time().Local().Year()) //nolint:gosmopolitan } func builtinDateGetUTCFullYear(call FunctionCall) Value { @@ -230,7 +230,7 @@ func builtinDateGetMonth(call FunctionCall) Value { if date.isNaN { return NaNValue() } - return intValue(dateFromGoMonth(date.Time().Local().Month())) + return intValue(dateFromGoMonth(date.Time().Local().Month())) //nolint:gosmopolitan } func builtinDateGetUTCMonth(call FunctionCall) Value { @@ -246,7 +246,7 @@ func builtinDateGetDate(call FunctionCall) Value { if date.isNaN { return NaNValue() } - return intValue(date.Time().Local().Day()) + return intValue(date.Time().Local().Day()) //nolint:gosmopolitan } func builtinDateGetUTCDate(call FunctionCall) Value { @@ -263,7 +263,7 @@ func builtinDateGetDay(call FunctionCall) Value { if date.isNaN { return NaNValue() } - return intValue(dateFromGoDay(date.Time().Local().Weekday())) + return intValue(dateFromGoDay(date.Time().Local().Weekday())) //nolint:gosmopolitan } func builtinDateGetUTCDay(call FunctionCall) Value { @@ -279,7 +279,7 @@ func builtinDateGetHours(call FunctionCall) Value { if date.isNaN { return NaNValue() } - return intValue(date.Time().Local().Hour()) + return intValue(date.Time().Local().Hour()) //nolint:gosmopolitan } func builtinDateGetUTCHours(call FunctionCall) Value { @@ -295,7 +295,7 @@ func builtinDateGetMinutes(call FunctionCall) Value { if date.isNaN { return NaNValue() } - return intValue(date.Time().Local().Minute()) + return intValue(date.Time().Local().Minute()) //nolint:gosmopolitan } func builtinDateGetUTCMinutes(call FunctionCall) Value { @@ -311,7 +311,7 @@ func builtinDateGetSeconds(call FunctionCall) Value { if date.isNaN { return NaNValue() } - return intValue(date.Time().Local().Second()) + return intValue(date.Time().Local().Second()) //nolint:gosmopolitan } func builtinDateGetUTCSeconds(call FunctionCall) Value { @@ -327,7 +327,7 @@ func builtinDateGetMilliseconds(call FunctionCall) Value { if date.isNaN { return NaNValue() } - return intValue(date.Time().Local().Nanosecond() / (100 * 100 * 100)) + return intValue(date.Time().Local().Nanosecond() / (100 * 100 * 100)) //nolint:gosmopolitan } func builtinDateGetUTCMilliseconds(call FunctionCall) Value { @@ -343,9 +343,9 @@ func builtinDateGetTimezoneOffset(call FunctionCall) Value { if date.isNaN { return NaNValue() } - timeLocal := date.Time().Local() + timeLocal := date.Time().Local() //nolint:gosmopolitan // Is this kosher? - timeLocalAsUTC := Time.Date( + timeLocalAsUTC := time.Date( timeLocal.Year(), timeLocal.Month(), timeLocal.Day(), @@ -353,7 +353,7 @@ func builtinDateGetTimezoneOffset(call FunctionCall) Value { timeLocal.Minute(), timeLocal.Second(), timeLocal.Nanosecond(), - Time.UTC, + time.UTC, ) return float64Value(date.Time().Sub(timeLocalAsUTC).Seconds() / 60) } diff --git a/builtin_json.go b/builtin_json.go index 960c3937..97a5db8f 100644 --- a/builtin_json.go +++ b/builtin_json.go @@ -8,8 +8,8 @@ import ( ) type builtinJSONParseContext struct { - call FunctionCall reviver Value + call FunctionCall } func builtinJSONParse(call FunctionCall) Value { @@ -45,21 +45,21 @@ func builtinJSONReviveWalk(ctx builtinJSONParseContext, holder *object, name str if isArray(obj) { length := int64(objectLength(obj)) for index := int64(0); index < length; index++ { - name := arrayIndexToString(index) - value := builtinJSONReviveWalk(ctx, obj, name) - if value.IsUndefined() { - obj.delete(name, false) + idxName := arrayIndexToString(index) + idxValue := builtinJSONReviveWalk(ctx, obj, idxName) + if idxValue.IsUndefined() { + obj.delete(idxName, false) } else { - obj.defineProperty(name, value, 0o111, false) + obj.defineProperty(idxName, idxValue, 0o111, false) } } } else { obj.enumerate(false, func(name string) bool { - value := builtinJSONReviveWalk(ctx, obj, name) - if value.IsUndefined() { + enumVal := builtinJSONReviveWalk(ctx, obj, name) + if enumVal.IsUndefined() { obj.delete(name, false) } else { - obj.defineProperty(name, value, 0o111, false) + obj.defineProperty(name, enumVal, 0o111, false) } return true }) @@ -99,11 +99,11 @@ func builtinJSONParseWalk(ctx builtinJSONParseContext, rawValue interface{}) (Va } type builtinJSONStringifyContext struct { - call FunctionCall - stack []*object - propertyList []string replacerFunction *Value gap string + stack []*object + propertyList []string + call FunctionCall } func builtinJSONStringify(call FunctionCall) Value { @@ -241,19 +241,19 @@ func builtinJSONStringifyWalk(ctx builtinJSONStringifyContext, key string, holde case valueNull: return nil, true case valueObject: - holder := value.object() + objHolder := value.object() if value := value.object(); nil != value { for _, obj := range ctx.stack { - if holder == obj { + if objHolder == obj { panic(ctx.call.runtime.panicTypeError("Converting circular structure to JSON")) } } ctx.stack = append(ctx.stack, value) defer func() { ctx.stack = ctx.stack[:len(ctx.stack)-1] }() } - if isArray(holder) { + if isArray(objHolder) { var length uint32 - switch value := holder.get(propertyLength).value.(type) { + switch value := objHolder.get(propertyLength).value.(type) { case uint32: length = value case int: @@ -266,15 +266,15 @@ func builtinJSONStringifyWalk(ctx builtinJSONStringifyContext, key string, holde array := make([]interface{}, length) for index := range array { name := arrayIndexToString(int64(index)) - value, _ := builtinJSONStringifyWalk(ctx, name, holder) + value, _ := builtinJSONStringifyWalk(ctx, name, objHolder) array[index] = value } return array, true - } else if holder.class != classFunctionName { + } else if objHolder.class != classFunctionName { obj := map[string]interface{}{} if ctx.propertyList != nil { for _, name := range ctx.propertyList { - value, exists := builtinJSONStringifyWalk(ctx, name, holder) + value, exists := builtinJSONStringifyWalk(ctx, name, objHolder) if exists { obj[name] = value } @@ -282,8 +282,8 @@ func builtinJSONStringifyWalk(ctx builtinJSONStringifyContext, key string, holde } else { // Go maps are without order, so this doesn't conform to the ECMA ordering // standard, but oh well... - holder.enumerate(false, func(name string) bool { - value, exists := builtinJSONStringifyWalk(ctx, name, holder) + objHolder.enumerate(false, func(name string) bool { + value, exists := builtinJSONStringifyWalk(ctx, name, objHolder) if exists { obj[name] = value } diff --git a/builtin_math.go b/builtin_math.go index 1774fd8d..6429aa5a 100644 --- a/builtin_math.go +++ b/builtin_math.go @@ -17,11 +17,21 @@ func builtinMathAcos(call FunctionCall) Value { return float64Value(math.Acos(number)) } +func builtinMathAcosh(call FunctionCall) Value { + number := call.Argument(0).float64() + return float64Value(math.Acosh(number)) +} + func builtinMathAsin(call FunctionCall) Value { number := call.Argument(0).float64() return float64Value(math.Asin(number)) } +func builtinMathAsinh(call FunctionCall) Value { + number := call.Argument(0).float64() + return float64Value(math.Asinh(number)) +} + func builtinMathAtan(call FunctionCall) Value { number := call.Argument(0).float64() return float64Value(math.Atan(number)) @@ -39,6 +49,16 @@ func builtinMathAtan2(call FunctionCall) Value { return float64Value(math.Atan2(y, x)) } +func builtinMathAtanh(call FunctionCall) Value { + number := call.Argument(0).float64() + return float64Value(math.Atanh(number)) +} + +func builtinMathCbrt(call FunctionCall) Value { + number := call.Argument(0).float64() + return float64Value(math.Cbrt(number)) +} + func builtinMathCos(call FunctionCall) Value { number := call.Argument(0).float64() return float64Value(math.Cos(number)) @@ -49,11 +69,21 @@ func builtinMathCeil(call FunctionCall) Value { return float64Value(math.Ceil(number)) } +func builtinMathCosh(call FunctionCall) Value { + number := call.Argument(0).float64() + return float64Value(math.Cosh(number)) +} + func builtinMathExp(call FunctionCall) Value { number := call.Argument(0).float64() return float64Value(math.Exp(number)) } +func builtinMathExpm1(call FunctionCall) Value { + number := call.Argument(0).float64() + return float64Value(math.Expm1(number)) +} + func builtinMathFloor(call FunctionCall) Value { number := call.Argument(0).float64() return float64Value(math.Floor(number)) @@ -64,6 +94,21 @@ func builtinMathLog(call FunctionCall) Value { return float64Value(math.Log(number)) } +func builtinMathLog10(call FunctionCall) Value { + number := call.Argument(0).float64() + return float64Value(math.Log10(number)) +} + +func builtinMathLog1p(call FunctionCall) Value { + number := call.Argument(0).float64() + return float64Value(math.Log1p(number)) +} + +func builtinMathLog2(call FunctionCall) Value { + number := call.Argument(0).float64() + return float64Value(math.Log2(number)) +} + func builtinMathMax(call FunctionCall) Value { switch len(call.ArgumentList) { case 0: @@ -121,7 +166,7 @@ func builtinMathRandom(call FunctionCall) Value { if call.runtime.random != nil { v = call.runtime.random() } else { - v = rand.Float64() //nolint: gosec + v = rand.Float64() //nolint:gosec } return float64Value(v) } @@ -140,6 +185,11 @@ func builtinMathSin(call FunctionCall) Value { return float64Value(math.Sin(number)) } +func builtinMathSinh(call FunctionCall) Value { + number := call.Argument(0).float64() + return float64Value(math.Sinh(number)) +} + func builtinMathSqrt(call FunctionCall) Value { number := call.Argument(0).float64() return float64Value(math.Sqrt(number)) @@ -149,3 +199,13 @@ func builtinMathTan(call FunctionCall) Value { number := call.Argument(0).float64() return float64Value(math.Tan(number)) } + +func builtinMathTanh(call FunctionCall) Value { + number := call.Argument(0).float64() + return float64Value(math.Tanh(number)) +} + +func builtinMathTrunc(call FunctionCall) Value { + number := call.Argument(0).float64() + return float64Value(math.Trunc(number)) +} diff --git a/builtin_object.go b/builtin_object.go index 029a75f7..82da2cf9 100644 --- a/builtin_object.go +++ b/builtin_object.go @@ -273,6 +273,17 @@ func builtinObjectKeys(call FunctionCall) Value { panic(call.runtime.panicTypeError("Object.Keys is nil")) } +func builtinObjectValues(call FunctionCall) Value { + if obj, values := call.Argument(0).object(), []Value(nil); nil != obj { + obj.enumerate(false, func(name string) bool { + values = append(values, obj.get(name)) + return true + }) + return objectValue(call.runtime.newArrayOf(values)) + } + panic(call.runtime.panicTypeError("Object.Values is nil")) +} + func builtinObjectGetOwnPropertyNames(call FunctionCall) Value { if obj, propertyNames := call.Argument(0).object(), []Value(nil); nil != obj { obj.enumerate(true, func(name string) bool { diff --git a/builtin_string.go b/builtin_string.go index e32b71f2..a11babe4 100644 --- a/builtin_string.go +++ b/builtin_string.go @@ -443,6 +443,17 @@ func builtinStringSubstr(call FunctionCall) Value { return stringValue(string(target[start : start+length])) } +func builtinStringStartsWith(call FunctionCall) Value { + checkObjectCoercible(call.runtime, call.This) + target := call.This.string() + search := call.Argument(0).string() + length := len(search) + if length > len(target) { + return boolValue(false) + } + return boolValue(target[:length] == search) +} + func builtinStringToLowerCase(call FunctionCall) Value { checkObjectCoercible(call.runtime, call.This) return stringValue(strings.ToLower(call.This.string())) @@ -462,6 +473,14 @@ func builtinStringTrim(call FunctionCall) Value { builtinStringTrimWhitespace)) } +func builtinStringTrimStart(call FunctionCall) Value { + return builtinStringTrimLeft(call) +} + +func builtinStringTrimEnd(call FunctionCall) Value { + return builtinStringTrimRight(call) +} + // Mozilla extension, not ECMAScript 5. func builtinStringTrimLeft(call FunctionCall) Value { checkObjectCoercible(call.runtime, call.This) @@ -478,7 +497,7 @@ func builtinStringTrimRight(call FunctionCall) Value { func builtinStringLocaleCompare(call FunctionCall) Value { checkObjectCoercible(call.runtime, call.This) - this := call.This.string() //nolint: ifshort + this := call.This.string() //nolint:ifshort that := call.Argument(0).string() if this < that { return intValue(-1) diff --git a/call_test.go b/call_test.go index 52fb6983..f239200e 100644 --- a/call_test.go +++ b/call_test.go @@ -20,7 +20,7 @@ func BenchmarkNativeCallWithString(b *testing.B) { require.NoError(b, err) for i := 0; i < b.N; i++ { - _, err := vm.Run(s) + _, err = vm.Run(s) require.NoError(b, err) } } @@ -34,7 +34,7 @@ func BenchmarkNativeCallWithFloat32(b *testing.B) { require.NoError(b, err) for i := 0; i < b.N; i++ { - _, err := vm.Run(s) + _, err = vm.Run(s) require.NoError(b, err) } } @@ -48,7 +48,7 @@ func BenchmarkNativeCallWithFloat64(b *testing.B) { require.NoError(b, err) for i := 0; i < b.N; i++ { - _, err := vm.Run(s) + _, err = vm.Run(s) require.NoError(b, err) } } @@ -62,7 +62,7 @@ func BenchmarkNativeCallWithInt(b *testing.B) { require.NoError(b, err) for i := 0; i < b.N; i++ { - _, err := vm.Run(s) + _, err = vm.Run(s) require.NoError(b, err) } } @@ -76,7 +76,7 @@ func BenchmarkNativeCallWithUint(b *testing.B) { require.NoError(b, err) for i := 0; i < b.N; i++ { - _, err := vm.Run(s) + _, err = vm.Run(s) require.NoError(b, err) } } @@ -90,7 +90,7 @@ func BenchmarkNativeCallWithInt8(b *testing.B) { require.NoError(b, err) for i := 0; i < b.N; i++ { - _, err := vm.Run(s) + _, err = vm.Run(s) require.NoError(b, err) } } @@ -104,7 +104,7 @@ func BenchmarkNativeCallWithUint8(b *testing.B) { require.NoError(b, err) for i := 0; i < b.N; i++ { - _, err := vm.Run(s) + _, err = vm.Run(s) require.NoError(b, err) } } @@ -118,7 +118,7 @@ func BenchmarkNativeCallWithInt16(b *testing.B) { require.NoError(b, err) for i := 0; i < b.N; i++ { - _, err := vm.Run(s) + _, err = vm.Run(s) require.NoError(b, err) } } @@ -132,7 +132,7 @@ func BenchmarkNativeCallWithUint16(b *testing.B) { require.NoError(b, err) for i := 0; i < b.N; i++ { - _, err := vm.Run(s) + _, err = vm.Run(s) require.NoError(b, err) } } @@ -146,7 +146,7 @@ func BenchmarkNativeCallWithInt32(b *testing.B) { require.NoError(b, err) for i := 0; i < b.N; i++ { - _, err := vm.Run(s) + _, err = vm.Run(s) require.NoError(b, err) } } @@ -160,7 +160,7 @@ func BenchmarkNativeCallWithUint32(b *testing.B) { require.NoError(b, err) for i := 0; i < b.N; i++ { - _, err := vm.Run(s) + _, err = vm.Run(s) require.NoError(b, err) } } @@ -174,7 +174,7 @@ func BenchmarkNativeCallWithInt64(b *testing.B) { require.NoError(b, err) for i := 0; i < b.N; i++ { - _, err := vm.Run(s) + _, err = vm.Run(s) require.NoError(b, err) } } @@ -188,7 +188,7 @@ func BenchmarkNativeCallWithUint64(b *testing.B) { require.NoError(b, err) for i := 0; i < b.N; i++ { - _, err := vm.Run(s) + _, err = vm.Run(s) require.NoError(b, err) } } @@ -202,7 +202,7 @@ func BenchmarkNativeCallWithStringInt(b *testing.B) { require.NoError(b, err) for i := 0; i < b.N; i++ { - _, err := vm.Run(s) + _, err = vm.Run(s) require.NoError(b, err) } } @@ -216,7 +216,7 @@ func BenchmarkNativeCallWithIntVariadic0(b *testing.B) { require.NoError(b, err) for i := 0; i < b.N; i++ { - _, err := vm.Run(s) + _, err = vm.Run(s) require.NoError(b, err) } } @@ -230,7 +230,7 @@ func BenchmarkNativeCallWithIntVariadic1(b *testing.B) { require.NoError(b, err) for i := 0; i < b.N; i++ { - _, err := vm.Run(s) + _, err = vm.Run(s) require.NoError(b, err) } } @@ -244,7 +244,7 @@ func BenchmarkNativeCallWithIntVariadic3(b *testing.B) { require.NoError(b, err) for i := 0; i < b.N; i++ { - _, err := vm.Run(s) + _, err = vm.Run(s) require.NoError(b, err) } } @@ -258,7 +258,7 @@ func BenchmarkNativeCallWithIntVariadic10(b *testing.B) { require.NoError(b, err) for i := 0; i < b.N; i++ { - _, err := vm.Run(s) + _, err = vm.Run(s) require.NoError(b, err) } } @@ -272,7 +272,7 @@ func BenchmarkNativeCallWithIntArray0(b *testing.B) { require.NoError(b, err) for i := 0; i < b.N; i++ { - _, err := vm.Run(s) + _, err = vm.Run(s) require.NoError(b, err) } } @@ -286,7 +286,7 @@ func BenchmarkNativeCallWithIntArray1(b *testing.B) { require.NoError(b, err) for i := 0; i < b.N; i++ { - _, err := vm.Run(s) + _, err = vm.Run(s) require.NoError(b, err) } } @@ -300,7 +300,7 @@ func BenchmarkNativeCallWithIntArray3(b *testing.B) { require.NoError(b, err) for i := 0; i < b.N; i++ { - _, err := vm.Run(s) + _, err = vm.Run(s) require.NoError(b, err) } } @@ -314,7 +314,7 @@ func BenchmarkNativeCallWithIntArray10(b *testing.B) { require.NoError(b, err) for i := 0; i < b.N; i++ { - _, err := vm.Run(s) + _, err = vm.Run(s) require.NoError(b, err) } } @@ -328,7 +328,7 @@ func BenchmarkNativeCallWithIntVariadicArray0(b *testing.B) { require.NoError(b, err) for i := 0; i < b.N; i++ { - _, err := vm.Run(s) + _, err = vm.Run(s) require.NoError(b, err) } } @@ -342,7 +342,7 @@ func BenchmarkNativeCallWithIntVariadicArray1(b *testing.B) { require.NoError(b, err) for i := 0; i < b.N; i++ { - _, err := vm.Run(s) + _, err = vm.Run(s) require.NoError(b, err) } } @@ -356,7 +356,7 @@ func BenchmarkNativeCallWithIntVariadicArray3(b *testing.B) { require.NoError(b, err) for i := 0; i < b.N; i++ { - _, err := vm.Run(s) + _, err = vm.Run(s) require.NoError(b, err) } } @@ -370,7 +370,7 @@ func BenchmarkNativeCallWithIntVariadicArray10(b *testing.B) { require.NoError(b, err) for i := 0; i < b.N; i++ { - _, err := vm.Run(s) + _, err = vm.Run(s) require.NoError(b, err) } } @@ -384,7 +384,7 @@ func BenchmarkNativeCallWithStringIntVariadic0(b *testing.B) { require.NoError(b, err) for i := 0; i < b.N; i++ { - _, err := vm.Run(s) + _, err = vm.Run(s) require.NoError(b, err) } } @@ -398,7 +398,7 @@ func BenchmarkNativeCallWithStringIntVariadic1(b *testing.B) { require.NoError(b, err) for i := 0; i < b.N; i++ { - _, err := vm.Run(s) + _, err = vm.Run(s) require.NoError(b, err) } } @@ -412,7 +412,7 @@ func BenchmarkNativeCallWithStringIntVariadic3(b *testing.B) { require.NoError(b, err) for i := 0; i < b.N; i++ { - _, err := vm.Run(s) + _, err = vm.Run(s) require.NoError(b, err) } } @@ -426,7 +426,7 @@ func BenchmarkNativeCallWithStringIntVariadic10(b *testing.B) { require.NoError(b, err) for i := 0; i < b.N; i++ { - _, err := vm.Run(s) + _, err = vm.Run(s) require.NoError(b, err) } } @@ -440,7 +440,7 @@ func BenchmarkNativeCallWithStringIntVariadicArray0(b *testing.B) { require.NoError(b, err) for i := 0; i < b.N; i++ { - _, err := vm.Run(s) + _, err = vm.Run(s) require.NoError(b, err) } } @@ -454,7 +454,7 @@ func BenchmarkNativeCallWithStringIntVariadicArray1(b *testing.B) { require.NoError(b, err) for i := 0; i < b.N; i++ { - _, err := vm.Run(s) + _, err = vm.Run(s) require.NoError(b, err) } } @@ -468,7 +468,7 @@ func BenchmarkNativeCallWithStringIntVariadicArray3(b *testing.B) { require.NoError(b, err) for i := 0; i < b.N; i++ { - _, err := vm.Run(s) + _, err = vm.Run(s) require.NoError(b, err) } } @@ -482,7 +482,7 @@ func BenchmarkNativeCallWithStringIntVariadicArray10(b *testing.B) { require.NoError(b, err) for i := 0; i < b.N; i++ { - _, err := vm.Run(s) + _, err = vm.Run(s) require.NoError(b, err) } } @@ -496,7 +496,7 @@ func BenchmarkNativeCallWithMap(b *testing.B) { require.NoError(b, err) for i := 0; i < b.N; i++ { - _, err := vm.Run(s) + _, err = vm.Run(s) require.NoError(b, err) } } @@ -510,7 +510,7 @@ func BenchmarkNativeCallWithMapVariadic(b *testing.B) { require.NoError(b, err) for i := 0; i < b.N; i++ { - _, err := vm.Run(s) + _, err = vm.Run(s) require.NoError(b, err) } } @@ -524,7 +524,7 @@ func BenchmarkNativeCallWithMapVariadicArray(b *testing.B) { require.NoError(b, err) for i := 0; i < b.N; i++ { - _, err := vm.Run(s) + _, err = vm.Run(s) require.NoError(b, err) } } @@ -538,7 +538,7 @@ func BenchmarkNativeCallWithFunction(b *testing.B) { require.NoError(b, err) for i := 0; i < b.N; i++ { - _, err := vm.Run(s) + _, err = vm.Run(s) require.NoError(b, err) } } @@ -552,7 +552,7 @@ func BenchmarkNativeCallWithFunctionInt(b *testing.B) { require.NoError(b, err) for i := 0; i < b.N; i++ { - _, err := vm.Run(s) + _, err = vm.Run(s) require.NoError(b, err) } } @@ -566,7 +566,7 @@ func BenchmarkNativeCallWithFunctionString(b *testing.B) { require.NoError(b, err) for i := 0; i < b.N; i++ { - _, err := vm.Run(s) + _, err = vm.Run(s) require.NoError(b, err) } } @@ -588,7 +588,7 @@ func TestNativeCallWithString(t *testing.T) { s, err := vm.Compile("test.js", `x("zzz")`) require.NoError(t, err) - if _, err := vm.Run(s); err != nil { + if _, err = vm.Run(s); err != nil { t.Logf("err should have been nil; was %s\n", err.Error()) t.Fail() } @@ -615,7 +615,7 @@ func TestNativeCallWithFloat32(t *testing.T) { s, err := vm.Compile("test.js", `x(1.1)`) require.NoError(t, err) - if _, err := vm.Run(s); err != nil { + if _, err = vm.Run(s); err != nil { t.Logf("err should have been nil; was %s\n", err.Error()) t.Fail() } @@ -642,7 +642,7 @@ func TestNativeCallWithFloat64(t *testing.T) { s, err := vm.Compile("test.js", `x(1.1)`) require.NoError(t, err) - if _, err := vm.Run(s); err != nil { + if _, err = vm.Run(s); err != nil { t.Logf("err should have been nil; was %s\n", err.Error()) t.Fail() } @@ -669,7 +669,7 @@ func TestNativeCallWithInt(t *testing.T) { s, err := vm.Compile("test.js", `x(1)`) require.NoError(t, err) - if _, err := vm.Run(s); err != nil { + if _, err = vm.Run(s); err != nil { t.Logf("err should have been nil; was %s\n", err.Error()) t.Fail() } @@ -696,7 +696,7 @@ func TestNativeCallWithUint(t *testing.T) { s, err := vm.Compile("test.js", `x(1)`) require.NoError(t, err) - if _, err := vm.Run(s); err != nil { + if _, err = vm.Run(s); err != nil { t.Logf("err should have been nil; was %s\n", err.Error()) t.Fail() } @@ -723,7 +723,7 @@ func TestNativeCallWithInt8(t *testing.T) { s, err := vm.Compile("test.js", `x(1)`) require.NoError(t, err) - if _, err := vm.Run(s); err != nil { + if _, err = vm.Run(s); err != nil { t.Logf("err should have been nil; was %s\n", err.Error()) t.Fail() } @@ -750,7 +750,7 @@ func TestNativeCallWithUint8(t *testing.T) { s, err := vm.Compile("test.js", `x(1)`) require.NoError(t, err) - if _, err := vm.Run(s); err != nil { + if _, err = vm.Run(s); err != nil { t.Logf("err should have been nil; was %s\n", err.Error()) t.Fail() } @@ -777,7 +777,7 @@ func TestNativeCallWithInt16(t *testing.T) { s, err := vm.Compile("test.js", `x(1)`) require.NoError(t, err) - if _, err := vm.Run(s); err != nil { + if _, err = vm.Run(s); err != nil { t.Logf("err should have been nil; was %s\n", err.Error()) t.Fail() } @@ -804,7 +804,7 @@ func TestNativeCallWithUint16(t *testing.T) { s, err := vm.Compile("test.js", `x(1)`) require.NoError(t, err) - if _, err := vm.Run(s); err != nil { + if _, err = vm.Run(s); err != nil { t.Logf("err should have been nil; was %s\n", err.Error()) t.Fail() } @@ -831,7 +831,7 @@ func TestNativeCallWithInt32(t *testing.T) { s, err := vm.Compile("test.js", `x(1)`) require.NoError(t, err) - if _, err := vm.Run(s); err != nil { + if _, err = vm.Run(s); err != nil { t.Logf("err should have been nil; was %s\n", err.Error()) t.Fail() } @@ -858,7 +858,7 @@ func TestNativeCallWithUint32(t *testing.T) { s, err := vm.Compile("test.js", `x(1)`) require.NoError(t, err) - if _, err := vm.Run(s); err != nil { + if _, err = vm.Run(s); err != nil { t.Logf("err should have been nil; was %s\n", err.Error()) t.Fail() } @@ -885,7 +885,7 @@ func TestNativeCallWithInt64(t *testing.T) { s, err := vm.Compile("test.js", `x(1)`) require.NoError(t, err) - if _, err := vm.Run(s); err != nil { + if _, err = vm.Run(s); err != nil { t.Logf("err should have been nil; was %s\n", err.Error()) t.Fail() } @@ -912,7 +912,7 @@ func TestNativeCallWithUint64(t *testing.T) { s, err := vm.Compile("test.js", `x(1)`) require.NoError(t, err) - if _, err := vm.Run(s); err != nil { + if _, err = vm.Run(s); err != nil { t.Logf("err should have been nil; was %s\n", err.Error()) t.Fail() } @@ -939,7 +939,7 @@ func TestNativeCallWithStringInt(t *testing.T) { s, err := vm.Compile("test.js", `x("zzz", 1)`) require.NoError(t, err) - if _, err := vm.Run(s); err != nil { + if _, err = vm.Run(s); err != nil { t.Logf("err should have been nil; was %s\n", err.Error()) t.Fail() } @@ -966,7 +966,7 @@ func TestNativeCallWithIntVariadic0(t *testing.T) { s, err := vm.Compile("test.js", `x()`) require.NoError(t, err) - if _, err := vm.Run(s); err != nil { + if _, err = vm.Run(s); err != nil { t.Logf("err should have been nil; was %s\n", err.Error()) t.Fail() } @@ -993,7 +993,7 @@ func TestNativeCallWithIntVariadic1(t *testing.T) { s, err := vm.Compile("test.js", `x(1)`) require.NoError(t, err) - if _, err := vm.Run(s); err != nil { + if _, err = vm.Run(s); err != nil { t.Logf("err should have been nil; was %s\n", err.Error()) t.Fail() } @@ -1020,7 +1020,7 @@ func TestNativeCallWithIntVariadic3(t *testing.T) { s, err := vm.Compile("test.js", `x(1, 2, 3)`) require.NoError(t, err) - if _, err := vm.Run(s); err != nil { + if _, err = vm.Run(s); err != nil { t.Logf("err should have been nil; was %s\n", err.Error()) t.Fail() } @@ -1047,7 +1047,7 @@ func TestNativeCallWithIntVariadic10(t *testing.T) { s, err := vm.Compile("test.js", `x(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)`) require.NoError(t, err) - if _, err := vm.Run(s); err != nil { + if _, err = vm.Run(s); err != nil { t.Logf("err should have been nil; was %s\n", err.Error()) t.Fail() } @@ -1074,7 +1074,7 @@ func TestNativeCallWithIntArray0(t *testing.T) { s, err := vm.Compile("test.js", `x([])`) require.NoError(t, err) - if _, err := vm.Run(s); err != nil { + if _, err = vm.Run(s); err != nil { t.Logf("err should have been nil; was %s\n", err.Error()) t.Fail() } @@ -1101,7 +1101,7 @@ func TestNativeCallWithIntArray1(t *testing.T) { s, err := vm.Compile("test.js", `x([1])`) require.NoError(t, err) - if _, err := vm.Run(s); err != nil { + if _, err = vm.Run(s); err != nil { t.Logf("err should have been nil; was %s\n", err.Error()) t.Fail() } @@ -1128,7 +1128,7 @@ func TestNativeCallWithIntArray3(t *testing.T) { s, err := vm.Compile("test.js", `x([1, 2, 3])`) require.NoError(t, err) - if _, err := vm.Run(s); err != nil { + if _, err = vm.Run(s); err != nil { t.Logf("err should have been nil; was %s\n", err.Error()) t.Fail() } @@ -1155,7 +1155,7 @@ func TestNativeCallWithIntArray10(t *testing.T) { s, err := vm.Compile("test.js", `x([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])`) require.NoError(t, err) - if _, err := vm.Run(s); err != nil { + if _, err = vm.Run(s); err != nil { t.Logf("err should have been nil; was %s\n", err.Error()) t.Fail() } @@ -1182,7 +1182,7 @@ func TestNativeCallWithIntVariadicArray0(t *testing.T) { s, err := vm.Compile("test.js", `x([])`) require.NoError(t, err) - if _, err := vm.Run(s); err != nil { + if _, err = vm.Run(s); err != nil { t.Logf("err should have been nil; was %s\n", err.Error()) t.Fail() } @@ -1209,7 +1209,7 @@ func TestNativeCallWithIntVariadicArray1(t *testing.T) { s, err := vm.Compile("test.js", `x([1])`) require.NoError(t, err) - if _, err := vm.Run(s); err != nil { + if _, err = vm.Run(s); err != nil { t.Logf("err should have been nil; was %s\n", err.Error()) t.Fail() } @@ -1236,7 +1236,7 @@ func TestNativeCallWithIntVariadicArray3(t *testing.T) { s, err := vm.Compile("test.js", `x([1, 2, 3])`) require.NoError(t, err) - if _, err := vm.Run(s); err != nil { + if _, err = vm.Run(s); err != nil { t.Logf("err should have been nil; was %s\n", err.Error()) t.Fail() } @@ -1263,7 +1263,7 @@ func TestNativeCallWithIntVariadicArray10(t *testing.T) { s, err := vm.Compile("test.js", `x([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])`) require.NoError(t, err) - if _, err := vm.Run(s); err != nil { + if _, err = vm.Run(s); err != nil { t.Logf("err should have been nil; was %s\n", err.Error()) t.Fail() } @@ -1290,7 +1290,7 @@ func TestNativeCallWithStringIntVariadic0(t *testing.T) { s, err := vm.Compile("test.js", `x("a")`) require.NoError(t, err) - if _, err := vm.Run(s); err != nil { + if _, err = vm.Run(s); err != nil { t.Logf("err should have been nil; was %s\n", err.Error()) t.Fail() } @@ -1317,7 +1317,7 @@ func TestNativeCallWithStringIntVariadic1(t *testing.T) { s, err := vm.Compile("test.js", `x("a", 1)`) require.NoError(t, err) - if _, err := vm.Run(s); err != nil { + if _, err = vm.Run(s); err != nil { t.Logf("err should have been nil; was %s\n", err.Error()) t.Fail() } @@ -1344,7 +1344,7 @@ func TestNativeCallWithStringIntVariadic3(t *testing.T) { s, err := vm.Compile("test.js", `x("a", 1, 2, 3)`) require.NoError(t, err) - if _, err := vm.Run(s); err != nil { + if _, err = vm.Run(s); err != nil { t.Logf("err should have been nil; was %s\n", err.Error()) t.Fail() } @@ -1371,7 +1371,7 @@ func TestNativeCallWithStringIntVariadic10(t *testing.T) { s, err := vm.Compile("test.js", `x("a", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)`) require.NoError(t, err) - if _, err := vm.Run(s); err != nil { + if _, err = vm.Run(s); err != nil { t.Logf("err should have been nil; was %s\n", err.Error()) t.Fail() } @@ -1398,7 +1398,7 @@ func TestNativeCallWithStringIntVariadicArray0(t *testing.T) { s, err := vm.Compile("test.js", `x("a", [])`) require.NoError(t, err) - if _, err := vm.Run(s); err != nil { + if _, err = vm.Run(s); err != nil { t.Logf("err should have been nil; was %s\n", err.Error()) t.Fail() } @@ -1425,7 +1425,7 @@ func TestNativeCallWithStringIntVariadicArray1(t *testing.T) { s, err := vm.Compile("test.js", `x("a", [1])`) require.NoError(t, err) - if _, err := vm.Run(s); err != nil { + if _, err = vm.Run(s); err != nil { t.Logf("err should have been nil; was %s\n", err.Error()) t.Fail() } @@ -1452,7 +1452,7 @@ func TestNativeCallWithStringIntVariadicArray3(t *testing.T) { s, err := vm.Compile("test.js", `x("a", [1, 2, 3])`) require.NoError(t, err) - if _, err := vm.Run(s); err != nil { + if _, err = vm.Run(s); err != nil { t.Logf("err should have been nil; was %s\n", err.Error()) t.Fail() } @@ -1479,7 +1479,7 @@ func TestNativeCallWithStringIntVariadicArray10(t *testing.T) { s, err := vm.Compile("test.js", `x("a", [1, 2, 3, 4, 5, 6, 7, 8, 9, 10])`) require.NoError(t, err) - if _, err := vm.Run(s); err != nil { + if _, err = vm.Run(s); err != nil { t.Logf("err should have been nil; was %s\n", err.Error()) t.Fail() } @@ -1506,7 +1506,7 @@ func TestNativeCallWithMap(t *testing.T) { s, err := vm.Compile("test.js", `x({a: "b", c: "d"})`) require.NoError(t, err) - if _, err := vm.Run(s); err != nil { + if _, err = vm.Run(s); err != nil { t.Logf("err should have been nil; was %s\n", err.Error()) t.Fail() } @@ -1533,7 +1533,7 @@ func TestNativeCallWithMapVariadic(t *testing.T) { s, err := vm.Compile("test.js", `x({a: "b", c: "d"}, {w: "x", y: "z"})`) require.NoError(t, err) - if _, err := vm.Run(s); err != nil { + if _, err = vm.Run(s); err != nil { t.Logf("err should have been nil; was %s\n", err.Error()) t.Fail() } @@ -1560,7 +1560,7 @@ func TestNativeCallWithMapVariadicArray(t *testing.T) { s, err := vm.Compile("test.js", `x([{a: "b", c: "d"}, {w: "x", y: "z"}])`) require.NoError(t, err) - if _, err := vm.Run(s); err != nil { + if _, err = vm.Run(s); err != nil { t.Logf("err should have been nil; was %s\n", err.Error()) t.Fail() } @@ -1587,7 +1587,7 @@ func TestNativeCallWithFunctionVoidBool(t *testing.T) { s, err := vm.Compile("test.js", `x(function() { return true; })`) require.NoError(t, err) - if _, err := vm.Run(s); err != nil { + if _, err = vm.Run(s); err != nil { t.Logf("err should have been nil; was %s\n", err.Error()) t.Fail() } @@ -1614,7 +1614,7 @@ func TestNativeCallWithFunctionIntInt(t *testing.T) { s, err := vm.Compile("test.js", `x(function(n) { return n; })`) require.NoError(t, err) - if _, err := vm.Run(s); err != nil { + if _, err = vm.Run(s); err != nil { t.Logf("err should have been nil; was %s\n", err.Error()) t.Fail() } @@ -1641,7 +1641,7 @@ func TestNativeCallWithFunctionStringString(t *testing.T) { s, err := vm.Compile("test.js", `x(function(n) { return n; })`) require.NoError(t, err) - if _, err := vm.Run(s); err != nil { + if _, err = vm.Run(s); err != nil { t.Logf("err should have been nil; was %s\n", err.Error()) t.Fail() } @@ -1702,7 +1702,7 @@ func TestNativeCallMethodWithStruct(t *testing.T) { s, err := vm.Compile("test.js", `t(x.CallWithStruct(x.MakeStruct("b")))`) require.NoError(t, err) - if _, err := vm.Run(s); err != nil { + if _, err = vm.Run(s); err != nil { t.Logf("err should have been nil; was %s\n", err.Error()) t.Fail() } @@ -1731,7 +1731,7 @@ func TestNativeCallPointerMethodWithStruct(t *testing.T) { s, err := vm.Compile("test.js", `t(x.CallPointerWithStruct(x.MakeStruct("b")))`) require.NoError(t, err) - if _, err := vm.Run(s); err != nil { + if _, err = vm.Run(s); err != nil { t.Logf("err should have been nil; was %s\n", err.Error()) t.Fail() } @@ -1760,7 +1760,7 @@ func TestNativeCallMethodWithStructPointer(t *testing.T) { s, err := vm.Compile("test.js", `t(x.CallWithStructPointer(x.MakeStructPointer("b")))`) require.NoError(t, err) - if _, err := vm.Run(s); err != nil { + if _, err = vm.Run(s); err != nil { t.Logf("err should have been nil; was %s\n", err.Error()) t.Fail() } @@ -1789,7 +1789,7 @@ func TestNativeCallPointerMethodWithStructPointer(t *testing.T) { s, err := vm.Compile("test.js", `t(x.CallPointerWithStructPointer(x.MakeStructPointer("b")))`) require.NoError(t, err) - if _, err := vm.Run(s); err != nil { + if _, err = vm.Run(s); err != nil { t.Logf("err should have been nil; was %s\n", err.Error()) t.Fail() } diff --git a/cmpl_evaluate_statement.go b/cmpl_evaluate_statement.go index e0a05ee5..724120ac 100644 --- a/cmpl_evaluate_statement.go +++ b/cmpl_evaluate_statement.go @@ -134,7 +134,7 @@ func (rt *runtime) cmplEvaluateNodeStatementList(list []nodeStatement) Value { } func (rt *runtime) cmplEvaluateNodeDoWhileStatement(node *nodeDoWhileStatement) Value { - labels := append(rt.labels, "") //nolint: gocritic + labels := append(rt.labels, "") //nolint:gocritic rt.labels = nil test := node.test @@ -169,7 +169,7 @@ resultBreak: } func (rt *runtime) cmplEvaluateNodeForInStatement(node *nodeForInStatement) Value { - labels := append(rt.labels, "") //nolint: gocritic + labels := append(rt.labels, "") //nolint:gocritic rt.labels = nil source := rt.cmplEvaluateNodeExpression(node.source) @@ -231,7 +231,7 @@ func (rt *runtime) cmplEvaluateNodeForInStatement(node *nodeForInStatement) Valu } func (rt *runtime) cmplEvaluateNodeForStatement(node *nodeForStatement) Value { - labels := append(rt.labels, "") //nolint: gocritic + labels := append(rt.labels, "") //nolint:gocritic rt.labels = nil initializer := node.initializer @@ -304,7 +304,7 @@ func (rt *runtime) cmplEvaluateNodeIfStatement(node *nodeIfStatement) Value { } func (rt *runtime) cmplEvaluateNodeSwitchStatement(node *nodeSwitchStatement) Value { - labels := append(rt.labels, "") //nolint: gocritic + labels := append(rt.labels, "") //nolint:gocritic rt.labels = nil discriminantResult := rt.cmplEvaluateNodeExpression(node.discriminant) @@ -384,7 +384,7 @@ func (rt *runtime) cmplEvaluateNodeTryStatement(node *nodeTryStatement) Value { func (rt *runtime) cmplEvaluateModeWhileStatement(node *nodeWhileStatement) Value { test := node.test body := node.body - labels := append(rt.labels, "") //nolint: gocritic + labels := append(rt.labels, "") //nolint:gocritic rt.labels = nil result := emptyValue diff --git a/cmpl_parse.go b/cmpl_parse.go index 3844ceca..d3fe8da5 100644 --- a/cmpl_parse.go +++ b/cmpl_parse.go @@ -391,12 +391,10 @@ func (cmpl *compiler) parse() *nodeProgram { } type nodeProgram struct { - body []nodeStatement - + file *file.File + body []nodeStatement varList []string functionList []*nodeFunctionLiteral - - file *file.File } type node interface{} @@ -412,22 +410,22 @@ type ( } nodeAssignExpression struct { - operator token.Token left nodeExpression right nodeExpression + operator token.Token } nodeBinaryExpression struct { - operator token.Token left nodeExpression right nodeExpression + operator token.Token comparison bool } nodeBracketExpression struct { - idx file.Idx left nodeExpression member nodeExpression + idx file.Idx } nodeCallExpression struct { @@ -442,24 +440,24 @@ type ( } nodeDotExpression struct { - idx file.Idx left nodeExpression identifier string + idx file.Idx } nodeFunctionLiteral struct { - name string body nodeStatement + file *file.File + name string source string parameterList []string varList []string functionList []*nodeFunctionLiteral - file *file.File } nodeIdentifier struct { - idx file.Idx name string + idx file.Idx } nodeLiteral struct { @@ -476,9 +474,9 @@ type ( } nodeProperty struct { + value nodeExpression key string kind string - value nodeExpression } nodeRegExpLiteral struct { @@ -493,15 +491,15 @@ type ( nodeThisExpression struct{} nodeUnaryExpression struct { - operator token.Token operand nodeExpression + operator token.Token postfix bool } nodeVariableExpression struct { - idx file.Idx - name string initializer nodeExpression + name string + idx file.Idx } ) @@ -516,8 +514,8 @@ type ( } nodeBranchStatement struct { - branch token.Token label string + branch token.Token } nodeCaseStatement struct { @@ -526,8 +524,8 @@ type ( } nodeCatchStatement struct { - parameter string body nodeStatement + parameter string } nodeDebuggerStatement struct{} @@ -563,8 +561,8 @@ type ( } nodeLabelledStatement struct { - label string statement nodeStatement + label string } nodeReturnStatement struct { @@ -573,8 +571,8 @@ type ( nodeSwitchStatement struct { discriminant nodeExpression - defaultIdx int body []*nodeCaseStatement + defaultIdx int } nodeThrowStatement struct { diff --git a/date_test.go b/date_test.go index 46ce362f..755cfd94 100644 --- a/date_test.go +++ b/date_test.go @@ -30,6 +30,10 @@ func TestDate(t *testing.T) { test(`Date`, "function Date() { [native code] }") test(`new Date(0).toUTCString()`, "Thu, 01 Jan 1970 00:00:00 GMT") test(`new Date(0).toGMTString()`, "Thu, 01 Jan 1970 00:00:00 GMT") + test(`new Date('2023').toGMTString()`, "Sun, 01 Jan 2023 00:00:00 GMT") + test(`new Date('2023/02').toGMTString()`, "Wed, 01 Feb 2023 00:00:00 GMT") + test(`new Date('2023/02/23').toGMTString()`, "Thu, 23 Feb 2023 00:00:00 GMT") + test(`new Date('2023/02/23 11:23:57').toGMTString()`, "Thu, 23 Feb 2023 11:23:57 GMT") if false { // TODO toLocale{Date,Time}String test(`new Date(0).toLocaleString()`, "") diff --git a/documentation_test.go b/documentation_test.go index a7f8341e..b3a6d8a9 100644 --- a/documentation_test.go +++ b/documentation_test.go @@ -5,7 +5,7 @@ import ( "os" ) -func ExampleSynopsis() { //nolint: govet +func ExampleSynopsis() { //nolint:govet vm := New() _, err := vm.Run(` abc = 2 + 2; @@ -116,7 +116,7 @@ func ExampleSynopsis() { //nolint: govet // 4 } -func ExampleConsole() { //nolint: govet +func ExampleConsole() { //nolint:govet vm := New() console := map[string]interface{}{ "log": func(call FunctionCall) Value { diff --git a/error.go b/error.go index 7f1bbafc..8c8b5d52 100644 --- a/error.go +++ b/error.go @@ -50,13 +50,13 @@ func (e ottoError) formatWithStack() string { } type frame struct { - native bool + fn interface{} + file *file.File nativeFile string + callee string nativeLine int - file *file.File offset int - callee string - fn interface{} + native bool } var nativeFrame = frame{} diff --git a/error_test.go b/error_test.go index 27b627f3..3811fcb7 100644 --- a/error_test.go +++ b/error_test.go @@ -1,7 +1,6 @@ package otto import ( - "errors" "testing" "github.com/stretchr/testify/require" @@ -67,7 +66,7 @@ func Test_catchPanic(t *testing.T) { func asError(t *testing.T, err error) *Error { t.Helper() var oerr *Error - require.True(t, errors.As(err, &oerr)) + require.ErrorAs(t, err, &oerr) return oerr } @@ -239,7 +238,7 @@ func TestErrorContext(t *testing.T) { { f, _ := vm.Get("B") - _, err := f.Call(UndefinedValue()) + _, err = f.Call(UndefinedValue()) err1 := asError(t, err) is(err1.message, "test") is(len(err1.trace), 2) @@ -249,7 +248,7 @@ func TestErrorContext(t *testing.T) { { f, _ := vm.Get("C") - _, err := f.Call(UndefinedValue()) + _, err = f.Call(UndefinedValue()) err1 := asError(t, err) is(err1.message, `Cannot access member "prop" of null`) is(len(err1.trace), 1) @@ -447,3 +446,20 @@ func TestErrorStackProperty(t *testing.T) { is(v.String(), "TypeError: uh oh\n at A (test.js:2:29)\n at B (test.js:3:26)\n at C (test.js:4:26)\n at test.js:8:10\n") }) } + +func TestErrorMessageContainingFormatCharacters(t *testing.T) { + tt(t, func() { + test, tester := test() + + tester.Set("F", func(call FunctionCall) Value { + return call.Otto.MakeCustomError(call.ArgumentList[0].String(), call.ArgumentList[1].String()) + }) + + test("Error('literal percent-s: %s')", "Error: literal percent-s: %s") + test("new Error('literal percent-s: %s')", "Error: literal percent-s: %s") + test("F('TestError', 'literal percent-s: %s')", "TestError: literal percent-s: %s") + test("raise: throw Error('literal percent-s: %s')", "Error: literal percent-s: %s") + test("raise: throw new Error('literal percent-s: %s')", "Error: literal percent-s: %s") + test("raise: throw F('TestError', 'literal percent-s: %s')", "TestError: literal percent-s: %s") + }) +} diff --git a/evaluate.go b/evaluate.go index 2108b7c3..269b9dd0 100644 --- a/evaluate.go +++ b/evaluate.go @@ -8,7 +8,7 @@ import ( "github.com/robertkrimen/otto/token" ) -func (rt *runtime) evaluateMultiply(left float64, right float64) Value { //nolint: unused +func (rt *runtime) evaluateMultiply(left float64, right float64) Value { //nolint:unused // TODO 11.5.1 return Value{} } @@ -44,7 +44,7 @@ func (rt *runtime) evaluateDivide(left float64, right float64) Value { return float64Value(left / right) } -func (rt *runtime) evaluateModulo(left float64, right float64) Value { //nolint: unused +func (rt *runtime) evaluateModulo(left float64, right float64) Value { //nolint:unused // TODO 11.5.3 return Value{} } @@ -255,7 +255,7 @@ func (rt *runtime) calculateComparison(comparator token.Token, left Value, right panic(fmt.Sprintf("unknown types for equal: %v ==? %v", x, y)) } default: - panic(fmt.Sprintf("unknown comparator %s", comparator.String())) + panic("unknown comparator " + comparator.String()) } if kindEqualKind { diff --git a/file/file.go b/file/file.go index 3dda8938..99dd6070 100644 --- a/file/file.go +++ b/file/file.go @@ -49,9 +49,9 @@ func (p *Position) String() string { } // A FileSet represents a set of source files. -type FileSet struct { //nolint: golint - files []*File +type FileSet struct { last *File + files []*File } // AddFile adds a new file with the given filename and src. @@ -99,10 +99,10 @@ func (fs *FileSet) Position(idx Idx) *Position { // File represents a file to parse. type File struct { + sm *sourcemap.Consumer name string src string - base int // This will always be 1 or greater - sm *sourcemap.Consumer + base int } // NewFile returns a new file with the given filename, src and base. diff --git a/function_stack_test.go b/function_stack_test.go index db5eb5c4..092fa81e 100644 --- a/function_stack_test.go +++ b/function_stack_test.go @@ -27,7 +27,7 @@ func TestFunction_stack(t *testing.T) { } err = vm.Set("A", func(c FunctionCall) Value { - _, err := c.Argument(0).Call(UndefinedValue()) + _, err = c.Argument(0).Call(UndefinedValue()) require.NoError(t, err) return UndefinedValue() }) diff --git a/functional_benchmark_test.go b/functional_benchmark_test.go index 58ccbfa5..5967d74e 100644 --- a/functional_benchmark_test.go +++ b/functional_benchmark_test.go @@ -1,8 +1,8 @@ package otto import ( - "fmt" "math/rand" + "strconv" "strings" "testing" @@ -124,7 +124,7 @@ func benchmarkGoSliceSort(b *testing.B, size int, sortFuncCall string, sortCode // generate arbitrary slice of 'size' testSlice := make([]int, size) for i := 0; i < size; i++ { - testSlice[i] = rand.Int() //nolint: gosec + testSlice[i] = rand.Int() //nolint:gosec } vm := New() @@ -147,7 +147,7 @@ func benchmarkJsArraySort(b *testing.B, size int, sortFuncCall string, sortCode // generate arbitrary slice of 'size' testSlice := make([]string, size) for i := range testSlice { - testSlice[i] = fmt.Sprintf("%d", rand.Int()) //nolint: gosec + testSlice[i] = strconv.Itoa(rand.Int()) //nolint:gosec } jsArrayString := "[" + strings.Join(testSlice, ",") + "]" diff --git a/global.go b/global.go index e15dd832..a46e6bcf 100644 --- a/global.go +++ b/global.go @@ -79,7 +79,7 @@ func (o *object) primitiveValue() Value { return Value{} } -func (o *object) hasPrimitive() bool { //nolint: unused +func (o *object) hasPrimitive() bool { //nolint:unused switch o.value.(type) { case Value, stringObjecter: return true diff --git a/inline.go b/inline.go index cb3dc187..30e00bee 100644 --- a/inline.go +++ b/inline.go @@ -902,6 +902,43 @@ func (rt *runtime) newContext() { }, }, }, + "values": { + mode: 0o101, + value: Value{ + kind: valueObject, + value: &object{ + runtime: rt, + class: classFunctionName, + objectClass: classObject, + prototype: rt.global.FunctionPrototype, + extensible: true, + property: map[string]property{ + propertyLength: { + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + propertyName: { + mode: 0, + value: Value{ + kind: valueString, + value: "values", + }, + }, + }, + propertyOrder: []string{ + propertyLength, + propertyName, + }, + value: nativeFunctionObject{ + name: "values", + call: builtinObjectValues, + }, + }, + }, + }, "getOwnPropertyNames": { mode: 0o101, value: Value{ @@ -955,6 +992,7 @@ func (rt *runtime) newContext() { "isFrozen", "freeze", "keys", + "values", "getOwnPropertyNames", }, } @@ -2410,6 +2448,43 @@ func (rt *runtime) newContext() { }, }, }, + "startsWith": { + mode: 0o101, + value: Value{ + kind: valueObject, + value: &object{ + runtime: rt, + class: classFunctionName, + objectClass: classObject, + prototype: rt.global.FunctionPrototype, + extensible: true, + property: map[string]property{ + propertyLength: { + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + propertyName: { + mode: 0, + value: Value{ + kind: valueString, + value: "startsWith", + }, + }, + }, + propertyOrder: []string{ + propertyLength, + propertyName, + }, + value: nativeFunctionObject{ + name: "startsWith", + call: builtinStringStartsWith, + }, + }, + }, + }, methodToString: { mode: 0o101, value: Value{ @@ -2558,6 +2633,80 @@ func (rt *runtime) newContext() { }, }, }, + "trimStart": { + mode: 0o101, + value: Value{ + kind: valueObject, + value: &object{ + runtime: rt, + class: classFunctionName, + objectClass: classObject, + prototype: rt.global.FunctionPrototype, + extensible: true, + property: map[string]property{ + propertyLength: { + mode: 0, + value: Value{ + kind: valueNumber, + value: 0, + }, + }, + propertyName: { + mode: 0, + value: Value{ + kind: valueString, + value: "trimStart", + }, + }, + }, + propertyOrder: []string{ + propertyLength, + propertyName, + }, + value: nativeFunctionObject{ + name: "trimStart", + call: builtinStringTrimStart, + }, + }, + }, + }, + "trimEnd": { + mode: 0o101, + value: Value{ + kind: valueObject, + value: &object{ + runtime: rt, + class: classFunctionName, + objectClass: classObject, + prototype: rt.global.FunctionPrototype, + extensible: true, + property: map[string]property{ + propertyLength: { + mode: 0, + value: Value{ + kind: valueNumber, + value: 0, + }, + }, + propertyName: { + mode: 0, + value: Value{ + kind: valueString, + value: "trimEnd", + }, + }, + }, + propertyOrder: []string{ + propertyLength, + propertyName, + }, + value: nativeFunctionObject{ + name: "trimEnd", + call: builtinStringTrimEnd, + }, + }, + }, + }, "toLocaleLowerCase": { mode: 0o101, value: Value{ @@ -2760,10 +2909,13 @@ func (rt *runtime) newContext() { "split", "substr", "substring", + "startsWith", methodToString, "trim", "trimLeft", "trimRight", + "trimStart", + "trimEnd", "toLocaleLowerCase", "toLocaleUpperCase", "toLowerCase", @@ -3433,7 +3585,7 @@ func (rt *runtime) newContext() { }, }, }, - "asin": { + "acosh": { mode: 0o101, value: Value{ kind: valueObject, @@ -3455,7 +3607,7 @@ func (rt *runtime) newContext() { mode: 0, value: Value{ kind: valueString, - value: "asin", + value: "acosh", }, }, }, @@ -3464,13 +3616,13 @@ func (rt *runtime) newContext() { propertyName, }, value: nativeFunctionObject{ - name: "asin", - call: builtinMathAsin, + name: "acosh", + call: builtinMathAcosh, }, }, }, }, - "atan": { + "asin": { mode: 0o101, value: Value{ kind: valueObject, @@ -3492,7 +3644,7 @@ func (rt *runtime) newContext() { mode: 0, value: Value{ kind: valueString, - value: "atan", + value: "asin", }, }, }, @@ -3501,13 +3653,13 @@ func (rt *runtime) newContext() { propertyName, }, value: nativeFunctionObject{ - name: "atan", - call: builtinMathAtan, + name: "asin", + call: builtinMathAsin, }, }, }, }, - "atan2": { + "asinh": { mode: 0o101, value: Value{ kind: valueObject, @@ -3529,7 +3681,7 @@ func (rt *runtime) newContext() { mode: 0, value: Value{ kind: valueString, - value: "atan2", + value: "asinh", }, }, }, @@ -3538,13 +3690,13 @@ func (rt *runtime) newContext() { propertyName, }, value: nativeFunctionObject{ - name: "atan2", - call: builtinMathAtan2, + name: "asinh", + call: builtinMathAsinh, }, }, }, }, - "ceil": { + "atan": { mode: 0o101, value: Value{ kind: valueObject, @@ -3566,7 +3718,7 @@ func (rt *runtime) newContext() { mode: 0, value: Value{ kind: valueString, - value: "ceil", + value: "atan", }, }, }, @@ -3575,13 +3727,13 @@ func (rt *runtime) newContext() { propertyName, }, value: nativeFunctionObject{ - name: "ceil", - call: builtinMathCeil, + name: "atan", + call: builtinMathAtan, }, }, }, }, - "cos": { + "atanh": { mode: 0o101, value: Value{ kind: valueObject, @@ -3603,7 +3755,7 @@ func (rt *runtime) newContext() { mode: 0, value: Value{ kind: valueString, - value: "cos", + value: "atanh", }, }, }, @@ -3612,13 +3764,13 @@ func (rt *runtime) newContext() { propertyName, }, value: nativeFunctionObject{ - name: "cos", - call: builtinMathCos, + name: "atanh", + call: builtinMathAtanh, }, }, }, }, - "exp": { + "atan2": { mode: 0o101, value: Value{ kind: valueObject, @@ -3640,7 +3792,7 @@ func (rt *runtime) newContext() { mode: 0, value: Value{ kind: valueString, - value: "exp", + value: "atan2", }, }, }, @@ -3649,13 +3801,13 @@ func (rt *runtime) newContext() { propertyName, }, value: nativeFunctionObject{ - name: "exp", - call: builtinMathExp, + name: "atan2", + call: builtinMathAtan2, }, }, }, }, - "floor": { + "cbrt": { mode: 0o101, value: Value{ kind: valueObject, @@ -3677,7 +3829,7 @@ func (rt *runtime) newContext() { mode: 0, value: Value{ kind: valueString, - value: "floor", + value: "cbrt", }, }, }, @@ -3686,13 +3838,13 @@ func (rt *runtime) newContext() { propertyName, }, value: nativeFunctionObject{ - name: "floor", - call: builtinMathFloor, + name: "cbrt", + call: builtinMathCbrt, }, }, }, }, - "log": { + "ceil": { mode: 0o101, value: Value{ kind: valueObject, @@ -3714,7 +3866,7 @@ func (rt *runtime) newContext() { mode: 0, value: Value{ kind: valueString, - value: "log", + value: "ceil", }, }, }, @@ -3723,13 +3875,13 @@ func (rt *runtime) newContext() { propertyName, }, value: nativeFunctionObject{ - name: "log", - call: builtinMathLog, + name: "ceil", + call: builtinMathCeil, }, }, }, }, - "max": { + "cos": { mode: 0o101, value: Value{ kind: valueObject, @@ -3744,14 +3896,14 @@ func (rt *runtime) newContext() { mode: 0, value: Value{ kind: valueNumber, - value: 2, + value: 1, }, }, propertyName: { mode: 0, value: Value{ kind: valueString, - value: "max", + value: "cos", }, }, }, @@ -3760,13 +3912,13 @@ func (rt *runtime) newContext() { propertyName, }, value: nativeFunctionObject{ - name: "max", - call: builtinMathMax, + name: "cos", + call: builtinMathCos, }, }, }, }, - "min": { + "cosh": { mode: 0o101, value: Value{ kind: valueObject, @@ -3781,14 +3933,14 @@ func (rt *runtime) newContext() { mode: 0, value: Value{ kind: valueNumber, - value: 2, + value: 1, }, }, propertyName: { mode: 0, value: Value{ kind: valueString, - value: "min", + value: "cosh", }, }, }, @@ -3797,13 +3949,13 @@ func (rt *runtime) newContext() { propertyName, }, value: nativeFunctionObject{ - name: "min", - call: builtinMathMin, + name: "cosh", + call: builtinMathCosh, }, }, }, }, - "pow": { + "exp": { mode: 0o101, value: Value{ kind: valueObject, @@ -3818,14 +3970,14 @@ func (rt *runtime) newContext() { mode: 0, value: Value{ kind: valueNumber, - value: 2, + value: 1, }, }, propertyName: { mode: 0, value: Value{ kind: valueString, - value: "pow", + value: "exp", }, }, }, @@ -3834,13 +3986,13 @@ func (rt *runtime) newContext() { propertyName, }, value: nativeFunctionObject{ - name: "pow", - call: builtinMathPow, + name: "exp", + call: builtinMathExp, }, }, }, }, - "random": { + "expm1": { mode: 0o101, value: Value{ kind: valueObject, @@ -3855,14 +4007,14 @@ func (rt *runtime) newContext() { mode: 0, value: Value{ kind: valueNumber, - value: 0, + value: 1, }, }, propertyName: { mode: 0, value: Value{ kind: valueString, - value: "random", + value: "expm1", }, }, }, @@ -3871,13 +4023,13 @@ func (rt *runtime) newContext() { propertyName, }, value: nativeFunctionObject{ - name: "random", - call: builtinMathRandom, + name: "expm1", + call: builtinMathExpm1, }, }, }, }, - "round": { + "floor": { mode: 0o101, value: Value{ kind: valueObject, @@ -3899,7 +4051,7 @@ func (rt *runtime) newContext() { mode: 0, value: Value{ kind: valueString, - value: "round", + value: "floor", }, }, }, @@ -3908,13 +4060,13 @@ func (rt *runtime) newContext() { propertyName, }, value: nativeFunctionObject{ - name: "round", - call: builtinMathRound, + name: "floor", + call: builtinMathFloor, }, }, }, }, - "sin": { + "log": { mode: 0o101, value: Value{ kind: valueObject, @@ -3936,7 +4088,7 @@ func (rt *runtime) newContext() { mode: 0, value: Value{ kind: valueString, - value: "sin", + value: "log", }, }, }, @@ -3945,13 +4097,13 @@ func (rt *runtime) newContext() { propertyName, }, value: nativeFunctionObject{ - name: "sin", - call: builtinMathSin, + name: "log", + call: builtinMathLog, }, }, }, }, - "sqrt": { + "log10": { mode: 0o101, value: Value{ kind: valueObject, @@ -3973,7 +4125,7 @@ func (rt *runtime) newContext() { mode: 0, value: Value{ kind: valueString, - value: "sqrt", + value: "log10", }, }, }, @@ -3982,13 +4134,13 @@ func (rt *runtime) newContext() { propertyName, }, value: nativeFunctionObject{ - name: "sqrt", - call: builtinMathSqrt, + name: "log10", + call: builtinMathLog10, }, }, }, }, - "tan": { + "log1p": { mode: 0o101, value: Value{ kind: valueObject, @@ -4010,7 +4162,7 @@ func (rt *runtime) newContext() { mode: 0, value: Value{ kind: valueString, - value: "tan", + value: "log1p", }, }, }, @@ -4019,8 +4171,452 @@ func (rt *runtime) newContext() { propertyName, }, value: nativeFunctionObject{ - name: "tan", - call: builtinMathTan, + name: "log1p", + call: builtinMathLog1p, + }, + }, + }, + }, + "log2": { + mode: 0o101, + value: Value{ + kind: valueObject, + value: &object{ + runtime: rt, + class: classFunctionName, + objectClass: classObject, + prototype: rt.global.FunctionPrototype, + extensible: true, + property: map[string]property{ + propertyLength: { + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + propertyName: { + mode: 0, + value: Value{ + kind: valueString, + value: "log2", + }, + }, + }, + propertyOrder: []string{ + propertyLength, + propertyName, + }, + value: nativeFunctionObject{ + name: "log2", + call: builtinMathLog2, + }, + }, + }, + }, + "max": { + mode: 0o101, + value: Value{ + kind: valueObject, + value: &object{ + runtime: rt, + class: classFunctionName, + objectClass: classObject, + prototype: rt.global.FunctionPrototype, + extensible: true, + property: map[string]property{ + propertyLength: { + mode: 0, + value: Value{ + kind: valueNumber, + value: 2, + }, + }, + propertyName: { + mode: 0, + value: Value{ + kind: valueString, + value: "max", + }, + }, + }, + propertyOrder: []string{ + propertyLength, + propertyName, + }, + value: nativeFunctionObject{ + name: "max", + call: builtinMathMax, + }, + }, + }, + }, + "min": { + mode: 0o101, + value: Value{ + kind: valueObject, + value: &object{ + runtime: rt, + class: classFunctionName, + objectClass: classObject, + prototype: rt.global.FunctionPrototype, + extensible: true, + property: map[string]property{ + propertyLength: { + mode: 0, + value: Value{ + kind: valueNumber, + value: 2, + }, + }, + propertyName: { + mode: 0, + value: Value{ + kind: valueString, + value: "min", + }, + }, + }, + propertyOrder: []string{ + propertyLength, + propertyName, + }, + value: nativeFunctionObject{ + name: "min", + call: builtinMathMin, + }, + }, + }, + }, + "pow": { + mode: 0o101, + value: Value{ + kind: valueObject, + value: &object{ + runtime: rt, + class: classFunctionName, + objectClass: classObject, + prototype: rt.global.FunctionPrototype, + extensible: true, + property: map[string]property{ + propertyLength: { + mode: 0, + value: Value{ + kind: valueNumber, + value: 2, + }, + }, + propertyName: { + mode: 0, + value: Value{ + kind: valueString, + value: "pow", + }, + }, + }, + propertyOrder: []string{ + propertyLength, + propertyName, + }, + value: nativeFunctionObject{ + name: "pow", + call: builtinMathPow, + }, + }, + }, + }, + "random": { + mode: 0o101, + value: Value{ + kind: valueObject, + value: &object{ + runtime: rt, + class: classFunctionName, + objectClass: classObject, + prototype: rt.global.FunctionPrototype, + extensible: true, + property: map[string]property{ + propertyLength: { + mode: 0, + value: Value{ + kind: valueNumber, + value: 0, + }, + }, + propertyName: { + mode: 0, + value: Value{ + kind: valueString, + value: "random", + }, + }, + }, + propertyOrder: []string{ + propertyLength, + propertyName, + }, + value: nativeFunctionObject{ + name: "random", + call: builtinMathRandom, + }, + }, + }, + }, + "round": { + mode: 0o101, + value: Value{ + kind: valueObject, + value: &object{ + runtime: rt, + class: classFunctionName, + objectClass: classObject, + prototype: rt.global.FunctionPrototype, + extensible: true, + property: map[string]property{ + propertyLength: { + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + propertyName: { + mode: 0, + value: Value{ + kind: valueString, + value: "round", + }, + }, + }, + propertyOrder: []string{ + propertyLength, + propertyName, + }, + value: nativeFunctionObject{ + name: "round", + call: builtinMathRound, + }, + }, + }, + }, + "sin": { + mode: 0o101, + value: Value{ + kind: valueObject, + value: &object{ + runtime: rt, + class: classFunctionName, + objectClass: classObject, + prototype: rt.global.FunctionPrototype, + extensible: true, + property: map[string]property{ + propertyLength: { + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + propertyName: { + mode: 0, + value: Value{ + kind: valueString, + value: "sin", + }, + }, + }, + propertyOrder: []string{ + propertyLength, + propertyName, + }, + value: nativeFunctionObject{ + name: "sin", + call: builtinMathSin, + }, + }, + }, + }, + "sinh": { + mode: 0o101, + value: Value{ + kind: valueObject, + value: &object{ + runtime: rt, + class: classFunctionName, + objectClass: classObject, + prototype: rt.global.FunctionPrototype, + extensible: true, + property: map[string]property{ + propertyLength: { + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + propertyName: { + mode: 0, + value: Value{ + kind: valueString, + value: "sinh", + }, + }, + }, + propertyOrder: []string{ + propertyLength, + propertyName, + }, + value: nativeFunctionObject{ + name: "sinh", + call: builtinMathSinh, + }, + }, + }, + }, + "sqrt": { + mode: 0o101, + value: Value{ + kind: valueObject, + value: &object{ + runtime: rt, + class: classFunctionName, + objectClass: classObject, + prototype: rt.global.FunctionPrototype, + extensible: true, + property: map[string]property{ + propertyLength: { + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + propertyName: { + mode: 0, + value: Value{ + kind: valueString, + value: "sqrt", + }, + }, + }, + propertyOrder: []string{ + propertyLength, + propertyName, + }, + value: nativeFunctionObject{ + name: "sqrt", + call: builtinMathSqrt, + }, + }, + }, + }, + "tan": { + mode: 0o101, + value: Value{ + kind: valueObject, + value: &object{ + runtime: rt, + class: classFunctionName, + objectClass: classObject, + prototype: rt.global.FunctionPrototype, + extensible: true, + property: map[string]property{ + propertyLength: { + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + propertyName: { + mode: 0, + value: Value{ + kind: valueString, + value: "tan", + }, + }, + }, + propertyOrder: []string{ + propertyLength, + propertyName, + }, + value: nativeFunctionObject{ + name: "tan", + call: builtinMathTan, + }, + }, + }, + }, + "tanh": { + mode: 0o101, + value: Value{ + kind: valueObject, + value: &object{ + runtime: rt, + class: classFunctionName, + objectClass: classObject, + prototype: rt.global.FunctionPrototype, + extensible: true, + property: map[string]property{ + propertyLength: { + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + propertyName: { + mode: 0, + value: Value{ + kind: valueString, + value: "tanh", + }, + }, + }, + propertyOrder: []string{ + propertyLength, + propertyName, + }, + value: nativeFunctionObject{ + name: "tanh", + call: builtinMathTanh, + }, + }, + }, + }, + "trunc": { + mode: 0o101, + value: Value{ + kind: valueObject, + value: &object{ + runtime: rt, + class: classFunctionName, + objectClass: classObject, + prototype: rt.global.FunctionPrototype, + extensible: true, + property: map[string]property{ + propertyLength: { + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + propertyName: { + mode: 0, + value: Value{ + kind: valueString, + value: "trunc", + }, + }, + }, + propertyOrder: []string{ + propertyLength, + propertyName, + }, + value: nativeFunctionObject{ + name: "trunc", + call: builtinMathTrunc, }, }, }, @@ -4085,22 +4681,34 @@ func (rt *runtime) newContext() { propertyOrder: []string{ "abs", "acos", + "acosh", "asin", + "asinh", "atan", + "atanh", "atan2", + "cbrt", "ceil", "cos", + "cosh", "exp", + "expm1", "floor", "log", + "log10", + "log1p", + "log2", "max", "min", "pow", "random", "round", "sin", + "sinh", "sqrt", "tan", + "tanh", + "trunc", "E", "LN10", "LN2", diff --git a/inline_test.go b/inline_test.go index 57277efd..849804a8 100644 --- a/inline_test.go +++ b/inline_test.go @@ -102,13 +102,13 @@ func TestGetOwnPropertyNames(t *testing.T) { "substr", "substring", // "sup", - // "startsWith", + "startsWith", "toString", "trim", - // "trimStart", "trimLeft", - // "trimEnd", "trimRight", + "trimStart", + "trimEnd", "toLocaleLowerCase", "toLocaleUpperCase", "toLowerCase", @@ -132,27 +132,27 @@ func TestGetOwnPropertyNames(t *testing.T) { "Math": { "abs", "acos", - // "acosh", + "acosh", "asin", - // "asinh", + "asinh", "atan", - // "atanh", + "atanh", "atan2", + "cbrt", "ceil", - // "cbrt", - // "expm1", // "clz32", "cos", - // "cosh", + "cosh", "exp", + "expm1", "floor", // "fround", // "hypot", // "imul", "log", - // "log1p", - // "log2", - // "log10", + "log10", + "log1p", + "log2", "max", "min", "pow", @@ -160,11 +160,11 @@ func TestGetOwnPropertyNames(t *testing.T) { "round", // "sign", "sin", - // "sinh", + "sinh", "sqrt", "tan", - // "tanh", - // "trunc", + "tanh", + "trunc", "E", "LN10", "LN2", diff --git a/issue_test.go b/issue_test.go index a64d9606..6366f4ef 100644 --- a/issue_test.go +++ b/issue_test.go @@ -5,7 +5,6 @@ import ( "database/sql/driver" "encoding/json" "errors" - "fmt" "testing" "time" @@ -698,9 +697,9 @@ func Test_issue234(t *testing.T) { func Test_issue186(t *testing.T) { tests := []struct { + err error name string script string - err error }{ { name: "missing", @@ -739,7 +738,7 @@ func Test_issue186(t *testing.T) { for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - _, err := vm.Run(tc.script) + _, err = vm.Run(tc.script) if tc.err != nil { require.Error(t, err) require.Equal(t, tc.err.Error(), err.Error()) @@ -806,7 +805,7 @@ func Test_issue369(t *testing.T) { type testResult struct{} func (r *testResult) LastInsertId() (int64, error) { - return 0, fmt.Errorf("not supported") + return 0, errors.New("not supported") } func (r *testResult) RowsAffected() (int64, error) { @@ -833,7 +832,7 @@ func (s *testStmt) Exec(args []driver.Value) (driver.Result, error) { // Query implements driver.Stmt. func (s *testStmt) Query(args []driver.Value) (driver.Rows, error) { - return nil, fmt.Errorf("not supported") + return nil, errors.New("not supported") } // testConn is a test driver.Conn. @@ -851,7 +850,7 @@ func (c *testConn) Close() error { // Begin implements driver.Conn. func (c *testConn) Begin() (driver.Tx, error) { - return nil, fmt.Errorf("not supported") + return nil, errors.New("not supported") } // testDriver is test driver.Driver. @@ -928,8 +927,8 @@ func Test_issue386(t *testing.T) { for name, code := range tests { t.Run(name, func(t *testing.T) { - val, err := vm.Run(code) - require.NoError(t, err) + val, err2 := vm.Run(code) + require.NoError(t, err2) require.Equal(t, "something", val.String()) }) } @@ -1132,7 +1131,7 @@ func Test_issue177(t *testing.T) { require.NoError(t, err) exp, err := val.Export() require.NoError(t, err) - require.Equal(t, float64(9), exp) + require.EqualValues(t, 9, exp) } func Test_issue285(t *testing.T) { diff --git a/math_test.go b/math_test.go index 49b33881..1a4cd9b1 100644 --- a/math_test.go +++ b/math_test.go @@ -50,6 +50,20 @@ func TestMath_acos(t *testing.T) { }) } +func TestMath_acosh(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`Math.acosh(-1)`, naN) + test(`Math.acosh(0)`, naN) + test(`Math.acosh(0.999999999999)`, naN) + test(`1/Math.acosh(1)`, infinity) + test(`Math.acosh(Infinity)`, infinity) + test(`Math.acosh(2)`, 1.3169578969248166) + test(`Math.acosh(2.5)`, 1.566799236972411) + }) +} + func TestMath_asin(t *testing.T) { tt(t, func() { test, _ := test() @@ -64,6 +78,20 @@ func TestMath_asin(t *testing.T) { }) } +func TestMath_asinh(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`Math.asinh(-1)`, -0.881373587019543) + test(`Math.asinh(1)`, 0.881373587019543) + test(`Math.asinh(-0)`, -0) + test(`Math.asinh(0)`, 0) + test(`Math.asinh(-Infinity)`, -infinity) + test(`Math.asinh(Infinity)`, infinity) + test(`Math.asinh(2)`, 1.4436354751788103) + }) +} + func TestMath_atan(t *testing.T) { tt(t, func() { test, _ := test() @@ -119,6 +147,35 @@ func TestMath_atan2(t *testing.T) { }) } +func TestMath_atanh(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`Math.atanh(-2)`, naN) + test(`Math.atanh(2)`, naN) + test(`Math.atanh(-1)`, -infinity) + test(`Math.atanh(1)`, infinity) + test(`Math.atanh(0)`, 0) + test(`Math.atanh(0.5)`, 0.5493061443340548) + }) +} + +func TestMath_cbrt(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`Math.cbrt(NaN)`, naN) + test(`Math.cbrt(-1)`, -1) + test(`Math.cbrt(1)`, 1) + test(`Math.cbrt(-0)`, -0) + test(`Math.cbrt(0)`, 0) + test(`Math.cbrt(-Infinity)`, -infinity) + test(`Math.cbrt(Infinity)`, infinity) + test(`Math.cbrt(null)`, 0) + test(`Math.cbrt(2)`, 1.2599210498948732) + }) +} + func TestMath_ceil(t *testing.T) { tt(t, func() { test, _ := test() @@ -150,6 +207,16 @@ func TestMath_cos(t *testing.T) { }) } +func TestMath_cosh(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`Math.cosh(0)`, 1) + test(`Math.cosh(1)`, 1.5430806348152437) + test(`Math.cosh(-1)`, 1.5430806348152437) + }) +} + func TestMath_exp(t *testing.T) { tt(t, func() { test, _ := test() @@ -162,6 +229,18 @@ func TestMath_exp(t *testing.T) { }) } +func TestMath_expm1(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`Math.expm1(0)`, 0) + test(`Math.expm1(1)`, 1.718281828459045) + test(`Math.expm1(-1)`, -0.6321205588285577) + test(`Math.expm1(2)`, 6.38905609893065) + test(`Math.expm1("foo")`, naN) + }) +} + func TestMath_floor(t *testing.T) { tt(t, func() { test, _ := test() @@ -193,6 +272,48 @@ func TestMath_log(t *testing.T) { }) } +func TestMath_log10(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`Math.log10(100000)`, 5) + test(`Math.log10(-2)`, naN) + test(`Math.log10(2)`, 0.3010299956639812) + test(`Math.log10(1)`, 0) + test(`Math.log10(-0)`, -infinity) + test(`Math.log10(0)`, -infinity) + test(`Math.log10(Infinity)`, infinity) + }) +} + +func TestMath_log1p(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`Math.log1p(-2)`, naN) + test(`Math.log1p(-1)`, -infinity) + test(`Math.log1p(1)`, 0.6931471805599453) + test(`Math.log1p(-0)`, -0) + test(`Math.log1p(0)`, 0) + test(`Math.log1p(Infinity)`, infinity) + }) +} + +func TestMath_log2(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`Math.log2(-2)`, naN) + test(`Math.log2(-0)`, -infinity) + test(`Math.log2(0)`, -infinity) + test(`Math.log2(1)`, 0) + test(`Math.log2(2)`, 1) + test(`Math.log2(5)`, 2.321928094887362) + test(`Math.log2(1024)`, 10) + test(`Math.log2(Infinity)`, infinity) + }) +} + func TestMath_max(t *testing.T) { tt(t, func() { test, _ := test() @@ -276,6 +397,20 @@ func TestMath_sin(t *testing.T) { }) } +func TestMath_sinh(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`Math.sinh(-Infinity)`, -infinity) + test(`Math.sinh(Infinity)`, infinity) + test(`Math.sinh(-0)`, -0) + test(`Math.sinh(0)`, 0) + test(`Math.sinh(-1)`, -1.1752011936438014) + test(`Math.sinh(1)`, 1.1752011936438014) + test(`Math.sinh(2)`, 3.626860407847019) + }) +} + func TestMath_sqrt(t *testing.T) { tt(t, func() { test, _ := test() @@ -303,3 +438,32 @@ func TestMath_tan(t *testing.T) { test(`Math.tan(0.5)`, 0.5463024898437905) }) } + +func TestMath_tanh(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`Math.tanh(Infinity)`, 1) + test(`Math.tanh(-Infinity)`, -1) + test(`Math.tanh(-1)`, -0.7615941559557649) + test(`Math.tanh(1)`, 0.7615941559557649) + test(`Math.tanh(-0)`, -0) + test(`Math.tanh(0)`, 0) + }) +} + +func TestMath_trunc(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`Math.trunc(-Infinity)`, -infinity) + test(`Math.trunc(Infinity)`, infinity) + test(`Math.trunc(-0.123)`, -0) + test(`Math.trunc(0.123)`, 0) + test(`Math.trunc(-0)`, -0) + test(`Math.trunc(0)`, 0) + test(`Math.trunc("-1.123")`, -1) + test(`Math.trunc(13.37)`, 13) + test(`Math.trunc(42.84)`, 42) + }) +} diff --git a/native_stack_test.go b/native_stack_test.go index 199b63d0..5e53ea59 100644 --- a/native_stack_test.go +++ b/native_stack_test.go @@ -18,7 +18,7 @@ func TestNativeStackFrames(t *testing.T) { require.NoError(t, err) err = vm.Set("ext1", func(c FunctionCall) Value { - if _, err := c.Otto.Eval("B()"); err != nil { + if _, err = c.Otto.Eval("B()"); err != nil { panic(err) } @@ -60,7 +60,7 @@ func TestNativeStackFrames(t *testing.T) { is(ctx.Column, 19) } - if _, err := c.Otto.Eval("ext3()"); err != nil { + if _, err = c.Otto.Eval("ext3()"); err != nil { panic(err) } diff --git a/object.go b/object.go index f5b7c6cb..40da58f7 100644 --- a/object.go +++ b/object.go @@ -1,17 +1,14 @@ package otto type object struct { - runtime *runtime - - class string - objectClass *objectClass - value interface{} - - prototype *object - extensible bool - + value interface{} + runtime *runtime + objectClass *objectClass + prototype *object property map[string]property + class string propertyOrder []string + extensible bool } func newObject(rt *runtime, class string) *object { @@ -101,7 +98,7 @@ func (o *object) String() string { return o.DefaultValue(defaultValueHintString).string() } -func (o *object) defineProperty(name string, value Value, mode propertyMode, throw bool) bool { //nolint: unparam +func (o *object) defineProperty(name string, value Value, mode propertyMode, throw bool) bool { //nolint:unparam return o.defineOwnProperty(name, property{value, mode}, throw) } diff --git a/object_class.go b/object_class.go index a9cfbf67..e77ad8d9 100644 --- a/object_class.go +++ b/object_class.go @@ -198,17 +198,15 @@ func objectCanPut(obj *object, name string) bool { return canPut } -func objectCanPutDetails(obj *object, name string) (canPut bool, prop *property, setter *object) { //nolint: nonamedreturns +func objectCanPutDetails(obj *object, name string) (canPut bool, prop *property, setter *object) { //nolint:nonamedreturns prop = obj.getOwnProperty(name) if prop != nil { switch propertyValue := prop.value.(type) { case Value: - canPut = prop.writable() - return + return prop.writable(), prop, nil case propertyGetSet: setter = propertyValue[1] - canPut = setter != nil - return + return setter != nil, prop, setter default: panic(obj.runtime.panicTypeError("unexpected type %T to Object.CanPutDetails", prop.value)) } @@ -231,8 +229,7 @@ func objectCanPutDetails(obj *object, name string) (canPut bool, prop *property, return prop.writable(), nil, nil case propertyGetSet: setter = propertyValue[1] - canPut = setter != nil - return + return setter != nil, prop, setter default: panic(obj.runtime.panicTypeError("unexpected type %T to Object.CanPutDetails", prop.value)) } diff --git a/object_test.go b/object_test.go index 3af01888..7ff3474e 100644 --- a/object_test.go +++ b/object_test.go @@ -360,6 +360,40 @@ func TestObject_keys(t *testing.T) { }) } +func TestObject_values(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`Object.values({ abc:"first_example", def:"second_example" })`, "first_example,second_example") + + test(` + function abc() { + this.abc = "first_example"; + this.def = "second_example"; + } + Object.values(new abc()) + `, "first_example,second_example") + + test(` + function def() { + this.ghi = "third_example" + } + def.prototype = new abc(); + Object.values(new def()); + `, "third_example") + + test(` + var arr = [1, 2, 3]; + Object.values(arr); + `, "1,2,3") + + test(` + var arr = [{"abc": "first_example"}, {"def": "second_example"}]; + Object.values(arr); + `, "[object Object],[object Object]") + }) +} + func TestObject_getOwnPropertyNames(t *testing.T) { tt(t, func() { test, _ := test() diff --git a/otto.go b/otto.go index ca80bc5a..31b7e301 100644 --- a/otto.go +++ b/otto.go @@ -221,7 +221,7 @@ package otto import ( "encoding/json" - "fmt" + "errors" "strings" "github.com/robertkrimen/otto/file" @@ -417,13 +417,13 @@ func (o Otto) MakeTypeError(message string) Value { // Context is a structure that contains information about the current execution // context. type Context struct { + This Value + Symbols map[string]Value Filename string - Line int - Column int Callee string - Symbols map[string]Value - This Value Stacktrace []string + Line int + Column int } // Context returns the current execution context of the vm, traversing up to @@ -552,12 +552,11 @@ func (o Otto) Call(source string, this interface{}, argumentList ...interface{}) program, err := o.runtime.cmplParse("", source+"()", nil) if err == nil { if node, ok := program.body[0].(*nodeExpressionStatement); ok { - if node, ok := node.expression.(*nodeCallExpression); ok { + if node, ok2 := node.expression.(*nodeCallExpression); ok2 { var value Value - err := catchPanic(func() { + if err = catchPanic(func() { value = o.runtime.cmplEvaluateNodeCallExpression(node, argumentList) - }) - if err != nil { + }); err != nil { return Value{}, err } return value, nil @@ -579,9 +578,9 @@ func (o Otto) Call(source string, this interface{}, argumentList ...interface{}) } if construct { - result, err := fn.constructSafe(o.runtime, val, argumentList...) - if err != nil { - return Value{}, err + result, err2 := fn.constructSafe(o.runtime, val, argumentList...) + if err2 != nil { + return Value{}, err2 } return result, nil } @@ -618,7 +617,7 @@ func (o Otto) Object(source string) (*Object, error) { if value.IsObject() { return value.Object(), nil } - return nil, fmt.Errorf("value is not an object") + return nil, errors.New("value is not an object") } // ToValue will convert an interface{} value to a value digestible by otto/JavaScript. diff --git a/otto/main.go b/otto/main.go index 7faacb68..b48bcce8 100644 --- a/otto/main.go +++ b/otto/main.go @@ -17,7 +17,7 @@ func readSource(filename string) ([]byte, error) { if filename == "" || filename == "-" { return io.ReadAll(os.Stdin) } - return os.ReadFile(filename) //nolint: gosec + return os.ReadFile(filename) //nolint:gosec } func main() { diff --git a/otto_.go b/otto_.go index bf44679d..2bf06d27 100644 --- a/otto_.go +++ b/otto_.go @@ -95,7 +95,7 @@ func valueToRangeIndex(indexValue Value, length int64, negativeIsZero bool) int6 return index } -func rangeStartEnd(array []Value, size int64, negativeIsZero bool) (start, end int64) { //nolint: nonamedreturns +func rangeStartEnd(array []Value, size int64, negativeIsZero bool) (start, end int64) { //nolint:nonamedreturns start = valueToRangeIndex(valueOfArrayIndex(array, 0), size, negativeIsZero) if len(array) == 1 { // If there is only the start argument, then end = size @@ -113,7 +113,7 @@ func rangeStartEnd(array []Value, size int64, negativeIsZero bool) (start, end i return } -func rangeStartLength(source []Value, size int64) (start, length int64) { //nolint: nonamedreturns +func rangeStartLength(source []Value, size int64) (start, length int64) { //nolint:nonamedreturns start = valueToRangeIndex(valueOfArrayIndex(source, 0), size, false) // Assume the second argument is missing or undefined @@ -132,9 +132,9 @@ func rangeStartLength(source []Value, size int64) (start, length int64) { //noli } func hereBeDragons(arguments ...interface{}) string { - pc, _, _, _ := goruntime.Caller(1) //nolint: dogsled + pc, _, _, _ := goruntime.Caller(1) //nolint:dogsled name := goruntime.FuncForPC(pc).Name() - message := fmt.Sprintf("Here be dragons -- %s", name) + message := "Here be dragons -- " + name if len(arguments) > 0 { message += ": " argument0 := fmt.Sprintf("%s", arguments[0]) diff --git a/otto_test.go b/otto_test.go index f4827316..2ad71569 100644 --- a/otto_test.go +++ b/otto_test.go @@ -1178,8 +1178,8 @@ func TestOttoCopy(t *testing.T) { is(value, "Xyzzy0[object Object]") { - vm0 := New() - _, err := vm0.Run(` + vm01 := New() + _, err2 := vm01.Run(` var global = (function () {return this;}()) var abc = 0; var vm = "vm0"; @@ -1197,34 +1197,34 @@ func TestOttoCopy(t *testing.T) { }; })(); `) - require.NoError(t, err) + require.NoError(t, err2) - value, err := vm0.Run(` + value2, err2 := vm01.Run(` def(); `) - require.NoError(t, err) - is(value, "vm0,0,0,1") + require.NoError(t, err2) + is(value2, "vm0,0,0,1") - vm1 := vm0.Copy() - err = vm1.Set("vm", "vm1") - require.NoError(t, err) - value, err = vm1.Run(` + vm11 := vm01.Copy() + err2 = vm11.Set("vm", "vm1") + require.NoError(t, err2) + value2, err2 = vm11.Run(` def(); `) - require.NoError(t, err) - is(value, "vm1,1,1,1") + require.NoError(t, err2) + is(value2, "vm1,1,1,1") - value, err = vm0.Run(` + value2, err2 = vm01.Run(` def(); `) - require.NoError(t, err) - is(value, "vm0,1,1,1") + require.NoError(t, err2) + is(value2, "vm0,1,1,1") - value, err = vm1.Run(` + value2, err2 = vm11.Run(` def(); `) - require.NoError(t, err) - is(value, "vm1,2,2,1") + require.NoError(t, err2) + is(value2, "vm1,2,2,1") } }) } @@ -1412,15 +1412,15 @@ func TestOttoRun(t *testing.T) { } { - script, err := vm.Compile("", `var abc; if (!abc) abc = 0; abc += 2; abc;`) - require.NoError(t, err) + script, err2 := vm.Compile("", `var abc; if (!abc) abc = 0; abc += 2; abc;`) + require.NoError(t, err2) - value, err = vm.Run(script) - require.NoError(t, err) + value, err2 = vm.Run(script) + require.NoError(t, err2) is(value, 14) - value, err = vm.Run(script) - require.NoError(t, err) + value, err2 = vm.Run(script) + require.NoError(t, err2) is(value, 16) is(script.String(), "// \nvar abc; if (!abc) abc = 0; abc += 2; abc;") @@ -1778,7 +1778,7 @@ func TestOttoInterrupt(t *testing.T) { go func() { defer func() { if caught := recover(); caught != nil { - if caught == halt { //nolint: errorlint + if caught == halt { //nolint:errorlint ec <- nil return } diff --git a/parser/comments_test.go b/parser/comments_test.go index d79229cc..09b985d0 100644 --- a/parser/comments_test.go +++ b/parser/comments_test.go @@ -33,11 +33,11 @@ func checkComments(actual []*ast.Comment, expected []string, position ast.Commen } func displayComments(m ast.CommentMap) { - fmt.Printf("Displaying comments:\n") //nolint: forbidigo + fmt.Printf("Displaying comments:\n") //nolint:forbidigo for n, comments := range m { - fmt.Printf("%v %v:\n", reflect.TypeOf(n), n) //nolint: forbidigo + fmt.Printf("%v %v:\n", reflect.TypeOf(n), n) //nolint:forbidigo for i, comment := range comments { - fmt.Printf(" [%v] %v @ %v\n", i, comment.Text, comment.Position) //nolint: forbidigo + fmt.Printf(" [%v] %v @ %v\n", i, comment.Text, comment.Position) //nolint:forbidigo } } } @@ -1436,7 +1436,7 @@ func TestParser_comments2(t *testing.T) { a = /*comment1*/new /*comment2*/ obj/*comment3*/() `, nil) n := program.Body[0] - fmt.Printf("FOUND NODE: %v, number of comments: %v\n", reflect.TypeOf(n), len(parser.comments.CommentMap[n])) //nolint: forbidigo + fmt.Printf("FOUND NODE: %v, number of comments: %v\n", reflect.TypeOf(n), len(parser.comments.CommentMap[n])) //nolint:forbidigo displayComments(parser.comments.CommentMap) }) } diff --git a/parser/error.go b/parser/error.go index ba66dec2..94d5a27b 100644 --- a/parser/error.go +++ b/parser/error.go @@ -49,8 +49,8 @@ const ( // 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 + Position file.Position } // FIXME Should this be "SyntaxError"? @@ -120,11 +120,11 @@ func (p *parser) errorUnexpectedToken(tkn token.Token) { } // ErrorList is a list of *Errors. -type ErrorList []*Error //nolint: errname +type ErrorList []*Error //nolint:errname // Add adds an Error with given position and message to an ErrorList. func (el *ErrorList) Add(position file.Position, msg string) { - *el = append(*el, &Error{position, msg}) + *el = append(*el, &Error{Position: position, Message: msg}) } // Reset resets an ErrorList to no errors. diff --git a/parser/expression.go b/parser/expression.go index dd8606e5..2c8e2994 100644 --- a/parser/expression.go +++ b/parser/expression.go @@ -136,8 +136,8 @@ func (p *parser) parseRegExpLiteral() *ast.RegExpLiteral { flags := "" if p.token == token.IDENTIFIER { // gim flags = p.literal + endOffset = p.chrOffset p.next() - endOffset = p.chrOffset - 1 } var value string @@ -268,7 +268,7 @@ func (p *parser) parseObjectProperty() ast.Property { literal, value := p.parseObjectPropertyKey() if literal == "get" && p.token != token.COLON { idx := p.idx - _, value := p.parseObjectPropertyKey() + _, value = p.parseObjectPropertyKey() parameterList := p.parseFunctionParameterList() node := &ast.FunctionLiteral{ @@ -283,7 +283,7 @@ func (p *parser) parseObjectProperty() ast.Property { } } else if literal == "set" && p.token != token.COLON { idx := p.idx - _, value := p.parseObjectPropertyKey() + _, value = p.parseObjectPropertyKey() parameterList := p.parseFunctionParameterList() node := &ast.FunctionLiteral{ @@ -379,26 +379,24 @@ func (p *parser) parseArrayLiteral() ast.Expression { } } -func (p *parser) parseArgumentList() (argumentList []ast.Expression, idx0, idx1 file.Idx) { //nolint: nonamedreturns +func (p *parser) parseArgumentList() (argumentList []ast.Expression, idx0, idx1 file.Idx) { //nolint:nonamedreturns if p.mode&StoreComments != 0 { p.comments.Unset() } idx0 = p.expect(token.LEFT_PARENTHESIS) - if p.token != token.RIGHT_PARENTHESIS { - for { - exp := p.parseAssignmentExpression() - if p.mode&StoreComments != 0 { - p.comments.SetExpression(exp) - } - argumentList = append(argumentList, exp) - if p.token != token.COMMA { - break - } - if p.mode&StoreComments != 0 { - p.comments.Unset() - } - p.next() + for p.token != token.RIGHT_PARENTHESIS { + exp := p.parseAssignmentExpression() + if p.mode&StoreComments != 0 { + p.comments.SetExpression(exp) + } + argumentList = append(argumentList, exp) + if p.token != token.COMMA { + break } + if p.mode&StoreComments != 0 { + p.comments.Unset() + } + p.next() } if p.mode&StoreComments != 0 { p.comments.Unset() @@ -884,7 +882,7 @@ func (p *parser) parseLogicalOrExpression() ast.Expression { return left } -func (p *parser) parseConditionlExpression() ast.Expression { +func (p *parser) parseConditionalExpression() ast.Expression { left := p.parseLogicalOrExpression() if p.token == token.QUESTION_MARK { @@ -911,7 +909,7 @@ func (p *parser) parseConditionlExpression() ast.Expression { } func (p *parser) parseAssignmentExpression() ast.Expression { - left := p.parseConditionlExpression() + left := p.parseConditionalExpression() var operator token.Token switch p.token { case token.ASSIGN: diff --git a/parser/lexer.go b/parser/lexer.go index 8a3412ed..0d9132f7 100644 --- a/parser/lexer.go +++ b/parser/lexer.go @@ -15,7 +15,7 @@ import ( "github.com/robertkrimen/otto/token" ) -type chr struct { //nolint: unused +type chr struct { //nolint:unused value rune width int } @@ -38,6 +38,53 @@ func digitValue(chr rune) int { return 16 // Larger than any legal digit value } +// See https://www.unicode.org/reports/tr31/ for reference on ID_Start and ID_Continue. +var includeIDStart = []*unicode.RangeTable{ + unicode.Lu, + unicode.Ll, + unicode.Lt, + unicode.Lm, + unicode.Lo, + unicode.Nl, + unicode.Other_ID_Start, +} + +var includeIDContinue = []*unicode.RangeTable{ + unicode.Lu, + unicode.Ll, + unicode.Lt, + unicode.Lm, + unicode.Lo, + unicode.Nl, + unicode.Other_ID_Start, + unicode.Mn, + unicode.Mc, + unicode.Nd, + unicode.Pc, + unicode.Other_ID_Continue, +} + +var exclude = []*unicode.RangeTable{ + unicode.Pattern_Syntax, + unicode.Pattern_White_Space, +} + +func unicodeIDStart(r rune) bool { + if unicode.In(r, exclude...) { + return false + } + + return unicode.In(r, includeIDStart...) +} + +func unicodeIDContinue(r rune) bool { + if unicode.In(r, exclude...) { + return false + } + + return unicode.In(r, includeIDContinue...) +} + func isDigit(chr rune, base int) bool { return digitValue(chr) < base } @@ -45,14 +92,14 @@ func isDigit(chr rune, base int) bool { func isIdentifierStart(chr rune) bool { return chr == '$' || chr == '_' || chr == '\\' || 'a' <= chr && chr <= 'z' || 'A' <= chr && chr <= 'Z' || - chr >= utf8.RuneSelf && unicode.IsLetter(chr) + chr >= utf8.RuneSelf && unicodeIDStart(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)) + chr >= utf8.RuneSelf && unicodeIDContinue(chr) } func (p *parser) scanIdentifier() (string, error) { @@ -98,7 +145,7 @@ func (p *parser) scanIdentifier() (string, error) { } // 7.2. -func isLineWhiteSpace(chr rune) bool { //nolint: unused, deadcode +func isLineWhiteSpace(chr rune) bool { //nolint:unused, deadcode switch chr { case '\u0009', '\u000b', '\u000c', '\u0020', '\u00a0', '\ufeff': return true @@ -119,7 +166,7 @@ func isLineTerminator(chr rune) bool { return false } -func (p *parser) scan() (tkn token.Token, literal string, idx file.Idx) { //nolint: nonamedreturns +func (p *parser) scan() (tkn token.Token, literal string, idx file.Idx) { //nolint:nonamedreturns p.implicitSemicolon = false for { @@ -143,23 +190,20 @@ func (p *parser) scan() (tkn token.Token, literal string, idx file.Idx) { //noli switch tkn { case 0: // Not a keyword - if literal == "true" || literal == "false" { + switch literal { + case "true", "false": p.insertSemicolon = true - tkn = token.BOOLEAN - return - } else if literal == "null" { + return token.BOOLEAN, literal, idx + case "null": p.insertSemicolon = true - tkn = token.NULL - return + return token.NULL, literal, idx } - case token.KEYWORD: - tkn = token.KEYWORD if strict { // TODO If strict and in strict mode, then this is not a break break } - return + return token.KEYWORD, literal, idx case token.THIS, @@ -169,19 +213,18 @@ func (p *parser) scan() (tkn token.Token, literal string, idx file.Idx) { //noli token.CONTINUE, token.DEBUGGER: p.insertSemicolon = true - return + return tkn, literal, idx default: - return + return tkn, literal, idx } } p.insertSemicolon = true - tkn = token.IDENTIFIER - return + return token.IDENTIFIER, literal, idx case '0' <= chr && chr <= '9': p.insertSemicolon = true tkn, literal = p.scanNumericLiteral(false) - return + return tkn, literal, idx default: p.read() switch chr { @@ -240,16 +283,16 @@ func (p *parser) scan() (tkn token.Token, literal string, idx file.Idx) { //noli switch p.chr { case '/': if p.mode&StoreComments != 0 { - literal := string(p.readSingleLineComment()) - p.comments.AddComment(ast.NewComment(literal, p.idx)) + comment := string(p.readSingleLineComment()) + p.comments.AddComment(ast.NewComment(comment, idx)) continue } p.skipSingleLineComment() continue case '*': if p.mode&StoreComments != 0 { - literal = string(p.readMultiLineComment()) - p.comments.AddComment(ast.NewComment(literal, p.idx)) + comment := string(p.readMultiLineComment()) + p.comments.AddComment(ast.NewComment(comment, idx)) continue } p.skipMultiLineComment() @@ -306,7 +349,7 @@ func (p *parser) scan() (tkn token.Token, literal string, idx file.Idx) { //noli } } p.insertSemicolon = insertSemicolon - return + return tkn, literal, idx } } @@ -370,7 +413,7 @@ func (p *parser) switch6(tkn0, tkn1 token.Token, chr2 rune, tkn2, tkn3 token.Tok return tkn0 } -func (p *parser) chrAt(index int) chr { //nolint: unused +func (p *parser) chrAt(index int) chr { //nolint:unused value, width := utf8.DecodeRuneInString(p.str[index:]) return chr{ value: value, @@ -615,7 +658,7 @@ func hex2decimal(chr byte) (rune, bool) { } } -func parseNumberLiteral(literal string) (value interface{}, err error) { //nolint: nonamedreturns +func parseNumberLiteral(literal string) (value interface{}, err error) { //nolint:nonamedreturns // TODO Is Uint okay? What about -MAX_UINT value, err = strconv.ParseInt(literal, 0, 64) if err == nil { @@ -749,8 +792,8 @@ func parseStringLiteral(literal string) (string, error) { if len(str) < j+1 { break } - chr := str[j] - if '0' > chr || chr > '7' { + + if ch := str[j]; '0' > ch || ch > '7' { break } decimal := rune(str[j]) - '0' @@ -791,7 +834,7 @@ func (p *parser) scanNumericLiteral(decimalPoint bool) (token.Token, string) { } if p.chr == '0' { - offset := p.chrOffset + chrOffset := p.chrOffset p.read() switch p.chr { case 'x', 'X': @@ -800,11 +843,11 @@ func (p *parser) scanNumericLiteral(decimalPoint bool) (token.Token, string) { if isDigit(p.chr, 16) { p.read() } else { - return token.ILLEGAL, p.str[offset:p.chrOffset] + return token.ILLEGAL, p.str[chrOffset:p.chrOffset] } p.scanMantissa(16) - if p.chrOffset-offset <= 2 { + if p.chrOffset-chrOffset <= 2 { // Only "0x" or "0X" p.error(0, "Illegal hexadecimal number") } @@ -820,7 +863,7 @@ func (p *parser) scanNumericLiteral(decimalPoint bool) (token.Token, string) { } p.scanMantissa(8) if p.chr == '8' || p.chr == '9' { - return token.ILLEGAL, p.str[offset:p.chrOffset] + return token.ILLEGAL, p.str[chrOffset:p.chrOffset] } goto octal } diff --git a/parser/lexer_test.go b/parser/lexer_test.go index faa98a48..7e1e5faf 100644 --- a/parser/lexer_test.go +++ b/parser/lexer_test.go @@ -351,6 +351,21 @@ Second line \ token.RIGHT_BRACKET, "", 6, ) + // Identifier from Unicode Nl + test("\u16ee", + token.IDENTIFIER, "ᛮ", 1, + ) + + // Identifier from Unicode Other_ID_Start + test("\u212e", + token.IDENTIFIER, "℮", 1, + ) + + // Using char from ID_Continue after valid start char + test("a\u0300", + token.IDENTIFIER, "à", 1, + ) + // ILLEGAL test(`3ea`, @@ -383,5 +398,15 @@ Second line \ token.STRING, "\"\\x0G\"", 1, token.EOF, "", 7, ) + + // Starting identifier with ID_Continue char from Nm + test("\u0300", + token.ILLEGAL, + ) + + // Starting identifier with Pattern_Syntax + test("'", + token.ILLEGAL, + ) }) } diff --git a/parser/parser.go b/parser/parser.go index 940f2950..f0b710d3 100644 --- a/parser/parser.go +++ b/parser/parser.go @@ -56,36 +56,27 @@ const ( StoreComments ) -type parser struct { //nolint: maligned - 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. +type parser struct { + comments *ast.Comments + file *file.File + scope *scope + literal string + str string + errors ErrorList + recover struct { idx file.Idx count int } - - mode Mode - - file *file.File - - comments *ast.Comments + idx file.Idx + token token.Token + offset int + chrOffset int + mode Mode + base int + length int + chr rune + insertSemicolon bool + implicitSemicolon bool // Scratch when trying to seek to the next statement, etc. } // Parser is implemented by types which can parse JavaScript Code. @@ -131,13 +122,13 @@ func ReadSource(filename string, src interface{}) ([]byte, error) { return nil, fmt.Errorf("invalid src type %T", src) } } - return os.ReadFile(filename) //nolint: gosec + return os.ReadFile(filename) //nolint:gosec } // ReadSourceMap reads the source map from src if not nil, otherwise is a noop. func ReadSourceMap(filename string, src interface{}) (*sourcemap.Consumer, error) { if src == nil { - return nil, nil //nolint: nilnil + return nil, nil //nolint:nilnil } switch src := src.(type) { @@ -173,7 +164,7 @@ func ParseFileWithSourceMap(fileSet *file.FileSet, filename string, javascriptSo if bytes.HasPrefix(lastLine, []byte("//# sourceMappingURL=data:application/json")) { bits := bytes.SplitN(lastLine, []byte(","), 2) if len(bits) == 2 { - if d, err := base64.StdEncoding.DecodeString(string(bits[1])); err == nil { + if d, errDecode := base64.StdEncoding.DecodeString(string(bits[1])); errDecode == nil { sourcemapSource = d } } diff --git a/parser/parser_test.go b/parser/parser_test.go index 202d8da5..303e01d9 100644 --- a/parser/parser_test.go +++ b/parser/parser_test.go @@ -26,7 +26,7 @@ func testParse(src string) (*parser, *ast.Program, error) { return testParseWithMode(src, 0) } -func testParseWithMode(src string, mode Mode) (parser *parser, program *ast.Program, err error) { //nolint: nonamedreturns +func testParseWithMode(src string, mode Mode) (parser *parser, program *ast.Program, err error) { //nolint:nonamedreturns defer func() { if tmp := recover(); tmp != nil { if tmp, ok := tmp.(string); ok { @@ -879,6 +879,15 @@ func TestParser(t *testing.T) { 2 debugger `, nil) + + test(` + function mergeObjects(x, y) { /* dummy body */} + + mergeObjects( + { "duck": "quack" }, + { "dog": "bark" }, // Allow trailing comma after the last argument. + ); + `, nil) }) } @@ -966,6 +975,32 @@ func Test_parseNumberLiteral(t *testing.T) { }) } +func Test_praseRegExpLiteral(t *testing.T) { + tt(t, func() { + test := func(input, literal, pattern, flags string) { + parser := newParser("", input, 1, nil) + program, err := parser.parse() + is(err, nil) + + regex := program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.RegExpLiteral) + is(regex.Literal, literal) + is(regex.Pattern, pattern) + is(regex.Flags, flags) + } + + test("/abc/", "/abc/", "abc", "") + test("/abc/gim", "/abc/gim", "abc", "gim") + test("/abc/ ", "/abc/", "abc", "") + test("/abc/gim ", "/abc/gim", "abc", "gim") + test("/abc/;", "/abc/", "abc", "") + test("/abc/gim;", "/abc/gim", "abc", "gim") + test("/abc/\n", "/abc/", "abc", "") + test("/abc/gim\n", "/abc/gim", "abc", "gim") + test("/abc/;\n", "/abc/", "abc", "") + test("/abc/gim;\n", "/abc/gim", "abc", "gim") + }) +} + func TestPosition(t *testing.T) { tt(t, func() { parser := newParser("", "// Lorem ipsum", 1, nil) @@ -1022,9 +1057,188 @@ func TestPosition(t *testing.T) { is(err, nil) block := program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.FunctionLiteral).Body.(*ast.BlockStatement) node = block.List[0].(*ast.IfStatement) - is(node.Idx0(), 21) + is(node.Idx0(), 14) + is(parser.slice(node.Idx0(), node.Idx1()), "if (abc) { throw 'failed'; }") node = node.(*ast.IfStatement).Consequent.(*ast.BlockStatement).List[0].(*ast.ThrowStatement) - is(node.Idx0(), 39) + is(node.Idx0(), 25) + is(parser.slice(node.Idx0(), node.Idx1()), "throw 'failed'") + + parser = newParser("", "(function(){ for (x=1; x<=4; x++) { console.log(x); } })", 1, nil) + program, err = parser.parse() + is(err, nil) + block = program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.FunctionLiteral).Body.(*ast.BlockStatement) + node = block.List[0].(*ast.ForStatement) + is(node.Idx0(), 14) + is(parser.slice(node.Idx0(), node.Idx1()), "for (x=1; x<=4; x++) { console.log(x); }") + + parser = newParser("", "(function(){ for (p in o) { console.log(p); } })", 1, nil) + program, err = parser.parse() + is(err, nil) + block = program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.FunctionLiteral).Body.(*ast.BlockStatement) + node = block.List[0].(*ast.ForInStatement) + is(node.Idx0(), 14) + is(parser.slice(node.Idx0(), node.Idx1()), "for (p in o) { console.log(p); }") + + parser = newParser("", "x = {x: 1}", 1, nil) + program, err = parser.parse() + is(err, nil) + node = program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.AssignExpression).Right.(*ast.ObjectLiteral) + is(node.Idx0(), 5) + is(parser.slice(node.Idx0(), node.Idx1()), "{x: 1}") + + parser = newParser("", "x = [1, 2]", 1, nil) + program, err = parser.parse() + is(err, nil) + node = program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.AssignExpression).Right.(*ast.ArrayLiteral) + is(node.Idx0(), 5) + is(parser.slice(node.Idx0(), node.Idx1()), "[1, 2]") + + parser = newParser("", "x = true ? 1 : 2", 1, nil) + program, err = parser.parse() + is(err, nil) + node = program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.AssignExpression).Right.(*ast.ConditionalExpression) + is(node.Idx0(), 5) + is(parser.slice(node.Idx0(), node.Idx1()), "true ? 1 : 2") + + parser = newParser("", "(function(){ x = 1, y = 2; })", 1, nil) + program, err = parser.parse() + is(err, nil) + block = program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.FunctionLiteral).Body.(*ast.BlockStatement) + node = block.List[0].(*ast.ExpressionStatement).Expression.(*ast.SequenceExpression) + is(node.Idx0(), 14) + is(parser.slice(node.Idx0(), node.Idx1()), "x = 1, y = 2") + + parser = newParser("", "x = ~x", 1, nil) + program, err = parser.parse() + is(err, nil) + node = program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.AssignExpression).Right.(*ast.UnaryExpression) + is(node.Idx0(), 5) + is(parser.slice(node.Idx0(), node.Idx1()), "~x") + + parser = newParser("", "(function(){ xyz++; })", 1, nil) + program, err = parser.parse() + is(err, nil) + block = program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.FunctionLiteral).Body.(*ast.BlockStatement) + node = block.List[0].(*ast.ExpressionStatement).Expression.(*ast.UnaryExpression) + is(node.Idx0(), 14) + is(parser.slice(node.Idx0(), node.Idx1()), "xyz++") + + parser = newParser("", "(function(){ var abc, xyz = 1; })", 1, nil) + program, err = parser.parse() + is(err, nil) + block = program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.FunctionLiteral).Body.(*ast.BlockStatement) + node = block.List[0].(*ast.VariableStatement) + is(node.Idx0(), 14) + is(parser.slice(node.Idx0(), node.Idx1()), "var abc, xyz = 1") + node = block.List[0].(*ast.VariableStatement).List[0].(*ast.VariableExpression) + is(node.Idx0(), 18) + is(parser.slice(node.Idx0(), node.Idx1()), "abc") + node = block.List[0].(*ast.VariableStatement).List[1].(*ast.VariableExpression) + is(node.Idx0(), 23) + is(parser.slice(node.Idx0(), node.Idx1()), "xyz = 1") + + parser = newParser("", "for (i = 0; i < 10; i++) { if (i == 5) break; }", 1, nil) + program, err = parser.parse() + is(err, nil) + block = program.Body[0].(*ast.ForStatement).Body.(*ast.BlockStatement) + node = block.List[0].(*ast.IfStatement).Consequent.(*ast.BranchStatement) + is(node.Idx0(), 40) + is(parser.slice(node.Idx0(), node.Idx1()), "break") + + parser = newParser("", "(function(){ xyz: for (i = 0; i < 10; i++) { if (i == 5) continue xyz; } })", 1, nil) + program, err = parser.parse() + is(err, nil) + block = program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.FunctionLiteral).Body.(*ast.BlockStatement) + node = block.List[0].(*ast.LabelledStatement) + is(node.Idx0(), 14) + is(parser.slice(node.Idx0(), node.Idx1()), "xyz: for (i = 0; i < 10; i++) { if (i == 5) continue xyz; }") + block = node.(*ast.LabelledStatement).Statement.(*ast.ForStatement).Body.(*ast.BlockStatement) + node = block.List[0].(*ast.IfStatement).Consequent.(*ast.BranchStatement) + is(node.Idx0(), 58) + is(parser.slice(node.Idx0(), node.Idx1()), "continue xyz") + + parser = newParser("", "(function(){ return; })", 1, nil) + program, err = parser.parse() + is(err, nil) + block = program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.FunctionLiteral).Body.(*ast.BlockStatement) + node = block.List[0].(*ast.ReturnStatement) + is(node.Idx0(), 14) + is(parser.slice(node.Idx0(), node.Idx1()), "return") + + parser = newParser("", "(function(){ return 10; })", 1, nil) + program, err = parser.parse() + is(err, nil) + block = program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.FunctionLiteral).Body.(*ast.BlockStatement) + node = block.List[0].(*ast.ReturnStatement) + is(node.Idx0(), 14) + is(parser.slice(node.Idx0(), node.Idx1()), "return 10") + + parser = newParser("", "(function(){ switch (a) { default: return; }})", 1, nil) + program, err = parser.parse() + is(err, nil) + block = program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.FunctionLiteral).Body.(*ast.BlockStatement) + node = block.List[0].(*ast.SwitchStatement) + is(node.Idx0(), 14) + is(parser.slice(node.Idx0(), node.Idx1()), "switch (a) { default: return; }") + + parser = newParser("", "(function(){ try { a(); } catch (error) { b(); } })", 1, nil) + program, err = parser.parse() + is(err, nil) + block = program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.FunctionLiteral).Body.(*ast.BlockStatement) + node = block.List[0].(*ast.TryStatement) + is(node.Idx0(), 14) + is(parser.slice(node.Idx0(), node.Idx1()), "try { a(); } catch (error) { b(); }") + node = block.List[0].(*ast.TryStatement).Catch + is(node.Idx0(), 27) + is(parser.slice(node.Idx0(), node.Idx1()), "catch (error) { b(); }") + + parser = newParser("", "(function(){ try { a(); } catch (error) { b(); } finally { c(); } })", 1, nil) + program, err = parser.parse() + is(err, nil) + block = program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.FunctionLiteral).Body.(*ast.BlockStatement) + node = block.List[0].(*ast.TryStatement) + is(node.Idx0(), 14) + is(parser.slice(node.Idx0(), node.Idx1()), "try { a(); } catch (error) { b(); } finally { c(); }") + + parser = newParser("", "(function(){ with (1) {} })", 1, nil) + program, err = parser.parse() + is(err, nil) + block = program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.FunctionLiteral).Body.(*ast.BlockStatement) + node = block.List[0].(*ast.WithStatement) + is(node.Idx0(), 14) + is(parser.slice(node.Idx0(), node.Idx1()), "with (1) {}") + + parser = newParser("", "while (i < 10) { i++; }", 1, nil) + program, err = parser.parse() + is(err, nil) + node = program.Body[0].(*ast.WhileStatement) + is(node.Idx0(), 1) + is(parser.slice(node.Idx0(), node.Idx1()), "while (i < 10) { i++; }") + + parser = newParser("", "do { i++; } while (i < 10 )", 1, nil) + program, err = parser.parse() + is(err, nil) + node = program.Body[0].(*ast.DoWhileStatement) + is(node.Idx0(), 1) + is(parser.slice(node.Idx0(), node.Idx1()), "do { i++; } while (i < 10 )") + + parser = newParser("", "(function() { // single-line comment\n })", 1, nil) + parser.mode |= StoreComments + program, err = parser.parse() + is(err, nil) + block = program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.FunctionLiteral).Body.(*ast.BlockStatement) + comment := parser.comments.CommentMap[block][0] + is(comment.Begin, 15) + is(parser.slice(comment.Begin, file.Idx(int(comment.Begin)+len(comment.Text)+2)), "// single-line comment") + + parser = newParser("", "(function() { /* multi-line comment */ })", 1, nil) + parser.mode |= StoreComments + program, err = parser.parse() + is(err, nil) + block = program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.FunctionLiteral).Body.(*ast.BlockStatement) + comment = parser.comments.CommentMap[block][0] + is(comment.Begin, 15) + is(parser.slice(comment.Begin, file.Idx(int(comment.Begin)+len(comment.Text)+4)), "/* multi-line comment */") }) } diff --git a/parser/regexp.go b/parser/regexp.go index 45245c13..df4a51b3 100644 --- a/parser/regexp.go +++ b/parser/regexp.go @@ -6,18 +6,15 @@ import ( "strconv" ) -type regExpParser struct { //nolint: maligned - 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 +type regExpParser struct { + goRegexp *bytes.Buffer + str string + errors []error + length int + chrOffset int + offset int + chr rune + invalid bool } // TransformRegExp transforms a JavaScript pattern into a Go "regexp" pattern. @@ -276,7 +273,7 @@ func (p *regExpParser) scanEscape(inClass bool) { if err != nil { p.errors = append(p.errors, err) } - } else { //nolint: staticcheck + } else { //nolint:staticcheck // Unescape the character for re2 } p.pass() @@ -346,7 +343,7 @@ func (p *regExpParser) pass() { } // TODO Better error reporting, use the offset, etc. -func (p *regExpParser) error(offset int, msg string, msgValues ...interface{}) { //nolint: unparam +func (p *regExpParser) error(offset int, msg string, msgValues ...interface{}) { //nolint:unparam err := fmt.Errorf(msg, msgValues...) p.errors = append(p.errors, err) } diff --git a/parser/regexp_test.go b/parser/regexp_test.go index 3222db1a..86781348 100644 --- a/parser/regexp_test.go +++ b/parser/regexp_test.go @@ -65,7 +65,7 @@ func TestRegExp(t *testing.T) { result, err := TransformRegExp(input) is(err, nil) if is(result, expect) { - _, err := regexp.Compile(result) + _, err = regexp.Compile(result) if !is(err, nil) { t.Log(result) } diff --git a/parser/scope.go b/parser/scope.go index 4f1b435f..9f49e47b 100644 --- a/parser/scope.go +++ b/parser/scope.go @@ -6,13 +6,12 @@ import ( type scope struct { outer *scope + declarationList []ast.Declaration + labels []string allowIn bool inIteration bool inSwitch bool inFunction bool - declarationList []ast.Declaration - - labels []string } func (p *parser) openScope() { diff --git a/parser/statement.go b/parser/statement.go index c4a03b40..e6469773 100644 --- a/parser/statement.go +++ b/parser/statement.go @@ -39,7 +39,7 @@ func (p *parser) parseEmptyStatement() ast.Statement { return &ast.EmptyStatement{Semicolon: idx} } -func (p *parser) parseStatementList() (list []ast.Statement) { //nolint: nonamedreturns +func (p *parser) parseStatementList() (list []ast.Statement) { //nolint:nonamedreturns for p.token != token.RIGHT_BRACE && p.token != token.EOF { statement := p.parseStatement() list = append(list, statement) @@ -356,7 +356,7 @@ func (p *parser) parseThrowStatement() ast.Statement { } node := &ast.ThrowStatement{ - Throw: p.idx, + Throw: idx, Argument: p.parseExpression(), } if p.mode&StoreComments != 0 { @@ -373,12 +373,13 @@ func (p *parser) parseSwitchStatement() ast.Statement { if p.mode&StoreComments != 0 { comments = p.comments.FetchAll() } - p.expect(token.SWITCH) + idx := p.expect(token.SWITCH) if p.mode&StoreComments != 0 { comments = append(comments, p.comments.FetchAll()...) } p.expect(token.LEFT_PARENTHESIS) node := &ast.SwitchStatement{ + Switch: idx, Discriminant: p.parseExpression(), Default: -1, } @@ -397,6 +398,7 @@ func (p *parser) parseSwitchStatement() ast.Statement { for index := 0; p.token != token.EOF; index++ { if p.token == token.RIGHT_BRACE { + node.RightBrace = p.idx p.next() break } @@ -423,7 +425,7 @@ func (p *parser) parseWithStatement() ast.Statement { if p.mode&StoreComments != 0 { comments = p.comments.FetchAll() } - p.expect(token.WITH) + idx := p.expect(token.WITH) var withComments []*ast.Comment if p.mode&StoreComments != 0 { withComments = p.comments.FetchAll() @@ -432,6 +434,7 @@ func (p *parser) parseWithStatement() ast.Statement { p.expect(token.LEFT_PARENTHESIS) node := &ast.WithStatement{ + With: idx, Object: p.parseExpression(), } p.expect(token.RIGHT_PARENTHESIS) @@ -561,14 +564,14 @@ func (p *parser) parseForOrForInStatement() ast.Statement { allowIn := p.scope.allowIn p.scope.allowIn = false if p.token == token.VAR { - idx := p.idx + tokenIdx := p.idx var varComments []*ast.Comment if p.mode&StoreComments != 0 { varComments = p.comments.FetchAll() p.comments.Unset() } p.next() - list := p.parseVariableDeclarationList(idx) + list := p.parseVariableDeclarationList(tokenIdx) if len(list) == 1 && p.token == token.IN { if p.mode&StoreComments != 0 { p.comments.Unset() @@ -603,6 +606,7 @@ func (p *parser) parseForOrForInStatement() ast.Statement { } forin := p.parseForIn(left[0]) + forin.For = idx if p.mode&StoreComments != 0 { p.comments.CommentMap.AddComments(forin, comments, ast.LEADING) p.comments.CommentMap.AddComments(forin, forComments, ast.FOR) @@ -616,6 +620,7 @@ func (p *parser) parseForOrForInStatement() ast.Statement { p.expect(token.SEMICOLON) initializer := &ast.SequenceExpression{Sequence: left} forstatement := p.parseFor(initializer) + forstatement.For = idx if p.mode&StoreComments != 0 { p.comments.CommentMap.AddComments(forstatement, comments, ast.LEADING) p.comments.CommentMap.AddComments(forstatement, forComments, ast.FOR) @@ -656,13 +661,13 @@ func (p *parser) parseDoWhileStatement() ast.Statement { if p.mode&StoreComments != 0 { comments = p.comments.FetchAll() } - p.expect(token.DO) + idx := p.expect(token.DO) var doComments []*ast.Comment if p.mode&StoreComments != 0 { doComments = p.comments.FetchAll() } - node := &ast.DoWhileStatement{} + node := &ast.DoWhileStatement{Do: idx} if p.token == token.LEFT_BRACE { node.Body = p.parseBlockStatement() } else { @@ -676,7 +681,7 @@ func (p *parser) parseDoWhileStatement() ast.Statement { } p.expect(token.LEFT_PARENTHESIS) node.Test = p.parseExpression() - p.expect(token.RIGHT_PARENTHESIS) + node.RightParenthesis = p.expect(token.RIGHT_PARENTHESIS) p.implicitSemicolon = true p.optionalSemicolon() @@ -695,7 +700,7 @@ func (p *parser) parseWhileStatement() ast.Statement { if p.mode&StoreComments != 0 { comments = p.comments.FetchAll() } - p.expect(token.WHILE) + idx := p.expect(token.WHILE) var whileComments []*ast.Comment if p.mode&StoreComments != 0 { @@ -704,7 +709,8 @@ func (p *parser) parseWhileStatement() ast.Statement { p.expect(token.LEFT_PARENTHESIS) node := &ast.WhileStatement{ - Test: p.parseExpression(), + While: idx, + Test: p.parseExpression(), } p.expect(token.RIGHT_PARENTHESIS) node.Body = p.parseIterationStatement() @@ -722,7 +728,7 @@ func (p *parser) parseIfStatement() ast.Statement { if p.mode&StoreComments != 0 { comments = p.comments.FetchAll() } - p.expect(token.IF) + pos := p.expect(token.IF) var ifComments []*ast.Comment if p.mode&StoreComments != 0 { ifComments = p.comments.FetchAll() @@ -730,7 +736,7 @@ func (p *parser) parseIfStatement() ast.Statement { p.expect(token.LEFT_PARENTHESIS) node := &ast.IfStatement{ - If: p.idx, + If: pos, Test: p.parseExpression(), } p.expect(token.RIGHT_PARENTHESIS) diff --git a/property.go b/property.go index 9c2758ed..768d6e56 100644 --- a/property.go +++ b/property.go @@ -69,11 +69,11 @@ func (p *property) configureOff() { p.mode &= ^modeConfigureMask } -func (p property) configureSet() bool { //nolint: unused +func (p property) configureSet() bool { //nolint:unused return p.mode&modeConfigureMask&modeSetMask == 0 } -func (p property) copy() *property { //nolint: unused +func (p property) copy() *property { //nolint:unused cpy := p return &cpy } @@ -150,12 +150,12 @@ func toPropertyDescriptor(rt *runtime, value Value) property { getterSetter := false if objectDescriptor.hasProperty("get") { - value := objectDescriptor.get("get") - if value.IsDefined() { - if !value.isCallable() { + val := objectDescriptor.get("get") + if val.IsDefined() { + if !val.isCallable() { panic(rt.panicTypeError("toPropertyDescriptor get not callable")) } - getter = value.object() + getter = val.object() getterSetter = true } else { getter = &nilGetSetObject @@ -164,12 +164,12 @@ func toPropertyDescriptor(rt *runtime, value Value) property { } if objectDescriptor.hasProperty("set") { - value := objectDescriptor.get("set") - if value.IsDefined() { - if !value.isCallable() { + val := objectDescriptor.get("set") + if val.IsDefined() { + if !val.isCallable() { panic(rt.panicTypeError("toPropertyDescriptor set not callable")) } - setter = value.object() + setter = val.object() getterSetter = true } else { setter = &nilGetSetObject diff --git a/reflect_test.go b/reflect_test.go index 5c3e6935..afa3e64b 100644 --- a/reflect_test.go +++ b/reflect_test.go @@ -13,12 +13,12 @@ import ( ) type abcStruct struct { - Abc bool - Def int - Ghi string Jkl interface{} - Mno _mnoStruct Pqr map[string]int8 + Ghi string + Mno _mnoStruct + Def int + Abc bool } func (abc abcStruct) String() string { @@ -763,11 +763,11 @@ func TestPassthrough(t *testing.T) { }) } -type TestDynamicFunctionReturningInterfaceMyStruct1 struct{} //nolint: errname +type TestDynamicFunctionReturningInterfaceMyStruct1 struct{} //nolint:errname func (m *TestDynamicFunctionReturningInterfaceMyStruct1) Error() string { return "MyStruct1" } -type TestDynamicFunctionReturningInterfaceMyStruct2 struct{} //nolint: errname +type TestDynamicFunctionReturningInterfaceMyStruct2 struct{} //nolint:errname func (m *TestDynamicFunctionReturningInterfaceMyStruct2) Error() string { return "MyStruct2" } diff --git a/registry/registry.go b/registry/registry.go index e1731332..f20b5ff3 100644 --- a/registry/registry.go +++ b/registry/registry.go @@ -7,8 +7,8 @@ var registry []*Entry = make([]*Entry, 0) // Entry represents a registry entry. type Entry struct { - active bool source func() string + active bool } // newEntry returns a new Entry for source. diff --git a/repl/autocompleter.go b/repl/autocompleter.go index cfd2a1cc..1a47a6d9 100644 --- a/repl/autocompleter.go +++ b/repl/autocompleter.go @@ -37,7 +37,7 @@ func (a *autoCompleter) Do(line []rune, pos int) ([][]rune, int) { if o := r.Object(); o != nil { for _, v := range o.KeysByParent() { - l = append(l, v...) //nolint: makezero + l = append(l, v...) //nolint:makezero } } } diff --git a/repl/repl.go b/repl/repl.go index 8764594c..ea62b4c8 100644 --- a/repl/repl.go +++ b/repl/repl.go @@ -89,7 +89,7 @@ func RunWithOptions(vm *otto.Otto, options Options) error { prelude := options.Prelude if prelude != "" { - if _, err := io.Copy(rl.Stderr(), strings.NewReader(prelude+"\n")); err != nil { + if _, err = io.Copy(rl.Stderr(), strings.NewReader(prelude+"\n")); err != nil { return err } @@ -99,9 +99,9 @@ func RunWithOptions(vm *otto.Otto, options Options) error { var d []string for { - l, err := rl.Readline() - if err != nil { - if errors.Is(err, readline.ErrInterrupt) { + l, errRL := rl.Readline() + if errRL != nil { + if errors.Is(errRL, readline.ErrInterrupt) { if d != nil { d = nil @@ -114,7 +114,7 @@ func RunWithOptions(vm *otto.Otto, options Options) error { break } - return err + return errRL } if l == "" { @@ -123,28 +123,28 @@ func RunWithOptions(vm *otto.Otto, options Options) error { d = append(d, l) - s, err := vm.Compile("repl", strings.Join(d, "\n")) - if err != nil { + s, errRL := vm.Compile("repl", strings.Join(d, "\n")) + if errRL != nil { rl.SetPrompt(strings.Repeat(" ", len(prompt))) } else { rl.SetPrompt(prompt) d = nil - v, err := vm.Eval(s) - if err != nil { + v, errEval := vm.Eval(s) + if errEval != nil { var oerr *otto.Error - if errors.As(err, &oerr) { - if _, err := io.Copy(rl.Stdout(), strings.NewReader(oerr.String())); err != nil { + if errors.As(errEval, &oerr) { + if _, err = io.Copy(rl.Stdout(), strings.NewReader(oerr.String())); err != nil { return fmt.Errorf("write out: %w", err) } } else { - if _, err := io.Copy(rl.Stdout(), strings.NewReader(err.Error())); err != nil { + if _, err = io.Copy(rl.Stdout(), strings.NewReader(errEval.Error())); err != nil { return fmt.Errorf("write out: %w", err) } } } else { - if _, err := rl.Stdout().Write([]byte(v.String() + "\n")); err != nil { + if _, err = rl.Stdout().Write([]byte(v.String() + "\n")); err != nil { return fmt.Errorf("write out: %w", err) } } diff --git a/result.go b/result.go index 1896c182..abda3353 100644 --- a/result.go +++ b/result.go @@ -10,19 +10,19 @@ const ( ) type result struct { - kind resultKind value Value target string + kind resultKind } func newReturnResult(value Value) result { - return result{resultReturn, value, ""} + return result{kind: resultReturn, value: value, target: ""} } func newContinueResult(target string) result { - return result{resultContinue, emptyValue, target} + return result{kind: resultContinue, value: emptyValue, target: target} } func newBreakResult(target string) result { - return result{resultBreak, emptyValue, target} + return result{kind: resultBreak, value: emptyValue, target: target} } diff --git a/runtime.go b/runtime.go index 883b5127..605cc1ec 100644 --- a/runtime.go +++ b/runtime.go @@ -59,14 +59,13 @@ type runtime struct { globalStash *objectStash scope *scope otto *Otto - eval *object // The builtin eval, for determine indirect versus direct invocation + eval *object debugger func(*Otto) random func() float64 + labels []string stackLimit int traceLimit int - - labels []string // FIXME - lck sync.Mutex + lck sync.Mutex } func (rt *runtime) enterScope(scop *scope) { @@ -116,7 +115,7 @@ func (rt *runtime) putValue(reference referencer, value Value) { } } -func (rt *runtime) tryCatchEvaluate(inner func() Value) (tryValue Value, isException bool) { //nolint: nonamedreturns +func (rt *runtime) tryCatchEvaluate(inner func() Value) (tryValue Value, isException bool) { //nolint:nonamedreturns // resultValue = The value of the block (e.g. the last statement) // throw = Something was thrown // throwValue = The value of what was thrown @@ -188,7 +187,7 @@ func checkObjectCoercible(rt *runtime, value Value) { } // testObjectCoercible. -func testObjectCoercible(value Value) (isObject, mustCoerce bool) { //nolint: nonamedreturns +func testObjectCoercible(value Value) (isObject, mustCoerce bool) { //nolint:nonamedreturns switch value.kind { case valueReference, valueEmpty, valueNull, valueUndefined: return false, false @@ -510,7 +509,7 @@ func (rt *runtime) convertCallParameter(v Value, t reflect.Type) (reflect.Value, } case reflect.Func: if t.NumOut() > 1 { - return reflect.Zero(t), fmt.Errorf("converting JavaScript values to Go functions with more than one return value is currently not supported") + return reflect.Zero(t), errors.New("converting JavaScript values to Go functions with more than one return value is currently not supported") } if o := v.object(); o != nil && o.class == classFunctionName { diff --git a/scope.go b/scope.go index 607f7c68..c535c316 100644 --- a/scope.go +++ b/scope.go @@ -5,11 +5,10 @@ type scope struct { lexical stasher variable stasher this *object - eval bool // Replace this with kind? outer *scope + frame frame depth int - - frame frame + eval bool } func newScope(lexical stasher, variable stasher, this *object) *scope { diff --git a/script.go b/script.go index ab444294..077a916d 100644 --- a/script.go +++ b/script.go @@ -83,7 +83,7 @@ func (s *Script) marshalBinary() ([]byte, error) { // will return an error. // // The binary format can change at any time and should be considered unspecified and opaque. -func (s *Script) unmarshalBinary(data []byte) (err error) { //nolint: nonamedreturns +func (s *Script) unmarshalBinary(data []byte) (err error) { //nolint:nonamedreturns decoder := gob.NewDecoder(bytes.NewReader(data)) defer func() { if err != nil { diff --git a/script_test.go b/script_test.go index 8b368820..0396b0e0 100644 --- a/script_test.go +++ b/script_test.go @@ -16,9 +16,9 @@ func TestScript(t *testing.T) { str := script.String() is(str, "// xyzzy\nvar abc; if (!abc) abc = 0; abc += 2; abc;") - value, err := vm.Run(script) + val, err := vm.Run(script) require.NoError(t, err) - is(value, 2) + is(val, 2) // TODO(steve): Fix the underlying issues as to why this returns early. if true { @@ -30,34 +30,34 @@ func TestScript(t *testing.T) { is(len(tmp), 1228) { - script := &Script{} - err = script.unmarshalBinary(tmp) + script2 := &Script{} + err = script2.unmarshalBinary(tmp) require.NoError(t, err) - is(script.String(), str) + is(script2.String(), str) - value, err = vm.Run(script) + val, err = vm.Run(script2) require.NoError(t, err) - is(value, 4) + is(val, 4) - tmp, err = script.marshalBinary() + tmp, err = script2.marshalBinary() require.NoError(t, err) is(len(tmp), 1228) } { - script := &Script{} - err = script.unmarshalBinary(tmp) + script2 := &Script{} + err = script2.unmarshalBinary(tmp) require.NoError(t, err) - is(script.String(), str) + is(script2.String(), str) - value, err := vm.Run(script) - require.NoError(t, err) - is(value, 6) + val2, err2 := vm.Run(script2) + require.NoError(t, err2) + is(val2, 6) - tmp, err = script.marshalBinary() - require.NoError(t, err) + tmp, err2 = script2.marshalBinary() + require.NoError(t, err2) is(len(tmp), 1228) } @@ -65,15 +65,15 @@ func TestScript(t *testing.T) { version := scriptVersion scriptVersion = "bogus" - script := &Script{} - err = script.unmarshalBinary(tmp) + script2 := &Script{} + err = script2.unmarshalBinary(tmp) is(err, "version mismatch") - is(script.String(), "// \n") - is(script.version, "") - is(script.program == nil, true) - is(script.filename, "") - is(script.src, "") + is(script2.String(), "// \n") + is(script2.version, "") + is(script2.program == nil, true) + is(script2.filename, "") + is(script2.src, "") scriptVersion = version } diff --git a/sourcemap_test.go b/sourcemap_test.go index 38276c79..11724317 100644 --- a/sourcemap_test.go +++ b/sourcemap_test.go @@ -1,7 +1,6 @@ package otto import ( - "errors" "testing" "github.com/stretchr/testify/require" @@ -30,8 +29,8 @@ func TestSourceMapOriginalWithNoSourcemap(t *testing.T) { _, err = vm.Run(`functionA()`) require.Error(t, err) var oerr *Error - require.True(t, errors.As(err, &oerr)) - require.Equal(t, oerr.String(), testSourcemapOriginalStack) + require.ErrorAs(t, err, &oerr) + require.Equal(t, testSourcemapOriginalStack, oerr.String()) }) } @@ -48,8 +47,8 @@ func TestSourceMapMangledWithNoSourcemap(t *testing.T) { _, err = vm.Run(`functionA()`) require.Error(t, err) var oerr *Error - require.True(t, errors.As(err, &oerr)) - require.Equal(t, oerr.String(), testSourcemapMangledStack) + require.ErrorAs(t, err, &oerr) + require.Equal(t, testSourcemapMangledStack, oerr.String()) }) } @@ -66,8 +65,8 @@ func TestSourceMapMangledWithSourcemap(t *testing.T) { _, err = vm.Run(`functionA()`) require.Error(t, err) var oerr *Error - require.True(t, errors.As(err, &oerr)) - require.Equal(t, oerr.String(), testSourcemapMappedStack) + require.ErrorAs(t, err, &oerr) + require.Equal(t, testSourcemapMappedStack, oerr.String()) }) } @@ -84,8 +83,8 @@ func TestSourceMapMangledWithInlineSourcemap(t *testing.T) { _, err = vm.Run(`functionA()`) require.Error(t, err) var oerr *Error - require.True(t, errors.As(err, &oerr)) - require.Equal(t, oerr.String(), testSourcemapMappedStack) + require.ErrorAs(t, err, &oerr) + require.Equal(t, testSourcemapMappedStack, oerr.String()) }) } diff --git a/stash.go b/stash.go index 40aeb858..85ee26d8 100644 --- a/stash.go +++ b/stash.go @@ -6,19 +6,19 @@ import ( // stasher is implemented by types which can stash data. type stasher 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 + hasBinding(name string) bool // + createBinding(name string, deletable bool, value Value) // CreateMutableBinding + setBinding(name string, value Value, strict bool) // SetMutableBinding + getBinding(name string, throw bool) Value // GetBindingValue + deleteBinding(name string) bool // + setValue(name string, value Value, throw bool) // createBinding + setBinding outer() stasher runtime() *runtime - newReference(string, bool, at) referencer + newReference(name string, strict bool, atv at) referencer - clone(*cloner) stasher + clone(cloner *cloner) stasher } type objectStash struct { diff --git a/string_test.go b/string_test.go index 0fd94a9b..3810e8bf 100644 --- a/string_test.go +++ b/string_test.go @@ -470,3 +470,40 @@ func TestString_localeCompare(t *testing.T) { test(`'a'.localeCompare('a');`, 0) }) } + +func TestString_startsWith(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`'a'.startsWith('c');`, false) + test(`'aa'.startsWith('a');`, true) + }) +} + +func TestString_trimStart(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`" abc\u000B".trimStart()`, "abc\u000B") + test(`"abc ".trimStart()`, "abc ") + test(` + var a = "\u180Eabc \u000B " + var b = a.trimStart() + a.length + b.length + `, 13) + }) +} + +func TestString_trimEnd(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`" abc\u000B".trimEnd()`, " abc") + test(`" abc ".trimEnd()`, " abc") + test(` + var a = "\u180Eabc \u000B " + var b = a.trimEnd() + a.length + b.length + `, 11) + }) +} diff --git a/token/token.go b/token/token.go index 8cb91138..9fee9e1e 100644 --- a/token/token.go +++ b/token/token.go @@ -57,11 +57,11 @@ type keyword struct { // public // static func IsKeyword(literal string) (Token, bool) { - if keyword, exists := keywordTable[literal]; exists { - if keyword.futureKeyword { - return KEYWORD, keyword.strict + if kw, exists := keywordTable[literal]; exists { + if kw.futureKeyword { + return KEYWORD, kw.strict } - return keyword.token, false + return kw.token, false } return 0, false } diff --git a/tools/gen-jscore/.gen-jscore.yaml b/tools/gen-jscore/.gen-jscore.yaml index a3f30fe3..5bca766f 100644 --- a/tools/gen-jscore/.gen-jscore.yaml +++ b/tools/gen-jscore/.gen-jscore.yaml @@ -32,6 +32,8 @@ types: function: 1 - name: keys function: 1 + - name: values + function: 1 - name: getOwnPropertyNames function: 1 prototype: @@ -184,6 +186,8 @@ types: function: 2 - name: substring function: 2 + - name: startsWith + function: 1 - name: toString function: -1 - name: trim @@ -192,6 +196,10 @@ types: function: -1 - name: trimRight function: -1 + - name: trimStart + function: -1 + - name: trimEnd + function: -1 - name: toLocaleLowerCase function: -1 - name: toLocaleUpperCase @@ -224,27 +232,27 @@ types: - name: Number properties: - - name: length - value: 1 - - name: prototype - value: rt.global.NumberPrototype - - name: isNaN - function: 1 - - name: MAX_VALUE - value: math.MaxFloat64 - kind: valueNumber - - name: MIN_VALUE - kind: valueNumber - value: math.SmallestNonzeroFloat64 - - name: NaN - kind: valueNumber - value: math.NaN() - - name: NEGATIVE_INFINITY - kind: valueNumber - value: math.Inf(-1) - - name: POSITIVE_INFINITY - kind: valueNumber - value: math.Inf(+1) + - name: length + value: 1 + - name: prototype + value: rt.global.NumberPrototype + - name: isNaN + function: 1 + - name: MAX_VALUE + value: math.MaxFloat64 + kind: valueNumber + - name: MIN_VALUE + kind: valueNumber + value: math.SmallestNonzeroFloat64 + - name: NaN + kind: valueNumber + value: math.NaN() + - name: NEGATIVE_INFINITY + kind: valueNumber + value: math.Inf(-1) + - name: POSITIVE_INFINITY + kind: valueNumber + value: math.Inf(+1) prototype: prototype: Object value: prototypeValueNumber @@ -268,66 +276,90 @@ types: class: Math objectPrototype: Object properties: - - name: abs - function: 1 - - name: acos - function: 1 - - name: asin - function: 1 - - name: atan - function: 1 - - name: atan2 - function: 1 - - name: ceil - function: 1 - - name: cos - function: 1 - - name: exp - function: 1 - - name: floor - function: 1 - - name: log - function: 1 - - name: max - function: 2 - - name: min - function: 2 - - name: pow - function: 2 - - name: random - function: -1 - - name: round - function: 1 - - name: sin - function: 1 - - name: sqrt - function: 1 - - name: tan - function: 1 - - name: E - kind: valueNumber - value: math.E - - name: LN10 - kind: valueNumber - value: math.Ln10 - - name: LN2 - kind: valueNumber - value: math.Ln2 - - name: LOG10E - kind: valueNumber - value: math.Log10E - - name: LOG2E - kind: valueNumber - value: math.Log2E - - name: PI - kind: valueNumber - value: math.Pi - - name: SQRT1_2 - kind: valueNumber - value: sqrt1_2 - - name: SQRT2 - kind: valueNumber - value: math.Sqrt2 + - name: abs + function: 1 + - name: acos + function: 1 + - name: acosh + function: 1 + - name: asin + function: 1 + - name: asinh + function: 1 + - name: atan + function: 1 + - name: atanh + function: 1 + - name: atan2 + function: 1 + - name: cbrt + function: 1 + - name: ceil + function: 1 + - name: cos + function: 1 + - name: cosh + function: 1 + - name: exp + function: 1 + - name: expm1 + function: 1 + - name: floor + function: 1 + - name: log + function: 1 + - name: log10 + function: 1 + - name: log1p + function: 1 + - name: log2 + function: 1 + - name: max + function: 2 + - name: min + function: 2 + - name: pow + function: 2 + - name: random + function: -1 + - name: round + function: 1 + - name: sin + function: 1 + - name: sinh + function: 1 + - name: sqrt + function: 1 + - name: tan + function: 1 + - name: tanh + function: 1 + - name: trunc + function: 1 + - name: E + kind: valueNumber + value: math.E + - name: LN10 + kind: valueNumber + value: math.Ln10 + - name: LN2 + kind: valueNumber + value: math.Ln2 + - name: LOG10E + kind: valueNumber + value: math.Log10E + - name: LOG2E + kind: valueNumber + value: math.Log2E + - name: PI + kind: valueNumber + value: math.Pi + - name: SQRT1_2 + kind: valueNumber + value: sqrt1_2 + - name: SQRT2 + kind: valueNumber + value: math.Sqrt2 - name: Date properties: diff --git a/tools/gen-jscore/main.go b/tools/gen-jscore/main.go index 6b3ec52c..0df61a16 100644 --- a/tools/gen-jscore/main.go +++ b/tools/gen-jscore/main.go @@ -22,14 +22,14 @@ var templates embed.FS // jsType represents JavaScript type to generate. type jsType struct { + Prototype *prototype `yaml:"prototype"` Name string `yaml:"name"` - Core bool `yaml:"core"` ObjectClass string `yaml:"objectClass"` ObjectPrototype string `yaml:"objectPrototype"` Class string `yaml:"class"` Value string `yaml:"value"` Properties []property `yaml:"properties"` - Prototype *prototype + Core bool `yaml:"core"` } // BlankConstructor is a default fallback returning false for templates. @@ -60,10 +60,10 @@ func (p prototype) Property(name string) (*property, error) { type property struct { Name string `yaml:"name"` Call string `yaml:"call"` - Function int `yaml:"function"` Mode string `yaml:"mode"` Value string `yaml:"value"` Kind string `yaml:"kind"` + Function int `yaml:"function"` } // value represents a JavaScript value to generate a Value creator for. @@ -75,8 +75,8 @@ type value struct { // config represents our configuration. type config struct { Types []jsType `yaml:"types"` - Log jsType `yaml:"log"` Values []value `yaml:"values"` + Log jsType `yaml:"log"` } // Type returns the type for name. @@ -93,7 +93,7 @@ func (c config) Type(name string) (*jsType, error) { // generate generates the context file writing the output to filename. func generate(filename string) (err error) { var cfg config - if err := yaml.Unmarshal(configData, &cfg); err != nil { + if err = yaml.Unmarshal(configData, &cfg); err != nil { return fmt.Errorf("decode config: %w", err) } @@ -108,7 +108,7 @@ func generate(filename string) (err error) { return fmt.Errorf("parse templates: %w", err) } - output, err := os.Create(filename) //nolint: gosec + output, err := os.Create(filename) //nolint:gosec if err != nil { return fmt.Errorf("open output: %w", err) } @@ -119,7 +119,7 @@ func generate(filename string) (err error) { } }() - if err := tmpl.ExecuteTemplate(output, "root.tmpl", cfg); err != nil { + if err = tmpl.ExecuteTemplate(output, "root.tmpl", cfg); err != nil { return fmt.Errorf("execute template: %w", err) } diff --git a/tools/gen-tokens/main.go b/tools/gen-tokens/main.go index faf967d9..d7b0160f 100644 --- a/tools/gen-tokens/main.go +++ b/tools/gen-tokens/main.go @@ -37,7 +37,7 @@ type config struct { // generate generates the context file writing the output to filename. func generate(filename string) (err error) { var cfg config - if err := yaml.Unmarshal(configData, &cfg); err != nil { + if err = yaml.Unmarshal(configData, &cfg); err != nil { return fmt.Errorf("decode config: %w", err) } @@ -50,7 +50,7 @@ func generate(filename string) (err error) { return fmt.Errorf("parse templates: %w", err) } - output, err := os.Create(filename) //nolint: gosec + output, err := os.Create(filename) //nolint:gosec if err != nil { return fmt.Errorf("open output: %w", err) } @@ -61,7 +61,7 @@ func generate(filename string) (err error) { } }() - if err := tmpl.ExecuteTemplate(output, "root.tmpl", cfg); err != nil { + if err = tmpl.ExecuteTemplate(output, "root.tmpl", cfg); err != nil { return fmt.Errorf("execute template: %w", err) } diff --git a/tools/tester/main.go b/tools/tester/main.go index 5641e6fe..82f21d49 100644 --- a/tools/tester/main.go +++ b/tools/tester/main.go @@ -103,14 +103,14 @@ func (l library) fetch() error { if err != nil { return fmt.Errorf("get library %q: %w", l.Name, err) } - defer resp.Body.Close() //nolint: errcheck + defer resp.Body.Close() //nolint:errcheck name := l.Name if !strings.HasSuffix(name, ".js") { name += ".js" } - f, err := os.Create(filepath.Join(dataDir, name)) //nolint: gosec + f, err := os.Create(filepath.Join(dataDir, name)) //nolint:gosec if err != nil { return fmt.Errorf("create library %q: %w", l.Name, err) } @@ -124,7 +124,7 @@ func (l library) fetch() error { // test runs the code from filename returning the time it took and any error // encountered when running a full parse without IgnoreRegExpErrors in parseError. -func test(filename string) (took time.Duration, parseError, err error) { //nolint: nonamedreturns +func test(filename string) (took time.Duration, parseError, err error) { //nolint:nonamedreturns defer func() { if r := recover(); r != nil { err = fmt.Errorf("panic on %q: %v", filename, r) @@ -140,13 +140,13 @@ func test(filename string) (took time.Duration, parseError, err error) { //nolin return 0, nil, fmt.Errorf("fatal %q", val) } - script, err := os.ReadFile(filename) //nolint: gosec + script, err := os.ReadFile(filename) //nolint:gosec if err != nil { return 0, nil, err } vm := otto.New() - if err := vm.Set("console", noopConsole); err != nil { + if err = vm.Set("console", noopConsole); err != nil { return 0, nil, fmt.Errorf("set console: %w", err) } @@ -187,15 +187,15 @@ func fetchAll(src string) error { if err != nil { return fmt.Errorf("get libraries %q: %w", src, err) } - defer resp.Body.Close() //nolint: errcheck + defer resp.Body.Close() //nolint:errcheck var libs libraries dec := json.NewDecoder(resp.Body) - if err := dec.Decode(&libs); err != nil { + if err = dec.Decode(&libs); err != nil { return fmt.Errorf("json decode: %w", err) } - if err := os.Mkdir(dataDir, 0o750); err != nil && !errors.Is(err, fs.ErrExist) { + if err = os.Mkdir(dataDir, 0o750); err != nil && !errors.Is(err, fs.ErrExist) { return fmt.Errorf("mkdir: %w", err) } @@ -239,9 +239,9 @@ func fetchAll(src string) error { // result represents the result from a test. type result struct { - filename string err error parseError error + filename string took time.Duration } @@ -332,7 +332,7 @@ func main() { err = report(flag.Args()) default: flag.PrintDefaults() - err = fmt.Errorf("missing flag") + err = errors.New("missing flag") } if err != nil { fmt.Fprintln(os.Stderr, err) diff --git a/type_arguments.go b/type_arguments.go index 2e313fb5..7f2a83dc 100644 --- a/type_arguments.go +++ b/type_arguments.go @@ -26,29 +26,23 @@ func (rt *runtime) newArgumentsObject(indexOfParameterName []string, stash stash } type argumentsObject struct { + stash stasher indexOfParameterName []string - // function(abc, def, ghi) - // indexOfParameterName[0] = "abc" - // indexOfParameterName[1] = "def" - // indexOfParameterName[2] = "ghi" - // ... - stash stasher } func (o argumentsObject) clone(c *cloner) argumentsObject { indexOfParameterName := make([]string, len(o.indexOfParameterName)) copy(indexOfParameterName, o.indexOfParameterName) return argumentsObject{ - indexOfParameterName, - c.stash(o.stash), + indexOfParameterName: indexOfParameterName, + stash: c.stash(o.stash), } } func (o argumentsObject) get(name string) (Value, bool) { index := stringToArrayIndex(name) if index >= 0 && index < int64(len(o.indexOfParameterName)) { - name := o.indexOfParameterName[index] - if name == "" { + if name = o.indexOfParameterName[index]; name == "" { return Value{}, false } return o.stash.getBinding(name, false), true diff --git a/type_date.go b/type_date.go index ac956760..d35d1e69 100644 --- a/type_date.go +++ b/type_date.go @@ -8,9 +8,9 @@ import ( ) type dateObject struct { - time Time.Time // Time from the "time" package, a cached version of time - epoch int64 + time Time.Time value Value + epoch int64 isNaN bool } @@ -22,6 +22,7 @@ var invalidDateObject = dateObject{ } type ecmaTime struct { + location *Time.Location year int month int day int @@ -29,19 +30,18 @@ type ecmaTime struct { minute int second int millisecond int - location *Time.Location // Basically, either local or UTC } func newEcmaTime(goTime Time.Time) ecmaTime { return ecmaTime{ - goTime.Year(), - dateFromGoMonth(goTime.Month()), - goTime.Day(), - goTime.Hour(), - goTime.Minute(), - goTime.Second(), - goTime.Nanosecond() / (100 * 100 * 100), - goTime.Location(), + year: goTime.Year(), + month: dateFromGoMonth(goTime.Month()), + day: goTime.Day(), + hour: goTime.Hour(), + minute: goTime.Minute(), + second: goTime.Second(), + millisecond: goTime.Nanosecond() / (100 * 100 * 100), + location: goTime.Location(), } } @@ -235,6 +235,10 @@ var ( "2006-01T15:04:05", "2006-01-02T15:04:05", + "2006/01", + "2006/01/02", + "2006/01/02 15:04:05", + "2006T15:04:05.000", "2006-01T15:04:05.000", "2006-01-02T15:04:05.000", diff --git a/type_error.go b/type_error.go index 0b810954..3ad9d6bd 100644 --- a/type_error.go +++ b/type_error.go @@ -3,9 +3,9 @@ package otto func (rt *runtime) newErrorObject(name string, message Value, stackFramesToPop int) *object { obj := rt.newClassObject(classErrorName) if message.IsDefined() { - msg := message.string() - obj.defineProperty("message", stringValue(msg), 0o111, false) - obj.value = newError(rt, name, stackFramesToPop, msg) + err := newError(rt, name, stackFramesToPop, "%s", message.string()) + obj.defineProperty("message", err.messageValue(), 0o111, false) + obj.value = err } else { obj.value = newError(rt, name, stackFramesToPop) } diff --git a/type_function.go b/type_function.go index bd407704..41e63933 100644 --- a/type_function.go +++ b/type_function.go @@ -27,11 +27,11 @@ type nativeFunction func(FunctionCall) Value // nativeFunctionObject. type nativeFunctionObject struct { + call nativeFunction + construct constructFunction name string file string line int - call nativeFunction // [[Call]] - construct constructFunction // [[Construct]] } func (rt *runtime) newNativeFunctionProperty(name, file string, line int, native nativeFunction, length int) *object { @@ -162,7 +162,7 @@ func (o *object) isCall() bool { } } -func (o *object) call(this Value, argumentList []Value, eval bool, frm frame) Value { +func (o *object) call(this Value, argumentList []Value, eval bool, frm frame) Value { //nolint:unparam // Isn't currently used except in recursive self. switch fn := o.value.(type) { case nativeFunctionObject: // Since eval is a native function, we only have to check for it here @@ -270,13 +270,12 @@ func (o *object) hasInstance(of Value) bool { // FunctionCall is an encapsulation of a JavaScript function call. type FunctionCall struct { - runtime *runtime - thisObj *object - eval bool // This call is a direct call to eval - This Value - ArgumentList []Value + runtime *runtime + thisObj *object Otto *Otto + ArgumentList []Value + eval bool } // Argument will return the value of the argument at the given index. diff --git a/type_go_array.go b/type_go_array.go index ad7b3202..397c017f 100644 --- a/type_go_array.go +++ b/type_go_array.go @@ -33,7 +33,7 @@ func newGoArrayObject(value reflect.Value) *goArrayObject { } } -func (o goArrayObject) getValue(name string) (reflect.Value, bool) { //nolint: unused +func (o goArrayObject) getValue(name string) (reflect.Value, bool) { //nolint:unused if index, err := strconv.ParseInt(name, 10, 64); err != nil { v, ok := o.getValueIndex(index) if ok { @@ -41,7 +41,7 @@ func (o goArrayObject) getValue(name string) (reflect.Value, bool) { //nolint: u } } - if m := o.value.MethodByName(name); m != (reflect.Value{}) { + if m := o.value.MethodByName(name); m.IsValid() { return m, true } @@ -93,7 +93,7 @@ func goArrayGetOwnProperty(obj *object, name string) *property { } } - if method := obj.value.(*goArrayObject).value.MethodByName(name); method != (reflect.Value{}) { + if method := obj.value.(*goArrayObject).value.MethodByName(name); method.IsValid() { return &property{ obj.runtime.toValue(method.Interface()), 0o110, diff --git a/type_go_map.go b/type_go_map.go index aa7b7801..d4953bfd 100644 --- a/type_go_map.go +++ b/type_go_map.go @@ -13,9 +13,9 @@ func (rt *runtime) newGoMapObject(value reflect.Value) *object { } type goMapObject struct { - value reflect.Value keyType reflect.Type valueType reflect.Type + value reflect.Value } func newGoMapObject(value reflect.Value) *goMapObject { @@ -47,7 +47,21 @@ func (o goMapObject) toValue(value Value) reflect.Value { func goMapGetOwnProperty(obj *object, name string) *property { goObj := obj.value.(*goMapObject) - value := goObj.value.MapIndex(goObj.toKey(name)) + + // an error here means that the key referenced by `name` could not possibly + // be a property of this object, so it should be safe to ignore this error + // + // TODO: figure out if any cases from + // https://go.dev/ref/spec#Comparison_operators meet the criteria of 1) + // being possible to represent as a string, 2) being possible to reconstruct + // from a string, and 3) having a meaningful failure case in this context + // other than "key does not exist" + key, err := stringToReflectValue(name, goObj.keyType.Kind()) + if err != nil { + return nil + } + + value := goObj.value.MapIndex(key) if value.IsValid() { return &property{obj.runtime.toValue(value.Interface()), 0o111} } diff --git a/type_go_map_test.go b/type_go_map_test.go index 395393b7..dcd306c6 100644 --- a/type_go_map_test.go +++ b/type_go_map_test.go @@ -1,8 +1,8 @@ package otto import ( - "fmt" "sort" + "strconv" "testing" ) @@ -25,7 +25,7 @@ func (s GoMapTest) Join() string { sort.Strings(keys) for _, key := range keys { - joinedStr += key + ": " + fmt.Sprintf("%d", s[key]) + " " + joinedStr += key + ": " + strconv.Itoa(s[key]) + " " } return joinedStr } diff --git a/type_go_struct.go b/type_go_struct.go index e191c57c..66363327 100644 --- a/type_go_struct.go +++ b/type_go_struct.go @@ -53,11 +53,11 @@ func (o goStructObject) getValue(name string) reflect.Value { return reflect.Value{} } -func (o goStructObject) fieldIndex(name string) []int { //nolint: unused +func (o goStructObject) fieldIndex(name string) []int { //nolint:unused return fieldIndexByName(reflect.Indirect(o.value).Type(), name) } -func (o goStructObject) method(name string) (reflect.Method, bool) { //nolint: unused +func (o goStructObject) method(name string) (reflect.Method, bool) { //nolint:unused return reflect.Indirect(o.value).Type().MethodByName(name) } diff --git a/type_go_struct_test.go b/type_go_struct_test.go index e9e6e004..0dd0ab7d 100644 --- a/type_go_struct_test.go +++ b/type_go_struct_test.go @@ -19,13 +19,6 @@ func TestGoStructEmbeddedFields(t *testing.T) { tt(t, func() { test, vm := test() - var b B - - b.A1 = "a1" - b.A2 = "a2" - b.A3 = "a3" - b.B1 = "b1" - vm.Set("v", B{A{"a1", "a2", "a3"}, "b1"}) test(`[v.a1,v.a2,v.a3,v.b1]`, "a1,a2,a3,b1") @@ -34,9 +27,9 @@ func TestGoStructEmbeddedFields(t *testing.T) { func TestGoStructNilBoolPointerField(t *testing.T) { type S struct { - A int `json:"a"` - B *bool `json:"b"` C interface{} `json:"c"` + B *bool `json:"b"` + A int `json:"a"` } tt(t, func() { @@ -58,13 +51,13 @@ func TestGoStructError(t *testing.T) { } type S2 struct { - A []S1 `json:"a"` B S1 `json:"b"` + A []S1 `json:"a"` } type S3 struct { - A []S2 `json:"a"` B S2 `json:"b"` + A []S2 `json:"a"` } tt(t, func() { diff --git a/type_reference.go b/type_reference.go index 3e45b421..1d2d0d3e 100644 --- a/type_reference.go +++ b/type_reference.go @@ -1,20 +1,20 @@ package otto type referencer interface { - invalid() bool // IsUnresolvableReference - getValue() Value // getValue - putValue(Value) string // PutValue + invalid() bool // IsUnresolvableReference + getValue() Value // getValue + putValue(value Value) string // PutValue delete() bool } // PropertyReference type propertyReference struct { - name string - strict bool base *object runtime *runtime + name string at at + strict bool } func newPropertyReference(rt *runtime, base *object, name string, strict bool, atv at) *propertyReference { @@ -55,9 +55,9 @@ func (pr *propertyReference) delete() bool { } type stashReference struct { + base stasher name string strict bool - base stasher } func (sr *stashReference) invalid() bool { diff --git a/type_regexp.go b/type_regexp.go index 19001fe8..1945828d 100644 --- a/type_regexp.go +++ b/type_regexp.go @@ -9,11 +9,11 @@ import ( type regExpObject struct { regularExpression *regexp.Regexp + source string + flags string global bool ignoreCase bool multiline bool - source string - flags string } func (rt *runtime) newRegExpObject(pattern string, flags string) *object { diff --git a/type_string.go b/type_string.go index cea9fc48..63836406 100644 --- a/type_string.go +++ b/type_string.go @@ -8,7 +8,7 @@ import ( type stringObjecter interface { Length() int - At(int) rune + At(at int) rune String() string } diff --git a/value.go b/value.go index 1f86e144..da1c1e10 100644 --- a/value.go +++ b/value.go @@ -27,8 +27,8 @@ const ( // Value is the representation of a JavaScript value. type Value struct { - kind valueKind value interface{} + kind valueKind } func (v Value) safe() bool { @@ -129,7 +129,7 @@ func (v Value) constructSafe(rt *runtime, this Value, argumentList ...interface{ return result, err } -func (v Value) construct(rt *runtime, this Value, argumentList ...interface{}) Value { //nolint: unparam +func (v Value) construct(rt *runtime, this Value, argumentList ...interface{}) Value { //nolint:unparam if fn, ok := v.value.(*object); ok { return fn.construct(fn.runtime.toValueArray(argumentList...)) } @@ -204,35 +204,35 @@ func (v Value) Class() string { return v.value.(*object).class } -func (v Value) isArray() bool { //nolint: unused +func (v Value) isArray() bool { //nolint:unused if v.kind != valueObject { return false } return isArray(v.value.(*object)) } -func (v Value) isStringObject() bool { //nolint: unused +func (v Value) isStringObject() bool { //nolint:unused if v.kind != valueObject { return false } return v.value.(*object).class == classStringName } -func (v Value) isBooleanObject() bool { //nolint: unused +func (v Value) isBooleanObject() bool { //nolint:unused if v.kind != valueObject { return false } return v.value.(*object).class == classBooleanName } -func (v Value) isNumberObject() bool { //nolint: unused +func (v Value) isNumberObject() bool { //nolint:unused if v.kind != valueObject { return false } return v.value.(*object).class == classNumberName } -func (v Value) isDate() bool { //nolint: unused +func (v Value) isDate() bool { //nolint:unused if v.kind != valueObject { return false } @@ -246,7 +246,7 @@ func (v Value) isRegExp() bool { return v.value.(*object).class == classRegExpName } -func (v Value) isError() bool { //nolint: unused +func (v Value) isError() bool { //nolint:unused if v.kind != valueObject { return false } @@ -272,46 +272,46 @@ func toValue(value interface{}) Value { case Value: return value case bool: - return Value{valueBoolean, value} + return Value{kind: valueBoolean, value: value} case int: - return Value{valueNumber, value} + return Value{kind: valueNumber, value: value} case int8: - return Value{valueNumber, value} + return Value{kind: valueNumber, value: value} case int16: - return Value{valueNumber, value} + return Value{kind: valueNumber, value: value} case int32: - return Value{valueNumber, value} + return Value{kind: valueNumber, value: value} case int64: - return Value{valueNumber, value} + return Value{kind: valueNumber, value: value} case uint: - return Value{valueNumber, value} + return Value{kind: valueNumber, value: value} case uint8: - return Value{valueNumber, value} + return Value{kind: valueNumber, value: value} case uint16: - return Value{valueNumber, value} + return Value{kind: valueNumber, value: value} case uint32: - return Value{valueNumber, value} + return Value{kind: valueNumber, value: value} case uint64: - return Value{valueNumber, value} + return Value{kind: valueNumber, value: value} case float32: - return Value{valueNumber, float64(value)} + return Value{kind: valueNumber, value: float64(value)} case float64: - return Value{valueNumber, value} + return Value{kind: valueNumber, value: value} case []uint16: - return Value{valueString, value} + return Value{kind: valueString, value: value} case string: - return Value{valueString, value} + return Value{kind: valueString, value: value} // A rune is actually an int32, which is handled above case *object: - return Value{valueObject, value} + return Value{kind: valueObject, value: value} case *Object: - return Value{valueObject, value.object} + return Value{kind: valueObject, value: value.object} case Object: - return Value{valueObject, value.object} + return Value{kind: valueObject, value: value.object} case referencer: // reference is an interface (already a pointer) - return Value{valueReference, value} + return Value{kind: valueReference, value: value} case result: - return Value{valueResult, value} + return Value{kind: valueResult, value: value} case nil: // TODO Ugh. return Value{} @@ -329,33 +329,33 @@ func toValue(value interface{}) Value { } switch value.Kind() { case reflect.Bool: - return Value{valueBoolean, value.Bool()} + return Value{kind: valueBoolean, value: value.Bool()} case reflect.Int: - return Value{valueNumber, int(value.Int())} + return Value{kind: valueNumber, value: int(value.Int())} case reflect.Int8: - return Value{valueNumber, int8(value.Int())} + return Value{kind: valueNumber, value: int8(value.Int())} case reflect.Int16: - return Value{valueNumber, int16(value.Int())} + return Value{kind: valueNumber, value: int16(value.Int())} case reflect.Int32: - return Value{valueNumber, int32(value.Int())} + return Value{kind: valueNumber, value: int32(value.Int())} case reflect.Int64: - return Value{valueNumber, value.Int()} + return Value{kind: valueNumber, value: value.Int()} case reflect.Uint: - return Value{valueNumber, uint(value.Uint())} + return Value{kind: valueNumber, value: uint(value.Uint())} case reflect.Uint8: - return Value{valueNumber, uint8(value.Uint())} + return Value{kind: valueNumber, value: uint8(value.Uint())} case reflect.Uint16: - return Value{valueNumber, uint16(value.Uint())} + return Value{kind: valueNumber, value: uint16(value.Uint())} case reflect.Uint32: - return Value{valueNumber, uint32(value.Uint())} + return Value{kind: valueNumber, value: uint32(value.Uint())} case reflect.Uint64: - return Value{valueNumber, value.Uint()} + return Value{kind: valueNumber, value: value.Uint()} case reflect.Float32: - return Value{valueNumber, float32(value.Float())} + return Value{kind: valueNumber, value: float32(value.Float())} case reflect.Float64: - return Value{valueNumber, value.Float()} + return Value{kind: valueNumber, value: value.Float()} case reflect.String: - return Value{valueString, value.String()} + return Value{kind: valueString, value: value.String()} default: reflectValuePanic(value.Interface(), value.Kind()) } @@ -371,7 +371,7 @@ func toValue(value interface{}) Value { // This method will make return the empty string if there is an error. func (v Value) String() string { var result string - catchPanic(func() { //nolint: errcheck, gosec + catchPanic(func() { //nolint:errcheck, gosec result = v.string() }) return result @@ -398,7 +398,7 @@ func (v Value) numberValue() Value { if v.kind == valueNumber { return v } - return Value{valueNumber, v.float64()} + return Value{kind: valueNumber, value: v.float64()} } // ToFloat will convert the value to a number (float64). @@ -494,23 +494,23 @@ var ( // // ToValue(math.NaN()) func NaNValue() Value { - return Value{valueNumber, nan} + return Value{kind: valueNumber, value: nan} } func positiveInfinityValue() Value { - return Value{valueNumber, positiveInfinity} + return Value{kind: valueNumber, value: positiveInfinity} } func negativeInfinityValue() Value { - return Value{valueNumber, negativeInfinity} + return Value{kind: valueNumber, value: negativeInfinity} } func positiveZeroValue() Value { - return Value{valueNumber, positiveZero} + return Value{kind: valueNumber, value: positiveZero} } func negativeZeroValue() Value { - return Value{valueNumber, negativeZero} + return Value{kind: valueNumber, value: negativeZero} } // TrueValue will return a value representing true. @@ -519,7 +519,7 @@ func negativeZeroValue() Value { // // ToValue(true) func TrueValue() Value { - return Value{valueBoolean, true} + return Value{kind: valueBoolean, value: true} } // FalseValue will return a value representing false. @@ -528,7 +528,7 @@ func TrueValue() Value { // // ToValue(false) func FalseValue() Value { - return Value{valueBoolean, false} + return Value{kind: valueBoolean, value: false} } func sameValue(x Value, y Value) bool { diff --git a/value_number.go b/value_number.go index 6e8e690e..6f14b4bb 100644 --- a/value_number.go +++ b/value_number.go @@ -18,9 +18,9 @@ func parseNumber(value string) float64 { return 0 } - parseFloat := false + var parseFloat bool switch { - case strings.IndexRune(value, '.') != -1: //nolint: gosimple + case strings.ContainsRune(value, '.'): parseFloat = true case stringToNumberParseInteger.MatchString(value): parseFloat = false diff --git a/value_test.go b/value_test.go index a94c01a0..b19e3021 100644 --- a/value_test.go +++ b/value_test.go @@ -30,8 +30,8 @@ type intAlias int func TestToValue(t *testing.T) { tt(t, func() { - _, tester := test() - vm := tester.vm + _, tester2 := test() + vm := tester2.vm value, _ := vm.ToValue(nil) is(value, "undefined") @@ -77,6 +77,42 @@ func TestToValue(t *testing.T) { value, _ = vm.ToValue(&tmp2) is(value, "undefined") } + + { + m := map[int64]string{0: "foo", 1: "bar"} + val, err := vm.ToValue(m) + is(err, nil) + v0, err := val.Object().Get("0") + is(err, nil) + is(v0, m[0]) + v1, err := val.Object().Get("1") + is(err, nil) + is(v1, m[1]) + missing, err := val.Object().Get("2") + is(err, nil) + is(missing, UndefinedValue()) + invalid, err := val.Object().Get("xxx") + is(err, nil) + is(invalid, UndefinedValue()) + } + + { + m := map[uint64]string{0: "foo", 1: "bar"} + val, err := vm.ToValue(m) + is(err, nil) + v0, err := val.Object().Get("0") + is(err, nil) + is(v0, m[0]) + v1, err := val.Object().Get("1") + is(err, nil) + is(v1, m[1]) + missing, err := val.Object().Get("2") + is(err, nil) + is(missing, UndefinedValue()) + invalid, err := val.Object().Get("xxx") + is(err, nil) + is(invalid, UndefinedValue()) + } }) } @@ -286,8 +322,8 @@ func TestExport(t *testing.T) { { abc := struct { - def int ghi interface{} + def int xyz float32 }{} abc.def = 3 @@ -295,6 +331,18 @@ func TestExport(t *testing.T) { vm.Set("abc", abc) is(test(`abc;`).export(), abc) } + + { + abc := map[int64]string{0: "foo", 1: "bar"} + vm.Set("abc", abc) + is(test(`abc;`).export(), abc) + } + + { + abc := map[uint64]string{0: "foo", 1: "bar"} + vm.Set("abc", abc) + is(test(`abc;`).export(), abc) + } }) } @@ -309,7 +357,7 @@ func Test_toReflectValue(t *testing.T) { func TestJSONMarshaling(t *testing.T) { tt(t, func() { - eval, tester := test() + eval, tester2 := test() toJSON := func(val interface{}) string { j, err := json.Marshal(val) is(err, nil) @@ -338,14 +386,14 @@ func TestJSONMarshaling(t *testing.T) { is(toJSON(eval(`({a:1, b:"hi", c:[true,false]})`)), `{"a":1,"b":"hi","c":[true,false]}`) goArray := []string{"foo", "bar"} - val, _ := tester.vm.ToValue(goArray) + val, _ := tester2.vm.ToValue(goArray) is(toJSON(val), `["foo","bar"]`) goMap := map[string]interface{}{ "bar": []int{1, 2, 3}, "foo": 17, } - val, _ = tester.vm.ToValue(goMap) + val, _ = tester2.vm.ToValue(goMap) is(toJSON(val), `{"bar":[1,2,3],"foo":17}`) }) }