Skip to content
This repository has been archived by the owner on Oct 12, 2023. It is now read-only.

Heap snapshots #39

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions context.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ package v8go
// #include "v8go.h"
import "C"
import (
"errors"
"runtime"
"sync"
"unsafe"
Expand Down Expand Up @@ -76,6 +77,27 @@ func NewContext(opt ...ContextOption) *Context {
return ctx
}

func NewContextFromSnapShot(iso *Isolate, snapshot_index int) (*Context, error) {
ctxMutex.Lock()
ctxSeq++
ref := ctxSeq
ctxMutex.Unlock()

createParams := iso.createParams
if createParams == nil || createParams.startupData == nil {
return nil, errors.New("Must create an isolate from a snapshot blob")
}

ctx := &Context{
ref: ref,
ptr: C.NewContextFromSnapShot(iso.ptr, C.size_t(snapshot_index), C.int(ref)),
iso: iso,
}

ctx.register()
return ctx, nil
}

// Isolate gets the current context's parent isolate.An error is returned
// if the isolate has been terninated.
func (c *Context) Isolate() *Isolate {
Expand Down
16 changes: 16 additions & 0 deletions context_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,22 @@ func TestContextExec(t *testing.T) {
}
}

func TestNewContextFromSnapShotErrorWhenIsolateHasNoStartupData(t *testing.T) {
t.Parallel()

iso := v8.NewIsolate()
defer iso.Dispose()

ctx, err := v8.NewContextFromSnapShot(iso, 1)

if ctx != nil {
t.Errorf("error expected nil context got: %+v", ctx)
}
if err == nil {
t.Error("error expected but was <nil>")
}
}

func TestJSExceptions(t *testing.T) {
t.Parallel()

Expand Down
37 changes: 32 additions & 5 deletions isolate.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,9 @@ type Isolate struct {
cbSeq int
cbs map[int]FunctionCallback

null *Value
undefined *Value
null *Value
undefined *Value
createParams *CreateParams
}

// HeapStatistics represents V8 isolate heap statistics
Expand All @@ -44,20 +45,45 @@ type HeapStatistics struct {
NumberOfDetachedContexts uint64
}

type createOptions func(*CreateParams)

func WithStartupData(startupData *StartupData) createOptions {
return func(params *CreateParams) {
params.startupData = startupData
}
}

type CreateParams struct {
startupData *StartupData
}

// NewIsolate creates a new V8 isolate. Only one thread may access
// a given isolate at a time, but different threads may access
// different isolates simultaneously.
// When an isolate is no longer used its resources should be freed
// by calling iso.Dispose().
// An *Isolate can be used as a v8go.ContextOption to create a new
// Context, rather than creating a new default Isolate.
func NewIsolate() *Isolate {
func NewIsolate(opts ...createOptions) *Isolate {
v8once.Do(func() {
C.Init()
})
params := &CreateParams{}
for _, opt := range opts {
opt(params)
}

var cOptions C.IsolateOptions

if params.startupData != nil {
cOptions.snapshot_blob_data = (*C.char)(unsafe.Pointer(&params.startupData.data[0]))
cOptions.snapshot_blob_raw_size = params.startupData.raw_size
}

iso := &Isolate{
ptr: C.NewIsolate(),
cbs: make(map[int]FunctionCallback),
ptr: C.NewIsolate(cOptions),
cbs: make(map[int]FunctionCallback),
createParams: params,
}
iso.null = newValueNull(iso)
iso.undefined = newValueUndefined(iso)
Expand Down Expand Up @@ -146,6 +172,7 @@ func (i *Isolate) Dispose() {
return
}
C.IsolateDispose(i.ptr)
i.createParams = nil
i.ptr = nil
}

Expand Down
104 changes: 104 additions & 0 deletions snapshot_creator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
// Copyright 2021 the v8go contributors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package v8go

// #include <stdlib.h>
// #include "v8go.h"
import "C"
import (
"errors"
"unsafe"
)

type FunctionCodeHandling int

const (
FunctionCodeHandlingKlear FunctionCodeHandling = iota
FunctionCodeHandlingKeep

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  //  kKeep - keeps any compiled functions
  //  kClear - does not keep any compiled functions

We should:

  1. Document what each one means here
  2. Decide if we want to keep the k prefix as well as correct FunctionCodeHandlingKlear->FunctionCodeHandlingClear

)

type StartupData struct {
data []byte
raw_size C.int
}

type SnapshotCreator struct {
ptr C.SnapshotCreatorPtr
iso *Isolate
defaultContextAdded bool
}

func NewSnapshotCreator() *SnapshotCreator {
v8once.Do(func() {
C.Init()
})
GustavoCaso marked this conversation as resolved.
Show resolved Hide resolved

rtn := C.NewSnapshotCreator()

return &SnapshotCreator{
ptr: rtn.creator,
iso: &Isolate{ptr: rtn.iso},
defaultContextAdded: false,
}
}

func (s *SnapshotCreator) GetIsolate() (*Isolate, error) {
if s.ptr == nil {
return nil, errors.New("v8go: Cannot get Isolate after creating the blob")
}

return s.iso, nil
}

func (s *SnapshotCreator) SetDeafultContext(ctx *Context) error {
if s.defaultContextAdded {
return errors.New("v8go: Cannot set multiple default context for snapshot creator")
}

C.SetDefaultContext(s.ptr, ctx.ptr)
s.defaultContextAdded = true
ctx.ptr = nil

return nil
}

func (s *SnapshotCreator) AddContext(ctx *Context) (int, error) {
if s.ptr == nil {
return 0, errors.New("v8go: Cannot add context to snapshot creator after creating the blob")
}

index := C.AddContext(s.ptr, ctx.ptr)
ctx.ptr = nil

return int(index), nil
}

func (s *SnapshotCreator) Create(functionCode FunctionCodeHandling) (*StartupData, error) {
if s.ptr == nil {
return nil, errors.New("v8go: Cannot use snapshot creator after creating the blob")
}

if !s.defaultContextAdded {
return nil, errors.New("v8go: Cannot create a snapshot without a default context")
}

rtn := C.CreateBlob(s.ptr, C.int(functionCode))

s.ptr = nil
s.iso.ptr = nil

raw_size := rtn.raw_size
data := C.GoBytes(unsafe.Pointer(rtn.data), raw_size)

C.SnapshotBlobDelete(rtn)

return &StartupData{data: data, raw_size: raw_size}, nil
}

func (s *SnapshotCreator) Dispose() {
if s.ptr != nil {
C.DeleteSnapshotCreator(s.ptr)
}
}
Loading