4

Hi so I am looking at duktape, but I can't seem to find a simple example which does something like:

  • pre compile some js.
  • pass that js some input e.g. strings and numbers and run it.
  • get the result of that js.

It seems the examples go from here is how to evaluate js to here is how to call C functions from js, maybe I missed it.

Well it seems after bashing over the API doc and trial and error I eventually got something like:

duk_context *ctx = duk_create_heap_default();

duk_eval_string(ctx, "(function helloWorld(a,b) { return a.includes(b); })");    
duk_dump_function(ctx);

int i;
for(i = 0; i < 10; i++) {
  duk_dup_top(ctx); // I don't know why but it seems needed
  duk_load_function(ctx);  /* [ ... bytecode ] -> [ ... function ] */
  duk_push_string(ctx, "the");
  duk_push_string(ctx, "th");
  duk_call(ctx, 2);
  
  duk_bool_t res = duk_get_boolean(ctx, -1);
  if(res) {
    printf("You got it!\n");
  } else {
    printf("yeah nah!\n");
  }
  duk_pop(ctx);
}

Although I notice that if the JS has an error I get a seg fault, maybe I am meant to check something?

Also is this the correct way to cache the JS?

Andrew
  • 5,839
  • 1
  • 51
  • 72
Luke
  • 884
  • 8
  • 21

1 Answers1

4

OK, Here's a cobbled together snippet based on code I've got working, which should do something similar to your example

bool compileJS(duk_context *ctx, const char* programBody)
{
  bool success = false;

  // Compile the JS into bytecode
  if (duk_pcompile_string(ctx, 0, programBody) != 0)
  {
    // Error in program code

    printf("Compile failed\n");
    printf("%s\n", duk_safe_to_string(ctx, -1));
  }
  else
  {
    // Actually evaluate it - this will push the compiled code into the global scope
    duk_pcall(ctx, 0);

    success = true;

  }
  duk_pop(ctx);

  return success;
}

duk_bool_t runJSFunction(duk_context *ctx, const char* funcName, const char* arga, const char* argb)
{
  duk_bool_t returnVal;

  // Get a reference to the named JS function
  if (duk_get_global_string(ctx, funcName))
  {
    // Function found, push the args

    duk_push_string(ctx, arga);
    duk_push_string(ctx, argb);

    // Use pcall - this lets you catch and handle any errors
    if (duk_pcall(ctx, 2) != DUK_EXEC_SUCCESS)
    {
      // An error occurred - display a stack trace
      duk_get_prop_string(ctx, -1, "stack");
      printf(duk_safe_to_string(ctx, -1));
    }
    else
    {
      // function executed successfully - get result
      returnVal = duk_get_boolean(ctx, -1);
    }
  }
  else
  {
    printf("JS function not found!\n");
    returnVal = false;
  }

  duk_pop(ctx); // pop result

  return returnVal;
}

void testJS()
{
  duk_context *ctx = duk_create_heap_default();

  const char* programBody = "function helloWorld(a,b) { return a.includes(b); }";

  if (compileJS(ctx, programBody))
  {
    for (int i = 0; i < 10; ++i)
    {
      bool ret = runJSFunction(ctx, "helloWorld", "the", "th");
      if (ret)
      {
        printf("You got it!\n");
      }
      else
      {
        printf("yeah nah!\n");
      }
    }
  }
}

I've separated this into three functions so you can see how the initialisation and compilation (and thus caching) of the function is handled separately from the JS execution. I've also added rudimentary error handling, that should display errors if your JS code fails to compile or run.

Andrew
  • 5,839
  • 1
  • 51
  • 72
benjymous
  • 2,102
  • 1
  • 14
  • 21