From 9410bcaa81d2ac8b8e7dbe7c47ba9b9375555697 Mon Sep 17 00:00:00 2001 From: Dmitry Panov Date: Wed, 6 Sep 2023 17:07:31 +0100 Subject: [PATCH] Implemented template-backed objects and used them for most of the built-ins. Closes #524, closes #459. --- array.go | 14 +- array_sparse.go | 14 +- builtin_array.go | 201 +++++++++------- builtin_arrray_test.go | 16 ++ builtin_boolean.go | 29 ++- builtin_date.go | 193 +++++++++------ builtin_error.go | 139 +++++++---- builtin_function.go | 114 +++++++-- builtin_global.go | 101 ++++++-- builtin_json.go | 24 +- builtin_map.go | 49 ++-- builtin_math.go | 133 +++++----- builtin_number.go | 145 ++++++++--- builtin_object.go | 156 ++++++++---- builtin_promise.go | 79 +++--- builtin_proxy.go | 17 +- builtin_reflect.go | 36 +-- builtin_regexp.go | 135 ++++++----- builtin_set.go | 43 ++-- builtin_string.go | 183 +++++++++----- builtin_symbol.go | 40 ++-- builtin_typedarrays.go | 493 ++++++++++++++++++++++++++------------ builtin_weakmap.go | 44 ++-- builtin_weakset.go | 29 ++- func.go | 16 +- object.go | 6 +- object_dynamic.go | 2 +- object_goarray_reflect.go | 2 +- object_goreflect.go | 10 +- object_goslice.go | 2 +- object_lazy.go | 314 ------------------------ object_template.go | 469 ++++++++++++++++++++++++++++++++++++ runtime.go | 214 +++++------------ runtime_test.go | 7 + string_ascii.go | 4 +- string_imported.go | 2 +- string_unicode.go | 4 +- typedarrays.go | 4 +- value.go | 14 +- vm.go | 30 +-- 40 files changed, 2218 insertions(+), 1309 deletions(-) delete mode 100644 object_lazy.go create mode 100644 object_template.go diff --git a/array.go b/array.go index 8b09df8b..7a67a47c 100644 --- a/array.go +++ b/array.go @@ -332,6 +332,18 @@ func (a *arrayObject) hasOwnPropertyIdx(idx valueInt) bool { return a.baseObject.hasOwnPropertyStr(idx.string()) } +func (a *arrayObject) hasPropertyIdx(idx valueInt) bool { + if a.hasOwnPropertyIdx(idx) { + return true + } + + if a.prototype != nil { + return a.prototype.self.hasPropertyIdx(idx) + } + + return false +} + func (a *arrayObject) expand(idx uint32) bool { targetLen := idx + 1 if targetLen > uint32(len(a.values)) { @@ -509,7 +521,7 @@ func (a *arrayObject) exportType() reflect.Type { func (a *arrayObject) exportToArrayOrSlice(dst reflect.Value, typ reflect.Type, ctx *objectExportCtx) error { r := a.val.runtime - if iter := a.getSym(SymIterator, nil); iter == r.global.arrayValues || iter == nil { + if iter := a.getSym(SymIterator, nil); iter == r.getArrayValues() || iter == nil { l := toIntStrict(int64(a.length)) if typ.Kind() == reflect.Array { if dst.Len() != l { diff --git a/array_sparse.go b/array_sparse.go index 201bc6fa..f99afd7e 100644 --- a/array_sparse.go +++ b/array_sparse.go @@ -302,6 +302,18 @@ func (a *sparseArrayObject) hasOwnPropertyIdx(idx valueInt) bool { return a.baseObject.hasOwnPropertyStr(idx.string()) } +func (a *sparseArrayObject) hasPropertyIdx(idx valueInt) bool { + if a.hasOwnPropertyIdx(idx) { + return true + } + + if a.prototype != nil { + return a.prototype.self.hasPropertyIdx(idx) + } + + return false +} + func (a *sparseArrayObject) expand(idx uint32) bool { if l := len(a.items); l >= 1024 { if ii := a.items[l-1].idx; ii > idx { @@ -458,7 +470,7 @@ func (a *sparseArrayObject) exportType() reflect.Type { func (a *sparseArrayObject) exportToArrayOrSlice(dst reflect.Value, typ reflect.Type, ctx *objectExportCtx) error { r := a.val.runtime - if iter := a.getSym(SymIterator, nil); iter == r.global.arrayValues || iter == nil { + if iter := a.getSym(SymIterator, nil); iter == r.getArrayValues() || iter == nil { l := toIntStrict(int64(a.length)) if typ.Kind() == reflect.Array { if dst.Len() != l { diff --git a/builtin_array.go b/builtin_array.go index 6ff244b6..6ba8802c 100644 --- a/builtin_array.go +++ b/builtin_array.go @@ -3,6 +3,7 @@ package goja import ( "math" "sort" + "sync" ) func (r *Runtime) newArray(prototype *Object) (a *arrayObject) { @@ -19,7 +20,7 @@ func (r *Runtime) newArray(prototype *Object) (a *arrayObject) { } func (r *Runtime) newArrayObject() *arrayObject { - return r.newArray(r.global.ArrayPrototype) + return r.newArray(r.getArrayPrototype()) } func setArrayValues(a *arrayObject, values []Value) *arrayObject { @@ -96,7 +97,7 @@ func (r *Runtime) builtin_newArray(args []Value, proto *Object) *Object { if float64(al) == float64(f) { return r.newArrayLength(al) } else { - panic(r.newError(r.global.RangeError, "Invalid array length")) + panic(r.newError(r.getRangeError(), "Invalid array length")) } } return setArrayValues(r.newArray(proto), []Value{args[0]}).val @@ -1259,7 +1260,7 @@ func (r *Runtime) checkStdArray(v Value) *arrayObject { func (r *Runtime) checkStdArrayIter(v Value) *arrayObject { if arr := r.checkStdArray(v); arr != nil && - arr.getSym(SymIterator, nil) == r.global.arrayValues { + arr.getSym(SymIterator, nil) == r.getArrayValues() { return arr } @@ -1398,80 +1399,110 @@ func (r *Runtime) arrayIterProto_next(call FunctionCall) Value { panic(r.NewTypeError("Method Array Iterator.prototype.next called on incompatible receiver %s", r.objectproto_toString(FunctionCall{This: thisObj}))) } -func (r *Runtime) createArrayProto(val *Object) objectImpl { - o := &arrayObject{ - baseObject: baseObject{ - class: classArray, - val: val, - extensible: true, - prototype: r.global.ObjectPrototype, - }, - } - o.init() - - o._putProp("at", r.newNativeFunc(r.arrayproto_at, nil, "at", nil, 1), true, false, true) - o._putProp("constructor", r.global.Array, true, false, true) - o._putProp("concat", r.newNativeFunc(r.arrayproto_concat, nil, "concat", nil, 1), true, false, true) - o._putProp("copyWithin", r.newNativeFunc(r.arrayproto_copyWithin, nil, "copyWithin", nil, 2), true, false, true) - o._putProp("entries", r.newNativeFunc(r.arrayproto_entries, nil, "entries", nil, 0), true, false, true) - o._putProp("every", r.newNativeFunc(r.arrayproto_every, nil, "every", nil, 1), true, false, true) - o._putProp("fill", r.newNativeFunc(r.arrayproto_fill, nil, "fill", nil, 1), true, false, true) - o._putProp("filter", r.newNativeFunc(r.arrayproto_filter, nil, "filter", nil, 1), true, false, true) - o._putProp("find", r.newNativeFunc(r.arrayproto_find, nil, "find", nil, 1), true, false, true) - o._putProp("findIndex", r.newNativeFunc(r.arrayproto_findIndex, nil, "findIndex", nil, 1), true, false, true) - o._putProp("findLast", r.newNativeFunc(r.arrayproto_findLast, nil, "findLast", nil, 1), true, false, true) - o._putProp("findLastIndex", r.newNativeFunc(r.arrayproto_findLastIndex, nil, "findLastIndex", nil, 1), true, false, true) - o._putProp("flat", r.newNativeFunc(r.arrayproto_flat, nil, "flat", nil, 0), true, false, true) - o._putProp("flatMap", r.newNativeFunc(r.arrayproto_flatMap, nil, "flatMap", nil, 1), true, false, true) - o._putProp("forEach", r.newNativeFunc(r.arrayproto_forEach, nil, "forEach", nil, 1), true, false, true) - o._putProp("includes", r.newNativeFunc(r.arrayproto_includes, nil, "includes", nil, 1), true, false, true) - o._putProp("indexOf", r.newNativeFunc(r.arrayproto_indexOf, nil, "indexOf", nil, 1), true, false, true) - o._putProp("join", r.newNativeFunc(r.arrayproto_join, nil, "join", nil, 1), true, false, true) - o._putProp("keys", r.newNativeFunc(r.arrayproto_keys, nil, "keys", nil, 0), true, false, true) - o._putProp("lastIndexOf", r.newNativeFunc(r.arrayproto_lastIndexOf, nil, "lastIndexOf", nil, 1), true, false, true) - o._putProp("map", r.newNativeFunc(r.arrayproto_map, nil, "map", nil, 1), true, false, true) - o._putProp("pop", r.newNativeFunc(r.arrayproto_pop, nil, "pop", nil, 0), true, false, true) - o._putProp("push", r.newNativeFunc(r.arrayproto_push, nil, "push", nil, 1), true, false, true) - o._putProp("reduce", r.newNativeFunc(r.arrayproto_reduce, nil, "reduce", nil, 1), true, false, true) - o._putProp("reduceRight", r.newNativeFunc(r.arrayproto_reduceRight, nil, "reduceRight", nil, 1), true, false, true) - o._putProp("reverse", r.newNativeFunc(r.arrayproto_reverse, nil, "reverse", nil, 0), true, false, true) - o._putProp("shift", r.newNativeFunc(r.arrayproto_shift, nil, "shift", nil, 0), true, false, true) - o._putProp("slice", r.newNativeFunc(r.arrayproto_slice, nil, "slice", nil, 2), true, false, true) - o._putProp("some", r.newNativeFunc(r.arrayproto_some, nil, "some", nil, 1), true, false, true) - o._putProp("sort", r.newNativeFunc(r.arrayproto_sort, nil, "sort", nil, 1), true, false, true) - o._putProp("splice", r.newNativeFunc(r.arrayproto_splice, nil, "splice", nil, 2), true, false, true) - o._putProp("toLocaleString", r.newNativeFunc(r.arrayproto_toLocaleString, nil, "toLocaleString", nil, 0), true, false, true) - o._putProp("toString", r.global.arrayToString, true, false, true) - o._putProp("unshift", r.newNativeFunc(r.arrayproto_unshift, nil, "unshift", nil, 1), true, false, true) - o._putProp("values", r.global.arrayValues, true, false, true) - - o._putSym(SymIterator, valueProp(r.global.arrayValues, true, false, true)) - - bl := r.newBaseObject(nil, classObject) - bl.setOwnStr("copyWithin", valueTrue, true) - bl.setOwnStr("entries", valueTrue, true) - bl.setOwnStr("fill", valueTrue, true) - bl.setOwnStr("find", valueTrue, true) - bl.setOwnStr("findIndex", valueTrue, true) - bl.setOwnStr("findLast", valueTrue, true) - bl.setOwnStr("findLastIndex", valueTrue, true) - bl.setOwnStr("flat", valueTrue, true) - bl.setOwnStr("flatMap", valueTrue, true) - bl.setOwnStr("includes", valueTrue, true) - bl.setOwnStr("keys", valueTrue, true) - bl.setOwnStr("values", valueTrue, true) - bl.setOwnStr("groupBy", valueTrue, true) - bl.setOwnStr("groupByToMap", valueTrue, true) - o._putSym(SymUnscopables, valueProp(bl.val, false, false, true)) +func createArrayProtoTemplate() *objectTemplate { + t := newObjectTemplate() + t.protoFactory = func(r *Runtime) *Object { + return r.global.ObjectPrototype + } + + t.putStr("length", func(r *Runtime) Value { return valueProp(_positiveZero, true, false, false) }) + + t.putStr("constructor", func(r *Runtime) Value { return valueProp(r.getArray(), true, false, true) }) + + t.putStr("at", func(r *Runtime) Value { return r.methodProp(r.arrayproto_at, "at", 1) }) + t.putStr("concat", func(r *Runtime) Value { return r.methodProp(r.arrayproto_concat, "concat", 1) }) + t.putStr("copyWithin", func(r *Runtime) Value { return r.methodProp(r.arrayproto_copyWithin, "copyWithin", 2) }) + t.putStr("entries", func(r *Runtime) Value { return r.methodProp(r.arrayproto_entries, "entries", 0) }) + t.putStr("every", func(r *Runtime) Value { return r.methodProp(r.arrayproto_every, "every", 1) }) + t.putStr("fill", func(r *Runtime) Value { return r.methodProp(r.arrayproto_fill, "fill", 1) }) + t.putStr("filter", func(r *Runtime) Value { return r.methodProp(r.arrayproto_filter, "filter", 1) }) + t.putStr("find", func(r *Runtime) Value { return r.methodProp(r.arrayproto_find, "find", 1) }) + t.putStr("findIndex", func(r *Runtime) Value { return r.methodProp(r.arrayproto_findIndex, "findIndex", 1) }) + t.putStr("findLast", func(r *Runtime) Value { return r.methodProp(r.arrayproto_findLast, "findLast", 1) }) + t.putStr("findLastIndex", func(r *Runtime) Value { return r.methodProp(r.arrayproto_findLastIndex, "findLastIndex", 1) }) + t.putStr("flat", func(r *Runtime) Value { return r.methodProp(r.arrayproto_flat, "flat", 0) }) + t.putStr("flatMap", func(r *Runtime) Value { return r.methodProp(r.arrayproto_flatMap, "flatMap", 1) }) + t.putStr("forEach", func(r *Runtime) Value { return r.methodProp(r.arrayproto_forEach, "forEach", 1) }) + t.putStr("includes", func(r *Runtime) Value { return r.methodProp(r.arrayproto_includes, "includes", 1) }) + t.putStr("indexOf", func(r *Runtime) Value { return r.methodProp(r.arrayproto_indexOf, "indexOf", 1) }) + t.putStr("join", func(r *Runtime) Value { return r.methodProp(r.arrayproto_join, "join", 1) }) + t.putStr("keys", func(r *Runtime) Value { return r.methodProp(r.arrayproto_keys, "keys", 0) }) + t.putStr("lastIndexOf", func(r *Runtime) Value { return r.methodProp(r.arrayproto_lastIndexOf, "lastIndexOf", 1) }) + t.putStr("map", func(r *Runtime) Value { return r.methodProp(r.arrayproto_map, "map", 1) }) + t.putStr("pop", func(r *Runtime) Value { return r.methodProp(r.arrayproto_pop, "pop", 0) }) + t.putStr("push", func(r *Runtime) Value { return r.methodProp(r.arrayproto_push, "push", 1) }) + t.putStr("reduce", func(r *Runtime) Value { return r.methodProp(r.arrayproto_reduce, "reduce", 1) }) + t.putStr("reduceRight", func(r *Runtime) Value { return r.methodProp(r.arrayproto_reduceRight, "reduceRight", 1) }) + t.putStr("reverse", func(r *Runtime) Value { return r.methodProp(r.arrayproto_reverse, "reverse", 0) }) + t.putStr("shift", func(r *Runtime) Value { return r.methodProp(r.arrayproto_shift, "shift", 0) }) + t.putStr("slice", func(r *Runtime) Value { return r.methodProp(r.arrayproto_slice, "slice", 2) }) + t.putStr("some", func(r *Runtime) Value { return r.methodProp(r.arrayproto_some, "some", 1) }) + t.putStr("sort", func(r *Runtime) Value { return r.methodProp(r.arrayproto_sort, "sort", 1) }) + t.putStr("splice", func(r *Runtime) Value { return r.methodProp(r.arrayproto_splice, "splice", 2) }) + t.putStr("toLocaleString", func(r *Runtime) Value { return r.methodProp(r.arrayproto_toLocaleString, "toLocaleString", 0) }) + t.putStr("toString", func(r *Runtime) Value { return valueProp(r.getArrayToString(), true, false, true) }) + t.putStr("unshift", func(r *Runtime) Value { return r.methodProp(r.arrayproto_unshift, "unshift", 1) }) + t.putStr("values", func(r *Runtime) Value { return valueProp(r.getArrayValues(), true, false, true) }) + + t.putSym(SymIterator, func(r *Runtime) Value { return valueProp(r.getArrayValues(), true, false, true) }) + t.putSym(SymUnscopables, func(r *Runtime) Value { + bl := r.newBaseObject(nil, classObject) + bl.setOwnStr("copyWithin", valueTrue, true) + bl.setOwnStr("entries", valueTrue, true) + bl.setOwnStr("fill", valueTrue, true) + bl.setOwnStr("find", valueTrue, true) + bl.setOwnStr("findIndex", valueTrue, true) + bl.setOwnStr("findLast", valueTrue, true) + bl.setOwnStr("findLastIndex", valueTrue, true) + bl.setOwnStr("flat", valueTrue, true) + bl.setOwnStr("flatMap", valueTrue, true) + bl.setOwnStr("includes", valueTrue, true) + bl.setOwnStr("keys", valueTrue, true) + bl.setOwnStr("values", valueTrue, true) + bl.setOwnStr("groupBy", valueTrue, true) + bl.setOwnStr("groupByToMap", valueTrue, true) + + return valueProp(bl.val, false, false, true) + }) - return o + return t +} + +var arrayProtoTemplate *objectTemplate +var arrayProtoTemplateOnce sync.Once + +func getArrayProtoTemplate() *objectTemplate { + arrayProtoTemplateOnce.Do(func() { + arrayProtoTemplate = createArrayProtoTemplate() + }) + return arrayProtoTemplate +} + +func (r *Runtime) getArrayPrototype() *Object { + ret := r.global.ArrayPrototype + if ret == nil { + ret = &Object{runtime: r} + r.global.ArrayPrototype = ret + r.newTemplatedArrayObject(getArrayProtoTemplate(), ret) + } + return ret +} + +func (r *Runtime) getArray() *Object { + ret := r.global.Array + if ret == nil { + ret = &Object{runtime: r} + ret.self = r.createArray(ret) + r.global.Array = ret + } + return ret } func (r *Runtime) createArray(val *Object) objectImpl { - o := r.newNativeFuncConstructObj(val, r.builtin_newArray, "Array", r.global.ArrayPrototype, 1) - o._putProp("from", r.newNativeFunc(r.array_from, nil, "from", nil, 1), true, false, true) - o._putProp("isArray", r.newNativeFunc(r.array_isArray, nil, "isArray", nil, 1), true, false, true) - o._putProp("of", r.newNativeFunc(r.array_of, nil, "of", nil, 0), true, false, true) + o := r.newNativeFuncConstructObj(val, r.builtin_newArray, "Array", r.getArrayPrototype(), 1) + o._putProp("from", r.newNativeFunc(r.array_from, "from", 1), true, false, true) + o._putProp("isArray", r.newNativeFunc(r.array_isArray, "isArray", 1), true, false, true) + o._putProp("of", r.newNativeFunc(r.array_of, "of", 0), true, false, true) r.putSpeciesReturnThis(o) return o @@ -1480,20 +1511,28 @@ func (r *Runtime) createArray(val *Object) objectImpl { func (r *Runtime) createArrayIterProto(val *Object) objectImpl { o := newBaseObjectObj(val, r.getIteratorPrototype(), classObject) - o._putProp("next", r.newNativeFunc(r.arrayIterProto_next, nil, "next", nil, 0), true, false, true) + o._putProp("next", r.newNativeFunc(r.arrayIterProto_next, "next", 0), true, false, true) o._putSym(SymToStringTag, valueProp(asciiString(classArrayIterator), false, false, true)) return o } -func (r *Runtime) initArray() { - r.global.arrayValues = r.newNativeFunc(r.arrayproto_values, nil, "values", nil, 0) - r.global.arrayToString = r.newNativeFunc(r.arrayproto_toString, nil, "toString", nil, 0) - - r.global.ArrayPrototype = r.newLazyObject(r.createArrayProto) +func (r *Runtime) getArrayValues() *Object { + ret := r.global.arrayValues + if ret == nil { + ret = r.newNativeFunc(r.arrayproto_values, "values", 0) + r.global.arrayValues = ret + } + return ret +} - r.global.Array = r.newLazyObject(r.createArray) - r.addToGlobal("Array", r.global.Array) +func (r *Runtime) getArrayToString() *Object { + ret := r.global.arrayToString + if ret == nil { + ret = r.newNativeFunc(r.arrayproto_toString, "toString", 0) + r.global.arrayToString = ret + } + return ret } func (r *Runtime) getArrayIteratorPrototype() *Object { diff --git a/builtin_arrray_test.go b/builtin_arrray_test.go index db73259c..a190f1f4 100644 --- a/builtin_arrray_test.go +++ b/builtin_arrray_test.go @@ -323,3 +323,19 @@ func TestArrayFlatMap(t *testing.T) { ` testScriptWithTestLibX(SCRIPT, _undefined, t) } + +func TestArrayProto(t *testing.T) { + const SCRIPT = ` + const a = Array.prototype; + a.push(1, 2, 3, 4, 5); + assert.sameValue(a.length, 5); + assert.sameValue(a[0], 1); + a.length = 3; + assert.sameValue(a.length, 3); + assert(compareArray(a, [1, 2, 3])); + a.shift(); + assert.sameValue(a.length, 2); + assert(compareArray(a, [2, 3])); + ` + testScriptWithTestLib(SCRIPT, _undefined, t) +} diff --git a/builtin_boolean.go b/builtin_boolean.go index b065615d..84763285 100644 --- a/builtin_boolean.go +++ b/builtin_boolean.go @@ -49,12 +49,27 @@ func (r *Runtime) booleanproto_valueOf(call FunctionCall) Value { return nil } -func (r *Runtime) initBoolean() { - r.global.BooleanPrototype = r.newPrimitiveObject(valueFalse, r.global.ObjectPrototype, classBoolean) - o := r.global.BooleanPrototype.self - o._putProp("toString", r.newNativeFunc(r.booleanproto_toString, nil, "toString", nil, 0), true, false, true) - o._putProp("valueOf", r.newNativeFunc(r.booleanproto_valueOf, nil, "valueOf", nil, 0), true, false, true) +func (r *Runtime) getBooleanPrototype() *Object { + ret := r.global.BooleanPrototype + if ret == nil { + ret = r.newPrimitiveObject(valueFalse, r.global.ObjectPrototype, classBoolean) + r.global.BooleanPrototype = ret + o := ret.self + o._putProp("toString", r.newNativeFunc(r.booleanproto_toString, "toString", 0), true, false, true) + o._putProp("valueOf", r.newNativeFunc(r.booleanproto_valueOf, "valueOf", 0), true, false, true) + o._putProp("constructor", r.getBoolean(), true, false, true) + } + return ret +} - r.global.Boolean = r.newNativeFunc(r.builtin_Boolean, r.builtin_newBoolean, "Boolean", r.global.BooleanPrototype, 1) - r.addToGlobal("Boolean", r.global.Boolean) +func (r *Runtime) getBoolean() *Object { + ret := r.global.Boolean + if ret == nil { + ret = &Object{runtime: r} + r.global.Boolean = ret + proto := r.getBooleanPrototype() + r.newNativeFuncAndConstruct(ret, r.builtin_Boolean, + r.wrapNativeConstruct(r.builtin_newBoolean, ret, proto), proto, "Boolean", intToValue(1)) + } + return ret } diff --git a/builtin_date.go b/builtin_date.go index 50070a29..84a80ac0 100644 --- a/builtin_date.go +++ b/builtin_date.go @@ -3,6 +3,7 @@ package goja import ( "fmt" "math" + "sync" "time" ) @@ -133,7 +134,7 @@ func (r *Runtime) dateproto_toISOString(call FunctionCall) Value { // extended year return asciiString(fmt.Sprintf("%+06d-", year) + utc.Format(isoDateTimeLayout[5:])) } else { - panic(r.newError(r.global.RangeError, "Invalid time value")) + panic(r.newError(r.getRangeError(), "Invalid time value")) } } panic(r.NewTypeError("Method Date.prototype.toISOString is called on incompatible receiver")) @@ -938,78 +939,120 @@ func (r *Runtime) dateproto_setUTCFullYear(call FunctionCall) Value { panic(r.NewTypeError("Method Date.prototype.setUTCFullYear is called on incompatible receiver")) } -func (r *Runtime) createDateProto(val *Object) objectImpl { - o := &baseObject{ - class: classObject, - val: val, - extensible: true, - prototype: r.global.ObjectPrototype, - } - o.init() - - o._putProp("constructor", r.global.Date, true, false, true) - o._putProp("toString", r.newNativeFunc(r.dateproto_toString, nil, "toString", nil, 0), true, false, true) - o._putProp("toDateString", r.newNativeFunc(r.dateproto_toDateString, nil, "toDateString", nil, 0), true, false, true) - o._putProp("toTimeString", r.newNativeFunc(r.dateproto_toTimeString, nil, "toTimeString", nil, 0), true, false, true) - o._putProp("toLocaleString", r.newNativeFunc(r.dateproto_toLocaleString, nil, "toLocaleString", nil, 0), true, false, true) - o._putProp("toLocaleDateString", r.newNativeFunc(r.dateproto_toLocaleDateString, nil, "toLocaleDateString", nil, 0), true, false, true) - o._putProp("toLocaleTimeString", r.newNativeFunc(r.dateproto_toLocaleTimeString, nil, "toLocaleTimeString", nil, 0), true, false, true) - o._putProp("valueOf", r.newNativeFunc(r.dateproto_valueOf, nil, "valueOf", nil, 0), true, false, true) - o._putProp("getTime", r.newNativeFunc(r.dateproto_getTime, nil, "getTime", nil, 0), true, false, true) - o._putProp("getFullYear", r.newNativeFunc(r.dateproto_getFullYear, nil, "getFullYear", nil, 0), true, false, true) - o._putProp("getUTCFullYear", r.newNativeFunc(r.dateproto_getUTCFullYear, nil, "getUTCFullYear", nil, 0), true, false, true) - o._putProp("getMonth", r.newNativeFunc(r.dateproto_getMonth, nil, "getMonth", nil, 0), true, false, true) - o._putProp("getUTCMonth", r.newNativeFunc(r.dateproto_getUTCMonth, nil, "getUTCMonth", nil, 0), true, false, true) - o._putProp("getDate", r.newNativeFunc(r.dateproto_getDate, nil, "getDate", nil, 0), true, false, true) - o._putProp("getUTCDate", r.newNativeFunc(r.dateproto_getUTCDate, nil, "getUTCDate", nil, 0), true, false, true) - o._putProp("getDay", r.newNativeFunc(r.dateproto_getDay, nil, "getDay", nil, 0), true, false, true) - o._putProp("getUTCDay", r.newNativeFunc(r.dateproto_getUTCDay, nil, "getUTCDay", nil, 0), true, false, true) - o._putProp("getHours", r.newNativeFunc(r.dateproto_getHours, nil, "getHours", nil, 0), true, false, true) - o._putProp("getUTCHours", r.newNativeFunc(r.dateproto_getUTCHours, nil, "getUTCHours", nil, 0), true, false, true) - o._putProp("getMinutes", r.newNativeFunc(r.dateproto_getMinutes, nil, "getMinutes", nil, 0), true, false, true) - o._putProp("getUTCMinutes", r.newNativeFunc(r.dateproto_getUTCMinutes, nil, "getUTCMinutes", nil, 0), true, false, true) - o._putProp("getSeconds", r.newNativeFunc(r.dateproto_getSeconds, nil, "getSeconds", nil, 0), true, false, true) - o._putProp("getUTCSeconds", r.newNativeFunc(r.dateproto_getUTCSeconds, nil, "getUTCSeconds", nil, 0), true, false, true) - o._putProp("getMilliseconds", r.newNativeFunc(r.dateproto_getMilliseconds, nil, "getMilliseconds", nil, 0), true, false, true) - o._putProp("getUTCMilliseconds", r.newNativeFunc(r.dateproto_getUTCMilliseconds, nil, "getUTCMilliseconds", nil, 0), true, false, true) - o._putProp("getTimezoneOffset", r.newNativeFunc(r.dateproto_getTimezoneOffset, nil, "getTimezoneOffset", nil, 0), true, false, true) - o._putProp("setTime", r.newNativeFunc(r.dateproto_setTime, nil, "setTime", nil, 1), true, false, true) - o._putProp("setMilliseconds", r.newNativeFunc(r.dateproto_setMilliseconds, nil, "setMilliseconds", nil, 1), true, false, true) - o._putProp("setUTCMilliseconds", r.newNativeFunc(r.dateproto_setUTCMilliseconds, nil, "setUTCMilliseconds", nil, 1), true, false, true) - o._putProp("setSeconds", r.newNativeFunc(r.dateproto_setSeconds, nil, "setSeconds", nil, 2), true, false, true) - o._putProp("setUTCSeconds", r.newNativeFunc(r.dateproto_setUTCSeconds, nil, "setUTCSeconds", nil, 2), true, false, true) - o._putProp("setMinutes", r.newNativeFunc(r.dateproto_setMinutes, nil, "setMinutes", nil, 3), true, false, true) - o._putProp("setUTCMinutes", r.newNativeFunc(r.dateproto_setUTCMinutes, nil, "setUTCMinutes", nil, 3), true, false, true) - o._putProp("setHours", r.newNativeFunc(r.dateproto_setHours, nil, "setHours", nil, 4), true, false, true) - o._putProp("setUTCHours", r.newNativeFunc(r.dateproto_setUTCHours, nil, "setUTCHours", nil, 4), true, false, true) - o._putProp("setDate", r.newNativeFunc(r.dateproto_setDate, nil, "setDate", nil, 1), true, false, true) - o._putProp("setUTCDate", r.newNativeFunc(r.dateproto_setUTCDate, nil, "setUTCDate", nil, 1), true, false, true) - o._putProp("setMonth", r.newNativeFunc(r.dateproto_setMonth, nil, "setMonth", nil, 2), true, false, true) - o._putProp("setUTCMonth", r.newNativeFunc(r.dateproto_setUTCMonth, nil, "setUTCMonth", nil, 2), true, false, true) - o._putProp("setFullYear", r.newNativeFunc(r.dateproto_setFullYear, nil, "setFullYear", nil, 3), true, false, true) - o._putProp("setUTCFullYear", r.newNativeFunc(r.dateproto_setUTCFullYear, nil, "setUTCFullYear", nil, 3), true, false, true) - o._putProp("toUTCString", r.newNativeFunc(r.dateproto_toUTCString, nil, "toUTCString", nil, 0), true, false, true) - o._putProp("toISOString", r.newNativeFunc(r.dateproto_toISOString, nil, "toISOString", nil, 0), true, false, true) - o._putProp("toJSON", r.newNativeFunc(r.dateproto_toJSON, nil, "toJSON", nil, 1), true, false, true) - - o._putSym(SymToPrimitive, valueProp(r.newNativeFunc(r.dateproto_toPrimitive, nil, "[Symbol.toPrimitive]", nil, 1), false, false, true)) - - return o -} - -func (r *Runtime) createDate(val *Object) objectImpl { - o := r.newNativeFuncObj(val, r.builtin_date, r.builtin_newDate, "Date", r.global.DatePrototype, intToValue(7)) - - o._putProp("parse", r.newNativeFunc(r.date_parse, nil, "parse", nil, 1), true, false, true) - o._putProp("UTC", r.newNativeFunc(r.date_UTC, nil, "UTC", nil, 7), true, false, true) - o._putProp("now", r.newNativeFunc(r.date_now, nil, "now", nil, 0), true, false, true) - - return o -} - -func (r *Runtime) initDate() { - r.global.DatePrototype = r.newLazyObject(r.createDateProto) - - r.global.Date = r.newLazyObject(r.createDate) - r.addToGlobal("Date", r.global.Date) +var dateTemplate *objectTemplate +var dateTemplateOnce sync.Once + +func getDateTemplate() *objectTemplate { + dateTemplateOnce.Do(func() { + dateTemplate = createDateTemplate() + }) + return dateTemplate +} + +func createDateTemplate() *objectTemplate { + t := newObjectTemplate() + t.protoFactory = func(r *Runtime) *Object { + return r.getFunctionPrototype() + } + + t.putStr("name", func(r *Runtime) Value { return valueProp(asciiString("Date"), false, false, true) }) + t.putStr("length", func(r *Runtime) Value { return valueProp(intToValue(7), false, false, true) }) + + t.putStr("prototype", func(r *Runtime) Value { return valueProp(r.getDatePrototype(), false, false, false) }) + + t.putStr("parse", func(r *Runtime) Value { return r.methodProp(r.date_parse, "parse", 1) }) + t.putStr("UTC", func(r *Runtime) Value { return r.methodProp(r.date_UTC, "UTC", 7) }) + t.putStr("now", func(r *Runtime) Value { return r.methodProp(r.date_now, "now", 0) }) + + return t +} + +func (r *Runtime) getDate() *Object { + ret := r.global.Date + if ret == nil { + ret = &Object{runtime: r} + r.global.Date = ret + r.newTemplatedFuncObject(getDateTemplate(), ret, r.builtin_date, + r.wrapNativeConstruct(r.builtin_newDate, ret, r.getDatePrototype())) + } + return ret +} + +func createDateProtoTemplate() *objectTemplate { + t := newObjectTemplate() + t.protoFactory = func(r *Runtime) *Object { + return r.global.ObjectPrototype + } + + t.putStr("constructor", func(r *Runtime) Value { return valueProp(r.getDate(), true, false, true) }) + + t.putStr("toString", func(r *Runtime) Value { return r.methodProp(r.dateproto_toString, "toString", 0) }) + t.putStr("toDateString", func(r *Runtime) Value { return r.methodProp(r.dateproto_toDateString, "toDateString", 0) }) + t.putStr("toTimeString", func(r *Runtime) Value { return r.methodProp(r.dateproto_toTimeString, "toTimeString", 0) }) + t.putStr("toLocaleString", func(r *Runtime) Value { return r.methodProp(r.dateproto_toLocaleString, "toLocaleString", 0) }) + t.putStr("toLocaleDateString", func(r *Runtime) Value { return r.methodProp(r.dateproto_toLocaleDateString, "toLocaleDateString", 0) }) + t.putStr("toLocaleTimeString", func(r *Runtime) Value { return r.methodProp(r.dateproto_toLocaleTimeString, "toLocaleTimeString", 0) }) + t.putStr("valueOf", func(r *Runtime) Value { return r.methodProp(r.dateproto_valueOf, "valueOf", 0) }) + t.putStr("getTime", func(r *Runtime) Value { return r.methodProp(r.dateproto_getTime, "getTime", 0) }) + t.putStr("getFullYear", func(r *Runtime) Value { return r.methodProp(r.dateproto_getFullYear, "getFullYear", 0) }) + t.putStr("getUTCFullYear", func(r *Runtime) Value { return r.methodProp(r.dateproto_getUTCFullYear, "getUTCFullYear", 0) }) + t.putStr("getMonth", func(r *Runtime) Value { return r.methodProp(r.dateproto_getMonth, "getMonth", 0) }) + t.putStr("getUTCMonth", func(r *Runtime) Value { return r.methodProp(r.dateproto_getUTCMonth, "getUTCMonth", 0) }) + t.putStr("getDate", func(r *Runtime) Value { return r.methodProp(r.dateproto_getDate, "getDate", 0) }) + t.putStr("getUTCDate", func(r *Runtime) Value { return r.methodProp(r.dateproto_getUTCDate, "getUTCDate", 0) }) + t.putStr("getDay", func(r *Runtime) Value { return r.methodProp(r.dateproto_getDay, "getDay", 0) }) + t.putStr("getUTCDay", func(r *Runtime) Value { return r.methodProp(r.dateproto_getUTCDay, "getUTCDay", 0) }) + t.putStr("getHours", func(r *Runtime) Value { return r.methodProp(r.dateproto_getHours, "getHours", 0) }) + t.putStr("getUTCHours", func(r *Runtime) Value { return r.methodProp(r.dateproto_getUTCHours, "getUTCHours", 0) }) + t.putStr("getMinutes", func(r *Runtime) Value { return r.methodProp(r.dateproto_getMinutes, "getMinutes", 0) }) + t.putStr("getUTCMinutes", func(r *Runtime) Value { return r.methodProp(r.dateproto_getUTCMinutes, "getUTCMinutes", 0) }) + t.putStr("getSeconds", func(r *Runtime) Value { return r.methodProp(r.dateproto_getSeconds, "getSeconds", 0) }) + t.putStr("getUTCSeconds", func(r *Runtime) Value { return r.methodProp(r.dateproto_getUTCSeconds, "getUTCSeconds", 0) }) + t.putStr("getMilliseconds", func(r *Runtime) Value { return r.methodProp(r.dateproto_getMilliseconds, "getMilliseconds", 0) }) + t.putStr("getUTCMilliseconds", func(r *Runtime) Value { return r.methodProp(r.dateproto_getUTCMilliseconds, "getUTCMilliseconds", 0) }) + t.putStr("getTimezoneOffset", func(r *Runtime) Value { return r.methodProp(r.dateproto_getTimezoneOffset, "getTimezoneOffset", 0) }) + t.putStr("setTime", func(r *Runtime) Value { return r.methodProp(r.dateproto_setTime, "setTime", 1) }) + t.putStr("setMilliseconds", func(r *Runtime) Value { return r.methodProp(r.dateproto_setMilliseconds, "setMilliseconds", 1) }) + t.putStr("setUTCMilliseconds", func(r *Runtime) Value { return r.methodProp(r.dateproto_setUTCMilliseconds, "setUTCMilliseconds", 1) }) + t.putStr("setSeconds", func(r *Runtime) Value { return r.methodProp(r.dateproto_setSeconds, "setSeconds", 2) }) + t.putStr("setUTCSeconds", func(r *Runtime) Value { return r.methodProp(r.dateproto_setUTCSeconds, "setUTCSeconds", 2) }) + t.putStr("setMinutes", func(r *Runtime) Value { return r.methodProp(r.dateproto_setMinutes, "setMinutes", 3) }) + t.putStr("setUTCMinutes", func(r *Runtime) Value { return r.methodProp(r.dateproto_setUTCMinutes, "setUTCMinutes", 3) }) + t.putStr("setHours", func(r *Runtime) Value { return r.methodProp(r.dateproto_setHours, "setHours", 4) }) + t.putStr("setUTCHours", func(r *Runtime) Value { return r.methodProp(r.dateproto_setUTCHours, "setUTCHours", 4) }) + t.putStr("setDate", func(r *Runtime) Value { return r.methodProp(r.dateproto_setDate, "setDate", 1) }) + t.putStr("setUTCDate", func(r *Runtime) Value { return r.methodProp(r.dateproto_setUTCDate, "setUTCDate", 1) }) + t.putStr("setMonth", func(r *Runtime) Value { return r.methodProp(r.dateproto_setMonth, "setMonth", 2) }) + t.putStr("setUTCMonth", func(r *Runtime) Value { return r.methodProp(r.dateproto_setUTCMonth, "setUTCMonth", 2) }) + t.putStr("setFullYear", func(r *Runtime) Value { return r.methodProp(r.dateproto_setFullYear, "setFullYear", 3) }) + t.putStr("setUTCFullYear", func(r *Runtime) Value { return r.methodProp(r.dateproto_setUTCFullYear, "setUTCFullYear", 3) }) + t.putStr("toUTCString", func(r *Runtime) Value { return r.methodProp(r.dateproto_toUTCString, "toUTCString", 0) }) + t.putStr("toISOString", func(r *Runtime) Value { return r.methodProp(r.dateproto_toISOString, "toISOString", 0) }) + t.putStr("toJSON", func(r *Runtime) Value { return r.methodProp(r.dateproto_toJSON, "toJSON", 1) }) + + t.putSym(SymToPrimitive, func(r *Runtime) Value { + return valueProp(r.newNativeFunc(r.dateproto_toPrimitive, "[Symbol.toPrimitive]", 1), false, false, true) + }) + + return t +} + +var dateProtoTemplate *objectTemplate +var dateProtoTemplateOnce sync.Once + +func getDateProtoTemplate() *objectTemplate { + dateProtoTemplateOnce.Do(func() { + dateProtoTemplate = createDateProtoTemplate() + }) + return dateProtoTemplate +} + +func (r *Runtime) getDatePrototype() *Object { + ret := r.global.DatePrototype + if ret == nil { + ret = &Object{runtime: r} + r.global.DatePrototype = ret + r.newTemplatedObject(getDateProtoTemplate(), ret) + } + return ret } diff --git a/builtin_error.go b/builtin_error.go index 5c63ea8a..b07bf6a7 100644 --- a/builtin_error.go +++ b/builtin_error.go @@ -176,61 +176,114 @@ func (r *Runtime) error_toString(call FunctionCall) Value { return sb.String() } -func (r *Runtime) createErrorPrototype(name String) *Object { - o := r.newBaseObject(r.global.ErrorPrototype, classObject) +func (r *Runtime) createErrorPrototype(name String, ctor *Object) *Object { + o := r.newBaseObject(r.getErrorPrototype(), classObject) o._putProp("message", stringEmpty, true, false, true) o._putProp("name", name, true, false, true) + o._putProp("constructor", ctor, true, false, true) return o.val } -func (r *Runtime) initErrors() { - r.global.ErrorPrototype = r.NewObject() - o := r.global.ErrorPrototype.self - o._putProp("message", stringEmpty, true, false, true) - o._putProp("name", stringError, true, false, true) - o._putProp("toString", r.newNativeFunc(r.error_toString, nil, "toString", nil, 0), true, false, true) - - r.global.Error = r.newNativeFuncConstruct(r.builtin_Error, "Error", r.global.ErrorPrototype, 1) - r.addToGlobal("Error", r.global.Error) - - r.global.AggregateErrorPrototype = r.createErrorPrototype(stringAggregateError) - r.global.AggregateError = r.newNativeFuncConstructProto(r.builtin_AggregateError, "AggregateError", r.global.AggregateErrorPrototype, r.global.Error, 2) - r.addToGlobal("AggregateError", r.global.AggregateError) - - r.global.TypeErrorPrototype = r.createErrorPrototype(stringTypeError) - - r.global.TypeError = r.newNativeFuncConstructProto(r.builtin_Error, "TypeError", r.global.TypeErrorPrototype, r.global.Error, 1) - r.addToGlobal("TypeError", r.global.TypeError) - - r.global.ReferenceErrorPrototype = r.createErrorPrototype(stringReferenceError) - - r.global.ReferenceError = r.newNativeFuncConstructProto(r.builtin_Error, "ReferenceError", r.global.ReferenceErrorPrototype, r.global.Error, 1) - r.addToGlobal("ReferenceError", r.global.ReferenceError) - - r.global.SyntaxErrorPrototype = r.createErrorPrototype(stringSyntaxError) +func (r *Runtime) getErrorPrototype() *Object { + ret := r.global.ErrorPrototype + if ret == nil { + ret = r.NewObject() + r.global.ErrorPrototype = ret + o := ret.self + o._putProp("message", stringEmpty, true, false, true) + o._putProp("name", stringError, true, false, true) + o._putProp("toString", r.newNativeFunc(r.error_toString, "toString", 0), true, false, true) + o._putProp("constructor", r.getError(), true, false, true) + } + return ret +} - r.global.SyntaxError = r.newNativeFuncConstructProto(r.builtin_Error, "SyntaxError", r.global.SyntaxErrorPrototype, r.global.Error, 1) - r.addToGlobal("SyntaxError", r.global.SyntaxError) +func (r *Runtime) getError() *Object { + ret := r.global.Error + if ret == nil { + ret = &Object{runtime: r} + r.global.Error = ret + r.newNativeFuncConstruct(ret, r.builtin_Error, "Error", r.getErrorPrototype(), 1) + } + return ret +} - r.global.RangeErrorPrototype = r.createErrorPrototype(stringRangeError) +func (r *Runtime) getAggregateError() *Object { + ret := r.global.AggregateError + if ret == nil { + ret = &Object{runtime: r} + r.global.AggregateError = ret + r.newNativeFuncConstructProto(ret, r.builtin_AggregateError, "AggregateError", r.createErrorPrototype(stringAggregateError, ret), r.getError(), 2) + } + return ret +} - r.global.RangeError = r.newNativeFuncConstructProto(r.builtin_Error, "RangeError", r.global.RangeErrorPrototype, r.global.Error, 1) - r.addToGlobal("RangeError", r.global.RangeError) +func (r *Runtime) getTypeError() *Object { + ret := r.global.TypeError + if ret == nil { + ret = &Object{runtime: r} + r.global.TypeError = ret + r.newNativeFuncConstructProto(ret, r.builtin_Error, "TypeError", r.createErrorPrototype(stringTypeError, ret), r.getError(), 1) + } + return ret +} - r.global.EvalErrorPrototype = r.createErrorPrototype(stringEvalError) - o = r.global.EvalErrorPrototype.self - o._putProp("name", stringEvalError, true, false, true) +func (r *Runtime) getReferenceError() *Object { + ret := r.global.ReferenceError + if ret == nil { + ret = &Object{runtime: r} + r.global.ReferenceError = ret + r.newNativeFuncConstructProto(ret, r.builtin_Error, "ReferenceError", r.createErrorPrototype(stringReferenceError, ret), r.getError(), 1) + } + return ret +} - r.global.EvalError = r.newNativeFuncConstructProto(r.builtin_Error, "EvalError", r.global.EvalErrorPrototype, r.global.Error, 1) - r.addToGlobal("EvalError", r.global.EvalError) +func (r *Runtime) getSyntaxError() *Object { + ret := r.global.SyntaxError + if ret == nil { + ret = &Object{runtime: r} + r.global.SyntaxError = ret + r.newNativeFuncConstructProto(ret, r.builtin_Error, "SyntaxError", r.createErrorPrototype(stringSyntaxError, ret), r.getError(), 1) + } + return ret +} - r.global.URIErrorPrototype = r.createErrorPrototype(stringURIError) +func (r *Runtime) getRangeError() *Object { + ret := r.global.RangeError + if ret == nil { + ret = &Object{runtime: r} + r.global.RangeError = ret + r.newNativeFuncConstructProto(ret, r.builtin_Error, "RangeError", r.createErrorPrototype(stringRangeError, ret), r.getError(), 1) + } + return ret +} - r.global.URIError = r.newNativeFuncConstructProto(r.builtin_Error, "URIError", r.global.URIErrorPrototype, r.global.Error, 1) - r.addToGlobal("URIError", r.global.URIError) +func (r *Runtime) getEvalError() *Object { + ret := r.global.EvalError + if ret == nil { + ret = &Object{runtime: r} + r.global.EvalError = ret + r.newNativeFuncConstructProto(ret, r.builtin_Error, "EvalError", r.createErrorPrototype(stringEvalError, ret), r.getError(), 1) + } + return ret +} - r.global.GoErrorPrototype = r.createErrorPrototype(stringGoError) +func (r *Runtime) getURIError() *Object { + ret := r.global.URIError + if ret == nil { + ret = &Object{runtime: r} + r.global.URIError = ret + r.newNativeFuncConstructProto(ret, r.builtin_Error, "URIError", r.createErrorPrototype(stringURIError, ret), r.getError(), 1) + } + return ret +} - r.global.GoError = r.newNativeFuncConstructProto(r.builtin_Error, "GoError", r.global.GoErrorPrototype, r.global.Error, 1) - r.addToGlobal("GoError", r.global.GoError) +func (r *Runtime) getGoError() *Object { + ret := r.global.GoError + if ret == nil { + ret = &Object{runtime: r} + r.global.GoError = ret + r.newNativeFuncConstructProto(ret, r.builtin_Error, "GoError", r.createErrorPrototype(stringGoError, ret), r.getError(), 1) + } + return ret } diff --git a/builtin_function.go b/builtin_function.go index e9f90d15..26a1287c 100644 --- a/builtin_function.go +++ b/builtin_function.go @@ -2,6 +2,7 @@ package goja import ( "math" + "sync" ) func (r *Runtime) functionCtor(args []Value, proto *Object, async, generator bool) *Object { @@ -53,16 +54,10 @@ func (r *Runtime) builtin_generatorFunction(args []Value, proto *Object) *Object func (r *Runtime) functionproto_toString(call FunctionCall) Value { obj := r.toObject(call.This) - if lazy, ok := obj.self.(*lazyObject); ok { - obj.self = lazy.create(obj) - } switch f := obj.self.(type) { case funcObjectImpl: return f.source() case *proxyObject: - if lazy, ok := f.target.self.(*lazyObject); ok { - f.target.self = lazy.create(f.target) - } if _, ok := f.target.self.(funcObjectImpl); ok { return asciiString("function () { [native code] }") } @@ -210,18 +205,76 @@ lenNotInt: return v } -func (r *Runtime) initFunction() { - o := r.global.FunctionPrototype.self.(*nativeFuncObject) - o.prototype = r.global.ObjectPrototype - o._putProp("name", stringEmpty, false, false, true) - o._putProp("apply", r.newNativeFunc(r.functionproto_apply, nil, "apply", nil, 2), true, false, true) - o._putProp("bind", r.newNativeFunc(r.functionproto_bind, nil, "bind", nil, 1), true, false, true) - o._putProp("call", r.newNativeFunc(r.functionproto_call, nil, "call", nil, 1), true, false, true) - o._putProp("toString", r.newNativeFunc(r.functionproto_toString, nil, "toString", nil, 0), true, false, true) - o._putSym(SymHasInstance, valueProp(r.newNativeFunc(r.functionproto_hasInstance, nil, "[Symbol.hasInstance]", nil, 1), false, false, false)) +func (r *Runtime) getThrower() *Object { + ret := r.global.thrower + if ret == nil { + ret = r.newNativeFunc(r.builtin_thrower, "", 0) + r.global.thrower = ret + r.object_freeze(FunctionCall{Arguments: []Value{ret}}) + } + return ret +} - r.global.Function = r.newNativeFuncConstruct(r.builtin_Function, "Function", r.global.FunctionPrototype, 1) - r.addToGlobal("Function", r.global.Function) +func (r *Runtime) newThrowerProperty(configurable bool) Value { + thrower := r.getThrower() + return &valueProperty{ + getterFunc: thrower, + setterFunc: thrower, + accessor: true, + configurable: configurable, + } +} + +func createFunctionProtoTemplate() *objectTemplate { + t := newObjectTemplate() + t.protoFactory = func(r *Runtime) *Object { + return r.global.ObjectPrototype + } + + t.putStr("constructor", func(r *Runtime) Value { return valueProp(r.getFunction(), true, false, true) }) + + t.putStr("length", func(r *Runtime) Value { return valueProp(_positiveZero, false, false, true) }) + t.putStr("name", func(r *Runtime) Value { return valueProp(stringEmpty, false, false, true) }) + + t.putStr("apply", func(r *Runtime) Value { return r.methodProp(r.functionproto_apply, "apply", 2) }) + t.putStr("bind", func(r *Runtime) Value { return r.methodProp(r.functionproto_bind, "bind", 1) }) + t.putStr("call", func(r *Runtime) Value { return r.methodProp(r.functionproto_call, "call", 1) }) + t.putStr("toString", func(r *Runtime) Value { return r.methodProp(r.functionproto_toString, "toString", 0) }) + + t.putStr("caller", func(r *Runtime) Value { return r.newThrowerProperty(true) }) + t.putStr("arguments", func(r *Runtime) Value { return r.newThrowerProperty(true) }) + + t.putSym(SymHasInstance, func(r *Runtime) Value { + return valueProp(r.newNativeFunc(r.functionproto_hasInstance, "[Symbol.hasInstance]", 1), false, false, false) + }) + + return t +} + +var functionProtoTemplate *objectTemplate +var functionProtoTemplateOnce sync.Once + +func getFunctionProtoTemplate() *objectTemplate { + functionProtoTemplateOnce.Do(func() { + functionProtoTemplate = createFunctionProtoTemplate() + }) + return functionProtoTemplate +} + +func (r *Runtime) getFunctionPrototype() *Object { + ret := r.global.FunctionPrototype + if ret == nil { + ret = &Object{runtime: r} + r.global.FunctionPrototype = ret + r.newTemplatedFuncObject(getFunctionProtoTemplate(), ret, func(FunctionCall) Value { + return _undefined + }, nil) + } + return ret +} + +func (r *Runtime) createFunction(v *Object) objectImpl { + return r.newNativeFuncConstructObj(v, r.builtin_Function, "Function", r.getFunctionPrototype(), 1) } func (r *Runtime) createAsyncFunctionProto(val *Object) objectImpl { @@ -229,7 +282,7 @@ func (r *Runtime) createAsyncFunctionProto(val *Object) objectImpl { class: classObject, val: val, extensible: true, - prototype: r.global.FunctionPrototype, + prototype: r.getFunctionPrototype(), } o.init() @@ -243,8 +296,9 @@ func (r *Runtime) createAsyncFunctionProto(val *Object) objectImpl { func (r *Runtime) getAsyncFunctionPrototype() *Object { var o *Object if o = r.global.AsyncFunctionPrototype; o == nil { - o = r.newLazyObject(r.createAsyncFunctionProto) + o = &Object{runtime: r} r.global.AsyncFunctionPrototype = o + o.self = r.createAsyncFunctionProto(o) } return o } @@ -293,7 +347,7 @@ func (r *Runtime) builtin_genproto_throw(call FunctionCall) Value { } func (r *Runtime) createGeneratorFunctionProto(val *Object) objectImpl { - o := newBaseObjectObj(val, r.global.FunctionPrototype, classObject) + o := newBaseObjectObj(val, r.getFunctionPrototype(), classObject) o._putProp("constructor", r.getGeneratorFunction(), false, false, true) o._putProp("prototype", r.getGeneratorPrototype(), false, false, true) @@ -305,8 +359,9 @@ func (r *Runtime) createGeneratorFunctionProto(val *Object) objectImpl { func (r *Runtime) getGeneratorFunctionPrototype() *Object { var o *Object if o = r.global.GeneratorFunctionPrototype; o == nil { - o = r.newLazyObject(r.createGeneratorFunctionProto) + o = &Object{runtime: r} r.global.GeneratorFunctionPrototype = o + o.self = r.createGeneratorFunctionProto(o) } return o } @@ -330,9 +385,9 @@ func (r *Runtime) createGeneratorProto(val *Object) objectImpl { o := newBaseObjectObj(val, r.getIteratorPrototype(), classObject) o._putProp("constructor", r.getGeneratorFunctionPrototype(), false, false, true) - o._putProp("next", r.newNativeFunc(r.builtin_genproto_next, nil, "next", nil, 1), true, false, true) - o._putProp("return", r.newNativeFunc(r.builtin_genproto_return, nil, "return", nil, 1), true, false, true) - o._putProp("throw", r.newNativeFunc(r.builtin_genproto_throw, nil, "throw", nil, 1), true, false, true) + o._putProp("next", r.newNativeFunc(r.builtin_genproto_next, "next", 1), true, false, true) + o._putProp("return", r.newNativeFunc(r.builtin_genproto_return, "return", 1), true, false, true) + o._putProp("throw", r.newNativeFunc(r.builtin_genproto_throw, "throw", 1), true, false, true) o._putSym(SymToStringTag, valueProp(asciiString(classGenerator), false, false, true)) @@ -348,3 +403,14 @@ func (r *Runtime) getGeneratorPrototype() *Object { } return o } + +func (r *Runtime) getFunction() *Object { + ret := r.global.Function + if ret == nil { + ret = &Object{runtime: r} + r.global.Function = ret + ret.self = r.createFunction(ret) + } + + return ret +} diff --git a/builtin_global.go b/builtin_global.go index cd6bfd40..5ef4176b 100644 --- a/builtin_global.go +++ b/builtin_global.go @@ -8,6 +8,7 @@ import ( "regexp" "strconv" "strings" + "sync" "unicode/utf8" ) @@ -70,7 +71,7 @@ func (r *Runtime) _encode(uriString String, unescaped *[256]bool) String { rn, _, err := reader.ReadRune() if err != nil { if err != io.EOF { - panic(r.newError(r.global.URIError, "Malformed URI")) + panic(r.newError(r.getURIError(), "Malformed URI")) } break } @@ -127,7 +128,7 @@ func (r *Runtime) _decode(sv String, reservedSet *[256]bool) String { switch s[i] { case '%': if i+2 >= len(s) || !ishex(s[i+1]) || !ishex(s[i+2]) { - panic(r.newError(r.global.URIError, "Malformed URI")) + panic(r.newError(r.getURIError(), "Malformed URI")) } c := unhex(s[i+1])<<4 | unhex(s[i+2]) if !reservedSet[c] { @@ -183,7 +184,7 @@ func (r *Runtime) _decode(sv String, reservedSet *[256]bool) String { rn, size := utf8.DecodeRune(t) if rn == utf8.RuneError { if size != 3 || t[0] != 0xef || t[1] != 0xbf || t[2] != 0xbd { - panic(r.newError(r.global.URIError, "Malformed URI")) + panic(r.newError(r.getURIError(), "Malformed URI")) } } us = append(us, rn) @@ -327,28 +328,84 @@ func (r *Runtime) builtin_unescape(call FunctionCall) Value { return asciiString(asciiBuf) } -func (r *Runtime) initGlobalObject() { - o := r.globalObject.self - o._putProp("globalThis", r.globalObject, true, false, true) - o._putProp("NaN", _NaN, false, false, false) - o._putProp("undefined", _undefined, false, false, false) - o._putProp("Infinity", _positiveInf, false, false, false) - - o._putProp("isNaN", r.newNativeFunc(r.builtin_isNaN, nil, "isNaN", nil, 1), true, false, true) - o._putProp("parseInt", r.newNativeFunc(r.builtin_parseInt, nil, "parseInt", nil, 2), true, false, true) - o._putProp("parseFloat", r.newNativeFunc(r.builtin_parseFloat, nil, "parseFloat", nil, 1), true, false, true) - o._putProp("isFinite", r.newNativeFunc(r.builtin_isFinite, nil, "isFinite", nil, 1), true, false, true) - o._putProp("decodeURI", r.newNativeFunc(r.builtin_decodeURI, nil, "decodeURI", nil, 1), true, false, true) - o._putProp("decodeURIComponent", r.newNativeFunc(r.builtin_decodeURIComponent, nil, "decodeURIComponent", nil, 1), true, false, true) - o._putProp("encodeURI", r.newNativeFunc(r.builtin_encodeURI, nil, "encodeURI", nil, 1), true, false, true) - o._putProp("encodeURIComponent", r.newNativeFunc(r.builtin_encodeURIComponent, nil, "encodeURIComponent", nil, 1), true, false, true) - o._putProp("escape", r.newNativeFunc(r.builtin_escape, nil, "escape", nil, 1), true, false, true) - o._putProp("unescape", r.newNativeFunc(r.builtin_unescape, nil, "unescape", nil, 1), true, false, true) - - o._putSym(SymToStringTag, valueProp(asciiString(classGlobal), false, false, true)) +func createGlobalObjectTemplate() *objectTemplate { + t := newObjectTemplate() + t.protoFactory = func(r *Runtime) *Object { + return r.global.ObjectPrototype + } + + t.putStr("Object", func(r *Runtime) Value { return valueProp(r.getObject(), true, false, true) }) + t.putStr("Function", func(r *Runtime) Value { return valueProp(r.getFunction(), true, false, true) }) + t.putStr("Array", func(r *Runtime) Value { return valueProp(r.getArray(), true, false, true) }) + t.putStr("String", func(r *Runtime) Value { return valueProp(r.getString(), true, false, true) }) + t.putStr("Number", func(r *Runtime) Value { return valueProp(r.getNumber(), true, false, true) }) + t.putStr("RegExp", func(r *Runtime) Value { return valueProp(r.getRegExp(), true, false, true) }) + t.putStr("Date", func(r *Runtime) Value { return valueProp(r.getDate(), true, false, true) }) + t.putStr("Boolean", func(r *Runtime) Value { return valueProp(r.getBoolean(), true, false, true) }) + t.putStr("Proxy", func(r *Runtime) Value { return valueProp(r.getProxy(), true, false, true) }) + t.putStr("Reflect", func(r *Runtime) Value { return valueProp(r.getReflect(), true, false, true) }) + t.putStr("Error", func(r *Runtime) Value { return valueProp(r.getError(), true, false, true) }) + t.putStr("AggregateError", func(r *Runtime) Value { return valueProp(r.getAggregateError(), true, false, true) }) + t.putStr("TypeError", func(r *Runtime) Value { return valueProp(r.getTypeError(), true, false, true) }) + t.putStr("ReferenceError", func(r *Runtime) Value { return valueProp(r.getReferenceError(), true, false, true) }) + t.putStr("SyntaxError", func(r *Runtime) Value { return valueProp(r.getSyntaxError(), true, false, true) }) + t.putStr("RangeError", func(r *Runtime) Value { return valueProp(r.getRangeError(), true, false, true) }) + t.putStr("EvalError", func(r *Runtime) Value { return valueProp(r.getEvalError(), true, false, true) }) + t.putStr("URIError", func(r *Runtime) Value { return valueProp(r.getURIError(), true, false, true) }) + t.putStr("GoError", func(r *Runtime) Value { return valueProp(r.getGoError(), true, false, true) }) + + t.putStr("eval", func(r *Runtime) Value { return valueProp(r.getEval(), true, false, true) }) + + t.putStr("Math", func(r *Runtime) Value { return valueProp(r.getMath(), true, false, true) }) + t.putStr("JSON", func(r *Runtime) Value { return valueProp(r.getJSON(), true, false, true) }) + addTypedArrays(t) + t.putStr("Symbol", func(r *Runtime) Value { return valueProp(r.getSymbol(), true, false, true) }) + t.putStr("WeakSet", func(r *Runtime) Value { return valueProp(r.getWeakSet(), true, false, true) }) + t.putStr("WeakMap", func(r *Runtime) Value { return valueProp(r.getWeakMap(), true, false, true) }) + t.putStr("Map", func(r *Runtime) Value { return valueProp(r.getMap(), true, false, true) }) + t.putStr("Set", func(r *Runtime) Value { return valueProp(r.getSet(), true, false, true) }) + t.putStr("Promise", func(r *Runtime) Value { return valueProp(r.getPromise(), true, false, true) }) + + t.putStr("globalThis", func(r *Runtime) Value { return valueProp(r.globalObject, true, false, true) }) + t.putStr("NaN", func(r *Runtime) Value { return valueProp(_NaN, false, false, false) }) + t.putStr("undefined", func(r *Runtime) Value { return valueProp(_undefined, false, false, false) }) + t.putStr("Infinity", func(r *Runtime) Value { return valueProp(_positiveInf, false, false, false) }) + + t.putStr("isNaN", func(r *Runtime) Value { return r.methodProp(r.builtin_isNaN, "isNaN", 1) }) + t.putStr("parseInt", func(r *Runtime) Value { return valueProp(r.getParseInt(), true, false, true) }) + t.putStr("parseFloat", func(r *Runtime) Value { return valueProp(r.getParseFloat(), true, false, true) }) + t.putStr("isFinite", func(r *Runtime) Value { return r.methodProp(r.builtin_isFinite, "isFinite", 1) }) + t.putStr("decodeURI", func(r *Runtime) Value { return r.methodProp(r.builtin_decodeURI, "decodeURI", 1) }) + t.putStr("decodeURIComponent", func(r *Runtime) Value { return r.methodProp(r.builtin_decodeURIComponent, "decodeURIComponent", 1) }) + t.putStr("encodeURI", func(r *Runtime) Value { return r.methodProp(r.builtin_encodeURI, "encodeURI", 1) }) + t.putStr("encodeURIComponent", func(r *Runtime) Value { return r.methodProp(r.builtin_encodeURIComponent, "encodeURIComponent", 1) }) + t.putStr("escape", func(r *Runtime) Value { return r.methodProp(r.builtin_escape, "escape", 1) }) + t.putStr("unescape", func(r *Runtime) Value { return r.methodProp(r.builtin_unescape, "unescape", 1) }) // TODO: Annex B + t.putSym(SymToStringTag, func(r *Runtime) Value { return valueProp(asciiString(classGlobal), false, false, true) }) + + return t +} + +var globalObjectTemplate *objectTemplate +var globalObjectTemplateOnce sync.Once + +func getGlobalObjectTemplate() *objectTemplate { + globalObjectTemplateOnce.Do(func() { + globalObjectTemplate = createGlobalObjectTemplate() + }) + return globalObjectTemplate +} + +func (r *Runtime) getEval() *Object { + ret := r.global.Eval + if ret == nil { + ret = r.newNativeFunc(r.builtin_eval, "eval", 1) + r.global.Eval = ret + } + return ret } func digitVal(d byte) int { diff --git a/builtin_json.go b/builtin_json.go index ff0c9c71..9adb121d 100644 --- a/builtin_json.go +++ b/builtin_json.go @@ -22,14 +22,14 @@ func (r *Runtime) builtinJSON_parse(call FunctionCall) Value { value, err := r.builtinJSON_decodeValue(d) if errors.Is(err, io.EOF) { - panic(r.newError(r.global.SyntaxError, "Unexpected end of JSON input (%v)", err.Error())) + panic(r.newError(r.getSyntaxError(), "Unexpected end of JSON input (%v)", err.Error())) } if err != nil { - panic(r.newError(r.global.SyntaxError, err.Error())) + panic(r.newError(r.getSyntaxError(), err.Error())) } if tok, err := d.Token(); err != io.EOF { - panic(r.newError(r.global.SyntaxError, "Unexpected token at the end: %v", tok)) + panic(r.newError(r.getSyntaxError(), "Unexpected token at the end: %v", tok)) } var reviver func(FunctionCall) Value @@ -522,11 +522,15 @@ func (ctx *_builtinJSON_stringifyContext) quote(str String) { ctx.buf.WriteByte('"') } -func (r *Runtime) initJSON() { - JSON := r.newBaseObject(r.global.ObjectPrototype, classObject) - JSON._putProp("parse", r.newNativeFunc(r.builtinJSON_parse, nil, "parse", nil, 2), true, false, true) - JSON._putProp("stringify", r.newNativeFunc(r.builtinJSON_stringify, nil, "stringify", nil, 3), true, false, true) - JSON._putSym(SymToStringTag, valueProp(asciiString(classJSON), false, false, true)) - - r.addToGlobal("JSON", JSON.val) +func (r *Runtime) getJSON() *Object { + ret := r.global.JSON + if ret == nil { + JSON := r.newBaseObject(r.global.ObjectPrototype, classObject) + ret = JSON.val + r.global.JSON = ret + JSON._putProp("parse", r.newNativeFunc(r.builtinJSON_parse, "parse", 2), true, false, true) + JSON._putProp("stringify", r.newNativeFunc(r.builtinJSON_stringify, "stringify", 3), true, false, true) + JSON._putSym(SymToStringTag, valueProp(asciiString(classJSON), false, false, true)) + } + return ret } diff --git a/builtin_map.go b/builtin_map.go index 15ce4226..819d025f 100644 --- a/builtin_map.go +++ b/builtin_map.go @@ -270,24 +270,24 @@ func (r *Runtime) mapIterProto_next(call FunctionCall) Value { func (r *Runtime) createMapProto(val *Object) objectImpl { o := newBaseObjectObj(val, r.global.ObjectPrototype, classObject) - o._putProp("constructor", r.global.Map, true, false, true) - o._putProp("clear", r.newNativeFunc(r.mapProto_clear, nil, "clear", nil, 0), true, false, true) - r.global.mapAdder = r.newNativeFunc(r.mapProto_set, nil, "set", nil, 2) + o._putProp("constructor", r.getMap(), true, false, true) + o._putProp("clear", r.newNativeFunc(r.mapProto_clear, "clear", 0), true, false, true) + r.global.mapAdder = r.newNativeFunc(r.mapProto_set, "set", 2) o._putProp("set", r.global.mapAdder, true, false, true) - o._putProp("delete", r.newNativeFunc(r.mapProto_delete, nil, "delete", nil, 1), true, false, true) - o._putProp("forEach", r.newNativeFunc(r.mapProto_forEach, nil, "forEach", nil, 1), true, false, true) - o._putProp("has", r.newNativeFunc(r.mapProto_has, nil, "has", nil, 1), true, false, true) - o._putProp("get", r.newNativeFunc(r.mapProto_get, nil, "get", nil, 1), true, false, true) + o._putProp("delete", r.newNativeFunc(r.mapProto_delete, "delete", 1), true, false, true) + o._putProp("forEach", r.newNativeFunc(r.mapProto_forEach, "forEach", 1), true, false, true) + o._putProp("has", r.newNativeFunc(r.mapProto_has, "has", 1), true, false, true) + o._putProp("get", r.newNativeFunc(r.mapProto_get, "get", 1), true, false, true) o.setOwnStr("size", &valueProperty{ - getterFunc: r.newNativeFunc(r.mapProto_getSize, nil, "get size", nil, 0), + getterFunc: r.newNativeFunc(r.mapProto_getSize, "get size", 0), accessor: true, writable: true, configurable: true, }, true) - o._putProp("keys", r.newNativeFunc(r.mapProto_keys, nil, "keys", nil, 0), true, false, true) - o._putProp("values", r.newNativeFunc(r.mapProto_values, nil, "values", nil, 0), true, false, true) + o._putProp("keys", r.newNativeFunc(r.mapProto_keys, "keys", 0), true, false, true) + o._putProp("values", r.newNativeFunc(r.mapProto_values, "values", 0), true, false, true) - entriesFunc := r.newNativeFunc(r.mapProto_entries, nil, "entries", nil, 0) + entriesFunc := r.newNativeFunc(r.mapProto_entries, "entries", 0) o._putProp("entries", entriesFunc, true, false, true) o._putSym(SymIterator, valueProp(entriesFunc, true, false, true)) o._putSym(SymToStringTag, valueProp(asciiString(classMap), false, false, true)) @@ -296,7 +296,7 @@ func (r *Runtime) createMapProto(val *Object) objectImpl { } func (r *Runtime) createMap(val *Object) objectImpl { - o := r.newNativeConstructOnly(val, r.builtin_newMap, r.global.MapPrototype, "Map", 0) + o := r.newNativeConstructOnly(val, r.builtin_newMap, r.getMapPrototype(), "Map", 0) r.putSpeciesReturnThis(o) return o @@ -305,7 +305,7 @@ func (r *Runtime) createMap(val *Object) objectImpl { func (r *Runtime) createMapIterProto(val *Object) objectImpl { o := newBaseObjectObj(val, r.getIteratorPrototype(), classObject) - o._putProp("next", r.newNativeFunc(r.mapIterProto_next, nil, "next", nil, 0), true, false, true) + o._putProp("next", r.newNativeFunc(r.mapIterProto_next, "next", 0), true, false, true) o._putSym(SymToStringTag, valueProp(asciiString(classMapIterator), false, false, true)) return o @@ -321,11 +321,22 @@ func (r *Runtime) getMapIteratorPrototype() *Object { return o } -func (r *Runtime) initMap() { - r.global.MapIteratorPrototype = r.newLazyObject(r.createMapIterProto) - - r.global.MapPrototype = r.newLazyObject(r.createMapProto) - r.global.Map = r.newLazyObject(r.createMap) +func (r *Runtime) getMapPrototype() *Object { + ret := r.global.MapPrototype + if ret == nil { + ret = &Object{runtime: r} + r.global.MapPrototype = ret + ret.self = r.createMapProto(ret) + } + return ret +} - r.addToGlobal("Map", r.global.Map) +func (r *Runtime) getMap() *Object { + ret := r.global.Map + if ret == nil { + ret = &Object{runtime: r} + r.global.Map = ret + ret.self = r.createMap(ret) + } + return ret } diff --git a/builtin_math.go b/builtin_math.go index 6347201b..169ea18c 100644 --- a/builtin_math.go +++ b/builtin_math.go @@ -3,6 +3,7 @@ package goja import ( "math" "math/bits" + "sync" ) func (r *Runtime) math_abs(call FunctionCall) Value { @@ -280,64 +281,78 @@ func (r *Runtime) math_trunc(call FunctionCall) Value { return floatToValue(math.Trunc(arg.ToFloat())) } -func (r *Runtime) createMath(val *Object) objectImpl { - m := &baseObject{ - class: classObject, - val: val, - extensible: true, - prototype: r.global.ObjectPrototype, +func createMathTemplate() *objectTemplate { + t := newObjectTemplate() + t.protoFactory = func(r *Runtime) *Object { + return r.global.ObjectPrototype } - m.init() - - m._putProp("E", valueFloat(math.E), false, false, false) - m._putProp("LN10", valueFloat(math.Ln10), false, false, false) - m._putProp("LN2", valueFloat(math.Ln2), false, false, false) - m._putProp("LOG10E", valueFloat(math.Log10E), false, false, false) - m._putProp("LOG2E", valueFloat(math.Log2E), false, false, false) - m._putProp("PI", valueFloat(math.Pi), false, false, false) - m._putProp("SQRT1_2", valueFloat(sqrt1_2), false, false, false) - m._putProp("SQRT2", valueFloat(math.Sqrt2), false, false, false) - m._putSym(SymToStringTag, valueProp(asciiString(classMath), false, false, true)) - - m._putProp("abs", r.newNativeFunc(r.math_abs, nil, "abs", nil, 1), true, false, true) - m._putProp("acos", r.newNativeFunc(r.math_acos, nil, "acos", nil, 1), true, false, true) - m._putProp("acosh", r.newNativeFunc(r.math_acosh, nil, "acosh", nil, 1), true, false, true) - m._putProp("asin", r.newNativeFunc(r.math_asin, nil, "asin", nil, 1), true, false, true) - m._putProp("asinh", r.newNativeFunc(r.math_asinh, nil, "asinh", nil, 1), true, false, true) - m._putProp("atan", r.newNativeFunc(r.math_atan, nil, "atan", nil, 1), true, false, true) - m._putProp("atanh", r.newNativeFunc(r.math_atanh, nil, "atanh", nil, 1), true, false, true) - m._putProp("atan2", r.newNativeFunc(r.math_atan2, nil, "atan2", nil, 2), true, false, true) - m._putProp("cbrt", r.newNativeFunc(r.math_cbrt, nil, "cbrt", nil, 1), true, false, true) - m._putProp("ceil", r.newNativeFunc(r.math_ceil, nil, "ceil", nil, 1), true, false, true) - m._putProp("clz32", r.newNativeFunc(r.math_clz32, nil, "clz32", nil, 1), true, false, true) - m._putProp("cos", r.newNativeFunc(r.math_cos, nil, "cos", nil, 1), true, false, true) - m._putProp("cosh", r.newNativeFunc(r.math_cosh, nil, "cosh", nil, 1), true, false, true) - m._putProp("exp", r.newNativeFunc(r.math_exp, nil, "exp", nil, 1), true, false, true) - m._putProp("expm1", r.newNativeFunc(r.math_expm1, nil, "expm1", nil, 1), true, false, true) - m._putProp("floor", r.newNativeFunc(r.math_floor, nil, "floor", nil, 1), true, false, true) - m._putProp("fround", r.newNativeFunc(r.math_fround, nil, "fround", nil, 1), true, false, true) - m._putProp("hypot", r.newNativeFunc(r.math_hypot, nil, "hypot", nil, 2), true, false, true) - m._putProp("imul", r.newNativeFunc(r.math_imul, nil, "imul", nil, 2), true, false, true) - m._putProp("log", r.newNativeFunc(r.math_log, nil, "log", nil, 1), true, false, true) - m._putProp("log1p", r.newNativeFunc(r.math_log1p, nil, "log1p", nil, 1), true, false, true) - m._putProp("log10", r.newNativeFunc(r.math_log10, nil, "log10", nil, 1), true, false, true) - m._putProp("log2", r.newNativeFunc(r.math_log2, nil, "log2", nil, 1), true, false, true) - m._putProp("max", r.newNativeFunc(r.math_max, nil, "max", nil, 2), true, false, true) - m._putProp("min", r.newNativeFunc(r.math_min, nil, "min", nil, 2), true, false, true) - m._putProp("pow", r.newNativeFunc(r.math_pow, nil, "pow", nil, 2), true, false, true) - m._putProp("random", r.newNativeFunc(r.math_random, nil, "random", nil, 0), true, false, true) - m._putProp("round", r.newNativeFunc(r.math_round, nil, "round", nil, 1), true, false, true) - m._putProp("sign", r.newNativeFunc(r.math_sign, nil, "sign", nil, 1), true, false, true) - m._putProp("sin", r.newNativeFunc(r.math_sin, nil, "sin", nil, 1), true, false, true) - m._putProp("sinh", r.newNativeFunc(r.math_sinh, nil, "sinh", nil, 1), true, false, true) - m._putProp("sqrt", r.newNativeFunc(r.math_sqrt, nil, "sqrt", nil, 1), true, false, true) - m._putProp("tan", r.newNativeFunc(r.math_tan, nil, "tan", nil, 1), true, false, true) - m._putProp("tanh", r.newNativeFunc(r.math_tanh, nil, "tanh", nil, 1), true, false, true) - m._putProp("trunc", r.newNativeFunc(r.math_trunc, nil, "trunc", nil, 1), true, false, true) - - return m -} - -func (r *Runtime) initMath() { - r.addToGlobal("Math", r.newLazyObject(r.createMath)) + + t.putStr("E", func(r *Runtime) Value { return valueProp(valueFloat(math.E), false, false, false) }) + t.putStr("LN10", func(r *Runtime) Value { return valueProp(valueFloat(math.Ln10), false, false, false) }) + t.putStr("LN2", func(r *Runtime) Value { return valueProp(valueFloat(math.Ln2), false, false, false) }) + t.putStr("LOG10E", func(r *Runtime) Value { return valueProp(valueFloat(math.Log10E), false, false, false) }) + t.putStr("LOG2E", func(r *Runtime) Value { return valueProp(valueFloat(math.Log2E), false, false, false) }) + t.putStr("PI", func(r *Runtime) Value { return valueProp(valueFloat(math.Pi), false, false, false) }) + t.putStr("SQRT1_2", func(r *Runtime) Value { return valueProp(valueFloat(sqrt1_2), false, false, false) }) + t.putStr("SQRT2", func(r *Runtime) Value { return valueProp(valueFloat(math.Sqrt2), false, false, false) }) + + t.putSym(SymToStringTag, func(r *Runtime) Value { return valueProp(asciiString(classMath), false, false, true) }) + + t.putStr("abs", func(r *Runtime) Value { return r.methodProp(r.math_abs, "abs", 1) }) + t.putStr("acos", func(r *Runtime) Value { return r.methodProp(r.math_acos, "acos", 1) }) + t.putStr("acosh", func(r *Runtime) Value { return r.methodProp(r.math_acosh, "acosh", 1) }) + t.putStr("asin", func(r *Runtime) Value { return r.methodProp(r.math_asin, "asin", 1) }) + t.putStr("asinh", func(r *Runtime) Value { return r.methodProp(r.math_asinh, "asinh", 1) }) + t.putStr("atan", func(r *Runtime) Value { return r.methodProp(r.math_atan, "atan", 1) }) + t.putStr("atanh", func(r *Runtime) Value { return r.methodProp(r.math_atanh, "atanh", 1) }) + t.putStr("atan2", func(r *Runtime) Value { return r.methodProp(r.math_atan2, "atan2", 2) }) + t.putStr("cbrt", func(r *Runtime) Value { return r.methodProp(r.math_cbrt, "cbrt", 1) }) + t.putStr("ceil", func(r *Runtime) Value { return r.methodProp(r.math_ceil, "ceil", 1) }) + t.putStr("clz32", func(r *Runtime) Value { return r.methodProp(r.math_clz32, "clz32", 1) }) + t.putStr("cos", func(r *Runtime) Value { return r.methodProp(r.math_cos, "cos", 1) }) + t.putStr("cosh", func(r *Runtime) Value { return r.methodProp(r.math_cosh, "cosh", 1) }) + t.putStr("exp", func(r *Runtime) Value { return r.methodProp(r.math_exp, "exp", 1) }) + t.putStr("expm1", func(r *Runtime) Value { return r.methodProp(r.math_expm1, "expm1", 1) }) + t.putStr("floor", func(r *Runtime) Value { return r.methodProp(r.math_floor, "floor", 1) }) + t.putStr("fround", func(r *Runtime) Value { return r.methodProp(r.math_fround, "fround", 1) }) + t.putStr("hypot", func(r *Runtime) Value { return r.methodProp(r.math_hypot, "hypot", 2) }) + t.putStr("imul", func(r *Runtime) Value { return r.methodProp(r.math_imul, "imul", 2) }) + t.putStr("log", func(r *Runtime) Value { return r.methodProp(r.math_log, "log", 1) }) + t.putStr("log1p", func(r *Runtime) Value { return r.methodProp(r.math_log1p, "log1p", 1) }) + t.putStr("log10", func(r *Runtime) Value { return r.methodProp(r.math_log10, "log10", 1) }) + t.putStr("log2", func(r *Runtime) Value { return r.methodProp(r.math_log2, "log2", 1) }) + t.putStr("max", func(r *Runtime) Value { return r.methodProp(r.math_max, "max", 2) }) + t.putStr("min", func(r *Runtime) Value { return r.methodProp(r.math_min, "min", 2) }) + t.putStr("pow", func(r *Runtime) Value { return r.methodProp(r.math_pow, "pow", 2) }) + t.putStr("random", func(r *Runtime) Value { return r.methodProp(r.math_random, "random", 0) }) + t.putStr("round", func(r *Runtime) Value { return r.methodProp(r.math_round, "round", 1) }) + t.putStr("sign", func(r *Runtime) Value { return r.methodProp(r.math_sign, "sign", 1) }) + t.putStr("sin", func(r *Runtime) Value { return r.methodProp(r.math_sin, "sin", 1) }) + t.putStr("sinh", func(r *Runtime) Value { return r.methodProp(r.math_sinh, "sinh", 1) }) + t.putStr("sqrt", func(r *Runtime) Value { return r.methodProp(r.math_sqrt, "sqrt", 1) }) + t.putStr("tan", func(r *Runtime) Value { return r.methodProp(r.math_tan, "tan", 1) }) + t.putStr("tanh", func(r *Runtime) Value { return r.methodProp(r.math_tanh, "tanh", 1) }) + t.putStr("trunc", func(r *Runtime) Value { return r.methodProp(r.math_trunc, "trunc", 1) }) + + return t +} + +var mathTemplate *objectTemplate +var mathTemplateOnce sync.Once + +func getMathTemplate() *objectTemplate { + mathTemplateOnce.Do(func() { + mathTemplate = createMathTemplate() + }) + return mathTemplate +} + +func (r *Runtime) getMath() *Object { + ret := r.global.Math + if ret == nil { + ret = &Object{runtime: r} + r.global.Math = ret + r.newTemplatedObject(getMathTemplate(), ret) + } + return ret } diff --git a/builtin_number.go b/builtin_number.go index 472e0147..43add4ff 100644 --- a/builtin_number.go +++ b/builtin_number.go @@ -2,6 +2,7 @@ package goja import ( "math" + "sync" "github.com/dop251/goja/ftoa" ) @@ -19,6 +20,9 @@ func (r *Runtime) toNumber(v Value) Value { return t.valueOf() } } + if t == r.global.NumberPrototype { + return _positiveZero + } } panic(r.NewTypeError("Value is not a number: %s", v)) } @@ -46,6 +50,9 @@ func (r *Runtime) numberproto_toString(call FunctionCall) Value { } } } + if t == r.global.NumberPrototype { + return asciiString("0") + } } if numVal == nil { panic(r.NewTypeError("Value is not a number")) @@ -58,7 +65,7 @@ func (r *Runtime) numberproto_toString(call FunctionCall) Value { } if radix < 2 || radix > 36 { - panic(r.newError(r.global.RangeError, "toString() radix argument must be between 2 and 36")) + panic(r.newError(r.getRangeError(), "toString() radix argument must be between 2 and 36")) } num := numVal.ToFloat() @@ -87,7 +94,7 @@ func (r *Runtime) numberproto_toFixed(call FunctionCall) Value { prec := call.Argument(0).ToInteger() if prec < 0 || prec > 100 { - panic(r.newError(r.global.RangeError, "toFixed() precision must be between 0 and 100")) + panic(r.newError(r.getRangeError(), "toFixed() precision must be between 0 and 100")) } if math.IsNaN(num) { return stringNaN @@ -116,7 +123,7 @@ func (r *Runtime) numberproto_toExponential(call FunctionCall) Value { } if prec < 0 || prec > 100 { - panic(r.newError(r.global.RangeError, "toExponential() precision must be between 0 and 100")) + panic(r.newError(r.getRangeError(), "toExponential() precision must be between 0 and 100")) } return asciiString(fToStr(num, ftoa.ModeExponential, int(prec+1))) @@ -141,7 +148,7 @@ func (r *Runtime) numberproto_toPrecision(call FunctionCall) Value { return stringNegInfinity } if prec < 1 || prec > 100 { - panic(r.newError(r.global.RangeError, "toPrecision() precision must be between 1 and 100")) + panic(r.newError(r.getRangeError(), "toPrecision() precision must be between 1 and 100")) } return asciiString(fToStr(num, ftoa.ModePrecision, int(prec))) @@ -189,32 +196,108 @@ func (r *Runtime) number_isSafeInteger(call FunctionCall) Value { return valueFalse } -func (r *Runtime) initNumber() { - r.global.NumberPrototype = r.newPrimitiveObject(valueInt(0), r.global.ObjectPrototype, classNumber) - o := r.global.NumberPrototype.self - o._putProp("toExponential", r.newNativeFunc(r.numberproto_toExponential, nil, "toExponential", nil, 1), true, false, true) - o._putProp("toFixed", r.newNativeFunc(r.numberproto_toFixed, nil, "toFixed", nil, 1), true, false, true) - o._putProp("toLocaleString", r.newNativeFunc(r.numberproto_toString, nil, "toLocaleString", nil, 0), true, false, true) - o._putProp("toPrecision", r.newNativeFunc(r.numberproto_toPrecision, nil, "toPrecision", nil, 1), true, false, true) - o._putProp("toString", r.newNativeFunc(r.numberproto_toString, nil, "toString", nil, 1), true, false, true) - o._putProp("valueOf", r.newNativeFunc(r.numberproto_valueOf, nil, "valueOf", nil, 0), true, false, true) - - r.global.Number = r.newNativeFunc(r.builtin_Number, r.builtin_newNumber, "Number", r.global.NumberPrototype, 1) - o = r.global.Number.self - o._putProp("EPSILON", _epsilon, false, false, false) - o._putProp("isFinite", r.newNativeFunc(r.number_isFinite, nil, "isFinite", nil, 1), true, false, true) - o._putProp("isInteger", r.newNativeFunc(r.number_isInteger, nil, "isInteger", nil, 1), true, false, true) - o._putProp("isNaN", r.newNativeFunc(r.number_isNaN, nil, "isNaN", nil, 1), true, false, true) - o._putProp("isSafeInteger", r.newNativeFunc(r.number_isSafeInteger, nil, "isSafeInteger", nil, 1), true, false, true) - o._putProp("MAX_SAFE_INTEGER", valueInt(maxInt-1), false, false, false) - o._putProp("MIN_SAFE_INTEGER", valueInt(-(maxInt - 1)), false, false, false) - o._putProp("MIN_VALUE", valueFloat(math.SmallestNonzeroFloat64), false, false, false) - o._putProp("MAX_VALUE", valueFloat(math.MaxFloat64), false, false, false) - o._putProp("NaN", _NaN, false, false, false) - o._putProp("NEGATIVE_INFINITY", _negativeInf, false, false, false) - o._putProp("parseFloat", r.Get("parseFloat"), true, false, true) - o._putProp("parseInt", r.Get("parseInt"), true, false, true) - o._putProp("POSITIVE_INFINITY", _positiveInf, false, false, false) - r.addToGlobal("Number", r.global.Number) +func createNumberProtoTemplate() *objectTemplate { + t := newObjectTemplate() + t.protoFactory = func(r *Runtime) *Object { + return r.global.ObjectPrototype + } + + t.putStr("constructor", func(r *Runtime) Value { return valueProp(r.getNumber(), true, false, true) }) + + t.putStr("toExponential", func(r *Runtime) Value { return r.methodProp(r.numberproto_toExponential, "toExponential", 1) }) + t.putStr("toFixed", func(r *Runtime) Value { return r.methodProp(r.numberproto_toFixed, "toFixed", 1) }) + t.putStr("toLocaleString", func(r *Runtime) Value { return r.methodProp(r.numberproto_toString, "toLocaleString", 0) }) + t.putStr("toPrecision", func(r *Runtime) Value { return r.methodProp(r.numberproto_toPrecision, "toPrecision", 1) }) + t.putStr("toString", func(r *Runtime) Value { return r.methodProp(r.numberproto_toString, "toString", 1) }) + t.putStr("valueOf", func(r *Runtime) Value { return r.methodProp(r.numberproto_valueOf, "valueOf", 0) }) + + return t +} + +var numberProtoTemplate *objectTemplate +var numberProtoTemplateOnce sync.Once +func getNumberProtoTemplate() *objectTemplate { + numberProtoTemplateOnce.Do(func() { + numberProtoTemplate = createNumberProtoTemplate() + }) + return numberProtoTemplate +} + +func (r *Runtime) getNumberPrototype() *Object { + ret := r.global.NumberPrototype + if ret == nil { + ret = &Object{runtime: r} + r.global.NumberPrototype = ret + o := r.newTemplatedObject(getNumberProtoTemplate(), ret) + o.class = classNumber + } + return ret +} + +func (r *Runtime) getParseFloat() *Object { + ret := r.global.parseFloat + if ret == nil { + ret = r.newNativeFunc(r.builtin_parseFloat, "parseFloat", 1) + r.global.parseFloat = ret + } + return ret +} + +func (r *Runtime) getParseInt() *Object { + ret := r.global.parseInt + if ret == nil { + ret = r.newNativeFunc(r.builtin_parseInt, "parseInt", 2) + r.global.parseInt = ret + } + return ret +} + +func createNumberTemplate() *objectTemplate { + t := newObjectTemplate() + t.protoFactory = func(r *Runtime) *Object { + return r.getFunctionPrototype() + } + t.putStr("length", func(r *Runtime) Value { return valueProp(intToValue(1), false, false, true) }) + t.putStr("name", func(r *Runtime) Value { return valueProp(asciiString("Number"), false, false, true) }) + + t.putStr("prototype", func(r *Runtime) Value { return valueProp(r.getNumberPrototype(), false, false, false) }) + + t.putStr("EPSILON", func(r *Runtime) Value { return valueProp(_epsilon, false, false, false) }) + t.putStr("isFinite", func(r *Runtime) Value { return r.methodProp(r.number_isFinite, "isFinite", 1) }) + t.putStr("isInteger", func(r *Runtime) Value { return r.methodProp(r.number_isInteger, "isInteger", 1) }) + t.putStr("isNaN", func(r *Runtime) Value { return r.methodProp(r.number_isNaN, "isNaN", 1) }) + t.putStr("isSafeInteger", func(r *Runtime) Value { return r.methodProp(r.number_isSafeInteger, "isSafeInteger", 1) }) + t.putStr("MAX_SAFE_INTEGER", func(r *Runtime) Value { return valueProp(valueInt(maxInt-1), false, false, false) }) + t.putStr("MIN_SAFE_INTEGER", func(r *Runtime) Value { return valueProp(valueInt(-(maxInt - 1)), false, false, false) }) + t.putStr("MIN_VALUE", func(r *Runtime) Value { return valueProp(valueFloat(math.SmallestNonzeroFloat64), false, false, false) }) + t.putStr("MAX_VALUE", func(r *Runtime) Value { return valueProp(valueFloat(math.MaxFloat64), false, false, false) }) + t.putStr("NaN", func(r *Runtime) Value { return valueProp(_NaN, false, false, false) }) + t.putStr("NEGATIVE_INFINITY", func(r *Runtime) Value { return valueProp(_negativeInf, false, false, false) }) + t.putStr("parseFloat", func(r *Runtime) Value { return valueProp(r.getParseFloat(), true, false, true) }) + t.putStr("parseInt", func(r *Runtime) Value { return valueProp(r.getParseInt(), true, false, true) }) + t.putStr("POSITIVE_INFINITY", func(r *Runtime) Value { return valueProp(_positiveInf, false, false, false) }) + + return t +} + +var numberTemplate *objectTemplate +var numberTemplateOnce sync.Once + +func getNumberTemplate() *objectTemplate { + numberTemplateOnce.Do(func() { + numberTemplate = createNumberTemplate() + }) + return numberTemplate +} + +func (r *Runtime) getNumber() *Object { + ret := r.global.Number + if ret == nil { + ret = &Object{runtime: r} + r.global.Number = ret + r.newTemplatedFuncObject(getNumberTemplate(), ret, r.builtin_Number, + r.wrapNativeConstruct(r.builtin_newNumber, ret, r.getNumberPrototype())) + } + return ret } diff --git a/builtin_object.go b/builtin_object.go index 4aabbbdc..6bf1ff80 100644 --- a/builtin_object.go +++ b/builtin_object.go @@ -2,10 +2,11 @@ package goja import ( "fmt" + "sync" ) func (r *Runtime) builtin_Object(args []Value, newTarget *Object) *Object { - if newTarget != nil && newTarget != r.global.Object { + if newTarget != nil && newTarget != r.getObject() { proto := r.getPrototypeFromCtor(newTarget, nil, r.global.ObjectPrototype) return r.newBaseObject(proto, classObject).val } @@ -595,45 +596,116 @@ func (r *Runtime) object_hasOwn(call FunctionCall) Value { } } -func (r *Runtime) initObject() { - o := r.global.ObjectPrototype.self - o._putProp("toString", r.newNativeFunc(r.objectproto_toString, nil, "toString", nil, 0), true, false, true) - o._putProp("toLocaleString", r.newNativeFunc(r.objectproto_toLocaleString, nil, "toLocaleString", nil, 0), true, false, true) - o._putProp("valueOf", r.newNativeFunc(r.objectproto_valueOf, nil, "valueOf", nil, 0), true, false, true) - o._putProp("hasOwnProperty", r.newNativeFunc(r.objectproto_hasOwnProperty, nil, "hasOwnProperty", nil, 1), true, false, true) - o._putProp("isPrototypeOf", r.newNativeFunc(r.objectproto_isPrototypeOf, nil, "isPrototypeOf", nil, 1), true, false, true) - o._putProp("propertyIsEnumerable", r.newNativeFunc(r.objectproto_propertyIsEnumerable, nil, "propertyIsEnumerable", nil, 1), true, false, true) - o.defineOwnPropertyStr(__proto__, PropertyDescriptor{ - Getter: r.newNativeFunc(r.objectproto_getProto, nil, "get __proto__", nil, 0), - Setter: r.newNativeFunc(r.objectproto_setProto, nil, "set __proto__", nil, 1), - Configurable: FLAG_TRUE, - }, true) - - r.global.Object = r.newNativeConstructOnly(nil, r.builtin_Object, r.global.ObjectPrototype, "Object", 1).val - r.global.ObjectPrototype.self._putProp("constructor", r.global.Object, true, false, true) - o = r.global.Object.self - o._putProp("assign", r.newNativeFunc(r.object_assign, nil, "assign", nil, 2), true, false, true) - o._putProp("defineProperty", r.newNativeFunc(r.object_defineProperty, nil, "defineProperty", nil, 3), true, false, true) - o._putProp("defineProperties", r.newNativeFunc(r.object_defineProperties, nil, "defineProperties", nil, 2), true, false, true) - o._putProp("entries", r.newNativeFunc(r.object_entries, nil, "entries", nil, 1), true, false, true) - o._putProp("getOwnPropertyDescriptor", r.newNativeFunc(r.object_getOwnPropertyDescriptor, nil, "getOwnPropertyDescriptor", nil, 2), true, false, true) - o._putProp("getOwnPropertyDescriptors", r.newNativeFunc(r.object_getOwnPropertyDescriptors, nil, "getOwnPropertyDescriptors", nil, 1), true, false, true) - o._putProp("getPrototypeOf", r.newNativeFunc(r.object_getPrototypeOf, nil, "getPrototypeOf", nil, 1), true, false, true) - o._putProp("is", r.newNativeFunc(r.object_is, nil, "is", nil, 2), true, false, true) - o._putProp("getOwnPropertyNames", r.newNativeFunc(r.object_getOwnPropertyNames, nil, "getOwnPropertyNames", nil, 1), true, false, true) - o._putProp("getOwnPropertySymbols", r.newNativeFunc(r.object_getOwnPropertySymbols, nil, "getOwnPropertySymbols", nil, 1), true, false, true) - o._putProp("create", r.newNativeFunc(r.object_create, nil, "create", nil, 2), true, false, true) - o._putProp("seal", r.newNativeFunc(r.object_seal, nil, "seal", nil, 1), true, false, true) - o._putProp("freeze", r.newNativeFunc(r.object_freeze, nil, "freeze", nil, 1), true, false, true) - o._putProp("preventExtensions", r.newNativeFunc(r.object_preventExtensions, nil, "preventExtensions", nil, 1), true, false, true) - o._putProp("isSealed", r.newNativeFunc(r.object_isSealed, nil, "isSealed", nil, 1), true, false, true) - o._putProp("isFrozen", r.newNativeFunc(r.object_isFrozen, nil, "isFrozen", nil, 1), true, false, true) - o._putProp("isExtensible", r.newNativeFunc(r.object_isExtensible, nil, "isExtensible", nil, 1), true, false, true) - o._putProp("keys", r.newNativeFunc(r.object_keys, nil, "keys", nil, 1), true, false, true) - o._putProp("setPrototypeOf", r.newNativeFunc(r.object_setPrototypeOf, nil, "setPrototypeOf", nil, 2), true, false, true) - o._putProp("values", r.newNativeFunc(r.object_values, nil, "values", nil, 1), true, false, true) - o._putProp("fromEntries", r.newNativeFunc(r.object_fromEntries, nil, "fromEntries", nil, 1), true, false, true) - o._putProp("hasOwn", r.newNativeFunc(r.object_hasOwn, nil, "hasOwn", nil, 2), true, false, true) - - r.addToGlobal("Object", r.global.Object) +func createObjectTemplate() *objectTemplate { + t := newObjectTemplate() + t.protoFactory = func(r *Runtime) *Object { + return r.getFunctionPrototype() + } + + t.putStr("length", func(r *Runtime) Value { return valueProp(intToValue(1), false, false, true) }) + t.putStr("name", func(r *Runtime) Value { return valueProp(asciiString("Object"), false, false, true) }) + + t.putStr("prototype", func(r *Runtime) Value { return valueProp(r.global.ObjectPrototype, false, false, false) }) + + t.putStr("assign", func(r *Runtime) Value { return r.methodProp(r.object_assign, "assign", 2) }) + t.putStr("defineProperty", func(r *Runtime) Value { return r.methodProp(r.object_defineProperty, "defineProperty", 3) }) + t.putStr("defineProperties", func(r *Runtime) Value { return r.methodProp(r.object_defineProperties, "defineProperties", 2) }) + t.putStr("entries", func(r *Runtime) Value { return r.methodProp(r.object_entries, "entries", 1) }) + t.putStr("getOwnPropertyDescriptor", func(r *Runtime) Value { + return r.methodProp(r.object_getOwnPropertyDescriptor, "getOwnPropertyDescriptor", 2) + }) + t.putStr("getOwnPropertyDescriptors", func(r *Runtime) Value { + return r.methodProp(r.object_getOwnPropertyDescriptors, "getOwnPropertyDescriptors", 1) + }) + t.putStr("getPrototypeOf", func(r *Runtime) Value { return r.methodProp(r.object_getPrototypeOf, "getPrototypeOf", 1) }) + t.putStr("is", func(r *Runtime) Value { return r.methodProp(r.object_is, "is", 2) }) + t.putStr("getOwnPropertyNames", func(r *Runtime) Value { return r.methodProp(r.object_getOwnPropertyNames, "getOwnPropertyNames", 1) }) + t.putStr("getOwnPropertySymbols", func(r *Runtime) Value { + return r.methodProp(r.object_getOwnPropertySymbols, "getOwnPropertySymbols", 1) + }) + t.putStr("create", func(r *Runtime) Value { return r.methodProp(r.object_create, "create", 2) }) + t.putStr("seal", func(r *Runtime) Value { return r.methodProp(r.object_seal, "seal", 1) }) + t.putStr("freeze", func(r *Runtime) Value { return r.methodProp(r.object_freeze, "freeze", 1) }) + t.putStr("preventExtensions", func(r *Runtime) Value { return r.methodProp(r.object_preventExtensions, "preventExtensions", 1) }) + t.putStr("isSealed", func(r *Runtime) Value { return r.methodProp(r.object_isSealed, "isSealed", 1) }) + t.putStr("isFrozen", func(r *Runtime) Value { return r.methodProp(r.object_isFrozen, "isFrozen", 1) }) + t.putStr("isExtensible", func(r *Runtime) Value { return r.methodProp(r.object_isExtensible, "isExtensible", 1) }) + t.putStr("keys", func(r *Runtime) Value { return r.methodProp(r.object_keys, "keys", 1) }) + t.putStr("setPrototypeOf", func(r *Runtime) Value { return r.methodProp(r.object_setPrototypeOf, "setPrototypeOf", 2) }) + t.putStr("values", func(r *Runtime) Value { return r.methodProp(r.object_values, "values", 1) }) + t.putStr("fromEntries", func(r *Runtime) Value { return r.methodProp(r.object_fromEntries, "fromEntries", 1) }) + t.putStr("hasOwn", func(r *Runtime) Value { return r.methodProp(r.object_hasOwn, "hasOwn", 2) }) + + return t +} + +var _objectTemplate *objectTemplate +var objectTemplateOnce sync.Once + +func getObjectTemplate() *objectTemplate { + objectTemplateOnce.Do(func() { + _objectTemplate = createObjectTemplate() + }) + return _objectTemplate +} + +func (r *Runtime) getObject() *Object { + ret := r.global.Object + if ret == nil { + ret = &Object{runtime: r} + r.global.Object = ret + r.newTemplatedFuncObject(getObjectTemplate(), ret, func(call FunctionCall) Value { + return r.builtin_Object(call.Arguments, nil) + }, r.builtin_Object) + } + return ret +} + +/* +func (r *Runtime) getObjectPrototype() *Object { + ret := r.global.ObjectPrototype + if ret == nil { + ret = &Object{runtime: r} + r.global.ObjectPrototype = ret + r.newTemplatedObject(getObjectProtoTemplate(), ret) + } + return ret +} +*/ + +var objectProtoTemplate *objectTemplate +var objectProtoTemplateOnce sync.Once + +func getObjectProtoTemplate() *objectTemplate { + objectProtoTemplateOnce.Do(func() { + objectProtoTemplate = createObjectProtoTemplate() + }) + return objectProtoTemplate +} + +func createObjectProtoTemplate() *objectTemplate { + t := newObjectTemplate() + + // null prototype + + t.putStr("constructor", func(r *Runtime) Value { return valueProp(r.getObject(), true, false, true) }) + + t.putStr("toString", func(r *Runtime) Value { return r.methodProp(r.objectproto_toString, "toString", 0) }) + t.putStr("toLocaleString", func(r *Runtime) Value { return r.methodProp(r.objectproto_toLocaleString, "toLocaleString", 0) }) + t.putStr("valueOf", func(r *Runtime) Value { return r.methodProp(r.objectproto_valueOf, "valueOf", 0) }) + t.putStr("hasOwnProperty", func(r *Runtime) Value { return r.methodProp(r.objectproto_hasOwnProperty, "hasOwnProperty", 1) }) + t.putStr("isPrototypeOf", func(r *Runtime) Value { return r.methodProp(r.objectproto_isPrototypeOf, "isPrototypeOf", 1) }) + t.putStr("propertyIsEnumerable", func(r *Runtime) Value { + return r.methodProp(r.objectproto_propertyIsEnumerable, "propertyIsEnumerable", 1) + }) + t.putStr(__proto__, func(r *Runtime) Value { + return &valueProperty{ + accessor: true, + getterFunc: r.newNativeFunc(r.objectproto_getProto, "get __proto__", 0), + setterFunc: r.newNativeFunc(r.objectproto_setProto, "set __proto__", 1), + configurable: true, + } + }) + + return t } diff --git a/builtin_promise.go b/builtin_promise.go index 96dd2f5f..d51f27d1 100644 --- a/builtin_promise.go +++ b/builtin_promise.go @@ -108,7 +108,7 @@ func (p *Promise) createResolvingFunctions() (resolve, reject *Object) { } } return p.fulfill(resolution) - }, nil, "", nil, 1), + }, "", 1), p.val.runtime.newNativeFunc(func(call FunctionCall) Value { if alreadyResolved { return _undefined @@ -116,7 +116,7 @@ func (p *Promise) createResolvingFunctions() (resolve, reject *Object) { alreadyResolved = true reason := call.Argument(0) return p.reject(reason) - }, nil, "", nil, 1) + }, "", 1) } func (p *Promise) reject(reason Value) Value { @@ -253,7 +253,7 @@ func (r *Runtime) builtin_newPromise(args []Value, newTarget *Object) *Object { } executor := r.toCallable(arg0) - proto := r.getPrototypeFromCtor(newTarget, r.global.Promise, r.global.PromisePrototype) + proto := r.getPrototypeFromCtor(newTarget, r.global.Promise, r.getPromisePrototype()) po := r.newPromise(proto) resolve, reject := po.createResolvingFunctions() @@ -271,7 +271,7 @@ func (r *Runtime) builtin_newPromise(args []Value, newTarget *Object) *Object { func (r *Runtime) promiseProto_then(call FunctionCall) Value { thisObj := r.toObject(call.This) if p, ok := thisObj.self.(*Promise); ok { - c := r.speciesConstructorObj(thisObj, r.global.Promise) + c := r.speciesConstructorObj(thisObj, r.getPromise()) resultCapability := r.newPromiseCapability(c) return r.performPromiseThen(p, call.Argument(0), call.Argument(1), resultCapability) } @@ -280,8 +280,8 @@ func (r *Runtime) promiseProto_then(call FunctionCall) Value { func (r *Runtime) newPromiseCapability(c *Object) *promiseCapability { pcap := new(promiseCapability) - if c == r.global.Promise { - p := r.newPromise(r.global.PromisePrototype) + if c == r.getPromise() { + p := r.newPromise(r.getPromisePrototype()) pcap.resolveObj, pcap.rejectObj = p.createResolvingFunctions() pcap.promise = p.val } else { @@ -300,7 +300,7 @@ func (r *Runtime) newPromiseCapability(c *Object) *promiseCapability { reject = arg } return nil - }, nil, "", nil, 2) + }, "", 2) pcap.promise = r.toConstructor(c)([]Value{executor}, c) pcap.resolveObj = r.toObject(resolve) r.toCallable(pcap.resolveObj) // make sure it's callable @@ -353,7 +353,7 @@ func (r *Runtime) promiseResolve(c *Object, x Value) *Object { func (r *Runtime) promiseProto_finally(call FunctionCall) Value { promise := r.toObject(call.This) - c := r.speciesConstructorObj(promise, r.global.Promise) + c := r.speciesConstructorObj(promise, r.getPromise()) onFinally := call.Argument(0) var thenFinally, catchFinally Value if onFinallyFn, ok := assertCallable(onFinally); !ok { @@ -365,9 +365,9 @@ func (r *Runtime) promiseProto_finally(call FunctionCall) Value { promise := r.promiseResolve(c, result) valueThunk := r.newNativeFunc(func(call FunctionCall) Value { return value - }, nil, "", nil, 0) + }, "", 0) return r.invoke(promise, "then", valueThunk) - }, nil, "", nil, 1) + }, "", 1) catchFinally = r.newNativeFunc(func(call FunctionCall) Value { reason := call.Argument(0) @@ -375,9 +375,9 @@ func (r *Runtime) promiseProto_finally(call FunctionCall) Value { promise := r.promiseResolve(c, result) thrower := r.newNativeFunc(func(call FunctionCall) Value { panic(reason) - }, nil, "", nil, 0) + }, "", 0) return r.invoke(promise, "then", thrower) - }, nil, "", nil, 1) + }, "", 1) } return r.invoke(promise, "then", thenFinally, catchFinally) } @@ -424,7 +424,7 @@ func (r *Runtime) promise_all(call FunctionCall) Value { pcap.resolve(r.newArrayValues(values)) } return _undefined - }, nil, "", nil, 1) + }, "", 1) remainingElementsCount++ r.invoke(nextPromise, "then", onFulfilled, pcap.rejectObj) }) @@ -465,7 +465,7 @@ func (r *Runtime) promise_allSettled(call FunctionCall) Value { pcap.resolve(r.newArrayValues(values)) } return _undefined - }, nil, "", nil, 1) + }, "", 1) } onFulfilled := reaction(asciiString("fulfilled"), "value") onRejected := reaction(asciiString("rejected"), "reason") @@ -502,19 +502,19 @@ func (r *Runtime) promise_any(call FunctionCall) Value { errors[index] = call.Argument(0) remainingElementsCount-- if remainingElementsCount == 0 { - _error := r.builtin_new(r.global.AggregateError, nil) + _error := r.builtin_new(r.getAggregateError(), nil) _error.self._putProp("errors", r.newArrayValues(errors), true, false, true) pcap.reject(_error) } return _undefined - }, nil, "", nil, 1) + }, "", 1) remainingElementsCount++ r.invoke(nextPromise, "then", pcap.resolveObj, onRejected) }) remainingElementsCount-- if remainingElementsCount == 0 { - _error := r.builtin_new(r.global.AggregateError, nil) + _error := r.builtin_new(r.getAggregateError(), nil) _error.self._putProp("errors", r.newArrayValues(errors), true, false, true) pcap.reject(_error) } @@ -549,11 +549,11 @@ func (r *Runtime) promise_resolve(call FunctionCall) Value { func (r *Runtime) createPromiseProto(val *Object) objectImpl { o := newBaseObjectObj(val, r.global.ObjectPrototype, classObject) - o._putProp("constructor", r.global.Promise, true, false, true) + o._putProp("constructor", r.getPromise(), true, false, true) - o._putProp("catch", r.newNativeFunc(r.promiseProto_catch, nil, "catch", nil, 1), true, false, true) - o._putProp("finally", r.newNativeFunc(r.promiseProto_finally, nil, "finally", nil, 1), true, false, true) - o._putProp("then", r.newNativeFunc(r.promiseProto_then, nil, "then", nil, 2), true, false, true) + o._putProp("catch", r.newNativeFunc(r.promiseProto_catch, "catch", 1), true, false, true) + o._putProp("finally", r.newNativeFunc(r.promiseProto_finally, "finally", 1), true, false, true) + o._putProp("then", r.newNativeFunc(r.promiseProto_then, "then", 2), true, false, true) o._putSym(SymToStringTag, valueProp(asciiString(classPromise), false, false, true)) @@ -561,25 +561,38 @@ func (r *Runtime) createPromiseProto(val *Object) objectImpl { } func (r *Runtime) createPromise(val *Object) objectImpl { - o := r.newNativeConstructOnly(val, r.builtin_newPromise, r.global.PromisePrototype, "Promise", 1) + o := r.newNativeConstructOnly(val, r.builtin_newPromise, r.getPromisePrototype(), "Promise", 1) - o._putProp("all", r.newNativeFunc(r.promise_all, nil, "all", nil, 1), true, false, true) - o._putProp("allSettled", r.newNativeFunc(r.promise_allSettled, nil, "allSettled", nil, 1), true, false, true) - o._putProp("any", r.newNativeFunc(r.promise_any, nil, "any", nil, 1), true, false, true) - o._putProp("race", r.newNativeFunc(r.promise_race, nil, "race", nil, 1), true, false, true) - o._putProp("reject", r.newNativeFunc(r.promise_reject, nil, "reject", nil, 1), true, false, true) - o._putProp("resolve", r.newNativeFunc(r.promise_resolve, nil, "resolve", nil, 1), true, false, true) + o._putProp("all", r.newNativeFunc(r.promise_all, "all", 1), true, false, true) + o._putProp("allSettled", r.newNativeFunc(r.promise_allSettled, "allSettled", 1), true, false, true) + o._putProp("any", r.newNativeFunc(r.promise_any, "any", 1), true, false, true) + o._putProp("race", r.newNativeFunc(r.promise_race, "race", 1), true, false, true) + o._putProp("reject", r.newNativeFunc(r.promise_reject, "reject", 1), true, false, true) + o._putProp("resolve", r.newNativeFunc(r.promise_resolve, "resolve", 1), true, false, true) r.putSpeciesReturnThis(o) return o } -func (r *Runtime) initPromise() { - r.global.PromisePrototype = r.newLazyObject(r.createPromiseProto) - r.global.Promise = r.newLazyObject(r.createPromise) +func (r *Runtime) getPromisePrototype() *Object { + ret := r.global.PromisePrototype + if ret == nil { + ret = &Object{runtime: r} + r.global.PromisePrototype = ret + ret.self = r.createPromiseProto(ret) + } + return ret +} - r.addToGlobal("Promise", r.global.Promise) +func (r *Runtime) getPromise() *Object { + ret := r.global.Promise + if ret == nil { + ret = &Object{runtime: r} + r.global.Promise = ret + ret.self = r.createPromise(ret) + } + return ret } func (r *Runtime) wrapPromiseReaction(fObj *Object) func(interface{}) { @@ -609,7 +622,7 @@ func (r *Runtime) wrapPromiseReaction(fObj *Object) func(interface{}) { // }() // } func (r *Runtime) NewPromise() (promise *Promise, resolve func(result interface{}), reject func(reason interface{})) { - p := r.newPromise(r.global.PromisePrototype) + p := r.newPromise(r.getPromisePrototype()) resolveF, rejectF := p.createResolvingFunctions() return p, r.wrapPromiseReaction(resolveF), r.wrapPromiseReaction(rejectF) } diff --git a/builtin_proxy.go b/builtin_proxy.go index ee62f75e..f5899306 100644 --- a/builtin_proxy.go +++ b/builtin_proxy.go @@ -345,7 +345,7 @@ func (r *Runtime) builtin_newProxy(args []Value, newTarget *Object) *Object { if newTarget == nil { panic(r.needNew("Proxy")) } - return r.newProxy(args, r.getPrototypeFromCtor(newTarget, r.global.Proxy, r.global.ObjectPrototype)) + return r.newProxy(args, r.getPrototypeFromCtor(newTarget, r.getProxy(), r.global.ObjectPrototype)) } func (r *Runtime) NewProxy(target *Object, nativeHandler *ProxyTrapConfig) Proxy { @@ -367,7 +367,7 @@ func (r *Runtime) builtin_proxy_revocable(call FunctionCall) Value { revoke := r.newNativeFunc(func(FunctionCall) Value { proxy.revoke() return _undefined - }, nil, "", nil, 0) + }, "", 0) ret := r.NewObject() ret.self._putProp("proxy", proxy.val, true, true, true) ret.self._putProp("revoke", revoke, true, true, true) @@ -381,11 +381,16 @@ func (r *Runtime) builtin_proxy_revocable(call FunctionCall) Value { func (r *Runtime) createProxy(val *Object) objectImpl { o := r.newNativeConstructOnly(val, r.builtin_newProxy, nil, "Proxy", 2) - o._putProp("revocable", r.newNativeFunc(r.builtin_proxy_revocable, nil, "revocable", nil, 2), true, false, true) + o._putProp("revocable", r.newNativeFunc(r.builtin_proxy_revocable, "revocable", 2), true, false, true) return o } -func (r *Runtime) initProxy() { - r.global.Proxy = r.newLazyObject(r.createProxy) - r.addToGlobal("Proxy", r.global.Proxy) +func (r *Runtime) getProxy() *Object { + ret := r.global.Proxy + if ret == nil { + ret = &Object{runtime: r} + r.global.Proxy = ret + r.createProxy(ret) + } + return ret } diff --git a/builtin_reflect.go b/builtin_reflect.go index 68a2bb05..17bb11a3 100644 --- a/builtin_reflect.go +++ b/builtin_reflect.go @@ -110,25 +110,31 @@ func (r *Runtime) builtin_reflect_setPrototypeOf(call FunctionCall) Value { func (r *Runtime) createReflect(val *Object) objectImpl { o := newBaseObjectObj(val, r.global.ObjectPrototype, classObject) - o._putProp("apply", r.newNativeFunc(r.builtin_reflect_apply, nil, "apply", nil, 3), true, false, true) - o._putProp("construct", r.newNativeFunc(r.builtin_reflect_construct, nil, "construct", nil, 2), true, false, true) - o._putProp("defineProperty", r.newNativeFunc(r.builtin_reflect_defineProperty, nil, "defineProperty", nil, 3), true, false, true) - o._putProp("deleteProperty", r.newNativeFunc(r.builtin_reflect_deleteProperty, nil, "deleteProperty", nil, 2), true, false, true) - o._putProp("get", r.newNativeFunc(r.builtin_reflect_get, nil, "get", nil, 2), true, false, true) - o._putProp("getOwnPropertyDescriptor", r.newNativeFunc(r.builtin_reflect_getOwnPropertyDescriptor, nil, "getOwnPropertyDescriptor", nil, 2), true, false, true) - o._putProp("getPrototypeOf", r.newNativeFunc(r.builtin_reflect_getPrototypeOf, nil, "getPrototypeOf", nil, 1), true, false, true) - o._putProp("has", r.newNativeFunc(r.builtin_reflect_has, nil, "has", nil, 2), true, false, true) - o._putProp("isExtensible", r.newNativeFunc(r.builtin_reflect_isExtensible, nil, "isExtensible", nil, 1), true, false, true) - o._putProp("ownKeys", r.newNativeFunc(r.builtin_reflect_ownKeys, nil, "ownKeys", nil, 1), true, false, true) - o._putProp("preventExtensions", r.newNativeFunc(r.builtin_reflect_preventExtensions, nil, "preventExtensions", nil, 1), true, false, true) - o._putProp("set", r.newNativeFunc(r.builtin_reflect_set, nil, "set", nil, 3), true, false, true) - o._putProp("setPrototypeOf", r.newNativeFunc(r.builtin_reflect_setPrototypeOf, nil, "setPrototypeOf", nil, 2), true, false, true) + o._putProp("apply", r.newNativeFunc(r.builtin_reflect_apply, "apply", 3), true, false, true) + o._putProp("construct", r.newNativeFunc(r.builtin_reflect_construct, "construct", 2), true, false, true) + o._putProp("defineProperty", r.newNativeFunc(r.builtin_reflect_defineProperty, "defineProperty", 3), true, false, true) + o._putProp("deleteProperty", r.newNativeFunc(r.builtin_reflect_deleteProperty, "deleteProperty", 2), true, false, true) + o._putProp("get", r.newNativeFunc(r.builtin_reflect_get, "get", 2), true, false, true) + o._putProp("getOwnPropertyDescriptor", r.newNativeFunc(r.builtin_reflect_getOwnPropertyDescriptor, "getOwnPropertyDescriptor", 2), true, false, true) + o._putProp("getPrototypeOf", r.newNativeFunc(r.builtin_reflect_getPrototypeOf, "getPrototypeOf", 1), true, false, true) + o._putProp("has", r.newNativeFunc(r.builtin_reflect_has, "has", 2), true, false, true) + o._putProp("isExtensible", r.newNativeFunc(r.builtin_reflect_isExtensible, "isExtensible", 1), true, false, true) + o._putProp("ownKeys", r.newNativeFunc(r.builtin_reflect_ownKeys, "ownKeys", 1), true, false, true) + o._putProp("preventExtensions", r.newNativeFunc(r.builtin_reflect_preventExtensions, "preventExtensions", 1), true, false, true) + o._putProp("set", r.newNativeFunc(r.builtin_reflect_set, "set", 3), true, false, true) + o._putProp("setPrototypeOf", r.newNativeFunc(r.builtin_reflect_setPrototypeOf, "setPrototypeOf", 2), true, false, true) o._putSym(SymToStringTag, valueProp(asciiString("Reflect"), false, false, true)) return o } -func (r *Runtime) initReflect() { - r.addToGlobal("Reflect", r.newLazyObject(r.createReflect)) +func (r *Runtime) getReflect() *Object { + ret := r.global.Reflect + if ret == nil { + ret = &Object{runtime: r} + r.global.Reflect = ret + ret.self = r.createReflect(ret) + } + return ret } diff --git a/builtin_regexp.go b/builtin_regexp.go index 7ac8ddc6..89402820 100644 --- a/builtin_regexp.go +++ b/builtin_regexp.go @@ -347,7 +347,7 @@ func (r *Runtime) builtin_RegExp(call FunctionCall) Value { } } } - return r.newRegExp(pattern, flags, r.global.RegExpPrototype).val + return r.newRegExp(pattern, flags, r.getRegExpPrototype()).val } func (r *Runtime) regexpproto_compile(call FunctionCall) Value { @@ -771,7 +771,7 @@ func (r *Runtime) regexpproto_stdMatcherAll(call FunctionCall) Value { thisObj := r.toObject(call.This) s := call.Argument(0).toString() flags := nilSafe(thisObj.self.getStr("flags", nil)).toString() - c := r.speciesConstructorObj(call.This.(*Object), r.global.RegExp) + c := r.speciesConstructorObj(call.This.(*Object), r.getRegExp()) matcher := r.toConstructor(c)([]Value{call.This, flags}, nil) matcher.self.setOwnStr("lastIndex", valueInt(toLength(thisObj.self.getStr("lastIndex", nil))), true) flagsStr := flags.String() @@ -963,7 +963,7 @@ func (r *Runtime) regexpproto_stdSplitter(call FunctionCall) Value { limitValue := call.Argument(1) var splitter *Object search := r.checkStdRegexp(rxObj) - c := r.speciesConstructorObj(rxObj, r.global.RegExp) + c := r.speciesConstructorObj(rxObj, r.getRegExp()) if search == nil || c != r.global.RegExp { flags := nilSafe(rxObj.self.getStr("flags", nil)).toString() flagsStr := flags.String() @@ -1214,7 +1214,7 @@ func (r *Runtime) regExpStringIteratorProto_next(call FunctionCall) Value { func (r *Runtime) createRegExpStringIteratorPrototype(val *Object) objectImpl { o := newBaseObjectObj(val, r.getIteratorPrototype(), classObject) - o._putProp("next", r.newNativeFunc(r.regExpStringIteratorProto_next, nil, "next", nil, 0), true, false, true) + o._putProp("next", r.newNativeFunc(r.regExpStringIteratorProto_next, "next", 0), true, false, true) o._putSym(SymToStringTag, valueProp(asciiString(classRegExpStringIterator), false, false, true)) return o @@ -1230,60 +1230,75 @@ func (r *Runtime) getRegExpStringIteratorPrototype() *Object { return o } -func (r *Runtime) initRegExp() { - o := r.newGuardedObject(r.global.ObjectPrototype, classObject) - r.global.RegExpPrototype = o.val - r.global.stdRegexpProto = o - - o._putProp("compile", r.newNativeFunc(r.regexpproto_compile, nil, "compile", nil, 2), true, false, true) - o._putProp("exec", r.newNativeFunc(r.regexpproto_exec, nil, "exec", nil, 1), true, false, true) - o._putProp("test", r.newNativeFunc(r.regexpproto_test, nil, "test", nil, 1), true, false, true) - o._putProp("toString", r.newNativeFunc(r.regexpproto_toString, nil, "toString", nil, 0), true, false, true) - o.setOwnStr("source", &valueProperty{ - configurable: true, - getterFunc: r.newNativeFunc(r.regexpproto_getSource, nil, "get source", nil, 0), - accessor: true, - }, false) - o.setOwnStr("global", &valueProperty{ - configurable: true, - getterFunc: r.newNativeFunc(r.regexpproto_getGlobal, nil, "get global", nil, 0), - accessor: true, - }, false) - o.setOwnStr("multiline", &valueProperty{ - configurable: true, - getterFunc: r.newNativeFunc(r.regexpproto_getMultiline, nil, "get multiline", nil, 0), - accessor: true, - }, false) - o.setOwnStr("ignoreCase", &valueProperty{ - configurable: true, - getterFunc: r.newNativeFunc(r.regexpproto_getIgnoreCase, nil, "get ignoreCase", nil, 0), - accessor: true, - }, false) - o.setOwnStr("unicode", &valueProperty{ - configurable: true, - getterFunc: r.newNativeFunc(r.regexpproto_getUnicode, nil, "get unicode", nil, 0), - accessor: true, - }, false) - o.setOwnStr("sticky", &valueProperty{ - configurable: true, - getterFunc: r.newNativeFunc(r.regexpproto_getSticky, nil, "get sticky", nil, 0), - accessor: true, - }, false) - o.setOwnStr("flags", &valueProperty{ - configurable: true, - getterFunc: r.newNativeFunc(r.regexpproto_getFlags, nil, "get flags", nil, 0), - accessor: true, - }, false) - - o._putSym(SymMatch, valueProp(r.newNativeFunc(r.regexpproto_stdMatcher, nil, "[Symbol.match]", nil, 1), true, false, true)) - o._putSym(SymMatchAll, valueProp(r.newNativeFunc(r.regexpproto_stdMatcherAll, nil, "[Symbol.matchAll]", nil, 1), true, false, true)) - o._putSym(SymSearch, valueProp(r.newNativeFunc(r.regexpproto_stdSearch, nil, "[Symbol.search]", nil, 1), true, false, true)) - o._putSym(SymSplit, valueProp(r.newNativeFunc(r.regexpproto_stdSplitter, nil, "[Symbol.split]", nil, 2), true, false, true)) - o._putSym(SymReplace, valueProp(r.newNativeFunc(r.regexpproto_stdReplacer, nil, "[Symbol.replace]", nil, 2), true, false, true)) - o.guard("exec", "global", "multiline", "ignoreCase", "unicode", "sticky") - - r.global.RegExp = r.newNativeFunc(r.builtin_RegExp, r.builtin_newRegExp, "RegExp", r.global.RegExpPrototype, 2) - rx := r.global.RegExp.self - r.putSpeciesReturnThis(rx) - r.addToGlobal("RegExp", r.global.RegExp) +func (r *Runtime) getRegExp() *Object { + ret := r.global.RegExp + if ret == nil { + ret = &Object{runtime: r} + r.global.RegExp = ret + proto := r.getRegExpPrototype() + r.newNativeFuncAndConstruct(ret, r.builtin_RegExp, + r.wrapNativeConstruct(r.builtin_newRegExp, ret, proto), proto, "RegExp", intToValue(2)) + rx := ret.self + r.putSpeciesReturnThis(rx) + } + return ret +} + +func (r *Runtime) getRegExpPrototype() *Object { + ret := r.global.RegExpPrototype + if ret == nil { + o := r.newGuardedObject(r.global.ObjectPrototype, classObject) + ret = o.val + r.global.RegExpPrototype = ret + r.global.stdRegexpProto = o + + o._putProp("constructor", r.getRegExp(), true, false, true) + o._putProp("compile", r.newNativeFunc(r.regexpproto_compile, "compile", 2), true, false, true) + o._putProp("exec", r.newNativeFunc(r.regexpproto_exec, "exec", 1), true, false, true) + o._putProp("test", r.newNativeFunc(r.regexpproto_test, "test", 1), true, false, true) + o._putProp("toString", r.newNativeFunc(r.regexpproto_toString, "toString", 0), true, false, true) + o.setOwnStr("source", &valueProperty{ + configurable: true, + getterFunc: r.newNativeFunc(r.regexpproto_getSource, "get source", 0), + accessor: true, + }, false) + o.setOwnStr("global", &valueProperty{ + configurable: true, + getterFunc: r.newNativeFunc(r.regexpproto_getGlobal, "get global", 0), + accessor: true, + }, false) + o.setOwnStr("multiline", &valueProperty{ + configurable: true, + getterFunc: r.newNativeFunc(r.regexpproto_getMultiline, "get multiline", 0), + accessor: true, + }, false) + o.setOwnStr("ignoreCase", &valueProperty{ + configurable: true, + getterFunc: r.newNativeFunc(r.regexpproto_getIgnoreCase, "get ignoreCase", 0), + accessor: true, + }, false) + o.setOwnStr("unicode", &valueProperty{ + configurable: true, + getterFunc: r.newNativeFunc(r.regexpproto_getUnicode, "get unicode", 0), + accessor: true, + }, false) + o.setOwnStr("sticky", &valueProperty{ + configurable: true, + getterFunc: r.newNativeFunc(r.regexpproto_getSticky, "get sticky", 0), + accessor: true, + }, false) + o.setOwnStr("flags", &valueProperty{ + configurable: true, + getterFunc: r.newNativeFunc(r.regexpproto_getFlags, "get flags", 0), + accessor: true, + }, false) + + o._putSym(SymMatch, valueProp(r.newNativeFunc(r.regexpproto_stdMatcher, "[Symbol.match]", 1), true, false, true)) + o._putSym(SymMatchAll, valueProp(r.newNativeFunc(r.regexpproto_stdMatcherAll, "[Symbol.matchAll]", 1), true, false, true)) + o._putSym(SymSearch, valueProp(r.newNativeFunc(r.regexpproto_stdSearch, "[Symbol.search]", 1), true, false, true)) + o._putSym(SymSplit, valueProp(r.newNativeFunc(r.regexpproto_stdSplitter, "[Symbol.split]", 2), true, false, true)) + o._putSym(SymReplace, valueProp(r.newNativeFunc(r.regexpproto_stdReplacer, "[Symbol.replace]", 2), true, false, true)) + o.guard("exec", "global", "multiline", "ignoreCase", "unicode", "sticky") + } + return ret } diff --git a/builtin_set.go b/builtin_set.go index 54bbf3c1..eeedb887 100644 --- a/builtin_set.go +++ b/builtin_set.go @@ -274,25 +274,25 @@ func (r *Runtime) setIterProto_next(call FunctionCall) Value { func (r *Runtime) createSetProto(val *Object) objectImpl { o := newBaseObjectObj(val, r.global.ObjectPrototype, classObject) - o._putProp("constructor", r.global.Set, true, false, true) - r.global.setAdder = r.newNativeFunc(r.setProto_add, nil, "add", nil, 1) + o._putProp("constructor", r.getSet(), true, false, true) + r.global.setAdder = r.newNativeFunc(r.setProto_add, "add", 1) o._putProp("add", r.global.setAdder, true, false, true) - o._putProp("clear", r.newNativeFunc(r.setProto_clear, nil, "clear", nil, 0), true, false, true) - o._putProp("delete", r.newNativeFunc(r.setProto_delete, nil, "delete", nil, 1), true, false, true) - o._putProp("forEach", r.newNativeFunc(r.setProto_forEach, nil, "forEach", nil, 1), true, false, true) - o._putProp("has", r.newNativeFunc(r.setProto_has, nil, "has", nil, 1), true, false, true) + o._putProp("clear", r.newNativeFunc(r.setProto_clear, "clear", 0), true, false, true) + o._putProp("delete", r.newNativeFunc(r.setProto_delete, "delete", 1), true, false, true) + o._putProp("forEach", r.newNativeFunc(r.setProto_forEach, "forEach", 1), true, false, true) + o._putProp("has", r.newNativeFunc(r.setProto_has, "has", 1), true, false, true) o.setOwnStr("size", &valueProperty{ - getterFunc: r.newNativeFunc(r.setProto_getSize, nil, "get size", nil, 0), + getterFunc: r.newNativeFunc(r.setProto_getSize, "get size", 0), accessor: true, writable: true, configurable: true, }, true) - valuesFunc := r.newNativeFunc(r.setProto_values, nil, "values", nil, 0) + valuesFunc := r.newNativeFunc(r.setProto_values, "values", 0) o._putProp("values", valuesFunc, true, false, true) o._putProp("keys", valuesFunc, true, false, true) - o._putProp("entries", r.newNativeFunc(r.setProto_entries, nil, "entries", nil, 0), true, false, true) + o._putProp("entries", r.newNativeFunc(r.setProto_entries, "entries", 0), true, false, true) o._putSym(SymIterator, valueProp(valuesFunc, true, false, true)) o._putSym(SymToStringTag, valueProp(asciiString(classSet), false, false, true)) @@ -300,7 +300,7 @@ func (r *Runtime) createSetProto(val *Object) objectImpl { } func (r *Runtime) createSet(val *Object) objectImpl { - o := r.newNativeConstructOnly(val, r.builtin_newSet, r.global.SetPrototype, "Set", 0) + o := r.newNativeConstructOnly(val, r.builtin_newSet, r.getSetPrototype(), "Set", 0) r.putSpeciesReturnThis(o) return o @@ -309,7 +309,7 @@ func (r *Runtime) createSet(val *Object) objectImpl { func (r *Runtime) createSetIterProto(val *Object) objectImpl { o := newBaseObjectObj(val, r.getIteratorPrototype(), classObject) - o._putProp("next", r.newNativeFunc(r.setIterProto_next, nil, "next", nil, 0), true, false, true) + o._putProp("next", r.newNativeFunc(r.setIterProto_next, "next", 0), true, false, true) o._putSym(SymToStringTag, valueProp(asciiString(classSetIterator), false, false, true)) return o @@ -325,9 +325,22 @@ func (r *Runtime) getSetIteratorPrototype() *Object { return o } -func (r *Runtime) initSet() { - r.global.SetPrototype = r.newLazyObject(r.createSetProto) - r.global.Set = r.newLazyObject(r.createSet) +func (r *Runtime) getSetPrototype() *Object { + ret := r.global.SetPrototype + if ret == nil { + ret = &Object{runtime: r} + r.global.SetPrototype = ret + ret.self = r.createSetProto(ret) + } + return ret +} - r.addToGlobal("Set", r.global.Set) +func (r *Runtime) getSet() *Object { + ret := r.global.Set + if ret == nil { + ret = &Object{runtime: r} + r.global.Set = ret + ret.self = r.createSet(ret) + } + return ret } diff --git a/builtin_string.go b/builtin_string.go index 5e35dc86..b2a0e4ea 100644 --- a/builtin_string.go +++ b/builtin_string.go @@ -4,6 +4,7 @@ import ( "github.com/dop251/goja/unistring" "math" "strings" + "sync" "unicode/utf16" "unicode/utf8" @@ -82,6 +83,9 @@ func (r *Runtime) stringproto_toStringValueOf(this Value, funcName string) Value return valueOf() } } + if obj == r.global.StringPrototype { + return stringEmpty + } } r.typeErrorResult(true, "String.prototype.%s is called on incompatible receiver", funcName) return nil @@ -131,11 +135,11 @@ func (r *Runtime) string_fromcodepoint(call FunctionCall) Value { var c rune if numInt, ok := num.(valueInt); ok { if numInt < 0 || numInt > utf8.MaxRune { - panic(r.newError(r.global.RangeError, "Invalid code point %d", numInt)) + panic(r.newError(r.getRangeError(), "Invalid code point %d", numInt)) } c = rune(numInt) } else { - panic(r.newError(r.global.RangeError, "Invalid code point %s", num)) + panic(r.newError(r.getRangeError(), "Invalid code point %s", num)) } sb.WriteRune(c) } @@ -391,7 +395,7 @@ func (r *Runtime) stringproto_match(call FunctionCall) Value { } if rx == nil { - rx = r.newRegExp(regexp, nil, r.global.RegExpPrototype) + rx = r.newRegExp(regexp, nil, r.getRegExpPrototype()) } if matcher, ok := r.toObject(rx.getSym(SymMatch, nil)).self.assertCallable(); ok { @@ -425,7 +429,7 @@ func (r *Runtime) stringproto_matchAll(call FunctionCall) Value { } } - rx := r.newRegExp(regexp, asciiString("g"), r.global.RegExpPrototype) + rx := r.newRegExp(regexp, asciiString("g"), r.getRegExpPrototype()) if matcher, ok := r.toObject(rx.getSym(SymMatchAll, nil)).self.assertCallable(); ok { return matcher(FunctionCall{ @@ -457,7 +461,7 @@ func (r *Runtime) stringproto_normalize(call FunctionCall) Value { case "NFKD": f = norm.NFKD default: - panic(r.newError(r.global.RangeError, "The normalization form should be one of NFC, NFD, NFKC, NFKD")) + panic(r.newError(r.getRangeError(), "The normalization form should be one of NFC, NFD, NFKC, NFKD")) } switch s := s.(type) { @@ -551,11 +555,11 @@ func (r *Runtime) stringproto_repeat(call FunctionCall) Value { s := call.This.toString() n := call.Argument(0).ToNumber() if n == _positiveInf { - panic(r.newError(r.global.RangeError, "Invalid count value")) + panic(r.newError(r.getRangeError(), "Invalid count value")) } numInt := n.ToInteger() if numInt < 0 { - panic(r.newError(r.global.RangeError, "Invalid count value")) + panic(r.newError(r.getRangeError(), "Invalid count value")) } if numInt == 0 || s.Length() == 0 { return stringEmpty @@ -736,7 +740,7 @@ func (r *Runtime) stringproto_search(call FunctionCall) Value { } if rx == nil { - rx = r.newRegExp(regexp, nil, r.global.RegExpPrototype) + rx = r.newRegExp(regexp, nil, r.getRegExpPrototype()) } if searcher, ok := r.toObject(rx.getSym(SymSearch, nil)).self.assertCallable(); ok { @@ -977,7 +981,7 @@ func (r *Runtime) stringIterProto_next(call FunctionCall) Value { func (r *Runtime) createStringIterProto(val *Object) objectImpl { o := newBaseObjectObj(val, r.getIteratorPrototype(), classObject) - o._putProp("next", r.newNativeFunc(r.stringIterProto_next, nil, "next", nil, 0), true, false, true) + o._putProp("next", r.newNativeFunc(r.stringIterProto_next, "next", 0), true, false, true) o._putSym(SymToStringTag, valueProp(asciiString(classStringIterator), false, false, true)) return o @@ -993,59 +997,120 @@ func (r *Runtime) getStringIteratorPrototype() *Object { return o } -func (r *Runtime) initString() { - r.global.StringPrototype = r.builtin_newString([]Value{stringEmpty}, r.global.ObjectPrototype) - - o := r.global.StringPrototype.self - o._putProp("at", r.newNativeFunc(r.stringproto_at, nil, "at", nil, 1), true, false, true) - o._putProp("charAt", r.newNativeFunc(r.stringproto_charAt, nil, "charAt", nil, 1), true, false, true) - o._putProp("charCodeAt", r.newNativeFunc(r.stringproto_charCodeAt, nil, "charCodeAt", nil, 1), true, false, true) - o._putProp("codePointAt", r.newNativeFunc(r.stringproto_codePointAt, nil, "codePointAt", nil, 1), true, false, true) - o._putProp("concat", r.newNativeFunc(r.stringproto_concat, nil, "concat", nil, 1), true, false, true) - o._putProp("endsWith", r.newNativeFunc(r.stringproto_endsWith, nil, "endsWith", nil, 1), true, false, true) - o._putProp("includes", r.newNativeFunc(r.stringproto_includes, nil, "includes", nil, 1), true, false, true) - o._putProp("indexOf", r.newNativeFunc(r.stringproto_indexOf, nil, "indexOf", nil, 1), true, false, true) - o._putProp("lastIndexOf", r.newNativeFunc(r.stringproto_lastIndexOf, nil, "lastIndexOf", nil, 1), true, false, true) - o._putProp("localeCompare", r.newNativeFunc(r.stringproto_localeCompare, nil, "localeCompare", nil, 1), true, false, true) - o._putProp("match", r.newNativeFunc(r.stringproto_match, nil, "match", nil, 1), true, false, true) - o._putProp("matchAll", r.newNativeFunc(r.stringproto_matchAll, nil, "matchAll", nil, 1), true, false, true) - o._putProp("normalize", r.newNativeFunc(r.stringproto_normalize, nil, "normalize", nil, 0), true, false, true) - o._putProp("padEnd", r.newNativeFunc(r.stringproto_padEnd, nil, "padEnd", nil, 1), true, false, true) - o._putProp("padStart", r.newNativeFunc(r.stringproto_padStart, nil, "padStart", nil, 1), true, false, true) - o._putProp("repeat", r.newNativeFunc(r.stringproto_repeat, nil, "repeat", nil, 1), true, false, true) - o._putProp("replace", r.newNativeFunc(r.stringproto_replace, nil, "replace", nil, 2), true, false, true) - o._putProp("replaceAll", r.newNativeFunc(r.stringproto_replaceAll, nil, "replaceAll", nil, 2), true, false, true) - o._putProp("search", r.newNativeFunc(r.stringproto_search, nil, "search", nil, 1), true, false, true) - o._putProp("slice", r.newNativeFunc(r.stringproto_slice, nil, "slice", nil, 2), true, false, true) - o._putProp("split", r.newNativeFunc(r.stringproto_split, nil, "split", nil, 2), true, false, true) - o._putProp("startsWith", r.newNativeFunc(r.stringproto_startsWith, nil, "startsWith", nil, 1), true, false, true) - o._putProp("substring", r.newNativeFunc(r.stringproto_substring, nil, "substring", nil, 2), true, false, true) - o._putProp("toLocaleLowerCase", r.newNativeFunc(r.stringproto_toLowerCase, nil, "toLocaleLowerCase", nil, 0), true, false, true) - o._putProp("toLocaleUpperCase", r.newNativeFunc(r.stringproto_toUpperCase, nil, "toLocaleUpperCase", nil, 0), true, false, true) - o._putProp("toLowerCase", r.newNativeFunc(r.stringproto_toLowerCase, nil, "toLowerCase", nil, 0), true, false, true) - o._putProp("toString", r.newNativeFunc(r.stringproto_toString, nil, "toString", nil, 0), true, false, true) - o._putProp("toUpperCase", r.newNativeFunc(r.stringproto_toUpperCase, nil, "toUpperCase", nil, 0), true, false, true) - o._putProp("trim", r.newNativeFunc(r.stringproto_trim, nil, "trim", nil, 0), true, false, true) - trimEnd := r.newNativeFunc(r.stringproto_trimEnd, nil, "trimEnd", nil, 0) - trimStart := r.newNativeFunc(r.stringproto_trimStart, nil, "trimStart", nil, 0) - o._putProp("trimEnd", trimEnd, true, false, true) - o._putProp("trimStart", trimStart, true, false, true) - o._putProp("trimRight", trimEnd, true, false, true) - o._putProp("trimLeft", trimStart, true, false, true) - o._putProp("valueOf", r.newNativeFunc(r.stringproto_valueOf, nil, "valueOf", nil, 0), true, false, true) - - o._putSym(SymIterator, valueProp(r.newNativeFunc(r.stringproto_iterator, nil, "[Symbol.iterator]", nil, 0), true, false, true)) +func createStringProtoTemplate() *objectTemplate { + t := newObjectTemplate() + t.protoFactory = func(r *Runtime) *Object { + return r.global.ObjectPrototype + } + + t.putStr("length", func(r *Runtime) Value { return valueProp(intToValue(0), false, false, false) }) + + t.putStr("constructor", func(r *Runtime) Value { return valueProp(r.getString(), true, false, true) }) + + t.putStr("at", func(r *Runtime) Value { return r.methodProp(r.stringproto_at, "at", 1) }) + t.putStr("charAt", func(r *Runtime) Value { return r.methodProp(r.stringproto_charAt, "charAt", 1) }) + t.putStr("charCodeAt", func(r *Runtime) Value { return r.methodProp(r.stringproto_charCodeAt, "charCodeAt", 1) }) + t.putStr("codePointAt", func(r *Runtime) Value { return r.methodProp(r.stringproto_codePointAt, "codePointAt", 1) }) + t.putStr("concat", func(r *Runtime) Value { return r.methodProp(r.stringproto_concat, "concat", 1) }) + t.putStr("endsWith", func(r *Runtime) Value { return r.methodProp(r.stringproto_endsWith, "endsWith", 1) }) + t.putStr("includes", func(r *Runtime) Value { return r.methodProp(r.stringproto_includes, "includes", 1) }) + t.putStr("indexOf", func(r *Runtime) Value { return r.methodProp(r.stringproto_indexOf, "indexOf", 1) }) + t.putStr("lastIndexOf", func(r *Runtime) Value { return r.methodProp(r.stringproto_lastIndexOf, "lastIndexOf", 1) }) + t.putStr("localeCompare", func(r *Runtime) Value { return r.methodProp(r.stringproto_localeCompare, "localeCompare", 1) }) + t.putStr("match", func(r *Runtime) Value { return r.methodProp(r.stringproto_match, "match", 1) }) + t.putStr("matchAll", func(r *Runtime) Value { return r.methodProp(r.stringproto_matchAll, "matchAll", 1) }) + t.putStr("normalize", func(r *Runtime) Value { return r.methodProp(r.stringproto_normalize, "normalize", 0) }) + t.putStr("padEnd", func(r *Runtime) Value { return r.methodProp(r.stringproto_padEnd, "padEnd", 1) }) + t.putStr("padStart", func(r *Runtime) Value { return r.methodProp(r.stringproto_padStart, "padStart", 1) }) + t.putStr("repeat", func(r *Runtime) Value { return r.methodProp(r.stringproto_repeat, "repeat", 1) }) + t.putStr("replace", func(r *Runtime) Value { return r.methodProp(r.stringproto_replace, "replace", 2) }) + t.putStr("replaceAll", func(r *Runtime) Value { return r.methodProp(r.stringproto_replaceAll, "replaceAll", 2) }) + t.putStr("search", func(r *Runtime) Value { return r.methodProp(r.stringproto_search, "search", 1) }) + t.putStr("slice", func(r *Runtime) Value { return r.methodProp(r.stringproto_slice, "slice", 2) }) + t.putStr("split", func(r *Runtime) Value { return r.methodProp(r.stringproto_split, "split", 2) }) + t.putStr("startsWith", func(r *Runtime) Value { return r.methodProp(r.stringproto_startsWith, "startsWith", 1) }) + t.putStr("substring", func(r *Runtime) Value { return r.methodProp(r.stringproto_substring, "substring", 2) }) + t.putStr("toLocaleLowerCase", func(r *Runtime) Value { return r.methodProp(r.stringproto_toLowerCase, "toLocaleLowerCase", 0) }) + t.putStr("toLocaleUpperCase", func(r *Runtime) Value { return r.methodProp(r.stringproto_toUpperCase, "toLocaleUpperCase", 0) }) + t.putStr("toLowerCase", func(r *Runtime) Value { return r.methodProp(r.stringproto_toLowerCase, "toLowerCase", 0) }) + t.putStr("toString", func(r *Runtime) Value { return r.methodProp(r.stringproto_toString, "toString", 0) }) + t.putStr("toUpperCase", func(r *Runtime) Value { return r.methodProp(r.stringproto_toUpperCase, "toUpperCase", 0) }) + t.putStr("trim", func(r *Runtime) Value { return r.methodProp(r.stringproto_trim, "trim", 0) }) + t.putStr("trimEnd", func(r *Runtime) Value { return valueProp(r.getStringproto_trimEnd(), true, false, true) }) + t.putStr("trimStart", func(r *Runtime) Value { return valueProp(r.getStringproto_trimStart(), true, false, true) }) + t.putStr("trimRight", func(r *Runtime) Value { return valueProp(r.getStringproto_trimEnd(), true, false, true) }) + t.putStr("trimLeft", func(r *Runtime) Value { return valueProp(r.getStringproto_trimStart(), true, false, true) }) + t.putStr("valueOf", func(r *Runtime) Value { return r.methodProp(r.stringproto_valueOf, "valueOf", 0) }) // Annex B - o._putProp("substr", r.newNativeFunc(r.stringproto_substr, nil, "substr", nil, 2), true, false, true) + t.putStr("substr", func(r *Runtime) Value { return r.methodProp(r.stringproto_substr, "substr", 2) }) + + t.putSym(SymIterator, func(r *Runtime) Value { + return valueProp(r.newNativeFunc(r.stringproto_iterator, "[Symbol.iterator]", 0), true, false, true) + }) - r.global.String = r.newNativeFunc(r.builtin_String, r.builtin_newString, "String", r.global.StringPrototype, 1) - o = r.global.String.self - o._putProp("fromCharCode", r.newNativeFunc(r.string_fromcharcode, nil, "fromCharCode", nil, 1), true, false, true) - o._putProp("fromCodePoint", r.newNativeFunc(r.string_fromcodepoint, nil, "fromCodePoint", nil, 1), true, false, true) - o._putProp("raw", r.newNativeFunc(r.string_raw, nil, "raw", nil, 1), true, false, true) + return t +} - r.addToGlobal("String", r.global.String) +func (r *Runtime) getStringproto_trimEnd() *Object { + ret := r.global.stringproto_trimEnd + if ret == nil { + ret = r.newNativeFunc(r.stringproto_trimEnd, "trimEnd", 0) + r.global.stringproto_trimEnd = ret + } + return ret +} - r.stringSingleton = r.builtin_new(r.global.String, nil).self.(*stringObject) +func (r *Runtime) getStringproto_trimStart() *Object { + ret := r.global.stringproto_trimStart + if ret == nil { + ret = r.newNativeFunc(r.stringproto_trimStart, "trimStart", 0) + r.global.stringproto_trimStart = ret + } + return ret +} + +func (r *Runtime) getStringSingleton() *stringObject { + ret := r.stringSingleton + if ret == nil { + ret = r.builtin_new(r.getString(), nil).self.(*stringObject) + r.stringSingleton = ret + } + return ret +} + +func (r *Runtime) getString() *Object { + ret := r.global.String + if ret == nil { + ret = &Object{runtime: r} + r.global.String = ret + proto := r.getStringPrototype() + o := r.newNativeFuncAndConstruct(ret, r.builtin_String, r.wrapNativeConstruct(r.builtin_newString, ret, proto), proto, "String", intToValue(1)) + ret.self = o + o._putProp("fromCharCode", r.newNativeFunc(r.string_fromcharcode, "fromCharCode", 1), true, false, true) + o._putProp("fromCodePoint", r.newNativeFunc(r.string_fromcodepoint, "fromCodePoint", 1), true, false, true) + o._putProp("raw", r.newNativeFunc(r.string_raw, "raw", 1), true, false, true) + } + return ret +} + +var stringProtoTemplate *objectTemplate +var stringProtoTemplateOnce sync.Once + +func getStringProtoTemplate() *objectTemplate { + stringProtoTemplateOnce.Do(func() { + stringProtoTemplate = createStringProtoTemplate() + }) + return stringProtoTemplate +} + +func (r *Runtime) getStringPrototype() *Object { + ret := r.global.StringPrototype + if ret == nil { + ret = &Object{runtime: r} + r.global.StringPrototype = ret + o := r.newTemplatedObject(getStringProtoTemplate(), ret) + o.class = classString + } + return ret } diff --git a/builtin_symbol.go b/builtin_symbol.go index f0723435..8231b7bf 100644 --- a/builtin_symbol.go +++ b/builtin_symbol.go @@ -110,29 +110,29 @@ func (r *Runtime) createSymbolProto(val *Object) objectImpl { } o.init() - o._putProp("constructor", r.global.Symbol, true, false, true) + o._putProp("constructor", r.getSymbol(), true, false, true) o.setOwnStr("description", &valueProperty{ configurable: true, getterFunc: r.newNativeFunc(func(call FunctionCall) Value { return r.thisSymbolValue(call.This).desc - }, nil, "get description", nil, 0), + }, "get description", 0), accessor: true, }, false) - o._putProp("toString", r.newNativeFunc(r.symbolproto_tostring, nil, "toString", nil, 0), true, false, true) - o._putProp("valueOf", r.newNativeFunc(r.symbolproto_valueOf, nil, "valueOf", nil, 0), true, false, true) - o._putSym(SymToPrimitive, valueProp(r.newNativeFunc(r.symbolproto_valueOf, nil, "[Symbol.toPrimitive]", nil, 1), false, false, true)) + o._putProp("toString", r.newNativeFunc(r.symbolproto_tostring, "toString", 0), true, false, true) + o._putProp("valueOf", r.newNativeFunc(r.symbolproto_valueOf, "valueOf", 0), true, false, true) + o._putSym(SymToPrimitive, valueProp(r.newNativeFunc(r.symbolproto_valueOf, "[Symbol.toPrimitive]", 1), false, false, true)) o._putSym(SymToStringTag, valueProp(newStringValue("Symbol"), false, false, true)) return o } func (r *Runtime) createSymbol(val *Object) objectImpl { - o := r.newNativeFuncObj(val, r.builtin_symbol, func(args []Value, proto *Object) *Object { + o := r.newNativeFuncAndConstruct(val, r.builtin_symbol, func(args []Value, newTarget *Object) *Object { panic(r.NewTypeError("Symbol is not a constructor")) - }, "Symbol", r.global.SymbolPrototype, _positiveZero) + }, r.getSymbolPrototype(), "Symbol", _positiveZero) - o._putProp("for", r.newNativeFunc(r.symbol_for, nil, "for", nil, 1), true, false, true) - o._putProp("keyFor", r.newNativeFunc(r.symbol_keyfor, nil, "keyFor", nil, 1), true, false, true) + o._putProp("for", r.newNativeFunc(r.symbol_for, "for", 1), true, false, true) + o._putProp("keyFor", r.newNativeFunc(r.symbol_keyfor, "keyFor", 1), true, false, true) for _, s := range []*Symbol{ SymHasInstance, @@ -156,10 +156,22 @@ func (r *Runtime) createSymbol(val *Object) objectImpl { return o } -func (r *Runtime) initSymbol() { - r.global.SymbolPrototype = r.newLazyObject(r.createSymbolProto) - - r.global.Symbol = r.newLazyObject(r.createSymbol) - r.addToGlobal("Symbol", r.global.Symbol) +func (r *Runtime) getSymbolPrototype() *Object { + ret := r.global.SymbolPrototype + if ret == nil { + ret = &Object{runtime: r} + r.global.SymbolPrototype = ret + ret.self = r.createSymbolProto(ret) + } + return ret +} +func (r *Runtime) getSymbol() *Object { + ret := r.global.Symbol + if ret == nil { + ret = &Object{runtime: r} + r.global.Symbol = ret + ret.self = r.createSymbol(ret) + } + return ret } diff --git a/builtin_typedarrays.go b/builtin_typedarrays.go index 02113e87..e867da83 100644 --- a/builtin_typedarrays.go +++ b/builtin_typedarrays.go @@ -4,6 +4,7 @@ import ( "fmt" "math" "sort" + "sync" "unsafe" "github.com/dop251/goja/unistring" @@ -78,7 +79,7 @@ func (r *Runtime) builtin_newArrayBuffer(args []Value, newTarget *Object) *Objec if newTarget == nil { panic(r.needNew("ArrayBuffer")) } - b := r._newArrayBuffer(r.getPrototypeFromCtor(newTarget, r.global.ArrayBuffer, r.global.ArrayBufferPrototype), nil) + b := r._newArrayBuffer(r.getPrototypeFromCtor(newTarget, r.getArrayBuffer(), r.getArrayBufferPrototype()), nil) if len(args) > 0 { b.data = allocByteSlice(r.toIndex(args[0])) } @@ -109,7 +110,7 @@ func (r *Runtime) arrayBufferProto_slice(call FunctionCall) Value { } stop = relToIdx(stop, l) newLen := max(stop-start, 0) - ret := r.speciesConstructor(o, r.global.ArrayBuffer)([]Value{intToValue(newLen)}, nil) + ret := r.speciesConstructor(o, r.getArrayBuffer())([]Value{intToValue(newLen)}, nil) if ab, ok := ret.self.(*arrayBufferObject); ok { if newLen > 0 { b.ensureNotDetached(true) @@ -145,7 +146,7 @@ func (r *Runtime) newDataView(args []Value, newTarget *Object) *Object { if newTarget == nil { panic(r.needNew("DataView")) } - proto := r.getPrototypeFromCtor(newTarget, r.global.DataView, r.global.DataViewPrototype) + proto := r.getPrototypeFromCtor(newTarget, r.getDataView(), r.getDataViewPrototype()) var bufArg Value if len(args) > 0 { bufArg = args[0] @@ -165,13 +166,13 @@ func (r *Runtime) newDataView(args []Value, newTarget *Object) *Object { byteOffset = r.toIndex(offsetArg) buffer.ensureNotDetached(true) if byteOffset > len(buffer.data) { - panic(r.newError(r.global.RangeError, "Start offset %s is outside the bounds of the buffer", offsetArg.String())) + panic(r.newError(r.getRangeError(), "Start offset %s is outside the bounds of the buffer", offsetArg.String())) } } if len(args) > 2 && args[2] != nil && args[2] != _undefined { byteLen = r.toIndex(args[2]) if byteOffset+byteLen > len(buffer.data) { - panic(r.newError(r.global.RangeError, "Invalid DataView length %d", byteLen)) + panic(r.newError(r.getRangeError(), "Invalid DataView length %d", byteLen)) } } else { byteLen = len(buffer.data) - byteOffset @@ -945,7 +946,7 @@ func (r *Runtime) typedArrayProto_set(call FunctionCall) Value { srcObj := call.Argument(0).ToObject(r) targetOffset := toIntStrict(call.Argument(1).ToInteger()) if targetOffset < 0 { - panic(r.newError(r.global.RangeError, "offset should be >= 0")) + panic(r.newError(r.getRangeError(), "offset should be >= 0")) } ta.viewedArrayBuf.ensureNotDetached(true) targetLen := ta.length @@ -953,7 +954,7 @@ func (r *Runtime) typedArrayProto_set(call FunctionCall) Value { src.viewedArrayBuf.ensureNotDetached(true) srcLen := src.length if x := srcLen + targetOffset; x < 0 || x > targetLen { - panic(r.newError(r.global.RangeError, "Source is too large")) + panic(r.newError(r.getRangeError(), "Source is too large")) } if src.defaultCtor == ta.defaultCtor { copy(ta.viewedArrayBuf.data[(ta.offset+targetOffset)*ta.elemSize:], @@ -1002,7 +1003,7 @@ func (r *Runtime) typedArrayProto_set(call FunctionCall) Value { targetLen := ta.length srcLen := toIntStrict(toLength(srcObj.self.getStr("length", nil))) if x := srcLen + targetOffset; x < 0 || x > targetLen { - panic(r.newError(r.global.RangeError, "Source is too large")) + panic(r.newError(r.getRangeError(), "Source is too large")) } for i := 0; i < srcLen; i++ { val := nilSafe(srcObj.self.getIdx(valueInt(i), nil)) @@ -1297,7 +1298,7 @@ func (r *Runtime) _newTypedArrayFromArrayBuffer(ab *arrayBufferObject, args []Va if len(args) > 1 && args[1] != nil && args[1] != _undefined { byteOffset = r.toIndex(args[1]) if byteOffset%ta.elemSize != 0 { - panic(r.newError(r.global.RangeError, "Start offset of %s should be a multiple of %d", newTarget.self.getStr("name", nil), ta.elemSize)) + panic(r.newError(r.getRangeError(), "Start offset of %s should be a multiple of %d", newTarget.self.getStr("name", nil), ta.elemSize)) } } var length int @@ -1305,16 +1306,16 @@ func (r *Runtime) _newTypedArrayFromArrayBuffer(ab *arrayBufferObject, args []Va length = r.toIndex(args[2]) ab.ensureNotDetached(true) if byteOffset+length*ta.elemSize > len(ab.data) { - panic(r.newError(r.global.RangeError, "Invalid typed array length: %d", length)) + panic(r.newError(r.getRangeError(), "Invalid typed array length: %d", length)) } } else { ab.ensureNotDetached(true) if len(ab.data)%ta.elemSize != 0 { - panic(r.newError(r.global.RangeError, "Byte length of %s should be a multiple of %d", newTarget.self.getStr("name", nil), ta.elemSize)) + panic(r.newError(r.getRangeError(), "Byte length of %s should be a multiple of %d", newTarget.self.getStr("name", nil), ta.elemSize)) } length = (len(ab.data) - byteOffset) / ta.elemSize if length < 0 { - panic(r.newError(r.global.RangeError, "Start offset %d is outside the bounds of the buffer", byteOffset)) + panic(r.newError(r.getRangeError(), "Start offset %d is outside the bounds of the buffer", byteOffset)) } } ta.offset = byteOffset / ta.elemSize @@ -1327,7 +1328,8 @@ func (r *Runtime) _newTypedArrayFromTypedArray(src *typedArrayObject, newTarget src.viewedArrayBuf.ensureNotDetached(true) l := src.length - dst.viewedArrayBuf.prototype = r.getPrototypeFromCtor(r.speciesConstructorObj(src.viewedArrayBuf.val, r.global.ArrayBuffer), r.global.ArrayBuffer, r.global.ArrayBufferPrototype) + arrayBuffer := r.getArrayBuffer() + dst.viewedArrayBuf.prototype = r.getPrototypeFromCtor(r.speciesConstructorObj(src.viewedArrayBuf.val, arrayBuffer), arrayBuffer, r.getArrayBufferPrototype()) dst.viewedArrayBuf.data = allocByteSlice(toIntStrict(int64(l) * int64(dst.elemSize))) src.viewedArrayBuf.ensureNotDetached(true) if src.defaultCtor == dst.defaultCtor { @@ -1408,192 +1410,371 @@ func (r *Runtime) createArrayBufferProto(val *Object) objectImpl { byteLengthProp := &valueProperty{ accessor: true, configurable: true, - getterFunc: r.newNativeFunc(r.arrayBufferProto_getByteLength, nil, "get byteLength", nil, 0), + getterFunc: r.newNativeFunc(r.arrayBufferProto_getByteLength, "get byteLength", 0), } b._put("byteLength", byteLengthProp) - b._putProp("constructor", r.global.ArrayBuffer, true, false, true) - b._putProp("slice", r.newNativeFunc(r.arrayBufferProto_slice, nil, "slice", nil, 2), true, false, true) + b._putProp("constructor", r.getArrayBuffer(), true, false, true) + b._putProp("slice", r.newNativeFunc(r.arrayBufferProto_slice, "slice", 2), true, false, true) b._putSym(SymToStringTag, valueProp(asciiString("ArrayBuffer"), false, false, true)) return b } func (r *Runtime) createArrayBuffer(val *Object) objectImpl { - o := r.newNativeConstructOnly(val, r.builtin_newArrayBuffer, r.global.ArrayBufferPrototype, "ArrayBuffer", 1) - o._putProp("isView", r.newNativeFunc(r.arrayBuffer_isView, nil, "isView", nil, 1), true, false, true) + o := r.newNativeConstructOnly(val, r.builtin_newArrayBuffer, r.getArrayBufferPrototype(), "ArrayBuffer", 1) + o._putProp("isView", r.newNativeFunc(r.arrayBuffer_isView, "isView", 1), true, false, true) r.putSpeciesReturnThis(o) return o } -func (r *Runtime) createDataViewProto(val *Object) objectImpl { - b := newBaseObjectObj(val, r.global.ObjectPrototype, classObject) - b._put("buffer", &valueProperty{ - accessor: true, - configurable: true, - getterFunc: r.newNativeFunc(r.dataViewProto_getBuffer, nil, "get buffer", nil, 0), - }) - b._put("byteLength", &valueProperty{ - accessor: true, - configurable: true, - getterFunc: r.newNativeFunc(r.dataViewProto_getByteLen, nil, "get byteLength", nil, 0), - }) - b._put("byteOffset", &valueProperty{ - accessor: true, - configurable: true, - getterFunc: r.newNativeFunc(r.dataViewProto_getByteOffset, nil, "get byteOffset", nil, 0), - }) - b._putProp("constructor", r.global.DataView, true, false, true) - b._putProp("getFloat32", r.newNativeFunc(r.dataViewProto_getFloat32, nil, "getFloat32", nil, 1), true, false, true) - b._putProp("getFloat64", r.newNativeFunc(r.dataViewProto_getFloat64, nil, "getFloat64", nil, 1), true, false, true) - b._putProp("getInt8", r.newNativeFunc(r.dataViewProto_getInt8, nil, "getInt8", nil, 1), true, false, true) - b._putProp("getInt16", r.newNativeFunc(r.dataViewProto_getInt16, nil, "getInt16", nil, 1), true, false, true) - b._putProp("getInt32", r.newNativeFunc(r.dataViewProto_getInt32, nil, "getInt32", nil, 1), true, false, true) - b._putProp("getUint8", r.newNativeFunc(r.dataViewProto_getUint8, nil, "getUint8", nil, 1), true, false, true) - b._putProp("getUint16", r.newNativeFunc(r.dataViewProto_getUint16, nil, "getUint16", nil, 1), true, false, true) - b._putProp("getUint32", r.newNativeFunc(r.dataViewProto_getUint32, nil, "getUint32", nil, 1), true, false, true) - b._putProp("setFloat32", r.newNativeFunc(r.dataViewProto_setFloat32, nil, "setFloat32", nil, 2), true, false, true) - b._putProp("setFloat64", r.newNativeFunc(r.dataViewProto_setFloat64, nil, "setFloat64", nil, 2), true, false, true) - b._putProp("setInt8", r.newNativeFunc(r.dataViewProto_setInt8, nil, "setInt8", nil, 2), true, false, true) - b._putProp("setInt16", r.newNativeFunc(r.dataViewProto_setInt16, nil, "setInt16", nil, 2), true, false, true) - b._putProp("setInt32", r.newNativeFunc(r.dataViewProto_setInt32, nil, "setInt32", nil, 2), true, false, true) - b._putProp("setUint8", r.newNativeFunc(r.dataViewProto_setUint8, nil, "setUint8", nil, 2), true, false, true) - b._putProp("setUint16", r.newNativeFunc(r.dataViewProto_setUint16, nil, "setUint16", nil, 2), true, false, true) - b._putProp("setUint32", r.newNativeFunc(r.dataViewProto_setUint32, nil, "setUint32", nil, 2), true, false, true) - b._putSym(SymToStringTag, valueProp(asciiString("DataView"), false, false, true)) - - return b +func (r *Runtime) createDataView(val *Object) objectImpl { + o := r.newNativeConstructOnly(val, r.newDataView, r.getDataViewPrototype(), "DataView", 1) + return o } -func (r *Runtime) createDataView(val *Object) objectImpl { - o := r.newNativeConstructOnly(val, r.newDataView, r.global.DataViewPrototype, "DataView", 1) +func (r *Runtime) createTypedArray(val *Object) objectImpl { + o := r.newNativeConstructOnly(val, r.newTypedArray, r.getTypedArrayPrototype(), "TypedArray", 0) + o._putProp("from", r.newNativeFunc(r.typedArray_from, "from", 1), true, false, true) + o._putProp("of", r.newNativeFunc(r.typedArray_of, "of", 0), true, false, true) + r.putSpeciesReturnThis(o) + return o } -func (r *Runtime) createTypedArrayProto(val *Object) objectImpl { - b := newBaseObjectObj(val, r.global.ObjectPrototype, classObject) - b._put("buffer", &valueProperty{ - accessor: true, - configurable: true, - getterFunc: r.newNativeFunc(r.typedArrayProto_getBuffer, nil, "get buffer", nil, 0), +func (r *Runtime) getTypedArray() *Object { + ret := r.global.TypedArray + if ret == nil { + ret = &Object{runtime: r} + r.global.TypedArray = ret + r.createTypedArray(ret) + } + return ret +} + +func (r *Runtime) createTypedArrayCtor(val *Object, ctor func(args []Value, newTarget, proto *Object) *Object, name unistring.String, bytesPerElement int) { + p := r.newBaseObject(r.getTypedArrayPrototype(), classObject) + o := r.newNativeConstructOnly(val, func(args []Value, newTarget *Object) *Object { + return ctor(args, newTarget, p.val) + }, p.val, name, 3) + + p._putProp("constructor", o.val, true, false, true) + + o.prototype = r.getTypedArray() + bpe := intToValue(int64(bytesPerElement)) + o._putProp("BYTES_PER_ELEMENT", bpe, false, false, false) + p._putProp("BYTES_PER_ELEMENT", bpe, false, false, false) +} + +func addTypedArrays(t *objectTemplate) { + t.putStr("ArrayBuffer", func(r *Runtime) Value { return valueProp(r.getArrayBuffer(), true, false, true) }) + t.putStr("DataView", func(r *Runtime) Value { return valueProp(r.getDataView(), true, false, true) }) + t.putStr("Uint8Array", func(r *Runtime) Value { return valueProp(r.getUint8Array(), true, false, true) }) + t.putStr("Uint8ClampedArray", func(r *Runtime) Value { return valueProp(r.getUint8ClampedArray(), true, false, true) }) + t.putStr("Int8Array", func(r *Runtime) Value { return valueProp(r.getInt8Array(), true, false, true) }) + t.putStr("Uint16Array", func(r *Runtime) Value { return valueProp(r.getUint16Array(), true, false, true) }) + t.putStr("Int16Array", func(r *Runtime) Value { return valueProp(r.getInt16Array(), true, false, true) }) + t.putStr("Uint32Array", func(r *Runtime) Value { return valueProp(r.getUint32Array(), true, false, true) }) + t.putStr("Int32Array", func(r *Runtime) Value { return valueProp(r.getInt32Array(), true, false, true) }) + t.putStr("Float32Array", func(r *Runtime) Value { return valueProp(r.getFloat32Array(), true, false, true) }) + t.putStr("Float64Array", func(r *Runtime) Value { return valueProp(r.getFloat64Array(), true, false, true) }) +} + +func createTypedArrayProtoTemplate() *objectTemplate { + t := newObjectTemplate() + t.protoFactory = func(r *Runtime) *Object { + return r.global.ObjectPrototype + } + + t.putStr("buffer", func(r *Runtime) Value { + return &valueProperty{ + accessor: true, + configurable: true, + getterFunc: r.newNativeFunc(r.typedArrayProto_getBuffer, "get buffer", 0), + } }) - b._put("byteLength", &valueProperty{ - accessor: true, - configurable: true, - getterFunc: r.newNativeFunc(r.typedArrayProto_getByteLen, nil, "get byteLength", nil, 0), + + t.putStr("byteLength", func(r *Runtime) Value { + return &valueProperty{ + accessor: true, + configurable: true, + getterFunc: r.newNativeFunc(r.typedArrayProto_getByteLen, "get byteLength", 0), + } }) - b._put("byteOffset", &valueProperty{ - accessor: true, - configurable: true, - getterFunc: r.newNativeFunc(r.typedArrayProto_getByteOffset, nil, "get byteOffset", nil, 0), + + t.putStr("byteOffset", func(r *Runtime) Value { + return &valueProperty{ + accessor: true, + configurable: true, + getterFunc: r.newNativeFunc(r.typedArrayProto_getByteOffset, "get byteOffset", 0), + } }) - b._putProp("at", r.newNativeFunc(r.typedArrayProto_at, nil, "at", nil, 1), true, false, true) - b._putProp("constructor", r.global.TypedArray, true, false, true) - b._putProp("copyWithin", r.newNativeFunc(r.typedArrayProto_copyWithin, nil, "copyWithin", nil, 2), true, false, true) - b._putProp("entries", r.newNativeFunc(r.typedArrayProto_entries, nil, "entries", nil, 0), true, false, true) - b._putProp("every", r.newNativeFunc(r.typedArrayProto_every, nil, "every", nil, 1), true, false, true) - b._putProp("fill", r.newNativeFunc(r.typedArrayProto_fill, nil, "fill", nil, 1), true, false, true) - b._putProp("filter", r.newNativeFunc(r.typedArrayProto_filter, nil, "filter", nil, 1), true, false, true) - b._putProp("find", r.newNativeFunc(r.typedArrayProto_find, nil, "find", nil, 1), true, false, true) - b._putProp("findIndex", r.newNativeFunc(r.typedArrayProto_findIndex, nil, "findIndex", nil, 1), true, false, true) - b._putProp("findLast", r.newNativeFunc(r.typedArrayProto_findLast, nil, "findLast", nil, 1), true, false, true) - b._putProp("findLastIndex", r.newNativeFunc(r.typedArrayProto_findLastIndex, nil, "findLastIndex", nil, 1), true, false, true) - b._putProp("forEach", r.newNativeFunc(r.typedArrayProto_forEach, nil, "forEach", nil, 1), true, false, true) - b._putProp("includes", r.newNativeFunc(r.typedArrayProto_includes, nil, "includes", nil, 1), true, false, true) - b._putProp("indexOf", r.newNativeFunc(r.typedArrayProto_indexOf, nil, "indexOf", nil, 1), true, false, true) - b._putProp("join", r.newNativeFunc(r.typedArrayProto_join, nil, "join", nil, 1), true, false, true) - b._putProp("keys", r.newNativeFunc(r.typedArrayProto_keys, nil, "keys", nil, 0), true, false, true) - b._putProp("lastIndexOf", r.newNativeFunc(r.typedArrayProto_lastIndexOf, nil, "lastIndexOf", nil, 1), true, false, true) - b._put("length", &valueProperty{ - accessor: true, - configurable: true, - getterFunc: r.newNativeFunc(r.typedArrayProto_getLength, nil, "get length", nil, 0), + + t.putStr("at", func(r *Runtime) Value { return r.methodProp(r.typedArrayProto_at, "at", 1) }) + t.putStr("constructor", func(r *Runtime) Value { return valueProp(r.getTypedArray(), true, false, true) }) + t.putStr("copyWithin", func(r *Runtime) Value { return r.methodProp(r.typedArrayProto_copyWithin, "copyWithin", 2) }) + t.putStr("entries", func(r *Runtime) Value { return r.methodProp(r.typedArrayProto_entries, "entries", 0) }) + t.putStr("every", func(r *Runtime) Value { return r.methodProp(r.typedArrayProto_every, "every", 1) }) + t.putStr("fill", func(r *Runtime) Value { return r.methodProp(r.typedArrayProto_fill, "fill", 1) }) + t.putStr("filter", func(r *Runtime) Value { return r.methodProp(r.typedArrayProto_filter, "filter", 1) }) + t.putStr("find", func(r *Runtime) Value { return r.methodProp(r.typedArrayProto_find, "find", 1) }) + t.putStr("findIndex", func(r *Runtime) Value { return r.methodProp(r.typedArrayProto_findIndex, "findIndex", 1) }) + t.putStr("findLast", func(r *Runtime) Value { return r.methodProp(r.typedArrayProto_findLast, "findLast", 1) }) + t.putStr("findLastIndex", func(r *Runtime) Value { return r.methodProp(r.typedArrayProto_findLastIndex, "findLastIndex", 1) }) + t.putStr("forEach", func(r *Runtime) Value { return r.methodProp(r.typedArrayProto_forEach, "forEach", 1) }) + t.putStr("includes", func(r *Runtime) Value { return r.methodProp(r.typedArrayProto_includes, "includes", 1) }) + t.putStr("indexOf", func(r *Runtime) Value { return r.methodProp(r.typedArrayProto_indexOf, "indexOf", 1) }) + t.putStr("join", func(r *Runtime) Value { return r.methodProp(r.typedArrayProto_join, "join", 1) }) + t.putStr("keys", func(r *Runtime) Value { return r.methodProp(r.typedArrayProto_keys, "keys", 0) }) + t.putStr("lastIndexOf", func(r *Runtime) Value { return r.methodProp(r.typedArrayProto_lastIndexOf, "lastIndexOf", 1) }) + t.putStr("length", func(r *Runtime) Value { + return &valueProperty{ + accessor: true, + configurable: true, + getterFunc: r.newNativeFunc(r.typedArrayProto_getLength, "get length", 0), + } }) - b._putProp("map", r.newNativeFunc(r.typedArrayProto_map, nil, "map", nil, 1), true, false, true) - b._putProp("reduce", r.newNativeFunc(r.typedArrayProto_reduce, nil, "reduce", nil, 1), true, false, true) - b._putProp("reduceRight", r.newNativeFunc(r.typedArrayProto_reduceRight, nil, "reduceRight", nil, 1), true, false, true) - b._putProp("reverse", r.newNativeFunc(r.typedArrayProto_reverse, nil, "reverse", nil, 0), true, false, true) - b._putProp("set", r.newNativeFunc(r.typedArrayProto_set, nil, "set", nil, 1), true, false, true) - b._putProp("slice", r.newNativeFunc(r.typedArrayProto_slice, nil, "slice", nil, 2), true, false, true) - b._putProp("some", r.newNativeFunc(r.typedArrayProto_some, nil, "some", nil, 1), true, false, true) - b._putProp("sort", r.newNativeFunc(r.typedArrayProto_sort, nil, "sort", nil, 1), true, false, true) - b._putProp("subarray", r.newNativeFunc(r.typedArrayProto_subarray, nil, "subarray", nil, 2), true, false, true) - b._putProp("toLocaleString", r.newNativeFunc(r.typedArrayProto_toLocaleString, nil, "toLocaleString", nil, 0), true, false, true) - b._putProp("toString", r.global.arrayToString, true, false, true) - valuesFunc := r.newNativeFunc(r.typedArrayProto_values, nil, "values", nil, 0) - b._putProp("values", valuesFunc, true, false, true) - b._putSym(SymIterator, valueProp(valuesFunc, true, false, true)) - b._putSym(SymToStringTag, &valueProperty{ - getterFunc: r.newNativeFunc(r.typedArrayProto_toStringTag, nil, "get [Symbol.toStringTag]", nil, 0), - accessor: true, - configurable: true, + t.putStr("map", func(r *Runtime) Value { return r.methodProp(r.typedArrayProto_map, "map", 1) }) + t.putStr("reduce", func(r *Runtime) Value { return r.methodProp(r.typedArrayProto_reduce, "reduce", 1) }) + t.putStr("reduceRight", func(r *Runtime) Value { return r.methodProp(r.typedArrayProto_reduceRight, "reduceRight", 1) }) + t.putStr("reverse", func(r *Runtime) Value { return r.methodProp(r.typedArrayProto_reverse, "reverse", 0) }) + t.putStr("set", func(r *Runtime) Value { return r.methodProp(r.typedArrayProto_set, "set", 1) }) + t.putStr("slice", func(r *Runtime) Value { return r.methodProp(r.typedArrayProto_slice, "slice", 2) }) + t.putStr("some", func(r *Runtime) Value { return r.methodProp(r.typedArrayProto_some, "some", 1) }) + t.putStr("sort", func(r *Runtime) Value { return r.methodProp(r.typedArrayProto_sort, "sort", 1) }) + t.putStr("subarray", func(r *Runtime) Value { return r.methodProp(r.typedArrayProto_subarray, "subarray", 2) }) + t.putStr("toLocaleString", func(r *Runtime) Value { return r.methodProp(r.typedArrayProto_toLocaleString, "toLocaleString", 0) }) + t.putStr("toString", func(r *Runtime) Value { return valueProp(r.getArrayToString(), true, false, true) }) + t.putStr("values", func(r *Runtime) Value { return valueProp(r.getTypedArrayValues(), true, false, true) }) + + t.putSym(SymIterator, func(r *Runtime) Value { return valueProp(r.getTypedArrayValues(), true, false, true) }) + t.putSym(SymToStringTag, func(r *Runtime) Value { + return &valueProperty{ + getterFunc: r.newNativeFunc(r.typedArrayProto_toStringTag, "get [Symbol.toStringTag]", 0), + accessor: true, + configurable: true, + } }) - return b + return t } -func (r *Runtime) createTypedArray(val *Object) objectImpl { - o := r.newNativeConstructOnly(val, r.newTypedArray, r.global.TypedArrayPrototype, "TypedArray", 0) - o._putProp("from", r.newNativeFunc(r.typedArray_from, nil, "from", nil, 1), true, false, true) - o._putProp("of", r.newNativeFunc(r.typedArray_of, nil, "of", nil, 0), true, false, true) - r.putSpeciesReturnThis(o) +func (r *Runtime) getTypedArrayValues() *Object { + ret := r.global.typedArrayValues + if ret == nil { + ret = r.newNativeFunc(r.typedArrayProto_values, "values", 0) + r.global.typedArrayValues = ret + } + return ret +} - return o +var typedArrayProtoTemplate *objectTemplate +var typedArrayProtoTemplateOnce sync.Once + +func getTypedArrayProtoTemplate() *objectTemplate { + typedArrayProtoTemplateOnce.Do(func() { + typedArrayProtoTemplate = createTypedArrayProtoTemplate() + }) + return typedArrayProtoTemplate +} + +func (r *Runtime) getTypedArrayPrototype() *Object { + ret := r.global.TypedArrayPrototype + if ret == nil { + ret = &Object{runtime: r} + r.global.TypedArrayPrototype = ret + r.newTemplatedObject(getTypedArrayProtoTemplate(), ret) + } + return ret +} + +func (r *Runtime) getUint8Array() *Object { + ret := r.global.Uint8Array + if ret == nil { + ret = &Object{runtime: r} + r.global.Uint8Array = ret + r.createTypedArrayCtor(ret, r.newUint8Array, "Uint8Array", 1) + } + return ret +} + +func (r *Runtime) getUint8ClampedArray() *Object { + ret := r.global.Uint8ClampedArray + if ret == nil { + ret = &Object{runtime: r} + r.global.Uint8ClampedArray = ret + r.createTypedArrayCtor(ret, r.newUint8ClampedArray, "Uint8ClampedArray", 1) + } + return ret +} + +func (r *Runtime) getInt8Array() *Object { + ret := r.global.Int8Array + if ret == nil { + ret = &Object{runtime: r} + r.global.Int8Array = ret + r.createTypedArrayCtor(ret, r.newInt8Array, "Int8Array", 1) + } + return ret +} + +func (r *Runtime) getUint16Array() *Object { + ret := r.global.Uint16Array + if ret == nil { + ret = &Object{runtime: r} + r.global.Uint16Array = ret + r.createTypedArrayCtor(ret, r.newUint16Array, "Uint16Array", 2) + } + return ret +} + +func (r *Runtime) getInt16Array() *Object { + ret := r.global.Int16Array + if ret == nil { + ret = &Object{runtime: r} + r.global.Int16Array = ret + r.createTypedArrayCtor(ret, r.newInt16Array, "Int16Array", 2) + } + return ret } -func (r *Runtime) typedArrayCreator(ctor func(args []Value, newTarget, proto *Object) *Object, name unistring.String, bytesPerElement int) func(val *Object) objectImpl { - return func(val *Object) objectImpl { - p := r.newBaseObject(r.global.TypedArrayPrototype, classObject) - o := r.newNativeConstructOnly(val, func(args []Value, newTarget *Object) *Object { - return ctor(args, newTarget, p.val) - }, p.val, name, 3) +func (r *Runtime) getUint32Array() *Object { + ret := r.global.Uint32Array + if ret == nil { + ret = &Object{runtime: r} + r.global.Uint32Array = ret + r.createTypedArrayCtor(ret, r.newUint32Array, "Uint32Array", 4) + } + return ret +} - p._putProp("constructor", o.val, true, false, true) +func (r *Runtime) getInt32Array() *Object { + ret := r.global.Int32Array + if ret == nil { + ret = &Object{runtime: r} + r.global.Int32Array = ret + r.createTypedArrayCtor(ret, r.newInt32Array, "Int32Array", 4) + } + return ret +} - o.prototype = r.global.TypedArray - bpe := intToValue(int64(bytesPerElement)) - o._putProp("BYTES_PER_ELEMENT", bpe, false, false, false) - p._putProp("BYTES_PER_ELEMENT", bpe, false, false, false) - return o +func (r *Runtime) getFloat32Array() *Object { + ret := r.global.Float32Array + if ret == nil { + ret = &Object{runtime: r} + r.global.Float32Array = ret + r.createTypedArrayCtor(ret, r.newFloat32Array, "Float32Array", 4) } + return ret } -func (r *Runtime) initTypedArrays() { +func (r *Runtime) getFloat64Array() *Object { + ret := r.global.Float64Array + if ret == nil { + ret = &Object{runtime: r} + r.global.Float64Array = ret + r.createTypedArrayCtor(ret, r.newFloat64Array, "Float64Array", 8) + } + return ret +} - r.global.ArrayBufferPrototype = r.newLazyObject(r.createArrayBufferProto) - r.global.ArrayBuffer = r.newLazyObject(r.createArrayBuffer) - r.addToGlobal("ArrayBuffer", r.global.ArrayBuffer) +func createDataViewProtoTemplate() *objectTemplate { + t := newObjectTemplate() + t.protoFactory = func(r *Runtime) *Object { + return r.global.ObjectPrototype + } - r.global.DataViewPrototype = r.newLazyObject(r.createDataViewProto) - r.global.DataView = r.newLazyObject(r.createDataView) - r.addToGlobal("DataView", r.global.DataView) + t.putStr("buffer", func(r *Runtime) Value { + return &valueProperty{ + accessor: true, + configurable: true, + getterFunc: r.newNativeFunc(r.dataViewProto_getBuffer, "get buffer", 0), + } + }) + t.putStr("byteLength", func(r *Runtime) Value { + return &valueProperty{ + accessor: true, + configurable: true, + getterFunc: r.newNativeFunc(r.dataViewProto_getByteLen, "get byteLength", 0), + } + }) + t.putStr("byteOffset", func(r *Runtime) Value { + return &valueProperty{ + accessor: true, + configurable: true, + getterFunc: r.newNativeFunc(r.dataViewProto_getByteOffset, "get byteOffset", 0), + } + }) - r.global.TypedArrayPrototype = r.newLazyObject(r.createTypedArrayProto) - r.global.TypedArray = r.newLazyObject(r.createTypedArray) + t.putStr("constructor", func(r *Runtime) Value { return valueProp(r.getDataView(), true, false, true) }) - r.global.Uint8Array = r.newLazyObject(r.typedArrayCreator(r.newUint8Array, "Uint8Array", 1)) - r.addToGlobal("Uint8Array", r.global.Uint8Array) + t.putStr("getFloat32", func(r *Runtime) Value { return r.methodProp(r.dataViewProto_getFloat32, "getFloat32", 1) }) + t.putStr("getFloat64", func(r *Runtime) Value { return r.methodProp(r.dataViewProto_getFloat64, "getFloat64", 1) }) + t.putStr("getInt8", func(r *Runtime) Value { return r.methodProp(r.dataViewProto_getInt8, "getInt8", 1) }) + t.putStr("getInt16", func(r *Runtime) Value { return r.methodProp(r.dataViewProto_getInt16, "getInt16", 1) }) + t.putStr("getInt32", func(r *Runtime) Value { return r.methodProp(r.dataViewProto_getInt32, "getInt32", 1) }) + t.putStr("getUint8", func(r *Runtime) Value { return r.methodProp(r.dataViewProto_getUint8, "getUint8", 1) }) + t.putStr("getUint16", func(r *Runtime) Value { return r.methodProp(r.dataViewProto_getUint16, "getUint16", 1) }) + t.putStr("getUint32", func(r *Runtime) Value { return r.methodProp(r.dataViewProto_getUint32, "getUint32", 1) }) + t.putStr("setFloat32", func(r *Runtime) Value { return r.methodProp(r.dataViewProto_setFloat32, "setFloat32", 2) }) + t.putStr("setFloat64", func(r *Runtime) Value { return r.methodProp(r.dataViewProto_setFloat64, "setFloat64", 2) }) + t.putStr("setInt8", func(r *Runtime) Value { return r.methodProp(r.dataViewProto_setInt8, "setInt8", 2) }) + t.putStr("setInt16", func(r *Runtime) Value { return r.methodProp(r.dataViewProto_setInt16, "setInt16", 2) }) + t.putStr("setInt32", func(r *Runtime) Value { return r.methodProp(r.dataViewProto_setInt32, "setInt32", 2) }) + t.putStr("setUint8", func(r *Runtime) Value { return r.methodProp(r.dataViewProto_setUint8, "setUint8", 2) }) + t.putStr("setUint16", func(r *Runtime) Value { return r.methodProp(r.dataViewProto_setUint16, "setUint16", 2) }) + t.putStr("setUint32", func(r *Runtime) Value { return r.methodProp(r.dataViewProto_setUint32, "setUint32", 2) }) - r.global.Uint8ClampedArray = r.newLazyObject(r.typedArrayCreator(r.newUint8ClampedArray, "Uint8ClampedArray", 1)) - r.addToGlobal("Uint8ClampedArray", r.global.Uint8ClampedArray) + t.putSym(SymToStringTag, func(r *Runtime) Value { return valueProp(asciiString("DataView"), false, false, true) }) - r.global.Int8Array = r.newLazyObject(r.typedArrayCreator(r.newInt8Array, "Int8Array", 1)) - r.addToGlobal("Int8Array", r.global.Int8Array) + return t +} - r.global.Uint16Array = r.newLazyObject(r.typedArrayCreator(r.newUint16Array, "Uint16Array", 2)) - r.addToGlobal("Uint16Array", r.global.Uint16Array) +var dataViewProtoTemplate *objectTemplate +var dataViewProtoTemplateOnce sync.Once - r.global.Int16Array = r.newLazyObject(r.typedArrayCreator(r.newInt16Array, "Int16Array", 2)) - r.addToGlobal("Int16Array", r.global.Int16Array) +func getDataViewProtoTemplate() *objectTemplate { + dataViewProtoTemplateOnce.Do(func() { + dataViewProtoTemplate = createDataViewProtoTemplate() + }) + return dataViewProtoTemplate +} - r.global.Uint32Array = r.newLazyObject(r.typedArrayCreator(r.newUint32Array, "Uint32Array", 4)) - r.addToGlobal("Uint32Array", r.global.Uint32Array) +func (r *Runtime) getDataViewPrototype() *Object { + ret := r.global.DataViewPrototype + if ret == nil { + ret = &Object{runtime: r} + r.global.DataViewPrototype = ret + r.newTemplatedObject(getDataViewProtoTemplate(), ret) + } + return ret +} - r.global.Int32Array = r.newLazyObject(r.typedArrayCreator(r.newInt32Array, "Int32Array", 4)) - r.addToGlobal("Int32Array", r.global.Int32Array) +func (r *Runtime) getDataView() *Object { + ret := r.global.DataView + if ret == nil { + ret = &Object{runtime: r} + r.global.DataView = ret + ret.self = r.createDataView(ret) + } + return ret +} - r.global.Float32Array = r.newLazyObject(r.typedArrayCreator(r.newFloat32Array, "Float32Array", 4)) - r.addToGlobal("Float32Array", r.global.Float32Array) +func (r *Runtime) getArrayBufferPrototype() *Object { + ret := r.global.ArrayBufferPrototype + if ret == nil { + ret = &Object{runtime: r} + r.global.ArrayBufferPrototype = ret + ret.self = r.createArrayBufferProto(ret) + } + return ret +} - r.global.Float64Array = r.newLazyObject(r.typedArrayCreator(r.newFloat64Array, "Float64Array", 8)) - r.addToGlobal("Float64Array", r.global.Float64Array) +func (r *Runtime) getArrayBuffer() *Object { + ret := r.global.ArrayBuffer + if ret == nil { + ret = &Object{runtime: r} + r.global.ArrayBuffer = ret + ret.self = r.createArrayBuffer(ret) + } + return ret } diff --git a/builtin_weakmap.go b/builtin_weakmap.go index a291f74b..40fc717d 100644 --- a/builtin_weakmap.go +++ b/builtin_weakmap.go @@ -90,17 +90,6 @@ func (r *Runtime) needNew(name string) *Object { return r.NewTypeError("Constructor %s requires 'new'", name) } -func (r *Runtime) getPrototypeFromCtor(newTarget, defCtor, defProto *Object) *Object { - if newTarget == defCtor { - return defProto - } - proto := newTarget.self.getStr("prototype", nil) - if obj, ok := proto.(*Object); ok { - return obj - } - return defProto -} - func (r *Runtime) builtin_newWeakMap(args []Value, newTarget *Object) *Object { if newTarget == nil { panic(r.needNew("WeakMap")) @@ -148,12 +137,12 @@ func (r *Runtime) builtin_newWeakMap(args []Value, newTarget *Object) *Object { func (r *Runtime) createWeakMapProto(val *Object) objectImpl { o := newBaseObjectObj(val, r.global.ObjectPrototype, classObject) - o._putProp("constructor", r.global.WeakMap, true, false, true) - r.global.weakMapAdder = r.newNativeFunc(r.weakMapProto_set, nil, "set", nil, 2) + o._putProp("constructor", r.getWeakMap(), true, false, true) + r.global.weakMapAdder = r.newNativeFunc(r.weakMapProto_set, "set", 2) o._putProp("set", r.global.weakMapAdder, true, false, true) - o._putProp("delete", r.newNativeFunc(r.weakMapProto_delete, nil, "delete", nil, 1), true, false, true) - o._putProp("has", r.newNativeFunc(r.weakMapProto_has, nil, "has", nil, 1), true, false, true) - o._putProp("get", r.newNativeFunc(r.weakMapProto_get, nil, "get", nil, 1), true, false, true) + o._putProp("delete", r.newNativeFunc(r.weakMapProto_delete, "delete", 1), true, false, true) + o._putProp("has", r.newNativeFunc(r.weakMapProto_has, "has", 1), true, false, true) + o._putProp("get", r.newNativeFunc(r.weakMapProto_get, "get", 1), true, false, true) o._putSym(SymToStringTag, valueProp(asciiString(classWeakMap), false, false, true)) @@ -161,14 +150,27 @@ func (r *Runtime) createWeakMapProto(val *Object) objectImpl { } func (r *Runtime) createWeakMap(val *Object) objectImpl { - o := r.newNativeConstructOnly(val, r.builtin_newWeakMap, r.global.WeakMapPrototype, "WeakMap", 0) + o := r.newNativeConstructOnly(val, r.builtin_newWeakMap, r.getWeakMapPrototype(), "WeakMap", 0) return o } -func (r *Runtime) initWeakMap() { - r.global.WeakMapPrototype = r.newLazyObject(r.createWeakMapProto) - r.global.WeakMap = r.newLazyObject(r.createWeakMap) +func (r *Runtime) getWeakMapPrototype() *Object { + ret := r.global.WeakMapPrototype + if ret == nil { + ret = &Object{runtime: r} + r.global.WeakMapPrototype = ret + ret.self = r.createWeakMapProto(ret) + } + return ret +} - r.addToGlobal("WeakMap", r.global.WeakMap) +func (r *Runtime) getWeakMap() *Object { + ret := r.global.WeakMap + if ret == nil { + ret = &Object{runtime: r} + r.global.WeakMap = ret + ret.self = r.createWeakMap(ret) + } + return ret } diff --git a/builtin_weakset.go b/builtin_weakset.go index 027f03a5..cd8183e5 100644 --- a/builtin_weakset.go +++ b/builtin_weakset.go @@ -98,10 +98,10 @@ func (r *Runtime) createWeakSetProto(val *Object) objectImpl { o := newBaseObjectObj(val, r.global.ObjectPrototype, classObject) o._putProp("constructor", r.global.WeakSet, true, false, true) - r.global.weakSetAdder = r.newNativeFunc(r.weakSetProto_add, nil, "add", nil, 1) + r.global.weakSetAdder = r.newNativeFunc(r.weakSetProto_add, "add", 1) o._putProp("add", r.global.weakSetAdder, true, false, true) - o._putProp("delete", r.newNativeFunc(r.weakSetProto_delete, nil, "delete", nil, 1), true, false, true) - o._putProp("has", r.newNativeFunc(r.weakSetProto_has, nil, "has", nil, 1), true, false, true) + o._putProp("delete", r.newNativeFunc(r.weakSetProto_delete, "delete", 1), true, false, true) + o._putProp("has", r.newNativeFunc(r.weakSetProto_has, "has", 1), true, false, true) o._putSym(SymToStringTag, valueProp(asciiString(classWeakSet), false, false, true)) @@ -109,14 +109,27 @@ func (r *Runtime) createWeakSetProto(val *Object) objectImpl { } func (r *Runtime) createWeakSet(val *Object) objectImpl { - o := r.newNativeConstructOnly(val, r.builtin_newWeakSet, r.global.WeakSetPrototype, "WeakSet", 0) + o := r.newNativeConstructOnly(val, r.builtin_newWeakSet, r.getWeakSetPrototype(), "WeakSet", 0) return o } -func (r *Runtime) initWeakSet() { - r.global.WeakSetPrototype = r.newLazyObject(r.createWeakSetProto) - r.global.WeakSet = r.newLazyObject(r.createWeakSet) +func (r *Runtime) getWeakSetPrototype() *Object { + ret := r.global.WeakSetPrototype + if ret == nil { + ret = &Object{runtime: r} + r.global.WeakSetPrototype = ret + ret.self = r.createWeakSetProto(ret) + } + return ret +} - r.addToGlobal("WeakSet", r.global.WeakSet) +func (r *Runtime) getWeakSet() *Object { + ret := r.global.WeakSet + if ret == nil { + ret = &Object{runtime: r} + r.global.WeakSet = ret + ret.self = r.createWeakSet(ret) + } + return ret } diff --git a/func.go b/func.go index 9040a531..c0469569 100644 --- a/func.go +++ b/func.go @@ -367,7 +367,7 @@ func (f *classFuncObject) construct(args []Value, newTarget *Object) *Object { if v := r.vm.stack[r.vm.sp+1]; v != nil { // using residual 'this' value (a bit hacky) instance = r.toObject(v) } else { - panic(r.newError(r.global.ReferenceError, "Must call super constructor in derived class before returning from derived constructor")) + panic(r.newError(r.getReferenceError(), "Must call super constructor in derived class before returning from derived constructor")) } } return instance @@ -509,9 +509,9 @@ func (f *baseFuncObject) init(name unistring.String, length Value) { f._putProp("name", stringValueFromRaw(name), false, false, true) } -func (f *baseFuncObject) hasInstance(v Value) bool { +func hasInstance(val *Object, v Value) bool { if v, ok := v.(*Object); ok { - o := f.val.self.getStr("prototype", nil) + o := val.self.getStr("prototype", nil) if o1, ok := o.(*Object); ok { for { v = v.self.proto() @@ -523,13 +523,17 @@ func (f *baseFuncObject) hasInstance(v Value) bool { } } } else { - f.val.runtime.typeErrorResult(true, "prototype is not an object") + panic(val.runtime.NewTypeError("prototype is not an object")) } } return false } +func (f *baseFuncObject) hasInstance(v Value) bool { + return hasInstance(f.val, v) +} + func (f *nativeFuncObject) defaultConstruct(ccall func(ConstructorCall) *Object, args []Value, newTarget *Object) *Object { obj := f.createInstance(newTarget) ret := ccall(ConstructorCall{ @@ -707,7 +711,7 @@ func (ar *asyncRunner) step(res Value, done bool, ex *Exception) { } // await - promise := r.promiseResolve(r.global.Promise, res) + promise := r.promiseResolve(r.getPromise(), res) promise.self.(*Promise).addReactions(&promiseReaction{ typ: promiseReactionFulfill, handler: &jobCallback{callback: ar.onFulfilled}, @@ -722,7 +726,7 @@ func (ar *asyncRunner) step(res Value, done bool, ex *Exception) { func (ar *asyncRunner) start(nArgs int) { r := ar.f.runtime ar.gen.vm = r.vm - ar.promiseCap = r.newPromiseCapability(r.global.Promise) + ar.promiseCap = r.newPromiseCapability(r.getPromise()) sp := r.vm.sp ar.gen.enter() ar.vmCall(r.vm, nArgs) diff --git a/object.go b/object.go index 99a06d37..79bd67df 100644 --- a/object.go +++ b/object.go @@ -871,7 +871,7 @@ func (o *Object) ordinaryToPrimitiveString() Value { return v } - panic(o.runtime.NewTypeError("Could not convert %v to primitive", o.self)) + panic(o.runtime.NewTypeError("Could not convert %v (%T) to primitive", o.self, o.self)) } func (o *Object) tryExoticToPrimitive(hint Value) Value { @@ -916,8 +916,8 @@ func (o *baseObject) assertCallable() (func(FunctionCall) Value, bool) { return nil, false } -func (o *baseObject) vmCall(vm *vm, n int) { - vm.r.typeErrorResult(true, "Not a function: %s", o.val.toString()) +func (o *baseObject) vmCall(vm *vm, _ int) { + panic(vm.r.NewTypeError("Not a function: %s", o.val.toString())) } func (o *baseObject) assertConstructor() func(args []Value, newTarget *Object) *Object { diff --git a/object_dynamic.go b/object_dynamic.go index f224224d..b1e3161e 100644 --- a/object_dynamic.go +++ b/object_dynamic.go @@ -141,7 +141,7 @@ func (r *Runtime) NewDynamicArray(a DynamicArray) *Object { a: a, baseDynamicObject: baseDynamicObject{ val: v, - prototype: r.global.ArrayPrototype, + prototype: r.getArrayPrototype(), }, } v.self = o diff --git a/object_goarray_reflect.go b/object_goarray_reflect.go index 3db5a892..e40364db 100644 --- a/object_goarray_reflect.go +++ b/object_goarray_reflect.go @@ -59,7 +59,7 @@ func (c *valueArrayCache) shrink(newlen int) { func (o *objectGoArrayReflect) _init() { o.objectGoReflect.init() o.class = classArray - o.prototype = o.val.runtime.global.ArrayPrototype + o.prototype = o.val.runtime.getArrayPrototype() o.baseObject._put("length", &o.lengthProp) } diff --git a/object_goreflect.go b/object_goreflect.go index a4584d39..7ad5970e 100644 --- a/object_goreflect.go +++ b/object_goreflect.go @@ -122,24 +122,24 @@ func (o *objectGoReflect) init() { switch o.fieldsValue.Kind() { case reflect.Bool: o.class = classBoolean - o.prototype = o.val.runtime.global.BooleanPrototype + o.prototype = o.val.runtime.getBooleanPrototype() o.toString = o._toStringBool o.valueOf = o._valueOfBool case reflect.String: o.class = classString - o.prototype = o.val.runtime.global.StringPrototype + o.prototype = o.val.runtime.getStringPrototype() o.toString = o._toStringString case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: o.class = classNumber - o.prototype = o.val.runtime.global.NumberPrototype + o.prototype = o.val.runtime.getNumberPrototype() o.valueOf = o._valueOfInt case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: o.class = classNumber - o.prototype = o.val.runtime.global.NumberPrototype + o.prototype = o.val.runtime.getNumberPrototype() o.valueOf = o._valueOfUint case reflect.Float32, reflect.Float64: o.class = classNumber - o.prototype = o.val.runtime.global.NumberPrototype + o.prototype = o.val.runtime.getNumberPrototype() o.valueOf = o._valueOfFloat default: o.class = classObject diff --git a/object_goslice.go b/object_goslice.go index 4f509dd4..1a522074 100644 --- a/object_goslice.go +++ b/object_goslice.go @@ -34,7 +34,7 @@ func (r *Runtime) newObjectGoSlice(data *[]interface{}, isPtr bool) *objectGoSli func (o *objectGoSlice) init() { o.baseObject.init() o.class = classArray - o.prototype = o.val.runtime.global.ArrayPrototype + o.prototype = o.val.runtime.getArrayPrototype() o.lengthProp.writable = true o.extensible = true o.baseObject._put("length", &o.lengthProp) diff --git a/object_lazy.go b/object_lazy.go deleted file mode 100644 index 171bdf63..00000000 --- a/object_lazy.go +++ /dev/null @@ -1,314 +0,0 @@ -package goja - -import ( - "reflect" - - "github.com/dop251/goja/unistring" -) - -type lazyObject struct { - val *Object - create func(*Object) objectImpl -} - -func (o *lazyObject) className() string { - obj := o.create(o.val) - o.val.self = obj - return obj.className() -} - -func (o *lazyObject) typeOf() String { - obj := o.create(o.val) - o.val.self = obj - return obj.typeOf() -} - -func (o *lazyObject) getIdx(p valueInt, receiver Value) Value { - obj := o.create(o.val) - o.val.self = obj - return obj.getIdx(p, receiver) -} - -func (o *lazyObject) getSym(p *Symbol, receiver Value) Value { - obj := o.create(o.val) - o.val.self = obj - return obj.getSym(p, receiver) -} - -func (o *lazyObject) getOwnPropIdx(idx valueInt) Value { - obj := o.create(o.val) - o.val.self = obj - return obj.getOwnPropIdx(idx) -} - -func (o *lazyObject) getOwnPropSym(s *Symbol) Value { - obj := o.create(o.val) - o.val.self = obj - return obj.getOwnPropSym(s) -} - -func (o *lazyObject) hasPropertyIdx(idx valueInt) bool { - obj := o.create(o.val) - o.val.self = obj - return obj.hasPropertyIdx(idx) -} - -func (o *lazyObject) hasPropertySym(s *Symbol) bool { - obj := o.create(o.val) - o.val.self = obj - return obj.hasPropertySym(s) -} - -func (o *lazyObject) hasOwnPropertyIdx(idx valueInt) bool { - obj := o.create(o.val) - o.val.self = obj - return obj.hasOwnPropertyIdx(idx) -} - -func (o *lazyObject) hasOwnPropertySym(s *Symbol) bool { - obj := o.create(o.val) - o.val.self = obj - return obj.hasOwnPropertySym(s) -} - -func (o *lazyObject) defineOwnPropertyStr(name unistring.String, desc PropertyDescriptor, throw bool) bool { - obj := o.create(o.val) - o.val.self = obj - return obj.defineOwnPropertyStr(name, desc, throw) -} - -func (o *lazyObject) defineOwnPropertyIdx(name valueInt, desc PropertyDescriptor, throw bool) bool { - obj := o.create(o.val) - o.val.self = obj - return obj.defineOwnPropertyIdx(name, desc, throw) -} - -func (o *lazyObject) defineOwnPropertySym(name *Symbol, desc PropertyDescriptor, throw bool) bool { - obj := o.create(o.val) - o.val.self = obj - return obj.defineOwnPropertySym(name, desc, throw) -} - -func (o *lazyObject) deleteIdx(idx valueInt, throw bool) bool { - obj := o.create(o.val) - o.val.self = obj - return obj.deleteIdx(idx, throw) -} - -func (o *lazyObject) deleteSym(s *Symbol, throw bool) bool { - obj := o.create(o.val) - o.val.self = obj - return obj.deleteSym(s, throw) -} - -func (o *lazyObject) getStr(name unistring.String, receiver Value) Value { - obj := o.create(o.val) - o.val.self = obj - return obj.getStr(name, receiver) -} - -func (o *lazyObject) getOwnPropStr(name unistring.String) Value { - obj := o.create(o.val) - o.val.self = obj - return obj.getOwnPropStr(name) -} - -func (o *lazyObject) setOwnStr(p unistring.String, v Value, throw bool) bool { - obj := o.create(o.val) - o.val.self = obj - return obj.setOwnStr(p, v, throw) -} - -func (o *lazyObject) setOwnIdx(p valueInt, v Value, throw bool) bool { - obj := o.create(o.val) - o.val.self = obj - return obj.setOwnIdx(p, v, throw) -} - -func (o *lazyObject) setOwnSym(p *Symbol, v Value, throw bool) bool { - obj := o.create(o.val) - o.val.self = obj - return obj.setOwnSym(p, v, throw) -} - -func (o *lazyObject) setForeignStr(p unistring.String, v, receiver Value, throw bool) (bool, bool) { - obj := o.create(o.val) - o.val.self = obj - return obj.setForeignStr(p, v, receiver, throw) -} - -func (o *lazyObject) setForeignIdx(p valueInt, v, receiver Value, throw bool) (bool, bool) { - obj := o.create(o.val) - o.val.self = obj - return obj.setForeignIdx(p, v, receiver, throw) -} - -func (o *lazyObject) setForeignSym(p *Symbol, v, receiver Value, throw bool) (bool, bool) { - obj := o.create(o.val) - o.val.self = obj - return obj.setForeignSym(p, v, receiver, throw) -} - -func (o *lazyObject) hasPropertyStr(name unistring.String) bool { - obj := o.create(o.val) - o.val.self = obj - return obj.hasPropertyStr(name) -} - -func (o *lazyObject) hasOwnPropertyStr(name unistring.String) bool { - obj := o.create(o.val) - o.val.self = obj - return obj.hasOwnPropertyStr(name) -} - -func (o *lazyObject) _putProp(unistring.String, Value, bool, bool, bool) Value { - panic("cannot use _putProp() in lazy object") -} - -func (o *lazyObject) _putSym(*Symbol, Value) { - panic("cannot use _putSym() in lazy object") -} - -func (o *lazyObject) assertCallable() (call func(FunctionCall) Value, ok bool) { - obj := o.create(o.val) - o.val.self = obj - return obj.assertCallable() -} - -func (o *lazyObject) vmCall(vm *vm, n int) { - obj := o.create(o.val) - o.val.self = obj - obj.vmCall(vm, n) -} - -func (o *lazyObject) assertConstructor() func(args []Value, newTarget *Object) *Object { - obj := o.create(o.val) - o.val.self = obj - return obj.assertConstructor() -} - -func (o *lazyObject) deleteStr(name unistring.String, throw bool) bool { - obj := o.create(o.val) - o.val.self = obj - return obj.deleteStr(name, throw) -} - -func (o *lazyObject) proto() *Object { - obj := o.create(o.val) - o.val.self = obj - return obj.proto() -} - -func (o *lazyObject) hasInstance(v Value) bool { - obj := o.create(o.val) - o.val.self = obj - return obj.hasInstance(v) -} - -func (o *lazyObject) isExtensible() bool { - obj := o.create(o.val) - o.val.self = obj - return obj.isExtensible() -} - -func (o *lazyObject) preventExtensions(throw bool) bool { - obj := o.create(o.val) - o.val.self = obj - return obj.preventExtensions(throw) -} - -func (o *lazyObject) iterateStringKeys() iterNextFunc { - obj := o.create(o.val) - o.val.self = obj - return obj.iterateStringKeys() -} - -func (o *lazyObject) iterateSymbols() iterNextFunc { - obj := o.create(o.val) - o.val.self = obj - return obj.iterateSymbols() -} - -func (o *lazyObject) iterateKeys() iterNextFunc { - obj := o.create(o.val) - o.val.self = obj - return obj.iterateKeys() -} - -func (o *lazyObject) export(ctx *objectExportCtx) interface{} { - obj := o.create(o.val) - o.val.self = obj - return obj.export(ctx) -} - -func (o *lazyObject) exportType() reflect.Type { - obj := o.create(o.val) - o.val.self = obj - return obj.exportType() -} - -func (o *lazyObject) exportToMap(m reflect.Value, typ reflect.Type, ctx *objectExportCtx) error { - obj := o.create(o.val) - o.val.self = obj - return obj.exportToMap(m, typ, ctx) -} - -func (o *lazyObject) exportToArrayOrSlice(s reflect.Value, typ reflect.Type, ctx *objectExportCtx) error { - obj := o.create(o.val) - o.val.self = obj - return obj.exportToArrayOrSlice(s, typ, ctx) -} - -func (o *lazyObject) equal(other objectImpl) bool { - obj := o.create(o.val) - o.val.self = obj - return obj.equal(other) -} - -func (o *lazyObject) stringKeys(all bool, accum []Value) []Value { - obj := o.create(o.val) - o.val.self = obj - return obj.stringKeys(all, accum) -} - -func (o *lazyObject) symbols(all bool, accum []Value) []Value { - obj := o.create(o.val) - o.val.self = obj - return obj.symbols(all, accum) -} - -func (o *lazyObject) keys(all bool, accum []Value) []Value { - obj := o.create(o.val) - o.val.self = obj - return obj.keys(all, accum) -} - -func (o *lazyObject) setProto(proto *Object, throw bool) bool { - obj := o.create(o.val) - o.val.self = obj - return obj.setProto(proto, throw) -} - -func (o *lazyObject) getPrivateEnv(typ *privateEnvType, create bool) *privateElements { - obj := o.create(o.val) - o.val.self = obj - return obj.getPrivateEnv(typ, create) -} - -func (o *lazyObject) sortLen() int { - obj := o.create(o.val) - o.val.self = obj - return obj.sortLen() -} - -func (o *lazyObject) sortGet(i int) Value { - obj := o.create(o.val) - o.val.self = obj - return obj.sortGet(i) -} - -func (o *lazyObject) swap(i int, j int) { - obj := o.create(o.val) - o.val.self = obj - obj.swap(i, j) -} diff --git a/object_template.go b/object_template.go new file mode 100644 index 00000000..6d42f9f9 --- /dev/null +++ b/object_template.go @@ -0,0 +1,469 @@ +package goja + +import ( + "fmt" + "github.com/dop251/goja/unistring" + "math" + "reflect" + "sort" +) + +type templatePropFactory func(*Runtime) Value + +type objectTemplate struct { + propNames []unistring.String + props map[unistring.String]templatePropFactory + + symProps map[*Symbol]templatePropFactory + symPropNames []*Symbol + + protoFactory func(*Runtime) *Object +} + +type templatedObject struct { + baseObject + tmpl *objectTemplate + + protoMaterialised bool +} + +type templatedFuncObject struct { + templatedObject + + f func(FunctionCall) Value + construct func(args []Value, newTarget *Object) *Object +} + +// This type exists because Array.prototype is supposed to be an array itself and I could not find +// a different way of implementing it without either introducing another layer of interfaces or hoisting +// the templates to baseObject both of which would have had a negative effect on the performance. +// The implementation is as simple as possible and is not optimised in any way, but I very much doubt anybody +// uses Array.prototype as an actual array. +type templatedArrayObject struct { + templatedObject +} + +func newObjectTemplate() *objectTemplate { + return &objectTemplate{ + props: make(map[unistring.String]templatePropFactory), + } +} + +func (t *objectTemplate) putStr(name unistring.String, f templatePropFactory) { + t.props[name] = f + t.propNames = append(t.propNames, name) +} + +func (t *objectTemplate) putSym(s *Symbol, f templatePropFactory) { + if t.symProps == nil { + t.symProps = make(map[*Symbol]templatePropFactory) + } + t.symProps[s] = f + t.symPropNames = append(t.symPropNames, s) +} + +func (r *Runtime) newTemplatedObject(tmpl *objectTemplate, obj *Object) *templatedObject { + if obj == nil { + obj = &Object{runtime: r} + } + o := &templatedObject{ + baseObject: baseObject{ + class: classObject, + val: obj, + extensible: true, + }, + tmpl: tmpl, + } + obj.self = o + o.init() + return o +} + +func (o *templatedObject) materialiseProto() { + if !o.protoMaterialised { + if o.tmpl.protoFactory != nil { + o.prototype = o.tmpl.protoFactory(o.val.runtime) + } + o.protoMaterialised = true + } +} + +func (o *templatedObject) getStr(name unistring.String, receiver Value) Value { + ownProp := o.getOwnPropStr(name) + if ownProp == nil { + o.materialiseProto() + } + return o.getStrWithOwnProp(ownProp, name, receiver) +} + +func (o *templatedObject) getSym(s *Symbol, receiver Value) Value { + ownProp := o.getOwnPropSym(s) + if ownProp == nil { + o.materialiseProto() + } + return o.getWithOwnProp(ownProp, s, receiver) +} + +func (o *templatedObject) getOwnPropStr(p unistring.String) Value { + if v, exists := o.values[p]; exists { + return v + } + if f := o.tmpl.props[p]; f != nil { + v := f(o.val.runtime) + o.values[p] = v + return v + } + return nil +} + +func (o *templatedObject) materialiseSymbols() { + if o.symValues == nil { + o.symValues = newOrderedMap(nil) + for _, p := range o.tmpl.symPropNames { + o.symValues.set(p, o.tmpl.symProps[p](o.val.runtime)) + } + } +} + +func (o *templatedObject) getOwnPropSym(s *Symbol) Value { + if o.symValues == nil && o.tmpl.symProps[s] == nil { + return nil + } + o.materialiseSymbols() + return o.baseObject.getOwnPropSym(s) +} + +func (o *templatedObject) materialisePropNames() { + if o.propNames == nil { + o.propNames = append(([]unistring.String)(nil), o.tmpl.propNames...) + } +} + +func (o *templatedObject) setOwnStr(p unistring.String, v Value, throw bool) bool { + existing := o.getOwnPropStr(p) // materialise property (in case it's an accessor) + if existing == nil { + o.materialiseProto() + o.materialisePropNames() + } + return o.baseObject.setOwnStr(p, v, throw) +} + +func (o *templatedObject) setOwnSym(name *Symbol, val Value, throw bool) bool { + o.materialiseSymbols() + o.materialiseProto() + return o.baseObject.setOwnSym(name, val, throw) +} + +func (o *templatedObject) setForeignStr(name unistring.String, val, receiver Value, throw bool) (bool, bool) { + ownProp := o.getOwnPropStr(name) + if ownProp == nil { + o.materialiseProto() + } + return o._setForeignStr(name, ownProp, val, receiver, throw) +} + +func (o *templatedObject) proto() *Object { + o.materialiseProto() + return o.prototype +} + +func (o *templatedObject) setProto(proto *Object, throw bool) bool { + o.protoMaterialised = true + ret := o.baseObject.setProto(proto, throw) + if ret { + o.protoMaterialised = true + } + return ret +} + +func (o *templatedObject) setForeignIdx(name valueInt, val, receiver Value, throw bool) (bool, bool) { + return o.setForeignStr(name.string(), val, receiver, throw) +} + +func (o *templatedObject) setForeignSym(name *Symbol, val, receiver Value, throw bool) (bool, bool) { + o.materialiseProto() + o.materialiseSymbols() + return o.baseObject.setForeignSym(name, val, receiver, throw) +} + +func (o *templatedObject) hasPropertyStr(name unistring.String) bool { + if o.val.self.hasOwnPropertyStr(name) { + return true + } + o.materialiseProto() + if o.prototype != nil { + return o.prototype.self.hasPropertyStr(name) + } + return false +} + +func (o *templatedObject) hasPropertySym(s *Symbol) bool { + if o.hasOwnPropertySym(s) { + return true + } + o.materialiseProto() + if o.prototype != nil { + return o.prototype.self.hasPropertySym(s) + } + return false +} + +func (o *templatedObject) hasOwnPropertyStr(name unistring.String) bool { + if v, exists := o.values[name]; exists { + return v != nil + } + + _, exists := o.tmpl.props[name] + return exists +} + +func (o *templatedObject) hasOwnPropertySym(s *Symbol) bool { + if o.symValues != nil { + return o.symValues.has(s) + } + _, exists := o.tmpl.symProps[s] + return exists +} + +func (o *templatedObject) defineOwnPropertyStr(name unistring.String, descr PropertyDescriptor, throw bool) bool { + existingVal := o.getOwnPropStr(name) + if v, ok := o._defineOwnProperty(name, existingVal, descr, throw); ok { + o.values[name] = v + if existingVal == nil { + o.materialisePropNames() + names := copyNamesIfNeeded(o.propNames, 1) + o.propNames = append(names, name) + } + return true + } + return false +} + +func (o *templatedObject) defineOwnPropertySym(s *Symbol, descr PropertyDescriptor, throw bool) bool { + o.materialiseSymbols() + return o.baseObject.defineOwnPropertySym(s, descr, throw) +} + +func (o *templatedObject) deleteStr(name unistring.String, throw bool) bool { + if val := o.getOwnPropStr(name); val != nil { + if !o.checkDelete(name, val, throw) { + return false + } + o.materialisePropNames() + o._delete(name) + if _, exists := o.tmpl.props[name]; exists { + o.values[name] = nil // white hole + } + } + return true +} + +func (o *templatedObject) deleteSym(s *Symbol, throw bool) bool { + o.materialiseSymbols() + return o.baseObject.deleteSym(s, throw) +} + +func (o *templatedObject) materialiseProps() { + for name, f := range o.tmpl.props { + if _, exists := o.values[name]; !exists { + o.values[name] = f(o.val.runtime) + } + } + o.materialisePropNames() +} + +func (o *templatedObject) iterateStringKeys() iterNextFunc { + o.materialiseProps() + return o.baseObject.iterateStringKeys() +} + +func (o *templatedObject) iterateSymbols() iterNextFunc { + o.materialiseSymbols() + return o.baseObject.iterateSymbols() +} + +func (o *templatedObject) stringKeys(all bool, keys []Value) []Value { + if all { + o.materialisePropNames() + } else { + o.materialiseProps() + } + return o.baseObject.stringKeys(all, keys) +} + +func (o *templatedObject) symbols(all bool, accum []Value) []Value { + o.materialiseSymbols() + return o.baseObject.symbols(all, accum) +} + +func (o *templatedObject) keys(all bool, accum []Value) []Value { + return o.symbols(all, o.stringKeys(all, accum)) +} + +func (r *Runtime) newTemplatedFuncObject(tmpl *objectTemplate, obj *Object, f func(FunctionCall) Value, ctor func([]Value, *Object) *Object) *templatedFuncObject { + if obj == nil { + obj = &Object{runtime: r} + } + o := &templatedFuncObject{ + templatedObject: templatedObject{ + baseObject: baseObject{ + class: classFunction, + val: obj, + extensible: true, + }, + tmpl: tmpl, + }, + f: f, + construct: ctor, + } + obj.self = o + o.init() + return o +} + +func (f *templatedFuncObject) source() String { + return newStringValue(fmt.Sprintf("function %s() { [native code] }", nilSafe(f.getStr("name", nil)).toString())) +} + +func (f *templatedFuncObject) export(*objectExportCtx) interface{} { + return f.f +} + +func (f *templatedFuncObject) assertCallable() (func(FunctionCall) Value, bool) { + if f.f != nil { + return f.f, true + } + return nil, false +} + +func (f *templatedFuncObject) vmCall(vm *vm, n int) { + var nf nativeFuncObject + nf.f = f.f + nf.vmCall(vm, n) +} + +func (f *templatedFuncObject) assertConstructor() func(args []Value, newTarget *Object) *Object { + return f.construct +} + +func (f *templatedFuncObject) exportType() reflect.Type { + return reflectTypeFunc +} + +func (f *templatedFuncObject) typeOf() String { + return stringFunction +} + +func (f *templatedFuncObject) hasInstance(v Value) bool { + return hasInstance(f.val, v) +} + +func (r *Runtime) newTemplatedArrayObject(tmpl *objectTemplate, obj *Object) *templatedArrayObject { + if obj == nil { + obj = &Object{runtime: r} + } + o := &templatedArrayObject{ + templatedObject: templatedObject{ + baseObject: baseObject{ + class: classArray, + val: obj, + extensible: true, + }, + tmpl: tmpl, + }, + } + obj.self = o + o.init() + return o +} + +func (a *templatedArrayObject) getLenProp() *valueProperty { + lenProp, _ := a.getOwnPropStr("length").(*valueProperty) + if lenProp == nil { + panic(a.val.runtime.NewTypeError("missing length property")) + } + return lenProp +} + +func (a *templatedArrayObject) _setOwnIdx(idx uint32) { + lenProp := a.getLenProp() + l := uint32(lenProp.value.ToInteger()) + if idx >= l { + lenProp.value = intToValue(int64(idx) + 1) + } +} + +func (a *templatedArrayObject) setLength(l uint32, throw bool) bool { + lenProp := a.getLenProp() + oldLen := uint32(lenProp.value.ToInteger()) + if l == oldLen { + return true + } + if !lenProp.writable { + a.val.runtime.typeErrorResult(throw, "length is not writable") + return false + } + ret := true + if l < oldLen { + a.materialisePropNames() + a.fixPropOrder() + i := sort.Search(a.idxPropCount, func(idx int) bool { + return strToArrayIdx(a.propNames[idx]) >= l + }) + for j := a.idxPropCount - 1; j >= i; j-- { + if !a.deleteStr(a.propNames[j], false) { + l = strToArrayIdx(a.propNames[j]) + 1 + ret = false + break + } + } + } + lenProp.value = intToValue(int64(l)) + return ret +} + +func (a *templatedArrayObject) setOwnStr(name unistring.String, value Value, throw bool) bool { + if name == "length" { + return a.setLength(a.val.runtime.toLengthUint32(value), throw) + } + if !a.templatedObject.setOwnStr(name, value, throw) { + return false + } + if idx := strToArrayIdx(name); idx != math.MaxUint32 { + a._setOwnIdx(idx) + } + return true +} + +func (a *templatedArrayObject) setOwnIdx(p valueInt, v Value, throw bool) bool { + if !a.templatedObject.setOwnStr(p.string(), v, throw) { + return false + } + if idx := toIdx(p); idx != math.MaxUint32 { + a._setOwnIdx(idx) + } + return true +} + +func (a *templatedArrayObject) defineOwnPropertyStr(name unistring.String, descr PropertyDescriptor, throw bool) bool { + if name == "length" { + return a.val.runtime.defineArrayLength(a.getLenProp(), descr, a.setLength, throw) + } + if !a.templatedObject.defineOwnPropertyStr(name, descr, throw) { + return false + } + if idx := strToArrayIdx(name); idx != math.MaxUint32 { + a._setOwnIdx(idx) + } + return true +} + +func (a *templatedArrayObject) defineOwnPropertyIdx(p valueInt, desc PropertyDescriptor, throw bool) bool { + if !a.templatedObject.defineOwnPropertyStr(p.string(), desc, throw) { + return false + } + if idx := toIdx(p); idx != math.MaxUint32 { + a._setOwnIdx(idx) + } + return true +} diff --git a/runtime.go b/runtime.go index 816037db..578b4679 100644 --- a/runtime.go +++ b/runtime.go @@ -58,7 +58,10 @@ type global struct { Date *Object Symbol *Object Proxy *Object + Reflect *Object Promise *Object + Math *Object + JSON *Object AsyncFunction *Object @@ -123,21 +126,11 @@ type global struct { StringIteratorPrototype *Object RegExpStringIteratorPrototype *Object - ErrorPrototype *Object - AggregateErrorPrototype *Object - TypeErrorPrototype *Object - SyntaxErrorPrototype *Object - RangeErrorPrototype *Object - ReferenceErrorPrototype *Object - EvalErrorPrototype *Object - URIErrorPrototype *Object - - GoErrorPrototype *Object + ErrorPrototype *Object Eval *Object - thrower *Object - throwerProperty Value + thrower *Object stdRegexpProto *guardedObject @@ -147,6 +140,13 @@ type global struct { setAdder *Object arrayValues *Object arrayToString *Object + + stringproto_trimEnd *Object + stringproto_trimStart *Object + + parseFloat, parseInt *Object + + typedArrayValues *Object } type Flag int @@ -399,14 +399,10 @@ func (e *Exception) Value() Value { return e.val } -func (r *Runtime) addToGlobal(name string, value Value) { - r.globalObject.self._putProp(unistring.String(name), value, true, false, true) -} - func (r *Runtime) createIterProto(val *Object) objectImpl { o := newBaseObjectObj(val, r.global.ObjectPrototype, classObject) - o._putSym(SymIterator, valueProp(r.newNativeFunc(r.returnThis, nil, "[Symbol.iterator]", nil, 0), true, false, true)) + o._putSym(SymIterator, valueProp(r.newNativeFunc(r.returnThis, "[Symbol.iterator]", 0), true, false, true)) return o } @@ -423,68 +419,17 @@ func (r *Runtime) getIteratorPrototype() *Object { func (r *Runtime) init() { r.rand = rand.Float64 r.now = time.Now - r.global.ObjectPrototype = r.newBaseObject(nil, classObject).val - r.globalObject = r.NewObject() + + r.global.ObjectPrototype = &Object{runtime: r} + r.newTemplatedObject(getObjectProtoTemplate(), r.global.ObjectPrototype) + + r.globalObject = &Object{runtime: r} + r.newTemplatedObject(getGlobalObjectTemplate(), r.globalObject) r.vm = &vm{ r: r, } r.vm.init() - - funcProto := r.newNativeFunc(func(FunctionCall) Value { - return _undefined - }, nil, " ", nil, 0) - r.global.FunctionPrototype = funcProto - funcProtoObj := funcProto.self.(*nativeFuncObject) - - r.initObject() - r.initFunction() - r.initArray() - r.initString() - r.initGlobalObject() - r.initNumber() - r.initRegExp() - r.initDate() - r.initBoolean() - r.initProxy() - r.initReflect() - - r.initErrors() - - r.global.Eval = r.newNativeFunc(r.builtin_eval, nil, "eval", nil, 1) - r.addToGlobal("eval", r.global.Eval) - - r.initMath() - r.initJSON() - - r.initTypedArrays() - r.initSymbol() - r.initWeakSet() - r.initWeakMap() - r.initMap() - r.initSet() - r.initPromise() - - r.global.thrower = r.newNativeFunc(r.builtin_thrower, nil, "", nil, 0) - r.global.throwerProperty = &valueProperty{ - getterFunc: r.global.thrower, - setterFunc: r.global.thrower, - accessor: true, - } - r.object_freeze(FunctionCall{Arguments: []Value{r.global.thrower}}) - - funcProtoObj._put("caller", &valueProperty{ - getterFunc: r.global.thrower, - setterFunc: r.global.thrower, - accessor: true, - configurable: true, - }) - funcProtoObj._put("arguments", &valueProperty{ - getterFunc: r.global.thrower, - setterFunc: r.global.thrower, - accessor: true, - configurable: true, - }) } func (r *Runtime) typeErrorResult(throw bool, args ...interface{}) { @@ -508,11 +453,11 @@ func (r *Runtime) throwReferenceError(name unistring.String) { } func (r *Runtime) newReferenceError(name unistring.String) Value { - return r.newError(r.global.ReferenceError, "%s is not defined", name) + return r.newError(r.getReferenceError(), "%s is not defined", name) } func (r *Runtime) newSyntaxError(msg string, offset int) Value { - return r.builtin_new(r.global.SyntaxError, []Value{newStringValue(msg)}) + return r.builtin_new(r.getSyntaxError(), []Value{newStringValue(msg)}) } func newBaseObjectObj(obj, proto *Object, class string) *baseObject { @@ -574,11 +519,11 @@ func (r *Runtime) NewTypeError(args ...interface{}) *Object { f, _ := args[0].(string) msg = fmt.Sprintf(f, args[1:]...) } - return r.builtin_new(r.global.TypeError, []Value{newStringValue(msg)}) + return r.builtin_new(r.getTypeError(), []Value{newStringValue(msg)}) } func (r *Runtime) NewGoError(err error) *Object { - e := r.newError(r.global.GoError, err.Error()).(*Object) + e := r.newError(r.getGoError(), err.Error()).(*Object) e.Set("value", err) return e } @@ -634,7 +579,7 @@ func (r *Runtime) initBaseJsFunction(f *baseJsFuncObject, strict bool) { f.val = v f.extensible = true f.strict = strict - f.prototype = r.global.FunctionPrototype + f.prototype = r.getFunctionPrototype() } func (r *Runtime) newMethod(name unistring.String, length int, strict bool) (f *methodFuncObject) { @@ -686,27 +631,6 @@ func (r *Runtime) newAsyncArrowFunc(name unistring.String, length int, strict bo return } -func (r *Runtime) newNativeFuncObj(v *Object, call func(FunctionCall) Value, construct func(args []Value, proto *Object) *Object, name unistring.String, proto *Object, length Value) *nativeFuncObject { - f := &nativeFuncObject{ - baseFuncObject: baseFuncObject{ - baseObject: baseObject{ - class: classFunction, - val: v, - extensible: true, - prototype: r.global.FunctionPrototype, - }, - }, - f: call, - construct: r.wrapNativeConstruct(construct, proto), - } - v.self = f - f.init(name, length) - if proto != nil { - f._putProp("prototype", proto, false, false, false) - } - return f -} - func (r *Runtime) newNativeConstructor(call func(ConstructorCall) *Object, name unistring.String, length int64) *Object { v := &Object{runtime: r} @@ -716,7 +640,7 @@ func (r *Runtime) newNativeConstructor(call func(ConstructorCall) *Object, name class: classFunction, val: v, extensible: true, - prototype: r.global.FunctionPrototype, + prototype: r.getFunctionPrototype(), }, }, } @@ -773,7 +697,7 @@ func (r *Runtime) newNativeFuncAndConstruct(v *Object, call func(call FunctionCa class: classFunction, val: v, extensible: true, - prototype: r.global.FunctionPrototype, + prototype: r.getFunctionPrototype(), }, }, f: call, @@ -788,7 +712,7 @@ func (r *Runtime) newNativeFuncAndConstruct(v *Object, call func(call FunctionCa return f } -func (r *Runtime) newNativeFunc(call func(FunctionCall) Value, construct func(args []Value, proto *Object) *Object, name unistring.String, proto *Object, length int) *Object { +func (r *Runtime) newNativeFunc(call func(FunctionCall) Value, name unistring.String, length int) *Object { v := &Object{runtime: r} f := &nativeFuncObject{ @@ -797,18 +721,13 @@ func (r *Runtime) newNativeFunc(call func(FunctionCall) Value, construct func(ar class: classFunction, val: v, extensible: true, - prototype: r.global.FunctionPrototype, + prototype: r.getFunctionPrototype(), }, }, - f: call, - construct: r.wrapNativeConstruct(construct, proto), + f: call, } v.self = f f.init(name, intToValue(int64(length))) - if proto != nil { - f._putProp("prototype", proto, false, false, false) - proto.self._putProp("constructor", v, true, false, true) - } return v } @@ -823,7 +742,7 @@ func (r *Runtime) newWrappedFunc(value reflect.Value) *Object { class: classFunction, val: v, extensible: true, - prototype: r.global.FunctionPrototype, + prototype: r.getFunctionPrototype(), }, }, f: r.wrapReflectFunc(value), @@ -843,11 +762,11 @@ func (r *Runtime) newNativeFuncConstructObj(v *Object, construct func(args []Val class: classFunction, val: v, extensible: true, - prototype: r.global.FunctionPrototype, + prototype: r.getFunctionPrototype(), }, }, f: r.constructToCall(construct, proto), - construct: r.wrapNativeConstruct(construct, proto), + construct: r.wrapNativeConstruct(construct, v, proto), } f.init(name, intToValue(int64(length))) @@ -857,13 +776,11 @@ func (r *Runtime) newNativeFuncConstructObj(v *Object, construct func(args []Val return f } -func (r *Runtime) newNativeFuncConstruct(construct func(args []Value, proto *Object) *Object, name unistring.String, prototype *Object, length int64) *Object { - return r.newNativeFuncConstructProto(construct, name, prototype, r.global.FunctionPrototype, length) +func (r *Runtime) newNativeFuncConstruct(v *Object, construct func(args []Value, proto *Object) *Object, name unistring.String, prototype *Object, length int64) *Object { + return r.newNativeFuncConstructProto(v, construct, name, prototype, r.getFunctionPrototype(), length) } -func (r *Runtime) newNativeFuncConstructProto(construct func(args []Value, proto *Object) *Object, name unistring.String, prototype, proto *Object, length int64) *Object { - v := &Object{runtime: r} - +func (r *Runtime) newNativeFuncConstructProto(v *Object, construct func(args []Value, proto *Object) *Object, name unistring.String, prototype, proto *Object, length int64) *Object { f := &nativeFuncObject{} f.class = classFunction f.val = v @@ -871,11 +788,10 @@ func (r *Runtime) newNativeFuncConstructProto(construct func(args []Value, proto v.self = f f.prototype = proto f.f = r.constructToCall(construct, prototype) - f.construct = r.wrapNativeConstruct(construct, prototype) + f.construct = r.wrapNativeConstruct(construct, v, prototype) f.init(name, intToValue(length)) if prototype != nil { f._putProp("prototype", prototype, false, false, false) - prototype.self._putProp("constructor", v, true, false, true) } return v } @@ -939,7 +855,7 @@ func (r *Runtime) builtin_newBoolean(args []Value, proto *Object) *Object { } func (r *Runtime) builtin_new(construct *Object, args []Value) *Object { - return r.toConstructor(construct)(args, nil) + return r.toConstructor(construct)(args, construct) } func (r *Runtime) builtin_thrower(call FunctionCall) Value { @@ -1013,21 +929,18 @@ func (r *Runtime) constructToCall(construct func(args []Value, proto *Object) *O } } -func (r *Runtime) wrapNativeConstruct(c func(args []Value, proto *Object) *Object, proto *Object) func(args []Value, newTarget *Object) *Object { +func (r *Runtime) wrapNativeConstruct(c func(args []Value, proto *Object) *Object, ctorObj, defProto *Object) func(args []Value, newTarget *Object) *Object { if c == nil { return nil } return func(args []Value, newTarget *Object) *Object { - var p *Object + var proto *Object if newTarget != nil { - if pp, ok := newTarget.self.getStr("prototype", nil).(*Object); ok { - p = pp - } - } - if p == nil { - p = proto + proto = r.getPrototypeFromCtor(newTarget, ctorObj, defProto) + } else { + proto = defProto } - return c(args, p) + return c(args, proto) } } @@ -1289,7 +1202,7 @@ repeat: return uint32(intVal) } fail: - panic(r.newError(r.global.RangeError, "Invalid array length")) + panic(r.newError(r.getRangeError(), "Invalid array length")) } func toIntStrict(i int64) int { @@ -1317,11 +1230,11 @@ func (r *Runtime) toIndex(v Value) int { num := v.ToInteger() if num >= 0 && num < maxInt { if bits.UintSize == 32 && num >= math.MaxInt32 { - panic(r.newError(r.global.RangeError, "Index %s overflows int", v.String())) + panic(r.newError(r.getRangeError(), "Index %s overflows int", v.String())) } return int(num) } - panic(r.newError(r.global.RangeError, "Invalid index %s", v.String())) + panic(r.newError(r.getRangeError(), "Invalid index %s", v.String())) } func (r *Runtime) toBoolean(b bool) Value { @@ -1422,11 +1335,11 @@ func (r *Runtime) compile(name, src string, strict, inGlobal bool, evalVm *vm) ( switch x1 := err.(type) { case *CompilerSyntaxError: err = &Exception{ - val: r.builtin_new(r.global.SyntaxError, []Value{newStringValue(x1.Error())}), + val: r.builtin_new(r.getSyntaxError(), []Value{newStringValue(x1.Error())}), } case *CompilerReferenceError: err = &Exception{ - val: r.newError(r.global.ReferenceError, x1.Message), + val: r.newError(r.getReferenceError(), x1.Message), } // TODO proper message } } @@ -1854,12 +1767,12 @@ func (r *Runtime) toValue(i interface{}, origValue reflect.Value) Value { } case func(FunctionCall) Value: name := unistring.NewFromString(runtime.FuncForPC(reflect.ValueOf(i).Pointer()).Name()) - return r.newNativeFunc(i, nil, name, nil, 0) + return r.newNativeFunc(i, name, 0) case func(FunctionCall, *Runtime) Value: name := unistring.NewFromString(runtime.FuncForPC(reflect.ValueOf(i).Pointer()).Name()) return r.newNativeFunc(func(call FunctionCall) Value { return i(call, r) - }, nil, name, nil, 0) + }, name, 0) case func(ConstructorCall) *Object: name := unistring.NewFromString(runtime.FuncForPC(reflect.ValueOf(i).Pointer()).Name()) return r.newNativeConstructor(i, name, 0) @@ -2810,16 +2723,6 @@ func (r *Runtime) createIterResultObject(value Value, done bool) Value { return o } -func (r *Runtime) newLazyObject(create func(*Object) objectImpl) *Object { - val := &Object{runtime: r} - o := &lazyObject{ - val: val, - create: create, - } - val.self = o - return val -} - func (r *Runtime) getHash() *maphash.Hash { if r.hash == nil { r.hash = &maphash.Hash{} @@ -2979,7 +2882,7 @@ func (r *Runtime) iterableToList(iterable Value, method func(FunctionCall) Value func (r *Runtime) putSpeciesReturnThis(o objectImpl) { o._putSym(SymSpecies, &valueProperty{ - getterFunc: r.newNativeFunc(r.returnThis, nil, "get [Symbol.species]", nil, 0), + getterFunc: r.newNativeFunc(r.returnThis, "get [Symbol.species]", 0), accessor: true, configurable: true, }) @@ -3209,3 +3112,18 @@ func assertCallable(v Value) (func(FunctionCall) Value, bool) { func (r *Runtime) InstanceOf(left Value, right *Object) (res bool) { return instanceOfOperator(left, right) } + +func (r *Runtime) methodProp(f func(FunctionCall) Value, name unistring.String, nArgs int) Value { + return valueProp(r.newNativeFunc(f, name, nArgs), true, false, true) +} + +func (r *Runtime) getPrototypeFromCtor(newTarget, defCtor, defProto *Object) *Object { + if newTarget == defCtor { + return defProto + } + proto := newTarget.self.getStr("prototype", nil) + if obj, ok := proto.(*Object); ok { + return obj + } + return defProto +} diff --git a/runtime_test.go b/runtime_test.go index b0c3b248..b084a4a3 100644 --- a/runtime_test.go +++ b/runtime_test.go @@ -3028,3 +3028,10 @@ func BenchmarkAsciiStringMapGet(b *testing.B) { } } } + +func BenchmarkNew(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + New() + } +} diff --git a/string_ascii.go b/string_ascii.go index 0728f1e1..5ff21bf7 100644 --- a/string_ascii.go +++ b/string_ascii.go @@ -209,7 +209,7 @@ func (s asciiString) ToNumber() Value { } func (s asciiString) ToObject(r *Runtime) *Object { - return r._newString(s, r.global.StringPrototype) + return r._newString(s, r.getStringPrototype()) } func (s asciiString) SameAs(other Value) bool { @@ -258,7 +258,7 @@ func (s asciiString) StrictEquals(other Value) bool { } func (s asciiString) baseObject(r *Runtime) *Object { - ss := r.stringSingleton + ss := r.getStringSingleton() ss.value = s ss.setLength() return ss.val diff --git a/string_imported.go b/string_imported.go index e78ee162..1c6cae88 100644 --- a/string_imported.go +++ b/string_imported.go @@ -88,7 +88,7 @@ func (i *importedString) ToBoolean() bool { } func (i *importedString) ToObject(r *Runtime) *Object { - return r._newString(i, r.global.StringPrototype) + return r._newString(i, r.getStringPrototype()) } func (i *importedString) SameAs(other Value) bool { diff --git a/string_unicode.go b/string_unicode.go index e3d9d96e..49e363fe 100644 --- a/string_unicode.go +++ b/string_unicode.go @@ -402,7 +402,7 @@ func (s unicodeString) ToNumber() Value { } func (s unicodeString) ToObject(r *Runtime) *Object { - return r._newString(s, r.global.StringPrototype) + return r._newString(s, r.getStringPrototype()) } func (s unicodeString) equals(other unicodeString) bool { @@ -447,7 +447,7 @@ func (s unicodeString) StrictEquals(other Value) bool { } func (s unicodeString) baseObject(r *Runtime) *Object { - ss := r.stringSingleton + ss := r.getStringSingleton() ss.value = s ss.setLength() return ss.val diff --git a/typedarrays.go b/typedarrays.go index bb2b3435..132cbf45 100644 --- a/typedarrays.go +++ b/typedarrays.go @@ -901,6 +901,8 @@ func (r *Runtime) _newTypedArrayObject(buf *arrayBufferObject, offset, length, e } func (r *Runtime) newUint8ArrayObject(buf *arrayBufferObject, offset, length int, proto *Object) *typedArrayObject { + // Note, no need to use r.getUint8Array() here or in the similar methods below, because the value is already set + // by the time they are called. return r._newTypedArrayObject(buf, offset, length, 1, r.global.Uint8Array, (*uint8Array)(&buf.data), proto) } @@ -939,7 +941,7 @@ func (r *Runtime) newFloat64ArrayObject(buf *arrayBufferObject, offset, length i func (o *dataViewObject) getIdxAndByteOrder(getIdx int, littleEndianVal Value, size int) (int, byteOrder) { o.viewedArrayBuf.ensureNotDetached(true) if getIdx+size > o.byteLen { - panic(o.val.runtime.newError(o.val.runtime.global.RangeError, "Index %d is out of bounds", getIdx)) + panic(o.val.runtime.newError(o.val.runtime.getRangeError(), "Index %d is out of bounds", getIdx)) } getIdx += o.byteOffset var bo byteOrder diff --git a/value.go b/value.go index 987b9c83..aeb96762 100644 --- a/value.go +++ b/value.go @@ -203,7 +203,7 @@ func (i valueInt) ToBoolean() bool { } func (i valueInt) ToObject(r *Runtime) *Object { - return r.newPrimitiveObject(i, r.global.NumberPrototype, classNumber) + return r.newPrimitiveObject(i, r.getNumberPrototype(), classNumber) } func (i valueInt) ToNumber() Value { @@ -243,7 +243,7 @@ func (i valueInt) StrictEquals(other Value) bool { } func (i valueInt) baseObject(r *Runtime) *Object { - return r.global.NumberPrototype + return r.getNumberPrototype() } func (i valueInt) Export() interface{} { @@ -299,7 +299,7 @@ func (b valueBool) ToBoolean() bool { } func (b valueBool) ToObject(r *Runtime) *Object { - return r.newPrimitiveObject(b, r.global.BooleanPrototype, "Boolean") + return r.newPrimitiveObject(b, r.getBooleanPrototype(), "Boolean") } func (b valueBool) ToNumber() Value { @@ -337,7 +337,7 @@ func (b valueBool) StrictEquals(other Value) bool { } func (b valueBool) baseObject(r *Runtime) *Object { - return r.global.BooleanPrototype + return r.getBooleanPrototype() } func (b valueBool) Export() interface{} { @@ -604,7 +604,7 @@ func (f valueFloat) ToBoolean() bool { } func (f valueFloat) ToObject(r *Runtime) *Object { - return r.newPrimitiveObject(f, r.global.NumberPrototype, "Number") + return r.newPrimitiveObject(f, r.getNumberPrototype(), "Number") } func (f valueFloat) ToNumber() Value { @@ -664,7 +664,7 @@ func (f valueFloat) StrictEquals(other Value) bool { } func (f valueFloat) baseObject(r *Runtime) *Object { - return r.global.NumberPrototype + return r.getNumberPrototype() } func (f valueFloat) Export() interface{} { @@ -1097,7 +1097,7 @@ func (s *Symbol) ExportType() reflect.Type { } func (s *Symbol) baseObject(r *Runtime) *Object { - return r.newPrimitiveObject(s, r.global.SymbolPrototype, classObject) + return r.newPrimitiveObject(s, r.getSymbolPrototype(), classObject) } func (s *Symbol) hash(*maphash.Hash) uint64 { diff --git a/vm.go b/vm.go index 2dbd705f..dd328444 100644 --- a/vm.go +++ b/vm.go @@ -2475,7 +2475,7 @@ func (_pushArrayItem) exec(vm *vm) { if arr.length < math.MaxUint32 { arr.length++ } else { - vm.throw(vm.r.newError(vm.r.global.RangeError, "Invalid array length")) + vm.throw(vm.r.newError(vm.r.getRangeError(), "Invalid array length")) return } val := vm.stack[vm.sp-1] @@ -2497,7 +2497,7 @@ func (_pushArraySpread) exec(vm *vm) { if arr.length < math.MaxUint32 { arr.length++ } else { - vm.throw(vm.r.newError(vm.r.global.RangeError, "Invalid array length")) + vm.throw(vm.r.newError(vm.r.getRangeError(), "Invalid array length")) return } arr.values = append(arr.values, val) @@ -2545,7 +2545,7 @@ type newRegexp struct { } func (n *newRegexp) exec(vm *vm) { - vm.push(vm.r.newRegExpp(n.pattern.clone(), n.src, vm.r.global.RegExpPrototype).val) + vm.push(vm.r.newRegExpp(n.pattern.clone(), n.src, vm.r.getRegExpPrototype()).val) vm.pc++ } @@ -3828,7 +3828,7 @@ func (n *newAsyncArrowFunc) exec(vm *vm) { } func (vm *vm) alreadyDeclared(name unistring.String) Value { - return vm.r.newError(vm.r.global.SyntaxError, "Identifier '%s' has already been declared", name) + return vm.r.newError(vm.r.getSyntaxError(), "Identifier '%s' has already been declared", name) } func (vm *vm) checkBindVarsGlobal(names []unistring.String) { @@ -4598,7 +4598,7 @@ func (formalArgs createArgsMapped) exec(vm *vm) { } args._putProp("callee", vm.stack[vm.sb-1], true, false, true) - args._putSym(SymIterator, valueProp(vm.r.global.arrayValues, true, false, true)) + args._putSym(SymIterator, valueProp(vm.r.getArrayValues(), true, false, true)) vm.push(v) vm.pc++ } @@ -4623,8 +4623,8 @@ func (formalArgs createArgsUnmapped) exec(vm *vm) { } args._putProp("length", intToValue(int64(vm.args)), true, false, true) - args._put("callee", vm.r.global.throwerProperty) - args._putSym(SymIterator, valueProp(vm.r.global.arrayValues, true, false, true)) + args._put("callee", vm.r.newThrowerProperty(false)) + args._putSym(SymIterator, valueProp(vm.r.getArrayValues(), true, false, true)) vm.push(args.val) vm.pc++ } @@ -5057,7 +5057,7 @@ func (c *newClass) create(protoParent, ctorParent *Object, vm *vm, derived bool) } func (c *newClass) exec(vm *vm) { - proto, cls := c.create(vm.r.global.ObjectPrototype, vm.r.global.FunctionPrototype, vm, false) + proto, cls := c.create(vm.r.global.ObjectPrototype, vm.r.getFunctionPrototype(), vm, false) sp := vm.sp vm.stack.expand(sp + 1) vm.stack[sp] = proto @@ -5086,7 +5086,7 @@ func (c *newDerivedClass) exec(vm *vm) { superClass = sc } } else { - superClass = vm.r.global.FunctionPrototype + superClass = vm.r.getFunctionPrototype() } proto, cls := c.create(protoParent, superClass, vm, true) @@ -5103,7 +5103,7 @@ type newStaticFieldInit struct { } func (c *newStaticFieldInit) exec(vm *vm) { - f := vm.r.newClassFunc("", 0, vm.r.global.FunctionPrototype, false) + f := vm.r.newClassFunc("", 0, vm.r.getFunctionPrototype(), false) if c.numPrivateFields > 0 || c.numPrivateMethods > 0 { vm.createPrivateType(f, c.numPrivateFields, c.numPrivateMethods) } @@ -5117,7 +5117,7 @@ func (vm *vm) loadThis(v Value) { if v != nil { vm.push(v) } else { - vm.throw(vm.r.newError(vm.r.global.ReferenceError, "Must call super constructor in derived class before accessing 'this'")) + vm.throw(vm.r.newError(vm.r.getReferenceError(), "Must call super constructor in derived class before accessing 'this'")) return } vm.pc++ @@ -5202,7 +5202,7 @@ func (resolveThisDynamic) exec(vm *vm) { } } } - panic(vm.r.newError(vm.r.global.ReferenceError, "Compiler bug: 'this' reference is not found in resolveThisDynamic")) + panic(vm.r.newError(vm.r.getReferenceError(), "Compiler bug: 'this' reference is not found in resolveThisDynamic")) } type defineComputedKey int @@ -5464,15 +5464,15 @@ func (vm *vm) exceptionFromValue(x interface{}) *Exception { } case referenceError: ex = &Exception{ - val: vm.r.newError(vm.r.global.ReferenceError, string(x1)), + val: vm.r.newError(vm.r.getReferenceError(), string(x1)), } case rangeError: ex = &Exception{ - val: vm.r.newError(vm.r.global.RangeError, string(x1)), + val: vm.r.newError(vm.r.getRangeError(), string(x1)), } case syntaxError: ex = &Exception{ - val: vm.r.newError(vm.r.global.SyntaxError, string(x1)), + val: vm.r.newError(vm.r.getSyntaxError(), string(x1)), } default: /*