#include "immutable_node.h" static VALUE cNode; static void deallocate(ImmutableNodeContext* context) { if (context->pc) { js_FinishParseContext(context->js, context->pc); free(context->pc); } JS_DestroyContext(context->js); JS_DestroyRuntime(context->runtime); free(context); } static VALUE allocate(VALUE klass) { ImmutableNodeContext * context = calloc(1L, sizeof(ImmutableNodeContext)); VALUE self = Data_Wrap_Struct(klass, 0, deallocate, context); assert(context->runtime = JS_NewRuntime(0x100000)); assert(context->js = JS_NewContext(context->runtime, 8192L)); return self; } /* * call-seq: * parse_io(stream, filename=nil, line_number=0) * * Parses an IO object, returning a native spidermonkey parse tree. */ static VALUE parse_io(int argc, VALUE *argv, VALUE klass) { VALUE self = allocate(klass); VALUE stream, filename, linenum; ImmutableNodeContext* context; Data_Get_Struct(self, ImmutableNodeContext, context); assert(context->pc = calloc(1L, sizeof(JSParseContext))); rb_scan_args( argc, argv, "12", &stream, &filename, &linenum ); VALUE file_contents = rb_funcall(stream, rb_intern("read"), 0); size_t length = NUM2INT(rb_funcall(file_contents, rb_intern("length"), 0)); jschar* chars; assert(chars = js_InflateString(context->js, StringValuePtr(file_contents), &length)); if(!filename && rb_respond_to(stream, rb_intern("path"))) { filename = rb_funcall(stream, rb_intern("path"), 0); } char* filenamez = RTEST(filename) ? StringValueCStr(filename) : NULL; int linenumi = RTEST(linenum) ? NUM2INT(linenum) : 1; assert(js_InitParseContext(context->js, context->pc, NULL, chars, length, NULL, filenamez, (unsigned)linenumi)); JS_SetVersion(context->js, JSVERSION_LATEST); context->node = js_ParseScript(context->js, JS_NewObject(context->js, NULL, NULL, NULL), context->pc); if(JS_IsExceptionPending(context->js)) { jsval exception, message, file_name, line_number; JS_GetPendingException(context->js, &exception); JS_GetProperty(context->js, JSVAL_TO_OBJECT(exception), "message",&message); JS_GetProperty(context->js, JSVAL_TO_OBJECT(exception), "fileName",&file_name); JS_GetProperty(context->js, JSVAL_TO_OBJECT(exception), "lineNumber",&line_number); JS_ClearPendingException(context->js); rb_funcall( self, rb_intern("raise_parse_error"), 3, message == JSVAL_NULL ? Qnil : rb_str_new2(JS_GetStringBytes(JSVAL_TO_STRING(message))), rb_str_new2(JS_GetStringBytes(JSVAL_TO_STRING(file_name))), INT2NUM((long)JSVAL_TO_INT(line_number)) ); } return self; } /* * call-seq: * line * * Returns the line number of the node. */ static VALUE line(VALUE self) { ImmutableNodeContext * ctx; Data_Get_Struct(self, ImmutableNodeContext, ctx); return INT2NUM((long)(ctx->node->pn_pos.begin.lineno)); } /* * call-seq: * index * * Returns the column number of the node. */ static VALUE begin_index(VALUE self) { ImmutableNodeContext * ctx; Data_Get_Struct(self, ImmutableNodeContext, ctx); return INT2NUM((long)(ctx->node->pn_pos.begin.index)); } /* * call-seq: * pn_arity * * Returns the arity of the node as a symbol. */ static VALUE pn_arity(VALUE self) { ImmutableNodeContext * ctx; Data_Get_Struct(self, ImmutableNodeContext, ctx); switch(ctx->node->pn_arity) { case PN_FUNC: return ID2SYM(rb_intern("pn_func")); case PN_LIST: return ID2SYM(rb_intern("pn_list")); case PN_TERNARY: return ID2SYM(rb_intern("pn_ternary")); case PN_BINARY: return ID2SYM(rb_intern("pn_binary")); case PN_UNARY: return ID2SYM(rb_intern("pn_unary")); case PN_NAME: return ID2SYM(rb_intern("pn_name")); case PN_NULLARY: return ID2SYM(rb_intern("pn_nullary")); } return Qnil; } /* * call-seq: * pn_type * * Returns the type of the node as a symbol. */ static VALUE pn_type(VALUE self) { ImmutableNodeContext * ctx; Data_Get_Struct(self, ImmutableNodeContext, ctx); switch(ctx->node->pn_type) { <% tokens.each do |token| %> case <%= token %>: return ID2SYM(rb_intern("<%= token.downcase %>")); <% end %> } return INT2NUM((long)(ctx->node->pn_type)); } /* * call-seq: * pn_expr * * Returns the parse node expression as an ImmutableNode. */ static VALUE data_pn_expr(VALUE self) { ImmutableNodeContext * ctx; Data_Get_Struct(self, ImmutableNodeContext, ctx); if(ctx->node->pn_expr) { ImmutableNodeContext *roc; VALUE node = Data_Make_Struct(cNode, ImmutableNodeContext, NULL, NULL, roc); roc->js = ctx->js; roc->node = ctx->node->pn_expr; return node; } return Qnil; } /* * call-seq: * pn_kid * * Returns the child ImmutableNode. */ static VALUE data_pn_kid(VALUE self) { ImmutableNodeContext * ctx; Data_Get_Struct(self, ImmutableNodeContext, ctx); if(ctx->node->pn_kid) { ImmutableNodeContext *roc; VALUE node = Data_Make_Struct(cNode, ImmutableNodeContext, NULL, NULL, roc); roc->js = ctx->js; roc->node = ctx->node->pn_kid; return node; } return Qnil; } /* * call-seq: * pn_kid1 * * Returns the first child ImmutableNode. */ static VALUE data_pn_kid1(VALUE self) { ImmutableNodeContext * ctx; Data_Get_Struct(self, ImmutableNodeContext, ctx); if(ctx->node->pn_kid1) { ImmutableNodeContext *roc; VALUE node = Data_Make_Struct(cNode, ImmutableNodeContext, NULL, NULL, roc); roc->js = ctx->js; roc->node = ctx->node->pn_kid1; return node; } return Qnil; } /* * call-seq: * pn_kid2 * * Returns the second child ImmutableNode. */ static VALUE data_pn_kid2(VALUE self) { ImmutableNodeContext * ctx; Data_Get_Struct(self, ImmutableNodeContext, ctx); if(ctx->node->pn_kid2) { ImmutableNodeContext *roc; VALUE node = Data_Make_Struct(cNode, ImmutableNodeContext, NULL, NULL, roc); roc->js = ctx->js; roc->node = ctx->node->pn_kid2; return node; } return Qnil; } /* * call-seq: * pn_kid3 * * Returns the third child ImmutableNode. */ static VALUE data_pn_kid3(VALUE self) { ImmutableNodeContext * ctx; Data_Get_Struct(self, ImmutableNodeContext, ctx); if(ctx->node->pn_kid3) { ImmutableNodeContext *roc; VALUE node = Data_Make_Struct(cNode, ImmutableNodeContext, NULL, NULL, roc); roc->js = ctx->js; roc->node = ctx->node->pn_kid3; return node; } return Qnil; } /* * call-seq: * pn_dval * * Returns the numeric value of the node. */ static VALUE data_pn_dval(VALUE self) { ImmutableNodeContext * ctx; Data_Get_Struct(self, ImmutableNodeContext, ctx); if(JSVAL_IS_NUMBER(ATOM_KEY(ctx->node->pn_atom))) { return rb_float_new(ctx->node->pn_dval); } else { return INT2NUM((long)(ctx->node->pn_dval)); } } /* * call-seq: * pn_op * * Returns the op code for the node as a symbol. */ static VALUE data_pn_op(VALUE self) { ImmutableNodeContext * ctx; Data_Get_Struct(self, ImmutableNodeContext, ctx); return jsop_to_symbol(ctx->node->pn_op); } /* * call-seq: * pn_left * * Returns the left side ImmutableNode. */ static VALUE data_pn_left(VALUE self) { ImmutableNodeContext * ctx; Data_Get_Struct(self, ImmutableNodeContext, ctx); if(ctx->node->pn_left) { ImmutableNodeContext *roc; VALUE node = Data_Make_Struct(cNode, ImmutableNodeContext, NULL, NULL, roc); roc->js = ctx->js; roc->node = ctx->node->pn_left; return node; } return Qnil; } /* * call-seq: * pn_extra * * Returns extra informaton about the node as an Integer. */ static VALUE data_pn_extra(VALUE self) { ImmutableNodeContext * ctx; Data_Get_Struct(self, ImmutableNodeContext, ctx); return UINT2NUM((unsigned long)(ctx->node->pn_extra)); } /* * call-seq: * name * * Returns the name of the node. */ static VALUE name(VALUE self) { ImmutableNodeContext * ctx; Data_Get_Struct(self, ImmutableNodeContext, ctx); return rb_str_new2(JS_GetStringBytes(ATOM_TO_STRING(ctx->node->pn_atom))); } /* * call-seq: * regexp * * Returns the regexp value as a String. */ static VALUE regexp(VALUE self) { ImmutableNodeContext * ctx; jsval result; Data_Get_Struct(self, ImmutableNodeContext, ctx); js_regexp_toString(ctx->js, ctx->node->pn_pob->object, &result); return rb_str_new2(JS_GetStringBytes(JSVAL_TO_STRING(result))); } /* * call-seq: * function_name * * Returns the function name as a String. */ static VALUE function_name(VALUE self) { ImmutableNodeContext * ctx; JSFunction * f; JSObject * object; Data_Get_Struct(self, ImmutableNodeContext, ctx); object = ctx->node->pn_funpob->object; f = (JSFunction *)JS_GetPrivate(ctx->js, ctx->node->pn_funpob->object); if(f->atom) { return rb_str_new2(JS_GetStringBytes(ATOM_TO_STRING(f->atom))); } else { return Qnil; } } /* * call-seq: * function_args * * Returns the function argument names as an Array of String. */ static VALUE function_args(VALUE self) { ImmutableNodeContext * ctx; JSFunction * f; JSObject * object; jsuword* names; VALUE func_args; int i; Data_Get_Struct(self, ImmutableNodeContext, ctx); object = ctx->node->pn_funpob->object; f = (JSFunction *)JS_GetPrivate(ctx->js, ctx->node->pn_funpob->object); func_args = rb_ary_new2((long)f->nargs); if(f->nargs > 0) { names = js_GetLocalNameArray(ctx->js, f, &ctx->js->tempPool); for(i = 0; i < f->nargs; i++) { rb_ary_push(func_args, rb_str_new2(JS_GetStringBytes(ATOM_TO_STRING(names[i]))) ); } } return func_args; } /* * call-seq: * function_body * * Returns the function body as an ImmutableNode. */ static VALUE function_body(VALUE self) { ImmutableNodeContext * ctx; JSFunction * f; JSObject * object; Data_Get_Struct(self, ImmutableNodeContext, ctx); object = ctx->node->pn_funpob->object; f = (JSFunction *)JS_GetPrivate(ctx->js, ctx->node->pn_funpob->object); if(ctx->node->pn_body) { ImmutableNodeContext *roc; VALUE node = Data_Make_Struct(cNode, ImmutableNodeContext, NULL, NULL, roc); roc->js = ctx->js; roc->node = ctx->node->pn_body; return node; } return Qnil; } /* * call-seq: * pn_right * * Returns right side as an ImmutableNode. */ static VALUE data_pn_right(VALUE self) { ImmutableNodeContext * ctx; Data_Get_Struct(self, ImmutableNodeContext, ctx); if(ctx->node->pn_right) { ImmutableNodeContext *roc; VALUE node = Data_Make_Struct(cNode, ImmutableNodeContext, NULL, NULL, roc); roc->js = ctx->js; roc->node = ctx->node->pn_right; return node; } return Qnil; } /* * call-seq: * children * * Returns children as an Array of ImmutableNode. */ static VALUE children(VALUE self) { ImmutableNodeContext * ctx; JSParseNode * p; VALUE children; Data_Get_Struct(self, ImmutableNodeContext, ctx); children = rb_ary_new(); for(p = ctx->node->pn_head; p != NULL; p = p->pn_next) { ImmutableNodeContext *roc; VALUE node = Data_Make_Struct(cNode, ImmutableNodeContext, NULL, NULL, roc); roc->js = ctx->js; roc->node = p; rb_ary_push(children, node); } return children; } VALUE jsop_to_symbol(JSUint32 jsop) { switch(jsop) { <% jsops.each do |jsop| %> case <%= jsop %>: return ID2SYM(rb_intern("<%= jsop.downcase %>")); <% end %> } return UINT2NUM((unsigned long)(jsop)); } void init_Johnson_SpiderMonkey_Immutable_Node(VALUE spidermonkey) { /* HACK: These comments are *only* to make RDoc happy. VALUE johnson = rb_define_module("Johnson"); VALUE spidermonkey = rb_define_module_under(johnson, "SpiderMonkey"); */ /* ImmutableNode class. */ cNode = rb_define_class_under(spidermonkey, "ImmutableNode", rb_cObject); rb_define_alloc_func(cNode, allocate); rb_define_singleton_method(cNode, "parse_io", parse_io, -1); rb_define_method(cNode, "line", line, 0); rb_define_method(cNode, "index", begin_index, 0); rb_define_method(cNode, "pn_arity", pn_arity, 0); rb_define_method(cNode, "pn_type", pn_type, 0); rb_define_method(cNode, "pn_expr", data_pn_expr, 0); rb_define_method(cNode, "pn_kid", data_pn_kid, 0); rb_define_method(cNode, "pn_kid1", data_pn_kid1, 0); rb_define_method(cNode, "pn_kid2", data_pn_kid2, 0); rb_define_method(cNode, "pn_kid3", data_pn_kid3, 0); rb_define_method(cNode, "pn_dval", data_pn_dval, 0); rb_define_method(cNode, "pn_op", data_pn_op, 0); rb_define_method(cNode, "pn_left", data_pn_left, 0); rb_define_method(cNode, "pn_extra", data_pn_extra, 0); rb_define_method(cNode, "name", name, 0); rb_define_method(cNode, "regexp", regexp, 0); rb_define_method(cNode, "function_name", function_name, 0); rb_define_method(cNode, "function_args", function_args, 0); rb_define_method(cNode, "function_body", function_body, 0); rb_define_method(cNode, "pn_right", data_pn_right, 0); rb_define_method(cNode, "children", children, 0); }