Add basic virtual machine concept

This commit is contained in:
Ladd Hoffman 2023-02-02 21:54:37 -06:00
parent 50df9efabc
commit 4eced65dbf
4 changed files with 140 additions and 1 deletions

View File

@ -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;
}
}

View File

@ -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>

View File

@ -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');
});
});

View File

@ -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>