Skip to content

Commit 9410bca

Browse files
committedSep 6, 2023
Implemented template-backed objects and used them for most of the built-ins. Closes #524, closes #459.
1 parent 3dbe69d commit 9410bca

40 files changed

+2218
-1309
lines changed
 

‎array.go

+13-1
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,18 @@ func (a *arrayObject) hasOwnPropertyIdx(idx valueInt) bool {
332332
return a.baseObject.hasOwnPropertyStr(idx.string())
333333
}
334334

335+
func (a *arrayObject) hasPropertyIdx(idx valueInt) bool {
336+
if a.hasOwnPropertyIdx(idx) {
337+
return true
338+
}
339+
340+
if a.prototype != nil {
341+
return a.prototype.self.hasPropertyIdx(idx)
342+
}
343+
344+
return false
345+
}
346+
335347
func (a *arrayObject) expand(idx uint32) bool {
336348
targetLen := idx + 1
337349
if targetLen > uint32(len(a.values)) {
@@ -509,7 +521,7 @@ func (a *arrayObject) exportType() reflect.Type {
509521

510522
func (a *arrayObject) exportToArrayOrSlice(dst reflect.Value, typ reflect.Type, ctx *objectExportCtx) error {
511523
r := a.val.runtime
512-
if iter := a.getSym(SymIterator, nil); iter == r.global.arrayValues || iter == nil {
524+
if iter := a.getSym(SymIterator, nil); iter == r.getArrayValues() || iter == nil {
513525
l := toIntStrict(int64(a.length))
514526
if typ.Kind() == reflect.Array {
515527
if dst.Len() != l {

‎array_sparse.go

+13-1
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,18 @@ func (a *sparseArrayObject) hasOwnPropertyIdx(idx valueInt) bool {
302302
return a.baseObject.hasOwnPropertyStr(idx.string())
303303
}
304304

305+
func (a *sparseArrayObject) hasPropertyIdx(idx valueInt) bool {
306+
if a.hasOwnPropertyIdx(idx) {
307+
return true
308+
}
309+
310+
if a.prototype != nil {
311+
return a.prototype.self.hasPropertyIdx(idx)
312+
}
313+
314+
return false
315+
}
316+
305317
func (a *sparseArrayObject) expand(idx uint32) bool {
306318
if l := len(a.items); l >= 1024 {
307319
if ii := a.items[l-1].idx; ii > idx {
@@ -458,7 +470,7 @@ func (a *sparseArrayObject) exportType() reflect.Type {
458470

459471
func (a *sparseArrayObject) exportToArrayOrSlice(dst reflect.Value, typ reflect.Type, ctx *objectExportCtx) error {
460472
r := a.val.runtime
461-
if iter := a.getSym(SymIterator, nil); iter == r.global.arrayValues || iter == nil {
473+
if iter := a.getSym(SymIterator, nil); iter == r.getArrayValues() || iter == nil {
462474
l := toIntStrict(int64(a.length))
463475
if typ.Kind() == reflect.Array {
464476
if dst.Len() != l {

‎builtin_array.go

+120-81
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package goja
33
import (
44
"math"
55
"sort"
6+
"sync"
67
)
78

89
func (r *Runtime) newArray(prototype *Object) (a *arrayObject) {
@@ -19,7 +20,7 @@ func (r *Runtime) newArray(prototype *Object) (a *arrayObject) {
1920
}
2021

2122
func (r *Runtime) newArrayObject() *arrayObject {
22-
return r.newArray(r.global.ArrayPrototype)
23+
return r.newArray(r.getArrayPrototype())
2324
}
2425

2526
func setArrayValues(a *arrayObject, values []Value) *arrayObject {
@@ -96,7 +97,7 @@ func (r *Runtime) builtin_newArray(args []Value, proto *Object) *Object {
9697
if float64(al) == float64(f) {
9798
return r.newArrayLength(al)
9899
} else {
99-
panic(r.newError(r.global.RangeError, "Invalid array length"))
100+
panic(r.newError(r.getRangeError(), "Invalid array length"))
100101
}
101102
}
102103
return setArrayValues(r.newArray(proto), []Value{args[0]}).val
@@ -1259,7 +1260,7 @@ func (r *Runtime) checkStdArray(v Value) *arrayObject {
12591260

12601261
func (r *Runtime) checkStdArrayIter(v Value) *arrayObject {
12611262
if arr := r.checkStdArray(v); arr != nil &&
1262-
arr.getSym(SymIterator, nil) == r.global.arrayValues {
1263+
arr.getSym(SymIterator, nil) == r.getArrayValues() {
12631264

12641265
return arr
12651266
}
@@ -1398,80 +1399,110 @@ func (r *Runtime) arrayIterProto_next(call FunctionCall) Value {
13981399
panic(r.NewTypeError("Method Array Iterator.prototype.next called on incompatible receiver %s", r.objectproto_toString(FunctionCall{This: thisObj})))
13991400
}
14001401

1401-
func (r *Runtime) createArrayProto(val *Object) objectImpl {
1402-
o := &arrayObject{
1403-
baseObject: baseObject{
1404-
class: classArray,
1405-
val: val,
1406-
extensible: true,
1407-
prototype: r.global.ObjectPrototype,
1408-
},
1409-
}
1410-
o.init()
1411-
1412-
o._putProp("at", r.newNativeFunc(r.arrayproto_at, nil, "at", nil, 1), true, false, true)
1413-
o._putProp("constructor", r.global.Array, true, false, true)
1414-
o._putProp("concat", r.newNativeFunc(r.arrayproto_concat, nil, "concat", nil, 1), true, false, true)
1415-
o._putProp("copyWithin", r.newNativeFunc(r.arrayproto_copyWithin, nil, "copyWithin", nil, 2), true, false, true)
1416-
o._putProp("entries", r.newNativeFunc(r.arrayproto_entries, nil, "entries", nil, 0), true, false, true)
1417-
o._putProp("every", r.newNativeFunc(r.arrayproto_every, nil, "every", nil, 1), true, false, true)
1418-
o._putProp("fill", r.newNativeFunc(r.arrayproto_fill, nil, "fill", nil, 1), true, false, true)
1419-
o._putProp("filter", r.newNativeFunc(r.arrayproto_filter, nil, "filter", nil, 1), true, false, true)
1420-
o._putProp("find", r.newNativeFunc(r.arrayproto_find, nil, "find", nil, 1), true, false, true)
1421-
o._putProp("findIndex", r.newNativeFunc(r.arrayproto_findIndex, nil, "findIndex", nil, 1), true, false, true)
1422-
o._putProp("findLast", r.newNativeFunc(r.arrayproto_findLast, nil, "findLast", nil, 1), true, false, true)
1423-
o._putProp("findLastIndex", r.newNativeFunc(r.arrayproto_findLastIndex, nil, "findLastIndex", nil, 1), true, false, true)
1424-
o._putProp("flat", r.newNativeFunc(r.arrayproto_flat, nil, "flat", nil, 0), true, false, true)
1425-
o._putProp("flatMap", r.newNativeFunc(r.arrayproto_flatMap, nil, "flatMap", nil, 1), true, false, true)
1426-
o._putProp("forEach", r.newNativeFunc(r.arrayproto_forEach, nil, "forEach", nil, 1), true, false, true)
1427-
o._putProp("includes", r.newNativeFunc(r.arrayproto_includes, nil, "includes", nil, 1), true, false, true)
1428-
o._putProp("indexOf", r.newNativeFunc(r.arrayproto_indexOf, nil, "indexOf", nil, 1), true, false, true)
1429-
o._putProp("join", r.newNativeFunc(r.arrayproto_join, nil, "join", nil, 1), true, false, true)
1430-
o._putProp("keys", r.newNativeFunc(r.arrayproto_keys, nil, "keys", nil, 0), true, false, true)
1431-
o._putProp("lastIndexOf", r.newNativeFunc(r.arrayproto_lastIndexOf, nil, "lastIndexOf", nil, 1), true, false, true)
1432-
o._putProp("map", r.newNativeFunc(r.arrayproto_map, nil, "map", nil, 1), true, false, true)
1433-
o._putProp("pop", r.newNativeFunc(r.arrayproto_pop, nil, "pop", nil, 0), true, false, true)
1434-
o._putProp("push", r.newNativeFunc(r.arrayproto_push, nil, "push", nil, 1), true, false, true)
1435-
o._putProp("reduce", r.newNativeFunc(r.arrayproto_reduce, nil, "reduce", nil, 1), true, false, true)
1436-
o._putProp("reduceRight", r.newNativeFunc(r.arrayproto_reduceRight, nil, "reduceRight", nil, 1), true, false, true)
1437-
o._putProp("reverse", r.newNativeFunc(r.arrayproto_reverse, nil, "reverse", nil, 0), true, false, true)
1438-
o._putProp("shift", r.newNativeFunc(r.arrayproto_shift, nil, "shift", nil, 0), true, false, true)
1439-
o._putProp("slice", r.newNativeFunc(r.arrayproto_slice, nil, "slice", nil, 2), true, false, true)
1440-
o._putProp("some", r.newNativeFunc(r.arrayproto_some, nil, "some", nil, 1), true, false, true)
1441-
o._putProp("sort", r.newNativeFunc(r.arrayproto_sort, nil, "sort", nil, 1), true, false, true)
1442-
o._putProp("splice", r.newNativeFunc(r.arrayproto_splice, nil, "splice", nil, 2), true, false, true)
1443-
o._putProp("toLocaleString", r.newNativeFunc(r.arrayproto_toLocaleString, nil, "toLocaleString", nil, 0), true, false, true)
1444-
o._putProp("toString", r.global.arrayToString, true, false, true)
1445-
o._putProp("unshift", r.newNativeFunc(r.arrayproto_unshift, nil, "unshift", nil, 1), true, false, true)
1446-
o._putProp("values", r.global.arrayValues, true, false, true)
1447-
1448-
o._putSym(SymIterator, valueProp(r.global.arrayValues, true, false, true))
1449-
1450-
bl := r.newBaseObject(nil, classObject)
1451-
bl.setOwnStr("copyWithin", valueTrue, true)
1452-
bl.setOwnStr("entries", valueTrue, true)
1453-
bl.setOwnStr("fill", valueTrue, true)
1454-
bl.setOwnStr("find", valueTrue, true)
1455-
bl.setOwnStr("findIndex", valueTrue, true)
1456-
bl.setOwnStr("findLast", valueTrue, true)
1457-
bl.setOwnStr("findLastIndex", valueTrue, true)
1458-
bl.setOwnStr("flat", valueTrue, true)
1459-
bl.setOwnStr("flatMap", valueTrue, true)
1460-
bl.setOwnStr("includes", valueTrue, true)
1461-
bl.setOwnStr("keys", valueTrue, true)
1462-
bl.setOwnStr("values", valueTrue, true)
1463-
bl.setOwnStr("groupBy", valueTrue, true)
1464-
bl.setOwnStr("groupByToMap", valueTrue, true)
1465-
o._putSym(SymUnscopables, valueProp(bl.val, false, false, true))
1402+
func createArrayProtoTemplate() *objectTemplate {
1403+
t := newObjectTemplate()
1404+
t.protoFactory = func(r *Runtime) *Object {
1405+
return r.global.ObjectPrototype
1406+
}
1407+
1408+
t.putStr("length", func(r *Runtime) Value { return valueProp(_positiveZero, true, false, false) })
1409+
1410+
t.putStr("constructor", func(r *Runtime) Value { return valueProp(r.getArray(), true, false, true) })
1411+
1412+
t.putStr("at", func(r *Runtime) Value { return r.methodProp(r.arrayproto_at, "at", 1) })
1413+
t.putStr("concat", func(r *Runtime) Value { return r.methodProp(r.arrayproto_concat, "concat", 1) })
1414+
t.putStr("copyWithin", func(r *Runtime) Value { return r.methodProp(r.arrayproto_copyWithin, "copyWithin", 2) })
1415+
t.putStr("entries", func(r *Runtime) Value { return r.methodProp(r.arrayproto_entries, "entries", 0) })
1416+
t.putStr("every", func(r *Runtime) Value { return r.methodProp(r.arrayproto_every, "every", 1) })
1417+
t.putStr("fill", func(r *Runtime) Value { return r.methodProp(r.arrayproto_fill, "fill", 1) })
1418+
t.putStr("filter", func(r *Runtime) Value { return r.methodProp(r.arrayproto_filter, "filter", 1) })
1419+
t.putStr("find", func(r *Runtime) Value { return r.methodProp(r.arrayproto_find, "find", 1) })
1420+
t.putStr("findIndex", func(r *Runtime) Value { return r.methodProp(r.arrayproto_findIndex, "findIndex", 1) })
1421+
t.putStr("findLast", func(r *Runtime) Value { return r.methodProp(r.arrayproto_findLast, "findLast", 1) })
1422+
t.putStr("findLastIndex", func(r *Runtime) Value { return r.methodProp(r.arrayproto_findLastIndex, "findLastIndex", 1) })
1423+
t.putStr("flat", func(r *Runtime) Value { return r.methodProp(r.arrayproto_flat, "flat", 0) })
1424+
t.putStr("flatMap", func(r *Runtime) Value { return r.methodProp(r.arrayproto_flatMap, "flatMap", 1) })
1425+
t.putStr("forEach", func(r *Runtime) Value { return r.methodProp(r.arrayproto_forEach, "forEach", 1) })
1426+
t.putStr("includes", func(r *Runtime) Value { return r.methodProp(r.arrayproto_includes, "includes", 1) })
1427+
t.putStr("indexOf", func(r *Runtime) Value { return r.methodProp(r.arrayproto_indexOf, "indexOf", 1) })
1428+
t.putStr("join", func(r *Runtime) Value { return r.methodProp(r.arrayproto_join, "join", 1) })
1429+
t.putStr("keys", func(r *Runtime) Value { return r.methodProp(r.arrayproto_keys, "keys", 0) })
1430+
t.putStr("lastIndexOf", func(r *Runtime) Value { return r.methodProp(r.arrayproto_lastIndexOf, "lastIndexOf", 1) })
1431+
t.putStr("map", func(r *Runtime) Value { return r.methodProp(r.arrayproto_map, "map", 1) })
1432+
t.putStr("pop", func(r *Runtime) Value { return r.methodProp(r.arrayproto_pop, "pop", 0) })
1433+
t.putStr("push", func(r *Runtime) Value { return r.methodProp(r.arrayproto_push, "push", 1) })
1434+
t.putStr("reduce", func(r *Runtime) Value { return r.methodProp(r.arrayproto_reduce, "reduce", 1) })
1435+
t.putStr("reduceRight", func(r *Runtime) Value { return r.methodProp(r.arrayproto_reduceRight, "reduceRight", 1) })
1436+
t.putStr("reverse", func(r *Runtime) Value { return r.methodProp(r.arrayproto_reverse, "reverse", 0) })
1437+
t.putStr("shift", func(r *Runtime) Value { return r.methodProp(r.arrayproto_shift, "shift", 0) })
1438+
t.putStr("slice", func(r *Runtime) Value { return r.methodProp(r.arrayproto_slice, "slice", 2) })
1439+
t.putStr("some", func(r *Runtime) Value { return r.methodProp(r.arrayproto_some, "some", 1) })
1440+
t.putStr("sort", func(r *Runtime) Value { return r.methodProp(r.arrayproto_sort, "sort", 1) })
1441+
t.putStr("splice", func(r *Runtime) Value { return r.methodProp(r.arrayproto_splice, "splice", 2) })
1442+
t.putStr("toLocaleString", func(r *Runtime) Value { return r.methodProp(r.arrayproto_toLocaleString, "toLocaleString", 0) })
1443+
t.putStr("toString", func(r *Runtime) Value { return valueProp(r.getArrayToString(), true, false, true) })
1444+
t.putStr("unshift", func(r *Runtime) Value { return r.methodProp(r.arrayproto_unshift, "unshift", 1) })
1445+
t.putStr("values", func(r *Runtime) Value { return valueProp(r.getArrayValues(), true, false, true) })
1446+
1447+
t.putSym(SymIterator, func(r *Runtime) Value { return valueProp(r.getArrayValues(), true, false, true) })
1448+
t.putSym(SymUnscopables, func(r *Runtime) Value {
1449+
bl := r.newBaseObject(nil, classObject)
1450+
bl.setOwnStr("copyWithin", valueTrue, true)
1451+
bl.setOwnStr("entries", valueTrue, true)
1452+
bl.setOwnStr("fill", valueTrue, true)
1453+
bl.setOwnStr("find", valueTrue, true)
1454+
bl.setOwnStr("findIndex", valueTrue, true)
1455+
bl.setOwnStr("findLast", valueTrue, true)
1456+
bl.setOwnStr("findLastIndex", valueTrue, true)
1457+
bl.setOwnStr("flat", valueTrue, true)
1458+
bl.setOwnStr("flatMap", valueTrue, true)
1459+
bl.setOwnStr("includes", valueTrue, true)
1460+
bl.setOwnStr("keys", valueTrue, true)
1461+
bl.setOwnStr("values", valueTrue, true)
1462+
bl.setOwnStr("groupBy", valueTrue, true)
1463+
bl.setOwnStr("groupByToMap", valueTrue, true)
1464+
1465+
return valueProp(bl.val, false, false, true)
1466+
})
14661467

1467-
return o
1468+
return t
1469+
}
1470+
1471+
var arrayProtoTemplate *objectTemplate
1472+
var arrayProtoTemplateOnce sync.Once
1473+
1474+
func getArrayProtoTemplate() *objectTemplate {
1475+
arrayProtoTemplateOnce.Do(func() {
1476+
arrayProtoTemplate = createArrayProtoTemplate()
1477+
})
1478+
return arrayProtoTemplate
1479+
}
1480+
1481+
func (r *Runtime) getArrayPrototype() *Object {
1482+
ret := r.global.ArrayPrototype
1483+
if ret == nil {
1484+
ret = &Object{runtime: r}
1485+
r.global.ArrayPrototype = ret
1486+
r.newTemplatedArrayObject(getArrayProtoTemplate(), ret)
1487+
}
1488+
return ret
1489+
}
1490+
1491+
func (r *Runtime) getArray() *Object {
1492+
ret := r.global.Array
1493+
if ret == nil {
1494+
ret = &Object{runtime: r}
1495+
ret.self = r.createArray(ret)
1496+
r.global.Array = ret
1497+
}
1498+
return ret
14681499
}
14691500

14701501
func (r *Runtime) createArray(val *Object) objectImpl {
1471-
o := r.newNativeFuncConstructObj(val, r.builtin_newArray, "Array", r.global.ArrayPrototype, 1)
1472-
o._putProp("from", r.newNativeFunc(r.array_from, nil, "from", nil, 1), true, false, true)
1473-
o._putProp("isArray", r.newNativeFunc(r.array_isArray, nil, "isArray", nil, 1), true, false, true)
1474-
o._putProp("of", r.newNativeFunc(r.array_of, nil, "of", nil, 0), true, false, true)
1502+
o := r.newNativeFuncConstructObj(val, r.builtin_newArray, "Array", r.getArrayPrototype(), 1)
1503+
o._putProp("from", r.newNativeFunc(r.array_from, "from", 1), true, false, true)
1504+
o._putProp("isArray", r.newNativeFunc(r.array_isArray, "isArray", 1), true, false, true)
1505+
o._putProp("of", r.newNativeFunc(r.array_of, "of", 0), true, false, true)
14751506
r.putSpeciesReturnThis(o)
14761507

14771508
return o
@@ -1480,20 +1511,28 @@ func (r *Runtime) createArray(val *Object) objectImpl {
14801511
func (r *Runtime) createArrayIterProto(val *Object) objectImpl {
14811512
o := newBaseObjectObj(val, r.getIteratorPrototype(), classObject)
14821513

1483-
o._putProp("next", r.newNativeFunc(r.arrayIterProto_next, nil, "next", nil, 0), true, false, true)
1514+
o._putProp("next", r.newNativeFunc(r.arrayIterProto_next, "next", 0), true, false, true)
14841515
o._putSym(SymToStringTag, valueProp(asciiString(classArrayIterator), false, false, true))
14851516

14861517
return o
14871518
}
14881519

1489-
func (r *Runtime) initArray() {
1490-
r.global.arrayValues = r.newNativeFunc(r.arrayproto_values, nil, "values", nil, 0)
1491-
r.global.arrayToString = r.newNativeFunc(r.arrayproto_toString, nil, "toString", nil, 0)
1492-
1493-
r.global.ArrayPrototype = r.newLazyObject(r.createArrayProto)
1520+
func (r *Runtime) getArrayValues() *Object {
1521+
ret := r.global.arrayValues
1522+
if ret == nil {
1523+
ret = r.newNativeFunc(r.arrayproto_values, "values", 0)
1524+
r.global.arrayValues = ret
1525+
}
1526+
return ret
1527+
}
14941528

1495-
r.global.Array = r.newLazyObject(r.createArray)
1496-
r.addToGlobal("Array", r.global.Array)
1529+
func (r *Runtime) getArrayToString() *Object {
1530+
ret := r.global.arrayToString
1531+
if ret == nil {
1532+
ret = r.newNativeFunc(r.arrayproto_toString, "toString", 0)
1533+
r.global.arrayToString = ret
1534+
}
1535+
return ret
14971536
}
14981537

14991538
func (r *Runtime) getArrayIteratorPrototype() *Object {

‎builtin_arrray_test.go

+16
Original file line numberDiff line numberDiff line change
@@ -323,3 +323,19 @@ func TestArrayFlatMap(t *testing.T) {
323323
`
324324
testScriptWithTestLibX(SCRIPT, _undefined, t)
325325
}
326+
327+
func TestArrayProto(t *testing.T) {
328+
const SCRIPT = `
329+
const a = Array.prototype;
330+
a.push(1, 2, 3, 4, 5);
331+
assert.sameValue(a.length, 5);
332+
assert.sameValue(a[0], 1);
333+
a.length = 3;
334+
assert.sameValue(a.length, 3);
335+
assert(compareArray(a, [1, 2, 3]));
336+
a.shift();
337+
assert.sameValue(a.length, 2);
338+
assert(compareArray(a, [2, 3]));
339+
`
340+
testScriptWithTestLib(SCRIPT, _undefined, t)
341+
}

‎builtin_boolean.go

+22-7
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,27 @@ func (r *Runtime) booleanproto_valueOf(call FunctionCall) Value {
4949
return nil
5050
}
5151

52-
func (r *Runtime) initBoolean() {
53-
r.global.BooleanPrototype = r.newPrimitiveObject(valueFalse, r.global.ObjectPrototype, classBoolean)
54-
o := r.global.BooleanPrototype.self
55-
o._putProp("toString", r.newNativeFunc(r.booleanproto_toString, nil, "toString", nil, 0), true, false, true)
56-
o._putProp("valueOf", r.newNativeFunc(r.booleanproto_valueOf, nil, "valueOf", nil, 0), true, false, true)
52+
func (r *Runtime) getBooleanPrototype() *Object {
53+
ret := r.global.BooleanPrototype
54+
if ret == nil {
55+
ret = r.newPrimitiveObject(valueFalse, r.global.ObjectPrototype, classBoolean)
56+
r.global.BooleanPrototype = ret
57+
o := ret.self
58+
o._putProp("toString", r.newNativeFunc(r.booleanproto_toString, "toString", 0), true, false, true)
59+
o._putProp("valueOf", r.newNativeFunc(r.booleanproto_valueOf, "valueOf", 0), true, false, true)
60+
o._putProp("constructor", r.getBoolean(), true, false, true)
61+
}
62+
return ret
63+
}
5764

58-
r.global.Boolean = r.newNativeFunc(r.builtin_Boolean, r.builtin_newBoolean, "Boolean", r.global.BooleanPrototype, 1)
59-
r.addToGlobal("Boolean", r.global.Boolean)
65+
func (r *Runtime) getBoolean() *Object {
66+
ret := r.global.Boolean
67+
if ret == nil {
68+
ret = &Object{runtime: r}
69+
r.global.Boolean = ret
70+
proto := r.getBooleanPrototype()
71+
r.newNativeFuncAndConstruct(ret, r.builtin_Boolean,
72+
r.wrapNativeConstruct(r.builtin_newBoolean, ret, proto), proto, "Boolean", intToValue(1))
73+
}
74+
return ret
6075
}

1 commit comments

Comments
 (1)

Calvinnix commented on Sep 11, 2023

@Calvinnix

Fantastic addition! Great work on this!

Please sign in to comment.