#include <v8.h>
#include "V8Context.h"
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include "ppport.h"
#undef New
using namespace v8;
static SV *_convert_v8value_to_sv(Handle<Value> value)
{
if (value->IsUndefined()) {
return &PL_sv_undef;
} else if (value->IsNull()) {
return &PL_sv_undef;
} else if (value->IsInt32()) {
return newSViv(value->Int32Value());
} else if (value->IsBoolean()) {
return newSVuv(value->Uint32Value());
} else if (value->IsNumber()) {
return newSVnv(value->NumberValue());
} else if (value->IsString()) {
SV *sv = newSVpv(*(String::Utf8Value(value)), 0);
sv_utf8_decode(sv);
return sv;
} else if (value->IsArray()) {
Handle<Array> arrayVal = Handle<Array>::Cast( value );
AV *av = newAV();
for (int i = 0; i < arrayVal->Length(); i++) {
Handle<Value> elementVal = arrayVal->Get( v8::Integer::New( i ) );
av_push( av, _convert_v8value_to_sv( elementVal ) );
}
return newRV_noinc((SV *) av);
} else if (value->IsObject()) {
Handle<Object> objectVal = Handle<Object>::Cast( value );
HV *hv = newHV();
Local<Array> properties = objectVal->GetPropertyNames();
for (int i = 0; i < properties->Length(); i++) {
Local<Integer> propertyIndex = Integer::New( i );
Local<String> propertyName = Local<String>::Cast( properties->Get( propertyIndex ) );
String::Utf8Value propertyNameUTF8( propertyName );
Local<Value> propertyValue = objectVal->Get( propertyName );
hv_store(hv, *propertyNameUTF8, 0 - propertyNameUTF8.length(), _convert_v8value_to_sv( propertyValue ), 0 );
}
return newRV_noinc((SV*)hv);
} else {
croak("Can not convert js value to a perl one");
return &PL_sv_undef;
}
}
static Handle<Value>
_convert_sv_to_v8value_unscoped(SV *sv)
{
int keycount;
HV *hash;
AV *list;
SV *sv_val, *sv_key;
HE *hash_entry;
I32 keylen;
Handle<Object> object;
Handle<Array> array;
Handle<Value> ret;
if (0) ;
else if (SvIOK_UV(sv))
return Uint32::New(SvUV(sv));
else if (SvIOK(sv))
return Integer::New(SvIV(sv));
else if (SvNOK(sv))
return Number::New(SvNV(sv));
else if (SvPOK(sv))
return String::New(SvPV_nolen(sv));
else if (SvROK(sv)) {
switch(SvTYPE(SvRV(sv))) {
case SVt_PVHV:
object = Object::New();
hash = (HV *)SvRV(sv);
keycount = hv_iterinit(hash);
printf("%d items in return\n",keycount);
while(keycount-- != 0) {
hash_entry = hv_iternext(hash);
sv_key = hv_iterkeysv(hash_entry);
sv_val = hv_iterval(hash, hash_entry);
object->Set(_convert_sv_to_v8value_unscoped(sv_key), _convert_sv_to_v8value_unscoped(sv_val));
}
return object;
case SVt_PVAV:
array = Array::New();
list = (AV *)SvRV(sv);
for(keycount = 0; keycount <= av_len(list); keycount++) {
sv_val = *av_fetch(list, keycount, NULL);
printf("=>>> %s\n", SvPV_nolen(sv_val));
array->Set(v8::Number::New(keycount), _convert_sv_to_v8value_unscoped(sv_val));
}
return array;
case SVt_IV:
case SVt_NV:
case SVt_PV:
return _convert_sv_to_v8value_unscoped((SV*)SvRV(sv));
}
}
return Undefined();
}
static Handle<Value>
_convert_sv_to_v8value(SV *sv)
{
HandleScope scope;
return _convert_sv_to_v8value_unscoped(sv);
}
static Handle<Value>
_perl_method(const Arguments &args)
{
dSP;
int count;
Handle<Value> result = Undefined();
ENTER;
SAVETMPS;
PUSHMARK(SP);
for (int i = 0; i < args.Length(); i ++) {
/* TODO think about refcounts */
XPUSHs(_convert_v8value_to_sv(args[i]));
}
PUTBACK;
SV *code = (SV *) External::Unwrap(args.Data());
count = call_sv(code, G_SCALAR);
SPAGAIN;
if (count >= 1) {
result = _convert_sv_to_v8value(POPs);
}
PUTBACK;
FREETMPS;
LEAVE;
return result;
}
void
V8Context::bind_function(const char *name,SV* code)
{
HandleScope scope;
TryCatch try_catch;
Context::Scope context_scope(context);
/*we will decrement the refcnt in the destructor*/
SvREFCNT_inc(code);
used.push_back(code);
context->Global()->Set(
String::New(name),
FunctionTemplate::New(_perl_method,
External::Wrap((void *) code))->GetFunction()
);
}
SV* V8Context::eval(const char* source) {
HandleScope handle_scope;
TryCatch try_catch;
Context::Scope context_scope(context);
Handle<Script> script = Script::Compile(String::New(source));
if (try_catch.HasCaught()) {
Handle<Value> exception = try_catch.Exception();
String::AsciiValue exception_str(exception);
sv_setpvn(ERRSV, *exception_str, exception_str.length());
return &PL_sv_undef;
} else {
Handle<Value> val = script->Run();
if (val.IsEmpty()) {
Handle<Value> exception = try_catch.Exception();
String::AsciiValue exception_str(exception);
sv_setpvn(ERRSV, *exception_str, exception_str.length());
return &PL_sv_undef;
} else {
sv_setsv(ERRSV,&PL_sv_undef);
return _convert_v8value_to_sv(val);
}
}
}
V8Context::~V8Context() {
for (int i=0;i<used.size();i++) {
SvREFCNT_dec(used[i]);
}
context.Dispose();
}