1
0
mirror of https://github.com/robertkrimen/otto synced 2025-10-05 19:19:10 +08:00

feat: add github action tests and linting (#418)

Leverage github actions for tests and linting.

This includes fixing a bunch of issues highlighted by golangci
including:
* Dead code.
* Ineffectual assigns.
* Goto warnings.
* Nil return err.
* Reused literal strings.
* Test parameter order.

Also:
* Setup clog.
This commit is contained in:
Steven Hartland 2021-09-27 16:19:28 +01:00 committed by GitHub
parent 209b1fb8e8
commit 9297a9abe4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
64 changed files with 1004 additions and 1078 deletions

13
.clog.toml Normal file
View File

@ -0,0 +1,13 @@
[clog]
repository = "https://github.com/robertkrimen/otto"
subtitle = "Release Notes"
[sections]
"Refactors" = ["refactor"]
"Chores" = ["chore"]
"Continuous Integration" = ["ci"]
"Improvements" = ["imp", "improvement"]
"Features" = ["feat", "feature"]
"Legacy" = ["legacy"]
"QA" = ["qa", "test", "tests"]
"Documentation" = ["doc", "docs"]

24
.github/workflows/test-lint.yml vendored Normal file
View File

@ -0,0 +1,24 @@
name: Go test and lint
on:
push:
branches: '**'
jobs:
go-test-lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Go
uses: actions/setup-go@v2
with:
go-version: 1.17
- name: Test
run: go test -v ./...
- name: golangci-lint
uses: golangci/golangci-lint-action@v2
with:
version: latest

79
.golangci.yml Normal file
View File

@ -0,0 +1,79 @@
run:
concurrency: 4
deadline: 5m
skip-dirs:
- terst
skip-files:
- dbg/dbg.go
linters-settings:
govet:
check-shadowing: false
gocyclo:
min-complexity: 20
maligned:
suggest-new: true
dupl:
threshold: 100
goconst:
min-len: 2
min-occurrences: 4
cyclop:
max-complexity: 15
linters:
enable-all: true
disable:
- dupl
- gas
- errcheck
- gofmt
- gosimple
- interfacer
- megacheck
- maligned
- structcheck
- staticcheck
- unconvert
- unparam
- varcheck
- lll
- prealloc
- gochecknoglobals
- gochecknoinits
- scopelint
- funlen
- godox
- exhaustivestruct
- goerr113
- wsl
- nlreturn
- tagliatelle
- gomnd
- paralleltest
- wrapcheck
- testpackage
- golint
- gofumpt
- forbidigo
- gocognit
- gocritic
- godot
- nakedret
- nestif
- revive
- whitespace
- errorlint
- exhaustive
- forcetypeassert
- ifshort
- stylecheck
- gocyclo
- misspell
- cyclop
issues:
exclude:
- .*Id.* should be .*ID.*
exclude-use-default: false
max-same-issues: 0

View File

@ -2,6 +2,7 @@ package ast
import (
"fmt"
"github.com/robertkrimen/otto/file"
)

View File

@ -1,8 +1,9 @@
package ast
import (
"github.com/robertkrimen/otto/file"
"testing"
"github.com/robertkrimen/otto/file"
)
func TestCommentMap(t *testing.T) {

View File

@ -41,22 +41,6 @@ func builtinGlobal_isFinite(call FunctionCall) Value {
return toValue_bool(!math.IsNaN(value) && !math.IsInf(value, 0))
}
// radix 3 => 2 (ASCII 50) +47
// radix 11 => A/a (ASCII 65/97) +54/+86
var parseInt_alphabetTable = func() []string {
table := []string{"", "", "01"}
for radix := 3; radix <= 36; radix += 1 {
alphabet := table[radix-1]
if radix <= 10 {
alphabet += string(rune(radix + 47))
} else {
alphabet += string(rune(radix+54)) + string(rune(radix+86))
}
table = append(table, alphabet)
}
return table
}()
func digitValue(chr rune) int {
switch {
case '0' <= chr && chr <= '9':
@ -129,7 +113,7 @@ func builtinGlobal_parseInt(call FunctionCall) Value {
for _, chr := range input {
digit := float64(digitValue(chr))
if digit >= base {
goto error
return NaNValue()
}
value = value*base + digit
}
@ -138,7 +122,6 @@ func builtinGlobal_parseInt(call FunctionCall) Value {
}
return toValue_float64(value)
}
error:
return NaNValue()
}
if negative {

View File

@ -38,7 +38,7 @@ func builtinArray_toString(call FunctionCall) Value {
func builtinArray_toLocaleString(call FunctionCall) Value {
separator := ","
thisObject := call.thisObject()
length := int64(toUint32(thisObject.get("length")))
length := int64(toUint32(thisObject.get(propertyLength)))
if length == 0 {
return toValue_string("")
}
@ -70,7 +70,7 @@ func builtinArray_concat(call FunctionCall) Value {
case valueObject:
object := item._object()
if isArray(object) {
length := object.get("length").number().int64
length := object.get(propertyLength).number().int64
for index := int64(0); index < length; index += 1 {
name := strconv.FormatInt(index, 10)
if object.hasProperty(name) {
@ -91,9 +91,9 @@ func builtinArray_concat(call FunctionCall) Value {
func builtinArray_shift(call FunctionCall) Value {
thisObject := call.thisObject()
length := int64(toUint32(thisObject.get("length")))
length := int64(toUint32(thisObject.get(propertyLength)))
if 0 == length {
thisObject.put("length", toValue_int64(0), true)
thisObject.put(propertyLength, toValue_int64(0), true)
return Value{}
}
first := thisObject.get("0")
@ -107,34 +107,34 @@ func builtinArray_shift(call FunctionCall) Value {
}
}
thisObject.delete(arrayIndexToString(length-1), true)
thisObject.put("length", toValue_int64(length-1), true)
thisObject.put(propertyLength, toValue_int64(length-1), true)
return first
}
func builtinArray_push(call FunctionCall) Value {
thisObject := call.thisObject()
itemList := call.ArgumentList
index := int64(toUint32(thisObject.get("length")))
index := int64(toUint32(thisObject.get(propertyLength)))
for len(itemList) > 0 {
thisObject.put(arrayIndexToString(index), itemList[0], true)
itemList = itemList[1:]
index += 1
}
length := toValue_int64(index)
thisObject.put("length", length, true)
thisObject.put(propertyLength, length, true)
return length
}
func builtinArray_pop(call FunctionCall) Value {
thisObject := call.thisObject()
length := int64(toUint32(thisObject.get("length")))
length := int64(toUint32(thisObject.get(propertyLength)))
if 0 == length {
thisObject.put("length", toValue_uint32(0), true)
thisObject.put(propertyLength, toValue_uint32(0), true)
return Value{}
}
last := thisObject.get(arrayIndexToString(length - 1))
thisObject.delete(arrayIndexToString(length-1), true)
thisObject.put("length", toValue_int64(length-1), true)
thisObject.put(propertyLength, toValue_int64(length-1), true)
return last
}
@ -147,7 +147,7 @@ func builtinArray_join(call FunctionCall) Value {
}
}
thisObject := call.thisObject()
length := int64(toUint32(thisObject.get("length")))
length := int64(toUint32(thisObject.get(propertyLength)))
if length == 0 {
return toValue_string("")
}
@ -167,7 +167,7 @@ func builtinArray_join(call FunctionCall) Value {
func builtinArray_splice(call FunctionCall) Value {
thisObject := call.thisObject()
length := int64(toUint32(thisObject.get("length")))
length := int64(toUint32(thisObject.get(propertyLength)))
start := valueToRangeIndex(call.Argument(0), length, false)
deleteCount := length - start
@ -240,7 +240,7 @@ func builtinArray_splice(call FunctionCall) Value {
for index := int64(0); index < itemCount; index++ {
thisObject.put(arrayIndexToString(index+start), itemList[index], true)
}
thisObject.put("length", toValue_int64(int64(length)+itemCount-deleteCount), true)
thisObject.put(propertyLength, toValue_int64(int64(length)+itemCount-deleteCount), true)
return toValue_object(call.runtime.newArrayOf(valueArray))
}
@ -248,7 +248,7 @@ func builtinArray_splice(call FunctionCall) Value {
func builtinArray_slice(call FunctionCall) Value {
thisObject := call.thisObject()
length := int64(toUint32(thisObject.get("length")))
length := int64(toUint32(thisObject.get(propertyLength)))
start, end := rangeStartEnd(call.ArgumentList, length, false)
if start >= end {
@ -270,7 +270,7 @@ func builtinArray_slice(call FunctionCall) Value {
func builtinArray_unshift(call FunctionCall) Value {
thisObject := call.thisObject()
length := int64(toUint32(thisObject.get("length")))
length := int64(toUint32(thisObject.get(propertyLength)))
itemList := call.ArgumentList
itemCount := int64(len(itemList))
@ -289,13 +289,13 @@ func builtinArray_unshift(call FunctionCall) Value {
}
newLength := toValue_int64(length + itemCount)
thisObject.put("length", newLength, true)
thisObject.put(propertyLength, newLength, true)
return newLength
}
func builtinArray_reverse(call FunctionCall) Value {
thisObject := call.thisObject()
length := int64(toUint32(thisObject.get("length")))
length := int64(toUint32(thisObject.get(propertyLength)))
lower := struct {
name string
@ -454,7 +454,7 @@ func arraySortQuickSort(thisObject *_object, left, right uint, compare *_object)
func builtinArray_sort(call FunctionCall) Value {
thisObject := call.thisObject()
length := uint(toUint32(thisObject.get("length")))
length := uint(toUint32(thisObject.get(propertyLength)))
compareValue := call.Argument(0)
compare := compareValue._object()
if compareValue.IsUndefined() {
@ -473,7 +473,7 @@ func builtinArray_isArray(call FunctionCall) Value {
func builtinArray_indexOf(call FunctionCall) Value {
thisObject, matchValue := call.thisObject(), call.Argument(0)
if length := int64(toUint32(thisObject.get("length"))); length > 0 {
if length := int64(toUint32(thisObject.get(propertyLength))); length > 0 {
index := int64(0)
if len(call.ArgumentList) > 1 {
index = call.Argument(1).number().int64
@ -501,7 +501,7 @@ func builtinArray_indexOf(call FunctionCall) Value {
func builtinArray_lastIndexOf(call FunctionCall) Value {
thisObject, matchValue := call.thisObject(), call.Argument(0)
length := int64(toUint32(thisObject.get("length")))
length := int64(toUint32(thisObject.get(propertyLength)))
index := length - 1
if len(call.ArgumentList) > 1 {
index = call.Argument(1).number().int64
@ -531,7 +531,7 @@ func builtinArray_every(call FunctionCall) Value {
thisObject := call.thisObject()
this := toValue_object(thisObject)
if iterator := call.Argument(0); iterator.isCallable() {
length := int64(toUint32(thisObject.get("length")))
length := int64(toUint32(thisObject.get(propertyLength)))
callThis := call.Argument(1)
for index := int64(0); index < length; index++ {
if key := arrayIndexToString(index); thisObject.hasProperty(key) {
@ -550,7 +550,7 @@ func builtinArray_some(call FunctionCall) Value {
thisObject := call.thisObject()
this := toValue_object(thisObject)
if iterator := call.Argument(0); iterator.isCallable() {
length := int64(toUint32(thisObject.get("length")))
length := int64(toUint32(thisObject.get(propertyLength)))
callThis := call.Argument(1)
for index := int64(0); index < length; index++ {
if key := arrayIndexToString(index); thisObject.hasProperty(key) {
@ -568,7 +568,7 @@ func builtinArray_forEach(call FunctionCall) Value {
thisObject := call.thisObject()
this := toValue_object(thisObject)
if iterator := call.Argument(0); iterator.isCallable() {
length := int64(toUint32(thisObject.get("length")))
length := int64(toUint32(thisObject.get(propertyLength)))
callThis := call.Argument(1)
for index := int64(0); index < length; index++ {
if key := arrayIndexToString(index); thisObject.hasProperty(key) {
@ -584,7 +584,7 @@ func builtinArray_map(call FunctionCall) Value {
thisObject := call.thisObject()
this := toValue_object(thisObject)
if iterator := call.Argument(0); iterator.isCallable() {
length := int64(toUint32(thisObject.get("length")))
length := int64(toUint32(thisObject.get(propertyLength)))
callThis := call.Argument(1)
values := make([]Value, length)
for index := int64(0); index < length; index++ {
@ -603,7 +603,7 @@ func builtinArray_filter(call FunctionCall) Value {
thisObject := call.thisObject()
this := toValue_object(thisObject)
if iterator := call.Argument(0); iterator.isCallable() {
length := int64(toUint32(thisObject.get("length")))
length := int64(toUint32(thisObject.get(propertyLength)))
callThis := call.Argument(1)
values := make([]Value, 0)
for index := int64(0); index < length; index++ {
@ -625,7 +625,7 @@ func builtinArray_reduce(call FunctionCall) Value {
if iterator := call.Argument(0); iterator.isCallable() {
initial := len(call.ArgumentList) > 1
start := call.Argument(1)
length := int64(toUint32(thisObject.get("length")))
length := int64(toUint32(thisObject.get(propertyLength)))
index := int64(0)
if length > 0 || initial {
var accumulator Value
@ -657,7 +657,7 @@ func builtinArray_reduceRight(call FunctionCall) Value {
if iterator := call.Argument(0); iterator.isCallable() {
initial := len(call.ArgumentList) > 1
start := call.Argument(1)
length := int64(toUint32(thisObject.get("length")))
length := int64(toUint32(thisObject.get(propertyLength)))
if length > 0 || initial {
index := length - 1
var accumulator Value

View File

@ -14,7 +14,7 @@ func builtinBoolean_toString(call FunctionCall) Value {
value := call.This
if !value.IsBoolean() {
// Will throw a TypeError if ThisObject is not a Boolean
value = call.thisClassObject("Boolean").primitiveValue()
value = call.thisClassObject(classBoolean).primitiveValue()
}
return toValue_string(value.string())
}
@ -22,7 +22,7 @@ func builtinBoolean_toString(call FunctionCall) Value {
func builtinBoolean_valueOf(call FunctionCall) Value {
value := call.This
if !value.IsBoolean() {
value = call.thisClassObject("Boolean").primitiveValue()
value = call.thisClassObject(classBoolean).primitiveValue()
}
return value
}

View File

@ -5,11 +5,11 @@ import (
)
func builtinError(call FunctionCall) Value {
return toValue_object(call.runtime.newError("Error", call.Argument(0), 1))
return toValue_object(call.runtime.newError(classError, call.Argument(0), 1))
}
func builtinNewError(self *_object, argumentList []Value) Value {
return toValue_object(self.runtime.newError("Error", valueOfArrayIndex(argumentList, 0), 0))
return toValue_object(self.runtime.newError(classError, valueOfArrayIndex(argumentList, 0), 0))
}
func builtinError_toString(call FunctionCall) Value {
@ -18,7 +18,7 @@ func builtinError_toString(call FunctionCall) Value {
panic(call.runtime.panicTypeError())
}
name := "Error"
name := classError
nameValue := thisObject.get("name")
if nameValue.IsDefined() {
name = nameValue.string()

View File

@ -2,7 +2,6 @@ package otto
import (
"fmt"
"regexp"
"strings"
"unicode"
@ -30,8 +29,6 @@ func argumentList2parameterList(argumentList []Value) []string {
return parameterList
}
var matchIdentifier = regexp.MustCompile(`^[$_\p{L}][$_\p{L}\d}]*$`)
func builtinNewFunctionNative(runtime *_runtime, argumentList []Value) *_object {
var parameterList, body string
count := len(argumentList)
@ -54,7 +51,7 @@ func builtinNewFunctionNative(runtime *_runtime, argumentList []Value) *_object
}
func builtinFunction_toString(call FunctionCall) Value {
object := call.thisClassObject("Function") // Should throw a TypeError unless Function
object := call.thisClassObject(classFunction) // Should throw a TypeError unless Function
switch fn := object.value.(type) {
case _nativeFunctionObject:
return toValue_string(fmt.Sprintf("function %s() { [native code] }", fn.name))
@ -87,7 +84,7 @@ func builtinFunction_apply(call FunctionCall) Value {
arrayObject := argumentList._object()
thisObject := call.thisObject()
length := int64(toUint32(arrayObject.get("length")))
length := int64(toUint32(arrayObject.get(propertyLength)))
valueArray := make([]Value, length)
for index := int64(0); index < length; index++ {
valueArray[index] = arrayObject.get(arrayIndexToString(index))

View File

@ -123,8 +123,8 @@ func builtinJSON_stringify(call FunctionCall) Value {
switch value.kind {
case valueObject:
switch value.value.(*_object).class {
case "String":
case "Number":
case classString:
case classNumber:
default:
continue
}
@ -142,7 +142,7 @@ func builtinJSON_stringify(call FunctionCall) Value {
propertyList[index] = name
}
ctx.propertyList = propertyList[0:length]
} else if replacer.class == "Function" {
} else if replacer.class == classFunction {
value := toValue_object(replacer)
ctx.replacerFunction = &value
}
@ -150,9 +150,9 @@ func builtinJSON_stringify(call FunctionCall) Value {
if spaceValue, exists := call.getArgument(2); exists {
if spaceValue.kind == valueObject {
switch spaceValue.value.(*_object).class {
case "String":
case classString:
spaceValue = toValue_string(spaceValue.string())
case "Number":
case classNumber:
spaceValue = spaceValue.numberValue()
}
}
@ -216,11 +216,11 @@ func builtinJSON_stringifyWalk(ctx _builtinJSON_stringifyContext, key string, ho
if value.kind == valueObject {
switch value.value.(*_object).class {
case "Boolean":
case classBoolean:
value = value._object().value.(Value)
case "String":
case classString:
value = toValue_string(value.string())
case "Number":
case classNumber:
value = value.numberValue()
}
}
@ -255,7 +255,7 @@ func builtinJSON_stringifyWalk(ctx _builtinJSON_stringifyContext, key string, ho
}
if isArray(holder) {
var length uint32
switch value := holder.get("length").value.(type) {
switch value := holder.get(propertyLength).value.(type) {
case uint32:
length = value
case int:
@ -272,7 +272,7 @@ func builtinJSON_stringifyWalk(ctx _builtinJSON_stringifyContext, key string, ho
array[index] = value
}
return array, true
} else if holder.class != "Function" {
} else if holder.class != classFunction {
object := map[string]interface{}{}
if ctx.propertyList != nil {
for _, name := range ctx.propertyList {

View File

@ -24,7 +24,7 @@ func builtinNewNumber(self *_object, argumentList []Value) Value {
func builtinNumber_toString(call FunctionCall) Value {
// Will throw a TypeError if ThisObject is not a Number
value := call.thisClassObject("Number").primitiveValue()
value := call.thisClassObject(classNumber).primitiveValue()
radix := 10
radixArgument := call.Argument(0)
if radixArgument.IsDefined() {
@ -41,7 +41,7 @@ func builtinNumber_toString(call FunctionCall) Value {
}
func builtinNumber_valueOf(call FunctionCall) Value {
return call.thisClassObject("Number").primitiveValue()
return call.thisClassObject(classNumber).primitiveValue()
}
func builtinNumber_toFixed(call FunctionCall) Value {

View File

@ -66,7 +66,7 @@ func builtinObject_propertyIsEnumerable(call FunctionCall) Value {
}
func builtinObject_toString(call FunctionCall) Value {
result := ""
var result string
if call.This.IsUndefined() {
result = "[object Undefined]"
} else if call.This.IsNull() {

View File

@ -10,7 +10,7 @@ func builtinRegExp(call FunctionCall) Value {
pattern := call.Argument(0)
flags := call.Argument(1)
if object := pattern._object(); object != nil {
if object.class == "RegExp" && flags.IsUndefined() {
if object.class == classRegExp && flags.IsUndefined() {
return pattern
}
}

View File

@ -26,10 +26,10 @@ func builtinNewString(self *_object, argumentList []Value) Value {
}
func builtinString_toString(call FunctionCall) Value {
return call.thisClassObject("String").primitiveValue()
return call.thisClassObject(classString).primitiveValue()
}
func builtinString_valueOf(call FunctionCall) Value {
return call.thisClassObject("String").primitiveValue()
return call.thisClassObject(classString).primitiveValue()
}
func builtinString_fromCharCode(call FunctionCall) Value {
@ -124,7 +124,7 @@ func builtinString_match(call FunctionCall) Value {
target := call.This.string()
matcherValue := call.Argument(0)
matcher := matcherValue._object()
if !matcherValue.IsObject() || matcher.class != "RegExp" {
if !matcherValue.IsObject() || matcher.class != classRegExp {
matcher = call.runtime.newRegExp(matcherValue, Value{})
}
global := matcher.get("global").bool()
@ -138,12 +138,11 @@ func builtinString_match(call FunctionCall) Value {
{
result := matcher.regExpValue().regularExpression.FindAllStringIndex(target, -1)
matchCount := len(result)
if result == nil {
matcher.put("lastIndex", toValue_int(0), true)
return Value{} // !match
}
matchCount = len(result)
matchCount := len(result)
valueArray := make([]Value, matchCount)
for index := 0; index < matchCount; index++ {
valueArray[index] = toValue_string(target[result[index][0]:result[index][1]])
@ -173,9 +172,12 @@ func builtinString_findAndReplaceString(input []byte, lastIndex int, match []int
case '\'':
return target[match[1]:len(target)]
}
matchNumberParse, error := strconv.ParseInt(string(part[1:]), 10, 64)
matchNumberParse, err := strconv.ParseInt(string(part[1:]), 10, 64)
if err != nil {
return []byte{}
}
matchNumber := int(matchNumberParse)
if error != nil || matchNumber >= matchCount {
if matchNumber >= matchCount {
return []byte{}
}
offset := 2 * matchNumber
@ -198,7 +200,7 @@ func builtinString_replace(call FunctionCall) Value {
var search *regexp.Regexp
global := false
find := 1
if searchValue.IsObject() && searchObject.class == "RegExp" {
if searchValue.IsObject() && searchObject.class == classRegExp {
regExp := searchObject.regExpValue()
search = regExp.regularExpression
if regExp.global {
@ -267,7 +269,7 @@ func builtinString_search(call FunctionCall) Value {
target := call.This.string()
searchValue := call.Argument(0)
search := searchValue._object()
if !searchValue.IsObject() || search.class != "RegExp" {
if !searchValue.IsObject() || search.class != classRegExp {
search = call.runtime.newRegExp(searchValue, Value{})
}
result := search.regExpValue().regularExpression.FindStringIndex(target)
@ -277,17 +279,6 @@ func builtinString_search(call FunctionCall) Value {
return toValue_int(result[0])
}
func stringSplitMatch(target string, targetLength int64, index uint, search string, searchLength int64) (bool, uint) {
if int64(index)+searchLength > searchLength {
return false, 0
}
found := strings.Index(target[index:], search)
if 0 > found {
return false, 0
}
return true, uint(found)
}
func builtinString_split(call FunctionCall) Value {
checkObjectCoercible(call.runtime, call.This)
target := call.This.string()

View File

@ -5,6 +5,10 @@ import (
"testing"
)
const (
testAb = "ab"
)
func BenchmarkNativeCallWithString(b *testing.B) {
vm := New()
vm.Set("x", func(a1 string) {})
@ -1405,7 +1409,7 @@ func TestNativeCallMethodWithStruct(t *testing.T) {
vm.Set("x", testNativeCallWithStruct{Prefix: "a"})
vm.Set("t", func(s string) {
if s != "ab" {
if s != testAb {
t.Fail()
}
@ -1432,7 +1436,7 @@ func TestNativeCallPointerMethodWithStruct(t *testing.T) {
vm.Set("x", &testNativeCallWithStruct{Prefix: "a"})
vm.Set("t", func(s string) {
if s != "ab" {
if s != testAb {
t.Fail()
}
@ -1459,7 +1463,7 @@ func TestNativeCallMethodWithStructPointer(t *testing.T) {
vm.Set("x", testNativeCallWithStruct{Prefix: "a"})
vm.Set("t", func(s string) {
if s != "ab" {
if s != testAb {
t.Fail()
}
@ -1486,7 +1490,7 @@ func TestNativeCallPointerMethodWithStructPointer(t *testing.T) {
vm.Set("x", &testNativeCallWithStruct{Prefix: "a"})
vm.Set("t", func(s string) {
if s != "ab" {
if s != testAb {
t.Fail()
}

View File

@ -5,12 +5,6 @@ import (
"github.com/robertkrimen/otto/file"
)
type _file struct {
name string
src string
base int // This will always be 1 or greater
}
type _compiler struct {
file *file.File
program *ast.Program

18
consts.go Normal file
View File

@ -0,0 +1,18 @@
package otto
const (
// Common classes.
classString = "String"
classGoArray = "GoArray"
classNumber = "Number"
classDate = "Date"
classArray = "Array"
classFunction = "Function"
classObject = "Object"
classRegExp = "RegExp"
classBoolean = "Boolean"
classError = "Error"
// Common properties.
propertyLength = "length"
)

View File

@ -4,8 +4,7 @@ import (
"fmt"
)
func ExampleSynopsis() {
func ExampleSynopsis() { // nolint: govet
vm := New()
vm.Run(`
abc = 2 + 2;
@ -70,8 +69,7 @@ func ExampleSynopsis() {
// 4
}
func ExampleConsole() {
func ExampleConsole() { // nolint: govet
vm := New()
console := map[string]interface{}{
"log": func(call FunctionCall) Value {
@ -81,10 +79,11 @@ func ExampleConsole() {
}
err := vm.Set("console", console)
if err != nil {
panic(fmt.Errorf("console error: %w", err))
}
value, err := vm.Run(`
console.log("Hello, World.");
`)
value, err := vm.Run(`console.log("Hello, World.");`)
fmt.Println(value)
fmt.Println(err)

View File

@ -272,7 +272,7 @@ func TestMakeCustomErrorReturn(t *testing.T) {
panic(err)
}
is(v.Class(), "Error")
is(v.Class(), classError)
name, err := v.Object().Get("name")
if err != nil {
@ -306,8 +306,6 @@ func TestMakeCustomError(t *testing.T) {
vm.Set("A", func(c FunctionCall) Value {
panic(vm.MakeCustomError("CarrotError", "carrots is life, carrots is love"))
return UndefinedValue()
})
s, _ := vm.Compile("test.js", `
@ -354,8 +352,6 @@ func TestMakeTypeError(t *testing.T) {
vm.Set("A", func(c FunctionCall) Value {
panic(vm.MakeTypeError("these aren't my glasses"))
return UndefinedValue()
})
s, _ := vm.Compile("test.js", `
@ -388,8 +384,6 @@ func TestMakeRangeError(t *testing.T) {
vm.Set("A", func(c FunctionCall) Value {
panic(vm.MakeRangeError("too many"))
return UndefinedValue()
})
s, _ := vm.Compile("test.js", `
@ -422,8 +416,6 @@ func TestMakeSyntaxError(t *testing.T) {
vm.Set("A", func(c FunctionCall) Value {
panic(vm.MakeSyntaxError("i think you meant \"you're\""))
return UndefinedValue()
})
s, _ := vm.Compile("test.js", `

View File

@ -140,23 +140,6 @@ func (self *_runtime) calculateBinaryExpression(operator token.Token, left Value
panic(hereBeDragons(operator))
}
func valueKindDispatchKey(left _valueKind, right _valueKind) int {
return (int(left) << 2) + int(right)
}
var equalDispatch map[int](func(Value, Value) bool) = makeEqualDispatch()
func makeEqualDispatch() map[int](func(Value, Value) bool) {
key := valueKindDispatchKey
return map[int](func(Value, Value) bool){
key(valueNumber, valueObject): func(x Value, y Value) bool { return x.float64() == y.float64() },
key(valueString, valueObject): func(x Value, y Value) bool { return x.float64() == y.float64() },
key(valueObject, valueNumber): func(x Value, y Value) bool { return x.float64() == y.float64() },
key(valueObject, valueString): func(x Value, y Value) bool { return x.float64() == y.float64() },
}
}
type _lessThanResult int
const (
@ -166,10 +149,7 @@ const (
)
func calculateLessThan(left Value, right Value, leftFirst bool) _lessThanResult {
x := Value{}
y := x
var x, y Value
if leftFirst {
x = toNumberPrimitive(left)
y = toNumberPrimitive(right)
@ -178,7 +158,7 @@ func calculateLessThan(left Value, right Value, leftFirst bool) _lessThanResult
x = toNumberPrimitive(left)
}
result := false
var result bool
if x.kind != valueString || y.kind != valueString {
x, y := x.float64(), y.float64()
if math.IsNaN(x) || math.IsNaN(y) {

View File

@ -8,23 +8,23 @@ import (
)
func TestGoSliceQuickSort(t *testing.T) {
testGoSliceSort("quickSort(testSlice, 0, testSlice.length-1);", jsQuickSort, t)
testGoSliceSort(t, "quickSort(testSlice, 0, testSlice.length-1);", jsQuickSort)
}
func TestGoSliceHeapSort(t *testing.T) {
testGoSliceSort("heapSort(testSlice)", jsHeapSort, t)
testGoSliceSort(t, "heapSort(testSlice)", jsHeapSort)
}
func TestJsArrayQuicksort(t *testing.T) {
testJsArraySort("quickSort(testSlice, 0, testSlice.length-1);", jsQuickSort, t)
testJsArraySort(t, "quickSort(testSlice, 0, testSlice.length-1);", jsQuickSort)
}
func TestJsArrayHeapSort(t *testing.T) {
testJsArraySort("heapSort(testSlice)", jsHeapSort, t)
testJsArraySort(t, "heapSort(testSlice)", jsHeapSort)
}
func TestJsArrayMergeSort(t *testing.T) {
testJsArraySort("testSlice = mergeSort(testSlice)", jsMergeSort, t)
testJsArraySort(t, "testSlice = mergeSort(testSlice)", jsMergeSort)
}
func TestCryptoAes(t *testing.T) {
@ -36,27 +36,26 @@ func TestCryptoAes(t *testing.T) {
}
func BenchmarkGoSliceQuickSort100000000(b *testing.B) {
benchmarkGoSliceSort(100000000, "quickSort(testSlice, 0, testSlice.length-1);", jsQuickSort, b)
benchmarkGoSliceSort(b, 100000000, "quickSort(testSlice, 0, testSlice.length-1);", jsQuickSort)
}
func BenchmarkGoSliceHeapSort100000000(b *testing.B) {
benchmarkGoSliceSort(100000000, "heapSort(testSlice);", jsHeapSort, b)
benchmarkGoSliceSort(b, 100000000, "heapSort(testSlice);", jsHeapSort)
}
func BenchmarkJsArrayQuickSort10000(b *testing.B) {
benchmarkJsArraySort(10000, "quickSort(testSlice, 0, testSlice.length-1);", jsQuickSort, b)
benchmarkJsArraySort(b, 10000, "quickSort(testSlice, 0, testSlice.length-1);", jsQuickSort)
}
func BenchmarkJsArrayMergeSort10000(b *testing.B) {
benchmarkJsArraySort(10000, "mergeSort(testSlice);", jsMergeSort, b)
benchmarkJsArraySort(b, 10000, "mergeSort(testSlice);", jsMergeSort)
}
func BenchmarkJsArrayHeapSort10000(b *testing.B) {
benchmarkJsArraySort(10000, "heapSort(testSlice);", jsHeapSort, b)
benchmarkJsArraySort(b, 10000, "heapSort(testSlice);", jsHeapSort)
}
func BenchmarkCryptoAES(b *testing.B) {
vm := New()
// Make sure VM creation time is not counted in runtime test
@ -67,7 +66,8 @@ func BenchmarkCryptoAES(b *testing.B) {
}
}
func testGoSliceSort(sortFuncCall string, sortCode string, t *testing.T) {
func testGoSliceSort(t *testing.T, sortFuncCall string, sortCode string) {
t.Helper()
tt(t, func() {
test, vm := test()
@ -94,7 +94,8 @@ func testGoSliceSort(sortFuncCall string, sortCode string, t *testing.T) {
})
}
func testJsArraySort(sortFuncCall string, sortCode string, t *testing.T) {
func testJsArraySort(t *testing.T, sortFuncCall string, sortCode string) {
t.Helper()
tt(t, func() {
test, vm := test()
@ -113,7 +114,8 @@ func testJsArraySort(sortFuncCall string, sortCode string, t *testing.T) {
})
}
func benchmarkGoSliceSort(size int, sortFuncCall string, sortCode string, b *testing.B) {
func benchmarkGoSliceSort(b *testing.B, size int, sortFuncCall string, sortCode string) {
b.Helper()
// generate arbitrary slice of 'size'
testSlice := make([]int, size)
for i := 0; i < size; i++ {
@ -133,7 +135,8 @@ func benchmarkGoSliceSort(size int, sortFuncCall string, sortCode string, b *tes
}
}
func benchmarkJsArraySort(size int, sortFuncCall string, sortCode string, b *testing.B) {
func benchmarkJsArraySort(b *testing.B, size int, sortFuncCall string, sortCode string) {
b.Helper()
// generate arbitrary slice of 'size'
testSlice := make([]string, size)
for i, _ := range testSlice {

View File

@ -90,7 +90,7 @@ func (self *_object) hasPrimitive() bool {
}
func (runtime *_runtime) newObject() *_object {
self := runtime.newClassObject("Object")
self := runtime.newClassObject(classObject)
self.prototype = runtime.global.ObjectPrototype
return self
}
@ -134,7 +134,7 @@ func (runtime *_runtime) newRegExp(patternValue Value, flagsValue Value) *_objec
pattern := ""
flags := ""
if object := patternValue._object(); object != nil && object.class == "RegExp" {
if object := patternValue._object(); object != nil && object.class == classRegExp {
if flagsValue.IsDefined() {
panic(runtime.panicTypeError("Cannot supply flags when constructing one RegExp from another"))
}

View File

@ -33,7 +33,7 @@ func TestGlobal(t *testing.T) {
// FIXME enterGlobalScope
if false {
value := runtime.scope.lexical.getBinding("Object", false)._object().call(UndefinedValue(), []Value{toValue(runtime.newObject())}, false, nativeFrame)
value := runtime.scope.lexical.getBinding(classObject, false)._object().call(UndefinedValue(), []Value{toValue(runtime.newObject())}, false, nativeFrame)
is(value.IsObject(), true)
is(value, "[object Object]")
is(value._object().prototype == runtime.global.ObjectPrototype, true)
@ -51,16 +51,16 @@ func TestGlobal(t *testing.T) {
is(call(value, "propertyIsEnumerable", "xyzzy"), true)
is(value._object().get("xyzzy"), "Nothing happens.")
is(call(runtime.scope.lexical.getBinding("Object", false), "isPrototypeOf", value), false)
is(call(runtime.scope.lexical.getBinding("Object", false)._object().get("prototype"), "isPrototypeOf", value), true)
is(call(runtime.scope.lexical.getBinding("Function", false), "isPrototypeOf", value), false)
is(call(runtime.scope.lexical.getBinding(classObject, false), "isPrototypeOf", value), false)
is(call(runtime.scope.lexical.getBinding(classObject, false)._object().get("prototype"), "isPrototypeOf", value), true)
is(call(runtime.scope.lexical.getBinding(classFunction, false), "isPrototypeOf", value), false)
is(runtime.newObject().prototype == runtime.global.Object.get("prototype")._object(), true)
abc := runtime.newBoolean(toValue_bool(true))
is(toValue_object(abc), "true") // TODO Call primitive?
//def := runtime.localGet("Boolean")._object().Construct(UndefinedValue(), []Value{})
//def := runtime.localGet(classBoolean)._object().Construct(UndefinedValue(), []Value{})
//is(def, "false") // TODO Call primitive?
}
}

3
go.sum
View File

@ -1,5 +1,8 @@
github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
gopkg.in/readline.v1 v1.0.0-20160726135117-62c6fe619375 h1:hPki/oSSWOLiI9Gc9jyIoj33O3j29fUc9PlLha2yDj0=
gopkg.in/readline.v1 v1.0.0-20160726135117-62c6fe619375/go.mod h1:lNEQeAhU009zbRxng+XOj5ITVgY24WcbNnQopyfKoYQ=
gopkg.in/sourcemap.v1 v1.0.5 h1:inv58fC9f9J3TK2Y2R1NPntXEn3/wjWHkonhIUODNTI=
gopkg.in/sourcemap.v1 v1.0.5/go.mod h1:2RlvNNSMglmRrcvhfuzp4hQHwOtjxlbjX7UPY/GXb78=

1252
inline.go

File diff suppressed because it is too large Load Diff

View File

@ -72,7 +72,7 @@ const (
// 8.12.8
func (self *_object) DefaultValue(hint _defaultValueHint) Value {
if hint == defaultValueNoHint {
if self.class == "Date" {
if self.class == classDate {
// Date exception
hint = defaultValueHintString
} else {

View File

@ -5,7 +5,6 @@ import (
"regexp"
runtime_ "runtime"
"strconv"
"strings"
)
var isIdentifier_Regexp *regexp.Regexp = regexp.MustCompile(`^[a-zA-Z\$][a-zA-Z0-9\$]*$`)
@ -134,16 +133,8 @@ func rangeStartLength(source []Value, size int64) (start, length int64) {
return
}
func boolFields(input string) (result map[string]bool) {
result = map[string]bool{}
for _, word := range strings.Fields(input) {
result[word] = true
}
return result
}
func hereBeDragons(arguments ...interface{}) string {
pc, _, _, _ := runtime_.Caller(1)
pc, _, _, _ := runtime_.Caller(1) // nolint: dogsled
name := runtime_.FuncForPC(pc).Name()
message := fmt.Sprintf("Here be dragons -- %s", name)
if len(arguments) > 0 {
@ -159,20 +150,3 @@ func hereBeDragons(arguments ...interface{}) string {
}
return message
}
func throwHereBeDragons(arguments ...interface{}) {
panic(hereBeDragons(arguments...))
}
func eachPair(list []interface{}, fn func(_0, _1 interface{})) {
for len(list) > 0 {
var _0, _1 interface{}
_0 = list[0]
list = list[1:] // Pop off first
if len(list) > 0 {
_1 = list[0]
list = list[1:] // Pop off second
}
fn(_0, _1)
}
}

View File

@ -1326,6 +1326,7 @@ func TestOttoCall_clone(t *testing.T) {
value, err := vm2.Run(`
[ abc.toString(), def.toString() ];
`)
is(err, nil)
is(value, `function() { return "abc"; },function() { return "def"; }`)
_, err = vm2.Run(`
@ -1336,11 +1337,13 @@ func TestOttoCall_clone(t *testing.T) {
value, err = vm1.Run(`
[ abc.toString(), def.toString() ];
`)
is(err, nil)
is(value, `function() { return "abc"; },function() { return "def"; }`)
value, err = vm2.Run(`
[ abc.toString(), def.toString() ];
`)
is(err, nil)
is(value, `function() { return "abc"; },function() { return "ghi"; }`)
}
@ -1367,6 +1370,7 @@ func TestOttoRun(t *testing.T) {
is(value, 4)
program, err = parser.ParseFile(nil, "", "var abc; if (!abc) abc = 0; abc += 2; abc;", 0)
is(err, nil)
value, err = vm.Run(program)
is(err, nil)
is(value, 2)
@ -1494,7 +1498,7 @@ func TestOttoContext(t *testing.T) {
builtins := []string{
"escape",
"URIError",
"RegExp",
classRegExp,
"ReferenceError",
"parseFloat",
"parseInt",
@ -1506,23 +1510,23 @@ func TestOttoContext(t *testing.T) {
"isNaN",
"unescape",
"decodeURI",
"Object",
"Function",
classObject,
classFunction,
"RangeError",
"Error",
classError,
"get_context",
"eval",
"Number",
classNumber,
"Math",
"NaN",
"Date",
"Boolean",
classDate,
classBoolean,
"console",
"encodeURI",
"EvalError",
"Array",
classArray,
"TypeError",
"String",
classString,
"isFinite",
"undefined",
}

View File

@ -2,9 +2,10 @@ package parser
import (
"fmt"
"github.com/robertkrimen/otto/ast"
"reflect"
"testing"
"github.com/robertkrimen/otto/ast"
)
func checkComments(actual []*ast.Comment, expected []string, position ast.CommentPosition) error {
@ -31,13 +32,6 @@ func checkComments(actual []*ast.Comment, expected []string, position ast.Commen
return nil
}
func displayStatements(statements []ast.Statement) {
fmt.Printf("Printing statements (%v)\n", len(statements))
for k, v := range statements {
fmt.Printf("Type of line %v: %v\n", k, reflect.TypeOf(v))
}
}
func displayComments(m ast.CommentMap) {
fmt.Printf("Displaying comments:\n")
for n, comments := range m {
@ -404,7 +398,7 @@ t2 = "Nothing happens."
is(checkComments((parser.comments.CommentMap)[program.Body[1]], []string{"Test", "Test2"}, ast.LEADING), nil)
// Misc
parser, program = test(`
parser, _ = test(`
var x = Object.create({y: {
},
// a
@ -413,7 +407,7 @@ var x = Object.create({y: {
is(parser.comments.CommentMap.Size(), 1)
// Misc 2
parser, program = test(`
parser, _ = test(`
var x = Object.create({y: {
},
// a
@ -818,7 +812,7 @@ if(a) {
is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.IfStatement).Alternate], []string{"comment"}, ast.TRAILING), nil)
// If then else pt 8
parser, program = test(`
parser, _ = test(`
if
/*comment*/
(a) {
@ -830,7 +824,7 @@ if
is(parser.comments.CommentMap.Size(), 1)
// If then else pt 9
parser, program = test(`
parser, _ = test(`
if
(a)
/*comment*/{
@ -842,7 +836,7 @@ if
is(parser.comments.CommentMap.Size(), 1)
// If then else pt 10
parser, program = test(`
parser, _ = test(`
if(a){
b
}
@ -971,7 +965,7 @@ while(a) {
is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.WhileStatement).Body.(*ast.BlockStatement)], []string{"comment"}, ast.FINAL), nil)
// While pt 8
parser, program = test(`
parser, _ = test(`
while
/*comment*/(a) {
@ -980,7 +974,7 @@ while
is(parser.comments.CommentMap.Size(), 1)
// While pt 9
parser, program = test(`
parser, _ = test(`
while
(a)
/*comment*/{
@ -1109,7 +1103,7 @@ with(a) {
is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.WithStatement).Body], []string{"comment"}, ast.TRAILING), nil)
// With pt 7
parser, program = test(`
parser, _ = test(`
with
/*comment*/(a) {
}
@ -1117,7 +1111,7 @@ with
is(parser.comments.CommentMap.Size(), 1)
// With pt 8
parser, program = test(`
parser, _ = test(`
with
(a)
/*comment*/{
@ -1176,7 +1170,7 @@ var a, b;
is(checkComments((parser.comments.CommentMap)[program], []string{"comment"}, ast.TRAILING), nil)
// Return
parser, program = test(`
parser, _ = test(`
function f() {
/*comment*/return o
}
@ -1356,7 +1350,7 @@ try {
is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.TryStatement).Finally], []string{"comment"}, ast.LEADING), nil)
// Switch / comment
parser, program = test(`
parser, _ = test(`
var volvo = 1
//comment
switch(abra) {
@ -1365,7 +1359,7 @@ switch(abra) {
is(parser.comments.CommentMap.Size(), 1)
// Switch / comment
parser, program = test(`
parser, _ = test(`
f("string",{
key: "val"
//comment

View File

@ -1,9 +0,0 @@
// This file was AUTOMATICALLY GENERATED by dbg-import (smuggol) for github.com/robertkrimen/dbg
package parser
import (
Dbg "github.com/robertkrimen/otto/dbg"
)
var dbg, dbgf = Dbg.New()

View File

@ -11,7 +11,6 @@ import (
const (
err_UnexpectedToken = "Unexpected token %v"
err_UnexpectedEndOfInput = "Unexpected end of input"
err_UnexpectedEscape = "Unexpected escape"
)
// UnexpectedNumber: 'Unexpected number',
@ -70,7 +69,7 @@ func (self Error) Error() string {
}
func (self *_parser) error(place interface{}, msg string, msgValues ...interface{}) *Error {
idx := file.Idx(0)
var idx file.Idx
switch place := place.(type) {
case int:
idx = self.idxOf(place)
@ -120,8 +119,7 @@ func (self *_parser) errorUnexpectedToken(tkn token.Token) error {
}
// ErrorList is a list of *Errors.
//
type ErrorList []*Error
type ErrorList []*Error // nolint: errname
// Add adds an Error with given position and message to an ErrorList.
func (self *ErrorList) Add(position file.Position, msg string) {

View File

@ -352,7 +352,7 @@ func (self *_parser) parseArrayLiteral() ast.Expression {
for self.token != token.RIGHT_BRACKET && self.token != token.EOF {
if self.token == token.COMMA {
// This kind of comment requires a special empty expression node.
empty := &ast.EmptyExpression{self.idx, self.idx}
empty := &ast.EmptyExpression{Begin: self.idx, End: self.idx}
if self.mode&StoreComments != 0 {
self.comments.SetExpression(empty)

View File

@ -628,14 +628,14 @@ func parseNumberLiteral(literal string) (value interface{}, err error) {
// TODO Is Uint okay? What about -MAX_UINT
value, err = strconv.ParseInt(literal, 0, 64)
if err == nil {
return
return value, nil
}
parseIntErr := err // Save this first error, just in case
value, err = strconv.ParseFloat(literal, 64)
if err == nil {
return
return value, nil
} else if err.(*strconv.NumError).Err == strconv.ErrRange {
// Infinity, etc.
return value, nil
@ -651,7 +651,7 @@ func parseNumberLiteral(literal string) (value interface{}, err error) {
for _, chr := range literal {
digit := digitValue(chr)
if digit >= 16 {
goto error
return nil, errors.New("Illegal numeric literal")
}
value = value*16 + float64(digit)
}
@ -659,7 +659,6 @@ func parseNumberLiteral(literal string) (value interface{}, err error) {
}
}
error:
return nil, errors.New("Illegal numeric literal")
}

View File

@ -854,8 +854,6 @@ func TestParserAST(t *testing.T) {
]
`)
return
test(`
if (!abc && abc.jkl(def) && abc[0] === +abc[0] && abc.length < ghi) {
}

View File

@ -98,9 +98,9 @@ func TestParserErr(t *testing.T) {
return program, parser
}
program, parser := test("", nil)
test("", nil)
program, parser = test(`
program, parser := test(`
var abc;
break; do {
} while(true);
@ -523,7 +523,7 @@ func TestParser(t *testing.T) {
abc()
`, nil)
program := test("", nil)
test("", nil)
test("//", nil)
@ -541,7 +541,7 @@ func TestParser(t *testing.T) {
test("new +", "(anonymous): Line 1:5 Unexpected token +")
program = test(";", nil)
program := test(";", nil)
is(len(program.Body), 1)
is(program.Body[0].(*ast.EmptyStatement).Semicolon, file.Idx(1))
@ -1012,6 +1012,7 @@ func TestPosition(t *testing.T) {
parser = _newParser("", "this.style", 1, nil)
program, err = parser.parse()
is(err, nil)
node = program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.DotExpression).Left.(*ast.ThisExpression)
is(node.Idx0(), file.Idx(1))
is(node.Idx1(), file.Idx(5))

View File

@ -9,7 +9,6 @@ const (
modeEnumerateMask = 0070
modeConfigureMask = 0007
modeOnMask = 0111
modeOffMask = 0000
modeSetMask = 0222 // If value is 2, then mode is neither "On" nor "Off"
)

View File

@ -761,11 +761,11 @@ func TestPassthrough(t *testing.T) {
})
}
type TestDynamicFunctionReturningInterface_MyStruct1 struct{}
type TestDynamicFunctionReturningInterface_MyStruct1 struct{} // nolint: errname
func (m *TestDynamicFunctionReturningInterface_MyStruct1) Error() string { return "MyStruct1" }
type TestDynamicFunctionReturningInterface_MyStruct2 struct{}
type TestDynamicFunctionReturningInterface_MyStruct2 struct{} // nolint: errname
func (m *TestDynamicFunctionReturningInterface_MyStruct2) Error() string { return "MyStruct2" }

View File

@ -36,13 +36,13 @@ func TestRegExp(t *testing.T) {
result := test(`/(a)?/.exec('b')`, ",")
is(result._object().get("0"), "")
is(result._object().get("1"), "undefined")
is(result._object().get("length"), 2)
is(result._object().get(propertyLength), 2)
result = test(`/(a)?(b)?/.exec('b')`, "b,,b")
is(result._object().get("0"), "b")
is(result._object().get("1"), "undefined")
is(result._object().get("2"), "b")
is(result._object().get("length"), 3)
is(result._object().get(propertyLength), 3)
test(`/\u0041/.source`, "\\u0041")
test(`/\a/.source`, "\\a")

View File

@ -41,7 +41,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...)
l = append(l, v...) // nolint: makezero
}
}
}

View File

@ -1,11 +1,9 @@
package otto
import ()
type _resultKind int
const (
resultNormal _resultKind = iota
_ _resultKind = iota
resultReturn
resultBreak
resultContinue

View File

@ -414,14 +414,14 @@ func (self *_runtime) convertCallParameter(v Value, t reflect.Type) (reflect.Val
}
case reflect.Slice:
if o := v._object(); o != nil {
if lv := o.get("length"); lv.IsNumber() {
if lv := o.get(propertyLength); lv.IsNumber() {
l := lv.number().int64
s := reflect.MakeSlice(t, int(l), int(l))
tt := t.Elem()
if o.class == "Array" {
if o.class == classArray {
for i := int64(0); i < l; i++ {
p, ok := o.property[strconv.FormatInt(i, 10)]
if !ok {
@ -440,7 +440,7 @@ func (self *_runtime) convertCallParameter(v Value, t reflect.Type) (reflect.Val
s.Index(int(i)).Set(ev)
}
} else if o.class == "GoArray" {
} else if o.class == classGoArray {
var gslice bool
switch o.value.(type) {
case *_goSliceObject:
@ -504,7 +504,7 @@ func (self *_runtime) convertCallParameter(v Value, t reflect.Type) (reflect.Val
return reflect.Zero(t), fmt.Errorf("converting JavaScript values to Go functions with more than one return value is currently not supported")
}
if o := v._object(); o != nil && o.class == "Function" {
if o := v._object(); o != nil && o.class == classFunction {
return reflect.MakeFunc(t, func(args []reflect.Value) []reflect.Value {
l := make([]interface{}, len(args))
for i, a := range args {
@ -531,7 +531,7 @@ func (self *_runtime) convertCallParameter(v Value, t reflect.Type) (reflect.Val
}), nil
}
case reflect.Struct:
if o := v._object(); o != nil && o.class == "Object" {
if o := v._object(); o != nil && o.class == classObject {
s := reflect.New(t)
for _, k := range o.propertyOrder {

View File

@ -86,34 +86,27 @@ func (self *Script) marshalBinary() ([]byte, error) {
// will return an error.
//
// The binary format can change at any time and should be considered unspecified and opaque.
//
func (self *Script) unmarshalBinary(data []byte) error {
func (self *Script) unmarshalBinary(data []byte) (err error) {
decoder := gob.NewDecoder(bytes.NewReader(data))
err := decoder.Decode(&self.version)
if err != nil {
goto error
defer func() {
if err != nil {
self.version = ""
self.program = nil
self.filename = ""
self.src = ""
}
}()
if err = decoder.Decode(&self.version); err != nil {
return err
}
if self.version != scriptVersion {
err = ErrVersion
goto error
return ErrVersion
}
err = decoder.Decode(&self.program)
if err != nil {
goto error
if err = decoder.Decode(&self.program); err != nil {
return err
}
err = decoder.Decode(&self.filename)
if err != nil {
goto error
if err = decoder.Decode(&self.filename); err != nil {
return err
}
err = decoder.Decode(&self.src)
if err != nil {
goto error
}
return nil
error:
self.version = ""
self.program = nil
self.filename = ""
self.src = ""
return err
return decoder.Decode(&self.src)
}

View File

@ -42,7 +42,7 @@ var target = map[string]string{
// http://api.cdnjs.com/libraries
func fetch(name, location string) error {
response, err := http.Get(location)
response, err := http.Get(location) // nolint: noctx
if err != nil {
return err
}
@ -110,7 +110,7 @@ func main() {
err := func() error {
if flag.Arg(0) == "fetch" {
response, err := http.Get("http://api.cdnjs.com/libraries")
response, err := http.Get("http://api.cdnjs.com/libraries") // nolint: noctx
if err != nil {
return err
}
@ -166,8 +166,8 @@ func main() {
case "parse":
fmt.Fprintln(writer, name, "\t| pass (parse)")
case "re2":
continue
fmt.Fprintln(writer, name, "\t| unknown (re2)")
continue
}
} else {
fmt.Fprintln(writer, name, "\t| fail")

View File

@ -10,6 +10,7 @@ import (
)
func tt(t *testing.T, arguments ...func()) {
t.Helper()
halt := errors.New("A test was taking too long")
timer := time.AfterFunc(20*time.Second, func() {
panic(halt)

View File

@ -74,7 +74,7 @@ const (
COLON // :
QUESTION_MARK // ?
firstKeyword
firstKeyword // nolint: deadcode
IF
IN
DO
@ -108,7 +108,7 @@ const (
DEBUGGER
INSTANCEOF
lastKeyword
lastKeyword // nolint: deadcode
)
var token2string = [...]string{

View File

@ -20,7 +20,7 @@ func (runtime *_runtime) newArgumentsObject(indexOfParameterName []string, stash
self.prototype = runtime.global.ObjectPrototype
self.defineProperty("length", toValue_int(length), 0101, false)
self.defineProperty(propertyLength, toValue_int(length), 0101, false)
return self
}

View File

@ -6,14 +6,14 @@ import (
func (runtime *_runtime) newArrayObject(length uint32) *_object {
self := runtime.newObject()
self.class = "Array"
self.defineProperty("length", toValue_uint32(length), 0100, false)
self.class = classArray
self.defineProperty(propertyLength, toValue_uint32(length), 0100, false)
self.objectClass = _classArray
return self
}
func isArray(object *_object) bool {
return object != nil && (object.class == "Array" || object.class == "GoArray")
return object != nil && (object.class == classArray || object.class == classGoArray)
}
func objectLength(object *_object) uint32 {
@ -21,12 +21,12 @@ func objectLength(object *_object) uint32 {
return 0
}
switch object.class {
case "Array":
return object.get("length").value.(uint32)
case "String":
return uint32(object.get("length").value.(int))
case "GoArray":
return uint32(object.get("length").value.(int))
case classArray:
return object.get(propertyLength).value.(uint32)
case classString:
return uint32(object.get(propertyLength).value.(int))
case classGoArray:
return uint32(object.get(propertyLength).value.(int))
}
return 0
}
@ -41,13 +41,13 @@ func arrayUint32(rt *_runtime, value Value) uint32 {
}
func arrayDefineOwnProperty(self *_object, name string, descriptor _property, throw bool) bool {
lengthProperty := self.getOwnProperty("length")
lengthProperty := self.getOwnProperty(propertyLength)
lengthValue, valid := lengthProperty.value.(Value)
if !valid {
panic("Array.length != Value{}")
}
length := lengthValue.value.(uint32)
if name == "length" {
if name == propertyLength {
if descriptor.value == nil {
return objectDefineOwnProperty(self, name, descriptor, throw)
}
@ -96,7 +96,7 @@ func arrayDefineOwnProperty(self *_object, name string, descriptor _property, th
}
if index >= int64(length) {
lengthProperty.value = toValue_uint32(uint32(index + 1))
objectDefineOwnProperty(self, "length", *lengthProperty, false)
objectDefineOwnProperty(self, propertyLength, *lengthProperty, false)
return true
}
}

View File

@ -1,13 +1,5 @@
package otto
import (
"strconv"
)
func (runtime *_runtime) newBooleanObject(value Value) *_object {
return runtime.newPrimitiveObject("Boolean", toValue_bool(value.bool()))
}
func booleanToString(value bool) string {
return strconv.FormatBool(value)
return runtime.newPrimitiveObject(classBoolean, toValue_bool(value.bool()))
}

View File

@ -84,12 +84,6 @@ func (self *_dateObject) SetTime(time Time.Time) {
self.Set(timeToEpoch(time))
}
func epoch2dateObject(epoch float64) _dateObject {
date := _dateObject{}
date.Set(epoch)
return date
}
func (self *_dateObject) Set(epoch float64) {
// epoch
self.epoch = epochToInteger(epoch)
@ -135,7 +129,7 @@ func timeToEpoch(time Time.Time) float64 {
func (runtime *_runtime) newDateObject(epoch float64) *_object {
self := runtime.newObject()
self.class = "Date"
self.class = classDate
// FIXME This is ugly...
date := _dateObject{}
@ -150,7 +144,7 @@ func (self *_object) dateValue() _dateObject {
}
func dateObjectOf(rt *_runtime, _dateObject *_object) _dateObject {
if _dateObject == nil || _dateObject.class != "Date" {
if _dateObject == nil || _dateObject.class != classDate {
panic(rt.panicTypeError())
}
return _dateObject.dateValue()
@ -165,11 +159,6 @@ func dateFromGoMonth(month Time.Month) int {
return int(month) - 1
}
// Both JavaScript & Go are 0-based (Sunday == 0)
func dateToGoDay(day int) Time.Weekday {
return Time.Weekday(day)
}
func dateFromGoDay(day Time.Weekday) int {
return int(day)
}

View File

@ -1,7 +1,7 @@
package otto
func (rt *_runtime) newErrorObject(name string, message Value, stackFramesToPop int) *_object {
self := rt.newClassObject("Error")
self := rt.newClassObject(classError)
if message.IsDefined() {
msg := message.string()
self.defineProperty("message", toValue_string(msg), 0111, false)

View File

@ -6,7 +6,7 @@ type _constructFunction func(*_object, []Value) Value
// 13.2.2 [[Construct]]
func defaultConstruct(fn *_object, argumentList []Value) Value {
object := fn.runtime.newObject()
object.class = "Object"
object.class = classObject
prototype := fn.get("prototype")
if prototype.kind != valueObject {
@ -38,7 +38,7 @@ type _nativeFunctionObject struct {
}
func (runtime *_runtime) _newNativeFunctionObject(name, file string, line int, native _nativeFunction, length int) *_object {
self := runtime.newClassObject("Function")
self := runtime.newClassObject(classFunction)
self.value = _nativeFunctionObject{
name: name,
file: file,
@ -47,7 +47,7 @@ func (runtime *_runtime) _newNativeFunctionObject(name, file string, line int, n
construct: defaultConstruct,
}
self.defineProperty("name", toValue_string(name), 0000, false)
self.defineProperty("length", toValue_int(length), 0000, false)
self.defineProperty(propertyLength, toValue_int(length), 0000, false)
return self
}
@ -86,19 +86,19 @@ type _bindFunctionObject struct {
}
func (runtime *_runtime) newBoundFunctionObject(target *_object, this Value, argumentList []Value) *_object {
self := runtime.newClassObject("Function")
self := runtime.newClassObject(classFunction)
self.value = _bindFunctionObject{
target: target,
this: this,
argumentList: argumentList,
}
length := int(toInt32(target.get("length")))
length := int(toInt32(target.get(propertyLength)))
length -= len(argumentList)
if length < 0 {
length = 0
}
self.defineProperty("name", toValue_string("bound "+target.get("name").String()), 0000, false)
self.defineProperty("length", toValue_int(length), 0000, false)
self.defineProperty(propertyLength, toValue_int(length), 0000, false)
self.defineProperty("caller", Value{}, 0000, false) // TODO Should throw a TypeError
self.defineProperty("arguments", Value{}, 0000, false) // TODO Should throw a TypeError
return self
@ -127,13 +127,13 @@ type _nodeFunctionObject struct {
}
func (runtime *_runtime) newNodeFunctionObject(node *_nodeFunctionLiteral, stash _stash) *_object {
self := runtime.newClassObject("Function")
self := runtime.newClassObject(classFunction)
self.value = _nodeFunctionObject{
node: node,
stash: stash,
}
self.defineProperty("name", toValue_string(node.name), 0000, false)
self.defineProperty("length", toValue_int(len(node.parameterList)), 0000, false)
self.defineProperty(propertyLength, toValue_int(len(node.parameterList)), 0000, false)
self.defineOwnProperty("caller", _property{
value: _propertyGetSet{
runtime.newNativeFunction("get", "internal", 0, func(fc FunctionCall) Value {

View File

@ -7,7 +7,7 @@ import (
func (runtime *_runtime) newGoArrayObject(value reflect.Value) *_object {
self := runtime.newObject()
self.class = "GoArray"
self.class = classGoArray
self.objectClass = _classGoArray
self.value = _newGoArrayObject(value)
return self
@ -72,7 +72,7 @@ func (self _goArrayObject) setValue(index int64, value Value) bool {
func goArrayGetOwnProperty(self *_object, name string) *_property {
// length
if name == "length" {
if name == propertyLength {
return &_property{
value: toValue(reflect.Indirect(self.value.(*_goArrayObject).value).Len()),
mode: 0,
@ -118,7 +118,7 @@ func goArrayEnumerate(self *_object, all bool, each func(string) bool) {
}
func goArrayDefineOwnProperty(self *_object, name string, descriptor _property, throw bool) bool {
if name == "length" {
if name == propertyLength {
return self.runtime.typeErrorResult(throw)
} else if index := stringToArrayIndex(name); index >= 0 {
object := self.value.(*_goArrayObject)
@ -134,7 +134,7 @@ func goArrayDefineOwnProperty(self *_object, name string, descriptor _property,
func goArrayDelete(self *_object, name string, throw bool) bool {
// length
if name == "length" {
if name == propertyLength {
return self.runtime.typeErrorResult(throw)
}

View File

@ -6,7 +6,7 @@ import (
func (runtime *_runtime) newGoMapObject(value reflect.Value) *_object {
self := runtime.newObject()
self.class = "Object" // TODO Should this be something else?
self.class = classObject // TODO Should this be something else?
self.objectClass = _classGoMap
self.value = _newGoMapObject(value)
return self

View File

@ -7,7 +7,7 @@ import (
func (runtime *_runtime) newGoSliceObject(value reflect.Value) *_object {
self := runtime.newObject()
self.class = "GoArray" // TODO GoSlice?
self.class = classGoArray // TODO GoSlice?
self.objectClass = _classGoSlice
self.value = _newGoSliceObject(value)
return self
@ -46,7 +46,7 @@ func (self _goSliceObject) setValue(index int64, value Value) bool {
func goSliceGetOwnProperty(self *_object, name string) *_property {
// length
if name == "length" {
if name == propertyLength {
return &_property{
value: toValue(self.value.(*_goSliceObject).value.Len()),
mode: 0,
@ -93,7 +93,7 @@ func goSliceEnumerate(self *_object, all bool, each func(string) bool) {
}
func goSliceDefineOwnProperty(self *_object, name string, descriptor _property, throw bool) bool {
if name == "length" {
if name == propertyLength {
return self.runtime.typeErrorResult(throw)
} else if index := stringToArrayIndex(name); index >= 0 {
if self.value.(*_goSliceObject).setValue(index, descriptor.value.(Value)) {

View File

@ -15,7 +15,7 @@ import (
func (runtime *_runtime) newGoStructObject(value reflect.Value) *_object {
self := runtime.newObject()
self.class = "Object" // TODO Should this be something else?
self.class = classObject // TODO Should this be something else?
self.objectClass = _classGoStruct
self.value = _newGoStructObject(value)
return self

View File

@ -1,5 +1,5 @@
package otto
func (runtime *_runtime) newNumberObject(value Value) *_object {
return runtime.newPrimitiveObject("Number", value.numberValue())
return runtime.newPrimitiveObject(classNumber, value.numberValue())
}

View File

@ -54,15 +54,6 @@ func (self *_propertyReference) delete() bool {
return self.base.delete(self.name, self.strict)
}
// ArgumentReference
func newArgumentReference(runtime *_runtime, base *_object, name string, strict bool, at _at) *_propertyReference {
if base == nil {
panic(hereBeDragons())
}
return newPropertyReference(runtime, base, name, strict, at)
}
type _stashReference struct {
name string
strict bool

View File

@ -19,7 +19,7 @@ type _regExpObject struct {
func (runtime *_runtime) newRegExpObject(pattern string, flags string) *_object {
self := runtime.newObject()
self.class = "RegExp"
self.class = classRegExp
global := false
ignoreCase := false
@ -85,7 +85,7 @@ func (self *_object) regExpValue() _regExpObject {
}
func execRegExp(this *_object, target string) (match bool, result []int) {
if this.class != "RegExp" {
if this.class != classRegExp {
panic(this.runtime.panicTypeError("Calling RegExp.exec on a non-RegExp object"))
}
lastIndex := this.get("lastIndex").number().int64

View File

@ -72,8 +72,8 @@ func stringAt(str _stringObject, index int) rune {
func (runtime *_runtime) newStringObject(value Value) *_object {
str := _newStringObject(value.string())
self := runtime.newClassObject("String")
self.defineProperty("length", toValue_int(str.Length()), 0, false)
self := runtime.newClassObject(classString)
self.defineProperty(propertyLength, toValue_int(str.Length()), 0, false)
self.objectClass = _classString
self.value = str
return self

View File

@ -193,7 +193,7 @@ func (value Value) IsFunction() bool {
if value.kind != valueObject {
return false
}
return value.value.(*_object).class == "Function"
return value.value.(*_object).class == classFunction
}
// Class will return the class string of the value or the empty string if value is not an object.
@ -227,42 +227,42 @@ func (value Value) isStringObject() bool {
if value.kind != valueObject {
return false
}
return value.value.(*_object).class == "String"
return value.value.(*_object).class == classString
}
func (value Value) isBooleanObject() bool {
if value.kind != valueObject {
return false
}
return value.value.(*_object).class == "Boolean"
return value.value.(*_object).class == classBoolean
}
func (value Value) isNumberObject() bool {
if value.kind != valueObject {
return false
}
return value.value.(*_object).class == "Number"
return value.value.(*_object).class == classNumber
}
func (value Value) isDate() bool {
if value.kind != valueObject {
return false
}
return value.value.(*_object).class == "Date"
return value.value.(*_object).class == classDate
}
func (value Value) isRegExp() bool {
if value.kind != valueObject {
return false
}
return value.value.(*_object).class == "RegExp"
return value.value.(*_object).class == classRegExp
}
func (value Value) isError() bool {
if value.kind != valueObject {
return false
}
return value.value.(*_object).class == "Error"
return value.value.(*_object).class == classError
}
// ---
@ -503,14 +503,6 @@ var (
__NegativeZero__ float64 = math.Float64frombits(0 | (1 << 63))
)
func positiveInfinity() float64 {
return __PositiveInfinity__
}
func negativeInfinity() float64 {
return __NegativeInfinity__
}
func positiveZero() float64 {
return __PositiveZero__
}
@ -569,62 +561,60 @@ func sameValue(x Value, y Value) bool {
if x.kind != y.kind {
return false
}
result := false
switch x.kind {
case valueUndefined, valueNull:
result = true
return true
case valueNumber:
x := x.float64()
y := y.float64()
if math.IsNaN(x) && math.IsNaN(y) {
result = true
} else {
result = x == y
if result && x == 0 {
// Since +0 != -0
result = math.Signbit(x) == math.Signbit(y)
}
return true
}
if x == y {
if x == 0 {
// Since +0 != -0
return math.Signbit(x) == math.Signbit(y)
}
return true
}
return false
case valueString:
result = x.string() == y.string()
return x.string() == y.string()
case valueBoolean:
result = x.bool() == y.bool()
return x.bool() == y.bool()
case valueObject:
result = x._object() == y._object()
return x._object() == y._object()
default:
panic(hereBeDragons())
}
return result
}
func strictEqualityComparison(x Value, y Value) bool {
if x.kind != y.kind {
return false
}
result := false
switch x.kind {
case valueUndefined, valueNull:
result = true
return true
case valueNumber:
x := x.float64()
y := y.float64()
if math.IsNaN(x) && math.IsNaN(y) {
result = false
} else {
result = x == y
return false
}
return x == y
case valueString:
result = x.string() == y.string()
return x.string() == y.string()
case valueBoolean:
result = x.bool() == y.bool()
return x.bool() == y.bool()
case valueObject:
result = x._object() == y._object()
return x._object() == y._object()
default:
panic(hereBeDragons())
}
return result
}
// Export will attempt to convert the value to a Go representation
@ -676,9 +666,9 @@ func (self Value) export() interface{} {
case *_goSliceObject:
return value.value.Interface()
}
if object.class == "Array" {
if object.class == classArray {
result := make([]interface{}, 0)
lengthValue := object.get("length")
lengthValue := object.get(propertyLength)
length := lengthValue.value.(uint32)
kind := reflect.Invalid
state := 0

View File

@ -85,37 +85,17 @@ func (value Value) float64() float64 {
}
const (
float_2_64 float64 = 18446744073709551616.0
float_2_63 float64 = 9223372036854775808.0
float_2_32 float64 = 4294967296.0
float_2_31 float64 = 2147483648.0
float_2_16 float64 = 65536.0
integer_2_32 int64 = 4294967296
integer_2_31 int64 = 2146483648
sqrt1_2 float64 = math.Sqrt2 / 2
float_2_32 float64 = 4294967296.0
float_2_31 float64 = 2147483648.0
float_2_16 float64 = 65536.0
sqrt1_2 float64 = math.Sqrt2 / 2
)
const (
maxInt8 = math.MaxInt8
minInt8 = math.MinInt8
maxInt16 = math.MaxInt16
minInt16 = math.MinInt16
maxInt32 = math.MaxInt32
minInt32 = math.MinInt32
maxInt64 = math.MaxInt64
minInt64 = math.MinInt64
maxUint8 = math.MaxUint8
maxUint16 = math.MaxUint16
maxUint32 = math.MaxUint32
maxUint64 = math.MaxUint64
maxUint = ^uint(0)
minUint = 0
maxInt = int(^uint(0) >> 1)
minInt = -maxInt - 1
// int64
int64_maxInt int64 = int64(maxInt)
int64_minInt int64 = int64(minInt)
int64_maxInt8 int64 = math.MaxInt8
int64_minInt8 int64 = math.MinInt8
int64_maxInt16 int64 = math.MaxInt16
@ -129,9 +109,7 @@ const (
// float64
float_maxInt float64 = float64(int(^uint(0) >> 1))
float_minInt float64 = float64(int(-maxInt - 1))
float_minUint float64 = float64(0)
float_maxUint float64 = float64(uint(^uint(0)))
float_minUint64 float64 = float64(0)
float_maxUint64 float64 = math.MaxUint64
float_maxInt64 float64 = math.MaxInt64
float_minInt64 float64 = math.MinInt64
@ -220,7 +198,7 @@ func (value Value) number() (number _number) {
return
}
integer := float64(0)
var integer float64
if float > 0 {
integer = math.Floor(float)
} else {

View File

@ -1,9 +1,5 @@
package otto
func toStringPrimitive(value Value) Value {
return _toPrimitive(value, defaultValueHintString)
}
func toNumberPrimitive(value Value) Value {
return _toPrimitive(value, defaultValueHintNumber)
}

View File

@ -253,14 +253,14 @@ func TestExport(t *testing.T) {
3.1459,
[]interface{}{true, false, 0, 3.1459, "abc"},
map[string]interface{}{
"Boolean": true,
"Number": 3.1459,
"String": "abc",
"Array": []interface{}{false, 0, "", nil},
"Object": map[string]interface{}{
"Boolean": false,
"Number": 0,
"String": "def",
classBoolean: true,
classNumber: 3.1459,
classString: "abc",
classArray: []interface{}{false, 0, "", nil},
classObject: map[string]interface{}{
classBoolean: false,
classNumber: 0,
classString: "def",
},
},
}