godprotocol
Version:
A distributed computing environment for Web 4.0 — integrating AI, decentralisation, and virtual computation.
478 lines (422 loc) • 14.7 kB
JavaScript
import create_server from "../utils/create_server.js";
import Account from "./Account.js";
import GDS from "generalised-datastore";
import { hash } from "godprotocol/utils/hash.js";
import { copy_object } from "generalised-datastore/utils/functions.js";
import Clock from "./Clock.js";
class Manager extends Clock {
constructor(name, options) {
super();
this.name = name;
this.options = options;
this.trinity = options.trinity;
this.accounts = new Object();
this.clock_speed = 0;
this.accounts_pairs = new Object();
}
swap_result = async (id, thread) => {
if (typeof id === "number") {
id = thread.results[id];
}
return id;
};
set_bullet = async (bullet, thread) => {
switch (bullet.type) {
case "assignment":
bullet.value = await this.swap_result(bullet.value, thread);
break;
case "call":
bullet.identifier = await this.swap_result(bullet.identifier, thread);
for (let a = 0; a < bullet.arguments.length; a++) {
bullet.arguments[a] = await this.swap_result(
bullet.arguments[a],
thread
);
}
break;
case "return":
bullet.output = await this.swap_result(bullet.output, thread);
break;
case "module":
for (let b = 0; b < bullet.body.length; b++) {
bullet.body[b] = await this.swap_result(bullet.body[b], thread);
}
break;
case "nest":
for (let n = 0; n < bullet.nests.length; n++) {
bullet.nests[n] = await this.swap_result(bullet.nests[n], thread);
}
if (bullet.assignment) {
bullet.assignment = await this.swap_result(bullet.assignment, thread);
}
break;
case "twain":
for (let key in bullet.value) {
let [prop, value] = bullet.value[key];
bullet.value[key][0] = await this.swap_result(prop, thread);
bullet.value[key][1] = await this.swap_result(value, thread);
}
break;
case "array":
for (let i = 0; i < bullet.value.length; i++) {
let val = bullet.value[i];
bullet.value[i] = await this.swap_result(val, thread);
}
break;
case "variable":
break;
case "try":
}
return bullet;
};
_stack = new Array();
break_count = 0;
start_clock = async () => {
this.clock = setInterval(async () => {
this.clock_running = true;
for (let account in this.accounts_pairs) {
let account_pair = this.accounts_pairs[account];
let { threads, threadlist, state, current_thread } = account_pair;
if (state === "running") continue;
let total_loop = 0;
while (threadlist.length && total_loop < threadlist.length) {
let thread_id = threadlist[account_pair.current_thread];
let thread = threads[thread_id];
if (!thread) {
account_pair.current_thread = 0;
continue;
}
let {
bullets,
pointer,
callback,
results,
chain,
spawn,
status,
_stack,
on_pointer,
} = thread;
if (status && status.start) {
if (Date.now() - status.start > status.duration) {
thread.status = "active";
}
}
if (status === "active") {
account_pair.current_thread++;
account_pair.state = "running";
} else {
total_loop++;
account_pair.current_thread++;
continue;
}
total_loop = 0;
let current_bullet = bullets[pointer],
res;
if (current_bullet) {
if (current_bullet.type === "loop") {
_stack.push(current_bullet);
} else if (current_bullet.type === "try") {
_stack.push(current_bullet);
} else if (current_bullet.type === "catch") {
let try_conf = _stack.pop();
thread.pointer += try_conf.catch_offset;
} else if (current_bullet.type === "endloop") {
let loop_conf = _stack.pop();
if (!loop_conf || loop_conf.type !== "loop") {
console.log(`ErrorLog: Corrupt stacking`);
thread.pointer = bullets.length;
} else thread.pointer = loop_conf.conditional_jmp;
} else {
let account_obj = this.accounts[account];
if (Object.keys(current_bullet).includes("jmp")) {
} else
res = await account_obj.vm.execute(
this.set_bullet(copy_object(current_bullet), thread),
{ chain, manager: this, thread }
);
if (res && res.__interrupt__) {
if (res.type === "return") {
if (spawn) {
let thrd = threads[spawn.thread_id];
thrd.results[spawn.pointer] = res.output;
thread.pointer = bullets.length - 1;
} else res = res.output;
} else if (["break", "continue"].includes(res.__interrupt__)) {
let caught;
while (_stack.length) {
let loop = _stack.slice(-1)[0];
if (loop && loop.type === "loop") {
if (res.__interrupt__ === "break") {
thread.pointer = loop.conditional_jmp - 1;
} else if (res.__interrupt__ === "continue") {
thread.pointer = loop.update_jmp - 1;
}
caught = true;
break;
}
_stack.pop();
}
if (!caught) {
console.log(
`ErrorLog: Loop keywords can only be used inside a loop`
);
thread.pointer = bullets.length;
}
} else {
let i = _stack.length - 1,
caught;
while (_stack.length) {
let tstack = _stack[i];
i--;
let t_catch = bullets[tstack.catch_index];
if (
!t_catch.objects.length ||
t_catch.objects.includes(res.payload.type)
) {
thread.pointer = tstack.catch_index;
if (tstack.alias)
await account_obj.vm.error_instance(
{ location: tstack.location, payload: res.payload },
tstack.alias
);
_stack.pop();
caught = true;
break;
}
_stack.pop();
}
if (!caught) {
console.log(`ErrorLog: ${res.payload.message}`);
if (spawn) {
let thrd = threads[spawn.thread_id];
thrd.results[spawn.pointer] = res;
}
thread.pointer = bullets.length;
}
}
} else {
if (current_bullet.type === "assignment") {
bullets.map((bull) => {
if (
bull.type === "variable" &&
bull.location === current_bullet.location
) {
bull._id = res._id;
}
});
}
if (
current_bullet.type === "nest" &&
current_bullet.assignment
) {
if (current_bullet.assignment_location) {
bullets.map((bull) => {
if (
bull.type === "variable" &&
bull.location === current_bullet.assignment_location
) {
bull._id = res._id;
let trd = thread;
while (trd.spawn) {
let tid = trd.spawn.thread_id;
trd = threads[tid];
trd.bullets.map((bl) => {
if (
bl.type === "variable" &&
bl.location === current_bullet.assignment_location
) {
bl._id = res._id;
}
});
}
}
});
}
}
if (current_bullet.conditional_jmp) {
if (!res.literal) {
console.log("Manager.js::SHOUT!");
}
let pass = await res.literal();
if (current_bullet.invert) {
if (!pass) {
thread.pointer = current_bullet.conditional_jmp - 1;
}
} else {
if (!pass) {
thread.pointer = current_bullet.conditional_jmp - 1;
}
}
}
if (current_bullet.jmp) {
thread.pointer = current_bullet.jmp - 1;
}
}
results[pointer] = res;
on_pointer && (await on_pointer(pointer, res));
}
}
thread.pointer++;
if (thread.pointer >= bullets.length) {
callback && (await callback(res, thread));
delete threads[thread_id];
delete this.accounts[account].vm.envs[thread_id];
account_pair.current_thread--;
threadlist.splice(account_pair.current_thread, 1);
if (spawn) {
let thrd = threads[spawn.thread_id];
if (thrd) {
thrd.status = "active";
}
}
}
if (account_pair.current_thread === threadlist.length) {
account_pair.current_thread = 0;
}
account_pair.state = "active";
break;
}
if (!threadlist.length) {
delete this.accounts_pairs[account];
if (!Object.keys(this.accounts_pairs).length) {
this.clock_running = false;
clearInterval(this.clock);
}
}
}
}, this.clock_speed);
};
load = async (thread, account, options = {}) => {
let { callback, on_pointer, spawn, addr, pointer } = options;
let tracker = this.accounts_pairs[account.name];
if (!tracker) {
tracker = {
threadlist: [],
threads: {},
current_thread: 0,
};
this.accounts_pairs[account.name] = tracker;
}
let thread_id = Math.random().toString();
tracker.threadlist.push(thread_id);
tracker.threads[thread_id] = {
bullets: thread,
_id: thread_id,
on_pointer,
pointer: 0,
results: {},
_stack: new Array(),
status: "active",
addr,
chain: account.chain,
spawn: spawn ? { thread_id: spawn, pointer } : null,
callback,
};
if (spawn) {
let spawn_thread = tracker.threads[spawn];
spawn_thread.status = thread_id;
}
if (!this.clock_running) {
await this.start_clock();
}
return thread_id;
};
sync = async () => {
this.path = `${this.trinity.game}/${this.name}`;
this.hash = hash;
this.ds = new GDS(this);
await this.ds.sync();
if (this.options.server) {
this.server = this.options.server;
if (this.options.server.port)
this.options.server_domain = `${this.options.server.hostname}:${this.options.server.port}`;
else this.options.server_domain = this.options.server.hostname;
let handle = create_server({
manager: this,
...this.options.server,
app: this.options.app,
});
this.handler = async (req, res, cb) => {
handle(req, res);
if (this.options.oracle && !this.served) {
this.served = true;
this.oracle = this.options.oracle;
if (this.options.init_account) {
console.log(
"Initiate manager account...",
this.options.init_account.name
);
await this.add_account(this.options.init_account.name, {
password: this.options.init_account.password,
init: true,
});
cb && cb();
}
}
};
}
};
get_init = async () => {
let acc = this.init_account;
if (!acc && this.options.init_account) {
acc = await this.add_account(this.options.init_account.name, {
password: this.options.init_account.password,
init: true,
});
}
return acc;
};
get_acc = (options) => {
let acc = options.account;
if (!acc) {
acc = this.options.init_account;
if (!acc) return;
acc = `Accounts/${acc.name}`;
}
return acc;
};
get_account = async (name, password) => {
let acc = this.accounts[name];
if (!acc) {
let servers = await this.oracle.read(`Accounts/${name}/.servers`);
servers = servers ? JSON.parse(servers) : [];
if (servers.length) {
acc = await this.add_account(name, { servers, password });
if (!acc) return password ? "Invalid Password" : null;
} else return password ? "Account not found." : null;
} else if (password) {
let res = await acc.verify(password);
if (!res) return "Invalid Password";
}
return acc;
};
add_account = async (name, options = {}) => {
let acc = this.accounts[name];
if (!acc) {
acc = new Account(name, { ...options, manager: this });
let res = await acc.sync();
if (res) this.accounts[name] = acc;
else return;
}
if (options.init) {
this.init_account = acc;
}
return acc;
};
folder = async (address, options = {}) => {
let acc;
if (options.account) {
acc = `Accounts/${options.account}`;
} else if (this.options.init_account) {
acc = `Accounts/${this.options.init_account.name}`;
}
address = `${acc}/${address}`;
let folder = await this.ds.folder(address, {
...options,
account: acc,
});
return folder;
};
}
export default Manager;