package goja

import (
	
	
)

func ( *Runtime) ( []Value,  *Object) *Object {
	if  != nil &&  != .getObject() {
		 := .getPrototypeFromCtor(, nil, .global.ObjectPrototype)
		return .newBaseObject(, classObject).val
	}
	if len() > 0 {
		 := [0]
		if  != _undefined &&  != _null {
			return .ToObject()
		}
	}
	return .NewObject()
}

func ( *Runtime) ( FunctionCall) Value {
	 := .Argument(0).ToObject()
	 := .self.proto()
	if  == nil {
		return _null
	}
	return 
}

func ( *Runtime) ( Value) Value {
	if  == nil {
		return _undefined
	}
	var , , ,  bool
	var ,  *Object
	var  Value
	if ,  := .(*valueProperty);  {
		 = .writable
		 = .configurable
		 = .enumerable
		 = .accessor
		 = .value
		 = .getterFunc
		 = .setterFunc
	} else {
		 = true
		 = true
		 = true
		 = 
	}

	 := .NewObject()
	 := .self
	if ! {
		.setOwnStr("value", , false)
		.setOwnStr("writable", .toBoolean(), false)
	} else {
		if  != nil {
			.setOwnStr("get", , false)
		} else {
			.setOwnStr("get", _undefined, false)
		}
		if  != nil {
			.setOwnStr("set", , false)
		} else {
			.setOwnStr("set", _undefined, false)
		}
	}
	.setOwnStr("enumerable", .toBoolean(), false)
	.setOwnStr("configurable", .toBoolean(), false)

	return 
}

func ( *Runtime) ( FunctionCall) Value {
	 := .Argument(0).ToObject()
	 := toPropertyKey(.Argument(1))
	return .valuePropToDescriptorObject(.getOwnProp())
}

func ( *Runtime) ( FunctionCall) Value {
	 := .Argument(0).ToObject()
	 := .newBaseObject(.global.ObjectPrototype, classObject).val
	for ,  := .self.iterateKeys()();  != nil; ,  = () {
		var  Value
		if .value == nil {
			 = .getOwnProp(.name)
			if  == nil {
				continue
			}
		} else {
			 = .value
		}
		 := .valuePropToDescriptorObject()
		if  != _undefined {
			createDataPropertyOrThrow(, .name, )
		}
	}
	return 
}

func ( *Runtime) ( FunctionCall) Value {
	 := .Argument(0).ToObject()

	return .newArrayValues(.self.stringKeys(true, nil))
}

func ( *Runtime) ( FunctionCall) Value {
	 := .Argument(0).ToObject()
	return .newArrayValues(.self.symbols(true, nil))
}

func ( *Runtime) ( Value) *valueProperty {
	if  == nil ||  == _undefined {
		return nil
	}
	 := .toObject()
	 := .self.getStr("get", nil)
	 := .self.getStr("set", nil)
	 := .self.getStr("writable", nil)
	 := .self.getStr("value", nil)
	if ( != nil ||  != nil) && ( != nil ||  != nil) {
		.typeErrorResult(true, "Invalid property descriptor. Cannot both specify accessors and a value or writable attribute")
	}

	 := &valueProperty{}
	if  != nil && .ToBoolean() {
		.writable = true
	}
	if  := .self.getStr("enumerable", nil);  != nil && .ToBoolean() {
		.enumerable = true
	}
	if  := .self.getStr("configurable", nil);  != nil && .ToBoolean() {
		.configurable = true
	}
	.value = 

	if  != nil &&  != _undefined {
		 := .toObject()
		if ,  := .self.assertCallable(); ! {
			.typeErrorResult(true, "getter must be a function")
		}
		.getterFunc = 
	}

	if  != nil &&  != _undefined {
		 := .toObject()
		if ,  := .self.assertCallable(); ! {
			.typeErrorResult(true, "setter must be a function")
		}
		.setterFunc = 
	}

	if .getterFunc != nil || .setterFunc != nil {
		.accessor = true
	}

	return 
}

func ( *Runtime) ( Value) ( PropertyDescriptor) {
	if ,  := .(*Object);  {
		 := .self

		// Save the original descriptor for reference
		.jsDescriptor = 

		.Value = .getStr("value", nil)

		if  := .getStr("writable", nil);  != nil {
			.Writable = ToFlag(.ToBoolean())
		}
		if  := .getStr("enumerable", nil);  != nil {
			.Enumerable = ToFlag(.ToBoolean())
		}
		if  := .getStr("configurable", nil);  != nil {
			.Configurable = ToFlag(.ToBoolean())
		}

		.Getter = .getStr("get", nil)
		.Setter = .getStr("set", nil)

		if .Getter != nil && .Getter != _undefined {
			if ,  := .toObject(.Getter).self.assertCallable(); ! {
				.typeErrorResult(true, "getter must be a function")
			}
		}

		if .Setter != nil && .Setter != _undefined {
			if ,  := .toObject(.Setter).self.assertCallable(); ! {
				.typeErrorResult(true, "setter must be a function")
			}
		}

		if (.Getter != nil || .Setter != nil) && (.Value != nil || .Writable != FLAG_NOT_SET) {
			.typeErrorResult(true, "Invalid property descriptor. Cannot both specify accessors and a value or writable attribute")
		}
	} else {
		.typeErrorResult(true, "Property description must be an object: %s", .String())
	}

	return
}

func ( *Runtime) ( *Object,  Value) {
	type  struct {
		 Value
		 PropertyDescriptor
	}
	 := .ToObject()
	var  []
	for ,  := iterateEnumerableProperties()();  != nil; ,  = () {
		 = append(, {
			: .name,
			: .toPropertyDescriptor(.value),
		})
	}
	for ,  := range  {
		.defineOwnProperty(., ., true)
	}
}

func ( *Runtime) ( FunctionCall) Value {
	var  *Object
	if  := .Argument(0);  != _null {
		if ,  := .(*Object);  {
			 = 
		} else {
			.typeErrorResult(true, "Object prototype may only be an Object or null: %s", .String())
		}
	}
	 := .newBaseObject(, classObject).val

	if  := .Argument(1);  != _undefined {
		._defineProperties(, )
	}

	return 
}

func ( *Runtime) ( FunctionCall) ( Value) {
	if ,  := .Argument(0).(*Object);  {
		 := .toPropertyDescriptor(.Argument(2))
		.defineOwnProperty(toPropertyKey(.Argument(1)), , true)
		 = .Argument(0)
	} else {
		.typeErrorResult(true, "Object.defineProperty called on non-object")
	}
	return
}

func ( *Runtime) ( FunctionCall) Value {
	 := .toObject(.Argument(0))
	._defineProperties(, .Argument(1))
	return 
}

func ( *Runtime) ( FunctionCall) Value {
	// ES6
	 := .Argument(0)
	if ,  := .(*Object);  {
		.self.preventExtensions(true)
		 := PropertyDescriptor{
			Configurable: FLAG_FALSE,
		}

		for ,  := .self.iterateKeys()();  != nil; ,  = () {
			if ,  := .value.(*valueProperty);  {
				.configurable = false
			} else {
				.defineOwnProperty(.name, , true)
			}
		}

		return 
	}
	return 
}

func ( *Runtime) ( FunctionCall) Value {
	 := .Argument(0)
	if ,  := .(*Object);  {
		.self.preventExtensions(true)

		for ,  := .self.iterateKeys()();  != nil; ,  = () {
			if ,  := .value.(*valueProperty);  {
				.configurable = false
				if !.accessor {
					.writable = false
				}
			} else {
				 := .getOwnProp(.name)
				 := PropertyDescriptor{
					Configurable: FLAG_FALSE,
				}
				if ,  := .(*valueProperty);  && .accessor {
					// no-op
				} else {
					.Writable = FLAG_FALSE
				}
				.defineOwnProperty(.name, , true)
			}
		}
		return 
	} else {
		// ES6 behavior
		return 
	}
}

func ( *Runtime) ( FunctionCall) ( Value) {
	 := .Argument(0)
	if ,  := .(*Object);  {
		.self.preventExtensions(true)
	}
	return 
}

func ( *Runtime) ( FunctionCall) Value {
	if ,  := .Argument(0).(*Object);  {
		if .self.isExtensible() {
			return valueFalse
		}
		for ,  := .self.iterateKeys()();  != nil; ,  = () {
			var  Value
			if .value == nil {
				 = .getOwnProp(.name)
				if  == nil {
					continue
				}
			} else {
				 = .value
			}
			if ,  := .(*valueProperty);  {
				if .configurable {
					return valueFalse
				}
			} else {
				return valueFalse
			}
		}
	}
	return valueTrue
}

func ( *Runtime) ( FunctionCall) Value {
	if ,  := .Argument(0).(*Object);  {
		if .self.isExtensible() {
			return valueFalse
		}
		for ,  := .self.iterateKeys()();  != nil; ,  = () {
			var  Value
			if .value == nil {
				 = .getOwnProp(.name)
				if  == nil {
					continue
				}
			} else {
				 = .value
			}
			if ,  := .(*valueProperty);  {
				if .configurable || .value != nil && .writable {
					return valueFalse
				}
			} else {
				return valueFalse
			}
		}
	}
	return valueTrue
}

func ( *Runtime) ( FunctionCall) Value {
	if ,  := .Argument(0).(*Object);  {
		if .self.isExtensible() {
			return valueTrue
		}
		return valueFalse
	} else {
		// ES6
		//r.typeErrorResult(true, "Object.isExtensible called on non-object")
		return valueFalse
	}
}

func ( *Runtime) ( FunctionCall) Value {
	 := .Argument(0).ToObject()

	return .newArrayValues(.self.stringKeys(false, nil))
}

func ( *Runtime) ( FunctionCall) Value {
	 := .Argument(0).ToObject()

	var  []Value

	for ,  := iterateEnumerableStringProperties()();  != nil; ,  = () {
		 = append(, .newArrayValues([]Value{.name, .value}))
	}

	return .newArrayValues()
}

func ( *Runtime) ( FunctionCall) Value {
	 := .Argument(0).ToObject()

	var  []Value

	for ,  := iterateEnumerableStringProperties()();  != nil; ,  = () {
		 = append(, .value)
	}

	return .newArrayValues()
}

func ( *Runtime) ( FunctionCall) Value {
	 := toPropertyKey(.Argument(0))
	 := .This.ToObject()
	if .hasOwnProperty() {
		return valueTrue
	} else {
		return valueFalse
	}
}

func ( *Runtime) ( FunctionCall) Value {
	if ,  := .Argument(0).(*Object);  {
		 := .This.ToObject()
		for {
			 = .self.proto()
			if  == nil {
				break
			}
			if  ==  {
				return valueTrue
			}
		}
	}
	return valueFalse
}

func ( *Runtime) ( FunctionCall) Value {
	 := toPropertyKey(.Argument(0))
	 := .This.ToObject()
	 := .getOwnProp()
	if  == nil {
		return valueFalse
	}
	if ,  := .(*valueProperty);  {
		if !.enumerable {
			return valueFalse
		}
	}
	return valueTrue
}

func ( *Runtime) ( FunctionCall) Value {
	switch o := .This.(type) {
	case valueNull:
		return stringObjectNull
	case valueUndefined:
		return stringObjectUndefined
	default:
		 := .ToObject()
		if ,  := .self.(*objectGoReflect);  {
			if  := .toString;  != nil {
				return ()
			}
		}
		var  string
		if isArray() {
			 = classArray
		} else {
			 = .self.className()
		}
		if  := .self.getSym(SymToStringTag, nil);  != nil {
			if ,  := .(String);  {
				 = .String()
			}
		}
		return newStringValue(fmt.Sprintf("[object %s]", ))
	}
}

func ( *Runtime) ( FunctionCall) Value {
	 := toMethod(.getVStr(.This, "toString"))
	return (FunctionCall{This: .This})
}

func ( *Runtime) ( FunctionCall) Value {
	 := .This.ToObject().self.proto()
	if  != nil {
		return 
	}
	return _null
}

func ( *Runtime) (,  Value) {
	.checkObjectCoercible()
	var  *Object
	if  != _null {
		if ,  := .(*Object);  {
			 = 
		} else {
			return
		}
	}
	if ,  := .(*Object);  {
		.self.setProto(, true)
	}
}

func ( *Runtime) ( FunctionCall) Value {
	.setObjectProto(.This, .Argument(0))
	return _undefined
}

func ( *Runtime) ( FunctionCall) Value {
	return .This.ToObject()
}

func ( *Runtime) ( FunctionCall) Value {
	 := .Argument(0).ToObject()
	if len(.Arguments) > 1 {
		for ,  := range .Arguments[1:] {
			if  != _undefined &&  != _null {
				 := .ToObject()
				for ,  := iterateEnumerableProperties()();  != nil; ,  = () {
					.setOwn(.name, .value, true)
				}
			}
		}
	}

	return 
}

func ( *Runtime) ( FunctionCall) Value {
	return .toBoolean(.Argument(0).SameAs(.Argument(1)))
}

func ( *Runtime) ( Value) *Object {
	if  != _null {
		if ,  := .(*Object);  {
			return 
		} else {
			panic(.NewTypeError("Object prototype may only be an Object or null: %s", ))
		}
	}
	return nil
}

func ( *Runtime) ( FunctionCall) Value {
	 := .Argument(0)
	.checkObjectCoercible()
	 := .toProto(.Argument(1))
	if ,  := .(*Object);  {
		.self.setProto(, true)
	}

	return 
}

func ( *Runtime) ( FunctionCall) Value {
	 := .Argument(0)
	.checkObjectCoercible()

	 := .newBaseObject(.global.ObjectPrototype, classObject).val

	 := .getIterator(, nil)
	.iterate(func( Value) {
		 := valueInt(0)
		 := valueInt(1)

		 := .toObject()
		 := .self.getIdx(, nil)
		 := .self.getIdx(, nil)
		 := toPropertyKey()

		createDataPropertyOrThrow(, , )
	})

	return 
}

func ( *Runtime) ( FunctionCall) Value {
	 := .Argument(0)
	 := .ToObject()
	 := toPropertyKey(.Argument(1))

	if .hasOwnProperty() {
		return valueTrue
	} else {
		return valueFalse
	}
}

func createObjectTemplate() *objectTemplate {
	 := newObjectTemplate()
	.protoFactory = func( *Runtime) *Object {
		return .getFunctionPrototype()
	}

	.putStr("length", func( *Runtime) Value { return valueProp(intToValue(1), false, false, true) })
	.putStr("name", func( *Runtime) Value { return valueProp(asciiString("Object"), false, false, true) })

	.putStr("prototype", func( *Runtime) Value { return valueProp(.global.ObjectPrototype, false, false, false) })

	.putStr("assign", func( *Runtime) Value { return .methodProp(.object_assign, "assign", 2) })
	.putStr("defineProperty", func( *Runtime) Value { return .methodProp(.object_defineProperty, "defineProperty", 3) })
	.putStr("defineProperties", func( *Runtime) Value { return .methodProp(.object_defineProperties, "defineProperties", 2) })
	.putStr("entries", func( *Runtime) Value { return .methodProp(.object_entries, "entries", 1) })
	.putStr("getOwnPropertyDescriptor", func( *Runtime) Value {
		return .methodProp(.object_getOwnPropertyDescriptor, "getOwnPropertyDescriptor", 2)
	})
	.putStr("getOwnPropertyDescriptors", func( *Runtime) Value {
		return .methodProp(.object_getOwnPropertyDescriptors, "getOwnPropertyDescriptors", 1)
	})
	.putStr("getPrototypeOf", func( *Runtime) Value { return .methodProp(.object_getPrototypeOf, "getPrototypeOf", 1) })
	.putStr("is", func( *Runtime) Value { return .methodProp(.object_is, "is", 2) })
	.putStr("getOwnPropertyNames", func( *Runtime) Value { return .methodProp(.object_getOwnPropertyNames, "getOwnPropertyNames", 1) })
	.putStr("getOwnPropertySymbols", func( *Runtime) Value {
		return .methodProp(.object_getOwnPropertySymbols, "getOwnPropertySymbols", 1)
	})
	.putStr("create", func( *Runtime) Value { return .methodProp(.object_create, "create", 2) })
	.putStr("seal", func( *Runtime) Value { return .methodProp(.object_seal, "seal", 1) })
	.putStr("freeze", func( *Runtime) Value { return .methodProp(.object_freeze, "freeze", 1) })
	.putStr("preventExtensions", func( *Runtime) Value { return .methodProp(.object_preventExtensions, "preventExtensions", 1) })
	.putStr("isSealed", func( *Runtime) Value { return .methodProp(.object_isSealed, "isSealed", 1) })
	.putStr("isFrozen", func( *Runtime) Value { return .methodProp(.object_isFrozen, "isFrozen", 1) })
	.putStr("isExtensible", func( *Runtime) Value { return .methodProp(.object_isExtensible, "isExtensible", 1) })
	.putStr("keys", func( *Runtime) Value { return .methodProp(.object_keys, "keys", 1) })
	.putStr("setPrototypeOf", func( *Runtime) Value { return .methodProp(.object_setPrototypeOf, "setPrototypeOf", 2) })
	.putStr("values", func( *Runtime) Value { return .methodProp(.object_values, "values", 1) })
	.putStr("fromEntries", func( *Runtime) Value { return .methodProp(.object_fromEntries, "fromEntries", 1) })
	.putStr("hasOwn", func( *Runtime) Value { return .methodProp(.object_hasOwn, "hasOwn", 2) })

	return 
}

var _objectTemplate *objectTemplate
var objectTemplateOnce sync.Once

func getObjectTemplate() *objectTemplate {
	objectTemplateOnce.Do(func() {
		_objectTemplate = createObjectTemplate()
	})
	return _objectTemplate
}

func ( *Runtime) () *Object {
	 := .global.Object
	if  == nil {
		 = &Object{runtime: }
		.global.Object = 
		.newTemplatedFuncObject(getObjectTemplate(), , func( FunctionCall) Value {
			return .builtin_Object(.Arguments, nil)
		}, .builtin_Object)
	}
	return 
}

/*
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 {
	 := newObjectTemplate()

	// null prototype

	.putStr("constructor", func( *Runtime) Value { return valueProp(.getObject(), true, false, true) })

	.putStr("toString", func( *Runtime) Value { return .methodProp(.objectproto_toString, "toString", 0) })
	.putStr("toLocaleString", func( *Runtime) Value { return .methodProp(.objectproto_toLocaleString, "toLocaleString", 0) })
	.putStr("valueOf", func( *Runtime) Value { return .methodProp(.objectproto_valueOf, "valueOf", 0) })
	.putStr("hasOwnProperty", func( *Runtime) Value { return .methodProp(.objectproto_hasOwnProperty, "hasOwnProperty", 1) })
	.putStr("isPrototypeOf", func( *Runtime) Value { return .methodProp(.objectproto_isPrototypeOf, "isPrototypeOf", 1) })
	.putStr("propertyIsEnumerable", func( *Runtime) Value {
		return .methodProp(.objectproto_propertyIsEnumerable, "propertyIsEnumerable", 1)
	})
	.putStr(__proto__, func( *Runtime) Value {
		return &valueProperty{
			accessor:     true,
			getterFunc:   .newNativeFunc(.objectproto_getProto, "get __proto__", 0),
			setterFunc:   .newNativeFunc(.objectproto_setProto, "set __proto__", 1),
			configurable: true,
		}
	})

	return 
}