#include <stdbool.h>
#include "easyxs/easyxs.h"
#include "quickjs/quickjs.h"
#include "quickjs/quickjs-libc.h"
#define PERL_NS_ROOT "JavaScript::QuickJS"
#define PERL_BOOLEAN_CLASS "Types::Serialiser::Boolean"
#define PQJS_JSOBJECT_CLASS PERL_NS_ROOT "::JSObject"
#define PQJS_FUNCTION_CLASS PERL_NS_ROOT "::Function"
#define PQJS_REGEXP_CLASS PERL_NS_ROOT "::RegExp"
#define PQJS_DATE_CLASS PERL_NS_ROOT "::Date"
#define PQJS_PROMISE_CLASS PERL_NS_ROOT "::Promise"
typedef
struct
{
JSContext *ctx;
pid_t pid;
bool
added_std;
bool
added_os;
bool
added_helpers;
char
* module_base_path;
} perl_qjs_s;
typedef
struct
{
JSContext *ctx;
JSValue jsobj;
pid_t pid;
} perl_qjs_jsobj_s;
typedef
struct
{
#ifdef MULTIPLICITY
tTHX aTHX;
#endif
SV** svs;
U32 svs_count;
U32 refcount;
bool
ran_js_std_init_handlers;
JSValue regexp_jsvalue;
JSValue date_jsvalue;
JSValue promise_jsvalue;
} ctx_opaque_s;
const
char
* __jstype_name_back[] = {
[JS_TAG_BIG_DECIMAL - JS_TAG_FIRST] =
"big decimal"
,
[JS_TAG_BIG_INT - JS_TAG_FIRST] =
"big integer"
,
[JS_TAG_BIG_FLOAT - JS_TAG_FIRST] =
"big float"
,
[JS_TAG_SYMBOL - JS_TAG_FIRST] =
"symbol"
,
[JS_TAG_MODULE - JS_TAG_FIRST] =
"module"
,
[JS_TAG_OBJECT - JS_TAG_FIRST] =
"object"
,
[JS_TAG_FLOAT64 - JS_TAG_FIRST] =
"float64"
,
[99] = NULL,
};
const
char
*
const
DATE_GETTER_FROM_IX[] = {
"toString"
,
"toUTCString"
,
"toGMTString"
,
"toISOString"
,
"toDateString"
,
"toTimeString"
,
"toLocaleString"
,
"toLocaleDateString"
,
"toLocaleTimeString"
,
"getTimezoneOffset"
,
"getTime"
,
"getFullYear"
,
"getUTCFullYear"
,
"getMonth"
,
"getUTCMonth"
,
"getDate"
,
"getUTCDate"
,
"getHours"
,
"getUTCHours"
,
"getMinutes"
,
"getUTCMinutes"
,
"getSeconds"
,
"getUTCSeconds"
,
"getMilliseconds"
,
"getUTCMilliseconds"
,
"getDay"
,
"getUTCDay"
,
"toJSON"
,
};
const
char
*
const
DATE_SETTER_FROM_IX[] = {
"setTime"
,
"setMilliseconds"
,
"setUTCMilliseconds"
,
"setSeconds"
,
"setUTCSeconds"
,
"setMinutes"
,
"setUTCMinutes"
,
"setHours"
,
"setUTCHours"
,
"setDate"
,
"setUTCDate"
,
"setMonth"
,
"setUTCMonth"
,
"setFullYear"
,
"setUTCFullYear"
,
};
#if defined _WIN32 || defined __CYGWIN__
# define PATH_SEPARATOR '\\'
#else
# define PATH_SEPARATOR '/'
#endif
#define _jstype_name(typenum) __jstype_name_back[ typenum - JS_TAG_FIRST ]
static
SV* _JSValue_to_SV (pTHX_ JSContext* ctx, JSValue jsval, SV** err_svp);
static
inline
SV* _JSValue_special_object_to_SV (pTHX_ JSContext* ctx, JSValue jsval, SV** err_svp,
const
char
*
class
) {
assert
(!*err_svp);
SV* sv = exs_new_structref(perl_qjs_jsobj_s,
class
);
perl_qjs_jsobj_s* pqjs = exs_structref_ptr(sv);
*pqjs = (perl_qjs_jsobj_s) {
.ctx = ctx,
.jsobj = JS_DupValue(ctx, jsval),
.pid = getpid(),
};
ctx_opaque_s* ctxdata = JS_GetContextOpaque(ctx);
ctxdata->refcount++;
return
sv;
}
static
inline
SV* _JSValue_object_to_SV (pTHX_ JSContext* ctx, JSValue jsval, SV** err_svp) {
assert
(!*err_svp);
JSPropertyEnum *tab_atom;
uint32_t tab_atom_count;
int
propnameserr = JS_GetOwnPropertyNames(ctx, &tab_atom, &tab_atom_count, jsval, JS_GPN_STRING_MASK);
PERL_UNUSED_VAR(propnameserr);
assert
(!propnameserr);
HV* hv = newHV();
for
(
int
i = 0; i < tab_atom_count; i++) {
JSValue key = JS_AtomToString(ctx, tab_atom[i].atom);
STRLEN
strlen
;
const
char
* keystr = JS_ToCStringLen(ctx, &
strlen
, key);
JSValue value = JS_GetProperty(ctx, jsval, tab_atom[i].atom);
SV* val_sv = _JSValue_to_SV(aTHX_ ctx, value, err_svp);
if
(val_sv) {
hv_store(hv, keystr, -
strlen
, val_sv, 0);
}
JS_FreeCString(ctx, keystr);
JS_FreeValue(ctx, key);
JS_FreeValue(ctx, value);
JS_FreeAtom(ctx, tab_atom[i].atom);
if
(!val_sv)
break
;
}
js_free(ctx, tab_atom);
if
(*err_svp) {
SvREFCNT_dec( (SV*) hv );
return
NULL;
}
return
newRV_noinc((SV*) hv);
}
static
inline
SV* _JSValue_array_to_SV (pTHX_ JSContext* ctx, JSValue jsval, SV** err_svp) {
JSValue jslen = JS_GetPropertyStr(ctx, jsval,
"length"
);
uint32_t len;
JS_ToUint32(ctx, &len, jslen);
JS_FreeValue(ctx, jslen);
AV* av = newAV();
if
(len) {
av_fill( av, len - 1 );
for
(uint32_t i=0; i<len; i++) {
JSValue jsitem = JS_GetPropertyUint32(ctx, jsval, i);
SV* val_sv = _JSValue_to_SV(aTHX_ ctx, jsitem, err_svp);
if
(val_sv) av_store( av, i, val_sv );
JS_FreeValue(ctx, jsitem);
if
(!val_sv)
break
;
}
}
if
(*err_svp) {
SvREFCNT_dec((SV*) av);
return
NULL;
}
return
newRV_noinc((SV*) av);
}
static
SV* _JSValue_to_SV (pTHX_ JSContext* ctx, JSValue jsval, SV** err_svp) {
assert
(!*err_svp);
SV* RETVAL;
int
tag = JS_VALUE_GET_NORM_TAG(jsval);
assert
(tag != JS_TAG_EXCEPTION);
switch
(tag) {
case
JS_TAG_STRING:
STMT_START {
STRLEN
strlen
;
const
char
* str = JS_ToCStringLen(ctx, &
strlen
, jsval);
RETVAL = newSVpvn_flags(str,
strlen
, SVf_UTF8);
JS_FreeCString(ctx, str);
} STMT_END;
break
;
case
JS_TAG_INT:
RETVAL = newSViv(JS_VALUE_GET_INT(jsval));
break
;
case
JS_TAG_FLOAT64:
RETVAL = newSVnv(JS_VALUE_GET_FLOAT64(jsval));
break
;
case
JS_TAG_BOOL:
RETVAL = boolSV(JS_VALUE_GET_BOOL(jsval));
break
;
case
JS_TAG_NULL:
case
JS_TAG_UNDEFINED:
RETVAL = &PL_sv_undef;
break
;
case
JS_TAG_OBJECT:
if
(JS_IsFunction(ctx, jsval)) {
load_module(
PERL_LOADMOD_NOIMPORT,
newSVpvs(PQJS_FUNCTION_CLASS),
NULL
);
SV* func_sv = exs_new_structref(perl_qjs_jsobj_s, PQJS_FUNCTION_CLASS);
perl_qjs_jsobj_s* pqjs = exs_structref_ptr(func_sv);
*pqjs = (perl_qjs_jsobj_s) {
.ctx = ctx,
.jsobj = JS_DupValue(ctx, jsval),
.pid = getpid(),
};
ctx_opaque_s* ctxdata = JS_GetContextOpaque(ctx);
ctxdata->refcount++;
RETVAL = func_sv;
}
else
if
(JS_IsArray(ctx, jsval)) {
RETVAL = _JSValue_array_to_SV(aTHX_ ctx, jsval, err_svp);
}
else
{
ctx_opaque_s* ctxdata = JS_GetContextOpaque(ctx);
if
(JS_IsInstanceOf(ctx, jsval, ctxdata->regexp_jsvalue)) {
RETVAL = _JSValue_special_object_to_SV(aTHX_ ctx, jsval, err_svp, PQJS_REGEXP_CLASS);
}
else
if
(JS_IsInstanceOf(ctx, jsval, ctxdata->date_jsvalue)) {
RETVAL = _JSValue_special_object_to_SV(aTHX_ ctx, jsval, err_svp, PQJS_DATE_CLASS);
}
else
if
(JS_IsInstanceOf(ctx, jsval, ctxdata->promise_jsvalue)) {
RETVAL = _JSValue_special_object_to_SV(aTHX_ ctx, jsval, err_svp, PQJS_PROMISE_CLASS);
}
else
{
RETVAL = _JSValue_object_to_SV(aTHX_ ctx, jsval, err_svp);
}
}
break
;
default
:
STMT_START {
const
char
*
typename
= _jstype_name(tag);
if
(
typename
) {
*err_svp = newSVpvf(
"Cannot convert JS %s (QuickJS tag %d) to Perl!"
,
typename
, tag);
}
else
{
*err_svp = newSVpvf(
"Cannot convert (unexpected) JS tag value %d to Perl!"
, tag);
}
return
NULL;
} STMT_END;
}
return
RETVAL;
}
static
inline
void
_ctx_add_sv(pTHX_ JSContext* ctx, SV* sv) {
ctx_opaque_s* ctxdata = JS_GetContextOpaque(ctx);
ctxdata->svs_count++;
if
(ctxdata->svs_count == 1) {
Newx(ctxdata->svs, ctxdata->svs_count, SV*);
}
else
{
Renew(ctxdata->svs, ctxdata->svs_count, SV*);
}
ctxdata->svs[ctxdata->svs_count - 1] = SvREFCNT_inc(sv);
}
static
JSValue _sv_to_jsvalue(pTHX_ JSContext* ctx, SV* value, SV** error);
#define _MAX_ERRSV_TO_JS_TRIES 10
static
JSValue _sv_error_to_jsvalue(pTHX_ JSContext* ctx, SV* error) {
SV* error2 = NULL;
uint32_t tries = 0;
JSValue to_js;
while
(1) {
to_js = _sv_to_jsvalue(aTHX_ ctx, error, &error2);
if
(!error2)
break
;
warn_sv(error);
tries++;
if
(tries > _MAX_ERRSV_TO_JS_TRIES) {
warn_sv(error2);
return
JS_NewString(ctx,
"Failed to convert Perl error to JavaScript after "
STRINGIFY(_MAX_ERRSV_TO_JS_TRIES)
" tries!"
);
}
error = error2;
error2 = NULL;
}
return
to_js;
}
static
JSValue __do_perl_callback(JSContext *ctx, JSValueConst this_val,
int
argc, JSValueConst *argv,
int
jsmagic, JSValue *func_data) {
#ifdef MULTIPLICITY
ctx_opaque_s* ctxdata = JS_GetContextOpaque(ctx);
pTHX = ctxdata->aTHX;
#endif
PERL_UNUSED_VAR(jsmagic);
SV* cb_sv = ((SV**) func_data)[0];
SV* args[argc + 1];
args[argc] = NULL;
SV* error_sv = NULL;
for
(
int
a=0; a<argc; a++) {
args[a] = _JSValue_to_SV(aTHX_ ctx, argv[a], &error_sv);
if
(error_sv) {
while
(--a >= 0) {
SvREFCNT_dec(args[a]);
}
break
;
}
}
if
(!error_sv) {
SV* from_perl = exs_call_sv_scalar_trapped(cb_sv, args, &error_sv);
if
(from_perl) {
JSValue to_js = _sv_to_jsvalue(aTHX_ ctx, from_perl, &error_sv);
sv_2mortal(from_perl);
if
(!error_sv)
return
to_js;
}
}
JSValue jserr = _sv_error_to_jsvalue(aTHX_ ctx, error_sv);
return
JS_Throw(ctx, jserr);
}
static
JSValue _sviv_to_js(pTHX_ JSContext* ctx, SV* value) {
if
(
sizeof
(IV) ==
sizeof
(int64_t)) {
return
JS_NewInt64(ctx, (int64_t) SvIV(value));
}
return
JS_NewInt32(ctx, (int32_t) SvIV(value));
}
static
JSValue _sv_to_jsvalue(pTHX_ JSContext* ctx, SV* value, SV** error_svp) {
SvGETMAGIC(value);
switch
( exs_sv_type(value) ) {
case
EXS_SVTYPE_UNDEF:
return
JS_NULL;
case
EXS_SVTYPE_BOOLEAN:
return
JS_NewBool(ctx, SvTRUE(value));
case
EXS_SVTYPE_STRING: STMT_START {
STRLEN len;
const
char
* str = SvPVutf8(value, len);
return
JS_NewStringLen(ctx, str, len);
} STMT_END;
case
EXS_SVTYPE_UV: STMT_START {
UV val_uv = SvUV(value);
if
(
sizeof
(UV) ==
sizeof
(uint64_t)) {
if
(val_uv > IV_MAX) {
return
JS_NewFloat64(ctx, val_uv);
}
else
{
return
JS_NewInt64(ctx, (int64_t) val_uv);
}
}
else
{
return
JS_NewUint32(ctx, (uint32_t) val_uv);
}
} STMT_END;
case
EXS_SVTYPE_IV: STMT_START {
return
_sviv_to_js(aTHX_ ctx, value);
} STMT_END;
case
EXS_SVTYPE_NV: STMT_START {
return
JS_NewFloat64(ctx, (
double
) SvNV(value));
} STMT_END;
case
EXS_SVTYPE_REFERENCE:
if
(sv_isobject(value)) {
if
(sv_derived_from(value, PERL_BOOLEAN_CLASS)) {
return
JS_NewBool(ctx, SvTRUE(SvRV(value)));
}
else
if
(sv_derived_from(value, PQJS_JSOBJECT_CLASS)) {
perl_qjs_jsobj_s* pqjs = exs_structref_ptr(value);
if
(LIKELY(pqjs->ctx == ctx)) {
return
JS_DupValue(ctx, pqjs->jsobj);
}
*error_svp = newSVpvf(
"%s for QuickJS %p given to QuickJS %p!"
, sv_reftype(SvRV(value), 1), pqjs->ctx, ctx);
return
JS_NULL;
}
break
;
}
switch
(SvTYPE(SvRV(value))) {
case
SVt_PVCV:
_ctx_add_sv(aTHX_ ctx, value);
JSValue dummy = JS_MKPTR(JS_TAG_INT, value);
return
JS_NewCFunctionData(
ctx,
__do_perl_callback,
0, 0,
1, &dummy
);
case
SVt_PVAV: STMT_START {
AV* av = (AV*) SvRV(value);
JSValue jsarray = JS_NewArray(ctx);
JS_SetPropertyStr(ctx, jsarray,
"length"
, JS_NewUint32(ctx, 1 + av_len(av)));
for
(int32_t i=0; i <= av_len(av); i++) {
SV** svp = av_fetch(av, i, 0);
assert
(svp);
assert
(*svp);
JSValue jsval = _sv_to_jsvalue(aTHX_ ctx, *svp, error_svp);
if
(*error_svp) {
JS_FreeValue(ctx, jsarray);
return
_sv_error_to_jsvalue(aTHX_ ctx, *error_svp);
}
JS_SetPropertyUint32(ctx, jsarray, i, jsval);
}
return
jsarray;
} STMT_END;
case
SVt_PVHV: STMT_START {
HV* hv = (HV*) SvRV(value);
JSValue jsobj = JS_NewObject(ctx);
hv_iterinit(hv);
HE* hvent;
while
( (hvent = hv_iternext(hv)) ) {
SV* key_sv = hv_iterkeysv(hvent);
SV* val_sv = hv_iterval(hv, hvent);
STRLEN keylen;
const
char
* key = SvPVutf8(key_sv, keylen);
JSValue jsval = _sv_to_jsvalue(aTHX_ ctx, val_sv, error_svp);
if
(*error_svp) {
JS_FreeValue(ctx, jsobj);
return
_sv_error_to_jsvalue(aTHX_ ctx, *error_svp);
}
JSAtom prop = JS_NewAtomLen(ctx, key, keylen);
JS_DefinePropertyValue(ctx, jsobj, prop, jsval, JS_PROP_WRITABLE);
JS_FreeAtom(ctx, prop);
}
return
jsobj;
} STMT_END;
default
:
break
;
}
default
:
break
;
}
*error_svp = newSVpvf(
"Cannot convert %"
SVf
" to JavaScript!"
, value);
return
JS_NULL;
}
static
JSContext* _create_new_jsctx( pTHX_ JSRuntime *rt ) {
JSContext *ctx = JS_NewContext(rt);
ctx_opaque_s* ctxdata;
Newxz(ctxdata, 1, ctx_opaque_s);
JS_SetContextOpaque(ctx, ctxdata);
JSValue global = JS_GetGlobalObject(ctx);
*ctxdata = (ctx_opaque_s) {
.refcount = 1,
.regexp_jsvalue = JS_GetPropertyStr(ctx, global,
"RegExp"
),
.date_jsvalue = JS_GetPropertyStr(ctx, global,
"Date"
),
.promise_jsvalue = JS_GetPropertyStr(ctx, global,
"Promise"
),
#ifdef MULTIPLICITY
.aTHX = aTHX,
#endif
};
JS_FreeValue(ctx, global);
return
ctx;
}
static
SV* _get_exception_from_jsvalue(pTHX_ JSContext* ctx, JSValue jsret) {
SV* err;
JSValue jserr = JS_GetException(ctx);
STRLEN
strlen
;
const
char
* str = JS_ToCStringLen(ctx, &
strlen
, jserr);
err = newSVpvn_flags(str,
strlen
, SVf_UTF8);
JS_FreeCString(ctx, str);
JS_FreeValue(ctx, jserr);
return
err;
}
static
inline
SV* _return_jsvalue_or_croak(pTHX_ JSContext* ctx, JSValue jsret) {
SV* err;
SV* RETVAL;
if
(JS_IsException(jsret)) {
err = _get_exception_from_jsvalue(aTHX_ ctx, jsret);
RETVAL = NULL;
}
else
{
err = NULL;
RETVAL = _JSValue_to_SV(aTHX_ ctx, jsret, &err);
}
JS_FreeValue(ctx, jsret);
if
(err) croak_sv(err);
return
RETVAL;
}
static
void
_free_jsctx(pTHX_ JSContext* ctx) {
ctx_opaque_s* ctxdata = JS_GetContextOpaque(ctx);
if
(--ctxdata->refcount == 0) {
JS_FreeValue(ctx, ctxdata->regexp_jsvalue);
JS_FreeValue(ctx, ctxdata->date_jsvalue);
JS_FreeValue(ctx, ctxdata->promise_jsvalue);
JSRuntime *rt = JS_GetRuntime(ctx);
for
(U32 i=0; i<ctxdata->svs_count; i++) {
SvREFCNT_dec(ctxdata->svs[i]);
}
if
(ctxdata->ran_js_std_init_handlers) {
js_std_free_handlers(rt);
}
Safefree(ctxdata);
JS_FreeContext(ctx);
JS_FreeRuntime(rt);
}
}
static
JSModuleDef *pqjs_module_loader(JSContext *ctx,
const
char
*module_name,
void
*opaque) {
char
** module_base_path_p = (
char
**) opaque;
char
* module_base_path = *module_base_path_p;
JSModuleDef *moduledef;
if
(module_base_path) {
size_t
base_path_len =
strlen
(module_base_path);
size_t
module_name_len =
strlen
(module_name);
char
real_path[1 + base_path_len + module_name_len];
memcpy
(real_path, module_base_path, base_path_len);
memcpy
(real_path + base_path_len, module_name, module_name_len);
real_path[base_path_len + module_name_len] = 0;
moduledef = js_module_loader(ctx, real_path, NULL);
}
else
{
moduledef = js_module_loader(ctx, module_name, NULL);
}
return
moduledef;
}
static
const
char
* _REGEXP_ACCESSORS[] = {
"flags"
,
"dotAll"
,
"global"
,
"hasIndices"
,
"ignoreCase"
,
"multiline"
,
"source"
,
"sticky"
,
"unicode"
,
"lastIndex"
,
};
static
const
char
* _FUNCTION_ACCESSORS[] = {
"length"
,
"name"
,
};
#define FUNC_CALL_INITIAL_ARGS 2
static
SV* _svs_to_jsvars(pTHX_ JSContext* jsctx, int32_t params_count, SV** svs, JSValue* jsvars) {
SV* error = NULL;
for
(int32_t i=0; i<params_count; i++) {
SV* cur_sv = svs[i];
JSValue jsval = _sv_to_jsvalue(aTHX_ jsctx, cur_sv, &error);
if
(error) {
while
(--i >= 0) {
JS_FreeValue(jsctx, jsvars[i]);
}
break
;
}
jsvars[i] = jsval;
}
return
error;
}
static
void
_import_module_to_global(pTHX_ JSContext* jsctx,
const
char
* modname) {
const
char
* jstemplate =
"import * as theModule from '%s';\n"
"globalThis.%s = theModule;\n"
;
char
js[255] = { 0 };
snprintf(js, 255, jstemplate, modname, modname);
JSValue val = JS_Eval(jsctx, js,
strlen
(js),
"<input>"
, JS_EVAL_TYPE_MODULE);
if
(JS_IsException(val)) {
SV* errsv = _get_exception_from_jsvalue(aTHX_ jsctx, val);
if
(errsv) {
croak_sv(errsv);
}
croak(
"Got empty exception??"
);
}
JS_FreeValue(jsctx, val);
}
MODULE = JavaScript::QuickJS PACKAGE = JavaScript::QuickJS
PROTOTYPES: DISABLE
SV*
_new (SV* classname_sv)
CODE:
JSRuntime *rt = JS_NewRuntime();
JS_SetHostPromiseRejectionTracker(rt, js_std_promise_rejection_tracker, NULL);
JS_SetModuleLoaderFunc(rt, NULL, js_module_loader, NULL);
JSContext *ctx = _create_new_jsctx(aTHX_ rt);
RETVAL = exs_new_structref(perl_qjs_s, SvPVbyte_nolen(classname_sv));
perl_qjs_s* pqjs = exs_structref_ptr(RETVAL);
*pqjs = (perl_qjs_s) {
.ctx = ctx,
.pid = getpid(),
};
JS_SetModuleLoaderFunc(
rt,
NULL,
pqjs_module_loader,
&pqjs->module_base_path
);
JS_SetRuntimeInfo(rt, PERL_NS_ROOT);
OUTPUT:
RETVAL
void
DESTROY (SV* self_sv)
CODE:
perl_qjs_s* pqjs = exs_structref_ptr(self_sv);
if
(PL_dirty && pqjs->pid == getpid()) {
warn(
"DESTROYing %"
SVf
" at global destruction; memory leak likely!\n"
, self_sv);
}
if
(pqjs->module_base_path) Safefree(pqjs->module_base_path);
_free_jsctx(aTHX_ pqjs->ctx);
SV*
_configure (SV* self_sv, SV* max_stack_size_sv, SV* memory_limit_sv, SV* gc_threshold_sv)
CODE:
perl_qjs_s* pqjs = exs_structref_ptr(self_sv);
JSRuntime *rt = JS_GetRuntime(pqjs->ctx);
if
(SvOK(max_stack_size_sv)) {
JS_SetMaxStackSize(rt, exs_SvUV(max_stack_size_sv));
}
if
(SvOK(memory_limit_sv)) {
JS_SetMemoryLimit(rt, exs_SvUV(memory_limit_sv));
}
if
(SvOK(gc_threshold_sv)) {
JS_SetGCThreshold(rt, exs_SvUV(gc_threshold_sv));
}
RETVAL = SvREFCNT_inc(self_sv);
OUTPUT:
RETVAL
SV*
std (SV* self_sv)
ALIAS:
os = 1
helpers = 2
CODE:
perl_qjs_s* pqjs = exs_structref_ptr(self_sv);
switch
(ix) {
case
0:
if
(!pqjs->added_std) {
js_init_module_std(pqjs->ctx,
"std"
);
_import_module_to_global(aTHX_ pqjs->ctx,
"std"
);
pqjs->added_std =
true
;
}
break
;
case
1:
if
(!pqjs->added_os) {
js_init_module_os(pqjs->ctx,
"os"
);
pqjs->added_os =
true
;
ctx_opaque_s* ctxdata = JS_GetContextOpaque(pqjs->ctx);
if
(!ctxdata->ran_js_std_init_handlers) {
JSRuntime *rt = JS_GetRuntime(pqjs->ctx);
js_std_init_handlers(rt);
ctxdata->ran_js_std_init_handlers =
true
;
}
_import_module_to_global(aTHX_ pqjs->ctx,
"os"
);
}
break
;
case
2:
if
(!pqjs->added_helpers) {
js_std_add_helpers(pqjs->ctx, 0, NULL);
pqjs->added_helpers =
true
;
}
break
;
default
:
croak(
"%s: Bad XS alias: %d\n"
, __func__, (
int
) ix);
}
RETVAL = SvREFCNT_inc(self_sv);
OUTPUT:
RETVAL
SV*
unset_module_base (SV* self_sv)
CODE:
perl_qjs_s* pqjs = exs_structref_ptr(self_sv);
if
(pqjs->module_base_path) {
Safefree(pqjs->module_base_path);
pqjs->module_base_path = NULL;
}
RETVAL = SvREFCNT_inc(self_sv);
OUTPUT:
RETVAL
SV*
set_module_base (SV* self_sv, SV* path_sv)
CODE:
if
(!SvOK(path_sv)) croak(
"Give a path! (Did you want unset_module_base?)"
);
perl_qjs_s* pqjs = exs_structref_ptr(self_sv);
const
char
* path = exs_SvPVbyte_nolen(path_sv);
size_t
path_len =
strlen
(path);
if
(pqjs->module_base_path) {
Renew(pqjs->module_base_path, 2 + path_len,
char
);
}
else
{
Newx(pqjs->module_base_path, 2 + path_len,
char
);
}
Copy(path, pqjs->module_base_path, 2 + path_len,
char
);
pqjs->module_base_path[path_len] = PATH_SEPARATOR;
pqjs->module_base_path[1 + path_len] = 0;
RETVAL = SvREFCNT_inc(self_sv);
OUTPUT:
RETVAL
SV*
set_globals (SV* self_sv, ...)
CODE:
if
(items < 2) croak(
"Need at least 1 key/value pair."
);
if
(!(items % 2)) croak(
"Need an even list of key/value pairs."
);
I32 valscount = (items - 1) >> 1;
perl_qjs_s* pqjs = exs_structref_ptr(self_sv);
SV* jsname_sv, *value_sv;
SV* error = NULL;
JSAtom jsnames[valscount];
JSValue jsvals[valscount];
for
(
int
i=0; i < valscount; i++) {
jsname_sv = ST( 1 + (i << 1) );
value_sv = ST( 2 + (i << 1) );
STRLEN jsnamelen;
const
char
* jsname_str = SvPVutf8(jsname_sv, jsnamelen);
JSValue jsval = _sv_to_jsvalue(aTHX_ pqjs->ctx, value_sv, &error);
if
(error) {
while
(i-- > 0) {
JS_FreeAtom(pqjs->ctx, jsnames[i]);
JS_FreeValue(pqjs->ctx, jsvals[i]);
}
croak_sv(error);
}
jsnames[i] = JS_NewAtomLen(pqjs->ctx, jsname_str, jsnamelen);
jsvals[i] = jsval;
}
JSValue jsglobal = JS_GetGlobalObject(pqjs->ctx);
for
(
int
i=0; i < valscount; i++) {
JS_DefinePropertyValue(pqjs->ctx, jsglobal, jsnames[i], jsvals[i], JS_PROP_WRITABLE);
JS_FreeAtom(pqjs->ctx, jsnames[i]);
}
JS_FreeValue(pqjs->ctx, jsglobal);
RETVAL = SvREFCNT_inc(self_sv);
OUTPUT:
RETVAL
SV*
eval (SV* self_sv, SV* js_code_sv)
ALIAS:
eval_module = 1
CODE:
perl_qjs_s* pqjs = exs_structref_ptr(self_sv);
JSContext *ctx = pqjs->ctx;
STRLEN js_code_len;
const
char
* js_code = SvPVutf8(js_code_sv, js_code_len);
int
eval_flags = ix ? JS_EVAL_TYPE_MODULE : JS_EVAL_TYPE_GLOBAL;
eval_flags |= JS_EVAL_FLAG_STRICT;
JSValue jsret = JS_Eval(ctx, js_code, js_code_len,
""
, eval_flags);
RETVAL = _return_jsvalue_or_croak(aTHX_ ctx, jsret);
OUTPUT:
RETVAL
SV*
await (SV* self_sv)
CODE:
perl_qjs_s* pqjs = exs_structref_ptr(self_sv);
JSContext *ctx = pqjs->ctx;
js_std_loop(ctx);
RETVAL = SvREFCNT_inc(self_sv);
OUTPUT:
RETVAL
# ----------------------------------------------------------------------
MODULE = JavaScript::QuickJS PACKAGE = JavaScript::QuickJS::Date
SV*
setTime (SV* self_sv, SV* num_sv)
ALIAS:
setMilliseconds = 1
setUTCMilliseconds = 2
setSeconds = 3
setUTCSeconds = 4
setMinutes = 5
setUTCMinutes = 6
setHours = 7
setUTCHours = 8
setDate = 9
setUTCDate = 10
setMonth = 11
setUTCMonth = 12
setFullYear = 13
setUTCFullYear = 14
CODE:
const
char
* setter_name = DATE_SETTER_FROM_IX[ix];
perl_qjs_jsobj_s* pqjs = exs_structref_ptr(self_sv);
JSContext *ctx = pqjs->ctx;
JSAtom prop = JS_NewAtom(ctx, setter_name);
JSValue arg;
NV nvval;
switch
(exs_sv_type(num_sv)) {
case
EXS_SVTYPE_NV:
case
EXS_SVTYPE_STRING:
nvval = SvNV(num_sv);
if
(nvval > IV_MAX || nvval < IV_MIN) {
arg = JS_NewFloat64(ctx, (
double
) nvval);
break
;
}
default
:
arg = _sviv_to_js(aTHX_ ctx, num_sv);
}
JSValue jsret = JS_Invoke(
ctx,
pqjs->jsobj,
prop,
1,
&arg
);
JS_FreeAtom(ctx, prop);
JS_FreeValue(ctx, arg);
RETVAL = _return_jsvalue_or_croak(aTHX_ pqjs->ctx, jsret);
OUTPUT:
RETVAL
SV*
toString (SV* self_sv, ...)
ALIAS:
toUTCString = 1
toGMTString = 2
toISOString = 3
toDateString = 4
toTimeString = 5
toLocaleString = 6
toLocaleDateString = 7
toLocaleTimeString = 8
getTimezoneOffset = 9
getTime = 10
getFullYear = 11
getUTCFullYear = 12
getMonth = 13
getUTCMonth = 14
getDate = 15
getUTCDate = 16
getHours = 17
getUTCHours = 18
getMinutes = 19
getUTCMinutes = 20
getSeconds = 21
getUTCSeconds = 22
getMilliseconds = 23
getUTCMilliseconds = 24
getDay = 25
getUTCDay = 26
toJSON = 27
CODE:
perl_qjs_jsobj_s* pqjs = exs_structref_ptr(self_sv);
JSContext *ctx = pqjs->ctx;
JSAtom prop = JS_NewAtom(ctx, DATE_GETTER_FROM_IX[ix]);
JSValue jsret;
if
(items > 1) {
int
params_count = items - 1;
JSValue jsargs[params_count];
SV* error = _svs_to_jsvars(aTHX_ ctx, params_count, &ST(1), jsargs);
if
(error) {
JS_FreeAtom(pqjs->ctx, prop);
croak_sv(error);
}
jsret = JS_Invoke(
ctx,
pqjs->jsobj,
prop,
params_count,
jsargs
);
for
(uint32_t i=0; i<params_count; i++) {
JS_FreeValue(ctx, jsargs[i]);
}
}
else
{
jsret = JS_Invoke(
ctx,
pqjs->jsobj,
prop,
0,
NULL
);
}
JS_FreeAtom(ctx, prop);
RETVAL = _return_jsvalue_or_croak(aTHX_ pqjs->ctx, jsret);
OUTPUT:
RETVAL
# ----------------------------------------------------------------------
MODULE = JavaScript::QuickJS PACKAGE = JavaScript::QuickJS::RegExp
SV*
exec (SV* self_sv, SV* specimen_sv)
ALIAS:
test = 1
CODE:
perl_qjs_jsobj_s* pqjs = exs_structref_ptr(self_sv);
JSContext *ctx = pqjs->ctx;
STRLEN specimen_len;
const
char
* specimen = SvPVutf8(specimen_sv, specimen_len);
JSAtom prop = JS_NewAtom(ctx, ix ?
"test"
:
"exec"
);
JSValue specimen_js = JS_NewStringLen(ctx, specimen, specimen_len);
JSValue jsret = JS_Invoke(
ctx,
pqjs->jsobj,
prop,
1,
&specimen_js
);
JS_FreeValue(ctx, specimen_js);
JS_FreeAtom(ctx, prop);
RETVAL = _return_jsvalue_or_croak(aTHX_ pqjs->ctx, jsret);
OUTPUT:
RETVAL
SV*
flags( SV* self_sv)
ALIAS:
dotAll = 1
global = 2
hasIndices = 3
ignoreCase = 4
multiline = 5
source = 6
sticky = 7
unicode = 8
lastIndex = 9
CODE:
perl_qjs_jsobj_s* pqjs = exs_structref_ptr(self_sv);
JSValue myret = JS_GetPropertyStr(pqjs->ctx, pqjs->jsobj, _REGEXP_ACCESSORS[ix]);
SV* err = NULL;
RETVAL = _JSValue_to_SV(aTHX_ pqjs->ctx, myret, &err);
JS_FreeValue(pqjs->ctx, myret);
if
(err) croak_sv(err);
OUTPUT:
RETVAL
# ----------------------------------------------------------------------
MODULE = JavaScript::QuickJS PACKAGE = JavaScript::QuickJS::JSObject
void
DESTROY( SV* self_sv )
CODE:
perl_qjs_jsobj_s* pqjs = exs_structref_ptr(self_sv);
if
(PL_dirty && pqjs->pid == getpid()) {
warn(
"DESTROYing %"
SVf
" at global destruction; memory leak likely!\n"
, self_sv);
}
JS_FreeValue(pqjs->ctx, pqjs->jsobj);
_free_jsctx(aTHX_ pqjs->ctx);
# ----------------------------------------------------------------------
MODULE = JavaScript::QuickJS PACKAGE = JavaScript::QuickJS::Function
SV*
_give_self( SV* self_sv, ... )
CODE:
RETVAL = SvREFCNT_inc(self_sv);
OUTPUT:
RETVAL
SV*
length( SV* self_sv)
ALIAS:
name = 1
CODE:
perl_qjs_jsobj_s* pqjs = exs_structref_ptr(self_sv);
JSValue myret = JS_GetPropertyStr(pqjs->ctx, pqjs->jsobj, _FUNCTION_ACCESSORS[ix]);
SV* err = NULL;
RETVAL = _JSValue_to_SV(aTHX_ pqjs->ctx, myret, &err);
JS_FreeValue(pqjs->ctx, myret);
if
(err) croak_sv(err);
OUTPUT:
RETVAL
SV*
call( SV* self_sv, SV* this_sv=&PL_sv_undef, ... )
CODE:
perl_qjs_jsobj_s* pqjs = exs_structref_ptr(self_sv);
U32 params_count = items - FUNC_CALL_INITIAL_ARGS;
SV* error = NULL;
JSValue thisjs = _sv_to_jsvalue(aTHX_ pqjs->ctx, this_sv, &error);
if
(error) croak_sv(error);
JSValue jsvars[params_count];
error = _svs_to_jsvars( aTHX_ pqjs->ctx, params_count, &ST(FUNC_CALL_INITIAL_ARGS), jsvars );
if
(error) {
JS_FreeValue(pqjs->ctx, thisjs);
croak_sv(error);
}
JSValue jsret = JS_Call(pqjs->ctx, pqjs->jsobj, thisjs, params_count, jsvars);
JS_FreeValue(pqjs->ctx, thisjs);
for
(uint32_t i=0; i<params_count; i++) {
JS_FreeValue(pqjs->ctx, jsvars[i]);
}
RETVAL = _return_jsvalue_or_croak(aTHX_ pqjs->ctx, jsret);
OUTPUT:
RETVAL
# ----------------------------------------------------------------------
MODULE = JavaScript::QuickJS PACKAGE = JavaScript::QuickJS::Promise
SV*
then( SV* self_sv, SV* on_success=&PL_sv_undef, SV* on_failure=&PL_sv_undef )
CODE:
PERL_UNUSED_ARG(on_success);
PERL_UNUSED_ARG(on_failure);
perl_qjs_jsobj_s* pqjs = exs_structref_ptr(self_sv);
SV* error = NULL;
JSValue jsvars[2];
int
callbacks_count =
sizeof
(jsvars) /
sizeof
(jsvars[0]);
error = _svs_to_jsvars( aTHX_
pqjs->ctx,
callbacks_count,
&ST(1),
jsvars
);
if
(error) {
croak_sv(error);
}
JSAtom method_name = JS_NewAtom(pqjs->ctx,
"then"
);
JSValue jsret = JS_Invoke(
pqjs->ctx,
pqjs->jsobj,
method_name,
callbacks_count,
jsvars
);
JS_FreeAtom(pqjs->ctx, method_name);
for
(uint32_t i=0; i<callbacks_count; i++) {
JS_FreeValue(pqjs->ctx, jsvars[i]);
}
RETVAL = _return_jsvalue_or_croak(aTHX_ pqjs->ctx, jsret);
OUTPUT:
RETVAL
SV*
catch
( SV* self_sv, SV* callback=&PL_sv_undef)
ALIAS:
finally = 1
CODE:
perl_qjs_jsobj_s* pqjs = exs_structref_ptr(self_sv);
SV* error = NULL;
JSValue callbackjs = _sv_to_jsvalue(aTHX_ pqjs->ctx, callback, &error);
if
(error) croak_sv(error);
JSAtom method_name = JS_NewAtom(pqjs->ctx, ix ?
"finally"
:
"catch"
);
JSValue jsret = JS_Invoke(
pqjs->ctx,
pqjs->jsobj,
method_name,
1,
&callbackjs
);
JS_FreeAtom(pqjs->ctx, method_name);
JS_FreeValue(pqjs->ctx, callbackjs);
RETVAL = _return_jsvalue_or_croak(aTHX_ pqjs->ctx, jsret);
OUTPUT:
RETVAL