Skip to content

Commit

Permalink
Begin reorganizing the kernel with kernel-space typescript fs
Browse files Browse the repository at this point in the history
  • Loading branch information
ElvishJerricco committed Aug 1, 2018
1 parent 09d501e commit 1ad25a4
Show file tree
Hide file tree
Showing 9 changed files with 388 additions and 0 deletions.
9 changes: 9 additions & 0 deletions example/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<!doctype html>
<html>
<head>
<title>Getting Started</title>
</head>
<body>
<script src="./build/main.js"></script>
</body>
</html>
19 changes: 19 additions & 0 deletions example/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"name": "webabi-example",
"version": "0.0.1",
"description": "",
"scripts": {
"build": "tsc",
"install": "npm run build",
"test": "npm run build && webpack && node build/main.js"
},
"author": "Will Fancher",
"dependencies": {
"typescript": "^2.9.2",
"webabi-kernel": "file:../kernel"
},
"devDependencies": {
"webpack": "^4.16.3",
"webpack-cli": "^3.1.0"
}
}
20 changes: 20 additions & 0 deletions example/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { Device, configureFileSystem, BFSCallback, Stats, File, FileFlag } from "webabi-kernel";

class JSaddleDevice implements Device {
open(flag: FileFlag, cb: BFSCallback<File>): void {
}
openSync(flag: FileFlag, mode: number): File {
throw "NYI";
}
stat(isLstat: boolean | null, cb: BFSCallback<Stats>): void {
}
statSync(isLstat: boolean | null): Stats {
throw "NYI";
}
}

configureFileSystem({ "/jsaddle": new JSaddleDevice() }, (err, fs) => {
console.log(err);
let buf = Buffer.from("hi\n");
fs.write(1, buf, 0, buf.length, null, () => {});
});
15 changes: 15 additions & 0 deletions example/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"compilerOptions": {
"target": "es6",
"outDir": "dist",
"lib": ["dom", "es2015", "es2016", "es2017"],
"module": "commonjs",
"declaration": true
},
"include": [
"src/**/*.ts"
],
"exclude": [
"node_modules"
]
}
16 changes: 16 additions & 0 deletions example/webpack.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
const path = require('path');

console.log(require.resolve("webabi-kernel"));

module.exports = {
entry: './dist/index.js',
mode: "production",
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'build')
},
resolve: {
// Using file:../kernel in package.json requires this
symlinks: false
}
};
26 changes: 26 additions & 0 deletions kernel/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"name": "webabi-kernel",
"version": "0.0.1",
"description": "",
"scripts": {
"build": "tsc",
"main": "npm run tsc && node dist/foo.js",
"install": "npm run build"
},
"main": "dist/index.js",
"author": "Will Fancher",
"dependencies": {
"@types/archiver": "^2.0.0",
"@types/async": "^2.0.49",
"@types/body-parser": "^1.16.4",
"@types/dropboxjs": "0.0.29",
"@types/express": "^4.0.36",
"@types/filesystem": "0.0.28",
"@types/isomorphic-fetch": "^0.0.34",
"@types/mocha": "^5.2.5",
"@types/node": "^7.0",
"@types/rimraf": "^2.0.2",
"browserfs": "^1.4.3",
"typescript": "^2.9.2"
}
}
98 changes: 98 additions & 0 deletions kernel/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import MountableFileSystem from "browserfs/dist/node/backend/MountableFileSystem";
import * as handles from "./stdio_handles";
import { BaseFileSystem, FileSystemConstructor, BFSCallback,
BFSOneArgCallback, BFSThreeArgCallback, FileSystem,
FileSystemOptions } from "browserfs/dist/node/core/file_system";
import { FileType } from 'browserfs/dist/node/core/node_fs_stats';
import Stats from 'browserfs/dist/node/core/node_fs_stats';
import { File } from "browserfs/dist/node/core/file";
import { FileFlag } from "browserfs/dist/node/core/file_flag";
import { ApiError, ErrorCode } from 'browserfs/dist/node/core/api_error';
import FS from "browserfs/dist/node/core/FS";

export interface Device {
open(flag: FileFlag, cb: BFSCallback<File>): void;
stat(isLstat: boolean | null, cb: BFSCallback<Stats>): void;
}

export interface DeviceFileSystemOptions {
devices: {[name: string]: Device};
}

export class DeviceFileSystem extends BaseFileSystem implements FileSystem {
public static readonly Name = "DeviceFileSystem";
public static readonly Options: FileSystemOptions = {};

public static Create(opts: DeviceFileSystemOptions, cb: BFSCallback<DeviceFileSystem>): void {
return cb(null, new DeviceFileSystem(opts));
}

public static isAvailable(): boolean {
return true;
}

options: DeviceFileSystemOptions;

constructor(options: DeviceFileSystemOptions) {
super();
this.options = options;
}

public getName() {
return "DeviceFileSystem";
}
public isReadOnly() {
return false;
}
public supportsProps() {
return false;
}
public supportsSynch() {
return false;
}

public openFile(p: string, flag: FileFlag, cb: BFSCallback<File>): void {
if (this.options.devices.hasOwnProperty(p)) {
return this.options.devices[p].open(flag, cb);
} else {
return cb(ApiError.ENOENT(p));
}
}
public stat(p: string, isLstat: boolean | null, cb: BFSCallback<Stats>): void {
if (this.options.devices.hasOwnProperty(p)) {
return this.options.devices[p].stat(isLstat, cb);
} else {
return cb(ApiError.ENOENT(p));
}
}
}

export function configureFileSystem(devices: { [name: string]: Device }, cb: BFSCallback<FS>): void {
DeviceFileSystem.Create({ devices: devices }, (e, dfs) => {
if (e) {
cb(e);
return;
}
MountableFileSystem.Create({
"/dev": dfs
}, (e, mfs) => {
if (e) {
cb(e);
return
}

const fs = new FS();
fs.initialize(mfs);

const fdMap: {[id: number]: File} = (fs as any).fdMap;
fdMap[0] = handles.stdin;
fdMap[1] = handles.stdout;
fdMap[2] = handles.stderr;

cb(undefined, fs);
});
});
}

// Re-export for device implementors
export { BFSCallback, Stats, File, FileFlag };
170 changes: 170 additions & 0 deletions kernel/src/stdio_handles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
import { File, BaseFile } from "browserfs/dist/node/core/file";
import { BaseFileSystem, FileSystemConstructor, BFSCallback, BFSOneArgCallback, BFSThreeArgCallback, FileSystem, FileSystemOptions } from "browserfs/dist/node/core/file_system";
import { FileType } from 'browserfs/dist/node/core/node_fs_stats';
import Stats from 'browserfs/dist/node/core/node_fs_stats';
import { ApiError, ErrorCode } from 'browserfs/dist/node/core/api_error';

export let stdin: File;
export let stdout: File;
export let stderr: File;

class UselessFile extends BaseFile implements File {
getPos(): number | undefined {
return undefined;
}
stat(cb: BFSCallback<Stats>): void {
return cb(undefined, new Stats(FileType.FILE, 0));
}
statSync(): Stats {
return new Stats(FileType.FILE, 0)
}
close(cb: BFSOneArgCallback): void {
cb(new ApiError(ErrorCode.ENOTSUP));
}
closeSync(): void {
throw new ApiError(ErrorCode.ENOTSUP);
}
truncate(len: number, cb: BFSOneArgCallback): void {
cb(new ApiError(ErrorCode.ENOTSUP));
}
truncateSync(len: number): void {
throw new ApiError(ErrorCode.ENOTSUP);
}
write(buffer: Buffer, offset: number, length: number, position: number | null, cb: BFSThreeArgCallback<number, Buffer>): void {
cb(new ApiError(ErrorCode.ENOTSUP));
}
writeSync(buffer: Buffer, offset: number, length: number, position: number | null): number {
throw new ApiError(ErrorCode.ENOTSUP);
}
read(buffer: Buffer, offset: number, length: number, position: number | null, cb: BFSThreeArgCallback<number, Buffer>): void {
cb(new ApiError(ErrorCode.ENOTSUP));
}
readSync(buffer: Buffer, offset: number, length: number, position: number): number {
throw new ApiError(ErrorCode.ENOTSUP);
}
}

if (process && !(process as any).browser) {
interface Request {
buffer: Buffer;
offset: number;
length: number;
cb: BFSThreeArgCallback<number, Buffer>;
}
class ReadWriteStreamFile extends UselessFile implements File {
stream: NodeJS.ReadWriteStream;
requests: [Request] = <[Request]> [];
leftover?: Buffer = null;

constructor(stream: NodeJS.ReadWriteStream) {
super();
this.stream = stream;
this.stream.pause();
this.stream.on("error", (err) => {
const reqs = this.requests;
this.requests = <[Request]> [];
for (const req of reqs) {
req.cb(err, undefined, undefined);
}
});
this.stream.on("data", (buf) => {
this.stream.pause();
if (this.leftover) {
buf = Buffer.concat([this.leftover, buf]);
this.leftover = null;
}
this.onData(buf);
});
}

onData(buf: Buffer): void {
const reqs = this.requests;
this.requests = <[Request]> [];
let nextBuf: Buffer | null = null;
for (const req of reqs) {
if (buf.length > req.length) {
nextBuf = buf.slice(req.length);
buf = buf.slice(0, req.length);
} else {
nextBuf = null;
}

const copied = buf.copy(req.buffer, req.offset);
req.cb(undefined, copied, req.buffer);

buf = nextBuf;
}

if (nextBuf) {
// nextBuf may still have the old leftover underlying it.
// Use Buffer.from to avoid retaining the entire history.
this.leftover = Buffer.from(nextBuf);
}
};
close(cb: BFSOneArgCallback): void {
this.stream.end(cb);
}
write(buffer: Buffer, offset: number, length: number, position: number | null, cb: BFSThreeArgCallback<number, Buffer>): void {
this.stream.write(buffer.slice(offset, offset + length), (err) => {
if (err) {
cb(err);
} else {
cb(undefined, length, buffer);
}
});
}
read(buffer: Buffer, offset: number, length: number, position: number | null, cb: BFSThreeArgCallback<number, Buffer>): void {
this.stream.resume();
this.requests.push({
buffer: buffer,
offset: offset,
length: length,
cb: cb
});
}
}

stdin = new ReadWriteStreamFile(process.stdin);
stdout = new ReadWriteStreamFile(process.stdout);
stderr = new ReadWriteStreamFile(process.stderr);
} else {
class ConsoleFile extends UselessFile implements File {
log: (msg: string) => void;
buffer?: Buffer = null;

constructor(log: (msg: string) => void) {
super();
this.log = log;
}

write(buffer: Buffer, offset: number, length: number, position: number | null, cb: BFSThreeArgCallback<number, Buffer>): void {
let slicedBuffer = buffer.slice(offset, offset + length);
let n = slicedBuffer.lastIndexOf("\n");
if (n < 0) {
if (this.buffer) {
this.buffer = Buffer.concat([this.buffer, slicedBuffer]);
} else {
this.buffer = slicedBuffer;
}
} else {
let logBuffer = slicedBuffer.slice(0, n);
if (this.buffer) {
logBuffer = Buffer.concat([this.buffer, logBuffer]);
}
this.log(logBuffer.toString());

// + 1 to skip the \n
if (n + 1 < slicedBuffer.length) {
this.buffer = slicedBuffer.slice(n + 1);
} else {
this.buffer = null;
}
}
cb(undefined, length, buffer);
}
}

stdin = new UselessFile();
stdout = new ConsoleFile((msg) => console.log(msg));
stderr = new ConsoleFile((msg) => console.error(msg));
}
15 changes: 15 additions & 0 deletions kernel/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"compilerOptions": {
"target": "es6",
"outDir": "dist",
"lib": ["dom", "es2015", "es2016", "es2017"],
"module": "commonjs",
"declaration": true
},
"include": [
"src/**/*.ts"
],
"exclude": [
"node_modules"
]
}

0 comments on commit 1ad25a4

Please sign in to comment.