Skip to content

Commit

Permalink
BAAS-34801: add forced mem checks on new arrays and obj props (#127)
Browse files Browse the repository at this point in the history
  • Loading branch information
Gabri3l authored Dec 9, 2024
1 parent 476c6e0 commit 8b50f5b
Show file tree
Hide file tree
Showing 29 changed files with 237 additions and 71 deletions.
4 changes: 1 addition & 3 deletions added_values_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,6 @@ func TestIntStringEquality(t *testing.T) {
}

func TestAddedValuesMemUsage(t *testing.T) {
vm := New()

for _, tc := range []struct {
name string
val MemUsageReporter
Expand Down Expand Up @@ -77,7 +75,7 @@ func TestAddedValuesMemUsage(t *testing.T) {
},
} {
t.Run(tc.name, func(t *testing.T) {
mem, err := tc.val.MemUsage(NewMemUsageContext(vm, 100, 100, 100, 100, 0.1, nil))
mem, err := tc.val.MemUsage(NewMemUsageContext(100, 100, 100, 100, 0.1, nil))
if err != nil {
t.Fatalf("Unexpected error. Actual: %v Expected: nil", err)
}
Expand Down
8 changes: 4 additions & 4 deletions array_sparse_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ func TestSparseArrayObjectMemUsage(t *testing.T) {
}{
{
name: "mem below threshold",
mu: NewMemUsageContext(vm, 88, 5000, 50, 50, 0.1, TestNativeMemUsageChecker{}),
mu: NewMemUsageContext(88, 5000, 50, 50, 0.1, TestNativeMemUsageChecker{}),
sao: &sparseArrayObject{
items: []sparseArrayItem{
{
Expand All @@ -289,14 +289,14 @@ func TestSparseArrayObjectMemUsage(t *testing.T) {
},
{
name: "mem is SizeEmptyStruct for nil sparse array",
mu: NewMemUsageContext(vm, 88, 5000, 50, 50, 0.1, TestNativeMemUsageChecker{}),
mu: NewMemUsageContext(88, 5000, 50, 50, 0.1, TestNativeMemUsageChecker{}),
sao: nil,
expected: SizeEmptyStruct,
errExpected: nil,
},
{
name: "mem way above threshold returns first crossing of threshold",
mu: NewMemUsageContext(vm, 88, 100, 50, 50, 0.1, TestNativeMemUsageChecker{}),
mu: NewMemUsageContext(88, 100, 50, 50, 0.1, TestNativeMemUsageChecker{}),
sao: &sparseArrayObject{
items: []sparseArrayItem{
{
Expand Down Expand Up @@ -331,7 +331,7 @@ func TestSparseArrayObjectMemUsage(t *testing.T) {
},
{
name: "mem above estimate threshold and within memory limit returns correct usage",
mu: NewMemUsageContext(vm, 88, 100, 5, 50, 0.1, TestNativeMemUsageChecker{}),
mu: NewMemUsageContext(88, 100, 5, 50, 0.1, TestNativeMemUsageChecker{}),
sao: &sparseArrayObject{
items: []sparseArrayItem{
{
Expand Down
8 changes: 4 additions & 4 deletions array_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,23 +144,23 @@ func TestArrayObjectMemUsage(t *testing.T) {
}{
{
name: "mem below threshold given a nil slice of values",
mu: NewMemUsageContext(vm, 88, 5000, 50, 50, 0.1, TestNativeMemUsageChecker{}),
mu: NewMemUsageContext(88, 5000, 50, 50, 0.1, TestNativeMemUsageChecker{}),
ao: &arrayObject{},
// array overhead + array baseObject
expectedMem: SizeEmptyStruct + SizeEmptyStruct,
errExpected: nil,
},
{
name: "mem below threshold given empty slice of values",
mu: NewMemUsageContext(vm, 88, 5000, 50, 50, 0.1, TestNativeMemUsageChecker{}),
mu: NewMemUsageContext(88, 5000, 50, 50, 0.1, TestNativeMemUsageChecker{}),
ao: &arrayObject{values: []Value{}},
// array overhead + array baseObject + values slice overhead
expectedMem: SizeEmptyStruct + SizeEmptyStruct + SizeEmptySlice,
errExpected: nil,
},
{
name: "mem way above threshold returns first crossing of threshold",
mu: NewMemUsageContext(vm, 88, 100, 50, 50, 0.1, TestNativeMemUsageChecker{}),
mu: NewMemUsageContext(88, 100, 50, 50, 0.1, TestNativeMemUsageChecker{}),
ao: &arrayObject{
values: []Value{
vm.ToValue("key0"),
Expand All @@ -179,7 +179,7 @@ func TestArrayObjectMemUsage(t *testing.T) {
},
{
name: "empty array with negative threshold",
mu: NewMemUsageContext(vm, 88, 100, -1, 50, 0.1, TestNativeMemUsageChecker{}),
mu: NewMemUsageContext(88, 100, -1, 50, 0.1, TestNativeMemUsageChecker{}),
ao: &arrayObject{
values: []Value{},
},
Expand Down
13 changes: 12 additions & 1 deletion builtin_array.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,18 @@ func relToIdx(rel, l int64) int64 {
return max(l+rel, 0)
}

func (r *Runtime) newArrayValues(values []Value) *Object {
func (r *Runtime) newArrayValues(values valueStack) *Object {
if r.shouldForceMemCheck {
if memCtx := newMemUsageContextClone(); memCtx != nil {
memUsage, err := valuesMemUsage(values, memCtx)
if err != nil {
panic(err)
}
if memCtx.MemUsageLimitExceeded(memUsage) {
panic(ErrMemLimitExceeded)
}
}
}
return setArrayValues(r.newArrayObject(), values).val
}

Expand Down
55 changes: 54 additions & 1 deletion builtin_arrray_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package goja

import "testing"
import (
"context"
"testing"
)

func TestArrayProtoProp(t *testing.T) {
const SCRIPT = `
Expand Down Expand Up @@ -338,3 +341,53 @@ func TestArrayProto(t *testing.T) {
`
testScriptWithTestLib(SCRIPT, _undefined, t)
}

func TestNewArrayValues(t *testing.T) {
for _, tc := range []struct {
name string
memLimit uint64
expectedPanic bool
shouldForceMemCheck bool
}{
{
name: "should not panic when creating a new array under the mem limit",
memLimit: 1000,
expectedPanic: false,
shouldForceMemCheck: false,
},
{
name: "should panic when creating a new array over the mem limit and mem usage check is forced",
memLimit: 0,
expectedPanic: true,
shouldForceMemCheck: true,
},
{
name: "should not panic when creating a new array over the mem limit and mem usage check is not forced",
memLimit: 0,
expectedPanic: false,
shouldForceMemCheck: false,
},
{
name: "should not panic when creating a new array under the mem limit and mem usage check is forced",
memLimit: 1000,
expectedPanic: false,
shouldForceMemCheck: true,
},
} {
t.Run(tc.name, func(t *testing.T) {
defer func() {
r := recover()
if tc.expectedPanic && r == nil {
t.Error("The code is expected to panic, but it didn't")
}
if !tc.expectedPanic && r != nil {
t.Errorf("The code panicked, but it should not have: %v", r)
}
}()
vm := NewWithContext(context.Background(), tc.shouldForceMemCheck)
// Creating a mem usage context so it populates the package variable
NewMemUsageContext(100, tc.memLimit, 100, 100, 0.5, nil)
vm.newArrayValues([]Value{valueInt(0)})
})
}
}
8 changes: 4 additions & 4 deletions builtin_map_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@ func TestMapObjectMemUsage(t *testing.T) {
}{
{
name: "mem below threshold",
mu: NewMemUsageContext(vm, 88, 5000, 50, 50, 0.1, TestNativeMemUsageChecker{}),
mu: NewMemUsageContext(88, 5000, 50, 50, 0.1, TestNativeMemUsageChecker{}),
mo: &mapObject{
m: &orderedMap{
hashTable: map[uint64]*mapEntry{
Expand All @@ -303,14 +303,14 @@ func TestMapObjectMemUsage(t *testing.T) {
},
{
name: "mem is SizeEmptyStruct given a nil map object",
mu: NewMemUsageContext(vm, 88, 5000, 50, 50, 0.1, TestNativeMemUsageChecker{}),
mu: NewMemUsageContext(88, 5000, 50, 50, 0.1, TestNativeMemUsageChecker{}),
mo: nil,
expectedMem: SizeEmptyStruct,
errExpected: nil,
},
{
name: "mem way above threshold returns first crossing of threshold",
mu: NewMemUsageContext(vm, 88, 100, 50, 50, 0.1, TestNativeMemUsageChecker{}),
mu: NewMemUsageContext(88, 100, 50, 50, 0.1, TestNativeMemUsageChecker{}),
mo: &mapObject{
m: &orderedMap{
hashTable: map[uint64]*mapEntry{
Expand Down Expand Up @@ -343,7 +343,7 @@ func TestMapObjectMemUsage(t *testing.T) {
},
{
name: "mem above estimate threshold and within memory limit returns correct mem usage",
mu: NewMemUsageContext(vm, 88, 100, 50, 5, 0.1, TestNativeMemUsageChecker{}),
mu: NewMemUsageContext(88, 100, 50, 5, 0.1, TestNativeMemUsageChecker{}),
mo: &mapObject{
m: createOrderedMap(vm, 20),
},
Expand Down
2 changes: 1 addition & 1 deletion builtin_proxy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1290,7 +1290,7 @@ func TestBuiltinProxyMemUsage(t *testing.T) {

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
total, err := tc.val.MemUsage(NewMemUsageContext(New(), 100, 100, 100, 100, 0.1, nil))
total, err := tc.val.MemUsage(NewMemUsageContext(100, 100, 100, 100, 0.1, nil))
if err != nil {
t.Fatalf("Unexpected error. Actual: %v Expected: nil", err)
}
Expand Down
12 changes: 6 additions & 6 deletions builtin_set_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ func TestSetObjectMemUsage(t *testing.T) {
}{
{
name: "mem below threshold",
mu: NewMemUsageContext(vm, 88, 5000, 50, 50, 0.1, TestNativeMemUsageChecker{}),
mu: NewMemUsageContext(88, 5000, 50, 50, 0.1, TestNativeMemUsageChecker{}),
so: &setObject{
m: &orderedMap{
hashTable: map[uint64]*mapEntry{
Expand All @@ -244,14 +244,14 @@ func TestSetObjectMemUsage(t *testing.T) {
},
{
name: "mem is SizeEmptyStruct given a nil map object",
mu: NewMemUsageContext(vm, 88, 5000, 50, 50, 0.1, TestNativeMemUsageChecker{}),
mu: NewMemUsageContext(88, 5000, 50, 50, 0.1, TestNativeMemUsageChecker{}),
so: nil,
expectedMem: SizeEmptyStruct,
errExpected: nil,
},
{
name: "mem way above threshold returns first crossing of threshold",
mu: NewMemUsageContext(vm, 88, 100, 50, 50, 0.1, TestNativeMemUsageChecker{}),
mu: NewMemUsageContext(88, 100, 50, 50, 0.1, TestNativeMemUsageChecker{}),
so: &setObject{
m: &orderedMap{
hashTable: map[uint64]*mapEntry{
Expand Down Expand Up @@ -284,7 +284,7 @@ func TestSetObjectMemUsage(t *testing.T) {
},
{
name: "mem above estimate threshold and within memory limit returns correct mem usage",
mu: NewMemUsageContext(vm, 88, 100, 50, 5, 0.1, TestNativeMemUsageChecker{}),
mu: NewMemUsageContext(88, 100, 50, 5, 0.1, TestNativeMemUsageChecker{}),
so: &setObject{
m: createOrderedMap(vm, 20),
},
Expand All @@ -298,7 +298,7 @@ func TestSetObjectMemUsage(t *testing.T) {
},
{
name: "mem above estimate threshold and within memory limit and nil values returns correct mem usage",
mu: NewMemUsageContext(vm, 88, 100, 50, 1, 0.1, TestNativeMemUsageChecker{}),
mu: NewMemUsageContext(88, 100, 50, 1, 0.1, TestNativeMemUsageChecker{}),
so: &setObject{
m: createOrderedMapWithNilValues(3),
},
Expand All @@ -308,7 +308,7 @@ func TestSetObjectMemUsage(t *testing.T) {
},
{
name: "mem is SizeEmptyStruct given a nil orderedMap object",
mu: NewMemUsageContext(vm, 88, 5000, 50, 50, 0.1, TestNativeMemUsageChecker{}),
mu: NewMemUsageContext(88, 5000, 50, 50, 0.1, TestNativeMemUsageChecker{}),
so: &setObject{},
expectedMem: SizeEmptyStruct,
errExpected: nil,
Expand Down
4 changes: 2 additions & 2 deletions compiler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5657,7 +5657,7 @@ func TestProgramMemUsage(t *testing.T) {
}{
{
name: "mem below threshold",
mu: NewMemUsageContext(New(), 88, 50, 50, 50, 0.1, TestNativeMemUsageChecker{}),
mu: NewMemUsageContext(88, 50, 50, 50, 0.1, TestNativeMemUsageChecker{}),
p: &Program{
values: []Value{
New().newDateObject(time.Now(), true, nil),
Expand All @@ -5669,7 +5669,7 @@ func TestProgramMemUsage(t *testing.T) {
},
{
name: "mem way above threshold returns first crossing of threshold",
mu: NewMemUsageContext(New(), 88, 50, 50, 50, 0.1, TestNativeMemUsageChecker{}),
mu: NewMemUsageContext(88, 50, 50, 50, 0.1, TestNativeMemUsageChecker{}),
p: &Program{
values: []Value{
New().newDateObject(time.Now(), true, nil),
Expand Down
2 changes: 1 addition & 1 deletion date_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -349,7 +349,7 @@ func TestDateMemUsage(t *testing.T) {

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
total, err := tc.val.MemUsage(NewMemUsageContext(New(), 100, 100, 100, 100, 0.1, nil))
total, err := tc.val.MemUsage(NewMemUsageContext(100, 100, 100, 100, 0.1, nil))
if err != tc.errExpected {
t.Fatalf("Unexpected error. Actual: %v Expected: %v", err, tc.errExpected)
}
Expand Down
2 changes: 1 addition & 1 deletion destruct_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ func TestDestructMemUsage(t *testing.T) {

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
total, err := tc.val.MemUsage(NewMemUsageContext(New(), 100, 100, 100, 100, 0.1, nil))
total, err := tc.val.MemUsage(NewMemUsageContext(100, 100, 100, 100, 0.1, nil))
if err != tc.errExpected {
t.Fatalf("Unexpected error. Actual: %v Expected: %v", err, tc.errExpected)
}
Expand Down
12 changes: 6 additions & 6 deletions func_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ func TestNativeFuncObjectMemUsage(t *testing.T) {

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
total, err := tc.val.MemUsage(NewMemUsageContext(New(), 100, 100, 100, 100, 0.1, nil))
total, err := tc.val.MemUsage(NewMemUsageContext(100, 100, 100, 100, 0.1, nil))
if err != tc.errExpected {
t.Fatalf("Unexpected error. Actual: %v Expected: %v", err, tc.errExpected)
}
Expand Down Expand Up @@ -234,7 +234,7 @@ func TestFuncObjectMemUsage(t *testing.T) {

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
total, err := tc.val.MemUsage(NewMemUsageContext(New(), 100, 100, 100, 100, 0.1, nil))
total, err := tc.val.MemUsage(NewMemUsageContext(100, 100, 100, 100, 0.1, nil))
if err != tc.errExpected {
t.Fatalf("Unexpected error. Actual: %v Expected: %v", err, tc.errExpected)
}
Expand Down Expand Up @@ -282,7 +282,7 @@ func TestBaseJsFuncObjectMemUsage(t *testing.T) {

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
total, err := tc.val.MemUsage(NewMemUsageContext(New(), 100, 100, 100, 100, 0.1, nil))
total, err := tc.val.MemUsage(NewMemUsageContext(100, 100, 100, 100, 0.1, nil))
if err != tc.errExpected {
t.Fatalf("Unexpected error. Actual: %v Expected: %v", err, tc.errExpected)
}
Expand Down Expand Up @@ -361,7 +361,7 @@ func TestClassFuncObjectMemUsage(t *testing.T) {

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
total, err := tc.val.MemUsage(NewMemUsageContext(New(), 100, 100, 100, 100, 0.1, nil))
total, err := tc.val.MemUsage(NewMemUsageContext(100, 100, 100, 100, 0.1, nil))
if err != tc.errExpected {
t.Fatalf("Unexpected error. Actual: %v Expected: %v", err, tc.errExpected)
}
Expand Down Expand Up @@ -421,7 +421,7 @@ func TestMethodFuncObjectMemUsage(t *testing.T) {

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
total, err := tc.val.MemUsage(NewMemUsageContext(New(), 100, 100, 100, 100, 0.1, nil))
total, err := tc.val.MemUsage(NewMemUsageContext(100, 100, 100, 100, 0.1, nil))
if err != tc.errExpected {
t.Fatalf("Unexpected error. Actual: %v Expected: %v", err, tc.errExpected)
}
Expand Down Expand Up @@ -490,7 +490,7 @@ func TestArrowFuncObjectMemUsage(t *testing.T) {

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
total, err := tc.val.MemUsage(NewMemUsageContext(New(), 100, 100, 100, 100, 0.1, nil))
total, err := tc.val.MemUsage(NewMemUsageContext(100, 100, 100, 100, 0.1, nil))
if err != tc.errExpected {
t.Fatalf("Unexpected error. Actual: %v Expected: %v", err, tc.errExpected)
}
Expand Down
Loading

0 comments on commit 8b50f5b

Please sign in to comment.