...
 
Commits (5)
......@@ -10,7 +10,6 @@
<!-- ABC interpreter -->
<script type="text/javascript" src="/js/abc-interpreter.js"></script>
<script type="text/javascript" src="/js/itasks-abc-interpreter.js"></script>
<!-- iTasks framework -->
<script type="text/javascript" src="/js/itasks-core.js"></script>
......
......@@ -2,7 +2,7 @@ module WasmTest
import StdEnv
import iTasks
import iTasks.UI.JavaScript
import ABC.Interpreter.JavaScript
// This is a simple test program to try out things with the WebAssembly ABC interpreter.
......
......@@ -4,10 +4,10 @@ implementation module iTasks.Extensions.Clock
*/
import iTasks
import iTasks.UI.Definition, iTasks.UI.Editor
import iTasks.UI.JavaScript
import iTasks.Extensions.DateTime
import qualified Data.Map as DM, Data.Tuple, Data.Error
import Text.HTML, Data.Func
import ABC.Interpreter.JavaScript
import StdEnv
derive JSONEncode AnalogClock
......
implementation module iTasks.Extensions.Dashboard
import iTasks
import iTasks.UI.Editor, iTasks.UI.Definition, iTasks.UI.JavaScript
import iTasks.UI.Editor, iTasks.UI.Definition
import qualified Data.Map as DM, Data.Error
import Text.HTML, StdMisc, StdArray, Data.Func
import ABC.Interpreter.JavaScript
derive JSONEncode ControlLight
derive JSONDecode ControlLight
......
......@@ -2,7 +2,7 @@ implementation module iTasks.Extensions.Editors.Ace
import iTasks
import iTasks.UI.Editor, iTasks.UI.Editor.Modifiers, iTasks.UI.Definition
import iTasks.UI.JavaScript
import ABC.Interpreter.JavaScript
import qualified Data.Map as DM
import Data.Func, StdArray
......
......@@ -2,10 +2,11 @@ implementation module iTasks.Extensions.Form.Pikaday
import StdEnv
import iTasks, Data.Func
import iTasks.UI.Definition, iTasks.UI.Editor, iTasks.UI.JavaScript
import iTasks.UI.Definition, iTasks.UI.Editor
import iTasks.UI.Editor.Modifiers, iTasks.UI.Editor.Controls
import iTasks.Extensions.DateTime
import qualified Data.Map as DM
import ABC.Interpreter.JavaScript
PIKADAY_JS_URL :== "/pikaday/pikaday.js"
PIKADAY_CSS_URL :== "/pikaday/css/pikaday.css"
......
implementation module iTasks.Extensions.GIS.Leaflet
import iTasks
import iTasks.UI.Definition, iTasks.UI.Editor, iTasks.UI.JavaScript
import iTasks.UI.Definition, iTasks.UI.Editor
import StdMisc, Data.Tuple, Data.Error, Data.Func, Text, Data.Functor
import qualified Data.Map as DM
//from Text.HTML import instance toString HtmlTag, instance toString SVGElt
import Text.HTML
from Text.Encodings.Base64 import base64Encode
from iTasks.UI.Editor.Common import diffChildren, :: ChildUpdate (..)
import ABC.Interpreter.JavaScript
import StdArray
LEAFLET_JS :== "/leaflet-1.3.4/leaflet.js"
......
implementation module iTasks.Extensions.SVG.SVGEditor
import iTasks.UI.JavaScript, iTasks.UI.Definition, iTasks.UI.Editor, iTasks.Internal.Serialization
import iTasks.UI.Definition, iTasks.UI.Editor, iTasks.Internal.Serialization
import Graphics.Scalable.Image
import Graphics.Scalable.Internal.Image`
import StdEnv
......@@ -21,6 +21,7 @@ import Math.Geometry
import Text
import Text.GenJSON
import Text.HTML
import ABC.Interpreter.JavaScript
import StdDebug
trace_n` a b :== /*trace_n a*/ b
......
......@@ -5,8 +5,8 @@ import Text.HTML, Text.GenJSON, Data.Error, Data.Func
import iTasks.UI.Definition
import iTasks.UI.Editor
import iTasks.UI.Editor.Modifiers
import iTasks.UI.JavaScript
import iTasks.Internal.Serialization
import ABC.Interpreter.JavaScript
import qualified Data.Map as DM
//Basic idea:
......
......@@ -8,7 +8,7 @@ import Data.Error
import Text.GenJSON
import Data.Maybe
import ABC.Interpreter
import ABC.Interpreter.JavaScript
import iTasks.Engine
import iTasks.Internal.IWorld
......@@ -53,5 +53,4 @@ dynamicJSONDecode _ = Nothing
serializeForClient :: a !*VSt -> *(!String, !*VSt)
serializeForClient graph vst=:{VSt| abcInterpreterEnv}
# serialized = serialize_for_prelinked_interpretation graph abcInterpreterEnv
= (base64Encode serialized, vst)
= (jsSerializeGraph graph abcInterpreterEnv, vst)
......@@ -4,9 +4,9 @@ definition module iTasks.UI.Editor
* the interact core task uses these editors to generate and update the user interface
*/
from ABC.Interpreter import :: PrelinkedInterpretationEnvironment
from ABC.Interpreter import :: PrelinkedInterpretationEnvironment
from ABC.Interpreter.JavaScript import :: JSWorld, :: JSVal
from iTasks.UI.Definition import :: UI, :: UIAttributes, :: UIChange, :: UIAttributeChange, :: TaskId
from iTasks.UI.JavaScript import :: JSWorld, :: JSVal
from iTasks.Internal.IWorld import :: IWorld
from iTasks.Internal.Generic.Defaults import generic gDefault
......
......@@ -4,10 +4,11 @@ import StdEnv
import Data.Maybe, Data.Functor, Data.Tuple, Data.Func, Data.Error
import iTasks.Internal.IWorld
import iTasks.Internal.Serialization
import iTasks.UI.Definition, iTasks.WF.Definition, iTasks.UI.JavaScript
import iTasks.UI.Definition, iTasks.WF.Definition
import qualified Data.Map as DM
import Text, Text.GenJSON
import Data.GenEq
import ABC.Interpreter.JavaScript
derive JSONEncode EditState, LeafState, EditMode
derive JSONDecode EditState, LeafState, EditMode
......@@ -153,7 +154,7 @@ withClientSideInit ::
!UIAttributes !DataPath !a !*VSt -> *(!MaybeErrorString (!UI, !st), !*VSt)
withClientSideInit initUI genUI attr dp val vst=:{VSt| taskId} = case genUI attr dp val vst of
(Ok (UI type attr items,mask),vst)
# (initUI, vst) = serializeForClient (wrapInitUIFunction initUI) vst
# (initUI, vst) = serializeForClient (wrapInitFunction initUI) vst
# extraAttr = 'DM'.fromList
[("taskId", JSONString taskId)
,("editorId",JSONString (editorId dp))
......
definition module iTasks.UI.JavaScript
/**
* This module provides ways to interact with JavaScript. All these functions
* are supposed to be used from within a function that is wrapped by
* `wrapInitUIFunction` (or hence, `withClientSideInit` in `iTasks.UI.Editor`),
* which ensures that they are run within the WebAssembly runtime in the
* browser. It does not make sense to use these functions on the server.
*
* The argument to `wrapInitUIFunction` receives a reference to the JavaScript
* iTasks component it is related to, which must be used to share Clean values
* with JavaScript (where indicated in documentation below).
*
* The JavaScript interfacing with this module can be found in itasks-core.js
* and itasks-abc-interpreter.js.
*/
import StdGeneric
from StdMaybe import :: Maybe
from StdOverloaded import class toString
from Text.GenJSON import :: JSONNode
/**
* All impure interfacing with JavaScript is handled through the `*JSWorld`, as
* native impure functionality is threaded through `*World`. `JSWorld` is not
* unique to be able to instantiate Monad (in `iTasks.UI.JavaScript.Monad`).
* Because all functions using `JSWorld` use it in a unique way, sharing is
* still prohibited.
*/
:: JSWorld
:: JSVal
:: JSFun :== JSVal
:: JSObj :== JSVal
generic gToJS a :: !a -> JSVal
derive gToJS Int, Bool, String, Real
derive gToJS JSVal, Maybe, [], (,), (,,), (,,,), (,,,,), JSONNode
derive gToJS PAIR, FIELD of {gfd_name}, RECORD
toJS x :== gToJS{|*|} x
/**
* Store a Clean value in the JavaScript heap. The value must be associated to
* an iTasks component (typically given by `wrapInitUIFunction`). When the
* iTasks component is destroyed, the value may eventually be garbage
* collected. The value can be retrieved using `jsGetCleanReference`.
* @param Any Clean value.
* @param A reference to an iTasks component.
* @result A JavaScript reference to the Clean value.
*/
jsMakeCleanReference :: a !JSVal !*JSWorld -> *(!JSVal, !*JSWorld)
/**
* Retrieve a Clean value from the JavaScript heap. The value must have been
* shared using `jsMakeCleanReference`.
*/
jsGetCleanReference :: !JSVal !*JSWorld -> *(!Maybe b, !*JSWorld)
/**
* Remove a Clean value from the JavaScript heap. The value must have been
* shared using `jsMakeCleanReference` or `jsWrapFun`.
*/
jsFreeCleanReference :: !JSVal !*JSWorld -> *JSWorld
jsTypeOf :: !JSVal -> JSVal
jsIsUndefined :: !JSVal -> Bool
jsIsNull :: !JSVal -> Bool
jsValToInt :: !JSVal -> Maybe Int
jsValToBool :: !JSVal -> Maybe Bool
jsValToString :: !JSVal -> Maybe String
jsValToReal :: !JSVal -> Maybe Real
jsValToInt` :: !Int !JSVal -> Int
jsValToBool` :: !Bool !JSVal -> Bool
jsValToString` :: !String !JSVal -> String
jsValToReal` :: !Real !JSVal -> Real
/**
* Retrieve a JavaScript iterable as a Clean list.
* @param The JavaScript iterable.
* @param The function to retrieve elements, called on every element of the
* iterable.
* @result `Nothing` if the value is not iterable (has no `length` property) or
* if any of the elements could not be retrieved; otherwise `Just` with a
* list of the retrieved values.
*/
jsValToList :: !JSVal !(JSVal -> Maybe a) !*JSWorld -> *(!Maybe [a], !*JSWorld)
/**
* Retrieve a JavaScript iterable as a Clean list.
* @param The JavaScript iterable.
* @param The function to retrieve elements, called on every element of the
* iterable.
* @result A list of the retrieved values. If the JavaScript value is not
* iterable (has no `length` property), the empty list is returned.
*/
jsValToList` :: !JSVal !(JSVal -> a) !*JSWorld -> *(![a], !*JSWorld)
/**
* Access properties of a JavaScript value.
*/
class (.#) infixl 3 attr :: !JSVal !attr -> JSVal
instance .# String // object access; may contain dots
instance .# Int // array access
/**
* Retrieve a JavaScript value. This can be useful if the argument contains
* computations which need to be shared among further usages of the result:
*
* ```
* // object is a JavaScript object with a property 'prop' which requires computation
* # (result,world) = object .# "prop" .? world
* // continue to use result, so that object.prop is only evaluated once
* ```
*
* However, if the argument does not contain computations, one can use a simple
* Clean let: `# result = object .# "prop"`.
*
* @param The value to retrieve.
* @result The retrieved value.
*/
(.?) infixl 1 :: !JSVal !*JSWorld -> *(!JSVal, !*JSWorld)
/**
* Set a JavaScript value to another value.
* @param The value to set.
* @param The new value.
*/
(.=) infixl 1 :: !JSVal !b !*JSWorld -> *JSWorld | gToJS{|*|} b
/**
* This is an internal class representing types which can be sent as function
* arguments to JavaScript:
*
* - `Int`, `Bool`, and `String` map to their JavaScript equivalents
* - `JSVal` requires no conversion
* - `Maybe a` uses the conversion for the underlying type for `Just`, otherwise `null`
* - `()` relates to no arguments; tuples relates to lists of arguments
*/
class toJSArgs a :: !a -> {!JSVal}
instance toJSArgs Int, Bool, String, JSVal, [a] | gToJS{|*|} a, (Maybe a) | gToJS{|*|} a, ()
instance toJSArgs (a,b) | gToJS{|*|} a & gToJS{|*|} b
instance toJSArgs (a,b,c) | gToJS{|*|} a & gToJS{|*|} b & gToJS{|*|} c
instance toJSArgs (a,b,c,d) | gToJS{|*|} a & gToJS{|*|} b & gToJS{|*|} c & gToJS{|*|} d
instance toJSArgs (a,b,c,d,e) | gToJS{|*|} a & gToJS{|*|} b & gToJS{|*|} c & gToJS{|*|} d & gToJS{|*|} e
instance toJSArgs (a,b,c,d,e,f) | gToJS{|*|} a & gToJS{|*|} b & gToJS{|*|} c & gToJS{|*|} d & gToJS{|*|} e & gToJS{|*|} f
/**
* Call a JavaScript function and return the result.
*/
(.$) infixl 2 :: !JSFun !b !*JSWorld -> *(!JSVal, !*JSWorld) | toJSArgs b
/**
* Call a JavaScript function and discard the result.
*/
(.$!) infixl 2 :: !JSFun !b !*JSWorld -> *JSWorld | toJSArgs b
/**
* Use the JavaScript `new` keyword to create a new object.
* @param The constructor name.
* @param The constructor arguments.
* @result The new object.
*/
jsNew :: !String !a !*JSWorld -> *(!JSVal, !*JSWorld) | toJSArgs a
/**
* Create an empty JavaScript object (`{}`).
*/
jsEmptyObject :: !*JSWorld -> *(!JSVal, !*JSWorld)
/**
* Delete a JavaScript value with the `delete` keyword.
*/
jsDelete :: !JSVal !*JSWorld -> *JSWorld
/**
* Lift a String to a JavaScript value using the global address space.
*/
jsGlobal :: !String -> JSVal
jsNull :== jsGlobal "null"
jsThis :== jsGlobal "this"
jsWindow :== jsGlobal "window"
jsDocument :== jsGlobal "document"
/**
* Wrap a function for use in JavaScript. This allows it to be used as the
* callback for events and the like. The function must be associated to an
* iTasks component (typically given by `wrapInitUIFunction`). When the iTasks
* component is destroyed, the function may eventually be garbage collected.
* @param The function to wrap. When called, it receives the JavaScript
* arguments as an array in its first parameter.
* @param The iTasks component to link the function to.
* @result A reference to the shared function.
*/
jsWrapFun :: !({!JSVal} *JSWorld -> *JSWorld) !JSVal !*JSWorld -> *(!JSFun, !*JSWorld)
/**
* Like {{`jsWrapFun`}}, but the Clean function can return a result.
*/
jsWrapFunWithResult :: !({!JSVal} *JSWorld -> *(JSVal, *JSWorld)) !JSVal !*JSWorld -> *(!JSFun, !*JSWorld)
/**
* Wrap a function receiving a reference to a JavaScript iTasks component to
* one matching the calling convention for the JavaScript interface (i.e.,
* receiving an array of JavaScript values) so that it can be called using the
* `initUI` step from `itasks-core.js`. Internally, this also sets up part of
* the WebAssembly backend. The first argument to the wrapped function is a
* reference to an iTasks component which can be used in `jsWrapFun` and
* `jsMakeCleanReference`.
*/
wrapInitUIFunction :: !(JSVal *JSWorld -> *JSWorld) -> {!JSVal} -> *JSWorld -> *JSWorld
/**
* Deserialize a graph that was serialized using the tools in
* `iTasks.Internal.Client.Serialization`.
* @param The string to deserialize in base64 encoding.
* @result The deserialized value.
*/
jsDeserializeGraph :: !*String !*JSWorld -> *(!.a, !*JSWorld)
/**
* Load external CSS stylesheet by its URL.
* @param The URL.
*/
addCSSFromUrl :: !String !*JSWorld -> *JSWorld
/**
* Load an external JavaScript file by its URL.
* @param The URL.
* @param An optional callback function for when the script has loaded.
*/
addJSFromUrl :: !String !(Maybe JSFun) !*JSWorld -> *JSWorld
/**
* A simple wrapper around JavaScript's `console.log`.
* Use {{`jsTraceVal`}} to trace JavaScript values.
* @param The value to log.
* @param The value to return.
*/
jsTrace :: !a .b -> .b | toString a
/**
* A simple wrapper around JavaScript's `console.log`.
* Use {{`jsTrace`}} to trace Clean values.
* @param The value to log.
* @param The value to return.
*/
jsTraceVal :: !JSVal .a -> .a
This diff is collapsed.
definition module iTasks.UI.JavaScript.Monad
/**
* This module provides an abstraction layer over `iTasks.UI.JavaScript`. In
* that module, low-level functions to interface with JavaScript through the
* WebAssembly ABC interpreter are defined. These functions work on a unique
* `*JSWorld` type. This module provides a state monad `JS` which can chain
* such functions.
*
* Typically, you will use `js` to lift functionality on the `*JSWorld` level
* to the `JS` monad, and `runJS` to run a `JS` monad on a `*JSWorld`.
*
* The benefit becomes particularly visible when using JavaScript promises,
* which themselves are very similar to monads. `` `then` `` allows chaining of
* promises and takes care of creating the callback function under the hood.
*
* However, you need to be very careful about which things return promises and
* which things return plain values. For example, when `f` returns a promise
* and `g` uses the value it resolves to you need to write ``f `then` g`` and
* not `f >>= g`, even though the latter is allowed by the type system.
* Typically the result of `` `then` `` is unused.
*/
from StdOverloaded import class toString
from Control.Applicative import class pure, class <*>, class Applicative
from Control.Monad import class Monad
from Data.Functor import class Functor
import iTasks.UI.JavaScript
:: JSState st =
{ jsworld :: !JSWorld
, component :: !JSVal //* The current component (to link shared objects to, for garbage collection)
, state :: !st
}
/**
* This type uses a non-unique JSWorld in order to implement {{`Monad`}}.
* The {{`js`}} and {{`runJS`}} functions can be used for casting.
*/
:: JS st a =: JS (.(JSState st) -> (a, .JSState st))
// These two functions use casts to enforce uniqueness of the JSWorld:
//* Lift a `*World` function to the `JS` monad.
js :: !(*JSWorld -> (a, *JSWorld)) -> JS st a
//* Execute a `JS` monad on a `*World`.
runJS :: !st !JSVal !(JS st a) !*JSWorld -> (a, *JSWorld)
//* Get a value from the `JS` monad state.
gets :: !((JSState st) -> a) -> JS st a
//* Modify the `JS` monad state.
modState :: !((JSState st) -> JSState st) -> JS st (JSState st)
instance Functor (JS st)
instance pure (JS st)
instance <*> (JS st)
instance Monad (JS st)
//* Type synonym used to indicate that a JavaScript value refers to a Promise.
:: JSPromise :== JSVal
/**
* Chain JavaScript promises in a monadic style.
* @param The promise.
* @param The continuation (possibly resolving to a promise).
* @result The resulting promise (which can be continued in a monadic style).
*/
(`then`) infixl 1 :: !(JS st JSPromise) !(JSVal -> JS st JSVal) -> JS st JSPromise
//* The Clean equivalent of `Promise.resolve()`, used to start a promise chain.
resolvePromise :: JS st JSPromise
implementation module iTasks.UI.JavaScript.Monad
import StdEnv
import Control.Applicative
import Control.Monad
import Data.Functor
import System._Unsafe
import iTasks.UI.JavaScript
js :: !(*JSWorld -> (a, *JSWorld)) -> JS st a
js f = JS \st
# (x,w) = (unsafeCoerce f) st.jsworld
# st & jsworld = w
-> (x,st)
runJS :: !st !JSVal !(JS st a) !*JSWorld -> (a, *JSWorld)
runJS state component (JS f) w = (unsafeCoerce \w
# st =
{ jsworld = w
, component = component
, state = state
}
# (x,st) = f st
-> (x,st.jsworld)) w
gets :: !((JSState st) -> a) -> JS st a
gets f = JS \st -> (f st,st)
modState :: !((JSState st) -> JSState st) -> JS st (JSState st)
modState f = JS \st -> let st` = f st in (st`,st`)
instance Functor (JS st)
where
fmap f (JS g) = JS \w -> let (r,w`) = g w in (f r,w`)
instance pure (JS st)
where
pure x = JS \w -> (x,w)
instance <*> (JS st)
where
<*> (JS f) (JS g) = JS
\w
# (f,w) = f w
# (x,w) = g w
-> (f x,w)
instance Monad (JS st)
where
bind (JS f) g = JS
\w
# (x,w) = f w
# (JS f) = g x
-> f w
(`then`) infixl 1 :: !(JS st JSPromise) !(JSVal -> JS st JSVal) -> JS st JSPromise
(`then`) first then =
gets id >>= \st ->
js (jsWrapFunWithResult (\args w -> runJS st.state st.component (then args.[0]) w) st.component) >>= \then ->
first >>= \promise ->
js (promise .# "then" .$ (then, jsGlobal "(e) => console.warn ('Promise failed (%s): %s',e.name,e.message)"))
resolvePromise :: JS st JSPromise
resolvePromise = js (jsGlobal "Promise" .# "resolve" .$ ())
......@@ -8,7 +8,6 @@
<!-- ABC interpreter -->
<script type="text/javascript" src="/js/abc-interpreter.js"></script>
<script type="text/javascript" src="/js/itasks-abc-interpreter.js"></script>
<!-- iTasks framework -->
<script type="text/javascript" src="/js/itasks-core.js"></script>
......
"use strict";
var ABC=null;
const ABC_loading_promise=ABCInterpreter.instantiate({
bytecode_path: '/js/app.pbc',
heap_size: 8<<20,
stack_size: 512<<10,
encoding: 'utf-8',
util_imports: {
has_host_reference: function (index) {
if (index>=ABC.shared_clean_values.length)
return 0;
if (ABC.shared_clean_values[index]==null)
return -1;
return ABC.shared_clean_values[index].ref;
},
update_host_reference: function (index, new_location) {
ABC.shared_clean_values[index].ref=new_location;
},
},
interpreter_imports: {
handle_illegal_instr: function (pc, instr, asp, bsp, csp, hp, hp_free) {
if (ABCInterpreter.instructions[instr]=='instruction') {
const arg=ABC.memory_array[(pc+8)/4];
switch (arg) {
case 0: /* evaluation finished */
return 0;
case 1: /* iTasks.UI.JS.Interface: set_js */
var v=ABC.get_clean_string(ABC.memory_array[asp/4], true);
var x=ABC.get_clean_string(ABC.memory_array[asp/4-2], true);
if (ABC_DEBUG)
console.log(v,'.=',x);
try {
var ref=eval(v+'.shared_clean_value_index');
if (typeof ref != 'undefined') {
if (ABC_DEBUG)
console.log('removing old reference to Clean',ref);
ABC.clear_shared_clean_value(ref);
}
} catch (e) {}
Function(v+'='+x)();
break;
case 2: /* iTasks.UI.JS.Interface: eval_js */
var string=ABC.get_clean_string(ABC.memory_array[asp/4], true);
if (ABC_DEBUG)
console.log('eval',string);
Function(string)();
break;
case 3: /* iTasks.UI.JS.Interface: eval_js_with_return_value */
var string=ABC.get_clean_string(ABC.memory_array[asp/4], true);
if (ABC_DEBUG)
console.log('eval',string);
var result=eval('('+string+')'); // the parentheses are needed for {}, for instance
var copied=ABC.copy_js_to_clean(result, asp);
ABC.interpreter.instance.exports.set_hp(copied.hp);
ABC.interpreter.instance.exports.set_hp_free(copied.hp_free);
break;
case 4: /* iTasks.UI.JS.Interface: share */
var attach_to=ABC.memory_array[bsp/4];
var index=ABC.share_clean_value(ABC.memory_array[asp/4],ABC.js[attach_to]);
ABC.memory_array[bsp/4]=index;
break;
case 5: /* iTasks.UI.JS.Interface: fetch */
var index=ABC.memory_array[bsp/4];
ABC.memory_array[asp/4]=ABC.shared_clean_values[index].ref;
break;
case 6: /* iTasks.UI.JS.Interface: deserialize */
var hp_ptr=ABC.memory_array[asp/4];
ABC.memory_array[asp/4]=ABC.deserialize_from_unique_string(hp_ptr);
break;
case 7: /* iTasks.UI.JS.Interface: initialize_client in wrapInitUIFunction */
var array=ABC.memory_array[asp/4]+24;
ABC.addresses.JSInt= ABC.memory_array[ABC.memory_array[array/4]/4];
ABC.addresses.JSBool= ABC.memory_array[ABC.memory_array[array/4+2]/4];
ABC.addresses.JSString= ABC.memory_array[ABC.memory_array[array/4+4]/4];
ABC.addresses.JSReal= ABC.memory_array[ABC.memory_array[array/4+6]/4];
ABC.addresses.JSNull= ABC.memory_array[ABC.memory_array[array/4+8]/4];
ABC.addresses.JSUndefined=ABC.memory_array[ABC.memory_array[array/4+10]/4];
ABC.addresses.JSArray= ABC.memory_array[ABC.memory_array[array/4+12]/4];
ABC.addresses.JSRef= ABC.memory_array[ABC.memory_array[array/4+14]/4];
ABC.addresses.JSCleanRef= ABC.memory_array[ABC.memory_array[array/4+16]/4];
ABC.util.instance.exports.set_js_ref_constructor(ABC.addresses.JSRef);
ABC.initialized=true;
break;
case 10: /* iTasks.UI.JS.Interface: add CSS */
var url=ABC.get_clean_string(ABC.memory_array[asp/4], false);
var css=document.createElement('link');
css.rel='stylesheet';
css.type='text/css';
css.async=true;
css.href=url;
document.head.appendChild(css);
break;
case 11: /* iTasks.UI.JS.Interface: add JS */
var url=ABC.get_clean_string(ABC.memory_array[asp/4], false);
var callback=ABC.get_clean_string(ABC.memory_array[asp/4-2], true);
var js=document.createElement('script');
js.type='text/javascript';
js.async=false;
if (callback.length>0)
js.onload=Function(callback+'();');
document.head.appendChild(js);
js.src=url;
break;
default:
throw new ABCError('unknown instruction',arg);
}
return pc+16;
}
return 0;
},
illegal_instr: function (addr, instr) {
ABC.empty_log_buffer();
if (ABCInterpreter.instructions[instr]=='instruction')
/* `instruction 0` ends the interpretation, so this is no error */
return;
throw new ABCError('illegal instruction',instr);
},
},
}).then(function(instance){
ABC=instance;
ABC.initialized=false;
// Overwrite ap to return a result (in the case of jsWrapFunWithResult)
ABC.ap=function(index){
var f=function () {
var args=[];
for (var i=0; i<arguments.length; i++)
args[i]=arguments[i];
ABC.interpret(new SharedCleanValue(index), args);
var result=undefined;
const new_asp=ABC.interpreter.instance.exports.get_asp();
const hp_ptr=ABC.memory_array[new_asp/4];
if (ABC.memory_array[hp_ptr/4]!=25*8+2) { // INT, i.e. JSWorld
// Assume we have received a tuple with the first element as the result
const str_ptr=ABC.memory_array[hp_ptr/4+2];
const string=ABC.get_clean_string(ABC.memory_array[str_ptr/4+2], false);
if (ABC_DEBUG)
console.log('result:',string);
result=eval('('+string+')');
}
if (typeof result!='undefined')
return result;
};
f.shared_clean_value_index=index;
return f;
};
});
//Global itasks namespace
itasks = {};
const ABC_loading_promise=ABCInterpreter.instantiate({
bytecode_path: '/js/app.pbc',
heap_size: 8<<20,
stack_size: 512<<10,
encoding: 'utf-8',
with_js_ffi: true,
}).then(instance => { itasks.ABC=instance; });
//Auxiliary definitions for sending Maybe values to server
const Nothing = ["Nothing"];
function Just(x) { return["Just", x]; }
......@@ -44,10 +55,10 @@ itasks.Component = {
var me=this;
if (me.attributes.initUI!=null && me.attributes.initUI!='') {
return ABC_loading_promise.then(function(){
var initUI=ABC.deserialize(atob(me.attributes.initUI));
var ref=ABC.share_clean_value(initUI,me);
ABC.interpret(new SharedCleanValue(ref), [me, ABC.initialized ? 0 : 1]);
ABC.clear_shared_clean_value(ref);
var initUI=itasks.ABC.deserialize(atob(me.attributes.initUI));
var ref=itasks.ABC.share_clean_value(initUI,me);
itasks.ABC.interpret(new SharedCleanValue(ref), [me, itasks.ABC.initialized ? 0 : 1]);
itasks.ABC.clear_shared_clean_value(ref);
});
}
},
......@@ -280,7 +291,7 @@ itasks.Component = {
if (this.shared_clean_values!=null) {
// garbage collect any remaining values shared with wasm
this.shared_clean_values.forEach(ref => ABC.clear_shared_clean_value(ref,false));
this.shared_clean_values.forEach(ref => itasks.ABC.clear_shared_clean_value(ref,false));
this.shared_clean_values.clear();
}
......