/* --------------------------------------------------------------------- */
/* SpiderMonkey.xs -- Perl Interface to the SpiderMonkey JavaScript */
/* implementation. */
/* */
/* Revision: $Revision: 1.3 $ */
/* Last Checkin: $Date: 2006/02/12 11:22:51 $ */
/* By: $Author: thomas_busch $ */
/* */
/* Author: Mike Schilli mschilli1@aol.com, 2001 */
/* --------------------------------------------------------------------- */
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include "jsapi.h"
#ifdef _MSC_VER
/* As suggested in https://rt.cpan.org/Ticket/Display.html?id=6984 */
#define snprintf _snprintf
#endif
/* JSRuntime needs this global class */
static
JSClass global_class = {
"Global", 0,
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub
};
static int Debug = 0;
/* It's kinda silly that we have to replicate this for getters and setters,
* but there doesn't seem to be a way to distinguish between getters
* and setters if we use the same function. (Somewhere I read in a
* usenet posting there's something like IS_ASSIGN, but this doesn't
* seem to be in SpiderMonkey 1.5).
*/
/* --------------------------------------------------------------------- */
JSBool getsetter_dispatcher(
JSContext *cx,
JSObject *obj,
jsval id,
jsval *vp,
char *what
/* --------------------------------------------------------------------- */
) {
dSP;
/* Call back into perl */
ENTER ;
SAVETMPS ;
PUSHMARK(SP);
/* A somewhat nasty trick: Since JS_DefineObject() down below
* returns a *JS_Object, which is typemapped as T_PTRREF,
* and which is a reference (!) pointing to the real C pointer,
* we need to brutally obtain the obj's address by casting
* it to an int and forming a scalar out of it.
* On the other hand, when Spidermonkey.pm stores the
* object's setters/getters, it will dereference
* what it gets from JS_DefineObject() (therefore
* obtain the object's address in memory) to index its
* hash table.
* I hope all reasonable machines can hold an address in
* an int.
*/
XPUSHs(sv_2mortal(newSViv((int)obj)));
XPUSHs(sv_2mortal(newSVpv(JS_GetStringBytes(JSVAL_TO_STRING(id)), 0)));
XPUSHs(sv_2mortal(newSVpv(what, 0)));
XPUSHs(sv_2mortal(newSVpv(JS_GetStringBytes(JSVAL_TO_STRING(*vp)), 0)));
PUTBACK;
call_pv("JavaScript::SpiderMonkey::getsetter_dispatcher", G_DISCARD);
FREETMPS;
LEAVE;
return JS_TRUE;
}
/* --------------------------------------------------------------------- */
JSBool getter_dispatcher(
JSContext *cx,
JSObject *obj,
jsval id,
jsval *vp
/* --------------------------------------------------------------------- */
) {
return getsetter_dispatcher(cx, obj, id, vp, "getter");
}
/* --------------------------------------------------------------------- */
JSBool setter_dispatcher(
JSContext *cx,
JSObject *obj,
jsval id,
jsval *vp
/* --------------------------------------------------------------------- */
) {
return getsetter_dispatcher(cx, obj, id, vp, "setter");
}
/* --------------------------------------------------------------------- */
int debug_enabled(
/* --------------------------------------------------------------------- */
) {
dSP;
int enabled = 0;
int count = 0;
/* Call back into perl */
ENTER ;
SAVETMPS ;
PUTBACK;
count = call_pv("JavaScript::SpiderMonkey::debug_enabled", G_SCALAR);
if(count == 1) {
if(POPi == 1) {
enabled = 1;
}
}
FREETMPS;
LEAVE;
return enabled;
}
/* --------------------------------------------------------------------- */
static JSBool
FunctionDispatcher(JSContext *cx, JSObject *obj, uintN argc,
jsval *argv, jsval *rval) {
/* --------------------------------------------------------------------- */
dSP;
SV *sv;
unsigned i;
int count;
JSFunction *fun;
fun = JS_ValueToFunction(cx, argv[-2]);
/* printf("Function %s received %d arguments\n",
(char *) JS_GetFunctionName(fun),
(int) argc); */
/* Call back into perl */
ENTER ;
SAVETMPS ;
PUSHMARK(SP);
XPUSHs(sv_2mortal(newSViv((int)obj)));
XPUSHs(sv_2mortal(newSVpv(
JS_GetFunctionName(fun), 0)));
for(i=0; i<argc; i++) {
XPUSHs(sv_2mortal(newSVpv(
JS_GetStringBytes(JS_ValueToString(cx, argv[i])), 0)));
}
PUTBACK;
count = call_pv("JavaScript::SpiderMonkey::function_dispatcher", G_SCALAR);
SPAGAIN;
if(Debug)
fprintf(stderr, "DEBUG: Count is %d\n", count);
if( count > 0) {
sv = POPs;
if(SvIOK(sv)) {
if(Debug)
fprintf(stderr, "DEBUG: %lx is an IV\n", (long) sv);
*rval = OBJECT_TO_JSVAL(SvIV(sv));
} else {
if(Debug)
fprintf(stderr, "DEBUG: %lx is a string\n", (long) sv);
*rval = STRING_TO_JSVAL(SvPV(sv, PL_na));
}
}
PUTBACK;
FREETMPS;
LEAVE;
return JS_TRUE;
}
/* --------------------------------------------------------------------- */
static void
ErrorReporter(JSContext *cx, const char *message, JSErrorReport *report) {
/* --------------------------------------------------------------------- */
char msg[400];
snprintf(msg, sizeof(msg),
"Error: %s at line %d: %s", message, report->lineno,
report->linebuf);
sv_setpv(get_sv("@", TRUE), msg);
}
MODULE = JavaScript::SpiderMonkey PACKAGE = JavaScript::SpiderMonkey
PROTOTYPES: DISABLE
######################################################################
char *
JS_GetImplementationVersion()
######################################################################
CODE:
{
RETVAL = (char *) JS_GetImplementationVersion();
}
OUTPUT:
RETVAL
######################################################################
JSRuntime *
JS_NewRuntime(maxbytes)
int maxbytes
######################################################################
PREINIT:
JSRuntime *rt;
CODE:
{
rt = JS_NewRuntime(maxbytes);
if(!rt) {
XSRETURN_UNDEF;
}
RETVAL = rt;
}
OUTPUT:
RETVAL
######################################################################
int
JS_DestroyRuntime(rt)
JSRuntime *rt
######################################################################
CODE:
{
JS_DestroyRuntime(rt);
RETVAL = 0;
}
OUTPUT:
RETVAL
######################################################################
JSRuntime *
JS_Init(maxbytes)
int maxbytes
######################################################################
PREINIT:
JSRuntime *rt;
CODE:
{
rt = JS_Init(maxbytes);
if(!rt) {
XSRETURN_UNDEF;
}
/* Replace this by Debug = debug_enabled(); once
* Log::Log4perl 0.47 is out */
Debug = 0;
RETVAL = rt;
}
OUTPUT:
RETVAL
######################################################################
JSContext *
JS_NewContext(rt, stack_chunk_size)
JSRuntime *rt
int stack_chunk_size
######################################################################
PREINIT:
JSContext *cx;
CODE:
{
cx = JS_NewContext(rt, stack_chunk_size);
if(!cx) {
XSRETURN_UNDEF;
}
RETVAL = cx;
}
OUTPUT:
RETVAL
######################################################################
int
JS_DestroyContext(cx)
JSContext *cx;
######################################################################
CODE:
{
JS_DestroyContext(cx);
RETVAL = 0;
}
OUTPUT:
RETVAL
######################################################################
JSObject *
JS_NewObject(cx, class, proto, parent)
JSContext * cx
JSClass * class
JSObject * proto
JSObject * parent
######################################################################
PREINIT:
JSObject *obj;
CODE:
{
obj = JS_NewObject(cx, class, NULL, NULL);
if(!obj) {
XSRETURN_UNDEF;
}
RETVAL = obj;
}
OUTPUT:
RETVAL
######################################################################
JSObject *
JS_InitClass(cx, iobj, parent_proto, clasp, constructor, nargs, ps, fs, static_ps, static_fs)
JSContext * cx
JSObject *iobj
JSObject *parent_proto
JSClass *clasp
JSNative constructor
int nargs
JSPropertySpec *ps
JSFunctionSpec *fs
JSPropertySpec *static_ps
JSFunctionSpec *static_fs
######################################################################
PREINIT:
JSObject *obj;
uintN na;
INIT:
na = (uintN) nargs;
CODE:
{
obj = JS_InitClass(cx, iobj, parent_proto, clasp,
constructor, nargs, ps, fs, static_ps,
static_fs);
if(!obj) {
XSRETURN_UNDEF;
}
RETVAL = obj;
}
OUTPUT:
RETVAL
######################################################################
JSClass *
JS_GlobalClass()
######################################################################
PREINIT:
JSClass *gc;
CODE:
{
gc = &global_class;
RETVAL = gc;
}
OUTPUT:
RETVAL
######################################################################
int
JS_EvaluateScript(cx, gobj, script, length, filename, lineno)
JSContext * cx
JSObject * gobj
char * script
int length
char * filename
int lineno
######################################################################
PREINIT:
uintN len;
uintN ln;
int rc;
jsval jsval;
INIT:
len = (uintN) length;
ln = (uintN) lineno;
CODE:
{
rc = JS_EvaluateScript(cx, gobj, script, len, filename,
ln, &jsval);
if(!rc) {
XSRETURN_UNDEF;
}
RETVAL = rc;
}
OUTPUT:
RETVAL
######################################################################
int
JS_InitStandardClasses(cx, gobj)
JSContext * cx
JSObject * gobj
######################################################################
PREINIT:
JSBool rc;
CODE:
{
rc = JS_InitStandardClasses(cx, gobj);
if(!rc) {
XSRETURN_UNDEF;
}
RETVAL = (int) rc;
}
OUTPUT:
RETVAL
######################################################################
int
JS_DefineFunction(cx, obj, name, nargs, flags)
JSContext * cx
JSObject * obj
char * name
int nargs
int flags
######################################################################
PREINIT:
JSFunction *rc;
CODE:
{
rc = JS_DefineFunction(cx, obj,
(const char *) name, FunctionDispatcher,
(uintN) nargs, (uintN) flags);
if(!rc) {
XSRETURN_UNDEF;
}
RETVAL = (int) rc;
}
OUTPUT:
RETVAL
######################################################################
int
JS_SetErrorReporter(cx)
JSContext * cx
######################################################################
CODE:
{
JS_SetErrorReporter(cx, ErrorReporter);
RETVAL = 0;
}
OUTPUT:
RETVAL
######################################################################
JSObject *
JS_DefineObject(cx, obj, name, class, proto)
JSContext * cx
JSObject * obj
char * name
JSClass * class
JSObject * proto
######################################################################
PREINIT:
SV *sv = sv_newmortal();
CODE:
{
RETVAL = JS_DefineObject(cx, obj, name, class, proto, 0);
}
OUTPUT:
RETVAL
######################################################################
int
JS_DefineProperty(cx, obj, name, value)
JSContext * cx
JSObject * obj
char * name
char * value
#JSPropertyOp getter
#JSPropertyOp setter
#uintN flags
######################################################################
PREINIT:
JSBool rc;
JSString *str;
CODE:
{
str = JS_NewStringCopyZ(cx, value);
/* This implementation is somewhat sub-optimal, since it
* calls back into perl even if no getters/setters have
* been defined. The necessity for a callback is determined
* at the perl level, where there's a data structure mapping
* out each object's properties and their getter/setter settings.
*/
rc = JS_DefineProperty(cx, obj, name, STRING_TO_JSVAL(str),
getter_dispatcher, setter_dispatcher, 0);
RETVAL = (int) rc;
}
OUTPUT:
RETVAL
######################################################################
void
JS_GetProperty(cx, obj, name)
JSContext * cx
JSObject * obj
char * name
######################################################################
PREINIT:
JSBool rc;
jsval vp;
JSString *str;
SV *sv = sv_newmortal();
PPCODE:
{
rc = JS_TRUE;
rc = JS_GetProperty(cx, obj, name, &vp);
if(rc) {
str = JS_ValueToString(cx, vp);
if(strcmp(JS_GetStringBytes(str), "undefined") == 0) {
sv = &PL_sv_undef;
} else {
sv_setpv(sv, JS_GetStringBytes(str));
}
} else {
sv = &PL_sv_undef;
}
XPUSHs(sv);
}
######################################################################
JSObject *
JS_NewArrayObject(cx)
JSContext * cx
######################################################################
PREINIT:
JSObject *rc;
CODE:
{
rc = JS_NewArrayObject(cx, 0, NULL);
RETVAL = rc;
}
OUTPUT:
RETVAL
######################################################################
int
JS_SetElement(cx, obj, idx, valptr)
JSContext *cx
JSObject *obj
int idx
char *valptr
######################################################################
PREINIT:
JSBool rc;
JSString *str;
jsval val;
CODE:
{
str = JS_NewStringCopyZ(cx, valptr);
val = STRING_TO_JSVAL(str);
rc = JS_SetElement(cx, obj, idx, &val);
if(rc) {
RETVAL = 1;
} else {
RETVAL = 0;
}
}
OUTPUT:
RETVAL
######################################################################
int
JS_SetElementAsObject(cx, obj, idx, elobj)
JSContext *cx
JSObject *obj
int idx
JSObject *elobj
######################################################################
PREINIT:
JSBool rc;
jsval val;
CODE:
{
val = OBJECT_TO_JSVAL(elobj);
rc = JS_SetElement(cx, obj, idx, &val);
if(rc) {
RETVAL = 1;
} else {
RETVAL = 0;
}
}
OUTPUT:
RETVAL
######################################################################
void
JS_GetElement(cx, obj, idx)
JSContext *cx
JSObject *obj
int idx
######################################################################
PREINIT:
JSBool rc;
jsval vp;
JSString *str;
SV *sv = sv_newmortal();
PPCODE:
{
rc = JS_GetElement(cx, obj, idx, &vp);
if(rc) {
str = JS_ValueToString(cx, vp);
if(strcmp(JS_GetStringBytes(str), "undefined") == 0) {
sv = &PL_sv_undef;
} else {
sv_setpv(sv, JS_GetStringBytes(str));
}
} else {
sv = &PL_sv_undef;
}
XPUSHs(sv);
}
######################################################################
JSClass *
JS_GetClass(obj)
JSObject * obj
######################################################################
PREINIT:
JSClass *rc;
CODE:
{
rc = JS_GetClass(obj);
RETVAL = rc;
}
OUTPUT:
RETVAL