From f9f2403de58c30dfcec5621961803a1c26da9ff5 Mon Sep 17 00:00:00 2001 From: Jeroen Ooms Date: Fri, 29 Jan 2016 14:48:46 +0100 Subject: [PATCH] Render vignettes --- inst/doc/npm.R | 2 +- inst/doc/npm.html | 75 ++++++++----- inst/doc/v8_intro.R | 24 +++-- inst/doc/v8_intro.Rmd | 58 ++++++++-- inst/doc/v8_intro.html | 238 +++++++++++++++++++++++------------------ 5 files changed, 246 insertions(+), 151 deletions(-) diff --git a/inst/doc/npm.R b/inst/doc/npm.R index 385b71e..5f73939 100644 --- a/inst/doc/npm.R +++ b/inst/doc/npm.R @@ -1,4 +1,4 @@ -## ----, echo = FALSE, message = FALSE------------------------------------- +## ---- echo = FALSE, message = FALSE-------------------------------------- knitr::opts_chunk$set(comment = "") library(V8) diff --git a/inst/doc/npm.html b/inst/doc/npm.html index aef1407..1bf8084 100644 --- a/inst/doc/npm.html +++ b/inst/doc/npm.html @@ -10,7 +10,7 @@ - + Using NPM packages in V8 @@ -18,23 +18,41 @@ - + @@ -54,7 +72,7 @@
@@ -114,9 +132,9 @@

What is V8 (not)

Can we use NPM?

Yes! But not all packages will work. Most libraries in npm are primarely written for Node or the browser. Obviously, anything that requires internet access, graphics or files is not going to work in plain V8. But there is quite a lot of stuff that does work.

Some general purpose libraries like underscore or crossfilter will work natively in V8:

-
ct <- new_context()
+
ct <- new_context()
 ct$source(system.file("js/underscore.js", package="V8"))
-ct$call("_.filter", mtcars, JS("function(x){return x.mpg < 15}"))
+ct$call("_.filter", mtcars, JS("function(x){return x.mpg < 15}"))
                     mpg cyl disp  hp drat    wt  qsec vs am gear carb
 Duster 360          14.3   8  360 245 3.21 3.570 15.84  0  0    3    4
 Cadillac Fleetwood  10.4   8  472 205 2.93 5.250 17.98  0  0    3    4
@@ -128,23 +146,24 @@ 

Can we use NPM?

Browserify to the rescue

-browserify logo

browserify logo

+browserify logo +

browserify logo

Browserify is a brilliant tool to bundle an npm package with all of its dependencies into a single js file that does not require disk access. It is mainly designed to make npm packages suitable for use on a webpage (duh) but it is useful with embedded V8 as well. To install it run:

-
npm install -g browserify
+
npm install -g browserify

Example: beautify-js

Beautify-js is a simple npm package to fix linebreaks and indentation in JavaScript, HTML or CSS code. To bundle it up, run these three lines in a shell:

-
npm install js-beautify
+
npm install js-beautify
 echo "global.beautify = require('js-beautify');" > in.js
-browserify in.js -o bundle.js
+browserify in.js -o bundle.js

The first line will install js-beautify in a the current dir under node_modules. The second line creates the input file for browserify. In this case it consists of only one line that imports the js-beautify library and exports it to the global environment. The third line runs browserify and saves the output to a new file bundle.js.

We now have a file that we can load in V8. Assuming you ran the above commands in your Desktop directory:

-
ct <- new_context()
-ct$source("~/Desktop/bundle.js")
+
ct <- new_context()
+ct$source("~/Desktop/bundle.js")

Let’s see whats in our global environment now:

-
ct$get(JS('Object.keys(global)'))
+
ct$get(JS('Object.keys(global)'))
 [1] "console"      "print"        "global"       "ArrayBuffer" 
  [5] "Int8Array"    "Uint8Array"   "Int16Array"   "Uint16Array" 
  [9] "Int32Array"   "Uint32Array"  "Float32Array" "Float64Array"
@@ -154,17 +173,17 @@ 

Example: beautify-js

Lets beautify stuff

To beautify JavaScript we need to use the js_beautify function. See the package homepage for a full list of options.

-
test <- "(function(x,y){x = x || 1; y = y || 1; return y * x;})(4, 9)"
+
test <- "(function(x,y){x = x || 1; y = y || 1; return y * x;})(4, 9)"
 pretty_test <- ct$call("beautify.js_beautify", test, list(indent_size = 2))
-cat(pretty_test)
+cat(pretty_test)
(function(x, y) {
   x = x || 1;
   y = y || 1;
   return y * x;
 })(4, 9)

The package also includes functions to beautify css and html:

-
html <- "<ul><li>one</li><li>two</li><li>three</li></ul>"
-cat(ct$call("beautify.html_beautify", html))
+
html <- "<ul><li>one</li><li>two</li><li>three</li></ul>"
+cat(ct$call("beautify.html_beautify", html))
<ul>
     <li>one</li>
     <li>two</li>
diff --git a/inst/doc/v8_intro.R b/inst/doc/v8_intro.R
index cbc07b7..7176476 100644
--- a/inst/doc/v8_intro.R
+++ b/inst/doc/v8_intro.R
@@ -1,10 +1,10 @@
-## ----, echo = FALSE, message = FALSE-------------------------------------
+## ---- echo = FALSE, message = FALSE--------------------------------------
 knitr::opts_chunk$set(comment = "")
 library(V8)
 
 ## ------------------------------------------------------------------------
 # Create a new context
-ct <- new_context();
+ct <- v8()
 
 # Evaluate some code
 ct$eval("var foo = 123")
@@ -18,7 +18,7 @@ cat(ct$eval("JSON.stringify({x:Math.random()})"))
 # Simple closure
 ct$eval("(function(x){return x+1;})(123)")
 
-## ----, eval=FALSE--------------------------------------------------------
+## ---- eval=FALSE---------------------------------------------------------
 #  ct$source(system.file("js/underscore.js", package="V8"))
 #  ct$source("https://cdnjs.cloudflare.com/ajax/libs/crossfilter/1.3.11/crossfilter.min.js")
 
@@ -38,31 +38,31 @@ ct$get("bar")
 ## ------------------------------------------------------------------------
 ct$call("_.filter", mtcars, JS("function(x){return x.mpg < 15}"))
 
-## ----, eval=FALSE--------------------------------------------------------
+## ---- eval=FALSE---------------------------------------------------------
 #  # Load some data
 #  data(diamonds, package = "ggplot2")
 #  ct$assign("diamonds", diamonds)
 #  ct$console()
 
-## ----, eval=FALSE--------------------------------------------------------
+## ---- eval=FALSE---------------------------------------------------------
 #  output <- ct$get("output")
 #  print(output)
 
-## ----, eval=FALSE--------------------------------------------------------
-#  ct <- new_context()
+## ---- eval=FALSE---------------------------------------------------------
+#  ct <- v8()
 #  ct$source("https://cdnjs.cloudflare.com/ajax/libs/crossfilter/1.3.11/crossfilter.min.js")
 #  ct$eval('var cf = crossfilter || console.error("failed to load crossfilter!")')
 
 ## ------------------------------------------------------------------------
-ct <- new_context(typed_arrays = FALSE);
+ct <- v8(typed_arrays = FALSE);
 ct$get(JS("Object.keys(global)"))
 
 ## ------------------------------------------------------------------------
-ct <- new_context(typed_arrays = TRUE);
+ct <- v8(typed_arrays = TRUE);
 ct$get(JS("Object.keys(global)"))
 
 ## ------------------------------------------------------------------------
-ct2 <- new_context(global = NULL, console = FALSE)
+ct2 <- v8(global = NULL, console = FALSE)
 ct2$get(JS("Object.keys(this).length"))
 ct2$assign("cars", cars)
 ct2$eval("var foo = 123")
@@ -86,3 +86,7 @@ ct$validate("function(x){2*x}")
 ct$validate("(function(x){2*x})")
 ct$validate("!function(x){2*x}")
 
+## ---- eval=FALSE---------------------------------------------------------
+#  ctx <- v8()
+#  ctx$console()
+
diff --git a/inst/doc/v8_intro.Rmd b/inst/doc/v8_intro.Rmd
index a8a40b1..8a8675c 100644
--- a/inst/doc/v8_intro.Rmd
+++ b/inst/doc/v8_intro.Rmd
@@ -1,14 +1,13 @@
 ---
 title: "Introduction to V8 for R"
-author: "Jeroen Ooms"
 date: "`r Sys.Date()`"
+output: 
+  html_document:
+    highlight : "kate"
 vignette: >
   %\VignetteEngine{knitr::rmarkdown}
   %\VignetteIndexEntry{Introduction to V8 for R}
   \usepackage[utf8]{inputenc} 
-output:
-  knitr:::html_vignette:
-    toc: yes
 ---
 
 ```{r, echo = FALSE, message = FALSE}
@@ -16,13 +15,12 @@ knitr::opts_chunk$set(comment = "")
 library(V8)
 ```
 
-## What is V8
 
 V8 is Google’s open source, high performance JavaScript engine. It is written in C++ and implements ECMAScript as specified in ECMA-262, 5th edition. The V8 R package builds on the C++ library to provide a completely standalone JavaScript engine within R:
 
 ```{r}
 # Create a new context
-ct <- new_context();
+ct <- v8()
 
 # Evaluate some code
 ct$eval("var foo = 123")
@@ -139,7 +137,7 @@ ct$eval('console.error("Oh no! An error!")')
 A example of using `console.error` is to verify that external resources were loaded:
 
 ```{r, eval=FALSE}
-ct <- new_context()
+ct <- v8()
 ct$source("https://cdnjs.cloudflare.com/ajax/libs/crossfilter/1.3.11/crossfilter.min.js")
 ct$eval('var cf = crossfilter || console.error("failed to load crossfilter!")')
 ```
@@ -149,21 +147,21 @@ ct$eval('var cf = crossfilter || console.error("failed to load crossfilter!")')
 Unlike what you might be used to from Node or your browser, the global namespace for a new context if very minimal. By default it contains only a few objects: `global` (a refence to itself), `console` (for `console.log` and friends) and `print` (an alias of console.log needed by some JavaScript libraries)
 
 ```{r}
-ct <- new_context(typed_arrays = FALSE);
+ct <- v8(typed_arrays = FALSE);
 ct$get(JS("Object.keys(global)"))
 ```
 
 If typed arrays are enabled it contains some additional functions:
 
 ```{r}
-ct <- new_context(typed_arrays = TRUE);
+ct <- v8(typed_arrays = TRUE);
 ct$get(JS("Object.keys(global)"))
 ```
 
 A context always has a global scope, even when no name is set. When a context is initiated with `global = NULL`, it can still be reached by evaluating the `this` keyword within the global scope:
 
 ```{r}
-ct2 <- new_context(global = NULL, console = FALSE)
+ct2 <- v8(global = NULL, console = FALSE)
 ct2$get(JS("Object.keys(this).length"))
 ct2$assign("cars", cars)
 ct2$eval("var foo = 123")
@@ -200,3 +198,43 @@ To check if an anonymous function is syntactically valid, prefix it with `!` or
 ct$validate("(function(x){2*x})")
 ct$validate("!function(x){2*x}")
 ```
+
+
+## Callback To R
+
+A recently added feature is to interact with R from within JavaScript using the `console.r` API`. This is most easily demonstrated via the interactive console. 
+
+```{r, eval=FALSE}
+ctx <- v8()
+ctx$console()
+```
+
+From JavaScript we can read/write R objects via `console.r.get` and `console.r.assign`. The final argument is an optional list specifying arguments passed to `toJSON` or `fromJSON`.
+
+```javascript
+// read the iris object into JS
+var iris = console.r.get("iris")
+var iris_col = console.r.get("iris", {dataframe : "col"})
+
+//write an object back to the R session
+console.r.assign("iris2", iris)
+console.r.assign("iris3", iris, {simplifyVector : false})
+```
+
+To call R functions use `console.r.call`. The first argument should be a string which evaluates to a function. The second argument contains a list of arguments passed to the function, similar to `do.call` in R. Both named and unnamed lists are supported. The return object is returned to JavaScript via JSON.
+
+```javascript
+//calls rnorm(n=2, mean=10, sd=5)
+var out = console.r.call('rnorm', {n: 2,mean:10, sd:5})
+var out = console.r.call('rnorm', [2, 20, 5])
+
+//anonymous function
+var out = console.r.call('function(x){x^2}', {x:12})
+```
+
+There is also an `console.r.eval` function, which evaluates some code. It takes only a single argument (the string to evaluate) and does not return anything. Output is printed to the console.
+
+```javascript
+console.r.eval('sessionInfo()')
+```
+
diff --git a/inst/doc/v8_intro.html b/inst/doc/v8_intro.html
index 82f11d0..ec3ac9d 100644
--- a/inst/doc/v8_intro.html
+++ b/inst/doc/v8_intro.html
@@ -8,33 +8,55 @@
 
 
 
-
 
-
+
 
 Introduction to V8 for R
 
+
+
+
+
+
+
 
 
 
 
 
 
 
-
 
 
 
 
 
+
+
- -
-

What is V8

V8 is Google’s open source, high performance JavaScript engine. It is written in C++ and implements ECMAScript as specified in ECMA-262, 5th edition. The V8 R package builds on the C++ library to provide a completely standalone JavaScript engine within R:

-
# Create a new context
-ct <- new_context();
+
# Create a new context
+ct <- v8()
 
 # Evaluate some code
 ct$eval("var foo = 123")
 ct$eval("var bar = 456")
-ct$eval("foo + bar")
+ct$eval("foo + bar")
[1] "579"

A major advantage over the other foreign language interfaces is that V8 requires no compilers, external executables or other run-time dependencies. The entire engine is contained within a 6MB package (2MB zipped) and works on all major platforms.

-
# Create some JSON
-cat(ct$eval("JSON.stringify({x:Math.random()})"))
-
{"x":0.3905785926617682}
-
# Simple closure
-ct$eval("(function(x){return x+1;})(123)")
+
# Create some JSON
+cat(ct$eval("JSON.stringify({x:Math.random()})"))
+
{"x":0.4112895305734128}
+
# Simple closure
+ct$eval("(function(x){return x+1;})(123)")
[1] "124"

However note that V8 by itself is just the naked JavaScript engine. Currently, there is no DOM (i.e. no window object), no network or disk IO, not even an event loop. Which is fine because we already have all of those in R. In this sense V8 resembles other foreign language interfaces such as Rcpp or rJava, but then for JavaScript.

-

Loading JavaScript Libraries

The ct$source method is a convenience function for loading JavaScript libraries from a file or url.

-
ct$source(system.file("js/underscore.js", package="V8"))
-ct$source("https://cdnjs.cloudflare.com/ajax/libs/crossfilter/1.3.11/crossfilter.min.js")
+
ct$source(system.file("js/underscore.js", package="V8"))
+ct$source("https://cdnjs.cloudflare.com/ajax/libs/crossfilter/1.3.11/crossfilter.min.js")

Data Interchange

By default all data interchange between R and JavaScript happens via JSON using the bidirectional mapping implemented in the jsonlite package.

-
ct$assign("mydata", mtcars)
-ct$get("mydata")
+
ct$assign("mydata", mtcars)
+ct$get("mydata")
                     mpg cyl  disp  hp drat    wt  qsec vs am gear carb
 Mazda RX4           21.0   6 160.0 110 3.90 2.620 16.46  0  1    4    4
 Mazda RX4 Wag       21.0   6 160.0 110 3.90 2.875 17.02  0  1    4    4
@@ -135,15 +156,15 @@ 

Data Interchange

Maserati Bora 15.0 8 301.0 335 3.54 3.570 14.60 0 1 5 8 Volvo 142E 21.4 4 121.0 109 4.11 2.780 18.60 1 1 4 2

Alternatively use JS() to assign the value of a JavaScript expression (without converting to JSON):

-
ct$assign("foo", JS("function(x){return x*x}"))
+
ct$assign("foo", JS("function(x){return x*x}"))
 ct$assign("bar", JS("foo(9)"))
-ct$get("bar")
+ct$get("bar")
[1] 81

Function Calls

The ct$call method calls a JavaScript function, automatically converting objects (arguments and return value) between R and JavaScript:

-
ct$call("_.filter", mtcars, JS("function(x){return x.mpg < 15}"))
+
ct$call("_.filter", mtcars, JS("function(x){return x.mpg < 15}"))
                     mpg cyl disp  hp drat    wt  qsec vs am gear carb
 Duster 360          14.3   8  360 245 3.21 3.570 15.84  0  0    3    4
 Cadillac Fleetwood  10.4   8  472 205 2.93 5.250 17.98  0  0    3    4
@@ -155,113 +176,126 @@ 

Function Calls

Interactive JavaScript Console

A fun way to learn JavaScript or debug a session is by entering the interactive console:

-
# Load some data
+
# Load some data
 data(diamonds, package = "ggplot2")
 ct$assign("diamonds", diamonds)
-ct$console()
+ct$console()

From here you can interactively work in JavaScript without typing ct$eval every time:

-
var cf = crossfilter(diamonds)
-var price = cf.dimension(function(x){return x.price})
-var depth = cf.dimension(function(x){return x.depth})
-price.filter([2000, 3000])
-output = depth.top(10)
+
var cf = crossfilter(diamonds)
+var price = cf.dimension(function(x){return x.price})
+var depth = cf.dimension(function(x){return x.depth})
+price.filter([2000, 3000])
+output = depth.top(10)

To exit the console, either press ESC or type exit. Afterwards you can retrieve the objects back into R:

-
output <- ct$get("output")
-print(output)
+
output <- ct$get("output")
+print(output)

Conditions (warnings, errors and console.log)

Evaluating invalid JavaScript code results in a SyntaxError:

-
# A common typo
-ct$eval('var foo <- 123;')
+
# A common typo
+ct$eval('var foo <- 123;')
Error in eval(expr, envir, enclos): SyntaxError: Unexpected token <

JavaScript runtime exceptions are automatically propagated into R errors:

-
# Runtime errors
-ct$eval("123 + doesnotexit")
+
# Runtime errors
+ct$eval("123 + doesnotexit")
Error in eval(expr, envir, enclos): ReferenceError: doesnotexit is not defined

Within JavaScript we can also call back to the R console manually using console.log, console.warn and console.error. This allows for explicilty generating output, warnings or errors from within a JavaScript application.

-
ct$eval('console.log("this is a message")')
+
ct$eval('console.log("this is a message")')
this is a message
-
ct$eval('console.warn("Heads up!")')
+
ct$eval('console.warn("Heads up!")')
Warning: Heads up!
-
ct$eval('console.error("Oh no! An error!")')
+
ct$eval('console.error("Oh no! An error!")')
Error in eval(expr, envir, enclos): Oh no! An error!

A example of using console.error is to verify that external resources were loaded:

-
ct <- new_context()
+
ct <- v8()
 ct$source("https://cdnjs.cloudflare.com/ajax/libs/crossfilter/1.3.11/crossfilter.min.js")
-ct$eval('var cf = crossfilter || console.error("failed to load crossfilter!")')
+ct$eval('var cf = crossfilter || console.error("failed to load crossfilter!")')

The Global Namespace

Unlike what you might be used to from Node or your browser, the global namespace for a new context if very minimal. By default it contains only a few objects: global (a refence to itself), console (for console.log and friends) and print (an alias of console.log needed by some JavaScript libraries)

-
ct <- new_context(typed_arrays = FALSE);
-ct$get(JS("Object.keys(global)"))
+
ct <- v8(typed_arrays = FALSE);
+ct$get(JS("Object.keys(global)"))
[1] "console" "print"   "global" 

If typed arrays are enabled it contains some additional functions:

-
ct <- new_context(typed_arrays = TRUE);
-ct$get(JS("Object.keys(global)"))
+
ct <- v8(typed_arrays = TRUE);
+ct$get(JS("Object.keys(global)"))
 [1] "console"      "print"        "global"       "ArrayBuffer" 
  [5] "Int8Array"    "Uint8Array"   "Int16Array"   "Uint16Array" 
  [9] "Int32Array"   "Uint32Array"  "Float32Array" "Float64Array"
 [13] "DataView"    

A context always has a global scope, even when no name is set. When a context is initiated with global = NULL, it can still be reached by evaluating the this keyword within the global scope:

-
ct2 <- new_context(global = NULL, console = FALSE)
-ct2$get(JS("Object.keys(this).length"))
+
ct2 <- v8(global = NULL, console = FALSE)
+ct2$get(JS("Object.keys(this).length"))
[1] 10
-
ct2$assign("cars", cars)
+
ct2$assign("cars", cars)
 ct2$eval("var foo = 123")
 ct2$eval("function test(x){x+1}")
-ct2$get(JS("Object.keys(this).length"))
+ct2$get(JS("Object.keys(this).length"))
[1] 13
-
ct2$get(JS("Object.keys(this)"))
+
ct2$get(JS("Object.keys(this)"))
 [1] "ArrayBuffer"  "Int8Array"    "Uint8Array"   "Int16Array"  
  [5] "Uint16Array"  "Int32Array"   "Uint32Array"  "Float32Array"
  [9] "Float64Array" "DataView"     "cars"         "foo"         
 [13] "test"        

To create your own global you could use something like:

-
ct2$eval("var __global__ = this")
+
ct2$eval("var __global__ = this")
 ct2$eval("(function(){var bar = [1,2,3,4]; __global__.bar = bar; })()")
-ct2$get("bar")
+ct2$get("bar")
[1] 1 2 3 4

Syntax Validation

V8 also allows for validating JavaScript syntax, without actually evaluating it.

-
ct$validate("function foo(x){2*x}")
+
ct$validate("function foo(x){2*x}")
[1] TRUE
-
ct$validate("foo = function(x){2*x}")
+
ct$validate("foo = function(x){2*x}")
[1] TRUE

This might be useful for all those R libraries that generate browser graphics via templated JavaScript. Note that JavaScript does not allow for defining anonymous functions in the global scope:

-
ct$validate("function(x){2*x}")
+
ct$validate("function(x){2*x}")
[1] FALSE

To check if an anonymous function is syntactically valid, prefix it with ! or wrap in (). These are OK:

-
ct$validate("(function(x){2*x})")
+
ct$validate("(function(x){2*x})")
[1] TRUE
-
ct$validate("!function(x){2*x}")
+
ct$validate("!function(x){2*x}")
[1] TRUE
+
+

Callback To R

+

A recently added feature is to interact with R from within JavaScript using the console.r API`. This is most easily demonstrated via the interactive console.

+
ctx <- v8()
+ctx$console()
+

From JavaScript we can read/write R objects via console.r.get and console.r.assign. The final argument is an optional list specifying arguments passed to toJSON or fromJSON.

+
// read the iris object into JS
+var iris = console.r.get("iris")
+var iris_col = console.r.get("iris", {dataframe : "col"})
 
-
+//write an object back to the R session
+console.r.assign("iris2", iris)
+console.r.assign("iris3", iris, {simplifyVector : false})
+

To call R functions use console.r.call. The first argument should be a string which evaluates to a function. The second argument contains a list of arguments passed to the function, similar to do.call in R. Both named and unnamed lists are supported. The return object is returned to JavaScript via JSON.

+
//calls rnorm(n=2, mean=10, sd=5)
+var out = console.r.call('rnorm', {n: 2,mean:10, sd:5})
+var out = console.r.call('rnorm', [2, 20, 5])
+
+//anonymous function
+var out = console.r.call('function(x){x^2}', {x:12})
+

There is also an console.r.eval function, which evaluates some code. It takes only a single argument (the string to evaluate) and does not return anything. Output is printed to the console.

+
console.r.eval('sessionInfo()')
+
+ + + + +