/* * Javascript binding for Entity * Copyright (c) 2000 Brian Bassett and Ian Main * * Author: Ian Main */ /* * 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 #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 (" tags must go within '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 * 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); } }