Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BAAS-34801: add forced mem checks on new arrays and obj props #127

Merged
merged 12 commits into from
Dec 9, 2024
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
Loading