Source: org/terraswarm/accessor/accessors/web/hosts/duktape/duktapeHost.js

// The modSearch function is based on code from http://wiki.duktape.org/HowtoModules.html
// The license is at https://github.com/svaarala/duktape-wiki, which refers to
// https://github.com/svaarala/duktape/blob/master/LICENSE.txt, which is reproduced below

//    ===============
//    Duktape license
//    ===============

// (http://opensource.org/licenses/MIT)
// Copyright (c) 2016-2017 The Regents of the University of California.
// All rights reserved.
//
// Permission is hereby granted, without written agreement and without
// license or royalty fees, to use, copy, modify, and distribute this
// software and its documentation for any purpose, provided that the above
// copyright notice and the following two paragraphs appear in all copies
// of this software.
//
// IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
// FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
// ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
// THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
// SUCH DAMAGE.
//
// THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
// PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
// CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
// ENHANCEMENTS, OR MODIFICATIONS.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE

/** An accessor host for small embedded systems that uses the Duktape JavaScript
 *  interpreter.
 *
 *  See [https://accessors.org/accessors/wiki/Main/DuktapeHost](https://accessors.org/wiki/Main/DuktapeHost).
 *
 *  @module @accessors-hosts/duktapeHost
 *  @author Christopher Brooks
 *  @version $$Id$$
 */
// FIXME: Move the Duktape code elsewhere so as to avoid copyright issues.
/** Search for a module.
 */
Duktape.modSearch = function (id, require, exports, module) {
    /* readFile(): as above.
     * loadAndInitDll(): load DLL, call its init function, return true/false.
     */
    var name;
    var src;
    var found = false;

    // print('loading module: ' + id);

    /* DLL check.  DLL init function is platform specific.  It gets 'exports'
     * but also 'require' so that it can require further modules if necessary.
     */
    // name = '/modules/' + id + '.so';
    // if (loadAndInitDll(name, require, exports, module)) {
    //     print('loaded DLL:', name);
    //     found = true;
    // }

    /* Ecmascript check. */
    //name = 'modules/' + id + '.js';
    if (id.indexOf(".js", id.length - 3) !== -1) {
        name = id;
    } else {
        name = id + '.js';
    }

    if (name.indexOf('@accessors-modules/') !== -1) {
        name = name.substring(19);
    }

    // Iterate through a series of search paths.
    // common/modules is needed for common/modules/events.js.
    var prefixes = [ '', 'modules/', 'common/modules/' ];
    for (var i = 0; i < prefixes.length; i++) {
        var prefixName = prefixes[i] + name;
        // console.log('duktapeHost.js: about to look for ' + prefixName);
        try {
            // Use NoFileIo instead of FileIo because small embedded systems
            // don't have file systems.
            src = NoFileIo.readfile(prefixName);
        } catch (error) {
            try {
                // Rusteduk has FileIo, not NoFileIo.
                src = FileIo.readfile(prefixName);
            } catch (error) {
                // Ignore.
            }
        }
        if (typeof src !== 'undefined' && src.length > 0) {
            break;
        }
    }

    // print('readFile returned', src);
    // print('src is of type', typeof src);
    if (typeof src === 'string') {
        // print('loaded Ecmascript:', name);
        return src;
    } else if (typeof src === 'buffer') {
        // print('loaded Ecmascript:', name);
        return src.toString();
    } else if (typeof src === 'object') {
        // Duktape 2.0 returns objects.
        // print('loaded Ecmascript:' + name + ", src is of type object");
        // print('loaded Ecmascript:' + name + ", src.length(): " + src.length);
        return src;
    }

    /* Must find either a DLL or an Ecmascript file (or both) */
    if (!found) {
        throw new Error('module not found: ' + id);
    }

    /* For pure C modules, 'src' may be undefined which is OK. */
    return src;
};

// We expect to run duk from the hosts directory.  See
// https://www.icyphy.org/accessors/wiki/Main/DuktapeHost#RequireModuleID
var commonHost = require("common/commonHost");

// This host allows trusted accessors, which means that any
// accessor whose class name begins with 'trusted/' can invoke the
// function getTopLevelAccessors().
commonHost.allowTrustedAccessors(true);

// Duktape does not have path nor fs modules.
//var path = require('path');
//var fs = require('fs');

/** Module variable giving the paths to search for accessors.
 *  By default this module assumes that accessors are stored in
 *  __dirname/../.., where __dirname is the directory where this
 *  script is located.
 */
//var accessorPath = [path.join(__dirname, '..', '..')];
var accessorPath = ['.', '..', '../..'];
/** Return the source code for an accessor from its fully qualified name.
 *  This will throw an exception if there is no such accessor on the accessor
 *  search path.
 *  @param name Fully qualified accessor name, e.g. 'net/REST'.
 */
function getAccessorCode(name) {
    var code;
    // Append a '.js' to the name, if needed.
    if (name.indexOf('.js') !== name.length - 3) {
        name += '.js';
    }
    for (var i = 0; i < accessorPath.length; i++) {
        //var location = path.join(accessorPath[i], name);
        var location = accessorPath[i] + "/" + name;
        try {
            //print('Reading accessor at: ' + location);
            //code = fs.readFileSync(location, 'utf8');
            //print("duktapeHost.js getAccessorCode(\"" + name + "\"): Reading using NoFileIo" + location);
            code = NoFileIo.readfile(location);
            break;
        } catch (error) {
            // Rusteduk has FileIo
            try {
                code = FileIo.readfile(location);
            } catch (error) {
                //print("duktapeHost.js getAccessorCode(\"" + name + "\"): error reading " + location + ": " + error);
                continue;
            }
        }
    }
    if (!code) {
        throw ('Accessor ' + name + ' not found on path: ' + accessorPath);
    }
    return code;
}

/** Get a resource.
 *
 *  @param uri A specification for the resource.
 */
getResource = function (uri) {
    // See nodeHost.js for an implementation.
    throw new Error('getResource(' + uri + ') not yet implemented');
};

/** Return the name of this host.
 *
 *  Return the string "Duktape".
 *
 *  @return In duktapeHost.js, return "Duktape".
 */ 
getHostName = function() {
    return "Duktape";
};

/** Instantiate and return an accessor.
 *  This will throw an exception if there is no such accessor class on the accessor
 *  search path.
 *  @param accessorName The name to give to the instance.
 *  @param accessorClass Fully qualified accessor class name, e.g. 'net/REST'.
 */
instantiate = function (accessorName, accessorClass) {
    // FIXME: The bindings should be a bindings object where require == a requireLocal
    // function that searches first for local modules.
    var bindings = {
        'require': require,
        'clearInterval': clearInterval,
        'clearTimeout': clearTimeout,
        'setInterval': setInterval,
        'setTimeout': setTimeout,
    };
    var result = new commonHost.instantiateAccessor(
        accessorName, accessorClass, getAccessorCode, bindings);
    //print('duktapeHost.js: instantiate() done: Instantiated accessor ' + accessorName + ' with class ' + accessorClass);
    return result;
};

/** If there are one or more arguments after duktapeHostInvoke.js, then
 * assume that each argument names a file defining an accessor.  Each
 * accessor is instantiated and initialized.
 *
 * See duktapeHostInvoke.js for a file that requires duktapeHost.js
 * and then invokes this method.
 *
 * Sample usage:
 *
 * duktapeHostInvoke.js contains:
 * <pre>
 * var commonHost = require('./duktapeHost.js');
 * // Remove "node.js" from the array of command line arguments.
 * process.argv.shift();
 * // Remove "duktapeHostInvoke.js" from the array of command line arguments.
 * process.argv.shift();
 * instantiateAndInitialize(process.argv);
 * </pre>
 *
 * To invoke:
 * <pre>
 *   node duktapeHostInvoke.js test/TestComposite
 * </pre>
 *
 * @param accessorNames An array of accessor names in a format suitable
 * for getAccessorCode(name).
 */
instantiateAndInitialize = function (accessorNames) {
    // console.log("duktapeHost.js: instantiateAndInitialize() start: " + accessorNames + " " + accessorNames.length);

    var length = accessorNames.length;
    for (index = 0; index < length; ++index) {
        // The name of the accessor is basename of the accessorClass.
        var accessorClass = accessorNames[index];
        // print("duktapeHost.js: instantiateAndInitialize(): about to handle " + accessorClass);

        // For example, if the accessorClass is
        // test/TestComposite, then the accessorName will be
        // TestComposite.

        // FIXME: this will cause problems under Windows
        var startIndex = accessorClass.lastIndexOf('/');
        var accessorName = accessorClass.substring(startIndex);
        if (accessorName.indexOf('\\') === 0 || accessorName.indexOf('/') === 0) {
            accessorName = accessorName.substring(1);
        }
        // If the same accessorClass appears more than once in the
        // list of arguments, then use different names.
        // To replicate: duk duktape/duktapeHostInvoke.js test/TestComposite test/TestComposite
        if (index > 0) {
            accessorName += "_" + (index - 1);
        }
        // print("duktapeHost.js: instantiateAndInitialize(): about to call instantiate(" + accessorName + ", " + accessorClass + ")");
        var accessor = instantiate(accessorName, accessorClass);
        // print("duktapeHost.js: instantiateAndInitialize(): about to call initialize on " + accessor);
        accessor.initialize();
        // print("duktapeHost.js: instantiateAndInitialize(): done with " + accessorClass);
    }
    // print("duktapeHost.js: instantiateAndInitialize() done");
};

// Make the Accessor constructor visible so that we may use it in the
// Cape Code Accessor Code Generator.
// See https://www.icyphy.org/accessors/wiki/Main/ResourcesForHostAuthors#ExportAccessor
Accessor = commonHost.Accessor;

// Define additional functions that should appear in the global scope
// so that they can be invoked on the command line.
provideInput = commonHost.provideInput;
setParameter = commonHost.setParameter;
isReifiableBy = commonHost.isReifiableBy;

////////////////////////////////////////
// Duktape host-specific require() calls and functions should go below here.

// Note that the version of ecma_eventloop.js that ships with duktape
// was modified so that clearInterval, clearTimer, setInterval and
// setTimer are all in the global scope.
//
// FIXME: It would be nice if we could use the unmodified version of
// ecma_eventloop.js.  How do we make functions declared in that file
// visible in the global scope?
//

// If ecma_eventloop is required here, then using c_eventloop.js will fail.
//var ecma_eventloop = require('duktape/duktape/examples/eventloop/ecma_eventloop');

console = {
    log: function () {
        print(Array.prototype.join.call(arguments, ' '));
    }
};

// To print the contents of an object in Duktape, download json2.js
//   cd hosts/duktape
//   wget https://raw.githubusercontent.com/douglascrockford/JSON-js/master/json2.js
// and then uncomment the code below:
// var json = require("duktape/json2");
// console.log("commonHost is: " + JSON.stringify(commonHost));

// In case this gets used a module, create an exports object.
exports = {
    'Accessor': Accessor,
    // Don't export clearInterval, setInterval and setTimeout here because
    // we no longer require ecma_eventloop.js above.
    //'clearInterval': clearInterval,
    'getTopLevelAccessors': commonHost.getTopLevelAccessors,
    'getHostName': getHostName,
    'instantiate': instantiate,
    'instantiateAndInitialize': instantiateAndInitialize,
    'provideInput': commonHost.provideInput,
    'setParameter': commonHost.setParameter,

    //'setInterval': setInterval,
    //'setTimeout': setTimeout,
};