From 8707fd6e7f696281c8eb6b62772a5819371b39a6 Mon Sep 17 00:00:00 2001
From: Daniel <me@daniel.gs>
Date: Wed, 13 Mar 2019 02:25:21 -0700
Subject: [PATCH] fix `to_json` builtin function bug (#138)

---
 objects/builtin_json.go    |  7 ++++++-
 objects/conversion.go      | 15 +++++++++++++++
 runtime/vm_builtin_test.go |  9 ++++++++-
 3 files changed, 29 insertions(+), 2 deletions(-)

diff --git a/objects/builtin_json.go b/objects/builtin_json.go
index b341365..d792212 100644
--- a/objects/builtin_json.go
+++ b/objects/builtin_json.go
@@ -12,7 +12,12 @@ func builtinToJSON(args ...Object) (Object, error) {
 		return nil, ErrWrongNumArguments
 	}
 
-	res, err := json.Marshal(objectToInterface(args[0]))
+	v := objectToInterface(args[0])
+	if vErr, isErr := v.(error); isErr {
+		v = vErr.Error()
+	}
+
+	res, err := json.Marshal(v)
 	if err != nil {
 		return &Error{Value: &String{Value: err.Error()}}, nil
 	}
diff --git a/objects/conversion.go b/objects/conversion.go
index 714f261..306fdb1 100644
--- a/objects/conversion.go
+++ b/objects/conversion.go
@@ -1,6 +1,7 @@
 package objects
 
 import (
+	"errors"
 	"fmt"
 	"strconv"
 	"time"
@@ -178,11 +179,25 @@ func objectToInterface(o Object) (res interface{}) {
 		for i, val := range o.Value {
 			res.([]interface{})[i] = objectToInterface(val)
 		}
+	case *ImmutableArray:
+		res = make([]interface{}, len(o.Value))
+		for i, val := range o.Value {
+			res.([]interface{})[i] = objectToInterface(val)
+		}
 	case *Map:
 		res = make(map[string]interface{})
 		for key, v := range o.Value {
 			res.(map[string]interface{})[key] = objectToInterface(v)
 		}
+	case *ImmutableMap:
+		res = make(map[string]interface{})
+		for key, v := range o.Value {
+			res.(map[string]interface{})[key] = objectToInterface(v)
+		}
+	case *Time:
+		res = o.Value
+	case *Error:
+		res = errors.New(o.String())
 	case Object:
 		return o
 	}
diff --git a/runtime/vm_builtin_test.go b/runtime/vm_builtin_test.go
index b5198b2..1ae3311 100644
--- a/runtime/vm_builtin_test.go
+++ b/runtime/vm_builtin_test.go
@@ -127,17 +127,24 @@ func TestBuiltinFunction(t *testing.T) {
 
 	// to_json
 	expect(t, `out = to_json(5)`, []byte("5"))
+	expect(t, `out = to_json("foobar")`, []byte(`"foobar"`))
 	expect(t, `out = to_json({foo: 5})`, []byte("{\"foo\":5}"))
+	expect(t, `out = to_json(immutable({foo: 5}))`, []byte("{\"foo\":5}"))
+	expect(t, `out = to_json([1,2,3])`, []byte("[1,2,3]"))
+	expect(t, `out = to_json(immutable([1,2,3]))`, []byte("[1,2,3]"))
 	expect(t, `out = to_json({foo: "bar"})`, []byte("{\"foo\":\"bar\"}"))
 	expect(t, `out = to_json({foo: 1.8})`, []byte("{\"foo\":1.8}"))
 	expect(t, `out = to_json({foo: true})`, []byte("{\"foo\":true}"))
 	expect(t, `out = to_json({foo: '8'})`, []byte("{\"foo\":56}"))
 	expect(t, `out = to_json({foo: bytes("foo")})`, []byte("{\"foo\":\"Zm9v\"}")) // json encoding returns []byte as base64 encoded string
 	expect(t, `out = to_json({foo: ["bar", 1, 1.8, '8', true]})`, []byte("{\"foo\":[\"bar\",1,1.8,56,true]}"))
+	expect(t, `out = to_json({foo: immutable(["bar", 1, 1.8, '8', true])})`, []byte("{\"foo\":[\"bar\",1,1.8,56,true]}"))
 	expect(t, `out = to_json({foo: [["bar", 1], ["bar", 1]]})`, []byte("{\"foo\":[[\"bar\",1],[\"bar\",1]]}"))
 	expect(t, `out = to_json({foo: {string: "bar", int: 1, float: 1.8, char: '8', bool: true}})`, []byte("{\"foo\":{\"bool\":true,\"char\":56,\"float\":1.8,\"int\":1,\"string\":\"bar\"}}"))
+	expect(t, `out = to_json({foo: immutable({string: "bar", int: 1, float: 1.8, char: '8', bool: true})})`, []byte("{\"foo\":{\"bool\":true,\"char\":56,\"float\":1.8,\"int\":1,\"string\":\"bar\"}}"))
 	expect(t, `out = to_json({foo: {map1: {string: "bar"}, map2: {int: "1"}}})`, []byte("{\"foo\":{\"map1\":{\"string\":\"bar\"},\"map2\":{\"int\":\"1\"}}}"))
 	expect(t, `out = to_json([["bar", 1], ["bar", 1]])`, []byte("[[\"bar\",1],[\"bar\",1]]"))
+	expect(t, `out = to_json(error("my error"))`, []byte(`"error: \"my error\""`))
 
 	// from_json
 	expect(t, `out = from_json("{\"foo\":5}").foo`, 5.0)
@@ -148,8 +155,8 @@ func TestBuiltinFunction(t *testing.T) {
 	expect(t, `out = from_json("{\"foo\":[[\"bar\",1],[\"bar\",1]]}").foo[0]`, ARR{"bar", 1.0})
 	expect(t, `out = from_json("{\"foo\":{\"bool\":true,\"char\":56,\"float\":1.8,\"int\":1,\"string\":\"bar\"}}").foo.bool`, true)
 	expect(t, `out = from_json("{\"foo\":{\"map1\":{\"string\":\"bar\"},\"map2\":{\"int\":\"1\"}}}").foo.map1.string`, "bar")
-
 	expect(t, `out = from_json("5")`, 5.0)
+	expect(t, `out = from_json("\"foobar\"")`, "foobar")
 	expect(t, `out = from_json("[\"bar\",1,1.8,56,true]")`, ARR{"bar", 1.0, 1.8, 56.0, true})
 
 	// sprintf