@ -30,6 +30,12 @@
global . fs = require ( "fs" ) ;
}
const enosys = ( ) => {
const err = new Error ( "not implemented" ) ;
err . code = "ENOSYS" ;
return err ;
} ;
if ( ! global . fs ) {
let outputBuf = "" ;
global . fs = {
@ -45,27 +51,53 @@
} ,
write ( fd , buf , offset , length , position , callback ) {
if ( offset !== 0 || length !== buf . length || position !== null ) {
throw new Error ( "not implemented" ) ;
callback ( enosys ( ) ) ;
return ;
}
const n = this . writeSync ( fd , buf ) ;
callback ( null , n ) ;
} ,
open ( path , flags , mode , callback ) {
const err = new Error ( "not implemented" ) ;
err . code = "ENOSYS" ;
callback ( err ) ;
} ,
read ( fd , buffer , offset , length , position , callback ) {
const err = new Error ( "not implemented" ) ;
err . code = "ENOSYS" ;
callback ( err ) ;
} ,
fsync ( fd , callback ) {
callback ( null ) ;
} ,
chmod ( path , mode , callback ) { callback ( enosys ( ) ) ; } ,
chown ( path , uid , gid , callback ) { callback ( enosys ( ) ) ; } ,
close ( fd , callback ) { callback ( enosys ( ) ) ; } ,
fchmod ( fd , mode , callback ) { callback ( enosys ( ) ) ; } ,
fchown ( fd , uid , gid , callback ) { callback ( enosys ( ) ) ; } ,
fstat ( fd , callback ) { callback ( enosys ( ) ) ; } ,
fsync ( fd , callback ) { callback ( null ) ; } ,
ftruncate ( fd , length , callback ) { callback ( enosys ( ) ) ; } ,
lchown ( path , uid , gid , callback ) { callback ( enosys ( ) ) ; } ,
link ( path , link , callback ) { callback ( enosys ( ) ) ; } ,
lstat ( path , callback ) { callback ( enosys ( ) ) ; } ,
mkdir ( path , perm , callback ) { callback ( enosys ( ) ) ; } ,
open ( path , flags , mode , callback ) { callback ( enosys ( ) ) ; } ,
read ( fd , buffer , offset , length , position , callback ) { callback ( enosys ( ) ) ; } ,
readdir ( path , callback ) { callback ( enosys ( ) ) ; } ,
readlink ( path , callback ) { callback ( enosys ( ) ) ; } ,
rename ( from , to , callback ) { callback ( enosys ( ) ) ; } ,
rmdir ( path , callback ) { callback ( enosys ( ) ) ; } ,
stat ( path , callback ) { callback ( enosys ( ) ) ; } ,
symlink ( path , link , callback ) { callback ( enosys ( ) ) ; } ,
truncate ( path , length , callback ) { callback ( enosys ( ) ) ; } ,
unlink ( path , callback ) { callback ( enosys ( ) ) ; } ,
utimes ( path , atime , mtime , callback ) { callback ( enosys ( ) ) ; } ,
} ;
}
if ( ! global . process ) {
global . process = {
getuid ( ) { return - 1 ; } ,
getgid ( ) { return - 1 ; } ,
geteuid ( ) { return - 1 ; } ,
getegid ( ) { return - 1 ; } ,
getgroups ( ) { throw enosys ( ) ; } ,
pid : - 1 ,
ppid : - 1 ,
umask ( ) { throw enosys ( ) ; } ,
cwd ( ) { throw enosys ( ) ; } ,
chdir ( ) { throw enosys ( ) ; } ,
}
}
if ( ! global . crypto ) {
const nodeCrypto = require ( "crypto" ) ;
global . crypto = {
@ -113,24 +145,19 @@
this . _scheduledTimeouts = new Map ( ) ;
this . _nextCallbackTimeoutID = 1 ;
const mem = ( ) => {
// The buffer may change when requesting more memory.
return new DataView ( this . _inst . exports . mem . buffer ) ;
}
const setInt64 = ( addr , v ) => {
mem ( ) . setUint32 ( addr + 0 , v , true ) ;
mem ( ) . setUint32 ( addr + 4 , Math . floor ( v / 4294967296 ) , true ) ;
this . mem . setUint32 ( addr + 0 , v , true ) ;
this . mem . setUint32 ( addr + 4 , Math . floor ( v / 4294967296 ) , true ) ;
}
const getInt64 = ( addr ) => {
const low = mem ( ) . getUint32 ( addr + 0 , true ) ;
const high = mem ( ) . getInt32 ( addr + 4 , true ) ;
const low = this . mem . getUint32 ( addr + 0 , true ) ;
const high = this . mem . getInt32 ( addr + 4 , true ) ;
return low + high * 4294967296 ;
}
const loadValue = ( addr ) => {
const f = mem ( ) . getFloat64 ( addr , true ) ;
const f = this . mem . getFloat64 ( addr , true ) ;
if ( f === 0 ) {
return undefined ;
}
@ -138,7 +165,7 @@
return f ;
}
const id = mem ( ) . getUint32 ( addr , true ) ;
const id = this . mem . getUint32 ( addr , true ) ;
return this . _values [ id ] ;
}
@ -147,57 +174,62 @@
if ( typeof v === "number" ) {
if ( isNaN ( v ) ) {
mem ( ) . setUint32 ( addr + 4 , nanHead , true ) ;
mem ( ) . setUint32 ( addr , 0 , true ) ;
this . mem . setUint32 ( addr + 4 , nanHead , true ) ;
this . mem . setUint32 ( addr , 0 , true ) ;
return ;
}
if ( v === 0 ) {
mem ( ) . setUint32 ( addr + 4 , nanHead , true ) ;
mem ( ) . setUint32 ( addr , 1 , true ) ;
this . mem . setUint32 ( addr + 4 , nanHead , true ) ;
this . mem . setUint32 ( addr , 1 , true ) ;
return ;
}
mem ( ) . setFloat64 ( addr , v , true ) ;
this . mem . setFloat64 ( addr , v , true ) ;
return ;
}
switch ( v ) {
case undefined :
mem ( ) . setFloat64 ( addr , 0 , true ) ;
this . mem . setFloat64 ( addr , 0 , true ) ;
return ;
case null :
mem ( ) . setUint32 ( addr + 4 , nanHead , true ) ;
mem ( ) . setUint32 ( addr , 2 , true ) ;
this . mem . setUint32 ( addr + 4 , nanHead , true ) ;
this . mem . setUint32 ( addr , 2 , true ) ;
return ;
case true :
mem ( ) . setUint32 ( addr + 4 , nanHead , true ) ;
mem ( ) . setUint32 ( addr , 3 , true ) ;
this . mem . setUint32 ( addr + 4 , nanHead , true ) ;
this . mem . setUint32 ( addr , 3 , true ) ;
return ;
case false :
mem ( ) . setUint32 ( addr + 4 , nanHead , true ) ;
mem ( ) . setUint32 ( addr , 4 , true ) ;
this . mem . setUint32 ( addr + 4 , nanHead , true ) ;
this . mem . setUint32 ( addr , 4 , true ) ;
return ;
}
let ref = this . _refs . get ( v ) ;
if ( ref === undefined ) {
ref = this . _values . length ;
this . _values . push ( v ) ;
this . _refs . set ( v , ref ) ;
let id = this . _ids . get ( v ) ;
if ( id === undefined ) {
id = this . _idPool . pop ( ) ;
if ( id === undefined ) {
id = this . _values . length ;
}
this . _values [ id ] = v ;
this . _goRefCounts [ id ] = 0 ;
this . _ids . set ( v , id ) ;
}
let typeFlag = 0 ;
this . _goRefCounts [ id ] ++ ;
let typeFlag = 1 ;
switch ( typeof v ) {
case "string" :
typeFlag = 1 ;
typeFlag = 2 ;
break ;
case "symbol" :
typeFlag = 2 ;
typeFlag = 3 ;
break ;
case "function" :
typeFlag = 3 ;
typeFlag = 4 ;
break ;
}
mem ( ) . setUint32 ( addr + 4 , nanHead | typeFlag , true ) ;
mem ( ) . setUint32 ( addr , ref , true ) ;
this . mem . setUint32 ( addr + 4 , nanHead | typeFlag , true ) ;
this . mem . setUint32 ( addr , id , true ) ;
}
const loadSlice = ( addr ) => {
@ -232,11 +264,13 @@
// func wasmExit(code int32)
"runtime.wasmExit" : ( sp ) => {
const code = mem ( ) . getInt32 ( sp + 8 , true ) ;
const code = this . mem . getInt32 ( sp + 8 , true ) ;
this . exited = true ;
delete this . _inst ;
delete this . _values ;
delete this . _refs ;
delete this . _goRefCounts ;
delete this . _ids ;
delete this . _idPool ;
this . exit ( code ) ;
} ,
@ -244,20 +278,25 @@
"runtime.wasmWrite" : ( sp ) => {
const fd = getInt64 ( sp + 8 ) ;
const p = getInt64 ( sp + 16 ) ;
const n = mem ( ) . getInt32 ( sp + 24 , true ) ;
const n = this . mem . getInt32 ( sp + 24 , true ) ;
fs . writeSync ( fd , new Uint8Array ( this . _inst . exports . mem . buffer , p , n ) ) ;
} ,
// func nanotime() int64
"runtime.nanotime" : ( sp ) => {
// func resetMemoryDataView()
"runtime.resetMemoryDataView" : ( sp ) => {
this . mem = new DataView ( this . _inst . exports . mem . buffer ) ;
} ,
// func nanotime1() int64
"runtime.nanotime1" : ( sp ) => {
setInt64 ( sp + 8 , ( timeOrigin + performance . now ( ) ) * 1000000 ) ;
} ,
// func walltime() (sec int64, nsec int32)
"runtime.walltime" : ( sp ) => {
// func walltime1 () (sec int64, nsec int32)
"runtime.walltime1 " : ( sp ) => {
const msec = ( new Date ) . getTime ( ) ;
setInt64 ( sp + 8 , msec / 1000 ) ;
mem ( ) . setInt32 ( sp + 16 , ( msec % 1000 ) * 1000000 , true ) ;
this . mem . setInt32 ( sp + 16 , ( msec % 1000 ) * 1000000 , true ) ;
} ,
// func scheduleTimeoutEvent(delay int64) int32
@ -276,12 +315,12 @@
} ,
getInt64 ( sp + 8 ) + 1 , // setTimeout has been seen to fire up to 1 millisecond early
) ) ;
mem ( ) . setInt32 ( sp + 16 , id , true ) ;
this . mem . setInt32 ( sp + 16 , id , true ) ;
} ,
// func clearTimeoutEvent(id int32)
"runtime.clearTimeoutEvent" : ( sp ) => {
const id = mem ( ) . getInt32 ( sp + 8 , true ) ;
const id = this . mem . getInt32 ( sp + 8 , true ) ;
clearTimeout ( this . _scheduledTimeouts . get ( id ) ) ;
this . _scheduledTimeouts . delete ( id ) ;
} ,
@ -291,6 +330,18 @@
crypto . getRandomValues ( loadSlice ( sp + 8 ) ) ;
} ,
// func finalizeRef(v ref)
"syscall/js.finalizeRef" : ( sp ) => {
const id = this . mem . getUint32 ( sp + 8 , true ) ;
this . _goRefCounts [ id ] -- ;
if ( this . _goRefCounts [ id ] === 0 ) {
const v = this . _values [ id ] ;
this . _values [ id ] = null ;
this . _ids . delete ( v ) ;
this . _idPool . push ( id ) ;
}
} ,
// func stringVal(value string) ref
"syscall/js.stringVal" : ( sp ) => {
storeValue ( sp + 24 , loadString ( sp + 8 ) ) ;
@ -308,6 +359,11 @@
Reflect . set ( loadValue ( sp + 8 ) , loadString ( sp + 16 ) , loadValue ( sp + 32 ) ) ;
} ,
// func valueDelete(v ref, p string)
"syscall/js.valueDelete" : ( sp ) => {
Reflect . deleteProperty ( loadValue ( sp + 8 ) , loadString ( sp + 16 ) ) ;
} ,
// func valueIndex(v ref, i int) ref
"syscall/js.valueIndex" : ( sp ) => {
storeValue ( sp + 24 , Reflect . get ( loadValue ( sp + 8 ) , getInt64 ( sp + 16 ) ) ) ;
@ -327,10 +383,10 @@
const result = Reflect . apply ( m , v , args ) ;
sp = this . _inst . exports . getsp ( ) ; // see comment above
storeValue ( sp + 56 , result ) ;
mem ( ) . setUint8 ( sp + 64 , 1 ) ;
this . mem . setUint8 ( sp + 64 , 1 ) ;
} catch ( err ) {
storeValue ( sp + 56 , err ) ;
mem ( ) . setUint8 ( sp + 64 , 0 ) ;
this . mem . setUint8 ( sp + 64 , 0 ) ;
}
} ,
@ -342,10 +398,10 @@
const result = Reflect . apply ( v , undefined , args ) ;
sp = this . _inst . exports . getsp ( ) ; // see comment above
storeValue ( sp + 40 , result ) ;
mem ( ) . setUint8 ( sp + 48 , 1 ) ;
this . mem . setUint8 ( sp + 48 , 1 ) ;
} catch ( err ) {
storeValue ( sp + 40 , err ) ;
mem ( ) . setUint8 ( sp + 48 , 0 ) ;
this . mem . setUint8 ( sp + 48 , 0 ) ;
}
} ,
@ -357,10 +413,10 @@
const result = Reflect . construct ( v , args ) ;
sp = this . _inst . exports . getsp ( ) ; // see comment above
storeValue ( sp + 40 , result ) ;
mem ( ) . setUint8 ( sp + 48 , 1 ) ;
this . mem . setUint8 ( sp + 48 , 1 ) ;
} catch ( err ) {
storeValue ( sp + 40 , err ) ;
mem ( ) . setUint8 ( sp + 48 , 0 ) ;
this . mem . setUint8 ( sp + 48 , 0 ) ;
}
} ,
@ -384,7 +440,7 @@
// func valueInstanceOf(v ref, t ref) bool
"syscall/js.valueInstanceOf" : ( sp ) => {
mem ( ) . setUint8 ( sp + 24 , loadValue ( sp + 8 ) instanceof loadValue ( sp + 16 ) ) ;
this . mem . setUint8 ( sp + 24 , loadValue ( sp + 8 ) instanceof loadValue ( sp + 16 ) ) ;
} ,
// func copyBytesToGo(dst []byte, src ref) (int, bool)
@ -392,13 +448,13 @@
const dst = loadSlice ( sp + 8 ) ;
const src = loadValue ( sp + 32 ) ;
if ( ! ( src instanceof Uint8Array ) ) {
mem ( ) . setUint8 ( sp + 48 , 0 ) ;
this . mem . setUint8 ( sp + 48 , 0 ) ;
return ;
}
const toCopy = src . subarray ( 0 , dst . length ) ;
dst . set ( toCopy ) ;
setInt64 ( sp + 40 , toCopy . length ) ;
mem ( ) . setUint8 ( sp + 48 , 1 ) ;
this . mem . setUint8 ( sp + 48 , 1 ) ;
} ,
// func copyBytesToJS(dst ref, src []byte) (int, bool)
@ -406,13 +462,13 @@
const dst = loadValue ( sp + 8 ) ;
const src = loadSlice ( sp + 16 ) ;
if ( ! ( dst instanceof Uint8Array ) ) {
mem ( ) . setUint8 ( sp + 48 , 0 ) ;
this . mem . setUint8 ( sp + 48 , 0 ) ;
return ;
}
const toCopy = src . subarray ( 0 , dst . length ) ;
dst . set ( toCopy ) ;
setInt64 ( sp + 40 , toCopy . length ) ;
mem ( ) . setUint8 ( sp + 48 , 1 ) ;
this . mem . setUint8 ( sp + 48 , 1 ) ;
} ,
"debug" : ( value ) => {
@ -424,7 +480,8 @@
async run ( instance ) {
this . _inst = instance ;
this . _values = [ // TODO: garbage collection
this . mem = new DataView ( this . _inst . exports . mem . buffer ) ;
this . _values = [ // JS values that Go currently has references to, indexed by reference id
NaN ,
0 ,
null ,
@ -433,10 +490,10 @@
global ,
this ,
] ;
this . _refs = new Map ( ) ;
this . exited = false ;
const mem = new DataView ( this . _inst . exports . mem . buffer )
this . _goRefCounts = [ ] ; // number of references that Go has to a JS value, indexed by reference id
this . _ids = new Map ( ) ; // mapping from JS values to reference ids
this . _idPool = [ ] ; // unused ids that have been garbage collected
this . exited = false ; // whether the Go program has exited
// Pass command line arguments and environment variables to WebAssembly by writing them to the linear memory.
let offset = 4096 ;
@ -444,7 +501,7 @@
const strPtr = ( str ) => {
const ptr = offset ;
const bytes = encoder . encode ( str + "\0" ) ;
new Uint8Array ( mem . buffer , offset , bytes . length ) . set ( bytes ) ;
new Uint8Array ( this . mem . buffer , offset , bytes . length ) . set ( bytes ) ;
offset += bytes . length ;
if ( offset % 8 !== 0 ) {
offset += 8 - ( offset % 8 ) ;
@ -458,17 +515,18 @@
this . argv . forEach ( ( arg ) => {
argvPtrs . push ( strPtr ( arg ) ) ;
} ) ;
argvPtrs . push ( 0 ) ;
const keys = Object . keys ( this . env ) . sort ( ) ;
argvPtrs . push ( keys . length ) ;
keys . forEach ( ( key ) => {
argvPtrs . push ( strPtr ( ` ${ key } = ${ this . env [ key ] } ` ) ) ;
} ) ;
argvPtrs . push ( 0 ) ;
const argv = offset ;
argvPtrs . forEach ( ( ptr ) => {
mem . setUint32 ( offset , ptr , true ) ;
mem . setUint32 ( offset + 4 , 0 , true ) ;
this . mem . setUint32 ( offset , ptr , true ) ;
this . mem . setUint32 ( offset + 4 , 0 , true ) ;
offset += 8 ;
} ) ;