Browse Source

Do not panic when trying to assign to a non-addressable reflect value (see #78).

Dmitry Panov 7 years ago
parent
commit
7239ea9475
2 changed files with 50 additions and 1 deletions
  1. 5 1
      object_goreflect.go
  2. 45 0
      object_goreflect_test.go

+ 5 - 1
object_goreflect.go

@@ -147,7 +147,7 @@ func (o *objectGoReflect) getOwnProp(name string) Value {
 		if v := o._getField(name); v.IsValid() {
 		if v := o._getField(name); v.IsValid() {
 			return &valueProperty{
 			return &valueProperty{
 				value:      o.val.runtime.ToValue(v.Interface()),
 				value:      o.val.runtime.ToValue(v.Interface()),
-				writable:   true,
+				writable:   v.CanSet(),
 				enumerable: true,
 				enumerable: true,
 			}
 			}
 		}
 		}
@@ -176,6 +176,10 @@ func (o *objectGoReflect) putStr(name string, val Value, throw bool) {
 func (o *objectGoReflect) _put(name string, val Value, throw bool) bool {
 func (o *objectGoReflect) _put(name string, val Value, throw bool) bool {
 	if o.value.Kind() == reflect.Struct {
 	if o.value.Kind() == reflect.Struct {
 		if v := o._getField(name); v.IsValid() {
 		if v := o._getField(name); v.IsValid() {
+			if !v.CanSet() {
+				o.val.runtime.typeErrorResult(throw, "Cannot assign to a non-addressable or read-only property %s of a host object", name)
+				return false
+			}
 			vv, err := o.val.runtime.toReflectValue(val, v.Type())
 			vv, err := o.val.runtime.toReflectValue(val, v.Type())
 			if err != nil {
 			if err != nil {
 				o.val.runtime.typeErrorResult(throw, "Go struct conversion error: %v", err)
 				o.val.runtime.typeErrorResult(throw, "Go struct conversion error: %v", err)

+ 45 - 0
object_goreflect_test.go

@@ -585,6 +585,51 @@ func TestNonStructAnonFields(t *testing.T) {
 	}
 	}
 }
 }
 
 
+func TestStructNonAddressable(t *testing.T) {
+	type S struct {
+		Field int
+	}
+
+	const SCRIPT = `
+	"use strict";
+	
+	if (Object.getOwnPropertyDescriptor(s, "Field").writable) {
+		throw new Error("Field is writable");
+	}
+
+	if (!Object.getOwnPropertyDescriptor(s1, "Field").writable) {
+		throw new Error("Field is non-writable");
+	}
+
+	s1.Field = 42;
+
+	var result;
+	try {
+		s.Field = 42;
+		result = false;
+	} catch (e) {
+		result = e instanceof TypeError;
+	}
+	
+	result;
+`
+
+	var s S
+	vm := New()
+	vm.Set("s", s)
+	vm.Set("s1", &s)
+	v, err := vm.RunString(SCRIPT)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if !v.StrictEquals(valueTrue) {
+		t.Fatalf("Unexpected result: %v", v)
+	}
+	if s.Field != 42 {
+		t.Fatalf("Unexpected s.Field value: %d", s.Field)
+	}
+}
+
 func BenchmarkGoReflectGet(b *testing.B) {
 func BenchmarkGoReflectGet(b *testing.B) {
 	type parent struct {
 	type parent struct {
 		field, Test1, Test2, Test3, Test4, Test5, Test string
 		field, Test1, Test2, Test3, Test4, Test5, Test string