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
 }
 
-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
 
-	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
 		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)
 	} else {
 		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 {
 			prop.writable = w
 		} else {
@@ -414,7 +399,7 @@ Reject:
 	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 {
 		var existing Value
 		if idx < int64(len(a.values)) {

+ 1 - 1
array_sparse.go

@@ -336,7 +336,7 @@ func (a *sparseArrayObject) expand() bool {
 	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 {
 		var existing Value
 		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) {
-	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"))
 	if obj, ok := item.(*Object); ok {
 		if isArray(obj) {
@@ -203,7 +205,7 @@ func (r *Runtime) arrayproto_concat_append(a *Object, item Value) {
 			for i := int64(0); i < length; i++ {
 				v := obj.self.get(intToValue(i))
 				if v != nil {
-					descr.putStr("value", v, false)
+					descr.Value = v
 					a.self.defineOwnProperty(intToValue(aLength), descr, false)
 					aLength++
 				} else {
@@ -214,7 +216,7 @@ func (r *Runtime) arrayproto_concat_append(a *Object, item Value) {
 			return
 		}
 	}
-	descr.putStr("value", item, false)
+	descr.Value = item
 	a.self.defineOwnProperty(intToValue(aLength), descr, false)
 }
 
@@ -269,14 +271,15 @@ func (r *Runtime) arrayproto_slice(call FunctionCall) Value {
 	a := r.newArrayLength(count)
 
 	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 {
 		p := o.self.get(intToValue(start))
 		if p != nil && p != _undefined {
-			descr.putStr("value", p, false)
+			descr.Value = p
 			a.self.defineOwnProperty(intToValue(n), descr, false)
 		}
 		start++

+ 6 - 5
builtin_json.go

@@ -86,11 +86,12 @@ func (r *Runtime) builtinJSON_decodeObject(d *json.Decoder) (*Object, error) {
 		}
 
 		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)
 		} else {
 			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)
 }
 
-func (r *Runtime) toPropertyDescriptor(v Value) objectImpl {
+func (r *Runtime) toPropertyDescr(v Value) (ret propertyDescr) {
 	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) {
 	type propItem struct {
 		name string
-		prop objectImpl
+		prop propertyDescr
 	}
 	props := p.ToObject(r)
 	var list []propItem
 	for item, f := props.self.enumerate(false, false)(); f != nil; item, f = f() {
 		list = append(list, propItem{
 			name: item.name,
-			prop: r.toPropertyDescriptor(props.self.getStr(item.name)),
+			prop: r.toPropertyDescr(props.self.getStr(item.name)),
 		})
 	}
 	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) {
 	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 {
 		r.typeErrorResult(true, "Object.defineProperty called on non-object")
 	}
@@ -177,7 +182,11 @@ func (r *Runtime) object_seal(call FunctionCall) Value {
 	// ES6
 	arg := call.Argument(0)
 	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() {
 			v := obj.self.getOwnProp(item.name)
 			if prop, ok := v.(*valueProperty); ok {
@@ -186,13 +195,7 @@ func (r *Runtime) object_seal(call FunctionCall) Value {
 				}
 				prop.configurable = false
 			} 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._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 {
 	arg := call.Argument(0)
 	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() {
 			v := obj.self.getOwnProp(item.name)
 			if prop, ok := v.(*valueProperty); ok {
@@ -215,13 +222,7 @@ func (r *Runtime) object_freeze(call FunctionCall) Value {
 					prop.writable = false
 				}
 			} 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)
 			}
 		}

+ 38 - 59
object.go

@@ -21,6 +21,14 @@ type Object struct {
 
 type iterNextFunc func() (propIterItem, iterNextFunc)
 
+type propertyDescr struct {
+	Value Value
+
+	Writable, Configurable, Enumerable Flag
+
+	Getter, Setter Value
+}
+
 type objectImpl interface {
 	sortable
 	className() string
@@ -36,12 +44,11 @@ type objectImpl interface {
 	hasOwnProperty(Value) bool
 	hasOwnPropertyStr(string) bool
 	_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
 	toPrimitiveString() Value
 	toPrimitive() Value
 	assertCallable() (call func(FunctionCall) Value, ok bool)
-	// defineOwnProperty(Value, property, bool) bool
 	deleteStr(name string, throw bool) bool
 	delete(name Value, throw bool) bool
 	proto() *Object
@@ -53,10 +60,6 @@ type objectImpl interface {
 	export() interface{}
 	exportType() reflect.Type
 	equal(objectImpl) bool
-
-	// clone(*_object, *_object, *_clone) *_object
-	// marshalJSON() json.Marshaler
-
 }
 
 type baseObject struct {
@@ -283,35 +286,10 @@ func (o *baseObject) hasOwnPropertyStr(name string) bool {
 	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
 
@@ -332,69 +310,69 @@ func (o *baseObject) _defineOwnProperty(name Value, existingValue Value, descr o
 		}
 
 		if !existing.configurable {
-			if configurable {
+			if descr.Configurable == FLAG_TRUE {
 				goto Reject
 			}
-			if hasEnumerable && enumerable != existing.enumerable {
+			if descr.Enumerable != FLAG_NOT_SET && descr.Enumerable.Bool() != existing.enumerable {
 				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 {
 				goto Reject
 			}
 		} else if !existing.accessor {
 			if !existing.configurable {
 				if !existing.writable {
-					if writable {
+					if descr.Writable == FLAG_TRUE {
 						goto Reject
 					}
-					if value != nil && !value.SameAs(existing.value) {
+					if descr.Value != nil && !descr.Value.SameAs(existing.value) {
 						goto Reject
 					}
 				}
 			}
 		} else {
 			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
 				}
 			}
 		}
 	}
 
-	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.setterFunc = nil
 	}
 
-	if value != nil || hasWritable {
+	if descr.Value != nil || descr.Writable != FLAG_NOT_SET {
 		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.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.accessor = true
 	}
@@ -408,14 +386,15 @@ func (o *baseObject) _defineOwnProperty(name Value, existingValue Value, descr o
 Reject:
 	o.val.runtime.typeErrorResult(throw, "Cannot redefine property: %s", name.ToString())
 	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()
-	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
-		if val == nil {
+		if existingVal == nil {
 			o.propNames = append(o.propNames, name)
 		}
 		return true

+ 1 - 1
object_args.go

@@ -93,7 +93,7 @@ func (a *argumentsObject) enumerate(all, recursive bool) iterNextFunc {
 	}).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()
 	if mapped, ok := a.values[name].(*mappedProperty); ok {
 		existing := &valueProperty{

+ 3 - 3
object_gomap.go

@@ -106,12 +106,12 @@ func (o *objectGoMapSimple) _putProp(name string, value Value, writable, enumera
 	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")
 		return false
 	}
-	o.put(name, descr.getStr("value"), throw)
+	o.put(name, descr.Value, throw)
 	return true
 }
 

+ 2 - 2
object_gomap_reflect.go

@@ -112,13 +112,13 @@ func (o *objectGoMapReflect) _putProp(name string, value Value, writable, enumer
 	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()
 	if !o.val.runtime.checkHostObjectPropertyDescr(name, descr, throw) {
 		return false
 	}
 
-	o.put(n, descr.getStr("value"), throw)
+	o.put(n, descr.Value, throw)
 	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)
 }
 
-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")
 		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)
 		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)
 		return false
 	}
 	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()
 	if ast.IsExported(name) {
 		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) {
 					return false
 				}
-				val := descr.getStr("value")
+				val := descr.Value
 				if val == nil {
 					val = _undefined
 				}

+ 1 - 1
object_goreflect_test.go

@@ -285,7 +285,7 @@ func TestGoReflectProp(t *testing.T) {
 
 func TestGoReflectRedefineFieldSuccess(t *testing.T) {
 	const SCRIPT = `
-	!!Object.defineProperty(o, "Test", {value: "AAA"});
+	Object.defineProperty(o, "Test", {value: "AAA"}) === 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
 }
 
-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 !o.val.runtime.checkHostObjectPropertyDescr(n.String(), descr, throw) {
 			return false
 		}
-		val := descr.getStr("value")
+		val := descr.Value
 		if val == nil {
 			val = _undefined
 		}

+ 3 - 4
object_goslice_reflect.go

@@ -150,12 +150,11 @@ func (o *objectGoSliceReflect) _putProp(name string, value Value, writable, enum
 	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
 	}
-	o.put(name, descr.getStr("value"), throw)
+	o.put(name, descr.Value, throw)
 	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)
 }
 
-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)
 	o.val.self = obj
 	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) {
 	v := &Object{}
 

+ 40 - 0
runtime.go

@@ -72,6 +72,25 @@ type global struct {
 	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 Runtime struct {
@@ -1380,3 +1399,24 @@ func Undefined() Value {
 func Null() Value {
 	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 {
 		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)
 }
 
-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 {
 		s.val.runtime.typeErrorResult(throw, "Cannot redefine property: %d", i)
 		return false

+ 29 - 12
value.go

@@ -716,19 +716,36 @@ func (o *Object) Keys() (keys []string) {
 	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).

+ 10 - 8
vm.go

@@ -1089,10 +1089,11 @@ func (s setPropGetter) exec(vm *vm) {
 	obj := vm.r.toObject(vm.stack[vm.sp-2])
 	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)
 
@@ -1106,10 +1107,11 @@ func (s setPropSetter) exec(vm *vm) {
 	obj := vm.r.toObject(vm.stack[vm.sp-2])
 	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)