Author Topic: JavaScript Extension Module  (Read 12163 times)

Support

  • Administrator
  • *****
  • Posts: 1
    • View Profile
JavaScript Extension Module
« on: November 18, 2016, 06:34:36 pm »
The Script BASIC JS extension module is based on Cesanta's V7 JavaScript Engine. It claims to be the world's smallest footprint JavaScript 5.1 compatible embeddable engine. There are no other dependencies required. I have attached a Ubuntu 16.04 64 bit binary shared object (.so) and interface include file. The js.inc is briefly documented but hopefully the following examples will get you started. This is just a first round beta and I would appreciate any feedback or examples you may be willing to share.

js.inc
MODULE JS

' CORE
DECLARE SUB      js_create                  ALIAS "js_create"                 LIB "js"
DECLARE SUB      js_destroy                 ALIAS "js_destroy"                LIB "js"
DECLARE SUB      js_get_global              ALIAS "js_get_global"             LIB "js"
DECLARE SUB      js_get_this                ALIAS "js_get_this"               LIB "js"
DECLARE SUB      js_get_arguments           ALIAS "js_get_arguments"          LIB "js"
DECLARE SUB      js_arg                     ALIAS "js_arg"                    LIB "js"
DECLARE SUB      js_argc                    ALIAS "js_argc"                   LIB "js"
DECLARE SUB      js_own                     ALIAS "js_own"                    LIB "js"
DECLARE SUB      js_disown                  ALIAS "js_disown"                 LIB "js"
DECLARE SUB      js_set_gc_enabled          ALIAS "js_set_gc_enabled"         LIB "js"
DECLARE SUB      js_interrupt               ALIAS "js_interrupt"              LIB "js"
DECLARE SUB      js_get_parser_error        ALIAS "js_get_parser_error"       LIB "js"

' PRIMITIVES
DECLARE SUB      js_mk_number               ALIAS "js_mk_number"              LIB "js"
DECLARE SUB      js_get_double              ALIAS "js_get_double"             LIB "js"
DECLARE SUB      js_get_int                 ALIAS "js_get_int"                LIB "js"
DECLARE SUB      js_is_number               ALIAS "js_is_number"              LIB "js"
DECLARE SUB      js_mk_boolean              ALIAS "js_mk_boolean"             LIB "js"
DECLARE SUB      js_get_bool                ALIAS "js_get_bool"               LIB "js"
DECLARE SUB      js_is_boolean              ALIAS "js_is_boolean"             LIB "js"
DECLARE SUB      js_mk_null                 ALIAS "js_mk_null"                LIB "js"
DECLARE SUB      js_is_null                 ALIAS "js_is_null"                LIB "js"
DECLARE SUB      js_mk_undefined            ALIAS "js_mk_undefined"           LIB "js"
DECLARE SUB      js_is_undefined            ALIAS "js_is_undefined"           LIB "js"
DECLARE SUB      js_mk_foreign              ALIAS "js_mk_foreign"             LIB "js"
DECLARE SUB      js_get_ptr                 ALIAS "js_get_ptr"                LIB "js"
DECLARE SUB      js_is_foreign              ALIAS "js_is_foreign"             LIB "js"

' STRINGS
DECLARE SUB      js_mk_string               ALIAS "js_mk_string"              LIB "js"
DECLARE SUB      js_is_string               ALIAS "js_is_string"              LIB "js"
DECLARE SUB      js_get_string              ALIAS "js_get_string"             LIB "js"
DECLARE SUB      js_get_cstring             ALIAS "js_get_cstring"            LIB "js"

' OBJECTS
DECLARE SUB      js_mk_object               ALIAS "js_mk_object"              LIB "js"
DECLARE SUB      js_is_object               ALIAS "js_is_object"              LIB "js"
DECLARE SUB      js_get_proto               ALIAS "js_get_proto"              LIB "js"
DECLARE SUB      js_set_proto               ALIAS "js_set_proto"              LIB "js"
DECLARE SUB      js_get                     ALIAS "js_get"                    LIB "js"
DECLARE SUB      js_def                     ALIAS "js_def"                    LIB "js"
DECLARE SUB      js_set                     ALIAS "js_set"                    LIB "js"
DECLARE SUB      js_del                     ALIAS "js_del"                    LIB "js"
DECLARE SUB      js_init_prop_iter_ctx      ALIAS "js_init_prop_iter_ctx"     LIB "js"
DECLARE SUB      js_next_prop               ALIAS "js_next_prop"              LIB "js"
DECLARE SUB      js_destruct_prop_iter_ctx  ALIAS "js_destruct_prop_iter_ctx" LIB "js"
DECLARE SUB      js_is_instanceof           ALIAS "js_is_instanceof"          LIB "js"
DECLARE SUB      js_is_instanceof_v         ALIAS "js_is_instanceof_v"        LIB "js"

' ARRAYS
DECLARE SUB      js_mk_array                ALIAS "js_mk_array"               LIB "js"
DECLARE SUB      js_is_array                ALIAS "js_is_array"               LIB "js"
DECLARE SUB      js_array_length            ALIAS "js_array_length"           LIB "js"
DECLARE SUB      js_array_push              ALIAS "js_array_push"             LIB "js"
DECLARE SUB      js_array_get               ALIAS "js_array_get"              LIB "js"
DECLARE SUB      js_array_set               ALIAS "js_array_set"              LIB "js"
DECLARE SUB      js_array_del               ALIAS "js_array_del"              LIB "js"

' EXECUTION
DECLARE SUB      js_exec                    ALIAS "js_exec"                   LIB "js"
DECLARE SUB      js_exec_file               ALIAS "js_exec_file"              LIB "js"
DECLARE SUB      js_apply                   ALIAS "js_apply"                  LIB "js"
DECLARE SUB      js_parse_json              ALIAS "js_parse_json"             LIB "js"
DECLARE SUB      js_parse_json_file         ALIAS "js_parse_json_file"        LIB "js"

'REGEX
DECLARE SUB      js_mk_regexp               ALIAS "js_mk_regexp"              LIB "js"
DECLARE SUB      js_is_regexp               ALIAS "js_is_regexp"              LIB "js"

' UTILITY
DECLARE SUB      js_stringify               ALIAS "js_stringify"              LIB "js"
DECLARE SUB      js_println                 ALIAS "js_println"                LIB "js"

DECLARE SUB      SB_shifts                  ALIAS "SB_shifts"                 LIB "js"
DECLARE COMMAND  js_iif                     ALIAS "js_iif"                    LIB "js"


' JS Global Module Variables
OBJ = 0
SYS = 0

' Stringify Modes
DEFAULT = 0
JSON    = 1
DEBUG   = 2

' Property Attribute Support

CONST V7_PROPERTY_NON_WRITABLE              = 1
CONST V7_PROPERTY_NON_ENUMERABLE            = 2
CONST V7_PROPERTY_NON_CONFIGURABLE          = 4
CONST V7_PROPERTY_GETTER                    = 8
CONST V7_PROPERTY_SETTER                    = 16
CONST _V7_PROPERTY_HIDDEN                   = 32
CONST _V7_PROPERTY_OFF_HEAP                 = 64
CONST _V7_PROPERTY_USER_DATA_AND_DESTRUCTOR = 128
CONST _V7_DESC_PRESERVE_VALUE               = 256
CONST _V7_DESC_MASK                         = &HFFFF

CONST PROPERTY_DEFAULT = 0

' TRUE or FALSE. Whether the property's value can be set.
FUNCTION WRITABLE(v)
  IF v THEN
    WRITABLE = PROPERTY_DEFAULT
  ELSE
    WRITABLE = V7_PROPERTY_NON_WRITABLE
  END IF
END FUNCTION

' TRUE or FALSE. Whether the property shows in some loop constructs.
FUNCTION ENUMERABLE(v)
  IF v THEN
    ENUMERABLE = PROPERTY_DEFAULT
  ELSE
    ENUMERABLE = V7_PROPERTY_NON_ENUMERABLE
  END IF
END FUNCTION

' TRUE or FALSE. Whether the property can be deleted and whether its attributes can be changed.
FUNCTION CONFIGURABLE(v)
  IF v THEN
    CONFIGURABLE = PROPERTY_DEFAULT
  ELSE
    CONFIGURABLE = V7_PROPERTY_NON_CONFIGURABLE
  END IF
END FUNCTION

' TRUE or FALSE. When a property is accessed the value is generated by calling a function implicitly.
FUNCTION GETTER(v)
  IF v THEN
    GETTER = V7_PROPERTY_GETTER
  ELSE
    GETTER = FALSE
  END IF
END FUNCTION

' TRUE or FALSE. When a property is set it will implicitly call a function and pass a
' value as argument, and the return value of the function is set to the property.
FUNCTION SETTER(v)
  IF v THEN
    SETTER = V7_PROPERTY_SETTER
  ELSE
    SETTER = FALSE
  END IF
END FUNCTION

FUNCTION PRESERVE_VALUE
    PRESERVE_VALUE = _V7_DESC_PRESERVE_VALUE
END FUNCTION

FUNCTION HIDDEN(v)
  IF v THEN
    HIDDEN = V7_PROPERTY_HIDDEN
  ELSE
    HIDDEN = FALSE
  END IF
END FUNCTION

FUNCTION OFF_HEAP(v)
  IF v THEN
    OFF_HEAP = _V7_PROPERTY_OFF_HEAP
  ELSE
    OFF_HEAP = FALSE
  END IF
END FUNCTION


' JS API Function Wrappers

' Create V7 instance
FUNCTION CREATE
  OBJ = js_create()
  SYS = js_get_global(OBJ)
  CREATE = OBJ
END FUNCTION

' Destroy V7 instance
SUB DESTROY
  js_destroy(OBJ)
  UNDEF OBJ
END SUB

' Return root level (`global`) object of the given V7 instance
FUNCTION GET_GLOBAL
  GET_GLOBAL = js_get_global(OBJ)
END FUNCTION

' Return current `this` object
FUNCTION GET_THIS
  GET_THIS = js_get_this(OBJ)
END FUNCTION

' Return current `arguments` array
FUNCTION GET_ARGUMENTS
  GET_ARGUMENTS = js_get_arguments(OBJ)
END FUNCTION

' Return i-th argument
FUNCTION ARG(i)
  ARG = js_arg(OBJ, i)
END FUNCTION

' Return the length (`count`) of `arguments`
FUNCTION ARGC
  ARGC = js_argc(OBJ)
END FUNCTION

' Tells the GC about a JS value variable/field owned by `C` code.
SUB OWN(v)
  js_own(OBJ, v)
END SUB

' User code should also explicitly disown the variables with v7_disown
' once it goes out of scope or the structure containing the v7_val_t field is freed.
' Returns 1 if value is found, 0 otherwise
FUNCTION DISOWN(v)
  DISOWN = js_disown(OBJ, v)
END FUNCTION

' Enable or disable GC
SUB SET_GC_ENABLED(enabled)
  js_set_gc_enabled(OBJ, enabled)
END SUB

' It sets a flag that will cause the interpreter to throw an Interrupted Error
SUB INTERRUPT
  js_interrupt(OBJ)
END SUB

' Returns last parser error message
FUNCTION GET_ERROR
  GET_ERROR = js_get_parser_error(OBJ)
END FUNCTION

' Make numeric primitive value
FUNCTION MK_NUMBER(num)
  MK_NUMBER = js_mk_number(OBJ, num)
END FUNCTION

' Returns number value stored in `v7_val_t` as `double`
FUNCTION GET_DOUBLE(v)
  GET_DOUBLE = js_get_double(OBJ, v)
END FUNCTION

' Returns number value stored in `v7_val_t` as `int`. If the number
' value is not an integer, the fraction part will be discarded.
FUNCTION GET_INT(v)
  GET_INT = js_get_int(OBJ, v)
END FUNCTION

' Returns true if given value is a primitive number value
FUNCTION IS_NUMBER(v)
  IS_NUMBER = js_is_number(v)
END FUNCTION

' Make boolean primitive value (either `true` or `false`)
FUNCTION MK_BOOLEAN(is_true)
  MK_BOOLEAN = js_mk_boolean(OBJ, is_true)
END FUNCTION

' Returns boolean stored in `v7_val_t`: 0 for `false` or
' non-boolean, non-0 for `true`
FUNCTION GET_BOOL(v)
  GET_BOOL = js_get_bool(OBJ, v)
END FUNCTION

' Returns `true` if given value is a primitive boolean value
FUNCTION IS_BOOLEAN(v)
  IS_BOOLEAN = js_is_boolean(v)
END FUNCTION

' Make `null` primitive value
FUNCTION MK_NULL
  MK_NULL = js_mk_null()
END FUNCTION

' Returns true if given value is a primitive `null` value
FUNCTION IS_NULL(v)
  IS_NULL = js_is_null(v)
END FUNCTION

' Make `undefined` primitive value
FUNCTION MK_UNDEFINED
  MK_UNDEFINED = js_mk_undefined()
END FUNCTION

' Returns true if given value is a primitive `undefined` value
FUNCTION IS_UNDEFINED(v)
  IS_UNDEFINED = js_is_undefined(v)
END FUNCTION

' Make JavaScript value that holds C/C++ `void *` pointer
FUNCTION MK_FOREIGN
  MK_FOREIGN = js_mk_foreign(OBJ)
END FUNCTION

' Returns `void *` pointer stored in `v7_val_t`
' Returns NULL `undef` if the value is not a foreign pointer
FUNCTION GET_PTR(v)
  GET_PTR = js_get_ptr(OBJ, v)
END FUNCTION

' Returns true if given value holds `void *` pointer
FUNCTION IS_FOREIGN(v)
  IS_FOREIGN = js_is_foreign(v)
END FUNCTION

' Creates a string primitive value
FUNCTION MK_STRING(strval)
  MK_STRING = js_mk_string(OBJ, strval, LEN(strval), 1)
END FUNCTION

' Returns true if given value is a primitive string value
FUNCTION IS_STRING(v)
  IS_STRING = js_is_string(v)
END FUNCTION

' Returns a pointer to the string stored in `v7_val_t`
FUNCTION GET_STRING(v)
  GET_STRING = js_get_string(OBJ, v)
END FUNCTION

' Returns a pointer to the string stored in `v7_val_t`
' Returns NULL `undef` if the value is not a string or
' if the string is not compatible with a C string
FUNCTION GET_CSTRING(v)
  GET_CSTRING = js_get_cstring(OBJ, v)
END FUNCTION

' Make an empty object
FUNCTION MK_OBJECT
  MK_OBJECT = js_mk_object(OBJ)
END FUNCTION

' Returns true if the given value is an object or function
FUNCTION IS_OBJECT(v)
  IS_OBJECT = js_is_object(v)
END FUNCTION

' Get object's prototype.
FUNCTION GET_PROTO(object)
  GET_PROTO = js_get_proto(OBJ, object)
END FUNCTION

' Set object's prototype. Return old prototype or undefined on error
FUNCTION SET_PROTO(object, proto)
  SET_PROTO = js_set_proto(OBJ, object, proto)
END FUNCTION

' Lookup property `name` in object `obj`. If `obj` holds no such property,
' an `undefined` value is returned
FUNCTION GETS(object, objname)
  GETS = js_get(OBJ, object, objname, LEN(objname))
END FUNCTION

' Define object property, similar to JavaScript `Object.defineProperty()`
FUNCTION DEF(object, objname, attr, value)
  DEF = js_def(OBJ, object, objname, LEN(objname), attr, value)
END FUNCTION

' Set object property. Behaves just like JavaScript assignment
FUNCTION SETS(object, objname, value)
  SETS = js_set(OBJ, object, objname, LEN(objname), value)
END FUNCTION

' Delete own property `name` of the object `obj`
' Does not follow the prototype chain
FUNCTION DEL(object, objname)
  DEL = js_del(OBJ, object, objname, LEN(objname))
END FUNCTION

' Returns true if the object is an instance of a given constructor / class name
FUNCTION IS_INSTANCEOF(object, classname)
  IS_INSTANCEOF = js_is_instanceof(OBJ, object, classname)
END FUNCTION

' Returns true if the object is an instance of a given constructor object class
FUNCTION IS_INSTANCEOF_V(object, objclass)
  IS_INSTANCEOF_V = js_is_instanceof_v(OBJ, object, objclass)
END FUNCTION

' Custom multi-property `GET` function
FUNCTION GET_PROPERTIES(object, proparray)
  LOCAL objname, value, attr, propcnt
  objname = ""
  value = 0
  attr = 0
  propcnt = 1
 ' UNDEF proparray
 js_init_prop_iter_ctx(OBJ, object)
  WHILE js_next_prop(OBJ, objname, value, attr) <> undef
    proparray[propcnt, 0] = js_get_string(OBJ, objname)
    proparray[propcnt, 1] = js_get_int(OBJ, value)
    proparray[propcnt, 2] = attr
    propcnt += 1
  WEND
  js_destruct_prop_iter_ctx(OBJ)
  GET_PROPERTIES = propcnt - 1
END FUNCTION

' Make an empty array object
FUNCTION MK_ARRAY
  MK_ARRAY = js_mk_array(OBJ)
END FUNCTION

' Returns true if given value is an array object
FUNCTION IS_ARRAY(object)
  IS_ARRAY = js_is_array(OBJ, object)
END FUNCTION

' Returns length on an array. If `object` is not an array, 0 is returned
FUNCTION ARRAY_LENGTH(object)
  ARRAY_LENGTH = js_array_length(OBJ, object)
END FUNCTION

' Insert `value` in `object` at the end of the array
FUNCTION ARRAY_PUSH(object, value)
  ARRAY_PUSH = js_array_push(OBJ, object, value)
END FUNCTION

'  Return array member at index `index`. If `index` is out of bounds, undefined is returned
FUNCTION ARRAY_GET(object, index)
  ARRAY_GET = js_array_get(OBJ, object, index)
END FUNCTION

' Insert value `v` into `arr` at 'index`
FUNCTION ARRAY_SET(object, index, value)
  ARRAY_SET = js_array_set(OBJ, object, index, value)
END FUNCTION

' Delete value in array `arr` at index `index`, if it exists
SUB ARRAY_DEL(object, index)
   js_array_del(OBJ, object, index)
END SUB

' Execute JavaScript `js_code`
' The result of evaluation is stored in the `return` variable
' The 'ok' argument will contain the function's execution success status flag
FUNCTION EXEC(code, ok)
  EXEC = js_exec(OBJ, code, ok)
END FUNCTION

' Same as `v7_exec()`, but loads source code from `path` file
FUNCTION EXEC_FILE(codepath, ok)
  EXEC_FILE = js_exec_file(OBJ, codepath, ok)
END FUNCTION

' Parse `json_code`
' The result of evaluation is stored in the `return` variable
' The 'ok' argument will contain the function's parse success status flag
FUNCTION PARSE_JSON(json_code, ok)
  PARSE_JSON = js_parse_json(OBJ, json_code, ok)
END FUNCTION

' Same as `v7_parse_json()`, but loads `json_code` from `path` file
FUNCTION PARSE_JSON_FILE(json_code_file, ok)
  PARSE_JSON_FILE = js_parse_json_file(OBJ, json_code_file, ok)
END FUNCTION

' Call function `func` with arguments `args`, using `object` as `this`
' `args` should be an array containing arguments or `undefined`
FUNCTION APPLY(func, object, args)
  APPLY = js_apply(OBJ, func, object, args)
END FUNCTION

' Make RegExp object. For example, `regex` is `(.+)`, `flags` is `gi`.
FUNCTION MK_REGEXP(regex, flags, rcode)
  MK_REGEXP = js_mk_regexp(OBJ, regex, LEN(regex), flags, LEN(flags), rcode)
END FUNCTION

' Returns true if given value is a JavaScript RegExp object
FUNCTION IS_REGEXP(object)
  IS_REGEXP = js_is_regexp(OBJ, object)
END FUNCTION

' Generate string representation of the JavaScript value
FUNCTION STRINGIFY(object, convtype)
  STRINGIFY = js_stringify(OBJ, object, convtype)
END FUNCTION

' Output a string representation of the value to stdout followed by a newline
SUB PRINTIFY(object)
  js_println(OBJ, object)
END SUB


END MODULE
 

Hello JavaScript
IMPORT js.inc

JS::CREATE
PRINT JS::GET_INT(JS::EXEC("1 + 1")),"\n"
JS::DESTROY
 

jrs@jrs-laptop:~/sb/examples/js$ scriba js_hello2.sb
2
jrs@jrs-laptop:~/sb/examples/js$


Fibonacci
IMPORT js.inc

jscode = """
function fibonacci(n) {
  if (n <= 2) {
    return 1;
  } else {
    return fibonacci(n - 1) + fibonacci(n - 2);
  }
}

print(fibonacci(24));
"
""

JS::CREATE
JS::EXEC(jscode)
JS::DESTROY
 

jrs@jrs-laptop:~/sb/examples/js$ scriba js_fibonacci.sb
46368
jrs@jrs-laptop:~/sb/examples/js$


Create object, property and attributes
IMPORT js.inc

JS::CREATE
myobj = JS::MK_OBJECT()
JS::DEF(myobj, "test", 0, JS::MK_NUMBER(64))
JS::SETS(myobj, "test", JS::MK_NUMBER(32))
JS::DEF(myobj, "test", JS::WRITABLE(FALSE) OR JS::PRESERVE_VALUE(),JS::MK_NULL())
JS::SETS(myobj, "test", JS::MK_NUMBER(16))
PRINT "test = ",JS::GET_INT(JS::GETS(myobj, "test")),"\n"
JS::DESTROY
 

jrs@jrs-laptop:~/sb/examples/js$ scriba js_deftest.sb
test = 32
jrs@jrs-laptop:~/sb/examples/js$


Load .js file and get properties
IMPORT js.inc

JS::CREATE
JS::EXEC_FILE "properties.js"
propcnt = JS::GET_PROPERTIES(JS::GETS(JS::SYS,"test"), proparray)
PRINT "Property\tValue\tAttribute\n"
FOR i = 1 to propcnt
  PRINT proparray[i,0],"\t\t",proparray[i,1],"\t",proparray[i,2],"\n"
NEXT
JS::DESTROY
 
properties.js
var test = {};

Object.defineProperty(test, 'a', {
  value: 1,
  writable: true,
  enumerable: true,
  configurable: true
});

Object.defineProperty(test, 'b', {
  value: 2,
  writable: false,
  enumerable: false,
  configurable: false
});
 

jrs@jrs-laptop:~/sb/examples/js$ scriba js_propfile.sb
Property   Value   Attribute
b      2   7
a      1   0
jrs@jrs-laptop:~/sb/examples/js$


Call JavaScript function
IMPORT js.inc

jscode = """
var sum = function(a, b, c) {
  print (c);
  return a + b; };
"
""

JS::CREATE()
JS::EXEC(jscode)
func = JS::GETS(JS::SYS, "sum")
args = JS::MK_ARRAY()
JS::ARRAY_PUSH(args, JS::MK_NUMBER(123.0))
JS::ARRAY_PUSH(args, JS::MK_NUMBER(0.456))
JS::ARRAY_PUSH(args, JS::MK_STRING("Script BASIC"))
result = JS::APPLY(func, 0, args, rcode)
PRINT FORMAT("Result: %g\n", JS::GET_DOUBLE(result))
JS::DESTROY
 

jrs@jrs-laptop:~/sb/examples/js$ scriba js_callfunc.sb
Script BASIC
Result: 123.456
jrs@jrs-laptop:~/sb/examples/js$


JSON / Stringify
IMPORT js.inc

JS::CREATE
myobj = JS::MK_OBJECT()
JS::DEF(myobj, "myprop_1", 0, JS::MK_NUMBER(64))
JS::DEF(myobj, "myprop_2", 0, JS::MK_NUMBER(1.23))
JS::DEF(myobj, "myprop_3", 0, JS::MK_STRING("JavaScript"))
PRINT JS::STRINGIFY(myobj, JS::JSON),"\n"
JS::DESTROY
 

jrs@jrs-laptop:~/sb/examples/js$ scriba js_stringify.sb
{"myprop_3":"JavaScript","myprop_2":1.23,"myprop_1":64}
jrs@jrs-laptop:~/sb/examples/js$


FYI: - A Windows 32 bit version may materialize if I can get by the unexplainable issues on this platform. Windows isn't generally supported by Cesanta.

« Last Edit: November 23, 2016, 04:34:12 am by support »