package goja

import (
	
	
	
	

	
)

const (
	classObject        = "Object"
	classArray         = "Array"
	classWeakSet       = "WeakSet"
	classWeakMap       = "WeakMap"
	classMap           = "Map"
	classMath          = "Math"
	classSet           = "Set"
	classFunction      = "Function"
	classAsyncFunction = "AsyncFunction"
	classNumber        = "Number"
	classString        = "String"
	classBoolean       = "Boolean"
	classError         = "Error"
	classRegExp        = "RegExp"
	classDate          = "Date"
	classJSON          = "JSON"
	classGlobal        = "global"
	classPromise       = "Promise"

	classArrayIterator        = "Array Iterator"
	classMapIterator          = "Map Iterator"
	classSetIterator          = "Set Iterator"
	classStringIterator       = "String Iterator"
	classRegExpStringIterator = "RegExp String Iterator"

	classGenerator         = "Generator"
	classGeneratorFunction = "GeneratorFunction"
)

var (
	hintDefault Value = asciiString("default")
	hintNumber  Value = asciiString("number")
	hintString  Value = asciiString("string")
)

type Object struct {
	id      uint64
	runtime *Runtime
	self    objectImpl

	weakRefs map[weakMap]Value
}

type iterNextFunc func() (propIterItem, iterNextFunc)

type PropertyDescriptor struct {
	jsDescriptor *Object

	Value Value

	Writable, Configurable, Enumerable Flag

	Getter, Setter Value
}

func ( *PropertyDescriptor) () bool {
	var  PropertyDescriptor
	return * == 
}

func ( *PropertyDescriptor) () bool {
	return .Setter != nil || .Getter != nil
}

func ( *PropertyDescriptor) () bool {
	return .Value != nil || .Writable != FLAG_NOT_SET
}

func ( *PropertyDescriptor) () bool {
	return !.IsAccessor() && !.IsData()
}

func ( *PropertyDescriptor) ( *Runtime) Value {
	if .jsDescriptor != nil {
		return .jsDescriptor
	}
	if .Empty() {
		return _undefined
	}
	 := .NewObject()
	 := .self

	if .Value != nil {
		._putProp("value", .Value, true, true, true)
	}

	if .Writable != FLAG_NOT_SET {
		._putProp("writable", valueBool(.Writable.Bool()), true, true, true)
	}

	if .Enumerable != FLAG_NOT_SET {
		._putProp("enumerable", valueBool(.Enumerable.Bool()), true, true, true)
	}

	if .Configurable != FLAG_NOT_SET {
		._putProp("configurable", valueBool(.Configurable.Bool()), true, true, true)
	}

	if .Getter != nil {
		._putProp("get", .Getter, true, true, true)
	}
	if .Setter != nil {
		._putProp("set", .Setter, true, true, true)
	}

	return 
}

func ( *PropertyDescriptor) () {
	if .Getter == nil && .Setter == nil {
		if .Value == nil {
			.Value = _undefined
		}
		if .Writable == FLAG_NOT_SET {
			.Writable = FLAG_FALSE
		}
	} else {
		if .Getter == nil {
			.Getter = _undefined
		}
		if .Setter == nil {
			.Setter = _undefined
		}
	}
	if .Enumerable == FLAG_NOT_SET {
		.Enumerable = FLAG_FALSE
	}
	if .Configurable == FLAG_NOT_SET {
		.Configurable = FLAG_FALSE
	}
}

type objectExportCacheItem map[reflect.Type]interface{}

type objectExportCtx struct {
	cache map[*Object]interface{}
}

type objectImpl interface {
	sortable
	className() string
	typeOf() String
	getStr(p unistring.String, receiver Value) Value
	getIdx(p valueInt, receiver Value) Value
	getSym(p *Symbol, receiver Value) Value

	getOwnPropStr(unistring.String) Value
	getOwnPropIdx(valueInt) Value
	getOwnPropSym(*Symbol) Value

	setOwnStr(p unistring.String, v Value, throw bool) bool
	setOwnIdx(p valueInt, v Value, throw bool) bool
	setOwnSym(p *Symbol, v Value, throw bool) bool

	setForeignStr(p unistring.String, v, receiver Value, throw bool) (res bool, handled bool)
	setForeignIdx(p valueInt, v, receiver Value, throw bool) (res bool, handled bool)
	setForeignSym(p *Symbol, v, receiver Value, throw bool) (res bool, handled bool)

	hasPropertyStr(unistring.String) bool
	hasPropertyIdx(idx valueInt) bool
	hasPropertySym(s *Symbol) bool

	hasOwnPropertyStr(unistring.String) bool
	hasOwnPropertyIdx(valueInt) bool
	hasOwnPropertySym(s *Symbol) bool

	defineOwnPropertyStr(name unistring.String, desc PropertyDescriptor, throw bool) bool
	defineOwnPropertyIdx(name valueInt, desc PropertyDescriptor, throw bool) bool
	defineOwnPropertySym(name *Symbol, desc PropertyDescriptor, throw bool) bool

	deleteStr(name unistring.String, throw bool) bool
	deleteIdx(idx valueInt, throw bool) bool
	deleteSym(s *Symbol, throw bool) bool

	assertCallable() (call func(FunctionCall) Value, ok bool)
	vmCall(vm *vm, n int)
	assertConstructor() func(args []Value, newTarget *Object) *Object
	proto() *Object
	setProto(proto *Object, throw bool) bool
	hasInstance(v Value) bool
	isExtensible() bool
	preventExtensions(throw bool) bool

	export(ctx *objectExportCtx) interface{}
	exportType() reflect.Type
	exportToMap(m reflect.Value, typ reflect.Type, ctx *objectExportCtx) error
	exportToArrayOrSlice(s reflect.Value, typ reflect.Type, ctx *objectExportCtx) error
	equal(objectImpl) bool

	iterateStringKeys() iterNextFunc
	iterateSymbols() iterNextFunc
	iterateKeys() iterNextFunc

	stringKeys(all bool, accum []Value) []Value
	symbols(all bool, accum []Value) []Value
	keys(all bool, accum []Value) []Value

	_putProp(name unistring.String, value Value, writable, enumerable, configurable bool) Value
	_putSym(s *Symbol, prop Value)
	getPrivateEnv(typ *privateEnvType, create bool) *privateElements
}

type baseObject struct {
	class      string
	val        *Object
	prototype  *Object
	extensible bool

	values    map[unistring.String]Value
	propNames []unistring.String

	lastSortedPropLen, idxPropCount int

	symValues *orderedMap

	privateElements map[*privateEnvType]*privateElements
}

type guardedObject struct {
	baseObject
	guardedProps map[unistring.String]struct{}
}

type primitiveValueObject struct {
	baseObject
	pValue Value
}

func ( *primitiveValueObject) (*objectExportCtx) interface{} {
	return .pValue.Export()
}

func ( *primitiveValueObject) () reflect.Type {
	return .pValue.ExportType()
}

type FunctionCall struct {
	This      Value
	Arguments []Value
}

type ConstructorCall struct {
	This      *Object
	Arguments []Value
	NewTarget *Object
}

func ( FunctionCall) ( int) Value {
	if  < len(.Arguments) {
		return .Arguments[]
	}
	return _undefined
}

func ( ConstructorCall) ( int) Value {
	if  < len(.Arguments) {
		return .Arguments[]
	}
	return _undefined
}

func ( *baseObject) () {
	.values = make(map[unistring.String]Value)
}

func ( *baseObject) () string {
	return .class
}

func ( *baseObject) () String {
	return stringObjectC
}

func ( *baseObject) ( unistring.String) bool {
	if .val.self.hasOwnPropertyStr() {
		return true
	}
	if .prototype != nil {
		return .prototype.self.hasPropertyStr()
	}
	return false
}

func ( *baseObject) ( valueInt) bool {
	return .val.self.hasPropertyStr(.string())
}

func ( *baseObject) ( *Symbol) bool {
	if .hasOwnPropertySym() {
		return true
	}
	if .prototype != nil {
		return .prototype.self.hasPropertySym()
	}
	return false
}

func ( *baseObject) (, ,  Value) Value {
	if  == nil && .prototype != nil {
		if  == nil {
			return .prototype.get(, .val)
		}
		return .prototype.get(, )
	}
	if ,  := .(*valueProperty);  {
		if  == nil {
			return .get(.val)
		}
		return .get()
	}
	return 
}

func ( *baseObject) ( Value,  unistring.String,  Value) Value {
	if  == nil && .prototype != nil {
		if  == nil {
			return .prototype.self.getStr(, .val)
		}
		return .prototype.self.getStr(, )
	}
	if ,  := .(*valueProperty);  {
		if  == nil {
			return .get(.val)
		}
		return .get()
	}
	return 
}

func ( *baseObject) ( valueInt,  Value) Value {
	return .val.self.getStr(.string(), )
}

func ( *baseObject) ( *Symbol,  Value) Value {
	return .getWithOwnProp(.getOwnPropSym(), , )
}

func ( *baseObject) ( unistring.String,  Value) Value {
	 := .values[]
	if  == nil {
		if .prototype != nil {
			if  == nil {
				return .prototype.self.getStr(, .val)
			}
			return .prototype.self.getStr(, )
		}
	}
	if ,  := .(*valueProperty);  {
		if  == nil {
			return .get(.val)
		}
		return .get()
	}
	return 
}

func ( *baseObject) ( valueInt) Value {
	return .val.self.getOwnPropStr(.string())
}

func ( *baseObject) ( *Symbol) Value {
	if .symValues != nil {
		return .symValues.get()
	}
	return nil
}

func ( *baseObject) ( unistring.String) Value {
	return .values[]
}

func ( *baseObject) ( unistring.String,  *valueProperty,  bool) bool {
	if !.configurable {
		if  {
			 := .val.runtime
			panic(.NewTypeError("Cannot delete property '%s' of %s", , .objectproto_toString(FunctionCall{This: .val})))
		}
		return false
	}
	return true
}

func ( *baseObject) ( unistring.String,  Value,  bool) bool {
	if ,  := .(*valueProperty);  {
		return .checkDeleteProp(, , )
	}
	return true
}

func ( *baseObject) ( unistring.String) {
	delete(.values, )
	for ,  := range .propNames {
		if  ==  {
			 := .propNames
			if namesMarkedForCopy() {
				 := make([]unistring.String, len()-1, shrinkCap(len(), cap()))
				copy(, [:])
				copy([:], [+1:])
				.propNames = 
			} else {
				copy([:], [+1:])
				[len()-1] = ""
				.propNames = [:len()-1]
			}
			if  < .lastSortedPropLen {
				.lastSortedPropLen--
				if  < .idxPropCount {
					.idxPropCount--
				}
			}
			break
		}
	}
}

func ( *baseObject) ( valueInt,  bool) bool {
	return .val.self.deleteStr(.string(), )
}

func ( *baseObject) ( *Symbol,  bool) bool {
	if .symValues != nil {
		if  := .symValues.get();  != nil {
			if !.checkDelete(.descriptiveString().string(), , ) {
				return false
			}
			.symValues.remove()
		}
	}
	return true
}

func ( *baseObject) ( unistring.String,  bool) bool {
	if ,  := .values[];  {
		if !.checkDelete(, , ) {
			return false
		}
		._delete()
	}
	return true
}

func ( *baseObject) ( *Object,  bool) bool {
	 := .prototype
	if .SameAs() {
		return true
	}
	if !.extensible {
		.val.runtime.typeErrorResult(, "%s is not extensible", .val)
		return false
	}
	for  := ;  != nil;  = .self.proto() {
		if .SameAs(.val) {
			.val.runtime.typeErrorResult(, "Cyclic __proto__ value")
			return false
		}
		if ,  := .self.(*proxyObject);  {
			break
		}
	}
	.prototype = 
	return true
}

func ( *baseObject) ( unistring.String,  Value,  bool) bool {
	 := .values[]
	if  == nil {
		if  := .prototype;  != nil {
			// we know it's foreign because prototype loops are not allowed
			if ,  := .self.setForeignStr(, , .val, );  {
				return 
			}
		}
		// new property
		if !.extensible {
			.val.runtime.typeErrorResult(, "Cannot add property %s, object is not extensible", )
			return false
		} else {
			.values[] = 
			 := copyNamesIfNeeded(.propNames, 1)
			.propNames = append(, )
		}
		return true
	}
	if ,  := .(*valueProperty);  {
		if !.isWritable() {
			.val.runtime.typeErrorResult(, "Cannot assign to read only property '%s'", )
			return false
		} else {
			.set(.val, )
		}
	} else {
		.values[] = 
	}
	return true
}

func ( *baseObject) ( valueInt,  Value,  bool) bool {
	return .val.self.setOwnStr(.string(), , )
}

func ( *baseObject) ( *Symbol,  Value,  bool) bool {
	var  Value
	if .symValues != nil {
		 = .symValues.get()
	}
	if  == nil {
		if  := .prototype;  != nil {
			// we know it's foreign because prototype loops are not allowed
			if ,  := .self.setForeignSym(, , .val, );  {
				return 
			}
		}
		// new property
		if !.extensible {
			.val.runtime.typeErrorResult(, "Cannot add property %s, object is not extensible", )
			return false
		} else {
			if .symValues == nil {
				.symValues = newOrderedMap(nil)
			}
			.symValues.set(, )
		}
		return true
	}
	if ,  := .(*valueProperty);  {
		if !.isWritable() {
			.val.runtime.typeErrorResult(, "Cannot assign to read only property '%s'", )
			return false
		} else {
			.set(.val, )
		}
	} else {
		.symValues.set(, )
	}
	return true
}

func ( *baseObject) ( unistring.String, , ,  Value,  bool) (bool, bool) {
	if  != nil {
		if ,  := .(*valueProperty);  {
			if !.isWritable() {
				.val.runtime.typeErrorResult(, "Cannot assign to read only property '%s'", )
				return false, true
			}
			if .setterFunc != nil {
				.set(, )
				return true, true
			}
		}
	} else {
		if  := .prototype;  != nil {
			if  !=  {
				return .self.setForeignStr(, , , )
			}
			return .self.setOwnStr(, , ), true
		}
	}
	return false, false
}

func ( *baseObject) ( valueInt, , ,  Value,  bool) (bool, bool) {
	if  != nil {
		if ,  := .(*valueProperty);  {
			if !.isWritable() {
				.val.runtime.typeErrorResult(, "Cannot assign to read only property '%d'", )
				return false, true
			}
			if .setterFunc != nil {
				.set(, )
				return true, true
			}
		}
	} else {
		if  := .prototype;  != nil {
			if  !=  {
				return .self.setForeignIdx(, , , )
			}
			return .self.setOwnIdx(, , ), true
		}
	}
	return false, false
}

func ( *baseObject) ( unistring.String, ,  Value,  bool) (bool, bool) {
	return ._setForeignStr(, .values[], , , )
}

func ( *baseObject) ( valueInt, ,  Value,  bool) (bool, bool) {
	if  := toIdx();  != math.MaxUint32 {
		.ensurePropOrder()
		if .idxPropCount == 0 {
			return ._setForeignIdx(, , nil, , )
		}
	}
	return .setForeignStr(.string(), , , )
}

func ( *baseObject) ( *Symbol, ,  Value,  bool) (bool, bool) {
	var  Value
	if .symValues != nil {
		 = .symValues.get()
	}
	if  != nil {
		if ,  := .(*valueProperty);  {
			if !.isWritable() {
				.val.runtime.typeErrorResult(, "Cannot assign to read only property '%s'", )
				return false, true
			}
			if .setterFunc != nil {
				.set(, )
				return true, true
			}
		}
	} else {
		if  := .prototype;  != nil {
			if  != .val {
				return .self.setForeignSym(, , , )
			}
			return .self.setOwnSym(, , ), true
		}
	}
	return false, false
}

func ( *baseObject) ( *Symbol) bool {
	if .symValues != nil {
		return .symValues.has()
	}
	return false
}

func ( *baseObject) ( unistring.String) bool {
	,  := .values[]
	return 
}

func ( *baseObject) ( valueInt) bool {
	return .val.self.hasOwnPropertyStr(.string())
}

func ( *baseObject) ( unistring.String,  Value,  PropertyDescriptor,  bool) ( Value,  bool) {

	,  := .Getter.(*Object)
	,  := .Setter.(*Object)

	var  *valueProperty

	if  == nil {
		if !.extensible {
			.val.runtime.typeErrorResult(, "Cannot define property %s, object is not extensible", )
			return nil, false
		}
		 = &valueProperty{}
	} else {
		if ,  = .(*valueProperty); ! {
			 = &valueProperty{
				writable:     true,
				enumerable:   true,
				configurable: true,
				value:        ,
			}
		}

		if !.configurable {
			if .Configurable == FLAG_TRUE {
				goto 
			}
			if .Enumerable != FLAG_NOT_SET && .Enumerable.Bool() != .enumerable {
				goto 
			}
		}
		if .accessor && .Value != nil || !.accessor && ( != nil ||  != nil) {
			if !.configurable {
				goto 
			}
		} else if !.accessor {
			if !.configurable {
				if !.writable {
					if .Writable == FLAG_TRUE {
						goto 
					}
					if .Value != nil && !.Value.SameAs(.value) {
						goto 
					}
				}
			}
		} else {
			if !.configurable {
				if .Getter != nil && .getterFunc !=  || .Setter != nil && .setterFunc !=  {
					goto 
				}
			}
		}
	}

	if .Writable == FLAG_TRUE && .Enumerable == FLAG_TRUE && .Configurable == FLAG_TRUE && .Value != nil {
		return .Value, true
	}

	if .Writable != FLAG_NOT_SET {
		.writable = .Writable.Bool()
	}
	if .Enumerable != FLAG_NOT_SET {
		.enumerable = .Enumerable.Bool()
	}
	if .Configurable != FLAG_NOT_SET {
		.configurable = .Configurable.Bool()
	}

	if .Value != nil {
		.value = .Value
		.getterFunc = nil
		.setterFunc = nil
	}

	if .Value != nil || .Writable != FLAG_NOT_SET {
		.accessor = false
	}

	if .Getter != nil {
		.getterFunc = propGetter(.val, .Getter, .val.runtime)
		.value = nil
		.accessor = true
	}

	if .Setter != nil {
		.setterFunc = propSetter(.val, .Setter, .val.runtime)
		.value = nil
		.accessor = true
	}

	if !.accessor && .value == nil {
		.value = _undefined
	}

	return , true

:
	.val.runtime.typeErrorResult(, "Cannot redefine property: %s", )
	return nil, false

}

func ( *baseObject) ( unistring.String,  PropertyDescriptor,  bool) bool {
	 := .values[]
	if ,  := ._defineOwnProperty(, , , );  {
		.values[] = 
		if  == nil {
			 := copyNamesIfNeeded(.propNames, 1)
			.propNames = append(, )
		}
		return true
	}
	return false
}

func ( *baseObject) ( valueInt,  PropertyDescriptor,  bool) bool {
	return .val.self.defineOwnPropertyStr(.string(), , )
}

func ( *baseObject) ( *Symbol,  PropertyDescriptor,  bool) bool {
	var  Value
	if .symValues != nil {
		 = .symValues.get()
	}
	if ,  := ._defineOwnProperty(.descriptiveString().string(), , , );  {
		if .symValues == nil {
			.symValues = newOrderedMap(nil)
		}
		.symValues.set(, )
		return true
	}
	return false
}

func ( *baseObject) ( unistring.String,  Value) {
	if ,  := .values[]; ! {
		 := copyNamesIfNeeded(.propNames, 1)
		.propNames = append(, )
	}

	.values[] = 
}

func valueProp( Value, , ,  bool) Value {
	if  &&  &&  {
		return 
	}
	return &valueProperty{
		value:        ,
		writable:     ,
		enumerable:   ,
		configurable: ,
	}
}

func ( *baseObject) ( unistring.String,  Value, , ,  bool) Value {
	 := valueProp(, , , )
	._put(, )
	return 
}

func ( *baseObject) ( *Symbol,  Value) {
	if .symValues == nil {
		.symValues = newOrderedMap(nil)
	}
	.symValues.set(, )
}

func ( *baseObject) ( *privateEnvType,  bool) *privateElements {
	 := .privateElements[]
	if  != nil &&  {
		panic(.val.runtime.NewTypeError("Private fields for the class have already been set"))
	}
	if  == nil &&  {
		 = &privateElements{
			fields: make([]Value, .numFields),
		}
		if .privateElements == nil {
			.privateElements = make(map[*privateEnvType]*privateElements)
		}
		.privateElements[] = 
	}
	return 
}

func ( *Object) ( unistring.String) Value {
	if ,  := .self.getStr(, nil).(*Object);  {
		if ,  := .self.assertCallable();  {
			 := (FunctionCall{
				This: ,
			})
			if ,  := .(*Object); ! {
				return 
			}
		}
	}
	return nil
}

func ( *Object) () Value {
	if  := .tryPrimitive("valueOf");  != nil {
		return 
	}

	if  := .tryPrimitive("toString");  != nil {
		return 
	}

	panic(.runtime.NewTypeError("Could not convert %v to primitive", .self))
}

func ( *Object) () Value {
	if  := .tryPrimitive("toString");  != nil {
		return 
	}

	if  := .tryPrimitive("valueOf");  != nil {
		return 
	}

	panic(.runtime.NewTypeError("Could not convert %v (%T) to primitive", .self, .self))
}

func ( *Object) ( Value) Value {
	 := toMethod(.self.getSym(SymToPrimitive, nil))
	if  != nil {
		 := (FunctionCall{
			This:      ,
			Arguments: []Value{},
		})
		if ,  := .(*Object); ! {
			return 
		}
		panic(.runtime.NewTypeError("Cannot convert object to primitive value"))
	}
	return nil
}

func ( *Object) () Value {
	if  := .tryExoticToPrimitive(hintNumber);  != nil {
		return 
	}

	return .ordinaryToPrimitiveNumber()
}

func ( *Object) () Value {
	if  := .tryExoticToPrimitive(hintString);  != nil {
		return 
	}

	return .ordinaryToPrimitiveString()
}

func ( *Object) () Value {
	if  := .tryExoticToPrimitive(hintDefault);  != nil {
		return 
	}
	return .ordinaryToPrimitiveNumber()
}

func ( *baseObject) () (func(FunctionCall) Value, bool) {
	return nil, false
}

func ( *baseObject) ( *vm,  int) {
	panic(.r.NewTypeError("Not a function: %s", .val.toString()))
}

func ( *baseObject) () func( []Value,  *Object) *Object {
	return nil
}

func ( *baseObject) () *Object {
	return .prototype
}

func ( *baseObject) () bool {
	return .extensible
}

func ( *baseObject) (bool) bool {
	.extensible = false
	return true
}

func ( *baseObject) () int {
	return toIntStrict(toLength(.val.self.getStr("length", nil)))
}

func ( *baseObject) ( int) Value {
	return .val.self.getIdx(valueInt(), nil)
}

func ( *baseObject) ( int,  int) {
	 := valueInt()
	 := valueInt()

	 := .val.self.getIdx(, nil)
	 := .val.self.getIdx(, nil)

	.val.self.setOwnIdx(, , false)
	.val.self.setOwnIdx(, , false)
}

func ( *baseObject) ( *objectExportCtx) interface{} {
	if ,  := .get(.val);  {
		return 
	}
	 := .stringKeys(false, nil)
	 := make(map[string]interface{}, len())
	.put(.val, )
	for ,  := range  {
		 := .String()
		 := .val.self.getStr(.string(), nil)
		if  != nil {
			[] = exportValue(, )
		} else {
			[] = nil
		}
	}

	return 
}

func ( *baseObject) () reflect.Type {
	return reflectTypeMap
}

func genericExportToMap( *Object,  reflect.Value,  reflect.Type,  *objectExportCtx) error {
	.Set(reflect.MakeMap())
	.putTyped(, , .Interface())
	 := .Key()
	 := .Elem()
	 := !reflectTypeString.AssignableTo()
	 := &enumerableIter{
		o:       ,
		wrapped: .self.iterateStringKeys(),
	}
	 := .runtime
	for ,  := .next();  != nil; ,  = () {
		var  reflect.Value
		var  error
		if  {
			 = reflect.New().Elem()
			 = .toReflectValue(.name, , )
			if  != nil {
				return fmt.Errorf("could not convert map key %s to %v: %w", .name.String(), , )
			}
		} else {
			 = reflect.ValueOf(.name.String())
		}

		 := .self.getStr(.name.string(), nil)
		if  != nil {
			 := reflect.New().Elem()
			 = .toReflectValue(, , )
			if  != nil {
				return fmt.Errorf("could not convert map value %v to %v at key %s: %w", , , .name.String(), )
			}
			.SetMapIndex(, )
		} else {
			.SetMapIndex(, reflect.Zero())
		}
	}

	return nil
}

func ( *baseObject) ( reflect.Value,  reflect.Type,  *objectExportCtx) error {
	return genericExportToMap(.val, , , )
}

func genericExportToArrayOrSlice( *Object,  reflect.Value,  reflect.Type,  *objectExportCtx) ( error) {
	 := .runtime

	if  := toMethod(.getV(, SymIterator));  != nil {
		// iterable

		var  []Value
		// cannot change (append to) the slice once it's been put into the cache, so we need to know its length beforehand
		 := .try(func() {
			 = .iterableToList(, )
		})
		if  != nil {
			return 
		}
		if .Kind() == reflect.Array {
			if .Len() != len() {
				return fmt.Errorf("cannot convert an iterable into an array, lengths mismatch (have %d, need %d)", len(), .Len())
			}
		} else {
			.Set(reflect.MakeSlice(, len(), len()))
		}
		.putTyped(, , .Interface())
		for ,  := range  {
			 = .toReflectValue(, .Index(), )
			if  != nil {
				return
			}
		}
	} else {
		// array-like
		var  Value
		if ,  := .self.assertCallable(); ! {
			 = .self.getStr("length", nil)
		}
		if  == nil {
			return fmt.Errorf("cannot convert %v to %v: not an array or iterable", , )
		}
		 := toIntStrict(toLength())
		if .Len() !=  {
			if .Kind() == reflect.Array {
				return fmt.Errorf("cannot convert an array-like object into an array, lengths mismatch (have %d, need %d)", , .Len())
			} else {
				.Set(reflect.MakeSlice(, , ))
			}
		}
		.putTyped(, , .Interface())
		for  := 0;  < ; ++ {
			 := nilSafe(.self.getIdx(valueInt(), nil))
			 = .toReflectValue(, .Index(), )
			if  != nil {
				return
			}
		}
	}

	return
}

func ( *baseObject) ( reflect.Value,  reflect.Type,  *objectExportCtx) error {
	return genericExportToArrayOrSlice(.val, , , )
}

type enumerableFlag int

const (
	_ENUM_UNKNOWN enumerableFlag = iota
	_ENUM_FALSE
	_ENUM_TRUE
)

type propIterItem struct {
	name       Value
	value      Value
	enumerable enumerableFlag
}

type objectPropIter struct {
	o         *baseObject
	propNames []unistring.String
	idx       int
}

type recursivePropIter struct {
	o    objectImpl
	cur  iterNextFunc
	seen map[unistring.String]struct{}
}

type enumerableIter struct {
	o       *Object
	wrapped iterNextFunc
}

func ( *enumerableIter) () (propIterItem, iterNextFunc) {
	for {
		var  propIterItem
		, .wrapped = .wrapped()
		if .wrapped == nil {
			return , nil
		}
		if .enumerable == _ENUM_FALSE {
			continue
		}
		if .enumerable == _ENUM_UNKNOWN {
			var  Value
			if .value == nil {
				 = .o.getOwnProp(.name)
			} else {
				 = .value
			}
			if  == nil {
				continue
			}
			if ,  := .(*valueProperty);  {
				if !.enumerable {
					continue
				}
			}
		}
		return , .
	}
}

func ( *recursivePropIter) () (propIterItem, iterNextFunc) {
	for {
		var  propIterItem
		, .cur = .cur()
		if .cur == nil {
			if  := .o.proto();  != nil {
				.cur = .self.iterateStringKeys()
				.o = .self
				continue
			}
			return propIterItem{}, nil
		}
		 := .name.string()
		if ,  := .seen[]; ! {
			.seen[] = struct{}{}
			return , .
		}
	}
}

func enumerateRecursive( *Object) iterNextFunc {
	return (&enumerableIter{
		o: ,
		wrapped: (&recursivePropIter{
			o:    .self,
			cur:  .self.iterateStringKeys(),
			seen: make(map[unistring.String]struct{}),
		}).next,
	}).next
}

func ( *objectPropIter) () (propIterItem, iterNextFunc) {
	for .idx < len(.propNames) {
		 := .propNames[.idx]
		.idx++
		 := .o.values[]
		if  != nil {
			return propIterItem{name: stringValueFromRaw(), value: }, .
		}
	}
	clearNamesCopyMarker(.propNames)
	return propIterItem{}, nil
}

var copyMarker = unistring.String(" ")

// Set a copy-on-write flag so that any subsequent modifications of anything below the current length
// trigger a copy.
// The marker is a special value put at the index position of cap-1. Capacity is set so that the marker is
// beyond the current length (therefore invisible to normal slice operations).
// This function is called before an iteration begins to avoid copying of the names array if
// there are no modifications within the iteration.
// Note that the copying also occurs in two cases: nested iterations (on the same object) and
// iterations after a previously abandoned iteration (because there is currently no mechanism to close an
// iterator). It is still better than copying every time.
func prepareNamesForCopy( []unistring.String) []unistring.String {
	if len() == 0 {
		return 
	}
	if namesMarkedForCopy() || cap() == len() {
		var  int
		if cap() == len() {
			 = growCap(len()+1, len(), cap())
		} else {
			 = cap()
		}
		 := make([]unistring.String, len(), )
		copy(, )
		 = 
	}
	[cap()-1 : cap()][0] = copyMarker
	return 
}

func namesMarkedForCopy( []unistring.String) bool {
	return cap() > len() && [cap()-1 : cap()][0] == copyMarker
}

func clearNamesCopyMarker( []unistring.String) {
	if cap() > len() {
		[cap()-1 : cap()][0] = ""
	}
}

func copyNamesIfNeeded( []unistring.String,  int) []unistring.String {
	if namesMarkedForCopy() && len()+ >= cap() {
		var  int
		 := len() +  + 1
		if  > cap() {
			 = growCap(, len(), cap())
		} else {
			 = cap()
		}
		 := make([]unistring.String, len(), )
		copy(, )
		return 
	}
	return 
}

func ( *baseObject) () iterNextFunc {
	.ensurePropOrder()
	 := prepareNamesForCopy(.propNames)
	.propNames = 
	return (&objectPropIter{
		o:         ,
		propNames: ,
	}).next
}

type objectSymbolIter struct {
	iter *orderedMapIter
}

func ( *objectSymbolIter) () (propIterItem, iterNextFunc) {
	 := .iter.next()
	if  != nil {
		return propIterItem{
			name:  .key,
			value: .value,
		}, .
	}
	return propIterItem{}, nil
}

func ( *baseObject) () iterNextFunc {
	if .symValues != nil {
		return (&objectSymbolIter{
			iter: .symValues.newIter(),
		}).next
	}
	return func() (propIterItem, iterNextFunc) {
		return propIterItem{}, nil
	}
}

type objectAllPropIter struct {
	o      *Object
	curStr iterNextFunc
}

func ( *objectAllPropIter) () (propIterItem, iterNextFunc) {
	,  := .curStr()
	if  != nil {
		.curStr = 
		return , .
	}
	return .o.self.iterateSymbols()()
}

func ( *baseObject) () iterNextFunc {
	return (&objectAllPropIter{
		o:      .val,
		curStr: .val.self.iterateStringKeys(),
	}).next
}

func ( *baseObject) (objectImpl) bool {
	// Rely on parent reference comparison
	return false
}

// hopefully this gets inlined
func ( *baseObject) () {
	if .lastSortedPropLen < len(.propNames) {
		.fixPropOrder()
	}
}

// Reorder property names so that any integer properties are shifted to the beginning of the list
// in ascending order. This is to conform to https://262.ecma-international.org/#sec-ordinaryownpropertykeys.
// Personally I think this requirement is strange. I can sort of understand where they are coming from,
// this way arrays can be specified just as objects with a 'magic' length property. However, I think
// it's safe to assume most devs don't use Objects to store integer properties. Therefore, performing
// property type checks when adding (and potentially looking up) properties would be unreasonable.
// Instead, we keep insertion order and only change it when (if) the properties get enumerated.
func ( *baseObject) () {
	 := .propNames
	for  := .lastSortedPropLen;  < len(); ++ {
		 := []
		if  := strToArrayIdx();  != math.MaxUint32 {
			 := sort.Search(.idxPropCount, func( int) bool {
				return strToArrayIdx([]) >= 
			})
			if  <  {
				if namesMarkedForCopy() {
					 := make([]unistring.String, len(), cap())
					copy([:], )
					copy([+1:+1], [:])
					copy([+1:], [+1:])
					 = 
					.propNames = 
				} else {
					copy([+1:+1], [:])
				}
				[] = 
			}
			.idxPropCount++
		}
	}
	.lastSortedPropLen = len()
}

func ( *baseObject) ( bool,  []Value) []Value {
	.ensurePropOrder()
	if  {
		for ,  := range .propNames {
			 = append(, stringValueFromRaw())
		}
	} else {
		for ,  := range .propNames {
			 := .values[]
			if ,  := .(*valueProperty);  && !.enumerable {
				continue
			}
			 = append(, stringValueFromRaw())
		}
	}
	return 
}

func ( *baseObject) ( bool,  []Value) []Value {
	if .symValues != nil {
		 := .symValues.newIter()
		if  {
			for {
				 := .next()
				if  == nil {
					break
				}
				 = append(, .key)
			}
		} else {
			for {
				 := .next()
				if  == nil {
					break
				}
				if ,  := .value.(*valueProperty);  {
					if !.enumerable {
						continue
					}
				}
				 = append(, .key)
			}
		}
	}

	return 
}

func ( *baseObject) ( bool,  []Value) []Value {
	return .symbols(, .val.self.stringKeys(, ))
}

func ( *baseObject) (Value) bool {
	panic(.val.runtime.NewTypeError("Expecting a function in instanceof check, but got %s", .val.toString()))
}

func toMethod( Value) func(FunctionCall) Value {
	if  == nil || IsUndefined() || IsNull() {
		return nil
	}
	if ,  := .(*Object);  {
		if ,  := .self.assertCallable();  {
			return 
		}
	}
	panic(newTypeError("%s is not a method", .String()))
}

func instanceOfOperator( Value,  *Object) bool {
	if  := toMethod(.self.getSym(SymHasInstance, ));  != nil {
		return (FunctionCall{
			This:      ,
			Arguments: []Value{},
		}).ToBoolean()
	}

	return .self.hasInstance()
}

func ( *Object) ( Value,  Value) Value {
	switch p := .(type) {
	case valueInt:
		return .self.getIdx(, )
	case *Symbol:
		return .self.getSym(, )
	default:
		return .self.getStr(.string(), )
	}
}

func ( *Object) ( Value) Value {
	switch p := .(type) {
	case valueInt:
		return .self.getOwnPropIdx()
	case *Symbol:
		return .self.getOwnPropSym()
	default:
		return .self.getOwnPropStr(.string())
	}
}

func ( *Object) ( Value) bool {
	switch p := .(type) {
	case valueInt:
		return .self.hasOwnPropertyIdx()
	case *Symbol:
		return .self.hasOwnPropertySym()
	default:
		return .self.hasOwnPropertyStr(.string())
	}
}

func ( *Object) ( Value) bool {
	switch p := .(type) {
	case valueInt:
		return .self.hasPropertyIdx()
	case *Symbol:
		return .self.hasPropertySym()
	default:
		return .self.hasPropertyStr(.string())
	}
}

func ( *Object) ( unistring.String, ,  Value,  bool) bool {
	if  ==  {
		return .self.setOwnStr(, , )
	} else {
		if ,  := .self.setForeignStr(, , , ); ! {
			if ,  := .(*Object);  {
				if  := .self.getOwnPropStr();  != nil {
					if ,  := .(*valueProperty);  {
						if .accessor {
							.runtime.typeErrorResult(, "Receiver property %s is an accessor", )
							return false
						}
						if !.writable {
							.runtime.typeErrorResult(, "Cannot assign to read only property '%s'", )
							return false
						}
					}
					return .self.defineOwnPropertyStr(, PropertyDescriptor{Value: }, )
				} else {
					return .self.defineOwnPropertyStr(, PropertyDescriptor{
						Value:        ,
						Writable:     FLAG_TRUE,
						Configurable: FLAG_TRUE,
						Enumerable:   FLAG_TRUE,
					}, )
				}
			} else {
				.runtime.typeErrorResult(, "Receiver is not an object: %v", )
				return false
			}
		} else {
			return 
		}
	}
}

func ( *Object) ( Value, ,  Value,  bool) bool {
	switch name := .(type) {
	case valueInt:
		return .setIdx(, , , )
	case *Symbol:
		return .setSym(, , , )
	default:
		return .setStr(.string(), , , )
	}
}

func ( *Object) ( Value,  Value,  bool) bool {
	switch name := .(type) {
	case valueInt:
		return .self.setOwnIdx(, , )
	case *Symbol:
		return .self.setOwnSym(, , )
	default:
		return .self.setOwnStr(.string(), , )
	}
}

func ( *Object) ( valueInt, ,  Value,  bool) bool {
	if  ==  {
		return .self.setOwnIdx(, , )
	} else {
		if ,  := .self.setForeignIdx(, , , ); ! {
			if ,  := .(*Object);  {
				if  := .self.getOwnPropIdx();  != nil {
					if ,  := .(*valueProperty);  {
						if .accessor {
							.runtime.typeErrorResult(, "Receiver property %s is an accessor", )
							return false
						}
						if !.writable {
							.runtime.typeErrorResult(, "Cannot assign to read only property '%s'", )
							return false
						}
					}
					.self.defineOwnPropertyIdx(, PropertyDescriptor{Value: }, )
				} else {
					.self.defineOwnPropertyIdx(, PropertyDescriptor{
						Value:        ,
						Writable:     FLAG_TRUE,
						Configurable: FLAG_TRUE,
						Enumerable:   FLAG_TRUE,
					}, )
				}
			} else {
				.runtime.typeErrorResult(, "Receiver is not an object: %v", )
				return false
			}
		} else {
			return 
		}
	}
	return true
}

func ( *Object) ( *Symbol, ,  Value,  bool) bool {
	if  ==  {
		return .self.setOwnSym(, , )
	} else {
		if ,  := .self.setForeignSym(, , , ); ! {
			if ,  := .(*Object);  {
				if  := .self.getOwnPropSym();  != nil {
					if ,  := .(*valueProperty);  {
						if .accessor {
							.runtime.typeErrorResult(, "Receiver property %s is an accessor", )
							return false
						}
						if !.writable {
							.runtime.typeErrorResult(, "Cannot assign to read only property '%s'", )
							return false
						}
					}
					.self.defineOwnPropertySym(, PropertyDescriptor{Value: }, )
				} else {
					.self.defineOwnPropertySym(, PropertyDescriptor{
						Value:        ,
						Writable:     FLAG_TRUE,
						Configurable: FLAG_TRUE,
						Enumerable:   FLAG_TRUE,
					}, )
				}
			} else {
				.runtime.typeErrorResult(, "Receiver is not an object: %v", )
				return false
			}
		} else {
			return 
		}
	}
	return true
}

func ( *Object) ( Value,  bool) bool {
	switch n := .(type) {
	case valueInt:
		return .self.deleteIdx(, )
	case *Symbol:
		return .self.deleteSym(, )
	default:
		return .self.deleteStr(.string(), )
	}
}

func ( *Object) ( Value,  PropertyDescriptor,  bool) bool {
	switch n := .(type) {
	case valueInt:
		return .self.defineOwnPropertyIdx(, , )
	case *Symbol:
		return .self.defineOwnPropertySym(, , )
	default:
		return .self.defineOwnPropertyStr(.string(), , )
	}
}

func ( *Object) () map[weakMap]Value {
	 := .weakRefs
	if  == nil {
		 = make(map[weakMap]Value)
		.weakRefs = 
	}
	return 
}

func ( *Object) () uint64 {
	 := .id
	if  == 0 {
		 = .runtime.genId()
		.id = 
	}
	return 
}

func ( *guardedObject) ( ...unistring.String) {
	if .guardedProps == nil {
		.guardedProps = make(map[unistring.String]struct{})
	}
	for ,  := range  {
		.guardedProps[] = struct{}{}
	}
}

func ( *guardedObject) ( unistring.String) {
	if ,  := .guardedProps[];  {
		.val.self = &.baseObject
	}
}

func ( *guardedObject) ( unistring.String,  Value,  bool) bool {
	 := .baseObject.setOwnStr(, , )
	if  {
		.check()
	}
	return 
}

func ( *guardedObject) ( unistring.String,  PropertyDescriptor,  bool) bool {
	 := .baseObject.defineOwnPropertyStr(, , )
	if  {
		.check()
	}
	return 
}

func ( *guardedObject) ( unistring.String,  bool) bool {
	 := .baseObject.deleteStr(, )
	if  {
		.check()
	}
	return 
}

func ( *objectExportCtx) ( *Object) (interface{}, bool) {
	if ,  := .cache[];  {
		if ,  := .(objectExportCacheItem);  {
			,  := [.self.exportType()]
			return , 
		} else {
			return , true
		}
	}
	return nil, false
}

func ( *objectExportCtx) ( *Object,  reflect.Type) (interface{}, bool) {
	if ,  := .cache[];  {
		if ,  := .(objectExportCacheItem);  {
			,  := []
			return , 
		} else {
			if reflect.TypeOf() ==  {
				return , true
			}
		}
	}
	return nil, false
}

func ( *objectExportCtx) ( *Object,  interface{}) {
	if .cache == nil {
		.cache = make(map[*Object]interface{})
	}
	if ,  := .cache[].(objectExportCacheItem);  {
		[.self.exportType()] = 
	} else {
		.cache[] = 
	}
}

func ( *objectExportCtx) ( *Object,  reflect.Type,  interface{}) {
	if .cache == nil {
		.cache = make(map[*Object]interface{})
	}
	,  := .cache[]
	if  {
		if ,  := .cache[].(objectExportCacheItem);  {
			[] = 
		} else {
			 := make(objectExportCacheItem, 2)
			[.self.exportType()] = 
			[] = 
			.cache[] = 
		}
	} else {
		 := make(objectExportCacheItem)
		[] = 
		.cache[] = 
	}
}

type enumPropertiesIter struct {
	o       *Object
	wrapped iterNextFunc
}

func ( *enumPropertiesIter) () (propIterItem, iterNextFunc) {
	for .wrapped != nil {
		,  := .wrapped()
		.wrapped = 
		if  == nil {
			break
		}
		if .value == nil {
			.value = .o.get(.name, nil)
			if .value == nil {
				continue
			}
		} else {
			if ,  := .value.(*valueProperty);  {
				.value = .get(.o)
			}
		}
		return , .
	}
	return propIterItem{}, nil
}

func iterateEnumerableProperties( *Object) iterNextFunc {
	return (&enumPropertiesIter{
		o: ,
		wrapped: (&enumerableIter{
			o:       ,
			wrapped: .self.iterateKeys(),
		}).next,
	}).next
}

func iterateEnumerableStringProperties( *Object) iterNextFunc {
	return (&enumPropertiesIter{
		o: ,
		wrapped: (&enumerableIter{
			o:       ,
			wrapped: .self.iterateStringKeys(),
		}).next,
	}).next
}

type privateId struct {
	typ      *privateEnvType
	name     unistring.String
	idx      uint32
	isMethod bool
}

type privateEnvType struct {
	numFields, numMethods uint32
}

type privateNames map[unistring.String]*privateId

type privateEnv struct {
	instanceType, staticType *privateEnvType

	names privateNames

	outer *privateEnv
}

type privateElements struct {
	methods []Value
	fields  []Value
}

func ( *privateId) () string {
	return "#" + .name.String()
}

func ( *privateId) () unistring.String {
	return privateIdString(.name)
}