diff --git a/added_values.go b/added_values.go index e53278ca..242bbe37 100644 --- a/added_values.go +++ b/added_values.go @@ -126,7 +126,7 @@ func (i valueNumber) ToBoolean() bool { } func (i valueNumber) ToObject(r *Runtime) *Object { - return r.newPrimitiveObject(i, r.global.NumberPrototype, classNumber) + return r.newPrimitiveObject(i, r.getNumberPrototype(), classNumber) } func (i valueNumber) ToNumber() Value { @@ -158,7 +158,7 @@ func (i valueNumber) Equals(other Value) bool { return i.ToInt() == o.ToInt() } if o, ok := other.(*Object); ok { - return i.Equals(o.self.toPrimitiveNumber()) + return i.Equals(o.toPrimitiveNumber()) } return false } @@ -194,7 +194,7 @@ func (i valueNumber) assertString() (valueString, bool) { } func (i valueNumber) baseObject(r *Runtime) *Object { - return r.global.NumberPrototype + return r.getNumberPrototype() } func (i valueNumber) Export() interface{} { @@ -256,7 +256,7 @@ func (i valueUInt32) ToBoolean() bool { } func (i valueUInt32) ToObject(r *Runtime) *Object { - return r.newPrimitiveObject(i, r.global.NumberPrototype, classNumber) + return r.newPrimitiveObject(i, r.getNumberPrototype(), classNumber) } func (i valueUInt32) ToNumber() Value { @@ -284,7 +284,7 @@ func (i valueUInt32) Equals(other Value) bool { return int(i) == o.ToInt() } if o, ok := other.(*Object); ok { - return i.Equals(o.self.toPrimitiveNumber()) + return i.Equals(o.toPrimitiveNumber()) } return false } @@ -324,7 +324,7 @@ func (i valueUInt32) assertString() (valueString, bool) { } func (i valueUInt32) baseObject(r *Runtime) *Object { - return r.global.NumberPrototype + return r.getNumberPrototype() } func (i valueUInt32) Export() interface{} { @@ -389,7 +389,7 @@ func (i valueInt32) ToBoolean() bool { } func (i valueInt32) ToObject(r *Runtime) *Object { - return r.newPrimitiveObject(i, r.global.NumberPrototype, classNumber) + return r.newPrimitiveObject(i, r.getNumberPrototype(), classNumber) } func (i valueInt32) ToNumber() Value { @@ -417,7 +417,7 @@ func (i valueInt32) Equals(other Value) bool { return int(i) == o.ToInt() } if o, ok := other.(*Object); ok { - return i.Equals(o.self.toPrimitiveNumber()) + return i.Equals(o.toPrimitiveNumber()) } return false } @@ -453,7 +453,7 @@ func (i valueInt32) assertString() (valueString, bool) { } func (i valueInt32) baseObject(r *Runtime) *Object { - return r.global.NumberPrototype + return r.getNumberPrototype() } func (i valueInt32) Export() interface{} { @@ -516,7 +516,7 @@ func (i valueInt64) ToBoolean() bool { } func (i valueInt64) ToObject(r *Runtime) *Object { - return r.newPrimitiveObject(i, r.global.NumberPrototype, classNumber) + return r.newPrimitiveObject(i, r.getNumberPrototype(), classNumber) } func (i valueInt64) ToNumber() Value { @@ -544,7 +544,7 @@ func (i valueInt64) Equals(other Value) bool { return int(i) == o.ToInt() } if o, ok := other.(*Object); ok { - return i.Equals(o.self.toPrimitiveNumber()) + return i.Equals(o.toPrimitiveNumber()) } return false } @@ -582,7 +582,7 @@ func (i valueInt64) hash(*maphash.Hash) uint64 { } func (i valueInt64) baseObject(r *Runtime) *Object { - return r.global.NumberPrototype + return r.getNumberPrototype() } func (i valueInt64) Export() interface{} { diff --git a/array.go b/array.go index dffc6922..f8dfc839 100644 --- a/array.go +++ b/array.go @@ -57,7 +57,7 @@ func (r *Runtime) createArrayIterator(iterObj *Object, kind iterationKind) Value ai.val = o ai.extensible = true o.self = ai - ai.prototype = r.global.ArrayIteratorPrototype + ai.prototype = r.getArrayIteratorPrototype() ai.init() return o @@ -333,6 +333,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)) { @@ -510,7 +522,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 8677fa93..61049888 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 a28ba9fa..0a8e6c53 100644 --- a/builtin_array.go +++ b/builtin_array.go @@ -4,6 +4,7 @@ import ( "context" "math" "sort" + "sync" ) func (r *Runtime) newArray(prototype *Object) (a *arrayObject) { @@ -20,7 +21,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 { @@ -99,7 +100,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 @@ -222,6 +223,16 @@ func getElementValueString(elem *Object, o *Object) valueString { func (r *Runtime) arrayproto_toString(call FunctionCall) Value { array := call.This.ToObject(r) + var toString func() Value + switch a := array.self.(type) { + case *objectGoSliceReflect: + toString = a.toString + case *objectGoArrayReflect: + toString = a.toString + } + if toString != nil { + return toString() + } f := array.self.getStr("join", nil) if fObj, ok := f.(*Object); ok { if fcall, ok := fObj.self.assertCallable(); ok { @@ -1277,7 +1288,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 } @@ -1416,113 +1427,151 @@ 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 } -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._putSym(SymSpecies, &valueProperty{ - getterFunc: r.newNativeFunc(r.returnThis, nil, "get [Symbol.species]", nil, 0), - accessor: true, - configurable: true, +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.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 } func (r *Runtime) createArrayIterProto(val *Object) objectImpl { - o := newBaseObjectObj(val, r.global.IteratorPrototype, classObject) + 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) +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.ArrayIteratorPrototype = r.newLazyObject(r.createArrayIterProto) - //r.global.ArrayPrototype = r.newArray(r.global.ObjectPrototype).val - //o := r.global.ArrayPrototype.self - r.global.ArrayPrototype = r.newLazyObject(r.createArrayProto) +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 +} - //r.global.Array = r.newNativeFuncConstruct(r.builtin_newArray, "Array", r.global.ArrayPrototype, 1) - //o = r.global.Array.self - //o._putProp("isArray", r.newNativeFunc(r.array_isArray, nil, "isArray", nil, 1), true, false, true) - r.global.Array = r.newLazyObject(r.createArray) +func (r *Runtime) getArrayIteratorPrototype() *Object { + var o *Object + if o = r.global.ArrayIteratorPrototype; o == nil { + o = &Object{runtime: r} + r.global.ArrayIteratorPrototype = o + o.self = r.createArrayIterProto(o) + } + return o - r.addToGlobal("Array", r.global.Array) } type sortable interface { diff --git a/builtin_arrray_test.go b/builtin_arrray_test.go index 4cbc3138..8ef91612 100644 --- a/builtin_arrray_test.go +++ b/builtin_arrray_test.go @@ -322,3 +322,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 df8d18cf..84763285 100644 --- a/builtin_boolean.go +++ b/builtin_boolean.go @@ -13,6 +13,11 @@ func (r *Runtime) booleanproto_toString(call FunctionCall) Value { goto success } } + if o, ok := o.self.(*objectGoReflect); ok { + if o.class == classBoolean && o.toString != nil { + return o.toString() + } + } } r.typeErrorResult(true, "Method Boolean.prototype.toString is called on incompatible receiver") @@ -33,18 +38,38 @@ func (r *Runtime) booleanproto_valueOf(call FunctionCall) Value { return b } } + if o, ok := o.self.(*objectGoReflect); ok { + if o.class == classBoolean && o.valueOf != nil { + return o.valueOf() + } + } } r.typeErrorResult(true, "Method Boolean.prototype.valueOf is called on incompatible receiver") 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 a18efbae..305145b8 100644 --- a/builtin_date.go +++ b/builtin_date.go @@ -3,6 +3,7 @@ package goja import ( "fmt" "math" + "sync" "time" ) @@ -135,7 +136,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")) @@ -168,10 +169,10 @@ func (r *Runtime) dateproto_toPrimitive(call FunctionCall) Value { arg := call.Argument(0) if asciiString("string").StrictEquals(arg) || asciiString("default").StrictEquals(arg) { - return o.self.toPrimitiveString() + return o.ordinaryToPrimitiveString() } if asciiString("number").StrictEquals(arg) { - return o.self.toPrimitiveNumber() + return o.ordinaryToPrimitiveNumber() } panic(r.NewTypeError("Invalid hint: %s", arg)) } @@ -942,78 +943,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 e2773035..ead6addf 100644 --- a/builtin_error.go +++ b/builtin_error.go @@ -177,61 +177,114 @@ func (r *Runtime) error_toString(call FunctionCall) Value { return sb.String() } -func (r *Runtime) createErrorPrototype(name valueString) *Object { - o := r.newBaseObject(r.global.ErrorPrototype, classObject) +func (r *Runtime) createErrorPrototype(name valueString, 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 667b30b2..7e47bc04 100644 --- a/builtin_function.go +++ b/builtin_function.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "math" + "sync" ) func (r *Runtime) builtin_Function(args []Value, proto *Object) *Object { @@ -29,39 +30,22 @@ func (r *Runtime) builtin_Function(args []Value, proto *Object) *Object { return ret } -func nativeFuncString(f *nativeFuncObject) Value { +func (f *nativeFuncObject) source() valueString { return newStringValue(fmt.Sprintf("function %s() { [native code] }", nilSafe(f.getStr("name", nil)).toString())) } +func (f *baseJsFuncObject) source() valueString { + return newStringValue(f.src) +} + func (r *Runtime) functionproto_toString(call FunctionCall) Value { obj := r.toObject(call.This) -repeat: switch f := obj.self.(type) { - case *funcObject: - return newStringValue(f.src) - case *classFuncObject: - return newStringValue(f.src) - case *methodFuncObject: - return newStringValue(f.src) - case *arrowFuncObject: - return newStringValue(f.src) - case *nativeFuncObject: - return nativeFuncString(f) - case *boundFuncObject: - return nativeFuncString(&f.nativeFuncObject) - case *wrappedFuncObject: - return nativeFuncString(&f.nativeFuncObject) - case *lazyObject: - obj.self = f.create(obj) - goto repeat + case funcObjectImpl: + return f.source() case *proxyObject: - repeat2: - switch c := f.target.self.(type) { - case *classFuncObject, *methodFuncObject, *funcObject, *arrowFuncObject, *nativeFuncObject, *boundFuncObject: + if _, ok := f.target.self.(funcObjectImpl); ok { return asciiString("function () { [native code] }") - case *lazyObject: - f.target.self = c.create(obj) - goto repeat2 } } panic(r.NewTypeError("Function.prototype.toString requires that 'this' be a Function")) @@ -212,16 +196,85 @@ 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 +} + +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) + }) - r.global.Function = r.newNativeFuncConstruct(r.builtin_Function, "Function", r.global.FunctionPrototype, 1) - r.addToGlobal("Function", r.global.Function) + 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) 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 637674d3..d19612b5 100644 --- a/builtin_global.go +++ b/builtin_global.go @@ -2,13 +2,15 @@ package goja import ( "errors" - "github.com/dop251/goja/unistring" "io" "math" "regexp" "strconv" "strings" + "sync" "unicode/utf8" + + "github.com/dop251/goja/unistring" ) const hexUpper = "0123456789ABCDEF" @@ -70,7 +72,7 @@ func (r *Runtime) _encode(uriString valueString, unescaped *[256]bool) valueStri 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 +129,7 @@ func (r *Runtime) _decode(sv valueString, reservedSet *[256]bool) valueString { 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 +185,7 @@ func (r *Runtime) _decode(sv valueString, reservedSet *[256]bool) valueString { 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 +329,85 @@ 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) }) + // DIVERSION: we are not supporting Goja Promises + //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 cf4cdb6d..f731fa6d 100644 --- a/builtin_json.go +++ b/builtin_json.go @@ -21,11 +21,11 @@ func (r *Runtime) builtinJSON_parse(call FunctionCall) Value { value, err := r.builtinJSON_decodeValue(d) 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 @@ -326,9 +326,9 @@ func (ctx *_builtinJSON_stringifyContext) str(key Value, holder *Object) bool { } else { switch o1.className() { case classNumber: - value = o1.toPrimitiveNumber() + value = o1.val.ordinaryToPrimitiveNumber() case classString: - value = o1.toPrimitiveString() + value = o1.val.ordinaryToPrimitiveString() case classBoolean: if o.ToInteger() != 0 { value = valueTrue @@ -534,11 +534,15 @@ func (ctx *_builtinJSON_stringifyContext) quote(str valueString) { ctx.buf.WriteByte('"') } -func (r *Runtime) initJSON() { - JSON := r.newBaseObject(r.global.ObjectPrototype, "JSON") - 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 5d3cecd5..a5b0586f 100644 --- a/builtin_map.go +++ b/builtin_map.go @@ -300,7 +300,7 @@ func (r *Runtime) createMapIterator(mapValue Value, kind iterationKind) Value { mi.val = o mi.extensible = true o.self = mi - mi.prototype = r.global.MapIteratorPrototype + mi.prototype = r.getMapIteratorPrototype() mi.init() return o @@ -317,24 +317,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)) @@ -343,30 +343,47 @@ 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._putSym(SymSpecies, &valueProperty{ - getterFunc: r.newNativeFunc(r.returnThis, nil, "get [Symbol.species]", nil, 0), - accessor: true, - configurable: true, - }) + o := r.newNativeConstructOnly(val, r.builtin_newMap, r.getMapPrototype(), "Map", 0) + r.putSpeciesReturnThis(o) return o } func (r *Runtime) createMapIterProto(val *Object) objectImpl { - o := newBaseObjectObj(val, r.global.IteratorPrototype, classObject) + 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 } -func (r *Runtime) initMap() { - r.global.MapIteratorPrototype = r.newLazyObject(r.createMapIterProto) +func (r *Runtime) getMapIteratorPrototype() *Object { + var o *Object + if o = r.global.MapIteratorPrototype; o == nil { + o = &Object{runtime: r} + r.global.MapIteratorPrototype = o + o.self = r.createMapIterProto(o) + } + return o +} - 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 436b24d0..86e50d0c 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 { @@ -297,64 +298,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: classMath, - 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 85c94078..26d13b8a 100644 --- a/builtin_number.go +++ b/builtin_number.go @@ -2,71 +2,60 @@ package goja import ( "math" + "sync" "github.com/dop251/goja/ftoa" ) -func (r *Runtime) numberproto_valueOf(call FunctionCall) Value { - this := call.This - if !isNumber(this) { - r.typeErrorResult(true, "Value is not a number") - } - if x, ok := this.assertInt64(); ok { - return valueNumber{ - val: x, - _type: reflectTypeInt64, - } - } - if x, ok := this.assertInt32(); ok { - return valueNumber{ - val: x, - _type: reflectTypeInt32, - } - } - if x, ok := this.assertUInt32(); ok { - return valueNumber{ - val: x, - _type: reflectTypeUInt32, - } - } - if x, ok := this.assertInt(); ok { - return valueNumber{ - val: x, - _type: reflectTypeInt, - } - } - switch t := this.(type) { - case valueInt, valueFloat, valueInt64: - return this +func (r *Runtime) toNumber(v Value) Value { + switch t := v.(type) { + case valueFloat, valueInt, valueInt64: + return v case *Object: - if v, ok := t.self.(*primitiveValueObject); ok { - return v.pValue + switch t := t.self.(type) { + case *primitiveValueObject: + return r.toNumber(t.pValue) + case *objectGoReflect: + if t.class == classNumber && t.valueOf != nil { + return t.valueOf() + } + } + if t == r.global.NumberPrototype { + return _positiveZero } } - - panic(r.NewTypeError("Number.prototype.valueOf is not generic")) + panic(r.NewTypeError("Value is not a number: %s", v)) } -func IsNumber(v Value) bool { - return isNumber(v) +func (r *Runtime) numberproto_valueOf(call FunctionCall) Value { + return r.toNumber(call.This) } -func isNumber(v Value) bool { - switch t := v.(type) { - case valueFloat, valueInt, valueInt32, valueInt64, valueUInt32, valueNumber: - return true +func (r *Runtime) numberproto_toString(call FunctionCall) Value { + var numVal Value + switch t := call.This.(type) { + case valueFloat, valueInt, valueInt64: + numVal = t case *Object: switch t := t.self.(type) { case *primitiveValueObject: - return isNumber(t.pValue) + numVal = r.toNumber(t.pValue) + case *objectGoReflect: + if t.class == classNumber { + if t.toString != nil { + return t.toString() + } + if t.valueOf != nil { + numVal = t.valueOf() + } + } + } + if t == r.global.NumberPrototype { + return asciiString("0") } } - return false -} - -func (r *Runtime) numberproto_toString(call FunctionCall) Value { - if !isNumber(call.This) { - r.typeErrorResult(true, "Value is not a number") + if numVal == nil { + panic(r.NewTypeError("Value is not a number")) } var radix int if arg := call.Argument(0); arg != _undefined { @@ -76,10 +65,10 @@ 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 := call.This.ToFloat() + num := numVal.ToFloat() if math.IsNaN(num) { return stringNaN @@ -105,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 @@ -134,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))) @@ -159,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))) @@ -218,32 +207,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 a6dae6d4..f2a80891 100644 --- a/builtin_object.go +++ b/builtin_object.go @@ -1,7 +1,9 @@ package goja +import "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 } @@ -476,6 +478,11 @@ func (r *Runtime) objectproto_toString(call FunctionCall) Value { if obj == nil { return newStringValue("[object Object]") } + if o, ok := obj.self.(*objectGoReflect); ok { + if toString := o.toString; toString != nil { + return toString() + } + } var clsName string if isArray(obj) { clsName = classArray @@ -604,52 +611,117 @@ 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) - o._putProp("__defineSetter__", r.newNativeFunc(r.object_defineSetter, nil, "__defineSetter__", nil, 2), true, false, 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) - - entriesFunc := r.newNativeFunc(r.object_entries, nil, "entries", nil, 1) - o._putSym(SymIterator, valueProp(entriesFunc, true, false, true)) - o._putSym(SymToStringTag, valueProp(asciiString(classObject), false, false, true)) - - o._putProp("entries", r.newNativeFunc(r.object_entries, nil, "entries", nil, 1), 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, + } + }) + t.putStr("__defineSetter__", func(r *Runtime) Value { return r.methodProp(r.object_defineSetter, "__defineSetter__", 2) }) + + return t } diff --git a/builtin_proxy.go b/builtin_proxy.go index ee2e7822..bde5318b 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,13 +381,18 @@ 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 } func (h *nativeProxyHandler) MemUsage(ctx *MemUsageContext) (memUsage uint64, newMemUsage uint64, err error) { diff --git a/builtin_reflect.go b/builtin_reflect.go index 2f28bfe7..b48fe943 100644 --- a/builtin_reflect.go +++ b/builtin_reflect.go @@ -112,25 +112,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 cc3f2428..390e209d 100644 --- a/builtin_regexp.go +++ b/builtin_regexp.go @@ -355,7 +355,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 { @@ -780,7 +780,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() @@ -802,7 +802,7 @@ func (r *Runtime) createRegExpStringIterator(matcher *Object, s valueString, glo ri.val = o ri.extensible = true o.self = ri - ri.prototype = r.global.RegExpStringIteratorPrototype + ri.prototype = r.getRegExpStringIteratorPrototype() ri.init() return o @@ -972,7 +972,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() @@ -1222,73 +1222,93 @@ func (r *Runtime) regExpStringIteratorProto_next(call FunctionCall) Value { } func (r *Runtime) createRegExpStringIteratorPrototype(val *Object) objectImpl { - o := newBaseObjectObj(val, r.global.IteratorPrototype, classObject) + 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 } -func (r *Runtime) initRegExp() { - o := r.newGuardedObject(r.global.ObjectPrototype, classObject) - r.global.RegExpPrototype = o.val - r.global.stdRegexpProto = o - r.global.RegExpStringIteratorPrototype = r.newLazyObject(r.createRegExpStringIteratorPrototype) - - 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 - rx._putSym(SymSpecies, &valueProperty{ - getterFunc: r.newNativeFunc(r.returnThis, nil, "get [Symbol.species]", nil, 0), - accessor: true, - configurable: true, - }) - r.addToGlobal("RegExp", r.global.RegExp) +func (r *Runtime) getRegExpStringIteratorPrototype() *Object { + var o *Object + if o = r.global.RegExpStringIteratorPrototype; o == nil { + o = &Object{runtime: r} + r.global.RegExpStringIteratorPrototype = o + o.self = r.createRegExpStringIteratorPrototype(o) + } + return o +} + +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 92cbbc05..4facb329 100644 --- a/builtin_set.go +++ b/builtin_set.go @@ -257,7 +257,7 @@ func (r *Runtime) createSetIterator(setValue Value, kind iterationKind) Value { si.val = o si.extensible = true o.self = si - si.prototype = r.global.SetIteratorPrototype + si.prototype = r.getSetIteratorPrototype() si.init() return o @@ -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,30 +300,47 @@ 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._putSym(SymSpecies, &valueProperty{ - getterFunc: r.newNativeFunc(r.returnThis, nil, "get [Symbol.species]", nil, 0), - accessor: true, - configurable: true, - }) + o := r.newNativeConstructOnly(val, r.builtin_newSet, r.getSetPrototype(), "Set", 0) + r.putSpeciesReturnThis(o) return o } func (r *Runtime) createSetIterProto(val *Object) objectImpl { - o := newBaseObjectObj(val, r.global.IteratorPrototype, classObject) + 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 } -func (r *Runtime) initSet() { - r.global.SetIteratorPrototype = r.newLazyObject(r.createSetIterProto) +func (r *Runtime) getSetIteratorPrototype() *Object { + var o *Object + if o = r.global.SetIteratorPrototype; o == nil { + o = &Object{runtime: r} + r.global.SetIteratorPrototype = o + o.self = r.createSetIterProto(o) + } + return o +} - 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 5b46fb66..fc8f65d0 100644 --- a/builtin_string.go +++ b/builtin_string.go @@ -4,6 +4,7 @@ import ( "context" "math" "strings" + "sync" "unicode/utf16" "unicode/utf8" @@ -75,6 +76,17 @@ func (r *Runtime) stringproto_toStringValueOf(this Value, funcName string) Value if strObj, ok := obj.self.(*stringObject); ok { return strObj.value } + if reflectObj, ok := obj.self.(*objectGoReflect); ok && reflectObj.class == classString { + if toString := reflectObj.toString; toString != nil { + return toString() + } + if valueOf := reflectObj.valueOf; valueOf != nil { + return valueOf() + } + } + if obj == r.global.StringPrototype { + return stringEmpty + } } r.typeErrorResult(true, "String.prototype.%s is called on incompatible receiver", funcName) return nil @@ -124,16 +136,16 @@ 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 if numInt, ok := num.(valueInt64); 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) } @@ -390,7 +402,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 { @@ -426,7 +438,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{ @@ -459,7 +471,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) { @@ -553,11 +565,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 @@ -741,7 +753,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 { @@ -982,68 +994,138 @@ func (r *Runtime) stringIterProto_next(call FunctionCall) Value { } func (r *Runtime) createStringIterProto(val *Object) objectImpl { - o := newBaseObjectObj(val, r.global.IteratorPrototype, classObject) + 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 } -func (r *Runtime) initString() { - r.global.StringIteratorPrototype = r.newLazyObject(r.createStringIterProto) - 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 (r *Runtime) getStringIteratorPrototype() *Object { + var o *Object + if o = r.global.StringIteratorPrototype; o == nil { + o = &Object{runtime: r} + r.global.StringIteratorPrototype = o + o.self = r.createStringIterProto(o) + } + return o +} + +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) + }) + + return t +} + +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 +} + +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 +} - 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) +var stringProtoTemplate *objectTemplate +var stringProtoTemplateOnce sync.Once - r.addToGlobal("String", r.global.String) +func getStringProtoTemplate() *objectTemplate { + stringProtoTemplateOnce.Do(func() { + stringProtoTemplate = createStringProtoTemplate() + }) + return stringProtoTemplate +} - r.stringSingleton = r.builtin_new(r.global.String, nil).self.(*stringObject) +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 02147a1f..7140062a 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 c2f7b3bd..361cb7e0 100644 --- a/builtin_typedarrays.go +++ b/builtin_typedarrays.go @@ -4,6 +4,7 @@ import ( "fmt" "math" "sort" + "sync" "unsafe" "github.com/dop251/goja/unistring" @@ -82,7 +83,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])) } @@ -118,7 +119,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) @@ -154,7 +155,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] @@ -174,13 +175,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 @@ -525,7 +526,7 @@ func (r *Runtime) typedArrayProto_filter(call FunctionCall) Value { } } c := r.speciesConstructorObj(o, ta.defaultCtor) - ab := r._newArrayBuffer(r.global.ArrayBufferPrototype, nil) + ab := r._newArrayBuffer(r.getArrayBufferPrototype(), nil) ab.data = buf kept := r.toConstructor(ta.defaultCtor)([]Value{ab.val}, ta.defaultCtor) if c == ta.defaultCtor { @@ -967,7 +968,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 @@ -975,7 +976,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:], @@ -1024,7 +1025,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)) @@ -1237,7 +1238,7 @@ func (r *Runtime) typedArray_of(call FunctionCall) Value { } func (r *Runtime) allocateTypedArray(newTarget *Object, length int, taCtor typedArrayObjectCtor, proto *Object) *typedArrayObject { - buf := r._newArrayBuffer(r.global.ArrayBufferPrototype, nil) + buf := r._newArrayBuffer(r.getArrayBufferPrototype(), nil) ta := taCtor(buf, 0, length, r.getPrototypeFromCtor(newTarget, nil, proto)) if length > 0 { buf.data = allocByteSlice(length * ta.elemSize) @@ -1327,7 +1328,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 @@ -1335,16 +1336,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 @@ -1357,7 +1358,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 { @@ -1439,204 +1441,376 @@ func (r *Runtime) createArrayBufferProto(val *Object) objectImpl { accessor: true, configurable: true, writable: true, - getterFunc: r.newNativeFunc(r.arrayBufferProto_getByteLength, nil, "get byteLength", nil, 0), - setterFunc: r.newNativeFunc(r.arrayBufferProto_setByteLength, nil, "set byteLength", nil, 0), + getterFunc: r.newNativeFunc(r.arrayBufferProto_getByteLength, "get byteLength", 0), + setterFunc: r.newNativeFunc(r.arrayBufferProto_setByteLength, "set 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._putSym(SymSpecies, &valueProperty{ - getterFunc: r.newNativeFunc(r.returnThis, nil, "get [Symbol.species]", nil, 0), - accessor: true, - configurable: 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, - writable: true, - getterFunc: r.newNativeFunc(r.dataViewProto_getByteLen, nil, "get byteLength", nil, 0), - setterFunc: r.newNativeFunc(r.dataViewProto_getByteLen, nil, "set 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, - writable: true, - getterFunc: r.newNativeFunc(r.typedArrayProto_getByteLen, nil, "get byteLength", nil, 0), - setterFunc: r.newNativeFunc(r.typedArrayProto_setByteLen, nil, "set byteLength", nil, 0), + + t.putStr("byteLength", func(r *Runtime) Value { + return &valueProperty{ + accessor: true, + configurable: true, + writable: true, + getterFunc: r.newNativeFunc(r.typedArrayProto_getByteLen, "get byteLength", 0), + setterFunc: r.newNativeFunc(r.arrayBufferProto_setByteLength, "set 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) - o._putSym(SymSpecies, &valueProperty{ - getterFunc: r.newNativeFunc(r.returnThis, nil, "get [Symbol.species]", nil, 0), - accessor: true, - configurable: true, +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 +} + +var typedArrayProtoTemplate *objectTemplate +var typedArrayProtoTemplateOnce sync.Once + +func getTypedArrayProtoTemplate() *objectTemplate { + typedArrayProtoTemplateOnce.Do(func() { + typedArrayProtoTemplate = createTypedArrayProtoTemplate() }) + return typedArrayProtoTemplate +} - return o +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) 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) 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 +} - p._putProp("constructor", o.val, true, false, true) +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 +} - 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) 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) initTypedArrays() { +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 +} - r.global.ArrayBufferPrototype = r.newLazyObject(r.createArrayBufferProto) - r.global.ArrayBuffer = r.newLazyObject(r.createArrayBuffer) - r.addToGlobal("ArrayBuffer", r.global.ArrayBuffer) +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 +} - r.global.DataViewPrototype = r.newLazyObject(r.createDataViewProto) - r.global.DataView = r.newLazyObject(r.createDataView) - r.addToGlobal("DataView", r.global.DataView) +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 +} - r.global.TypedArrayPrototype = r.newLazyObject(r.createTypedArrayProto) - r.global.TypedArray = r.newLazyObject(r.createTypedArray) +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 +} - r.global.Uint8Array = r.newLazyObject(r.typedArrayCreator(r.newUint8Array, "Uint8Array", 1)) - r.addToGlobal("Uint8Array", r.global.Uint8Array) +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 +} - r.global.Uint8ClampedArray = r.newLazyObject(r.typedArrayCreator(r.newUint8ClampedArray, "Uint8ClampedArray", 1)) - r.addToGlobal("Uint8ClampedArray", r.global.Uint8ClampedArray) +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.Int8Array = r.newLazyObject(r.typedArrayCreator(r.newInt8Array, "Int8Array", 1)) - r.addToGlobal("Int8Array", r.global.Int8Array) +func createDataViewProtoTemplate() *objectTemplate { + t := newObjectTemplate() + t.protoFactory = func(r *Runtime) *Object { + return r.global.ObjectPrototype + } - r.global.Uint16Array = r.newLazyObject(r.typedArrayCreator(r.newUint16Array, "Uint16Array", 2)) - r.addToGlobal("Uint16Array", r.global.Uint16Array) + 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, + writable: true, + getterFunc: r.newNativeFunc(r.dataViewProto_getByteLen, "get byteLength", 0), + setterFunc: r.newNativeFunc(r.dataViewProto_getByteLen, "set 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.Int16Array = r.newLazyObject(r.typedArrayCreator(r.newInt16Array, "Int16Array", 2)) - r.addToGlobal("Int16Array", r.global.Int16Array) + t.putStr("constructor", func(r *Runtime) Value { return valueProp(r.getDataView(), true, false, true) }) - r.global.Uint32Array = r.newLazyObject(r.typedArrayCreator(r.newUint32Array, "Uint32Array", 4)) - r.addToGlobal("Uint32Array", r.global.Uint32Array) + 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.Int32Array = r.newLazyObject(r.typedArrayCreator(r.newInt32Array, "Int32Array", 4)) - r.addToGlobal("Int32Array", r.global.Int32Array) + t.putSym(SymToStringTag, func(r *Runtime) Value { return valueProp(asciiString("DataView"), false, false, true) }) - r.global.Float32Array = r.newLazyObject(r.typedArrayCreator(r.newFloat32Array, "Float32Array", 4)) - r.addToGlobal("Float32Array", r.global.Float32Array) + return t +} + +var dataViewProtoTemplate *objectTemplate +var dataViewProtoTemplateOnce sync.Once - r.global.Float64Array = r.newLazyObject(r.typedArrayCreator(r.newFloat64Array, "Float64Array", 8)) - r.addToGlobal("Float64Array", r.global.Float64Array) +func getDataViewProtoTemplate() *objectTemplate { + dataViewProtoTemplateOnce.Do(func() { + dataViewProtoTemplate = createDataViewProtoTemplate() + }) + return dataViewProtoTemplate +} + +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 +} + +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 +} + +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 +} + +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_typedarrays_test.go b/builtin_typedarrays_test.go index ad9f6d87..2c62d418 100644 --- a/builtin_typedarrays_test.go +++ b/builtin_typedarrays_test.go @@ -17,7 +17,7 @@ func TestArrayBufferNew(t *testing.T) { func TestArrayBufferSetUint32(t *testing.T) { vm := New() - b := vm._newArrayBuffer(vm.global.ArrayBufferPrototype, nil) + b := vm._newArrayBuffer(vm.getArrayBufferPrototype(), nil) b.data = make([]byte, 4) b.setUint32(0, 0xCAFEBABE, bigEndian) @@ -39,7 +39,7 @@ func TestArrayBufferSetUint32(t *testing.T) { func TestArrayBufferSetInt32(t *testing.T) { vm := New() - b := vm._newArrayBuffer(vm.global.ArrayBufferPrototype, nil) + b := vm._newArrayBuffer(vm.getArrayBufferPrototype(), nil) b.data = make([]byte, 4) b.setInt32(0, -42, littleEndian) if v := b.getInt32(0, littleEndian); v != -42 { diff --git a/builtin_weakmap.go b/builtin_weakmap.go index 1ff8444e..79f49720 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 dc99c72c..8cf69aae 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/date.go b/date.go index 6abc3def..9a950161 100644 --- a/date.go +++ b/date.go @@ -131,10 +131,6 @@ func timeToMsec(t time.Time) int64 { return t.Unix()*1000 + int64(t.Nanosecond())/1e6 } -func (d *dateObject) toPrimitive() Value { - return d.toPrimitiveString() -} - func (d *dateObject) exportType() reflect.Type { return typeTime } diff --git a/destruct.go b/destruct.go index 927b446c..249a4dce 100644 --- a/destruct.go +++ b/destruct.go @@ -155,18 +155,6 @@ func (d *destructKeyedSource) deleteSym(s *Symbol, throw bool) bool { return d.w().deleteSym(s, throw) } -func (d *destructKeyedSource) toPrimitiveNumber() Value { - return d.w().toPrimitiveNumber() -} - -func (d *destructKeyedSource) toPrimitiveString() Value { - return d.w().toPrimitiveString() -} - -func (d *destructKeyedSource) toPrimitive() Value { - return d.w().toPrimitive() -} - func (d *destructKeyedSource) assertCallable() (call func(FunctionCall) Value, ok bool) { return d.w().assertCallable() } diff --git a/func.go b/func.go index d610fc8b..d1f7083b 100644 --- a/func.go +++ b/func.go @@ -26,6 +26,10 @@ type baseJsFuncObject struct { strict bool } +type funcObjectImpl interface { + source() valueString +} + type funcObject struct { baseJsFuncObject } @@ -270,7 +274,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 @@ -368,9 +372,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() @@ -382,13 +386,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{ @@ -422,6 +430,27 @@ func (f *nativeFuncObject) Call(call FunctionCall) Value { return rv } +func (f *nativeFuncObject) vmCall(vm *vm, n int) { + if f.f != nil { + vm.pushCtx() + vm.prg = nil + vm.sb = vm.sp - n // so that [sb-1] points to the callee + ret := f.f(FunctionCall{ + Arguments: vm.stack[vm.sp-n : vm.sp], + This: vm.stack[vm.sp-n-2], + }) + if ret == nil { + ret = _undefined + } + vm.stack[vm.sp-n-2] = ret + vm.popCtx() + } else { + vm.stack[vm.sp-n-2] = _undefined + } + vm.sp -= n + 1 + vm.pc++ +} + func (f *nativeFuncObject) assertConstructor() func(args []Value, newTarget *Object) *Object { return f.construct } diff --git a/memory_test.go b/memory_test.go index 4e4829da..690ef974 100644 --- a/memory_test.go +++ b/memory_test.go @@ -451,7 +451,12 @@ func TestMemCheck(t *testing.T) { // TODO(REALMC-10739) add a test that calls Error.captureStackTrace when it is implemented) { desc: "stash", - script: `checkMem(); + script: ` + // With the new template-object feature, objects are now not initialized on vm start but instead initialized + // when actually being used. To account for this difference we create a New Error error so that this memory + // usage is already included in the initial checkMem call. + const err = new Error(); + checkMem(); try { throw new Error("abc"); } catch(e) { @@ -569,7 +574,8 @@ func TestMemMaxDepth(t *testing.T) { } _, _, err = vm.MemUsage( - NewMemUsageContext(vm, tc.expectedDepth+1, memUsageLimit, arrLenThreshold, objPropsLenThreshold, TestNativeMemUsageChecker{}), + // need to add 2 to the expectedDepth since Object is lazy loaded it adds onto the expected depth + NewMemUsageContext(vm, tc.expectedDepth+2, memUsageLimit, arrLenThreshold, objPropsLenThreshold, TestNativeMemUsageChecker{}), ) if err != nil { t.Fatalf("expected to NOT hit mem check hit depth limit error, but got %v", err) diff --git a/native.go b/native.go index 13ad41a7..5527bd21 100644 --- a/native.go +++ b/native.go @@ -38,7 +38,7 @@ func (r *Runtime) TryToValue(i interface{}) (Value, error) { } func (r *Runtime) MakeCustomError(name, msg string) *Object { - e := r.newError(r.global.Error, msg).(*Object) + e := r.newError(r.getError(), msg).(*Object) e.self.setOwnStr("name", asciiString(name), false) return e } @@ -56,13 +56,13 @@ func (r *Runtime) CreateNativeErrorClass( classProps []Property, funcProps []Property, ) NativeClass { - classProto := r.builtin_new(r.global.Error, []Value{}) + classProto := r.builtin_new(r.getError(), []Value{}) proto := classProto.self for _, prop := range classProps { proto._putProp(unistring.String(prop.Name), prop.Value, true, false, true) } - - classFunc := r.newNativeFuncConstruct(func(args []Value, proto *Object) *Object { + v := &Object{runtime: r} + classFunc := r.newNativeFuncConstruct(v, func(args []Value, proto *Object) *Object { obj := r.newBaseObject(proto, className) call := FunctionCall{ ctx: r.vm.ctx, @@ -98,11 +98,12 @@ func (r *Runtime) CreateNativeErrorClass( } func (r *Runtime) CreateNativeError(name string) (Value, func(err error) Value) { - proto := r.builtin_new(r.global.Error, []Value{}) + proto := r.builtin_new(r.getError(), []Value{}) o := proto.self o._putProp("name", asciiString(name), true, false, true) - e := r.newNativeFuncConstructProto(r.builtin_Error, unistring.String(name), proto, r.global.Error, 1) + v := &Object{runtime: r} + e := r.newNativeFuncConstructProto(v, r.builtin_Error, unistring.String(name), proto, r.getError(), 1) return e, func(err error) Value { return r.MakeCustomError(name, err.Error()) @@ -115,13 +116,14 @@ func (r *Runtime) CreateNativeClass( classProps []Property, funcProps []Property, ) NativeClass { - classProto := r.builtin_new(r.global.Object, []Value{}) + classProto := r.builtin_new(r.getObject(), []Value{}) proto := classProto.self for _, prop := range classProps { proto._putProp(unistring.String(prop.Name), prop.Value, true, false, true) } - classFunc := r.newNativeFuncConstruct(func(args []Value, proto *Object) *Object { + v := &Object{runtime: r} + classFunc := r.newNativeFuncConstruct(v, func(args []Value, proto *Object) *Object { obj := r.newBaseObject(proto, className) call := FunctionCall{ @@ -160,7 +162,8 @@ func (n NativeClass) InstanceOf(val interface{}) Value { r := n.runtime className := n.className classProto := n.classProto - obj, err := r.New(r.newNativeFuncConstruct(func(args []Value, proto *Object) *Object { + v := &Object{runtime: r} + obj, err := r.New(r.newNativeFuncConstruct(v, func(args []Value, proto *Object) *Object { obj := r.newBaseObject(proto, className) g := &_goNativeValue{baseObject: obj, value: val} obj.val.self = g @@ -195,20 +198,6 @@ func (n NativeClass) InstanceOf(val interface{}) Value { return obj } -// NewLazyObject creates a lazy object that will do initialization when the object is called upon during runtime -func (r *Runtime) NewLazyObject(create func(val *Object) *Object) (Value, error) { - if create == nil { - return UndefinedValue(), errors.New("create cannot be nil") - } - - createObjectImpl := func(val *Object) objectImpl { - obj := create(val) - return obj.self - } - - return r.newLazyObject(createObjectImpl), nil -} - // CreateNativeFunction creates a native function that will call the given call function. // This provides for a way to detail how the function appears to a user within JS // compared to passing the call in via toValue. @@ -217,7 +206,7 @@ func (r *Runtime) CreateNativeFunction(name, file string, call func(FunctionCall return UndefinedValue(), errors.New("call cannot be nil") } - return r.newNativeFunc(call, nil, unistring.String(name), nil, 1), nil + return r.newNativeFunc(call, unistring.String(name), 1), nil } func (r *Runtime) Eval(name, src string, direct, strict bool) (Value, error) { diff --git a/native_test.go b/native_test.go index 2a44b1e2..23f4a259 100644 --- a/native_test.go +++ b/native_test.go @@ -117,56 +117,3 @@ func TestNativeClass(t *testing.T) { is(t, err, nil) is(t, ret.String(), "undefined") } - -func TestNewLazyObject(t *testing.T) { - t.Run("should return an error if no callback is supplied to NewLazyObject", func(t *testing.T) { - vm := New() - _, err := vm.NewLazyObject(nil) - is(t, err.Error(), "create cannot be nil") - }) - - t.Run("creating new lazy object should not allocate size when not accessed", func(t *testing.T) { - vm := New() - o, err := vm.NewLazyObject(func(val *Object) *Object { - o := vm.newBaseObject(nil, "myClass") - o._putProp("myProp", newStringValue("hello world"), false, false, false) - return o.val - }) - is(t, err, nil) - - mem, newMem, err := o.MemUsage(NewMemUsageContext(vm, 100, 100, 100, 1, nil)) - is(t, newMem, SizeEmptyStruct*2) // memUsage of Object + lazyObject - is(t, mem, SizeEmptyStruct*2) // memUsage of Object + lazyObject - }) - - t.Run("accessing a lazy object should allocate memory and initialize the object", func(t *testing.T) { - vm := New() - var didInitialize bool - o, err := vm.NewLazyObject(func(val *Object) *Object { - didInitialize = true - o := vm.newBaseObject(nil, "myClass") - o._putProp("myProp", newStringValue("hello world"), false, false, false) - return o.val - }) - is(t, err, nil) - - memBeforeAccess, newMemBeforeAccess, err := o.MemUsage(NewMemUsageContext(vm, 100, 100, 100, 1, nil)) - is(t, err, nil) - is(t, memBeforeAccess, SizeEmptyStruct*2) // memUsage of Object + lazyObject - is(t, newMemBeforeAccess, SizeEmptyStruct*2) // memUsage of Object + lazyObject - - obj, ok := o.(*Object) - is(t, ok, true) - - // access the lazyobject which causes the lazyObject's init callback to run - hasMyProp := obj.hasOwnProperty(newStringValue("myProp")) - is(t, hasMyProp, true) - is(t, didInitialize, true) - - mem, newMem, err := o.MemUsage(NewMemUsageContext(vm, 100, 100, 100, 1, nil)) - is(t, err, nil) - if mem <= memBeforeAccess || newMem <= newMemBeforeAccess { - t.Fatal("memory usage of lazy object should have grown after accessing it but it didnt") - } - }) -} diff --git a/object.go b/object.go index e5f3428b..ab782696 100644 --- a/object.go +++ b/object.go @@ -238,9 +238,6 @@ type objectImpl interface { deleteIdx(idx valueInt, throw bool) bool deleteSym(s *Symbol, throw bool) bool - toPrimitiveNumber() Value - toPrimitiveString() Value - toPrimitive() Value assertCallable() (call func(FunctionCall) Value, ok bool) assertConstructor() func(args []Value, newTarget *Object) *Object proto() *Object @@ -915,7 +912,7 @@ func (o *Object) tryPrimitive(methodName unistring.String) Value { return nil } -func (o *Object) genericToPrimitiveNumber() Value { +func (o *Object) ordinaryToPrimitiveNumber() Value { if o.Prototype() == nil { o.self.setProto(o.runtime.global.ObjectPrototype, false) } @@ -931,11 +928,7 @@ func (o *Object) genericToPrimitiveNumber() Value { panic(o.runtime.NewTypeError("Could not convert %v to primitive", o.self)) } -func (o *baseObject) toPrimitiveNumber() Value { - return o.val.genericToPrimitiveNumber() -} - -func (o *Object) genericToPrimitiveString() Value { +func (o *Object) ordinaryToPrimitiveString() Value { if o.Prototype() == nil { o.self.setProto(o.runtime.global.ObjectPrototype, false) } @@ -948,19 +941,7 @@ func (o *Object) genericToPrimitiveString() Value { return v } - panic(o.runtime.NewTypeError("Could not convert %v to primitive", o.self)) -} - -func (o *Object) genericToPrimitive() Value { - return o.genericToPrimitiveNumber() -} - -func (o *baseObject) toPrimitiveString() Value { - return o.val.genericToPrimitiveString() -} - -func (o *baseObject) toPrimitive() Value { - return o.val.genericToPrimitiveNumber() + panic(o.runtime.NewTypeError("Could not convert %v (%T) to primitive", o.self, o.self)) } func (o *Object) tryExoticToPrimitive(hint Value) Value { @@ -984,7 +965,7 @@ func (o *Object) toPrimitiveNumber() Value { return v } - return o.self.toPrimitiveNumber() + return o.ordinaryToPrimitiveNumber() } func (o *Object) toPrimitiveString() Value { @@ -992,14 +973,14 @@ func (o *Object) toPrimitiveString() Value { return v } - return o.self.toPrimitiveString() + return o.ordinaryToPrimitiveString() } func (o *Object) toPrimitive() Value { if v := o.tryExoticToPrimitive(hintDefault); v != nil { return v } - return o.self.toPrimitive() + return o.ordinaryToPrimitiveNumber() } func (o *baseObject) assertCallable() (func(FunctionCall) Value, bool) { diff --git a/object_dynamic.go b/object_dynamic.go index 60899d5a..4afc298c 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 @@ -431,18 +431,6 @@ func (*baseDynamicObject) deleteSym(_ *Symbol, _ bool) bool { return true } -func (o *baseDynamicObject) toPrimitiveNumber() Value { - return o.val.genericToPrimitiveNumber() -} - -func (o *baseDynamicObject) toPrimitiveString() Value { - return o.val.genericToPrimitiveString() -} - -func (o *baseDynamicObject) toPrimitive() Value { - return o.val.genericToPrimitive() -} - func (o *baseDynamicObject) assertCallable() (call func(FunctionCall) Value, ok bool) { return nil, false } diff --git a/object_goarray_reflect.go b/object_goarray_reflect.go index ae856bb1..eccf7296 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.updateLen() o.baseObject._put("length", &o.lengthProp) } @@ -223,7 +223,7 @@ func (o *objectGoArrayReflect) hasOwnPropertyStr(name unistring.String) bool { if o._hasStr(name) || name == "length" { return true } - return o.objectGoReflect._has(name.String()) + return o.objectGoReflect.hasOwnPropertyStr(name) } func (o *objectGoArrayReflect) defineOwnPropertyIdx(idx valueInt, descr PropertyDescriptor, throw bool) bool { @@ -256,10 +256,6 @@ func (o *objectGoArrayReflect) defineOwnPropertyStr(name unistring.String, descr return false } -func (o *objectGoArrayReflect) toPrimitive() Value { - return o.toPrimitiveString() -} - func (o *objectGoArrayReflect) _deleteIdx(idx int) { if idx < o.fieldsValue.Len() { if cv := o.valueCache.get(idx); cv != nil { diff --git a/object_gomap.go b/object_gomap.go index 4b7b7677..93a8ad73 100644 --- a/object_gomap.go +++ b/object_gomap.go @@ -97,24 +97,6 @@ func (o *objectGoMapSimple) defineOwnPropertyStr(name unistring.String, descr Pr return false } -/* -func (o *objectGoMapSimple) toPrimitiveNumber() Value { - return o.toPrimitiveString() -} - -func (o *objectGoMapSimple) toPrimitiveString() Value { - return stringObjectObject -} - -func (o *objectGoMapSimple) toPrimitive() Value { - return o.toPrimitiveString() -} - -func (o *objectGoMapSimple) assertCallable() (call func(FunctionCall) Value, ok bool) { - return nil, false -} -*/ - func (o *objectGoMapSimple) deleteStr(name unistring.String, _ bool) bool { delete(o.data, name.String()) return true diff --git a/object_goreflect.go b/object_goreflect.go index 398f5180..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 @@ -181,11 +181,6 @@ func (o *objectGoReflect) init() { o.toString = o._toStringError } - if o.toString != nil || o.valueOf != nil { - o.baseObject._putProp("toString", o.val.runtime.newNativeFunc(o.toStringFunc, nil, "toString", nil, 0), true, false, true) - o.baseObject._putProp("valueOf", o.val.runtime.newNativeFunc(o.valueOfFunc, nil, "valueOf", nil, 0), true, false, true) - } - if len(o.methodsInfo.Names) > 0 && o.fieldsValue.Kind() != reflect.Interface { o.methodsValue = o.fieldsValue.Addr() } else { @@ -197,14 +192,6 @@ func (o *objectGoReflect) init() { } } -func (o *objectGoReflect) toStringFunc(FunctionCall) Value { - return o.toPrimitiveString() -} - -func (o *objectGoReflect) valueOfFunc(FunctionCall) Value { - return o.toPrimitiveNumber() -} - func (o *objectGoReflect) getStr(name unistring.String, receiver Value) Value { if v := o._get(name.String()); v != nil { return v @@ -243,7 +230,7 @@ func (o *objectGoReflect) elemToValue(ev reflect.Value) (Value, reflectValueWrap return ret, nil } - for ev.Kind() == reflect.Interface { + if ev.Kind() == reflect.Interface { ev = ev.Elem() } @@ -304,7 +291,7 @@ func (o *objectGoReflect) getOwnPropStr(name unistring.String) Value { } } - return nil + return o.baseObject.getOwnPropStr(name) } func (o *objectGoReflect) setOwnStr(name unistring.String, val Value, throw bool) bool { @@ -402,7 +389,7 @@ func (o *objectGoReflect) _has(name string) bool { } func (o *objectGoReflect) hasOwnPropertyStr(name unistring.String) bool { - return o._has(name.String()) + return o._has(name.String()) || o.baseObject.hasOwnPropertyStr(name) } func (o *objectGoReflect) _valueOfInt() Value { @@ -445,37 +432,6 @@ func (o *objectGoReflect) _toStringError() Value { return newStringValue(o.origValue.Interface().(error).Error()) } -func (o *objectGoReflect) toPrimitiveNumber() Value { - if o.valueOf != nil { - return o.valueOf() - } - if o.toString != nil { - return o.toString() - } - return o.baseObject.toPrimitiveNumber() -} - -func (o *objectGoReflect) toPrimitiveString() Value { - if o.toString != nil { - return o.toString() - } - if o.valueOf != nil { - return o.valueOf().toString() - } - return o.baseObject.toPrimitiveString() -} - -func (o *objectGoReflect) toPrimitive() Value { - if o.valueOf != nil { - return o.valueOf() - } - if o.toString != nil { - return o.toString() - } - - return o.baseObject.toPrimitive() -} - func (o *objectGoReflect) deleteStr(name unistring.String, throw bool) bool { n := name.String() if o._has(n) { diff --git a/object_goreflect_test.go b/object_goreflect_test.go index 1af6c1c9..e5c0d5f6 100644 --- a/object_goreflect_test.go +++ b/object_goreflect_test.go @@ -1559,3 +1559,37 @@ func TestGoReflectFuncWithRuntime(t *testing.T) { t.Fatal(res) } } + +func TestGoReflectDefaultToString(t *testing.T) { + var s testStringS + vm := New() + v := vm.ToValue(s).(*Object) + v.Delete("toString") + v.Delete("valueOf") + vm.Set("s", v) + _, err := vm.RunString(` + class S { + toString() { + return "X"; + } + } + + if (s.toString() !== "S") { + throw new Error(s.toString()); + } + if (("" + s) !== "S") { + throw new Error("" + s); + } + + Object.setPrototypeOf(s, S.prototype); + if (s.toString() !== "X") { + throw new Error(s.toString()); + } + if (("" + s) !== "X") { + throw new Error("" + s); + } + `) + if err != nil { + t.Fatal(err) + } +} diff --git a/object_goslice.go b/object_goslice.go index 8c710816..2180fb14 100644 --- a/object_goslice.go +++ b/object_goslice.go @@ -32,7 +32,7 @@ func (r *Runtime) newObjectGoSlice(data *[]interface{}) *objectGoSlice { 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.updateLen() diff --git a/object_goslice_test.go b/object_goslice_test.go index c191f1b2..538e9798 100644 --- a/object_goslice_test.go +++ b/object_goslice_test.go @@ -358,7 +358,7 @@ func TestGoSliceMemUsage(t *testing.T) { // default + default since we don't account for objectGoSlice in (*Object).MemUsage expectedMem: SizeEmptyStruct + SizeEmptyStruct, // overhead + (value + len("length") with string overhead + "length".value + prototype + ints) - expectedNewMem: SizeEmptyStruct + (SizeEmptyStruct + (6 + SizeString) + SizeEmptyStruct + (SizeEmptyStruct + SizeEmptyStruct) + SizeNumber*2), + expectedNewMem: SizeEmptyStruct + (SizeEmptyStruct + (6 + SizeString) + SizeEmptyStruct + SizeEmptyStruct + SizeNumber*2), errExpected: nil, }, { @@ -377,7 +377,7 @@ func TestGoSliceMemUsage(t *testing.T) { // default + default since we don't account for objectGoSlice in (*Object).MemUsage expectedMem: SizeEmptyStruct + SizeEmptyStruct, // overhead + (value + len("length") with string overhead + "length".value + prototype + ints) - expectedNewMem: SizeEmptyStruct + (SizeEmptyStruct + (6 + SizeString) + SizeEmptyStruct + (SizeEmptyStruct + SizeEmptyStruct) + SizeNumber*2), + expectedNewMem: SizeEmptyStruct + (SizeEmptyStruct + (6 + SizeString) + SizeEmptyStruct + SizeEmptyStruct + SizeNumber*2), errExpected: nil, }, } diff --git a/object_lazy.go b/object_lazy.go deleted file mode 100644 index 1f88b840..00000000 --- a/object_lazy.go +++ /dev/null @@ -1,333 +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) 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) toPrimitiveNumber() Value { - obj := o.create(o.val) - o.val.self = obj - return obj.toPrimitiveNumber() -} - -func (o *lazyObject) toPrimitiveString() Value { - obj := o.create(o.val) - o.val.self = obj - return obj.toPrimitiveString() -} - -func (o *lazyObject) toPrimitive() Value { - obj := o.create(o.val) - o.val.self = obj - return obj.toPrimitive() -} - -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) 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) -} - -func (o *lazyObject) MemUsage(ctx *MemUsageContext) (memUsage uint64, newMemUsage uint64, err error) { - if o == nil || o.val == nil || ctx.IsObjVisited(o) { - return SizeEmptyStruct, SizeEmptyStruct, nil - } - ctx.VisitObj(o) - - memUsage = SizeEmptyStruct - newMemUsage = SizeEmptyStruct - inc, newInc, err := o.val.MemUsage(ctx) - - return memUsage + inc, newMemUsage + newInc, err -} diff --git a/object_lazy_test.go b/object_lazy_test.go deleted file mode 100644 index 21f771b9..00000000 --- a/object_lazy_test.go +++ /dev/null @@ -1,55 +0,0 @@ -package goja - -import ( - "testing" -) - -func TestObjectLazyMemUsage(t *testing.T) { - tests := []struct { - name string - val *lazyObject - expectedMem uint64 - expectedNewMem uint64 - errExpected error - }{ - { - name: "should have a value of SizeEmptyStruct given a nil lazy object", - val: nil, - expectedMem: SizeEmptyStruct, - expectedNewMem: SizeEmptyStruct, - errExpected: nil, - }, - { - name: "should have a value of SizeEmptyStruct given an empty lazy object", - val: &lazyObject{}, - expectedMem: SizeEmptyStruct, - expectedNewMem: SizeEmptyStruct, - errExpected: nil, - }, - { - name: "should have a value of SizeEmptyStruct given a base dynamic array with an empty val", - val: &lazyObject{val: &Object{}}, - expectedMem: SizeEmptyStruct + SizeEmptyStruct, - expectedNewMem: SizeEmptyStruct + SizeEmptyStruct, - errExpected: nil, - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - total, newTotal, err := tc.val.MemUsage(NewMemUsageContext(New(), 100, 100, 100, 100, nil)) - if err != tc.errExpected { - t.Fatalf("Unexpected error. Actual: %v Expected: %v", err, tc.errExpected) - } - if err != nil && tc.errExpected != nil && err.Error() != tc.errExpected.Error() { - t.Fatalf("Errors do not match. Actual: %v Expected: %v", err, tc.errExpected) - } - if total != tc.expectedMem { - t.Fatalf("Unexpected memory return. Actual: %v Expected: %v", total, tc.expectedMem) - } - if newTotal != tc.expectedNewMem { - t.Fatalf("Unexpected new memory return. Actual: %v Expected: %v", newTotal, tc.expectedNewMem) - } - }) - } -} diff --git a/object_template.go b/object_template.go new file mode 100644 index 00000000..814787a8 --- /dev/null +++ b/object_template.go @@ -0,0 +1,470 @@ +package goja + +import ( + "fmt" + "math" + "reflect" + "sort" + + "github.com/dop251/goja/unistring" +) + +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() valueString { + 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() valueString { + 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 a69684d6..5259f4bd 100644 --- a/runtime.go +++ b/runtime.go @@ -62,6 +62,11 @@ type global struct { Date *Object Symbol *Object Proxy *Object + Reflect *Object + // DIVERSION: we are not supporting Goja Promises + // Promise *Object + Math *Object + JSON *Object ArrayBuffer *Object DataView *Object @@ -130,8 +135,7 @@ type global struct { Eval *Object - thrower *Object - throwerProperty Value + thrower *Object stdRegexpProto *guardedObject @@ -141,6 +145,13 @@ type global struct { setAdder *Object arrayValues *Object arrayToString *Object + + stringproto_trimEnd *Object + stringproto_trimStart *Object + + parseFloat, parseInt *Object + + typedArrayValues *Object } type Flag int @@ -426,22 +437,32 @@ 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 +} + +func (r *Runtime) getIteratorPrototype() *Object { + var o *Object + if o = r.global.IteratorPrototype; o == nil { + o = &Object{runtime: r} + r.global.IteratorPrototype = o + o.self = r.createIterProto(o) + } return o } 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, @@ -449,62 +470,6 @@ func (r *Runtime) init() { } r.vm.init() - funcProto := r.newNativeFunc(func(FunctionCall) Value { - return _undefined - }, nil, " ", nil, 0) - r.global.FunctionPrototype = funcProto - funcProtoObj := funcProto.self.(*nativeFuncObject) - - r.global.IteratorPrototype = r.newLazyObject(r.createIterProto) - - 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.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, - }) - r.SetRateLimiter(rate.NewLimiter(rate.Inf, maxInt)) } @@ -575,11 +540,11 @@ func (r *Runtime) newError(typ *Object, format string, args ...interface{}) Valu } func (r *Runtime) throwReferenceError(name unistring.String) { - panic(r.newError(r.global.ReferenceError, "'%s' is not defined", name)) + panic(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 { @@ -641,26 +606,19 @@ func (r *Runtime) NewTypeError(args ...interface{}) *Object { f, _ := args[0].(string) msg = fmt.Sprintf(f, args[1:]...) } - e := r.builtin_new(r.global.TypeError, []Value{newStringValue(msg)}) - return e + 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 } func (r *Runtime) newFunc(name unistring.String, length int, strict bool) (f *funcObject) { - v := &Object{runtime: r} - f = &funcObject{} - f.class = classFunction - f.val = v - f.extensible = true - f.strict = strict - v.self = f - f.prototype = r.global.FunctionPrototype + r.initBaseJsFunction(&f.baseJsFuncObject, strict) + f.val.self = f f.init(name, intToValue(int64(length))) return } @@ -680,61 +638,37 @@ func (r *Runtime) newClassFunc(name unistring.String, length int, proto *Object, return } -func (r *Runtime) newMethod(name unistring.String, length int, strict bool) (f *methodFuncObject) { +func (r *Runtime) initBaseJsFunction(f *baseJsFuncObject, strict bool) { v := &Object{runtime: r} - f = &methodFuncObject{} f.class = classFunction f.val = v f.extensible = true f.strict = strict - v.self = f - f.prototype = r.global.FunctionPrototype + f.prototype = r.getFunctionPrototype() +} + +func (r *Runtime) newMethod(name unistring.String, length int, strict bool) (f *methodFuncObject) { + f = &methodFuncObject{} + r.initBaseJsFunction(&f.baseJsFuncObject, strict) + f.val.self = f f.init(name, intToValue(int64(length))) return } -func (r *Runtime) newArrowFunc(name unistring.String, length int, strict bool) (f *arrowFuncObject) { - v := &Object{runtime: r} +func (r *Runtime) initArrowFunc(f *arrowFuncObject, strict bool) { + r.initBaseJsFunction(&f.baseJsFuncObject, strict) + f.newTarget = r.vm.newTarget +} +func (r *Runtime) newArrowFunc(name unistring.String, length int, strict bool) (f *arrowFuncObject) { f = &arrowFuncObject{} - f.class = classFunction - f.val = v - f.extensible = true - f.strict = strict - - vm := r.vm - - f.newTarget = vm.newTarget - v.self = f - f.prototype = r.global.FunctionPrototype + r.initArrowFunc(f, strict) + f.val.self = f f.init(name, intToValue(int64(length))) 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{ - ctx: r.vm.ctx, - baseFuncObject: baseFuncObject{ - ctx: r.vm.ctx, - 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} @@ -746,7 +680,7 @@ func (r *Runtime) newNativeConstructor(call func(ConstructorCall) *Object, name class: classFunction, val: v, extensible: true, - prototype: r.global.FunctionPrototype, + prototype: r.getFunctionPrototype(), }, }, } @@ -805,7 +739,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, @@ -820,7 +754,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{ @@ -831,18 +765,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 } @@ -857,7 +786,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), @@ -879,11 +808,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))) @@ -893,13 +822,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{ctx: r.vm.ctx} f.class = classFunction f.val = v @@ -907,11 +834,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 } @@ -980,7 +906,7 @@ func (r *Runtime) error_captureStackTrace(call FunctionCall) Value { } 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 { @@ -1058,21 +984,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) } } @@ -1373,7 +1296,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 { @@ -1401,11 +1324,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 { @@ -1514,12 +1437,12 @@ 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())}), traceLimit: r.stackTraceLimit, } case *CompilerReferenceError: err = &Exception{ - val: r.newError(r.global.ReferenceError, x1.Message), + val: r.newError(r.getReferenceError(), x1.Message), traceLimit: r.stackTraceLimit, } // TODO proper message } @@ -1913,12 +1836,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) @@ -1945,7 +1868,7 @@ func (r *Runtime) toValue(i interface{}, origValue reflect.Value) Value { // all its methods. This is done deliberately instead of converting it to a `Date` because these two types are not fully // compatible: `time.Time` includes zone, whereas JS `Date` doesn't. Doing the conversion implicitly therefore would // result in a loss of information. - return r.newDateObject(i, true, r.global.DatePrototype) + return r.newDateObject(i, true, r.getDatePrototype()) case uint: if uint64(i) <= math.MaxInt64 { return intToValue(int64(i)) @@ -2735,18 +2658,6 @@ func (r *Runtime) toObject(v Value, args ...interface{}) *Object { } } -func (r *Runtime) toNumber(v Value) Value { - switch o := v.(type) { - case valueInt, valueFloat, valueInt64: - return v - case *Object: - if pvo, ok := o.self.(*primitiveValueObject); ok { - return r.toNumber(pvo.pValue) - } - } - panic(r.NewTypeError("Value is not a number: %s", v)) -} - func (r *Runtime) speciesConstructor(o, defaultConstructor *Object) func(args []Value, newTarget *Object) *Object { c := o.self.getStr("constructor", nil) if c != nil && c != _undefined { @@ -2904,16 +2815,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{} @@ -2981,15 +2882,10 @@ func (r *Runtime) MakeTypeError(args ...interface{}) *Object { msg = fmt.Sprintf(f, args[1:]...) } - e := r.builtin_new(r.global.TypeError, []Value{newStringValue(msg)}) + e := r.builtin_new(r.getTypeError(), []Value{newStringValue(msg)}) return e } -func (r *Runtime) NewNativeFunction(name string, length Value, call func(FunctionCall) Value) (*Object, error) { - x := r.newNativeFuncObj(r.NewObject(), call, nil, unistring.String(name), nil, length) - return x.val, nil -} - func shrinkCap(newSize, oldCap int) int { if oldCap > 8 { if cap := oldCap / 2; cap >= newSize { @@ -3074,6 +2970,14 @@ func (r *Runtime) iterableToList(iterable Value, method func(FunctionCall) Value return values } +func (r *Runtime) putSpeciesReturnThis(o objectImpl) { + o._putSym(SymSpecies, &valueProperty{ + getterFunc: r.newNativeFunc(r.returnThis, "get [Symbol.species]", 0), + accessor: true, + configurable: true, + }) +} + func strToArrayIdx(s unistring.String) uint32 { if s == "" { return math.MaxUint32 @@ -3285,3 +3189,18 @@ func strToIdx64(s unistring.String) int64 { } return -1 } + +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/string.go b/string.go index 626b8b20..d1999bab 100644 --- a/string.go +++ b/string.go @@ -108,7 +108,7 @@ func (r *Runtime) createStringIterator(s valueString) Value { si.val = o si.extensible = true o.self = si - si.prototype = r.global.StringIteratorPrototype + si.prototype = r.getStringIteratorPrototype() si.init() return o diff --git a/string_ascii.go b/string_ascii.go index 472dc2d3..8c9f6bd3 100644 --- a/string_ascii.go +++ b/string_ascii.go @@ -175,7 +175,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 { @@ -230,7 +230,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 7288a750..430a8a7c 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) ToInt() int { diff --git a/string_unicode.go b/string_unicode.go index d43f58df..af3b0381 100644 --- a/string_unicode.go +++ b/string_unicode.go @@ -343,7 +343,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 { @@ -388,7 +388,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 26955d10..039c65f2 100644 --- a/typedarrays.go +++ b/typedarrays.go @@ -113,7 +113,7 @@ func (a ArrayBuffer) MemUsage(ctx *MemUsageContext) (memUsage uint64, newMemUsag } func (r *Runtime) NewArrayBuffer(data []byte) ArrayBuffer { - buf := r._newArrayBuffer(r.global.ArrayBufferPrototype, nil) + buf := r._newArrayBuffer(r.getArrayBufferPrototype(), nil) buf.data = data return ArrayBuffer{ buf: buf, @@ -123,9 +123,9 @@ func (r *Runtime) NewArrayBuffer(data []byte) ArrayBuffer { // DIVERSION: NewUint8Array converts a byte slice to a Uint8Array // javascript object in order to bypass a Buffer.from call in js-land func (r *Runtime) NewUint8Array(data []byte) *Object { - buf := r._newArrayBuffer(r.global.ArrayBufferPrototype, nil) + buf := r._newArrayBuffer(r.getArrayBufferPrototype(), nil) buf.data = data - a := r.newUint8ArrayObject(buf, 0, len(buf.data), r.global.Uint8Array) + a := r.newUint8ArrayObject(buf, 0, len(buf.data), r.getUint8Array()) return a.val } @@ -759,6 +759,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) } @@ -797,7 +799,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 8a41c8fb..f5669ab6 100644 --- a/value.go +++ b/value.go @@ -301,7 +301,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 { @@ -345,7 +345,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{} { @@ -456,7 +456,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 { @@ -494,7 +494,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{} { @@ -937,7 +937,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 { @@ -1027,7 +1027,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{} { @@ -1198,16 +1198,16 @@ func (o *Object) MemUsage(ctx *MemUsageContext) (memUsage uint64, newMemUsage ui } func (o *Object) ToInt() int { - return o.self.toPrimitiveNumber().ToNumber().ToInt() + return o.toPrimitiveNumber().ToNumber().ToInt() } func (o *Object) ToInt32() int32 { - return o.self.toPrimitiveNumber().ToNumber().ToInt32() + return o.toPrimitiveNumber().ToNumber().ToInt32() } func (o *Object) ToUInt32() uint32 { - return o.self.toPrimitiveNumber().ToNumber().ToUInt32() + return o.toPrimitiveNumber().ToNumber().ToUInt32() } func (o *Object) ToInt64() int64 { - return o.self.toPrimitiveNumber().ToNumber().ToInt64() + return o.toPrimitiveNumber().ToNumber().ToInt64() } func (o *Object) assertInt() (int, bool) { @@ -1633,7 +1633,7 @@ func (s *Symbol) ExportType() reflect.Type { } func (s *Symbol) baseObject(r *Runtime) *Object { - return r.newPrimitiveObject(s, r.global.SymbolPrototype, "Symbol") + return r.newPrimitiveObject(s, r.getSymbolPrototype(), "Symbol") } func (s *Symbol) hash(*maphash.Hash) uint64 { diff --git a/vm.go b/vm.go index fc7b0825..5bcde776 100644 --- a/vm.go +++ b/vm.go @@ -794,15 +794,15 @@ func (vm *vm) try(ctx1 context.Context, f func()) (ex *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: /* @@ -2329,7 +2329,7 @@ func (g getPropCallee) exec(vm *vm) { if prop == nil { // TODO(REALMC-10739) remove this and ensure the captureStackTrace can be implicitly called from another dependency if g == "captureStackTrace" { - prop = vm.r.newNativeFunc(vm.r.error_captureStackTrace, nil, "captureStackTrace", nil, 0) + prop = vm.r.newNativeFunc(vm.r.error_captureStackTrace, "captureStackTrace", 0) } else { prop = memberUnresolved{valueUnresolved{r: vm.r, ref: n}} } @@ -2499,7 +2499,7 @@ func (_pushArrayItem) exec(vm *vm) { if arr.length < math.MaxUint32 { arr.length++ } else { - panic(vm.r.newError(vm.r.global.RangeError, "Invalid array length")) + panic(vm.r.newError(vm.r.getRangeError(), "Invalid array length")) } val := vm.stack[vm.sp-1] arr.values = append(arr.values, val) @@ -2520,7 +2520,7 @@ func (_pushArraySpread) exec(vm *vm) { if arr.length < math.MaxUint32 { arr.length++ } else { - panic(vm.r.newError(vm.r.global.RangeError, "Invalid array length")) + panic(vm.r.newError(vm.r.getRangeError(), "Invalid array length")) } arr.values = append(arr.values, val) arr.objCount++ @@ -2567,7 +2567,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++ } @@ -3387,7 +3387,6 @@ func (numargs call) exec(vm *vm) { v := vm.stack[vm.sp-n-1] // callee obj := vm.toCallee(v) -repeat: switch f := obj.self.(type) { case *classFuncObject: f.Call(FunctionCall{}) // throws @@ -3440,9 +3439,8 @@ repeat: vm.popCtx() vm.sp -= n + 1 vm.pc++ - case *lazyObject: - obj.self = f.create(obj) - goto repeat + case *templatedFuncObject: + f.vmCall(vm, n) default: vm.r.typeErrorResult(true, "Not a function: %s", obj.toString()) } @@ -3859,7 +3857,7 @@ func (n *newArrowFunc) 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) { @@ -4568,7 +4566,6 @@ func (_typeof) exec(vm *vm) { case valueNull: r = stringObjectC case *Object: - repeat: if v == nil { r = stringFunction vm.stack[vm.sp-1] = r @@ -4576,7 +4573,7 @@ func (_typeof) exec(vm *vm) { break } switch s := v.self.(type) { - case *classFuncObject, *methodFuncObject, *funcObject, *nativeFuncObject, *wrappedFuncObject, *boundFuncObject, *arrowFuncObject: + case *classFuncObject, *methodFuncObject, *funcObject, *nativeFuncObject, *wrappedFuncObject, *boundFuncObject, *arrowFuncObject, *templatedFuncObject: r = stringFunction case *proxyObject: if s.call == nil { @@ -4584,9 +4581,6 @@ func (_typeof) exec(vm *vm) { } else { r = stringFunction } - case *lazyObject: - v.self = s.create(v) - goto repeat default: r = stringObjectC } @@ -4639,7 +4633,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++ } @@ -4664,8 +4658,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++ } @@ -5096,7 +5090,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 @@ -5123,7 +5117,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) @@ -5140,7 +5134,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) } @@ -5154,7 +5148,7 @@ func (vm *vm) loadThis(v Value) { if v != nil { vm.push(v) } else { - panic(vm.r.newError(vm.r.global.ReferenceError, "Must call super constructor in derived class before accessing 'this'")) + panic(vm.r.newError(vm.r.getReferenceError(), "Must call super constructor in derived class before accessing 'this'")) } vm.pc++ } @@ -5238,7 +5232,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