Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
f5d7e06
Build static sub-tasks in parallel
JohnStarich Sep 4, 2021
45c7f1e
Bump to latest Go 1.16 release
JohnStarich Feb 6, 2022
a3fab95
Add caller info to log output
JohnStarich Feb 6, 2022
408cf79
Replace process interface with only struct
JohnStarich Apr 2, 2022
1264aba
Add (js)worker concepts and message system, extract process construct…
JohnStarich Apr 2, 2022
044a5a2
Fix shims, pass process into init
JohnStarich Apr 3, 2022
649d4ce
Add local and remote worker init/start system; successful installs
JohnStarich Apr 3, 2022
893b7ab
Fix DOM worker crashes, add panic handlers for better visibility
JohnStarich Apr 3, 2022
228f942
Track child pids
JohnStarich Apr 3, 2022
31a9e5d
Add worker lifecycle management, move FS setup to main.go
JohnStarich Apr 4, 2022
7fccabd
Temporarily patch spawnTerminal back in
JohnStarich Apr 4, 2022
bd13945
Add FS setup in worker, TODO reuse from main.go
JohnStarich Apr 4, 2022
6c97ee7
Fix blocking event loop in event handler
JohnStarich Apr 4, 2022
5f3b386
Add .env file for automatic GOOS/GOARCH editor settings
JohnStarich Apr 10, 2022
d0c3bac
Audit js.FuncOf usage, replace with jsfunc package; fix import cycle …
JohnStarich Apr 10, 2022
d2ed018
Remove unused func
JohnStarich Apr 19, 2022
f395e40
Make window event handlers async
JohnStarich Apr 19, 2022
4c1bdea
Fix wasm boot crash due to synchronous spawn
JohnStarich Apr 19, 2022
99aad31
Fix FS not fully initialized at process start
JohnStarich Apr 19, 2022
8fef316
Add worker name to logs
JohnStarich Apr 19, 2022
1c7753c
Allow waiting on self
JohnStarich Apr 19, 2022
341e830
WIP: Fix MessagePort transfers, process start works now
JohnStarich Jun 17, 2022
e62332d
WIP: Successfully sent stdout to dom process, print in log
JohnStarich Jun 17, 2022
e5e0b6f
Increment open count for piped files in new process
JohnStarich Jun 17, 2022
28bff3c
Fix inconsistent blob type
JohnStarich Jun 17, 2022
030b6a5
Close remote worker files on exit
JohnStarich Jun 18, 2022
195cc84
Fix remote worker working directory
JohnStarich Jun 18, 2022
66569c2
Continue setup after go version
JohnStarich Jun 18, 2022
d8f7c7d
Disable debug logs for worker
JohnStarich Jun 18, 2022
3f7c394
Copy current env to remote worker
JohnStarich Jun 18, 2022
aa0ff3e
Add idb-based global file locker
JohnStarich Jun 26, 2022
76bc53c
Mount /tmp in worker too
JohnStarich Jun 26, 2022
a55e39c
Fix exit status error missing newline
JohnStarich Jun 27, 2022
47400a8
Reduce logging
JohnStarich Jun 27, 2022
b7eaa9f
Add env info inside worker's hackpad.process
JohnStarich Jun 27, 2022
5358139
Fix missing read loop on local port side
JohnStarich Jun 27, 2022
460a392
Allow pipe chan to read without blocking if it already has 1 byte
JohnStarich Jun 27, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .env
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
GOOS=js
GOARCH=wasm
10 changes: 6 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
SHELL := /usr/bin/env bash
GO_VERSION = 1.16.6
GO_VERSION = 1.16
GOROOT =
PATH := ${PWD}/cache/go/bin:${PWD}/cache/go/misc/wasm:${PATH}
GOOS = js
GOARCH = wasm
export
LINT_VERSION=1.27.0

BUILD_FLAGS = -trimpath

.PHONY: serve
serve:
go run ./server
Expand Down Expand Up @@ -71,7 +73,7 @@ cache/go${GO_VERSION}: cache
git clone \
--depth 1 \
--single-branch \
--branch hackpad-go${GO_VERSION} \
--branch hackpad/release-branch.go${GO_VERSION} \
https://github.com/hack-pad/go.git \
"$$TMP"; \
pushd "$$TMP/src"; \
Expand All @@ -91,10 +93,10 @@ cache/go${GO_VERSION}: cache
touch cache/go.mod # Makes it so linters will ignore this dir

server/public/wasm/%.wasm: server/public/wasm go
go build -o $@ ./cmd/$*
go build ${BUILD_FLAGS} -o $@ ./cmd/$*

server/public/wasm/main.wasm: server/public/wasm go
go build -o server/public/wasm/main.wasm .
go build ${BUILD_FLAGS} -o server/public/wasm/main.wasm .

server/public/wasm/wasm_exec.js: go
cp cache/go/misc/wasm/wasm_exec.js server/public/wasm/wasm_exec.js
Expand Down
3 changes: 2 additions & 1 deletion cmd/editor/dom/element.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (

"github.com/hack-pad/hackpad/internal/common"
"github.com/hack-pad/hackpad/internal/interop"
"github.com/hack-pad/hackpad/internal/jsfunc"
"github.com/hack-pad/hackpad/internal/log"
)

Expand Down Expand Up @@ -112,7 +113,7 @@ func (e *Element) QuerySelectorAll(query string) []*Element {
}

func (e *Element) AddEventListener(name string, listener EventListener) {
e.elem.Call("addEventListener", name, js.FuncOf(func(this js.Value, args []js.Value) interface{} {
e.elem.Call("addEventListener", name, jsfunc.NonBlocking(func(this js.Value, args []js.Value) interface{} {
defer common.CatchExceptionHandler(func(err error) {
log.Error("recovered from panic: ", err, "\n", string(debug.Stack()))
})
Expand Down
9 changes: 5 additions & 4 deletions cmd/editor/dom/window.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"time"

"github.com/hack-pad/hackpad/internal/interop"
"github.com/hack-pad/hackpad/internal/jsfunc"
)

var (
Expand All @@ -15,8 +16,8 @@ var (

func SetTimeout(fn func(args []js.Value), delay time.Duration, args ...js.Value) int {
intArgs := append([]interface{}{
interop.SingleUseFunc(func(_ js.Value, args []js.Value) interface{} {
fn(args)
jsfunc.SingleUse(func(_ js.Value, args []js.Value) interface{} {
go fn(args)
return nil
}),
delay.Milliseconds(),
Expand All @@ -28,8 +29,8 @@ func SetTimeout(fn func(args []js.Value), delay time.Duration, args ...js.Value)
func QueueMicrotask(fn func()) {
queueMicrotask := window.GetProperty("queueMicrotask")
if queueMicrotask.Truthy() {
queueMicrotask.Invoke(interop.SingleUseFunc(func(this js.Value, args []js.Value) interface{} {
fn()
queueMicrotask.Invoke(jsfunc.SingleUse(func(this js.Value, args []js.Value) interface{} {
go fn()
return nil
}))
} else {
Expand Down
26 changes: 13 additions & 13 deletions cmd/editor/editor.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
//go:build js
// +build js

package main
Expand All @@ -9,6 +10,7 @@ import (

"github.com/hack-pad/hackpad/cmd/editor/dom"
"github.com/hack-pad/hackpad/cmd/editor/ide"
"github.com/hack-pad/hackpad/internal/jsfunc"
"github.com/hack-pad/hackpad/internal/log"
)

Expand All @@ -25,7 +27,7 @@ func (e editorJSFunc) New(elem *dom.Element) ide.Editor {
editor := &jsEditor{
titleChan: make(chan string, 1),
}
editor.elem = js.Value(e).Invoke(elem, js.FuncOf(editor.onEdit))
editor.elem = js.Value(e).Invoke(elem, jsfunc.NonBlocking(editor.onEdit))
return editor
}

Expand All @@ -36,18 +38,16 @@ type jsEditor struct {
}

func (j *jsEditor) onEdit(js.Value, []js.Value) interface{} {
go func() {
contents := j.elem.Call("getContents").String()
perm := os.FileMode(0700)
info, err := os.Stat(j.filePath)
if err == nil {
perm = info.Mode()
}
err = ioutil.WriteFile(j.filePath, []byte(contents), perm)
if err != nil {
log.Error("Failed to write file contents: ", err)
}
}()
contents := j.elem.Call("getContents").String()
perm := os.FileMode(0700)
info, err := os.Stat(j.filePath)
if err == nil {
perm = info.Mode()
}
err = ioutil.WriteFile(j.filePath, []byte(contents), perm)
if err != nil {
log.Error("Failed to write file contents: ", err)
}
return nil
}

Expand Down
10 changes: 9 additions & 1 deletion cmd/editor/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,17 @@ import (
"flag"
"io/ioutil"
"os"
"runtime/debug"
"syscall/js"

"github.com/hack-pad/hackpad/cmd/editor/dom"
"github.com/hack-pad/hackpad/cmd/editor/ide"
"github.com/hack-pad/hackpad/cmd/editor/plaineditor"
"github.com/hack-pad/hackpad/cmd/editor/taskconsole"
"github.com/hack-pad/hackpad/cmd/editor/terminal"
"github.com/hack-pad/hackpad/internal/common"
"github.com/hack-pad/hackpad/internal/interop"
"github.com/hack-pad/hackpad/internal/jsfunc"
"github.com/hack-pad/hackpad/internal/log"
)

Expand All @@ -22,6 +25,11 @@ const (
)

func main() {
defer common.CatchExceptionHandler(func(err error) {
log.Error("Editor panic:", err, "\n", string(debug.Stack()))
os.Exit(1)
})

editorID := flag.String("editor", "", "Editor element ID to attach")
flag.Parse()

Expand All @@ -33,7 +41,7 @@ func main() {
app := dom.GetDocument().GetElementByID(*editorID)
app.AddClass("ide")
globalEditorProps := js.Global().Get("editor")
globalEditorProps.Set("profile", js.FuncOf(interop.ProfileJS))
globalEditorProps.Set("profile", jsfunc.NonBlocking(interop.ProfileJS))
newEditor := globalEditorProps.Get("newEditor")
var editorBuilder ide.EditorBuilder = editorJSFunc(newEditor)
if !newEditor.Truthy() {
Expand Down
2 changes: 1 addition & 1 deletion cmd/editor/taskconsole/console.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ func (c *console) runLoopIter() {
defer cancel(commandErr)
elapsed := time.Since(startTime)
if commandErr != nil {
_, _ = c.stderr.Write([]byte(commandErr.Error()))
_, _ = c.stderr.Write([]byte(commandErr.Error() + "\n"))
}

exitCode := 0
Expand Down
3 changes: 2 additions & 1 deletion cmd/editor/terminal/terminal.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/hack-pad/hackpad/cmd/editor/dom"
"github.com/hack-pad/hackpad/cmd/editor/ide"
"github.com/hack-pad/hackpad/internal/common"
"github.com/hack-pad/hackpad/internal/jsfunc"
"github.com/hack-pad/hackpad/internal/log"
"github.com/hack-pad/hackpadfs/indexeddb/idbblob"
"github.com/hack-pad/hackpadfs/keyvalue/blob"
Expand Down Expand Up @@ -74,7 +75,7 @@ func (t *terminal) start(rawName, name string, args ...string) error {
return err
}

f := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
f := jsfunc.NonBlocking(func(this js.Value, args []js.Value) interface{} {
chunk := []byte(args[0].String())
_, err := stdin.Write(chunk)
if err == io.EOF {
Expand Down
90 changes: 90 additions & 0 deletions cmd/worker/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package main

import (
"context"
"os"
"runtime/debug"

"github.com/hack-pad/go-indexeddb/idb"
"github.com/hack-pad/hackpad/internal/common"
"github.com/hack-pad/hackpad/internal/fs"
"github.com/hack-pad/hackpad/internal/jsworker"
"github.com/hack-pad/hackpad/internal/log"
"github.com/hack-pad/hackpad/internal/worker"
"github.com/hack-pad/hackpadfs/indexeddb"
)

func main() {
defer common.CatchExceptionHandler(func(err error) {
log.Errorf("Worker panicked: %+v", err)
log.Error(string(debug.Stack()))
os.Exit(1)
})

bootCtx := context.Background()
log.Debug("booting worker")
local, err := worker.NewLocal(bootCtx, jsworker.GetLocal())
if err != nil {
panic(err)
}
log.Debug("worker inited")
if err := setUpFS(); err != nil {
panic(err)
}
log.Debug("fs is setup")
if err := local.Start(); err != nil {
panic(err)
}
log.Debug("worker starting...")
<-local.Started()
pid := local.PID()
log.Debug("worker process started PID ", pid)
exitCode, err := local.Wait(pid)
if err != nil {
log.Error("Failed to wait for PID ", pid, ":", err)
exitCode = 1
}
log.Debug("worker stopped for PID ", pid, "; exit code = ", exitCode)
local.Exit(exitCode)
os.Exit(exitCode)
}

func setUpFS() error {
const dirPerm = 0700
mkdirMount := func(mountPath string, durability idb.TransactionDurability) error {
if err := os.MkdirAll(mountPath, dirPerm); err != nil {
return err
}
if err := overlayIndexedDB(mountPath, durability); err != nil {
return err
}
return nil
}

if err := mkdirMount("/bin", idb.DurabilityRelaxed); err != nil {
return err
}
if err := mkdirMount("/home/me", idb.DurabilityDefault); err != nil {
return err
}
if err := mkdirMount("/home/me/.cache", idb.DurabilityRelaxed); err != nil {
return err
}
if err := mkdirMount("/tmp", idb.DurabilityRelaxed); err != nil {
return err
}
if err := mkdirMount("/usr/local/go", idb.DurabilityRelaxed); err != nil {
return err
}
return nil
}

func overlayIndexedDB(mountPath string, durability idb.TransactionDurability) error {
idbFS, err := indexeddb.NewFS(context.Background(), mountPath, indexeddb.Options{
TransactionDurability: durability,
})
if err != nil {
return err
}
return fs.Overlay(mountPath, idbFS)
}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ go 1.16
require (
github.com/avct/uasurfer v0.0.0-20191028135549-26b5daa857f1
github.com/hack-pad/go-indexeddb v0.1.0
github.com/hack-pad/hackpadfs v0.1.2
github.com/hack-pad/hackpadfs v0.1.4
github.com/hack-pad/hush v0.1.0
github.com/johnstarich/go/datasize v0.0.1
github.com/machinebox/progress v0.2.0
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ github.com/hack-pad/hackpadfs v0.1.1 h1:DhzS50ln5XAOxZ0Xlnb/o3P/+MWUqlcbGdOQ5m+F
github.com/hack-pad/hackpadfs v0.1.1/go.mod h1:8bsINHOQhQUioUUiCzCyZZNLfEXjs0RwBIf3lTG+CEg=
github.com/hack-pad/hackpadfs v0.1.2 h1:ZsHfvrNAMNNBVLMKprOiN2rLD37x+YGj3QPJrhUdRF4=
github.com/hack-pad/hackpadfs v0.1.2/go.mod h1:8bsINHOQhQUioUUiCzCyZZNLfEXjs0RwBIf3lTG+CEg=
github.com/hack-pad/hackpadfs v0.1.4 h1:vwLyuaVPFDqiy6YjLzvQ5fBTt0upzCaCkTok9aoKOdY=
github.com/hack-pad/hackpadfs v0.1.4/go.mod h1:8bsINHOQhQUioUUiCzCyZZNLfEXjs0RwBIf3lTG+CEg=
github.com/hack-pad/hush v0.0.0-20210730065049-bd589dbef3a3 h1:0WBvEONkD8zXBRe7+5+mp34L2Upmok0yPKvOqOzpksw=
github.com/hack-pad/hush v0.0.0-20210730065049-bd589dbef3a3/go.mod h1:NqjEIfyA2YtlnEPlI/1K3tNuyXGByWFadPxPlGrDPms=
github.com/hack-pad/hush v0.1.0 h1:lm/iUaRpVsKkpbN6U9wf45arVnCXzTqsMG1jyihIgkI=
Expand Down
1 change: 1 addition & 0 deletions http_get.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
//go:build js
// +build js

package main
Expand Down
29 changes: 10 additions & 19 deletions install.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,30 +9,22 @@ import (
"runtime"
"syscall/js"

"github.com/hack-pad/hackpad/internal/interop"
"github.com/hack-pad/hackpad/internal/log"
"github.com/hack-pad/hackpad/internal/process"
"github.com/hack-pad/hackpad/internal/promise"
)

func installFunc(this js.Value, args []js.Value) interface{} {
resolve, reject, prom := promise.New()
go func() {
err := install(args)
if err != nil {
reject(interop.WrapAsJSError(err, "Failed to install binary"))
return
}
resolve(nil)
}()
return prom
func (s domShim) installFunc(this js.Value, args []js.Value) (js.Wrapper, error) {
return nil, s.install(args)
}

func install(args []js.Value) error {
func (s domShim) install(args []js.Value) error {
if len(args) != 1 {
return errors.New("Expected command name to install")
}
command := args[0].String()
return s.Install(command)
}

func (s domShim) Install(command string) error {
command = filepath.Base(command) // ensure no path chars are present

if err := os.MkdirAll("/bin", 0644); err != nil {
Expand All @@ -44,13 +36,12 @@ func install(args []js.Value) error {
return err
}
defer runtime.GC()
fs := process.Current().Files()
fd, err := fs.Open("/bin/"+command, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0750)
file, err := os.OpenFile("/bin/"+command, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0750)
if err != nil {
return err
}
defer fs.Close(fd)
if _, err := fs.Write(fd, body, 0, body.Len(), nil); err != nil {
defer file.Close()
if _, err := file.Write(body.Bytes()); err != nil {
return err
}
log.Print("Install completed: ", command)
Expand Down
11 changes: 11 additions & 0 deletions internal/common/fid.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ package common

import (
"fmt"
"io"

"github.com/hack-pad/hackpadfs"
)

type FID uint64
Expand All @@ -12,3 +15,11 @@ func (f *FID) String() string {
}
return fmt.Sprintf("%d", *f)
}

type OpenFileAttr struct {
FilePath string
SeekOffset int64
Flags int
Mode hackpadfs.FileMode
RawDevice io.ReadWriteCloser
}
Loading