Add basic virtual machine concept
This commit is contained in:
parent
50df9efabc
commit
4eced65dbf
|
@ -0,0 +1,65 @@
|
||||||
|
import { Action } from './action.js';
|
||||||
|
|
||||||
|
class ContractRecord {
|
||||||
|
constructor(id, instance) {
|
||||||
|
this.id = id;
|
||||||
|
this.instance = instance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class VMHandle {
|
||||||
|
constructor(vm, sender) {
|
||||||
|
this.vm = vm;
|
||||||
|
this.sender = sender;
|
||||||
|
this.actions = {
|
||||||
|
call: new Action('call'),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} id Contract ID
|
||||||
|
* @param {string} method
|
||||||
|
*/
|
||||||
|
callContract(id, method, ...args) {
|
||||||
|
const instance = this.vm.getContractInstance(id);
|
||||||
|
const fn = instance[method];
|
||||||
|
if (!fn) throw new Error(`Contract ${id} method ${method} not found!`);
|
||||||
|
this.actions.call.log(this.sender, instance, method);
|
||||||
|
return fn.call(instance, this.sender, ...args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class VM {
|
||||||
|
constructor() {
|
||||||
|
this.contracts = new Map();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} id
|
||||||
|
* @param {class} ContractClass
|
||||||
|
* @param {any[]} ...args Passed to contractClass constructor after `vm`
|
||||||
|
*/
|
||||||
|
addContract(id, ContractClass, ...args) {
|
||||||
|
const instance = new ContractClass(this, ...args);
|
||||||
|
const contract = new ContractRecord(id, instance);
|
||||||
|
this.contracts.set(id, contract);
|
||||||
|
}
|
||||||
|
|
||||||
|
getHandle(sender) {
|
||||||
|
return new VMHandle(this, sender);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} id
|
||||||
|
*/
|
||||||
|
getContract(id) {
|
||||||
|
return this.contracts.get(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} id
|
||||||
|
*/
|
||||||
|
getContractInstance(id) {
|
||||||
|
return this.getContract(id)?.instance;
|
||||||
|
}
|
||||||
|
}
|
|
@ -24,8 +24,8 @@
|
||||||
</ul>
|
</ul>
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="./tests/forum-network.test.html">Forum Network</a></li>
|
<li><a href="./tests/forum-network.test.html">Forum Network</a></li>
|
||||||
|
<li><a href="./tests/vm.test.html">VM</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<h3>Tertiary</h3>
|
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="./tests/basic.test.html">Basic Sequencing</a></li>
|
<li><a href="./tests/basic.test.html">Basic Sequencing</a></li>
|
||||||
<li><a href="./tests/wdag.test.html">WDAG</a></li>
|
<li><a href="./tests/wdag.test.html">WDAG</a></li>
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
import { Actor } from '../../classes/actor.js';
|
||||||
|
import { Box } from '../../classes/box.js';
|
||||||
|
import { Scene } from '../../classes/scene.js';
|
||||||
|
import { VM } from '../../classes/vm.js';
|
||||||
|
|
||||||
|
const rootElement = document.getElementById('scene');
|
||||||
|
const rootBox = new Box('rootBox', rootElement).flex();
|
||||||
|
window.scene = new Scene('VM test', rootBox).withSequenceDiagram();
|
||||||
|
|
||||||
|
const testContractId = 'test-contract-id';
|
||||||
|
class TestContract extends Actor {
|
||||||
|
constructor(vm, value) {
|
||||||
|
super('TestContract');
|
||||||
|
this.vm = vm;
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
hello(sender) {
|
||||||
|
return `${sender.name} ${this.value}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('VM', () => {
|
||||||
|
let vm;
|
||||||
|
let sender;
|
||||||
|
let vmHandle;
|
||||||
|
|
||||||
|
before(() => {
|
||||||
|
vm = (window.vm = new VM());
|
||||||
|
sender = new Actor('Sender');
|
||||||
|
vm.addContract(testContractId, TestContract, 'world');
|
||||||
|
vmHandle = vm.getHandle(sender);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should exist', () => {
|
||||||
|
should.exist(vm);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Call a contract method', () => {
|
||||||
|
vmHandle.callContract(testContractId, 'hello').should.equal('Sender world');
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,32 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<title>VM</title>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<link rel="stylesheet" href="https://unpkg.com/mocha/mocha.css" />
|
||||||
|
<link type="text/css" rel="stylesheet" href="../index.css" />
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<h2><a href="../">DGF Tests</a></h2>
|
||||||
|
<div id="mocha"></div>
|
||||||
|
<div id="scene"></div>
|
||||||
|
</body>
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/radash/10.7.0/radash.js"
|
||||||
|
integrity="sha512-S207zKWG3iqXqe6msO7/Mr8X3DzzF4u8meFlokHjGtBPTGUhgzVo0lpcqEy0GoiMUdcoct+H+SqzoLsxXbynzg=="
|
||||||
|
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||||
|
<script src="https://unpkg.com/mocha/mocha.js"></script>
|
||||||
|
<script src="https://unpkg.com/chai/chai.js"></script>
|
||||||
|
<script type="module" src="./scripts/vm.test.js"></script>
|
||||||
|
<script defer class="mocha-init">
|
||||||
|
mocha.setup({
|
||||||
|
ui: 'bdd',
|
||||||
|
globals: ['vm', '__REACT_DEVTOOLS_*'],
|
||||||
|
});
|
||||||
|
mocha.checkLeaks();
|
||||||
|
window.should = chai.should();
|
||||||
|
</script>
|
||||||
|
<script defer class="mocha-exec">
|
||||||
|
// TODO: Weird race condition -- resolve this in a better way
|
||||||
|
setTimeout(() => mocha.run(), 1000);
|
||||||
|
</script>
|
Loading…
Reference in New Issue