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

Make parseInt behavior correspond to the 15.1.2.2 algorithm

This commit is contained in:
Robert Krimen 2013-04-28 15:00:26 +02:00
parent 2ec9b0843c
commit b5e40f8a85
3 changed files with 89 additions and 21 deletions

View File

@ -49,18 +49,77 @@ func builtinGlobal_isFinite(call FunctionCall) Value {
return toValue(!math.IsNaN(value) && !math.IsInf(value, 0))
}
func builtinGlobal_parseInt(call FunctionCall) Value {
// Caveat emptor: This implementation does NOT match the specification
string_ := strings.TrimSpace(toString(call.Argument(0)))
radix := call.Argument(1)
radixValue := 0
if radix.IsDefined() {
radixValue = int(toInt32(radix))
// 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(radix + 47)
} else {
alphabet += string(radix+54) + string(radix+86)
}
table = append(table, alphabet)
}
value, err := strconv.ParseInt(string_, radixValue, 64)
return table
}()
func builtinGlobal_parseInt(call FunctionCall) Value {
input := strings.TrimSpace(toString(call.Argument(0)))
if len(input) == 0 {
return NaNValue()
}
radix := int(toInt32(call.Argument(1)))
sign := int64(1)
switch input[0] {
case '+':
sign = 1
input = input[1:]
case '-':
sign = -1
input = input[1:]
}
strip := true
if radix == 0 {
radix = 10
} else {
if radix < 2 || radix > 36 {
return NaNValue()
} else if radix != 16 {
strip = false
}
}
switch len(input) {
case 0:
return NaNValue()
case 1:
default:
if strip {
if input[0] == '0' && (input[1] == 'x' || input[1] == 'X') {
input = input[2:]
radix = 16
}
}
}
alphabet := parseInt_alphabetTable[radix]
if index := strings.IndexFunc(input, func(chr rune) bool {
return !strings.ContainsRune(alphabet, chr)
}); index != -1 {
input = input[0:index]
}
value, err := strconv.ParseInt(input, radix, 64)
if err != nil {
return NaNValue()
}
value *= sign
return toValue(value)
}

View File

@ -701,8 +701,8 @@ func newContext() *_runtime {
"undefined", UndefinedValue(),
"NaN", NaNValue(),
"Infinity", positiveInfinityValue(),
"eval", 1, builtinGlobal_eval,
"parseInt", builtinGlobal_parseInt,
"eval", -1, builtinGlobal_eval,
"parseInt", -2, builtinGlobal_parseInt,
"parseFloat", builtinGlobal_parseFloat,
"isNaN", builtinGlobal_isNaN,
"isFinite", builtinGlobal_isFinite,
@ -722,9 +722,6 @@ func newContext() *_runtime {
self._newError["URIError"] = self.defineError("URIError")
self.eval = self.GlobalObject.get("eval")._object()
// eval.prototype === undefined
self.eval.stash.delete("prototype")
self.eval._Function.Construct = nil
self.GlobalObject.prototype = self.Global.ObjectPrototype
@ -817,11 +814,21 @@ func (runtime *_runtime) newError(name string, message Value) *_object {
}
func (runtime *_runtime) newNativeFunction(_nativeFunction _nativeFunction, length int, name string) *_object {
prototype := true
// TODO Do this a better way...
if 0 > length {
length *= -1
prototype = false
}
self := runtime.newNativeFunctionObject(_nativeFunction, length, name)
self.prototype = runtime.Global.FunctionPrototype
prototype := runtime.newObject()
self.stash.set("prototype", toValue(prototype), _propertyMode(0100))
prototype.stash.set("constructor", toValue(self), _propertyMode(0101))
if prototype {
prototype := runtime.newObject()
self.stash.set("prototype", toValue(prototype), _propertyMode(0100))
prototype.stash.set("constructor", toValue(self), _propertyMode(0101))
} else {
self._Function.Construct = nil
}
return self
}

View File

@ -119,13 +119,15 @@ func Test_parseInt(t *testing.T) {
test(`parseInt(" 11\n")`, "11")
test(`parseInt(" 11\n", 16)`, "17")
test(`parseInt("Xyzzy")`, "NaN")
test(`parseInt("0x0a")`, "10")
test(`parseInt(" 0x11\n", 16)`, "17")
test(`parseInt("0x0aXyzzy", 16)`, "10")
test(`parseInt("0x1", 0)`, "1")
if false {
test(`parseInt(" 0x11\n", 16)`, "17")
// TODO parseInt should return 10 in this scenario
test(`parseInt("0x0aXyzzy")`, "10")
// TODO
test(`parseInt("0x10000000000000000000", 16)`, "75557863725914323419136")
}
test(`parseInt("0x0a", Infinity)`, "10")
test(`parseInt.length === 2`, "true")
test(`parseInt.prototype === undefined`, "true")
}
func Test_parseFloat(t *testing.T) {