Creating custom JavaScript functions in V8? - javascript

I'm trying to embed an custom function to my project, that uses the V8 engine, and apparently I can't make it working. I've used code, that I've found, but it seems to be outdated, or I just do it wrong. My point is to include a custom javascript file. My current code (for testing) is this :
HandleScope handle_scope(isolate);
v8::Local<v8::ObjectTemplate> global = v8::ObjectTemplate::New(isolate);
global->Set(v8::String::NewFromUtf8(isolate, "test", v8::NewStringType::kNormal).ToLocalChecked(),
v8::FunctionTemplate::New(isolate, test));
Handle<Context> context = Context::New(isolate);
Persistent<Context> persistent_context(isolate, context);
Context::Scope context_scope(context);
const char* data = "test";
Handle<String> source = String::NewFromUtf8(isolate, data);
Handle<Script> script = Script::Compile(source);
if (!script.IsEmpty())
Handle<Value> result = script->Run();
Test Code (obviously just for testing):
void test(const v8::FunctionCallbackInfo<v8::Value>& args) {
MessageBoxA(NULL,"test", "", 0);
}
But the engine returns this error :
Uncaught ReferenceError: test is not defined
So my question is if I even do it correct, I would be able to make the including myself I hope, but I just can't get the function to get executed.

Here's some code from a project that works:
Isolate::Scope iscope( isolate_ );
HandleScope hs( isolate_ );
Local<Object> test = Object::New(isolate_);
test->Set(String::NewFromUtf8(isolate_, "javaScriptFunctionName"), Function::New(isolate_, CPP_FN_CALLBACK));
global_template->Set(String::NewFromUtf8(isolate_, "test"), test);
That will result in an object for window.test with a function called window.test.javaScriptFunctionName()

Related

Create a javascript function in such a way to proxy variable accesses to properties of an external object

This seems like a problem that requires some JS expertize that I'm apparently not in posses.
I'm writing a scripting module for an app. The scripting and the app are in Javascript.
It will be used by developers to provide scripting extensions to various modules that run on demand or when triggered by something.
I've got a request to alter the way it works in order to simplify the coding of the scripts.
And I'm kinda stuck since I don't know how to proxy local variables inside the script into an external object.
This is a sample of what works currently:
// this is part of the app developer API
function compileScript(userScript, argSignature) {
let _scriptFunc_ = null;
/* scripting API - there are available in scripts */
const print = function(message, window) {
const msg = document.createElement("span")
msg.innerText = message;
document.getElementById("output").append(msg)
document.getElementById("output").append(document.createElement("br"))
};
/* end Scripting API section */
try {
const scriptSource = `
(async () => {
try {
${userScript}
} catch (err) {
//_errEmit.fire(err)
}
})()
`; // wrap the execution in async so they can use await in their userScript
// argument signatures are defined by the module that "compiles" the script
// they call it with those and use them inside the userScript
eval("_scriptFunc_ = function(" + argSignature + ") {\n" + scriptSource + "\n}");
}
catch (err) {
//EvtScriptEmitEvalError.fire(err); // script "compilation" exception
return null;
}
return _scriptFunc_.bind(this);
}
// USAGE
// have some context with "variables" inside accessible by the script
// changes to this might be propagated elsewhere if it has property getters/setter
let scriptData = {
something: 10
};
// define the script
let userScript = `
for (let i = 0; i < count; i++) {
this.something++; // this changes scriptData
print(this.something)
}
`
// "compile" and run
const script = compileScript.call(scriptData, userScript, "count")
script(5) // output: 11,12,13,14,15
console.log(scriptData.something) // 15
<pre id="output">
</pre>
Note the usage of "this." inside the script to refer to scriptData members.
The request they have is to access the properties of the scriptData object inside the script by simply referring to its properties as if they were variables inside the script.
This is how they would want to write it (note there is no "this." before something):
let userScript = `
for (let i = 0; i < count; i++) {
something++; // this changes scriptData
print(something)
}
`
They are fine with possible name collisions between parameters and members of scriptData, it is developer work to set that up correctly.
My problem, tough, is that I don't have any idea how to modify "compileScript" in order to inject members of scriptData as plain variables inside the script is such a way that they proxy to the scriptData object.
It is easy to define a function in the scope of compileScript like "print", but I have no ideas on how to do this concept of "proxy variables".
"with" is not available in strict mode which the app runs in.
JS Proxy class does not seem useful.
Deconstructing scriptData into variables can be done but those variables are no longer going to the scriptData object, they are local.
Defining property getters/setters is available only for objects, not for the compileScript function...
I cannot modify the scriptData object, the user passes it as is. I can only tweak the generation of the script so that it behaves as required.
It should also work in a web worker (so no global scope / window), since a script could be triggered at the completion of a web worker.
Any ideas?
You're looking for the with statement. It's the only way to make variable assignments become interceptable as property assignments on your scriptData object (apart from going the full way to transpiling the script code with something like babel).
If writable variables are not necessary, you could also use this technique to inject values into the scope of a function. I would recommend to use it anyway, instead of eval.
/* scripting API - there are available in scripts */
const api = {
print(message, window) {
const msg = document.createElement("p");
msg.textContent = message;
document.getElementById("output").append(msg);
},
};
/* end Scripting API section */
// this is part of the app developer API
function compileScript(context, userScript, parameters) {
const scriptSource = `
const { ${Object.keys(api).join(', ')} } = api;
with (this) {
return async (${parameters.join(', ')}) => {
"use strict";
try {
${userScript}
} catch (err) {
//_errEmit.fire(err)
}
};
}
`;
try {
return new Function('api', scriptSource).call(context, api);
} catch (err) {
console.warn(err); // script "compilation" exception
return null;
}
}
let scriptData = {
something: 10
};
// define the script
let userScript = `
for (let i = 0; i < count; i++) {
something++; // this changes scriptData
print(something)
}
`;
// "compile" and run
const script = compileScript(scriptData, userScript, ["count"])
script(5) // output: 11,12,13,14,15
console.log(scriptData.something) // 15
<pre id="output">
</pre>

How should I map tvOS AppDelegate's applicationWillEnterForeground to a javascript function?

I'm following Apple's Sample project for Playing Media in a Client-Server App.
func executeRemoteMethod(_ methodName: String, completion: #escaping (Bool) -> Void) {
appController?.evaluate(inJavaScriptContext: { (context: JSContext) in
let appObject : JSValue = context.objectForKeyedSubscript("App")
if appObject.hasProperty(methodName) {
appObject.invokeMethod(methodName, withArguments: [])
}
}, completion: completion)
}
The method is getting called during Application lifecycle events like this:
func applicationWillEnterForeground(_ application: UIApplication) {
executeRemoteMethod("onWillEnterForeground", completion: { (success: Bool) in
// ...
})
}
I'm wondering how it's working. Is this so that the native iOS codebase can communicate the lifecycle event to the Javascript code? When I put a breakpoint I see executeRemoteMethod function getting called. But I don't think it's actually doing anything. How can I map it to a js function? Do I have to create a new js file or just create a new function in my application.js file?
Thanks to reading this tutorial. I learned:
In the block beginning, we first get a reference to our
JavaScript “App” object. Then, we test the existence of a property
with the method name that was passed to us. If it exists, we
invoke the method. Although the current implementation
doesn’t pass arguments to the method, we could modify it to do that as
well if needed. Finally, the completion block that was passed to us is
executed when the execution of the JavaScript method has been
completed That similar to how the function is already written
inside application.js:
In its tutorial the application.js looks like this:
App.onLaunch = function(options) {
var alert = createAlert("Hello World!", "Welcome to tvOS");
navigationDocument.pushDocument(alert);
}
App.onWillResignActive = function() {
}
App.onDidEnterBackground = function() {
}
App.onWillEnterForeground = function() {
}
App.onDidBecomeActive = function() {
}
App.onWillTerminate = function() {
}
var createAlert = function(title, description) {
var alertString = `<?xml version="1.0" encoding="UTF-8" ?>
<document>
<alertTemplate>
<title>${title}</title>
<description>${description}</description>
</alertTemplate>
</document>`
var parser = new DOMParser();
var alertDoc = parser.parseFromString(alertString, "application/xml");
return alertDoc
}
As a result I just have to add the following inside application.js then it will get called upon AppDelegates's WillEnterForeGround callback.
App.onWillEnterForeground = function(options) {
console.log("something")
}

Calling a JavaScript function from C

Is it possible to call a JavaScript function (written over, say, node.js) from C?
(There are plenty of tutorials on calling C/C++ from node.js. But not the other way around.)
I've been working on the same problem recently, and found a tractable solution using QuickJS and esbuild. It's not the prettiest, but it works quite well!
To call JS from C, the general process is:
Get QuickJS and esbuild
esbuild your desired library/script into an ESM format using CommonJS. This will output one big script with all needed dependencies included.
output=/path/to/esbuild/output
npx esbuild --bundle /path/to/original/node-library --format=esm --outfile="$output"
Patch the output of esbuild to make it compatible with QuickJS:
sed -i 's/Function(\"return this\")()/globalThis/g' $output
sed -i 's#export default#//export default#g' $output
Load the script text into an object file using your linker:
ld -r -b binary my_obj_file.o $output
Depending on your compiler, this will automatically create 3 symbols in the object file:
- name_start
- name_end
- name_size
name in this context is automatically generated from the filename you provided as the last argument to ld. It replaces all non-alphanumeric characters with underscores, so my-cool-lib.mjs gives a name of my_cool_lib_mjs.
You can use ld_magic.h (here) for a cross platform way to access this data from your C code.
After the object file is generated, you should see the patched esbuild output if you run strings:
% strings foo_lib_js.o
var __getOwnPropNames = Object.getOwnPropertyNames;
var __commonJS = (cb, mod) => function __require() {
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
// src/foo.js
var require_foo = __commonJS({
"src/foo.js"(exports, module) {
function foo(bar, baz) {
return bar + baz;
}
module.exports = foo;
//export default require_foo();
_binary_my_foo_lib_mjs_end
_binary_my_foo_lib_mjs_start
_binary_my_foo_lib_mjs_size
.symtab
.strtab
.shstrtab
.data
Link the object file into your binary:
gcc my_obj_file.o <other object files> -o my_static_binary
You can also link the object file into a shared library, for use in other applications:
gcc -shared -o my_shared_library.so my_obj_file.o <other object files>
The source of this repo shows how to do this with a CMake project.
How to actually call the JS functions
Let's say you have a NodeJS library with a function you want to call from C:
// Let's say this lives in foo.js, and esbuild output goes in my-lib-foo.mjs
function foo(bar, baz) {
return bar + baz
}
module.exports = foo;
esbuild creates a series of require_thing() functions, which can be used to get the underlying thing(param1, param2...) function object which you can make calls with.
A simple loader in QuickJS looks like this:
JSValue commonjs_module_data_to_function(JSContext *ctx, const uint8_t *data, size_t data_length, const char *function_name)
{
JSValue result = JS_UNDEFINED;
char * module_function_name = NULL;
// Make sure you properly free all JSValues created from this procedure
if(data == NULL) {
goto done;
}
/**
* To pull the script objects, including require_thing() etc, into global scope,
* load the patched NodeJS script from the object file embedded in the binary
*/
result = JS_Eval(ctx, data, data_length, "<embed>", JS_EVAL_TYPE_GLOBAL);
if(JS_IsException(result)) {
printf("failed to parse module function '%s'\n", function_name);
goto cleanup_fail;
}
JSValue global = JS_GetGlobalObject(ctx);
/**
* Automatically create the require_thing() function name
*/
asprintf(&module_function_name, "require_%s", function_name);
JSValue module = JS_GetPropertyStr(ctx, global, module_function_name);
if(JS_IsException(module)) {
printf("failed to find %s module function\n", function_name);
goto cleanup_fail;
}
result = JS_Call(ctx, module, global, 0, NULL);
if(JS_IsException(result)) {
goto cleanup_fail;
}
/* don't lose the object we've built by passing over failure case */
goto done;
cleanup_fail:
/* nothing to do, cleanup context elsewhere */
result = JS_UNDEFINED;
done:
free(module_function_name);
return result;
}
If you wanted to, for example, get the foo(bar, baz) function mentioned above, you would write a function like this:
#include <stdio.h>
#include <inttypes.h>
// A simple helper for getting a JSContext
JSContext * easy_context(void)
{
JSRuntime *runtime = JS_NewRuntime();
if(runtime == NULL) {
puts("unable to create JS Runtime");
goto cleanup_content_fail;
}
JSContext *ctx = JS_NewContext(runtime);
if(ctx == NULL) {
puts("unable to create JS context");
goto cleanup_runtime_fail;
}
return ctx;
cleanup_runtime_fail:
free(runtime);
cleanup_content_fail:
return NULL;
}
int call_foo(int bar, int baz)
{
JSContext *ctx = easy_context();
JSValue global = JS_GetGlobalObject(ctx);
/**
* esbuild output was to my-foo-lib.mjs, so symbols will be named with my_foo_lib_mjs
*/
JSValue foo_fn = commonjs_module_data_to_function(
ctx
, _binary_my_foo_lib_mjs_start // gcc/Linux-specific naming
, _binary_my_foo_lib_mjs_size
, "foo"
);
/**
* To create more complex objects as arguments, use
* JS_ParseJSON(ctx, json_str, strlen(json_str), "<input>");
* You can also pass callback functions by loading them just like we loaded foo_fn
*/
JSValue args[] = {
JS_NewInt32(ctx, bar),
JS_NewInt32(ctx, baz)
};
JSValue js_result = JS_Call(ctx
, foo_fn
, global
, sizeof(args)/sizeof(*args)
, args
);
int32_t c_result = -1;
JS_ToInt32(ctx, &c_result, js_result);
return c_result;
}
Check out a minimal example project using CMake here: https://github.com/ijustlovemath/jescx/blob/master/README.md
You could use Emscripten.
Emscripten is an LLVM-to-JavaScript compiler.
It takes LLVM bitcode - which can be generated from C/C++, using llvm-gcc (DragonEgg) or clang, or any other language that can be converted into LLVM - and compiles that into JavaScript, which can be run on the web (or anywhere else JavaScript can run).
Also see this: How to execute Javascript function in C++

How to call a C++ method from javascript

I have a C++ method (which role is killing some processes). She needs 2 parameters : a hostname and a port.
On the other hand, I am developing a web-application (using Nodejs and AngularJS), running on Google Chrome.
When I click on a button through the browser, I would like to be able to call the C++ function, through my app.js file.
I haven't found how to "bind" javascript with C++.
EDIT : I think this link could be very useful
How can I use a C++ library from node.js?
You can use Google's V8. V8 is Google's open source JavaScript engine.
V8 can run standalone, or can be embedded into any C++ application.
http://code.google.com/p/v8/
Following example from github demonstrates, binding a C++ class with Google V8.
v8_wrap_class.cpp - Author is nicholas
/*
* v8_wrap_class.cpp
*
* Created on: 14/01/2013
* Author: nicholas
* License: public domain
*/
#include <v8.h>
#include <cstdio>
#include <string>
#include <stdexcept>
#include <memory>
using namespace v8;
/*
var Simple = function(v)
{
this.value = v;
}
Simple.prototype.func = function()
{
alert(this.value);
}
var obj = new Simple(4);
obj.func();
*/
struct Simple
{
double value;
Simple(double v)
: value(v)
{
fprintf(stderr, "Simple::ctor\n");
}
void func()
{
fprintf(stderr, "Simple::func(%f)\n", value);
}
~Simple()
{
fprintf(stderr, "Simple::dtor\n");
}
};
namespace js
{
/*
* Retrieve the c++ object pointer from the js object
*/
template <typename T>
T* unwrap(const Arguments& args)
{
auto self = args.Holder();
auto wrap = Local<External>::Cast(self->GetInternalField(0));
return static_cast<T*>(wrap->Value());
}
/*
* Construct a new c++ object and wrap it in a js object
*/
template <typename T, typename... Args>
Persistent<Object> make_object(Handle<Object> object, Args&&... args)
{
auto x = new T(std::forward<Args>(args)...);
auto obj = Persistent<Object>::New(object);
obj->SetInternalField(0, External::New(x));
obj.MakeWeak(x, [](Persistent<Value> obj, void* data)
{
auto x = static_cast<T*>(data);
delete x;
obj.Dispose();
obj.Clear();
});
return obj;
}
}
void bind_Simple(Local<Object> global)
{
// Name the class in js
auto name = String::NewSymbol("Simple");
auto tpl = FunctionTemplate::New([&](const Arguments& args) -> Handle<Value>
{
if (!args.IsConstructCall())
return ThrowException(String::New("Cannot call constructor as function"));
HandleScope scope;
// Read and pass constructor arguments
js::make_object<Simple>(args.This(), args[0]->NumberValue());
return args.This();
});
tpl->SetClassName(name);
tpl->InstanceTemplate()->SetInternalFieldCount(1);
auto prototype = tpl->PrototypeTemplate();
// Add object properties to the prototype
// Methods, Properties, etc.
prototype->Set(String::New("func"), FunctionTemplate::New([](const Arguments& args) -> Handle<Value>
{
auto s = js::unwrap<Simple>(args);
s->func();
return {};
})->GetFunction());
auto constructor = Persistent<Function>::New(tpl->GetFunction());
global->Set(name, constructor);
}
int main()
{
std::string js_source = R"(
for(var i = 0; i < 1000; ++i)
{
var s = new Simple(4);
s.value = 5;
s.func();
}
)";
/*
* This code is mostly uninteresting.
* Just run the vm with the script provided.
*/
{
HandleScope handle_scope;
Handle<ObjectTemplate> global_template = ObjectTemplate::New();
Persistent<Context> context = Context::New(0, global_template);
Context::Scope context_scope(context);
auto global = context->Global();
// Wrap the class and bind to the global scope.
bind_Simple(global);
{
HandleScope handle_scope;
TryCatch trycatch;
Local<String> source = String::New(js_source.c_str(), js_source.size());
Local<Script> script = Script::Compile(source);
if (script.IsEmpty())
{
Handle<Value> exception = trycatch.Exception();
String::AsciiValue exception_str(exception);
throw std::runtime_error(*exception_str);
}
Local<Value> result = script->Run();
if (result.IsEmpty())
{
Local<Value> exception = trycatch.Exception();
String::AsciiValue exception_str(exception);
throw std::runtime_error(*exception_str);
}
}
context.Dispose();
context.Clear();
}
// Run the GC until there is nothing to reclaim.
while (!V8::IdleNotification())
;
return 0;
}
This answer gives four appraoches to using C++ in javascript. The methods shown try to keep the original C++ in a standard and simple C++ implementation.
Two methods using WASM and two using SWIG and JRPC are used to give examples and ideas on executing C++ in javascript. In detail, this answer gives one example for executing in the browser and one for executing in nodejs using WASM. The end of this answer also lists two other ways to execute C++ in javascript, one of which calls nodejs from the browser which is slightly more complex but possible using jrpc-oo.
If you want to execute in the browser or nodejs, then you can compile your C++ to WASM and load that module in the browser or nodejs, executing the C++ there. This WASM repo exemplifies how to do that. I will expand the key code in the WASM repo here.
Create some C++ code and declare your WASM binding, for example (from the files include/Test.H and src/Test.C) :
class Test {
public:
void sayHello(){
printf("Hi, my name is test\n");
}
};
#include <emscripten/bind.h>
EMSCRIPTEN_BINDINGS(Test_ex) {
emscripten::class_<Test>("Test")
.function("sayHello", &Test::sayHello)
;
}
Compile that down and you can now run that in nodejs (from the file nodejs/WASMTestNode.js) :
libWASM = require('./libwasmNode.js');
libWASM().then((mod)=>{
libWASM = mod;
let test = new libWASM.Test;
test.sayHello();
});
In the browser you can use the WASM code as well. To do that firs import the WASM library (from the file webcomponent/libWASM.js) :
import modProm from './libwasm.js';
The create your webcomponent and compile your WASM then execute the C++ method Test::sayHello (from the file webcomponent/libWASM.js) :
import { LitElement } from 'lit';
export class LibWASM extends LitElement {
constructor() {
super();
modProm().then((mod)=>{
this.libWASM = mod; // for rendered wasm that delay
this.WASMReady();
})
}
WASMReady(){
console.log('LibWASM.libWASM module compiled and ready to go.')
let test = new this.libWASM.Test;
test.sayHello();
}
}
This code example is implemented in the reference repo.
Alternatively a third way to do this is to only use C++, SWIG and nodejs from this reference repo.
If you want to execute nodejs from the browser or use a a different method of integrating C++ into nodejs, you can also look at this SWIG and jrpc-oo reference for doing the same thing, but not only in nodejs, also from the browser calling nodejs.
There are also other ways to execute C++ form javascript, however the methods demonstrated in this answer are reasonably straightforward and either rely on WASM binding or SWIG abstraction which leaves your original code as standard C++.

Passing JS function to Emscripten-generated code

I have a piece of C++ code converted to JavaScript via Emscripten. I would like the converted C++ code to call back to the JavaScript code that calls it. Something like:
JavaScript:
function callback(message) {
alert(message);
}
ccall("my_c_function", ..., callback);
C++:
void my_c_function(whatever_type_t *callback) {
callback("Hello World!");
}
Is this possible somehow?
I believe the accepted answer is a bit outdated.
Please refer to this bullet point in the "Interacting with code" emscripten tutorial.
E.g.
C:
void invoke_function_pointer(void(*f)(void)) {
(*f)();
}
JS:
var pointer = Runtime.addFunction(function() {
console.log('I was called from C world!');
});
Module.ccall('invoke_function_pointer', 'number', ['number'], [pointer]);
Runtime.removeFunction(pointer);
This way the C-code does not need to be aware of that it is transpiled to JS and any bridges required can purely be controlled from JS.
(code hacked into message composer; may contain errors)
There is a new way of achieving your requirement which is via embind.
Consider the following piece of C++ code.
#include <emscripten/bind.h>
using namespace emscripten;
void cbTest(emscripten::val cb)
{
cb();
}
EMSCRIPTEN_BINDINGS(my_module) {
function("cbTest", &cbTest);
}
The cbTest C++ function takes in a emscripten::val. This can be an object of any kind. For us this is a function object.
This is how you will call it from JS
var cbFunc = function() {
console.log("Hi, this is a cb");
}
Module.cbTest(cbFunc);
P.S This api is still under construction.
A thing that is frequently done in Emscripten is to map strong types to simple ones.
JS:
function callback(message) {
alert(message);
}
var func_map = {
0: callback
};
// C/C++ functions get a _ prefix added
function _invoke_callback(callback_id, text_ptr) {
func_map[callback_id](Pointer_stringify(text_ptr));
}
ccall("my_c_function", ..., 0);
C++:
// In C/C++ you only need to declare the func signature and
// make sure C is used to prevent name mangling
extern "C" void invoke_callback(int callback_id, const char* text);
void my_c_function(int callback_id) {
invoke_callback( callback_id, "Hello World!" );
}
And of course, you can add some glue code, so this gets very seamless.
I needed to write something very similar to what is described in the question. My code ended up looking like this:
C:
void call(void (*back)(char*)){
back("Hello!");
}
JS:
function back(text){
alert(Pointer_stringify(text));
}
var pointer = Runtime.addFunction(back);
var call = Module.cwrap('call', 'void', ['pointer']);
call(pointer);
Runtime.removeFunction(pointer);
Note that the pointer returned to the callback has to be dereferenced with Pointer_stringify.
You can find example code like this on GitHub.
Here's what I have gathered from several posts and by looking at Emscripten bundled code:
In C++:
#include <iostream>
#include <functional>
extern "C" {
void registerCallback(void(*back)(const char*));
void triggerCallback(char* message); // for invoking it from JS, just for this example
}
// global
std::function<void(const char*)> gCallback;
void registerCallback(void(*back)(const char*)){
gCallback = back;
}
void triggerCallback(char* message){
if (gCallback) {
gCallback(message);
} else {
std::cerr << "Cannot pass '"<< message <<"' to undefined callback\n";
}
}
An important thing, which was missing in other posts, is to compile C++ with RESERVED_FUNCTION_POINTERS=... flag, e.g.:
em++ -std=c++11 -s RESERVED_FUNCTION_POINTERS=20 source.cpp -s EXPORTED_FUNCTIONS="['_registerCallback','_triggerCallback']" -o try.html
After loading try.html into a browser, you can execute the following JS code in its console:
// Register a callback function
function callback(text){ alert("In JS: "+Pointer_stringify(text)); }
var cb = Runtime.addFunction(callback);
_registerCallback(cb);
// Invoke it with some "C string"
var jsStr = "XOXOXO";
var cStr = allocate(intArrayFromString(jsStr), 'i8', ALLOC_NORMAL)
_triggerCallback(cStr);
// Free Emscripten heap and release the function pointer
_free(cStr);
Runtime.removeFunction(cb);
You should see an alert with "In JS: XOXOXO".

Categories

Resources