/*
* Javascript binding for Entity
* Copyright (c) 2000 Brian Bassett and Ian Main
*
* Author: Ian Main <imain@gtk.org
* Brian Bassett <bbassett@bbassett.net>
*/
/*
* This renderer is free software; please see the LICENSE file for
* specific information as to licensing conditions.
*/
/*
* $Source: /home/cvs/entity/renderers/javascript/js-embed.c,v $
* $Id: js-embed.c,v 1.41 2000/12/14 22:53:33 imain Exp $
*/
#include <njs/njs.h>
#include "entity.h"
#include "js-embed.h"
#include "js-ENode.h"
static void
jse_init_types (JSInterpPtr interp)
{
JSVirtualMachine *vm = interp->vm;
njs_extension_entry (vm);
js_Entity_ENodeAttrib (vm);
return;
}
static JSInterpPtr
jse_create_compile_interp ()
{
JSInterpOptions options;
JSInterpPtr interp;
js_init_default_options (&options);
interp = js_create_interp (&options);
if (interp == NULL) {
g_warning ("javascript: Error creating JSInterpreter");
return (NULL);
}
/* Make a smaller than average heap to force more * rigorous garbage
* collection. Usually this is 1024 * 1024. */
interp->vm->gc.trigger = 1024L * 16L;
if (!js_ext_default_directories (interp)) {
g_warning ("javascript: Cannot load standard extension directories");
}
return (interp);
}
static JSInterpPtr
jse_create_interp ()
{
JSInterpOptions options;
JSInterpPtr interp;
js_init_default_options (&options);
options.warn_missing_semicolon = 1;
options.warn_shadow = 1;
options.warn_unused_variable = 1;
options.no_compiler = 1;
options.optimize_heavy = 1;
/* options.stacktrace_on_error = 1; */
interp = js_create_interp (&options);
if (interp == NULL) {
g_warning ("javascript: Error creating JSInterpreter");
return (NULL);
}
/* Make a smaller than average heap to force more * rigorous garbage
* collection. Usually this is 1024 * 1024. */
interp->vm->gc.trigger = 1024L * 64L;
if (!js_ext_default_directories (interp)) {
g_warning ("javascript: Cannot load standard extension directories");
}
if (!js_define_module (interp, jse_init_types)) {
g_warning ("javascript: Cannot define ENode object type");
}
return interp;
}
static ENode *
jse_find_containing_object (ENode * node)
{
if (ebuf_equal_str (node->element, "object"))
return node;
else
return enode_parent (node, "object");
}
static void
jse_node_render (ENode * node)
{
ENode *containing_object = jse_find_containing_object (node);
EBufConst *data = enode_get_data (node);
JSInterpPtr interp;
static JSInterpPtr compile_interp = NULL;
if (containing_object) {
interp = (JSInterpPtr) enode_get_kv (containing_object, "js-interp");
} else {
g_warning ("<javascript> tags must go within <object>'s");
return;
}
if (!compile_interp)
compile_interp = jse_create_compile_interp ();
/* Save reference node on stack. Any functions or methods called dealing
* * with enode lookups will use this as the reference. */
enode_call_reference_push (node);
EDEBUG (("javascript", "rendering"));
/* Create our interpreter if we don't have one already. */
if (interp == NULL) {
interp = jse_create_interp ();
if (!interp)
return;
enode_set_kv (containing_object, "js-interp", interp);
}
if (ebuf_not_empty (data)) {
unsigned char *bytecode;
unsigned int codelen;
int ret;
ret =
js_compile_data_to_byte_code (compile_interp, data->str, data->len,
&bytecode, &codelen);
if (!ret) {
g_warning ("javascript: byte compile failed in node %s.%s: %s",
node->element->str,
enode_attrib_str (node, "name", NULL),
js_error_message (compile_interp));
} else {
EDEBUG (("js-embed", "byte code compiled, length is %d", codelen));
ret = js_execute_byte_code (interp, bytecode, codelen);
if (!ret) {
g_warning
("javascript: error executing bytecode in node %s.%s: %s",
node->element->str, enode_attrib_str (node, "name", NULL),
js_error_message (interp));
}
}
}
enode_call_reference_pop ();
return;
}
static void
jse_node_destroy (ENode * node)
{
EDEBUG (("javascript", "destroying"));
/* Really ought to destroy the interpreters here. */
/* Actually, what I'd probably recommend here, is attaching * a destroy
* watcher to the containing object when you set * up the interpreter,
* and use that callback to destroy it. * That way you don't have to
* worry about multiple <javascript> * sections and the semantics
* involved there. */
return;
}
static EBuf *
jse_execute_function (ENode * node, gchar * function, GSList * args)
{
GSList *tmp;
LangArg *arg;
gint n_args;
JSNode *js_args = NULL;
ENode *containing_object = jse_find_containing_object (node);
JSInterpPtr interp =
(JSInterpPtr) enode_get_kv (containing_object, "js-interp");
static JSNode js_tmp;
gint i = 1;
if (interp == NULL) {
g_warning
("javascript function '%s' asked to be executed, but no interpreter has been created for this object.",
function);
return (NULL);
}
n_args = g_slist_length (args);
/* njs uses first arg as argument count */
n_args++;
js_args = js_calloc (interp->vm, 1, n_args * sizeof (JSNode));
js_args[0].u.vinteger = n_args;
js_args[0].type = JS_INTEGER;
for (tmp = args; tmp; tmp = tmp->next) {
arg = (LangArg *) tmp->data;
if (arg->type == LANG_NODE) {
ENodeInstanceCtx *ni;
ENode *node = arg->data;
JSNode *enode_node;
JSBuiltinInfo *enode_info;
enode_node =
&interp->vm->globals[js_vm_intern (interp->vm, "ENode")];
enode_info = enode_node->u.vbuiltin->info;
ni = js_calloc (interp->vm, 1, sizeof (*ni));
ni->enode = node;
enode_ref (node);
js_vm_builtin_create (interp->vm, &js_args[i], enode_info, ni);
} else if (arg->type == LANG_STRING) {
char *str = arg->data;
js_vm_make_string (interp->vm, &js_args[i], str, strlen (str));
js_args[i].type = JS_STRING;
} else if (arg->type == LANG_INT) {
js_args[i].type = JS_INTEGER;
js_args[i].u.vinteger = arg->intdata;
} else if (arg->type == LANG_BINSTRING) {
char *str = arg->data;
int len = arg->size;
js_vm_make_string (interp->vm, &js_args[i], str, len);
js_args[i].type = JS_STRING;
} else if (arg->type == LANG_DOUBLE) {
js_args[i].type = JS_FLOAT;
js_args[i].u.vfloat = arg->doubledata;
}
i++;
enode_call_free_arg (arg);
}
EDEBUG (("javascript", "calling function '%s'", function));
if (!interp->vm->consts) {
g_print ("interp->vm->globals is NULL\n");
}
if (!js_vm_apply (interp->vm, function, &js_tmp, n_args, js_args)) {
/* I doubt this is correct, but it works :) */
g_warning ("Error executing function '%s', called from node %s.%s: %s",
function, node->element->str, enode_attrib_str (node, "name",
NULL),
interp->vm->error);
}
EDEBUG (("javascript", "call complete", function));
g_free (js_args);
return NULL;
}
void
#ifdef STATIC_JAVASCRIPT
javascript_init (RendererFlags flags)
#else
renderer_init (RendererFlags flags)
#endif
{
Element *element;
if (flags & RENDERER_REGISTER) {
/* Register javascript as a tag type */
element = g_malloc0 (sizeof (Element));
element->render_func = jse_node_render;
element->destroy_func = jse_node_destroy;
element->description = "Embed JavaScript in your application.";
element->tag = "javascript";
element_register (element);
/* Register javascript language type */
language_register ("javascript", jse_execute_function);
}
}
syntax highlighted by Code2HTML, v. 0.9.1