playground: prevent caching memory-related compile time errors

It is important that nondeterministic errors are not cached. Otherwise,
playground users may encounter errors from a prevous run when the
current run may have otherwise succeeded.

The existing implementation prevents runtime OOM errors from being
cached. This change prevents compile time OOM errors from being cached
as well.

See related: https://golang.org/cl/130035

Fixes golang/go#28283

Change-Id: If734915935cc1013bcb9a87d0ae6ecd67505f231
Reviewed-on: https://go-review.googlesource.com/c/144297
Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org>
diff --git a/sandbox.go b/sandbox.go
index 9b80f78..5988085 100644
--- a/sandbox.go
+++ b/sandbox.go
@@ -42,8 +42,9 @@
 	progName = "prog.go"
 )
 
-// Runtime error strings that will not be cached if found in an Event message.
-var nonCachingRuntimeErrors = []string{"out of memory", "cannot allocate memory"}
+// Responses that contain these strings will not be cached due to
+// their non-deterministic nature.
+var nonCachingErrors = []string{"out of memory", "cannot allocate memory"}
 
 type request struct {
 	Body string
@@ -94,11 +95,18 @@
 				http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
 				return
 			}
+			for _, e := range nonCachingErrors {
+				if strings.Contains(resp.Errors, e) {
+					s.log.Errorf("cmdFunc compilation error: %q", resp.Errors)
+					http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
+					return
+				}
+			}
 			for _, el := range resp.Events {
 				if el.Kind != "stderr" {
 					continue
 				}
-				for _, e := range nonCachingRuntimeErrors {
+				for _, e := range nonCachingErrors {
 					if strings.Contains(el.Message, e) {
 						s.log.Errorf("cmdFunc runtime error: %q", el.Message)
 						http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
diff --git a/server_test.go b/server_test.go
index 34c8c26..4ff2f03 100644
--- a/server_test.go
+++ b/server_test.go
@@ -175,6 +175,12 @@
 		if r.Body == "allocate-memory-error" {
 			return &response{Events: []Event{{"cannot allocate memory", "stderr", 0}}}, nil
 		}
+		if r.Body == "oom-compile-error" {
+			return &response{Errors: "out of memory"}, nil
+		}
+		if r.Body == "allocate-memory-compile-error" {
+			return &response{Errors: "cannot allocate memory"}, nil
+		}
 		resp := &response{Events: []Event{{r.Body, "stdout", 0}}}
 		return resp, nil
 	})
@@ -204,6 +210,10 @@
 			[]byte(`{"Body":"oom-error"}`), nil},
 		{"Cannot allocate memory error in response body event message", http.MethodPost, http.StatusInternalServerError,
 			[]byte(`{"Body":"allocate-memory-error"}`), nil},
+		{"Out of memory error in response errors", http.MethodPost, http.StatusInternalServerError,
+			[]byte(`{"Body":"oom-compile-error"}`), nil},
+		{"Cannot allocate memory error in response errors", http.MethodPost, http.StatusInternalServerError,
+			[]byte(`{"Body":"allocate-memory-compile-error"}`), nil},
 	}
 
 	for _, tc := range testCases {