Browse Source

Added Object.DefineDataProperty() and Object.DefineAccessorProperty(). See #45

Dmitry Panov 8 years ago
parent
commit
9bbb616c12
20 changed files with 275 additions and 186 deletions
  1. 6 21
      array.go
  2. 1 1
      array_sparse.go
  3. 14 11
      builtin_array.go
  4. 6 5
      builtin_json.go
  5. 49 48
      builtin_object.go
  6. 38 59
      object.go
  7. 1 1
      object_args.go
  8. 3 3
      object_gomap.go
  9. 2 2
      object_gomap_reflect.go
  10. 6 6
      object_goreflect.go
  11. 1 1
      object_goreflect_test.go
  12. 2 2
      object_goslice.go
  13. 3 4
      object_goslice_reflect.go
  14. 1 1
      object_lazy.go
  15. 56 0
      object_test.go
  16. 40 0
      runtime.go
  17. 6 0
      runtime_test.go
  18. 1 1
      string.go
  19. 29 12
      value.go
  20. 10 8
      vm.go

+ 6 - 21
array.go

@@ -365,37 +365,22 @@ func (a *arrayObject) expand(idx int64) bool {
 	return true
 	return true
 }
 }
 
 
-func (r *Runtime) defineArrayLength(prop *valueProperty, descr objectImpl, setter func(Value, bool) bool, throw bool) bool {
+func (r *Runtime) defineArrayLength(prop *valueProperty, descr propertyDescr, setter func(Value, bool) bool, throw bool) bool {
 	ret := true
 	ret := true
 
 
-	if cfg := descr.getStr("configurable"); cfg != nil && cfg.ToBoolean() {
+	if descr.Configurable == FLAG_TRUE || descr.Enumerable == FLAG_TRUE || descr.Getter != nil || descr.Setter != nil {
 		ret = false
 		ret = false
 		goto Reject
 		goto Reject
 	}
 	}
 
 
-	if cfg := descr.getStr("enumerable"); cfg != nil && cfg.ToBoolean() {
-		ret = false
-		goto Reject
-	}
-
-	if cfg := descr.getStr("get"); cfg != nil {
-		ret = false
-		goto Reject
-	}
-
-	if cfg := descr.getStr("set"); cfg != nil {
-		ret = false
-		goto Reject
-	}
-
-	if newLen := descr.getStr("value"); newLen != nil {
+	if newLen := descr.Value; newLen != nil {
 		ret = setter(newLen, false)
 		ret = setter(newLen, false)
 	} else {
 	} else {
 		ret = true
 		ret = true
 	}
 	}
 
 
-	if wr := descr.getStr("writable"); wr != nil {
-		w := wr.ToBoolean()
+	if descr.Writable != FLAG_NOT_SET {
+		w := descr.Writable.Bool()
 		if prop.writable {
 		if prop.writable {
 			prop.writable = w
 			prop.writable = w
 		} else {
 		} else {
@@ -414,7 +399,7 @@ Reject:
 	return ret
 	return ret
 }
 }
 
 
-func (a *arrayObject) defineOwnProperty(n Value, descr objectImpl, throw bool) bool {
+func (a *arrayObject) defineOwnProperty(n Value, descr propertyDescr, throw bool) bool {
 	if idx := toIdx(n); idx >= 0 {
 	if idx := toIdx(n); idx >= 0 {
 		var existing Value
 		var existing Value
 		if idx < int64(len(a.values)) {
 		if idx < int64(len(a.values)) {

+ 1 - 1
array_sparse.go

@@ -336,7 +336,7 @@ func (a *sparseArrayObject) expand() bool {
 	return true
 	return true
 }
 }
 
 
-func (a *sparseArrayObject) defineOwnProperty(n Value, descr objectImpl, throw bool) bool {
+func (a *sparseArrayObject) defineOwnProperty(n Value, descr propertyDescr, throw bool) bool {
 	if idx := toIdx(n); idx >= 0 {
 	if idx := toIdx(n); idx >= 0 {
 		var existing Value
 		var existing Value
 		i := a.findIdx(idx)
 		i := a.findIdx(idx)

+ 14 - 11
builtin_array.go

@@ -192,10 +192,12 @@ func (r *Runtime) arrayproto_toLocaleString(call FunctionCall) Value {
 }
 }
 
 
 func (r *Runtime) arrayproto_concat_append(a *Object, item Value) {
 func (r *Runtime) arrayproto_concat_append(a *Object, item Value) {
-	descr := r.NewObject().self
-	descr.putStr("writable", valueTrue, false)
-	descr.putStr("enumerable", valueTrue, false)
-	descr.putStr("configurable", valueTrue, false)
+	descr := propertyDescr{
+		Writable:     FLAG_TRUE,
+		Enumerable:   FLAG_TRUE,
+		Configurable: FLAG_TRUE,
+	}
+
 	aLength := toLength(a.self.getStr("length"))
 	aLength := toLength(a.self.getStr("length"))
 	if obj, ok := item.(*Object); ok {
 	if obj, ok := item.(*Object); ok {
 		if isArray(obj) {
 		if isArray(obj) {
@@ -203,7 +205,7 @@ func (r *Runtime) arrayproto_concat_append(a *Object, item Value) {
 			for i := int64(0); i < length; i++ {
 			for i := int64(0); i < length; i++ {
 				v := obj.self.get(intToValue(i))
 				v := obj.self.get(intToValue(i))
 				if v != nil {
 				if v != nil {
-					descr.putStr("value", v, false)
+					descr.Value = v
 					a.self.defineOwnProperty(intToValue(aLength), descr, false)
 					a.self.defineOwnProperty(intToValue(aLength), descr, false)
 					aLength++
 					aLength++
 				} else {
 				} else {
@@ -214,7 +216,7 @@ func (r *Runtime) arrayproto_concat_append(a *Object, item Value) {
 			return
 			return
 		}
 		}
 	}
 	}
-	descr.putStr("value", item, false)
+	descr.Value = item
 	a.self.defineOwnProperty(intToValue(aLength), descr, false)
 	a.self.defineOwnProperty(intToValue(aLength), descr, false)
 }
 }
 
 
@@ -269,14 +271,15 @@ func (r *Runtime) arrayproto_slice(call FunctionCall) Value {
 	a := r.newArrayLength(count)
 	a := r.newArrayLength(count)
 
 
 	n := int64(0)
 	n := int64(0)
-	descr := r.NewObject().self
-	descr.putStr("writable", valueTrue, false)
-	descr.putStr("enumerable", valueTrue, false)
-	descr.putStr("configurable", valueTrue, false)
+	descr := propertyDescr{
+		Writable:     FLAG_TRUE,
+		Enumerable:   FLAG_TRUE,
+		Configurable: FLAG_TRUE,
+	}
 	for start < end {
 	for start < end {
 		p := o.self.get(intToValue(start))
 		p := o.self.get(intToValue(start))
 		if p != nil && p != _undefined {
 		if p != nil && p != _undefined {
-			descr.putStr("value", p, false)
+			descr.Value = p
 			a.self.defineOwnProperty(intToValue(n), descr, false)
 			a.self.defineOwnProperty(intToValue(n), descr, false)
 		}
 		}
 		start++
 		start++

+ 6 - 5
builtin_json.go

@@ -86,11 +86,12 @@ func (r *Runtime) builtinJSON_decodeObject(d *json.Decoder) (*Object, error) {
 		}
 		}
 
 
 		if key == "__proto__" {
 		if key == "__proto__" {
-			descr := r.NewObject().self
-			descr.putStr("value", value, false)
-			descr.putStr("writable", valueTrue, false)
-			descr.putStr("enumerable", valueTrue, false)
-			descr.putStr("configurable", valueTrue, false)
+			descr := propertyDescr{
+				Value:        value,
+				Writable:     FLAG_TRUE,
+				Enumerable:   FLAG_TRUE,
+				Configurable: FLAG_TRUE,
+			}
 			object.self.defineOwnProperty(string__proto__, descr, false)
 			object.self.defineOwnProperty(string__proto__, descr, false)
 		} else {
 		} else {
 			object.self.putStr(key, value, false)
 			object.self.putStr(key, value, false)

+ 49 - 48
builtin_object.go

@@ -83,51 +83,59 @@ func (r *Runtime) object_getOwnPropertyNames(call FunctionCall) Value {
 	return r.newArrayValues(values)
 	return r.newArrayValues(values)
 }
 }
 
 
-func (r *Runtime) toPropertyDescriptor(v Value) objectImpl {
+func (r *Runtime) toPropertyDescr(v Value) (ret propertyDescr) {
 	if o, ok := v.(*Object); ok {
 	if o, ok := v.(*Object); ok {
-		desc := o.self
-		hasValue := desc.getStr("value") != nil
-		hasWritable := desc.getStr("writable") != nil
-		hasGet := false
-		hasSet := false
-		if get := desc.getStr("get"); get != nil {
-			if get != _undefined {
-				if _, ok := r.toObject(get).self.assertCallable(); !ok {
-					r.typeErrorResult(true, "getter must be callable")
-				}
-			}
-			hasGet = true
+		descr := o.self
+
+		ret.Value = descr.getStr("value")
+
+		if p := descr.getStr("writable"); p != nil {
+			ret.Writable = ToFlag(p.ToBoolean())
 		}
 		}
-		if set := desc.getStr("set"); set != nil {
-			if set != _undefined {
-				if _, ok := r.toObject(set).self.assertCallable(); !ok {
-					r.typeErrorResult(true, "setter must be callable")
-				}
+		if p := descr.getStr("enumerable"); p != nil {
+			ret.Enumerable = ToFlag(p.ToBoolean())
+		}
+		if p := descr.getStr("configurable"); p != nil {
+			ret.Configurable = ToFlag(p.ToBoolean())
+		}
+
+		ret.Getter = descr.getStr("get")
+		ret.Setter = descr.getStr("set")
+
+		if ret.Getter != nil && ret.Getter != _undefined {
+			if _, ok := r.toObject(ret.Getter).self.assertCallable(); !ok {
+				r.typeErrorResult(true, "getter must be a function")
 			}
 			}
-			hasSet = true
 		}
 		}
 
 
-		if (hasGet || hasSet) && (hasValue || hasWritable) {
-			r.typeErrorResult(true, "Invalid property.  A property cannot both have accessors and be writable or have a value")
+		if ret.Setter != nil && ret.Setter != _undefined {
+			if _, ok := r.toObject(ret.Setter).self.assertCallable(); !ok {
+				r.typeErrorResult(true, "setter must be a function")
+			}
 		}
 		}
 
 
-		return desc
+		if (ret.Getter != nil || ret.Setter != nil) && (ret.Value != nil || ret.Writable != FLAG_NOT_SET) {
+			r.typeErrorResult(true, "Invalid property descriptor. Cannot both specify accessors and a value or writable attribute")
+			return
+		}
+	} else {
+		r.typeErrorResult(true, "Property description must be an object: %s", v.String())
 	}
 	}
-	r.typeErrorResult(true, "Property description must be an object: %s", v.String())
-	return nil
+
+	return
 }
 }
 
 
 func (r *Runtime) _defineProperties(o *Object, p Value) {
 func (r *Runtime) _defineProperties(o *Object, p Value) {
 	type propItem struct {
 	type propItem struct {
 		name string
 		name string
-		prop objectImpl
+		prop propertyDescr
 	}
 	}
 	props := p.ToObject(r)
 	props := p.ToObject(r)
 	var list []propItem
 	var list []propItem
 	for item, f := props.self.enumerate(false, false)(); f != nil; item, f = f() {
 	for item, f := props.self.enumerate(false, false)(); f != nil; item, f = f() {
 		list = append(list, propItem{
 		list = append(list, propItem{
 			name: item.name,
 			name: item.name,
-			prop: r.toPropertyDescriptor(props.self.getStr(item.name)),
+			prop: r.toPropertyDescr(props.self.getStr(item.name)),
 		})
 		})
 	}
 	}
 	for _, prop := range list {
 	for _, prop := range list {
@@ -155,12 +163,9 @@ func (r *Runtime) object_create(call FunctionCall) Value {
 
 
 func (r *Runtime) object_defineProperty(call FunctionCall) (ret Value) {
 func (r *Runtime) object_defineProperty(call FunctionCall) (ret Value) {
 	if obj, ok := call.Argument(0).(*Object); ok {
 	if obj, ok := call.Argument(0).(*Object); ok {
-		if descr, ok := call.Argument(2).(*Object); ok {
-			obj.self.defineOwnProperty(call.Argument(1), descr.self, true)
-			ret = call.Argument(0)
-		} else {
-			r.typeErrorResult(true, "Property description must be an object: %v", call.Argument(2))
-		}
+		descr := r.toPropertyDescr(call.Argument(2))
+		obj.self.defineOwnProperty(call.Argument(1), descr, true)
+		ret = call.Argument(0)
 	} else {
 	} else {
 		r.typeErrorResult(true, "Object.defineProperty called on non-object")
 		r.typeErrorResult(true, "Object.defineProperty called on non-object")
 	}
 	}
@@ -177,7 +182,11 @@ func (r *Runtime) object_seal(call FunctionCall) Value {
 	// ES6
 	// ES6
 	arg := call.Argument(0)
 	arg := call.Argument(0)
 	if obj, ok := arg.(*Object); ok {
 	if obj, ok := arg.(*Object); ok {
-		var descr objectImpl
+		descr := propertyDescr{
+			Writable:     FLAG_TRUE,
+			Enumerable:   FLAG_TRUE,
+			Configurable: FLAG_FALSE,
+		}
 		for item, f := obj.self.enumerate(true, false)(); f != nil; item, f = f() {
 		for item, f := obj.self.enumerate(true, false)(); f != nil; item, f = f() {
 			v := obj.self.getOwnProp(item.name)
 			v := obj.self.getOwnProp(item.name)
 			if prop, ok := v.(*valueProperty); ok {
 			if prop, ok := v.(*valueProperty); ok {
@@ -186,13 +195,7 @@ func (r *Runtime) object_seal(call FunctionCall) Value {
 				}
 				}
 				prop.configurable = false
 				prop.configurable = false
 			} else {
 			} else {
-				if descr == nil {
-					descr = r.NewObject().self
-					descr.putStr("writable", valueTrue, false)
-					descr.putStr("enumerable", valueTrue, false)
-					descr.putStr("configurable", valueFalse, false)
-				}
-				descr.putStr("value", v, false)
+				descr.Value = v
 				obj.self.defineOwnProperty(newStringValue(item.name), descr, true)
 				obj.self.defineOwnProperty(newStringValue(item.name), descr, true)
 				//obj.self._putProp(item.name, v, true, true, false)
 				//obj.self._putProp(item.name, v, true, true, false)
 			}
 			}
@@ -206,7 +209,11 @@ func (r *Runtime) object_seal(call FunctionCall) Value {
 func (r *Runtime) object_freeze(call FunctionCall) Value {
 func (r *Runtime) object_freeze(call FunctionCall) Value {
 	arg := call.Argument(0)
 	arg := call.Argument(0)
 	if obj, ok := arg.(*Object); ok {
 	if obj, ok := arg.(*Object); ok {
-		var descr objectImpl
+		descr := propertyDescr{
+			Writable:     FLAG_FALSE,
+			Enumerable:   FLAG_TRUE,
+			Configurable: FLAG_FALSE,
+		}
 		for item, f := obj.self.enumerate(true, false)(); f != nil; item, f = f() {
 		for item, f := obj.self.enumerate(true, false)(); f != nil; item, f = f() {
 			v := obj.self.getOwnProp(item.name)
 			v := obj.self.getOwnProp(item.name)
 			if prop, ok := v.(*valueProperty); ok {
 			if prop, ok := v.(*valueProperty); ok {
@@ -215,13 +222,7 @@ func (r *Runtime) object_freeze(call FunctionCall) Value {
 					prop.writable = false
 					prop.writable = false
 				}
 				}
 			} else {
 			} else {
-				if descr == nil {
-					descr = r.NewObject().self
-					descr.putStr("writable", valueFalse, false)
-					descr.putStr("enumerable", valueTrue, false)
-					descr.putStr("configurable", valueFalse, false)
-				}
-				descr.putStr("value", v, false)
+				descr.Value = v
 				obj.self.defineOwnProperty(newStringValue(item.name), descr, true)
 				obj.self.defineOwnProperty(newStringValue(item.name), descr, true)
 			}
 			}
 		}
 		}

+ 38 - 59
object.go

@@ -21,6 +21,14 @@ type Object struct {
 
 
 type iterNextFunc func() (propIterItem, iterNextFunc)
 type iterNextFunc func() (propIterItem, iterNextFunc)
 
 
+type propertyDescr struct {
+	Value Value
+
+	Writable, Configurable, Enumerable Flag
+
+	Getter, Setter Value
+}
+
 type objectImpl interface {
 type objectImpl interface {
 	sortable
 	sortable
 	className() string
 	className() string
@@ -36,12 +44,11 @@ type objectImpl interface {
 	hasOwnProperty(Value) bool
 	hasOwnProperty(Value) bool
 	hasOwnPropertyStr(string) bool
 	hasOwnPropertyStr(string) bool
 	_putProp(name string, value Value, writable, enumerable, configurable bool) Value
 	_putProp(name string, value Value, writable, enumerable, configurable bool) Value
-	defineOwnProperty(name Value, descr objectImpl, throw bool) bool
+	defineOwnProperty(name Value, descr propertyDescr, throw bool) bool
 	toPrimitiveNumber() Value
 	toPrimitiveNumber() Value
 	toPrimitiveString() Value
 	toPrimitiveString() Value
 	toPrimitive() Value
 	toPrimitive() Value
 	assertCallable() (call func(FunctionCall) Value, ok bool)
 	assertCallable() (call func(FunctionCall) Value, ok bool)
-	// defineOwnProperty(Value, property, bool) bool
 	deleteStr(name string, throw bool) bool
 	deleteStr(name string, throw bool) bool
 	delete(name Value, throw bool) bool
 	delete(name Value, throw bool) bool
 	proto() *Object
 	proto() *Object
@@ -53,10 +60,6 @@ type objectImpl interface {
 	export() interface{}
 	export() interface{}
 	exportType() reflect.Type
 	exportType() reflect.Type
 	equal(objectImpl) bool
 	equal(objectImpl) bool
-
-	// clone(*_object, *_object, *_clone) *_object
-	// marshalJSON() json.Marshaler
-
 }
 }
 
 
 type baseObject struct {
 type baseObject struct {
@@ -283,35 +286,10 @@ func (o *baseObject) hasOwnPropertyStr(name string) bool {
 	return v != nil
 	return v != nil
 }
 }
 
 
-func (o *baseObject) _defineOwnProperty(name Value, existingValue Value, descr objectImpl, throw bool) (val Value, ok bool) {
-	var hasWritable, hasEnumerable, hasConfigurable bool
-	var writable, enumerable, configurable bool
+func (o *baseObject) _defineOwnProperty(name, existingValue Value, descr propertyDescr, throw bool) (val Value, ok bool) {
 
 
-	value := descr.getStr("value")
-
-	if p := descr.getStr("writable"); p != nil {
-		hasWritable = true
-		writable = p.ToBoolean()
-	}
-	if p := descr.getStr("enumerable"); p != nil {
-		hasEnumerable = true
-		enumerable = p.ToBoolean()
-	}
-	if p := descr.getStr("configurable"); p != nil {
-		hasConfigurable = true
-		configurable = p.ToBoolean()
-	}
-
-	getter := descr.getStr("get")
-	setter := descr.getStr("set")
-
-	if (getter != nil || setter != nil) && (value != nil || hasWritable) {
-		o.val.runtime.typeErrorResult(throw, "Invalid property descriptor. Cannot both specify accessors and a value or writable attribute")
-		return nil, false
-	}
-
-	getterObj, _ := getter.(*Object)
-	setterObj, _ := setter.(*Object)
+	getterObj, _ := descr.Getter.(*Object)
+	setterObj, _ := descr.Setter.(*Object)
 
 
 	var existing *valueProperty
 	var existing *valueProperty
 
 
@@ -332,69 +310,69 @@ func (o *baseObject) _defineOwnProperty(name Value, existingValue Value, descr o
 		}
 		}
 
 
 		if !existing.configurable {
 		if !existing.configurable {
-			if configurable {
+			if descr.Configurable == FLAG_TRUE {
 				goto Reject
 				goto Reject
 			}
 			}
-			if hasEnumerable && enumerable != existing.enumerable {
+			if descr.Enumerable != FLAG_NOT_SET && descr.Enumerable.Bool() != existing.enumerable {
 				goto Reject
 				goto Reject
 			}
 			}
 		}
 		}
-		if existing.accessor && value != nil || !existing.accessor && (getterObj != nil || setterObj != nil) {
+		if existing.accessor && descr.Value != nil || !existing.accessor && (getterObj != nil || setterObj != nil) {
 			if !existing.configurable {
 			if !existing.configurable {
 				goto Reject
 				goto Reject
 			}
 			}
 		} else if !existing.accessor {
 		} else if !existing.accessor {
 			if !existing.configurable {
 			if !existing.configurable {
 				if !existing.writable {
 				if !existing.writable {
-					if writable {
+					if descr.Writable == FLAG_TRUE {
 						goto Reject
 						goto Reject
 					}
 					}
-					if value != nil && !value.SameAs(existing.value) {
+					if descr.Value != nil && !descr.Value.SameAs(existing.value) {
 						goto Reject
 						goto Reject
 					}
 					}
 				}
 				}
 			}
 			}
 		} else {
 		} else {
 			if !existing.configurable {
 			if !existing.configurable {
-				if getter != nil && existing.getterFunc != getterObj || setter != nil && existing.setterFunc != setterObj {
+				if descr.Getter != nil && existing.getterFunc != getterObj || descr.Setter != nil && existing.setterFunc != setterObj {
 					goto Reject
 					goto Reject
 				}
 				}
 			}
 			}
 		}
 		}
 	}
 	}
 
 
-	if writable && enumerable && configurable && value != nil {
-		return value, true
+	if descr.Writable == FLAG_TRUE && descr.Enumerable == FLAG_TRUE && descr.Configurable == FLAG_TRUE && descr.Value != nil {
+		return descr.Value, true
 	}
 	}
 
 
-	if hasWritable {
-		existing.writable = writable
+	if descr.Writable != FLAG_NOT_SET {
+		existing.writable = descr.Writable.Bool()
 	}
 	}
-	if hasEnumerable {
-		existing.enumerable = enumerable
+	if descr.Enumerable != FLAG_NOT_SET {
+		existing.enumerable = descr.Enumerable.Bool()
 	}
 	}
-	if hasConfigurable {
-		existing.configurable = configurable
+	if descr.Configurable != FLAG_NOT_SET {
+		existing.configurable = descr.Configurable.Bool()
 	}
 	}
 
 
-	if value != nil {
-		existing.value = value
+	if descr.Value != nil {
+		existing.value = descr.Value
 		existing.getterFunc = nil
 		existing.getterFunc = nil
 		existing.setterFunc = nil
 		existing.setterFunc = nil
 	}
 	}
 
 
-	if value != nil || hasWritable {
+	if descr.Value != nil || descr.Writable != FLAG_NOT_SET {
 		existing.accessor = false
 		existing.accessor = false
 	}
 	}
 
 
-	if getter != nil {
-		existing.getterFunc = propGetter(o.val, getter, o.val.runtime)
+	if descr.Getter != nil {
+		existing.getterFunc = propGetter(o.val, descr.Getter, o.val.runtime)
 		existing.value = nil
 		existing.value = nil
 		existing.accessor = true
 		existing.accessor = true
 	}
 	}
 
 
-	if setter != nil {
-		existing.setterFunc = propSetter(o.val, setter, o.val.runtime)
+	if descr.Setter != nil {
+		existing.setterFunc = propSetter(o.val, descr.Setter, o.val.runtime)
 		existing.value = nil
 		existing.value = nil
 		existing.accessor = true
 		existing.accessor = true
 	}
 	}
@@ -408,14 +386,15 @@ func (o *baseObject) _defineOwnProperty(name Value, existingValue Value, descr o
 Reject:
 Reject:
 	o.val.runtime.typeErrorResult(throw, "Cannot redefine property: %s", name.ToString())
 	o.val.runtime.typeErrorResult(throw, "Cannot redefine property: %s", name.ToString())
 	return nil, false
 	return nil, false
+
 }
 }
 
 
-func (o *baseObject) defineOwnProperty(n Value, descr objectImpl, throw bool) bool {
+func (o *baseObject) defineOwnProperty(n Value, descr propertyDescr, throw bool) bool {
 	name := n.String()
 	name := n.String()
-	val := o.values[name]
-	if v, ok := o._defineOwnProperty(n, val, descr, throw); ok {
+	existingVal := o.values[name]
+	if v, ok := o._defineOwnProperty(n, existingVal, descr, throw); ok {
 		o.values[name] = v
 		o.values[name] = v
-		if val == nil {
+		if existingVal == nil {
 			o.propNames = append(o.propNames, name)
 			o.propNames = append(o.propNames, name)
 		}
 		}
 		return true
 		return true

+ 1 - 1
object_args.go

@@ -93,7 +93,7 @@ func (a *argumentsObject) enumerate(all, recursive bool) iterNextFunc {
 	}).next
 	}).next
 }
 }
 
 
-func (a *argumentsObject) defineOwnProperty(n Value, descr objectImpl, throw bool) bool {
+func (a *argumentsObject) defineOwnProperty(n Value, descr propertyDescr, throw bool) bool {
 	name := n.String()
 	name := n.String()
 	if mapped, ok := a.values[name].(*mappedProperty); ok {
 	if mapped, ok := a.values[name].(*mappedProperty); ok {
 		existing := &valueProperty{
 		existing := &valueProperty{

+ 3 - 3
object_gomap.go

@@ -106,12 +106,12 @@ func (o *objectGoMapSimple) _putProp(name string, value Value, writable, enumera
 	return value
 	return value
 }
 }
 
 
-func (o *objectGoMapSimple) defineOwnProperty(name Value, descr objectImpl, throw bool) bool {
-	if descr.hasPropertyStr("get") || descr.hasPropertyStr("set") {
+func (o *objectGoMapSimple) defineOwnProperty(name Value, descr propertyDescr, throw bool) bool {
+	if descr.Getter != nil || descr.Setter != nil {
 		o.val.runtime.typeErrorResult(throw, "Host objects do not support accessor properties")
 		o.val.runtime.typeErrorResult(throw, "Host objects do not support accessor properties")
 		return false
 		return false
 	}
 	}
-	o.put(name, descr.getStr("value"), throw)
+	o.put(name, descr.Value, throw)
 	return true
 	return true
 }
 }
 
 

+ 2 - 2
object_gomap_reflect.go

@@ -112,13 +112,13 @@ func (o *objectGoMapReflect) _putProp(name string, value Value, writable, enumer
 	return value
 	return value
 }
 }
 
 
-func (o *objectGoMapReflect) defineOwnProperty(n Value, descr objectImpl, throw bool) bool {
+func (o *objectGoMapReflect) defineOwnProperty(n Value, descr propertyDescr, throw bool) bool {
 	name := n.String()
 	name := n.String()
 	if !o.val.runtime.checkHostObjectPropertyDescr(name, descr, throw) {
 	if !o.val.runtime.checkHostObjectPropertyDescr(name, descr, throw) {
 		return false
 		return false
 	}
 	}
 
 
-	o.put(n, descr.getStr("value"), throw)
+	o.put(n, descr.Value, throw)
 	return true
 	return true
 }
 }
 
 

+ 6 - 6
object_goreflect.go

@@ -195,23 +195,23 @@ func (o *objectGoReflect) _putProp(name string, value Value, writable, enumerabl
 	return o.baseObject._putProp(name, value, writable, enumerable, configurable)
 	return o.baseObject._putProp(name, value, writable, enumerable, configurable)
 }
 }
 
 
-func (r *Runtime) checkHostObjectPropertyDescr(name string, descr objectImpl, throw bool) bool {
-	if descr.hasPropertyStr("get") || descr.hasPropertyStr("set") {
+func (r *Runtime) checkHostObjectPropertyDescr(name string, descr propertyDescr, throw bool) bool {
+	if descr.Getter != nil || descr.Setter != nil {
 		r.typeErrorResult(throw, "Host objects do not support accessor properties")
 		r.typeErrorResult(throw, "Host objects do not support accessor properties")
 		return false
 		return false
 	}
 	}
-	if wr := descr.getStr("writable"); wr != nil && !wr.ToBoolean() {
+	if descr.Writable == FLAG_FALSE {
 		r.typeErrorResult(throw, "Host object field %s cannot be made read-only", name)
 		r.typeErrorResult(throw, "Host object field %s cannot be made read-only", name)
 		return false
 		return false
 	}
 	}
-	if cfg := descr.getStr("configurable"); cfg != nil && cfg.ToBoolean() {
+	if descr.Configurable == FLAG_TRUE {
 		r.typeErrorResult(throw, "Host object field %s cannot be made configurable", name)
 		r.typeErrorResult(throw, "Host object field %s cannot be made configurable", name)
 		return false
 		return false
 	}
 	}
 	return true
 	return true
 }
 }
 
 
-func (o *objectGoReflect) defineOwnProperty(n Value, descr objectImpl, throw bool) bool {
+func (o *objectGoReflect) defineOwnProperty(n Value, descr propertyDescr, throw bool) bool {
 	name := n.String()
 	name := n.String()
 	if ast.IsExported(name) {
 	if ast.IsExported(name) {
 		if o.value.Kind() == reflect.Struct {
 		if o.value.Kind() == reflect.Struct {
@@ -219,7 +219,7 @@ func (o *objectGoReflect) defineOwnProperty(n Value, descr objectImpl, throw boo
 				if !o.val.runtime.checkHostObjectPropertyDescr(name, descr, throw) {
 				if !o.val.runtime.checkHostObjectPropertyDescr(name, descr, throw) {
 					return false
 					return false
 				}
 				}
-				val := descr.getStr("value")
+				val := descr.Value
 				if val == nil {
 				if val == nil {
 					val = _undefined
 					val = _undefined
 				}
 				}

+ 1 - 1
object_goreflect_test.go

@@ -285,7 +285,7 @@ func TestGoReflectProp(t *testing.T) {
 
 
 func TestGoReflectRedefineFieldSuccess(t *testing.T) {
 func TestGoReflectRedefineFieldSuccess(t *testing.T) {
 	const SCRIPT = `
 	const SCRIPT = `
-	!!Object.defineProperty(o, "Test", {value: "AAA"});
+	Object.defineProperty(o, "Test", {value: "AAA"}) === o;
 	`
 	`
 
 
 	o := testGoReflectMethod_O{}
 	o := testGoReflectMethod_O{}

+ 2 - 2
object_goslice.go

@@ -187,12 +187,12 @@ func (o *objectGoSlice) _putProp(name string, value Value, writable, enumerable,
 	return value
 	return value
 }
 }
 
 
-func (o *objectGoSlice) defineOwnProperty(n Value, descr objectImpl, throw bool) bool {
+func (o *objectGoSlice) defineOwnProperty(n Value, descr propertyDescr, throw bool) bool {
 	if idx := toIdx(n); idx >= 0 {
 	if idx := toIdx(n); idx >= 0 {
 		if !o.val.runtime.checkHostObjectPropertyDescr(n.String(), descr, throw) {
 		if !o.val.runtime.checkHostObjectPropertyDescr(n.String(), descr, throw) {
 			return false
 			return false
 		}
 		}
-		val := descr.getStr("value")
+		val := descr.Value
 		if val == nil {
 		if val == nil {
 			val = _undefined
 			val = _undefined
 		}
 		}

+ 3 - 4
object_goslice_reflect.go

@@ -150,12 +150,11 @@ func (o *objectGoSliceReflect) _putProp(name string, value Value, writable, enum
 	return value
 	return value
 }
 }
 
 
-func (o *objectGoSliceReflect) defineOwnProperty(name Value, descr objectImpl, throw bool) bool {
-	if descr.hasPropertyStr("get") || descr.hasPropertyStr("set") {
-		o.val.runtime.typeErrorResult(throw, "Host objects do not support accessor properties")
+func (o *objectGoSliceReflect) defineOwnProperty(name Value, descr propertyDescr, throw bool) bool {
+	if !o.val.runtime.checkHostObjectPropertyDescr(name.String(), descr, throw) {
 		return false
 		return false
 	}
 	}
-	o.put(name, descr.getStr("value"), throw)
+	o.put(name, descr.Value, throw)
 	return true
 	return true
 }
 }
 
 

+ 1 - 1
object_lazy.go

@@ -85,7 +85,7 @@ func (o *lazyObject) _putProp(name string, value Value, writable, enumerable, co
 	return obj._putProp(name, value, writable, enumerable, configurable)
 	return obj._putProp(name, value, writable, enumerable, configurable)
 }
 }
 
 
-func (o *lazyObject) defineOwnProperty(name Value, descr objectImpl, throw bool) bool {
+func (o *lazyObject) defineOwnProperty(name Value, descr propertyDescr, throw bool) bool {
 	obj := o.create(o.val)
 	obj := o.create(o.val)
 	o.val.self = obj
 	o.val.self = obj
 	return obj.defineOwnProperty(name, descr, throw)
 	return obj.defineOwnProperty(name, descr, throw)

+ 56 - 0
object_test.go

@@ -11,6 +11,62 @@ func TestArray1(t *testing.T) {
 	}
 	}
 }
 }
 
 
+func TestDefineProperty(t *testing.T) {
+	r := New()
+	o := r.NewObject()
+
+	err := o.DefineDataProperty("data", r.ToValue(42), FLAG_TRUE, FLAG_TRUE, FLAG_TRUE)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	err = o.DefineAccessorProperty("accessor_ro", r.ToValue(func() int {
+		return 1
+	}), nil, FLAG_TRUE, FLAG_TRUE)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	err = o.DefineAccessorProperty("accessor_rw",
+		r.ToValue(func(call FunctionCall) Value {
+			return o.Get("__hidden")
+		}),
+		r.ToValue(func(call FunctionCall) (ret Value) {
+			o.Set("__hidden", call.Argument(0))
+			return
+		}),
+		FLAG_TRUE, FLAG_TRUE)
+
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if v := o.Get("accessor_ro"); v.ToInteger() != 1 {
+		t.Fatalf("Unexpected accessor value: %v", v)
+	}
+
+	err = o.Set("accessor_ro", r.ToValue(2))
+	if err == nil {
+		t.Fatal("Expected an error")
+	}
+	if ex, ok := err.(*Exception); ok {
+		if msg := ex.Error(); msg != "TypeError: Cannot assign to read only property 'accessor_ro'" {
+			t.Fatalf("Unexpected error: '%s'", msg)
+		}
+	} else {
+		t.Fatalf("Unexected error type: %T", err)
+	}
+
+	err = o.Set("accessor_rw", 42)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if v := o.Get("accessor_rw"); v.ToInteger() != 42 {
+		t.Fatalf("Unexpected value: %v", v)
+	}
+}
+
 func BenchmarkPut(b *testing.B) {
 func BenchmarkPut(b *testing.B) {
 	v := &Object{}
 	v := &Object{}
 
 

+ 40 - 0
runtime.go

@@ -72,6 +72,25 @@ type global struct {
 	throwerProperty Value
 	throwerProperty Value
 }
 }
 
 
+type Flag int
+
+const (
+	FLAG_NOT_SET Flag = iota
+	FLAG_FALSE
+	FLAG_TRUE
+)
+
+func (f Flag) Bool() bool {
+	return f == FLAG_TRUE
+}
+
+func ToFlag(b bool) Flag {
+	if b {
+		return FLAG_TRUE
+	}
+	return FLAG_FALSE
+}
+
 type RandSource func() float64
 type RandSource func() float64
 
 
 type Runtime struct {
 type Runtime struct {
@@ -1380,3 +1399,24 @@ func Undefined() Value {
 func Null() Value {
 func Null() Value {
 	return _undefined
 	return _undefined
 }
 }
+
+func tryFunc(f func()) (err error) {
+	defer func() {
+		if x := recover(); x != nil {
+			switch x := x.(type) {
+			case *Exception:
+				err = x
+			case Value:
+				err = &Exception{
+					val: x,
+				}
+			default:
+				panic(x)
+			}
+		}
+	}()
+
+	f()
+
+	return nil
+}

+ 6 - 0
runtime_test.go

@@ -964,6 +964,12 @@ func TestNativeConstruct(t *testing.T) {
 	} else {
 	} else {
 		t.Fatal("Not a function")
 		t.Fatal("Not a function")
 	}
 	}
+
+	resp := &testNativeConstructHelper{}
+	value := rt.ToValue(resp)
+	if value.Export() != resp {
+		t.Fatal("no")
+	}
 }
 }
 
 
 /*
 /*

+ 1 - 1
string.go

@@ -150,7 +150,7 @@ func (s *stringObject) putStr(name string, val Value, throw bool) {
 	s.baseObject.putStr(name, val, throw)
 	s.baseObject.putStr(name, val, throw)
 }
 }
 
 
-func (s *stringObject) defineOwnProperty(n Value, descr objectImpl, throw bool) bool {
+func (s *stringObject) defineOwnProperty(n Value, descr propertyDescr, throw bool) bool {
 	if i := toIdx(n); i >= 0 && i < s.length {
 	if i := toIdx(n); i >= 0 && i < s.length {
 		s.val.runtime.typeErrorResult(throw, "Cannot redefine property: %d", i)
 		s.val.runtime.typeErrorResult(throw, "Cannot redefine property: %d", i)
 		return false
 		return false

+ 29 - 12
value.go

@@ -716,19 +716,36 @@ func (o *Object) Keys() (keys []string) {
 	return
 	return
 }
 }
 
 
-func (o *Object) Set(name string, value interface{}) (err error) {
-	defer func() {
-		if x := recover(); x != nil {
-			if ex, ok := x.(*Exception); ok {
-				err = ex
-			} else {
-				panic(x)
-			}
-		}
-	}()
+// DefineDataProperty is a Go equivalent of Object.defineProperty(o, name, {value: value, writable: writable,
+// configurable: configurable, enumerable: enumerable})
+func (o *Object) DefineDataProperty(name string, value Value, writable, configurable, enumerable Flag) error {
+	return tryFunc(func() {
+		o.self.defineOwnProperty(newStringValue(name), propertyDescr{
+			Value:        value,
+			Writable:     writable,
+			Configurable: configurable,
+			Enumerable:   enumerable,
+		}, true)
+	})
+}
 
 
-	o.self.putStr(name, o.runtime.ToValue(value), true)
-	return
+// DefineAccessorProperty is a Go equivalent of Object.defineProperty(o, name, {get: getter, set: setter,
+// configurable: configurable, enumerable: enumerable})
+func (o *Object) DefineAccessorProperty(name string, getter, setter Value, configurable, enumerable Flag) error {
+	return tryFunc(func() {
+		o.self.defineOwnProperty(newStringValue(name), propertyDescr{
+			Getter:       getter,
+			Setter:       setter,
+			Configurable: configurable,
+			Enumerable:   enumerable,
+		}, true)
+	})
+}
+
+func (o *Object) Set(name string, value interface{}) error {
+	return tryFunc(func() {
+		o.self.putStr(name, o.runtime.ToValue(value), true)
+	})
 }
 }
 
 
 // MarshalJSON returns JSON representation of the Object. It is equivalent to JSON.stringify(o).
 // MarshalJSON returns JSON representation of the Object. It is equivalent to JSON.stringify(o).

+ 10 - 8
vm.go

@@ -1089,10 +1089,11 @@ func (s setPropGetter) exec(vm *vm) {
 	obj := vm.r.toObject(vm.stack[vm.sp-2])
 	obj := vm.r.toObject(vm.stack[vm.sp-2])
 	val := vm.stack[vm.sp-1]
 	val := vm.stack[vm.sp-1]
 
 
-	descr := vm.r.NewObject().self
-	descr.putStr("get", val, false)
-	descr.putStr("configurable", valueTrue, false)
-	descr.putStr("enumerable", valueTrue, false)
+	descr := propertyDescr{
+		Getter:       val,
+		Configurable: FLAG_TRUE,
+		Enumerable:   FLAG_TRUE,
+	}
 
 
 	obj.self.defineOwnProperty(newStringValue(string(s)), descr, false)
 	obj.self.defineOwnProperty(newStringValue(string(s)), descr, false)
 
 
@@ -1106,10 +1107,11 @@ func (s setPropSetter) exec(vm *vm) {
 	obj := vm.r.toObject(vm.stack[vm.sp-2])
 	obj := vm.r.toObject(vm.stack[vm.sp-2])
 	val := vm.stack[vm.sp-1]
 	val := vm.stack[vm.sp-1]
 
 
-	descr := vm.r.NewObject().self
-	descr.putStr("set", val, false)
-	descr.putStr("configurable", valueTrue, false)
-	descr.putStr("enumerable", valueTrue, false)
+	descr := propertyDescr{
+		Setter:       val,
+		Configurable: FLAG_TRUE,
+		Enumerable:   FLAG_TRUE,
+	}
 
 
 	obj.self.defineOwnProperty(newStringValue(string(s)), descr, false)
 	obj.self.defineOwnProperty(newStringValue(string(s)), descr, false)