Skip to content
Merged
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
31 changes: 27 additions & 4 deletions context.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package quickjs

import (
"errors"
"fmt"
"os"
goruntime "runtime"
Expand Down Expand Up @@ -398,6 +399,21 @@ func (ctx *Context) NewString(v string) *Value {
return &Value{ctx: ctx, ref: C.JS_NewStringLen(ctx.ref, ptr, C.size_t(len(v)))}
}

// NewStringUTF16 returns a string value from UTF-16 code units.
func (ctx *Context) NewStringUTF16(v []uint16) *Value {
if !ctx.hasValidRef() {
return nil
}

var ptr *C.uint16_t
if len(v) > 0 {
ptr = (*C.uint16_t)(unsafe.Pointer(&v[0]))
}
Comment thread
buke marked this conversation as resolved.
Comment thread
buke marked this conversation as resolved.
ref := C.JS_NewStringUTF16(ctx.ref, ptr, C.size_t(len(v)))
goruntime.KeepAlive(v)
return &Value{ctx: ctx, ref: ref}
}

// NewDate returns a JavaScript Date object from epoch milliseconds.
func (ctx *Context) NewDate(epochMS float64) *Value {
if !ctx.hasValidRef() {
Expand Down Expand Up @@ -1232,14 +1248,21 @@ func (ctx *Context) HasException() bool {
return bool(C.JS_HasException(ctx.ref))
}

func (ctx *Context) exceptionError() error {
val := &Value{ctx: ctx, ref: C.JS_GetException(ctx.ref)}
defer val.Free()
if err := val.Error(); err != nil {
return err
}
return errors.New("javascript exception")
Comment thread
buke marked this conversation as resolved.
Comment thread
buke marked this conversation as resolved.
}
Comment thread
buke marked this conversation as resolved.

// Exception returns a context's exception value.
func (ctx *Context) Exception() error {
if !ctx.hasValidRef() {
if !ctx.hasValidRef() || !ctx.HasException() {
return nil
}
val := &Value{ctx: ctx, ref: C.JS_GetException(ctx.ref)}
defer val.Free()
return val.Error()
return ctx.exceptionError()
}

// Loop runs the context's event loop.
Expand Down
54 changes: 53 additions & 1 deletion context_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,6 @@ func TestContextPostCloseContracts(t *testing.T) {
require.NotNil(t, v)

ctx.Close()

require.Nil(t, ctx.Runtime())
require.False(t, ctx.HasException())
require.Nil(t, ctx.Exception())
Expand All @@ -119,6 +118,59 @@ func TestContextPostCloseContracts(t *testing.T) {
rt.Close()
}

func TestContextNewStringUTF16(t *testing.T) {
useStableOwnerHooksForLegacySubtests(t)

rt := NewRuntime()
defer rt.Close()
ctx := rt.NewContext()
defer ctx.Close()

ascii := ctx.NewStringUTF16([]uint16{'o', 'k'})
require.NotNil(t, ascii)
defer ascii.Free()
require.True(t, ascii.IsString())
require.Equal(t, "ok", ascii.ToString())

empty := ctx.NewStringUTF16(nil)
require.NotNil(t, empty)
defer empty.Free()
require.Equal(t, "", empty.ToString())

wide := ctx.NewStringUTF16([]uint16{0xD800})
require.NotNil(t, wide)
defer wide.Free()
utf16, err := wide.ToStringUTF16()
require.NoError(t, err)
require.Equal(t, []uint16{0xD800}, utf16)

var nilCtx *Context
require.Nil(t, nilCtx.NewStringUTF16([]uint16{'x'}))

closedRT := NewRuntime()
closedCtx := closedRT.NewContext()
closedCtx.Close()
require.Nil(t, closedCtx.NewStringUTF16([]uint16{'x'}))
closedRT.Close()
}

func TestContextExceptionPrimitiveFallback(t *testing.T) {
useStableOwnerHooksForLegacySubtests(t)

rt := NewRuntime()
defer rt.Close()
ctx := rt.NewContext()
defer ctx.Close()

result := ctx.Eval(`throw 42`)
defer result.Free()
require.True(t, result.IsException())
require.True(t, ctx.HasException())
require.EqualError(t, ctx.Exception(), "javascript exception")
Comment thread
buke marked this conversation as resolved.
require.False(t, ctx.HasException())
require.Nil(t, ctx.Exception())
}

func TestContextInternalStateHelpers(t *testing.T) {
var nilCtx *Context
require.Nil(t, nilCtx.Runtime())
Expand Down
101 changes: 101 additions & 0 deletions value.go
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,107 @@ func (v *Value) ToBigInt() *big.Int {
return val
}

// ToNumber returns the numeric value as a new JavaScript Number.
func (v *Value) ToNumber() (*Value, error) {
if !v.hasValidContext() {
return nil, errors.New("value context is not available")
}

converted := &Value{ctx: v.ctx, ref: C.JS_ToNumber(v.ctx.ref, v.ref)}
if converted.IsException() {
return nil, v.ctx.Exception()
}
Comment thread
buke marked this conversation as resolved.
Comment thread
buke marked this conversation as resolved.

return converted, nil
Comment thread
buke marked this conversation as resolved.
}

// ToIndex returns the value converted to a JavaScript array index.
func (v *Value) ToIndex() (uint64, error) {
if !v.hasValidContext() {
return 0, errors.New("value context is not available")
}

val := C.uint64_t(0)
if C.JS_ToIndex(v.ctx.ref, &val, v.ref) != 0 {
return 0, v.ctx.Exception()
}
Comment thread
buke marked this conversation as resolved.
Comment thread
buke marked this conversation as resolved.

return uint64(val), nil
}

// ToBigInt64 returns the value converted to int64 using BigInt semantics.
func (v *Value) ToBigInt64() (int64, error) {
if !v.hasValidContext() {
return 0, errors.New("value context is not available")
}

val := C.int64_t(0)
if C.JS_ToBigInt64(v.ctx.ref, &val, v.ref) != 0 {
return 0, v.ctx.Exception()
}
Comment thread
buke marked this conversation as resolved.

return int64(val), nil
}

// ToBigUint64 returns the value converted to uint64 using BigInt semantics.
func (v *Value) ToBigUint64() (uint64, error) {
if !v.hasValidContext() {
return 0, errors.New("value context is not available")
}

val := C.uint64_t(0)
if C.JS_ToBigUint64(v.ctx.ref, &val, v.ref) != 0 {
return 0, v.ctx.Exception()
}
Comment thread
buke marked this conversation as resolved.

return uint64(val), nil
}

// ToInt64Ext returns the value converted to int64, accepting BigInt inputs.
func (v *Value) ToInt64Ext() (int64, error) {
if !v.hasValidContext() {
return 0, errors.New("value context is not available")
}

val := C.int64_t(0)
if C.JS_ToInt64Ext(v.ctx.ref, &val, v.ref) != 0 {
return 0, v.ctx.Exception()
}
Comment thread
buke marked this conversation as resolved.

return int64(val), nil
}

// ToPropertyKey returns the value converted to a JavaScript property key.
func (v *Value) ToPropertyKey() (*Value, error) {
if !v.hasValidContext() {
return nil, errors.New("value context is not available")
}

converted := &Value{ctx: v.ctx, ref: C.JS_ToPropertyKey(v.ctx.ref, v.ref)}
if converted.IsException() {
return nil, v.ctx.Exception()
}
Comment thread
buke marked this conversation as resolved.

return converted, nil
Comment thread
buke marked this conversation as resolved.
}

// ToStringUTF16 returns the UTF-16 code units of the string representation of the value.
func (v *Value) ToStringUTF16() ([]uint16, error) {
if !v.hasValidContext() {
return nil, errors.New("value context is not available")
}

length := C.size_t(0)
ptr := C.JS_ToCStringLenUTF16(v.ctx.ref, &length, v.ref)
if ptr == nil {
return nil, v.ctx.Exception()
}
Comment thread
buke marked this conversation as resolved.
Comment thread
buke marked this conversation as resolved.
defer C.JS_FreeCStringUTF16(v.ctx.ref, ptr)

utf16 := unsafe.Slice((*uint16)(unsafe.Pointer(ptr)), int(length))
return append([]uint16(nil), utf16...), nil
}

// Len returns the length of the array.
func (v *Value) Len() int64 {
length := v.Get("length")
Expand Down
Loading
Loading