289b30715d
This commit converts the dependency management from Godeps to the vendor folder, also switching the tool from godep to trash. Since the upstream tool lacks a few features proposed via a few PRs, until those PRs are merged in (if), use github.com/karalabe/trash. You can update dependencies via trash --update. All dependencies have been updated to their latest version. Parts of the build system are reworked to drop old notions of Godeps and invocation of the go vet command so that it doesn't run against the vendor folder, as that will just blow up during vetting. The conversion drops OpenCL (and hence GPU mining support) from ethash and our codebase. The short reasoning is that there's noone to maintain and having opencl libs in our deps messes up builds as go install ./... tries to build them, failing with unsatisfied link errors for the C OpenCL deps. golang.org/x/net/context is not vendored in. We expect it to be fetched by the user (i.e. using go get). To keep ci.go builds reproducible the package is "vendored" in build/_vendor.
496 lines
13 KiB
Go
496 lines
13 KiB
Go
package otto
|
|
|
|
import (
|
|
"bytes"
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
"unicode/utf8"
|
|
)
|
|
|
|
// String
|
|
|
|
func stringValueFromStringArgumentList(argumentList []Value) Value {
|
|
if len(argumentList) > 0 {
|
|
return toValue_string(argumentList[0].string())
|
|
}
|
|
return toValue_string("")
|
|
}
|
|
|
|
func builtinString(call FunctionCall) Value {
|
|
return stringValueFromStringArgumentList(call.ArgumentList)
|
|
}
|
|
|
|
func builtinNewString(self *_object, argumentList []Value) Value {
|
|
return toValue_object(self.runtime.newString(stringValueFromStringArgumentList(argumentList)))
|
|
}
|
|
|
|
func builtinString_toString(call FunctionCall) Value {
|
|
return call.thisClassObject("String").primitiveValue()
|
|
}
|
|
func builtinString_valueOf(call FunctionCall) Value {
|
|
return call.thisClassObject("String").primitiveValue()
|
|
}
|
|
|
|
func builtinString_fromCharCode(call FunctionCall) Value {
|
|
chrList := make([]uint16, len(call.ArgumentList))
|
|
for index, value := range call.ArgumentList {
|
|
chrList[index] = toUint16(value)
|
|
}
|
|
return toValue_string16(chrList)
|
|
}
|
|
|
|
func builtinString_charAt(call FunctionCall) Value {
|
|
checkObjectCoercible(call.runtime, call.This)
|
|
idx := int(call.Argument(0).number().int64)
|
|
chr := stringAt(call.This._object().stringValue(), idx)
|
|
if chr == utf8.RuneError {
|
|
return toValue_string("")
|
|
}
|
|
return toValue_string(string(chr))
|
|
}
|
|
|
|
func builtinString_charCodeAt(call FunctionCall) Value {
|
|
checkObjectCoercible(call.runtime, call.This)
|
|
idx := int(call.Argument(0).number().int64)
|
|
chr := stringAt(call.This._object().stringValue(), idx)
|
|
if chr == utf8.RuneError {
|
|
return NaNValue()
|
|
}
|
|
return toValue_uint16(uint16(chr))
|
|
}
|
|
|
|
func builtinString_concat(call FunctionCall) Value {
|
|
checkObjectCoercible(call.runtime, call.This)
|
|
var value bytes.Buffer
|
|
value.WriteString(call.This.string())
|
|
for _, item := range call.ArgumentList {
|
|
value.WriteString(item.string())
|
|
}
|
|
return toValue_string(value.String())
|
|
}
|
|
|
|
func builtinString_indexOf(call FunctionCall) Value {
|
|
checkObjectCoercible(call.runtime, call.This)
|
|
value := call.This.string()
|
|
target := call.Argument(0).string()
|
|
if 2 > len(call.ArgumentList) {
|
|
return toValue_int(strings.Index(value, target))
|
|
}
|
|
start := toIntegerFloat(call.Argument(1))
|
|
if 0 > start {
|
|
start = 0
|
|
} else if start >= float64(len(value)) {
|
|
if target == "" {
|
|
return toValue_int(len(value))
|
|
}
|
|
return toValue_int(-1)
|
|
}
|
|
index := strings.Index(value[int(start):], target)
|
|
if index >= 0 {
|
|
index += int(start)
|
|
}
|
|
return toValue_int(index)
|
|
}
|
|
|
|
func builtinString_lastIndexOf(call FunctionCall) Value {
|
|
checkObjectCoercible(call.runtime, call.This)
|
|
value := call.This.string()
|
|
target := call.Argument(0).string()
|
|
if 2 > len(call.ArgumentList) || call.ArgumentList[1].IsUndefined() {
|
|
return toValue_int(strings.LastIndex(value, target))
|
|
}
|
|
length := len(value)
|
|
if length == 0 {
|
|
return toValue_int(strings.LastIndex(value, target))
|
|
}
|
|
start := call.ArgumentList[1].number()
|
|
if start.kind == numberInfinity { // FIXME
|
|
// startNumber is infinity, so start is the end of string (start = length)
|
|
return toValue_int(strings.LastIndex(value, target))
|
|
}
|
|
if 0 > start.int64 {
|
|
start.int64 = 0
|
|
}
|
|
end := int(start.int64) + len(target)
|
|
if end > length {
|
|
end = length
|
|
}
|
|
return toValue_int(strings.LastIndex(value[:end], target))
|
|
}
|
|
|
|
func builtinString_match(call FunctionCall) Value {
|
|
checkObjectCoercible(call.runtime, call.This)
|
|
target := call.This.string()
|
|
matcherValue := call.Argument(0)
|
|
matcher := matcherValue._object()
|
|
if !matcherValue.IsObject() || matcher.class != "RegExp" {
|
|
matcher = call.runtime.newRegExp(matcherValue, Value{})
|
|
}
|
|
global := matcher.get("global").bool()
|
|
if !global {
|
|
match, result := execRegExp(matcher, target)
|
|
if !match {
|
|
return nullValue
|
|
}
|
|
return toValue_object(execResultToArray(call.runtime, target, result))
|
|
}
|
|
|
|
{
|
|
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)
|
|
valueArray := make([]Value, matchCount)
|
|
for index := 0; index < matchCount; index++ {
|
|
valueArray[index] = toValue_string(target[result[index][0]:result[index][1]])
|
|
}
|
|
matcher.put("lastIndex", toValue_int(result[matchCount-1][1]), true)
|
|
return toValue_object(call.runtime.newArrayOf(valueArray))
|
|
}
|
|
}
|
|
|
|
var builtinString_replace_Regexp = regexp.MustCompile("\\$(?:[\\$\\&\\'\\`1-9]|0[1-9]|[1-9][0-9])")
|
|
|
|
func builtinString_findAndReplaceString(input []byte, lastIndex int, match []int, target []byte, replaceValue []byte) (output []byte) {
|
|
matchCount := len(match) / 2
|
|
output = input
|
|
if match[0] != lastIndex {
|
|
output = append(output, target[lastIndex:match[0]]...)
|
|
}
|
|
replacement := builtinString_replace_Regexp.ReplaceAllFunc(replaceValue, func(part []byte) []byte {
|
|
// TODO Check if match[0] or match[1] can be -1 in this scenario
|
|
switch part[1] {
|
|
case '$':
|
|
return []byte{'$'}
|
|
case '&':
|
|
return target[match[0]:match[1]]
|
|
case '`':
|
|
return target[:match[0]]
|
|
case '\'':
|
|
return target[match[1]:len(target)]
|
|
}
|
|
matchNumberParse, error := strconv.ParseInt(string(part[1:]), 10, 64)
|
|
matchNumber := int(matchNumberParse)
|
|
if error != nil || matchNumber >= matchCount {
|
|
return []byte{}
|
|
}
|
|
offset := 2 * matchNumber
|
|
if match[offset] != -1 {
|
|
return target[match[offset]:match[offset+1]]
|
|
}
|
|
return []byte{} // The empty string
|
|
})
|
|
output = append(output, replacement...)
|
|
return output
|
|
}
|
|
|
|
func builtinString_replace(call FunctionCall) Value {
|
|
checkObjectCoercible(call.runtime, call.This)
|
|
target := []byte(call.This.string())
|
|
searchValue := call.Argument(0)
|
|
searchObject := searchValue._object()
|
|
|
|
// TODO If a capture is -1?
|
|
var search *regexp.Regexp
|
|
global := false
|
|
find := 1
|
|
if searchValue.IsObject() && searchObject.class == "RegExp" {
|
|
regExp := searchObject.regExpValue()
|
|
search = regExp.regularExpression
|
|
if regExp.global {
|
|
find = -1
|
|
}
|
|
} else {
|
|
search = regexp.MustCompile(regexp.QuoteMeta(searchValue.string()))
|
|
}
|
|
|
|
found := search.FindAllSubmatchIndex(target, find)
|
|
if found == nil {
|
|
return toValue_string(string(target)) // !match
|
|
}
|
|
|
|
{
|
|
lastIndex := 0
|
|
result := []byte{}
|
|
|
|
replaceValue := call.Argument(1)
|
|
if replaceValue.isCallable() {
|
|
target := string(target)
|
|
replace := replaceValue._object()
|
|
for _, match := range found {
|
|
if match[0] != lastIndex {
|
|
result = append(result, target[lastIndex:match[0]]...)
|
|
}
|
|
matchCount := len(match) / 2
|
|
argumentList := make([]Value, matchCount+2)
|
|
for index := 0; index < matchCount; index++ {
|
|
offset := 2 * index
|
|
if match[offset] != -1 {
|
|
argumentList[index] = toValue_string(target[match[offset]:match[offset+1]])
|
|
} else {
|
|
argumentList[index] = Value{}
|
|
}
|
|
}
|
|
argumentList[matchCount+0] = toValue_int(match[0])
|
|
argumentList[matchCount+1] = toValue_string(target)
|
|
replacement := replace.call(Value{}, argumentList, false, nativeFrame).string()
|
|
result = append(result, []byte(replacement)...)
|
|
lastIndex = match[1]
|
|
}
|
|
|
|
} else {
|
|
replace := []byte(replaceValue.string())
|
|
for _, match := range found {
|
|
result = builtinString_findAndReplaceString(result, lastIndex, match, target, replace)
|
|
lastIndex = match[1]
|
|
}
|
|
}
|
|
|
|
if lastIndex != len(target) {
|
|
result = append(result, target[lastIndex:]...)
|
|
}
|
|
|
|
if global && searchObject != nil {
|
|
searchObject.put("lastIndex", toValue_int(lastIndex), true)
|
|
}
|
|
|
|
return toValue_string(string(result))
|
|
}
|
|
}
|
|
|
|
func builtinString_search(call FunctionCall) Value {
|
|
checkObjectCoercible(call.runtime, call.This)
|
|
target := call.This.string()
|
|
searchValue := call.Argument(0)
|
|
search := searchValue._object()
|
|
if !searchValue.IsObject() || search.class != "RegExp" {
|
|
search = call.runtime.newRegExp(searchValue, Value{})
|
|
}
|
|
result := search.regExpValue().regularExpression.FindStringIndex(target)
|
|
if result == nil {
|
|
return toValue_int(-1)
|
|
}
|
|
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()
|
|
|
|
separatorValue := call.Argument(0)
|
|
limitValue := call.Argument(1)
|
|
limit := -1
|
|
if limitValue.IsDefined() {
|
|
limit = int(toUint32(limitValue))
|
|
}
|
|
|
|
if limit == 0 {
|
|
return toValue_object(call.runtime.newArray(0))
|
|
}
|
|
|
|
if separatorValue.IsUndefined() {
|
|
return toValue_object(call.runtime.newArrayOf([]Value{toValue_string(target)}))
|
|
}
|
|
|
|
if separatorValue.isRegExp() {
|
|
targetLength := len(target)
|
|
search := separatorValue._object().regExpValue().regularExpression
|
|
valueArray := []Value{}
|
|
result := search.FindAllStringSubmatchIndex(target, -1)
|
|
lastIndex := 0
|
|
found := 0
|
|
|
|
for _, match := range result {
|
|
if match[0] == match[1] {
|
|
// FIXME Ugh, this is a hack
|
|
if match[0] == 0 || match[0] == targetLength {
|
|
continue
|
|
}
|
|
}
|
|
|
|
if lastIndex != match[0] {
|
|
valueArray = append(valueArray, toValue_string(target[lastIndex:match[0]]))
|
|
found++
|
|
} else if lastIndex == match[0] {
|
|
if lastIndex != -1 {
|
|
valueArray = append(valueArray, toValue_string(""))
|
|
found++
|
|
}
|
|
}
|
|
|
|
lastIndex = match[1]
|
|
if found == limit {
|
|
goto RETURN
|
|
}
|
|
|
|
captureCount := len(match) / 2
|
|
for index := 1; index < captureCount; index++ {
|
|
offset := index * 2
|
|
value := Value{}
|
|
if match[offset] != -1 {
|
|
value = toValue_string(target[match[offset]:match[offset+1]])
|
|
}
|
|
valueArray = append(valueArray, value)
|
|
found++
|
|
if found == limit {
|
|
goto RETURN
|
|
}
|
|
}
|
|
}
|
|
|
|
if found != limit {
|
|
if lastIndex != targetLength {
|
|
valueArray = append(valueArray, toValue_string(target[lastIndex:targetLength]))
|
|
} else {
|
|
valueArray = append(valueArray, toValue_string(""))
|
|
}
|
|
}
|
|
|
|
RETURN:
|
|
return toValue_object(call.runtime.newArrayOf(valueArray))
|
|
|
|
} else {
|
|
separator := separatorValue.string()
|
|
|
|
splitLimit := limit
|
|
excess := false
|
|
if limit > 0 {
|
|
splitLimit = limit + 1
|
|
excess = true
|
|
}
|
|
|
|
split := strings.SplitN(target, separator, splitLimit)
|
|
|
|
if excess && len(split) > limit {
|
|
split = split[:limit]
|
|
}
|
|
|
|
return call.runtime.toValue(split)
|
|
}
|
|
}
|
|
|
|
func builtinString_slice(call FunctionCall) Value {
|
|
checkObjectCoercible(call.runtime, call.This)
|
|
target := call.This.string()
|
|
|
|
length := int64(len(target))
|
|
start, end := rangeStartEnd(call.ArgumentList, length, false)
|
|
if end-start <= 0 {
|
|
return toValue_string("")
|
|
}
|
|
return toValue_string(target[start:end])
|
|
}
|
|
|
|
func builtinString_substring(call FunctionCall) Value {
|
|
checkObjectCoercible(call.runtime, call.This)
|
|
target := call.This.string()
|
|
|
|
length := int64(len(target))
|
|
start, end := rangeStartEnd(call.ArgumentList, length, true)
|
|
if start > end {
|
|
start, end = end, start
|
|
}
|
|
return toValue_string(target[start:end])
|
|
}
|
|
|
|
func builtinString_substr(call FunctionCall) Value {
|
|
target := call.This.string()
|
|
|
|
size := int64(len(target))
|
|
start, length := rangeStartLength(call.ArgumentList, size)
|
|
|
|
if start >= size {
|
|
return toValue_string("")
|
|
}
|
|
|
|
if length <= 0 {
|
|
return toValue_string("")
|
|
}
|
|
|
|
if start+length >= size {
|
|
// Cap length to be to the end of the string
|
|
// start = 3, length = 5, size = 4 [0, 1, 2, 3]
|
|
// 4 - 3 = 1
|
|
// target[3:4]
|
|
length = size - start
|
|
}
|
|
|
|
return toValue_string(target[start : start+length])
|
|
}
|
|
|
|
func builtinString_toLowerCase(call FunctionCall) Value {
|
|
checkObjectCoercible(call.runtime, call.This)
|
|
return toValue_string(strings.ToLower(call.This.string()))
|
|
}
|
|
|
|
func builtinString_toUpperCase(call FunctionCall) Value {
|
|
checkObjectCoercible(call.runtime, call.This)
|
|
return toValue_string(strings.ToUpper(call.This.string()))
|
|
}
|
|
|
|
// 7.2 Table 2 — Whitespace Characters & 7.3 Table 3 - Line Terminator Characters
|
|
const builtinString_trim_whitespace = "\u0009\u000A\u000B\u000C\u000D\u0020\u00A0\u1680\u180E\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF"
|
|
|
|
func builtinString_trim(call FunctionCall) Value {
|
|
checkObjectCoercible(call.runtime, call.This)
|
|
return toValue(strings.Trim(call.This.string(),
|
|
builtinString_trim_whitespace))
|
|
}
|
|
|
|
// Mozilla extension, not ECMAScript 5
|
|
func builtinString_trimLeft(call FunctionCall) Value {
|
|
checkObjectCoercible(call.runtime, call.This)
|
|
return toValue(strings.TrimLeft(call.This.string(),
|
|
builtinString_trim_whitespace))
|
|
}
|
|
|
|
// Mozilla extension, not ECMAScript 5
|
|
func builtinString_trimRight(call FunctionCall) Value {
|
|
checkObjectCoercible(call.runtime, call.This)
|
|
return toValue(strings.TrimRight(call.This.string(),
|
|
builtinString_trim_whitespace))
|
|
}
|
|
|
|
func builtinString_localeCompare(call FunctionCall) Value {
|
|
checkObjectCoercible(call.runtime, call.This)
|
|
this := call.This.string()
|
|
that := call.Argument(0).string()
|
|
if this < that {
|
|
return toValue_int(-1)
|
|
} else if this == that {
|
|
return toValue_int(0)
|
|
}
|
|
return toValue_int(1)
|
|
}
|
|
|
|
/*
|
|
An alternate version of String.trim
|
|
func builtinString_trim(call FunctionCall) Value {
|
|
checkObjectCoercible(call.This)
|
|
return toValue_string(strings.TrimFunc(call.string(.This), isWhiteSpaceOrLineTerminator))
|
|
}
|
|
*/
|
|
|
|
func builtinString_toLocaleLowerCase(call FunctionCall) Value {
|
|
return builtinString_toLowerCase(call)
|
|
}
|
|
|
|
func builtinString_toLocaleUpperCase(call FunctionCall) Value {
|
|
return builtinString_toUpperCase(call)
|
|
}
|