Skip to content

Commit

Permalink
Merge branch 'upgrade-tests'
Browse files Browse the repository at this point in the history
  • Loading branch information
dop251 committed Nov 15, 2021
2 parents d6b8625 + f1567f3 commit 26ebff6
Show file tree
Hide file tree
Showing 54 changed files with 1,882 additions and 1,278 deletions.
2 changes: 1 addition & 1 deletion .tc39_test262_checkout.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#!/bin/sh
sha=ddfe24afe3043388827aa220ef623b8540958bbd # this is just the commit it was last tested with
sha=26f1f4567ee7e33163d961c867d689173cbb9065 # this is just the commit it was last tested with
mkdir -p testdata/test262
cd testdata/test262
git init
Expand Down
27 changes: 23 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,11 @@ Features
--------

* Full ECMAScript 5.1 support (including regex and strict mode).
* Passes nearly all [tc39 tests](https://github.com/tc39/test262) tagged with es5id. The goal is to pass all of them.
Note, the current working commit is https://github.com/tc39/test262/commit/ddfe24afe3043388827aa220ef623b8540958bbd.
The next commit removed most of the es5id tags which made it impossible to distinguish which tests to run.
* Passes nearly all [tc39 tests](https://github.com/tc39/test262) for the features implemented so far. The goal is to
pass all of them. Note, the current working commit is https://github.com/tc39/test262/commit/26f1f4567ee7e33163d961c867d689173cbb9065.
* Capable of running Babel, Typescript compiler and pretty much anything written in ES5.
* Sourcemaps.
* Some ES6 functionality, still work in progress, see https://github.com/dop251/goja/milestone/1?closed=1
* Most of ES6 functionality, still work in progress, see https://github.com/dop251/goja/milestone/1?closed=1

Known incompatibilities and caveats
-----------------------------------
Expand Down Expand Up @@ -53,6 +52,26 @@ above is the only reasonable way I can think of without involving finalizers. Th

Note, this does not have any effect on the application logic, but may cause a higher-than-expected memory usage.

### WeakRef and FinalizationRegistry
For the reason mentioned above implementing WeakRef and FinalizationRegistry does not seem to be possible at this stage.

### JSON
`JSON.parse()` uses the standard Go library which operates in UTF-8. Therefore, it cannot correctly parse broken UTF-16
surrogate pairs, for example:

```javascript
JSON.parse(`"\\uD800"`).charCodeAt(0).toString(16) // returns "fffd" instead of "d800"
```

### Date
Conversion from calendar date to epoch timestamp uses the standard Go library which uses `int`, rather than `float` as per
ECMAScript specification. This means if you pass arguments that overflow int to the `Date()` constructor or if there is
an integer overflow, the result will be incorrect, for example:

```javascript
Date.UTC(1970, 0, 1, 80063993375, 29, 1, -288230376151711740) // returns 29256 instead of 29312
```

FAQ
---

Expand Down
101 changes: 50 additions & 51 deletions array.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ func (ai *arrayIterObject) next() Value {
if ai.obj == nil {
return ai.val.runtime.createIterResultObject(_undefined, true)
}
if ta, ok := ai.obj.self.(*typedArrayObject); ok {
ta.viewedArrayBuf.ensureNotDetached(true)
}
l := toLength(ai.obj.self.getStr("length", nil))
index := ai.nextIdx
if index >= l {
Expand Down Expand Up @@ -74,49 +77,45 @@ func (a *arrayObject) init() {
a._put("length", &a.lengthProp)
}

func (a *arrayObject) _setLengthInt(l int64, throw bool) bool {
if l >= 0 && l <= math.MaxUint32 {
l := uint32(l)
ret := true
if l <= a.length {
if a.propValueCount > 0 {
// Slow path
for i := len(a.values) - 1; i >= int(l); i-- {
if prop, ok := a.values[i].(*valueProperty); ok {
if !prop.configurable {
l = uint32(i) + 1
ret = false
break
}
a.propValueCount--
func (a *arrayObject) _setLengthInt(l uint32, throw bool) bool {
ret := true
if l <= a.length {
if a.propValueCount > 0 {
// Slow path
for i := len(a.values) - 1; i >= int(l); i-- {
if prop, ok := a.values[i].(*valueProperty); ok {
if !prop.configurable {
l = uint32(i) + 1
ret = false
break
}
a.propValueCount--
}
}
}
if l <= uint32(len(a.values)) {
if l >= 16 && l < uint32(cap(a.values))>>2 {
ar := make([]Value, l)
copy(ar, a.values)
a.values = ar
} else {
ar := a.values[l:len(a.values)]
for i := range ar {
ar[i] = nil
}
a.values = a.values[:l]
}
if l <= uint32(len(a.values)) {
if l >= 16 && l < uint32(cap(a.values))>>2 {
ar := make([]Value, l)
copy(ar, a.values)
a.values = ar
} else {
ar := a.values[l:len(a.values)]
for i := range ar {
ar[i] = nil
}
a.values = a.values[:l]
}
a.length = l
if !ret {
a.val.runtime.typeErrorResult(throw, "Cannot redefine property: length")
}
return ret
}
panic(a.val.runtime.newError(a.val.runtime.global.RangeError, "Invalid array length"))
a.length = l
if !ret {
a.val.runtime.typeErrorResult(throw, "Cannot redefine property: length")
}
return ret
}

func (a *arrayObject) setLengthInt(l int64, throw bool) bool {
if l == int64(a.length) {
func (a *arrayObject) setLengthInt(l uint32, throw bool) bool {
if l == a.length {
return true
}
if !a.lengthProp.writable {
Expand All @@ -126,19 +125,15 @@ func (a *arrayObject) setLengthInt(l int64, throw bool) bool {
return a._setLengthInt(l, throw)
}

func (a *arrayObject) setLength(v Value, throw bool) bool {
l, ok := toIntIgnoreNegZero(v)
if ok && l == int64(a.length) {
func (a *arrayObject) setLength(v uint32, throw bool) bool {
if v == a.length {
return true
}
if !a.lengthProp.writable {
a.val.runtime.typeErrorResult(throw, "length is not writable")
return false
}
if ok {
return a._setLengthInt(l, throw)
}
panic(a.val.runtime.newError(a.val.runtime.global.RangeError, "Invalid array length"))
return a._setLengthInt(v, throw)
}

func (a *arrayObject) getIdx(idx valueInt, receiver Value) Value {
Expand Down Expand Up @@ -237,7 +232,7 @@ func (a *arrayObject) _setOwnIdx(idx uint32, val Value, throw bool) bool {
return false
} else {
if idx >= a.length {
if !a.setLengthInt(int64(idx)+1, throw) {
if !a.setLengthInt(idx+1, throw) {
return false
}
}
Expand Down Expand Up @@ -268,7 +263,7 @@ func (a *arrayObject) setOwnStr(name unistring.String, val Value, throw bool) bo
return a._setOwnIdx(idx, val, throw)
} else {
if name == "length" {
return a.setLength(val, throw)
return a.setLength(a.val.runtime.toLengthUint32(val), throw)
} else {
return a.baseObject.setOwnStr(name, val, throw)
}
Expand All @@ -291,25 +286,25 @@ type arrayPropIter struct {

func (i *arrayPropIter) next() (propIterItem, iterNextFunc) {
for i.idx < len(i.a.values) && i.idx < i.limit {
name := unistring.String(strconv.Itoa(i.idx))
name := asciiString(strconv.Itoa(i.idx))
prop := i.a.values[i.idx]
i.idx++
if prop != nil {
return propIterItem{name: name, value: prop}, i.next
}
}

return i.a.baseObject.enumerateOwnKeys()()
return i.a.baseObject.iterateStringKeys()()
}

func (a *arrayObject) enumerateOwnKeys() iterNextFunc {
func (a *arrayObject) iterateStringKeys() iterNextFunc {
return (&arrayPropIter{
a: a,
limit: len(a.values),
}).next
}

func (a *arrayObject) ownKeys(all bool, accum []Value) []Value {
func (a *arrayObject) stringKeys(all bool, accum []Value) []Value {
for i, prop := range a.values {
name := strconv.Itoa(i)
if prop != nil {
Expand All @@ -321,7 +316,7 @@ func (a *arrayObject) ownKeys(all bool, accum []Value) []Value {
accum = append(accum, asciiString(name))
}
}
return a.baseObject.ownKeys(all, accum)
return a.baseObject.stringKeys(all, accum)
}

func (a *arrayObject) hasOwnPropertyStr(name unistring.String) bool {
Expand Down Expand Up @@ -373,15 +368,19 @@ func (a *arrayObject) expand(idx uint32) bool {
return true
}

func (r *Runtime) defineArrayLength(prop *valueProperty, descr PropertyDescriptor, setter func(Value, bool) bool, throw bool) bool {
func (r *Runtime) defineArrayLength(prop *valueProperty, descr PropertyDescriptor, setter func(uint32, bool) bool, throw bool) bool {
var newLen uint32
ret := true
if descr.Value != nil {
newLen = r.toLengthUint32(descr.Value)
}

if descr.Configurable == FLAG_TRUE || descr.Enumerable == FLAG_TRUE || descr.Getter != nil || descr.Setter != nil {
ret = false
goto Reject
}

if newLen := descr.Value; newLen != nil {
if descr.Value != nil {
ret = setter(newLen, false)
} else {
ret = true
Expand Down Expand Up @@ -415,7 +414,7 @@ func (a *arrayObject) _defineIdxProperty(idx uint32, desc PropertyDescriptor, th
prop, ok := a.baseObject._defineOwnProperty(unistring.String(strconv.FormatUint(uint64(idx), 10)), existing, desc, throw)
if ok {
if idx >= a.length {
if !a.setLengthInt(int64(idx)+1, throw) {
if !a.setLengthInt(idx+1, throw) {
return false
}
}
Expand Down

0 comments on commit 26ebff6

Please sign in to comment.