#include <stdarg.h>
#include <stdio.h>
#include <v8.h>
#include "pl_util.h"
#include "pl_console.h"
#define NEED_newRV_noinc_GLOBAL
#include "ppport.h"
#define CONSOLE_FLUSH 0x01
#define CONSOLE_TARGET_STDOUT 0x10
#define CONSOLE_TARGET_STDERR 0x20
static
void
save_msg(pTHX_ V8Context* ctx,
const
char
* target, SV* message)
{
STRLEN tlen =
strlen
(target);
AV* data = 0;
int
top = 0;
SV* pvalue = 0;
SV** found = hv_fetch(ctx->msgs, target, tlen, 0);
if
(found) {
SV* ref = SvRV(*found);
if
(SvTYPE(ref) != SVt_PVAV) {
return
;
}
data = (AV*) ref;
top = av_top_index(data);
}
else
{
SV* ref = 0;
data = newAV();
ref = newRV_noinc((SV*) data);
if
(hv_store(ctx->msgs, target, tlen, ref, 0)) {
SvREFCNT_inc(ref);
}
top = -1;
}
pvalue = sv_2mortal(message);
if
(av_store(data, ++top, pvalue)) {
SvREFCNT_inc(pvalue);
}
else
{
croak(
"Could not store message in target %*.*s\n"
, (
int
) tlen, (
int
) tlen, target);
}
}
static
int
console_output_line(pTHX_ V8Context* ctx, SV* message, unsigned
int
flags)
{
STRLEN mlen = 0;
char
* mstr = SvPV(message, mlen);
if
(ctx->flags & V8_OPT_FLAG_SAVE_MESSAGES) {
const
char
* target = (flags & CONSOLE_TARGET_STDERR) ?
"stderr"
:
"stdout"
;
save_msg(aTHX_ ctx, target, message);
}
else
{
PerlIO* fp = (flags & CONSOLE_TARGET_STDERR) ? PerlIO_stderr() : PerlIO_stdout();
PerlIO_printf(fp,
"%s\n"
, mstr);
if
(flags & CONSOLE_FLUSH) {
PerlIO_flush(fp);
}
}
return
mlen;
}
static
int
console_output(
const
FunctionCallbackInfo<Value>& args, unsigned
int
flags,
const
char
* preamble = 0,
bool
stack =
false
,
int
start = 0)
{
dTHX;
Isolate* isolate = args.GetIsolate();
HandleScope handle_scope(isolate);
Local<External> v8_val = Local<External>::Cast(args.Data());
V8Context* ctx = (V8Context*) v8_val->Value();
Local<Context> context = Local<Context>::New(isolate, *ctx->persistent_context);
Context::Scope context_scope(context);
SV* message = newSVpvs(
""
);
bool
separate =
false
;
if
(preamble) {
Perl_sv_catpv(aTHX_ message, preamble);
Perl_sv_catpvf(aTHX_ message,
":"
);
separate =
true
;
}
Local<Object> global = context->Global();
Local<Name> to_str_nam = String::NewFromUtf8(isolate,
"JSON_stringify_with_cycles"
, NewStringType::kNormal).ToLocalChecked();
Local<Value> to_str_val = global->Get(to_str_nam);
if
(to_str_val->IsFunction()) {
Local<Function> to_str_fun = Local<Function>::Cast(to_str_val);
Local<Value> sargs[1];
for
(
int
j = start; j < args.Length(); j++) {
if
(separate) {
Perl_sv_catpvf(aTHX_ message,
" "
);
}
separate =
true
;
if
(!args[j]->IsObject()) {
String::Utf8Value str(isolate, args[j]);
Perl_sv_catpvf(aTHX_ message,
"%s"
, *str);
continue
;
}
sargs[0] = args[j];
Local<Value> ret;
if
(!to_str_fun->Call(context, global, 1, sargs).ToLocal(&ret)) {
continue
;
}
Local<String> json = Local<String>::Cast(ret);
String::Utf8Value str(isolate, json);
Perl_sv_catpvf(aTHX_ message,
"%s"
, *str);
}
}
if
(stack) {
#if 0
v8_inspector::V8Inspector* inspector =
new
v8_inspector::V8InspectorImpl::V8InspectorImpl();
inspector->captureStackTrace();
#endif
}
return
console_output_line(aTHX_ ctx, message, flags);
}
static
void
console_assert(
const
FunctionCallbackInfo<Value>& args)
{
if
(args.Length() < 1) {
return
;
}
bool
silent = args[0]->BooleanValue();
if
(silent) {
return
;
}
console_output(args, CONSOLE_TARGET_STDOUT | CONSOLE_FLUSH,
"AssertionError"
,
true
, 1);
}
static
void
console_log(
const
FunctionCallbackInfo<Value>& args)
{
console_output(args, CONSOLE_TARGET_STDOUT | CONSOLE_FLUSH);
}
static
void
console_debug(
const
FunctionCallbackInfo<Value>& args)
{
console_output(args, CONSOLE_TARGET_STDOUT | CONSOLE_FLUSH);
}
static
void
console_trace(
const
FunctionCallbackInfo<Value>& args)
{
console_output(args, CONSOLE_TARGET_STDOUT | CONSOLE_FLUSH,
"Trace"
,
true
);
}
static
void
console_info(
const
FunctionCallbackInfo<Value>& args)
{
console_output(args, CONSOLE_TARGET_STDOUT | CONSOLE_FLUSH);
}
static
void
console_warn(
const
FunctionCallbackInfo<Value>& args)
{
console_output(args, CONSOLE_TARGET_STDERR | CONSOLE_FLUSH);
}
static
void
console_error(
const
FunctionCallbackInfo<Value>& args)
{
console_output(args, CONSOLE_TARGET_STDERR | CONSOLE_FLUSH,
"Error"
,
true
);
}
static
void
console_exception(
const
FunctionCallbackInfo<Value>& args)
{
console_output(args, CONSOLE_TARGET_STDERR | CONSOLE_FLUSH,
"Error"
,
true
);
}
static
void
console_dir(
const
FunctionCallbackInfo<Value>& args)
{
console_output(args, CONSOLE_TARGET_STDERR | CONSOLE_FLUSH);
}
int
pl_register_console_functions(V8Context* ctx)
{
typedef
void
(*Handler)(
const
FunctionCallbackInfo<Value>& args);
static
struct
Data {
const
char
* name;
Handler func;
} data[] = {
{
"console.assert"
, console_assert },
{
"console.log"
, console_log },
{
"console.debug"
, console_debug },
{
"console.trace"
, console_trace },
{
"console.info"
, console_info },
{
"console.warn"
, console_warn },
{
"console.error"
, console_error },
{
"console.exception"
, console_exception },
{
"console.dir"
, console_dir },
};
HandleScope handle_scope(ctx->isolate);
Local<Context> context = Local<Context>::New(ctx->isolate, *ctx->persistent_context);
Context::Scope context_scope(context);
Local<Value> v8_ctx = External::New(ctx->isolate, ctx);
int
n =
sizeof
(data) /
sizeof
(data[0]);
for
(
int
j = 0; j < n; ++j) {
Local<Object> object;
Local<Value> slot;
bool
found = find_parent(ctx, data[j].name, context, object, slot,
true
);
if
(!found) {
pl_show_error(ctx,
"could not create parent for %s"
, data[j].name);
continue
;
}
Local<FunctionTemplate> ft = FunctionTemplate::New(ctx->isolate, data[j].func, v8_ctx);
Local<Function> v8_func = ft->GetFunction();
object->Set(slot, v8_func);
}
return
n;
}
int
pl_show_error(V8Context* ctx,
const
char
* fmt, ...)
{
dTHX;
SV* message = newSVpvs(
""
);
va_list
ap;
va_start
(ap, fmt);
Perl_sv_vcatpvf(aTHX_ message, fmt, &ap);
va_end
(ap);
return
console_output_line(aTHX_ ctx, message, CONSOLE_TARGET_STDERR | CONSOLE_FLUSH);
}