runtime: testing runtime

This commit is contained in:
Li Jie
2025-02-14 17:18:13 +08:00
parent 66909b3000
commit 5329f28580
83 changed files with 3376 additions and 2061 deletions

View File

@@ -1,6 +1,5 @@
package hmac
// llgo:skipall
import (
"crypto/sha256"
"crypto/subtle"
@@ -11,6 +10,9 @@ import (
"github.com/goplus/llgo/runtime/internal/clite/openssl"
)
// llgo:skipall
type _hmac struct{}
type eface struct {
_type unsafe.Pointer
funcPtr *unsafe.Pointer

View File

@@ -16,7 +16,6 @@
package md5
// llgo:skipall
import (
"crypto"
"hash"
@@ -26,6 +25,9 @@ import (
"github.com/goplus/llgo/runtime/internal/clite/openssl"
)
// llgo:skipall
type _md5 struct{}
func init() {
crypto.RegisterHash(crypto.MD5, New)
}

View File

@@ -16,7 +16,6 @@
package rand
// llgo:skipall
import (
"io"
@@ -25,6 +24,9 @@ import (
"github.com/qiniu/x/errors"
)
// llgo:skipall
type _rand struct{}
type rndReader struct{}
func (rndReader) Read(p []byte) (n int, err error) {

View File

@@ -16,7 +16,6 @@
package sha1
// llgo:skipall
import (
"crypto"
"hash"
@@ -26,6 +25,9 @@ import (
"github.com/goplus/llgo/runtime/internal/clite/openssl"
)
// llgo:skipall
type _sha1 struct{}
func init() {
crypto.RegisterHash(crypto.SHA1, New)
}

View File

@@ -16,7 +16,6 @@
package sha256
// llgo:skipall
import (
"crypto"
"hash"
@@ -26,6 +25,9 @@ import (
"github.com/goplus/llgo/runtime/internal/clite/openssl"
)
// llgo:skipall
type _sha256 struct{}
func init() {
crypto.RegisterHash(crypto.SHA224, New224)
crypto.RegisterHash(crypto.SHA256, New)

View File

@@ -16,7 +16,6 @@
package sha512
// llgo:skipall
import (
"crypto"
"hash"
@@ -26,6 +25,9 @@ import (
"github.com/goplus/llgo/runtime/internal/clite/openssl"
)
// llgo:skipall
type _sha512 struct{}
func init() {
crypto.RegisterHash(crypto.SHA384, New384)
crypto.RegisterHash(crypto.SHA512, New)

View File

@@ -1,78 +0,0 @@
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package fmt
import (
"errors"
"sort"
)
// Errorf formats according to a format specifier and returns the string as a
// value that satisfies error.
//
// If the format specifier includes a %w verb with an error operand,
// the returned error will implement an Unwrap method returning the operand.
// If there is more than one %w verb, the returned error will implement an
// Unwrap method returning a []error containing all the %w operands in the
// order they appear in the arguments.
// It is invalid to supply the %w verb with an operand that does not implement
// the error interface. The %w verb is otherwise a synonym for %v.
func Errorf(format string, a ...any) error {
p := newPrinter()
p.wrapErrs = true
p.doPrintf(format, a)
s := string(p.buf)
var err error
switch len(p.wrappedErrs) {
case 0:
err = errors.New(s)
case 1:
w := &wrapError{msg: s}
w.err, _ = a[p.wrappedErrs[0]].(error)
err = w
default:
if p.reordered {
sort.Ints(p.wrappedErrs)
}
var errs []error
for i, argNum := range p.wrappedErrs {
if i > 0 && p.wrappedErrs[i-1] == argNum {
continue
}
if e, ok := a[argNum].(error); ok {
errs = append(errs, e)
}
}
err = &wrapErrors{s, errs}
}
p.free()
return err
}
type wrapError struct {
msg string
err error
}
func (e *wrapError) Error() string {
return e.msg
}
func (e *wrapError) Unwrap() error {
return e.err
}
type wrapErrors struct {
msg string
errs []error
}
func (e *wrapErrors) Error() string {
return e.msg
}
func (e *wrapErrors) Unwrap() []error {
return e.errs
}

View File

@@ -1,24 +0,0 @@
/*
* Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package fmt
import (
_ "unsafe"
)
// // llgo:skipall
type _fmt struct{}

View File

@@ -1,594 +0,0 @@
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package fmt
import (
"strconv"
"unicode/utf8"
)
const (
ldigits = "0123456789abcdefx"
udigits = "0123456789ABCDEFX"
)
const (
signed = true
unsigned = false
)
// flags placed in a separate struct for easy clearing.
type fmtFlags struct {
widPresent bool
precPresent bool
minus bool
plus bool
sharp bool
space bool
zero bool
// For the formats %+v %#v, we set the plusV/sharpV flags
// and clear the plus/sharp flags since %+v and %#v are in effect
// different, flagless formats set at the top level.
plusV bool
sharpV bool
}
// A fmt is the raw formatter used by Printf etc.
// It prints into a buffer that must be set up separately.
type fmt struct {
buf *buffer
fmtFlags
wid int // width
prec int // precision
// intbuf is large enough to store %b of an int64 with a sign and
// avoids padding at the end of the struct on 32 bit architectures.
intbuf [68]byte
}
func (f *fmt) clearflags() {
f.fmtFlags = fmtFlags{}
}
func (f *fmt) init(buf *buffer) {
f.buf = buf
f.clearflags()
}
// writePadding generates n bytes of padding.
func (f *fmt) writePadding(n int) {
if n <= 0 { // No padding bytes needed.
return
}
buf := *f.buf
oldLen := len(buf)
newLen := oldLen + n
// Make enough room for padding.
if newLen > cap(buf) {
buf = make(buffer, cap(buf)*2+n)
copy(buf, *f.buf)
}
// Decide which byte the padding should be filled with.
padByte := byte(' ')
if f.zero {
padByte = byte('0')
}
// Fill padding with padByte.
padding := buf[oldLen:newLen]
for i := range padding {
padding[i] = padByte
}
*f.buf = buf[:newLen]
}
// pad appends b to f.buf, padded on left (!f.minus) or right (f.minus).
func (f *fmt) pad(b []byte) {
if !f.widPresent || f.wid == 0 {
f.buf.write(b)
return
}
width := f.wid - utf8.RuneCount(b)
if !f.minus {
// left padding
f.writePadding(width)
f.buf.write(b)
} else {
// right padding
f.buf.write(b)
f.writePadding(width)
}
}
// padString appends s to f.buf, padded on left (!f.minus) or right (f.minus).
func (f *fmt) padString(s string) {
if !f.widPresent || f.wid == 0 {
f.buf.writeString(s)
return
}
width := f.wid - utf8.RuneCountInString(s)
if !f.minus {
// left padding
f.writePadding(width)
f.buf.writeString(s)
} else {
// right padding
f.buf.writeString(s)
f.writePadding(width)
}
}
// fmtBoolean formats a boolean.
func (f *fmt) fmtBoolean(v bool) {
if v {
f.padString("true")
} else {
f.padString("false")
}
}
// fmtUnicode formats a uint64 as "U+0078" or with f.sharp set as "U+0078 'x'".
func (f *fmt) fmtUnicode(u uint64) {
buf := f.intbuf[0:]
// With default precision set the maximum needed buf length is 18
// for formatting -1 with %#U ("U+FFFFFFFFFFFFFFFF") which fits
// into the already allocated intbuf with a capacity of 68 bytes.
prec := 4
if f.precPresent && f.prec > 4 {
prec = f.prec
// Compute space needed for "U+" , number, " '", character, "'".
width := 2 + prec + 2 + utf8.UTFMax + 1
if width > len(buf) {
buf = make([]byte, width)
}
}
// Format into buf, ending at buf[i]. Formatting numbers is easier right-to-left.
i := len(buf)
// For %#U we want to add a space and a quoted character at the end of the buffer.
if f.sharp && u <= utf8.MaxRune && strconv.IsPrint(rune(u)) {
i--
buf[i] = '\''
i -= utf8.RuneLen(rune(u))
utf8.EncodeRune(buf[i:], rune(u))
i--
buf[i] = '\''
i--
buf[i] = ' '
}
// Format the Unicode code point u as a hexadecimal number.
for u >= 16 {
i--
buf[i] = udigits[u&0xF]
prec--
u >>= 4
}
i--
buf[i] = udigits[u]
prec--
// Add zeros in front of the number until requested precision is reached.
for prec > 0 {
i--
buf[i] = '0'
prec--
}
// Add a leading "U+".
i--
buf[i] = '+'
i--
buf[i] = 'U'
oldZero := f.zero
f.zero = false
f.pad(buf[i:])
f.zero = oldZero
}
// fmtInteger formats signed and unsigned integers.
func (f *fmt) fmtInteger(u uint64, base int, isSigned bool, verb rune, digits string) {
negative := isSigned && int64(u) < 0
if negative {
u = -u
}
buf := f.intbuf[0:]
// The already allocated f.intbuf with a capacity of 68 bytes
// is large enough for integer formatting when no precision or width is set.
if f.widPresent || f.precPresent {
// Account 3 extra bytes for possible addition of a sign and "0x".
width := 3 + f.wid + f.prec // wid and prec are always positive.
if width > len(buf) {
// We're going to need a bigger boat.
buf = make([]byte, width)
}
}
// Two ways to ask for extra leading zero digits: %.3d or %03d.
// If both are specified the f.zero flag is ignored and
// padding with spaces is used instead.
prec := 0
if f.precPresent {
prec = f.prec
// Precision of 0 and value of 0 means "print nothing" but padding.
if prec == 0 && u == 0 {
oldZero := f.zero
f.zero = false
f.writePadding(f.wid)
f.zero = oldZero
return
}
} else if f.zero && f.widPresent {
prec = f.wid
if negative || f.plus || f.space {
prec-- // leave room for sign
}
}
// Because printing is easier right-to-left: format u into buf, ending at buf[i].
// We could make things marginally faster by splitting the 32-bit case out
// into a separate block but it's not worth the duplication, so u has 64 bits.
i := len(buf)
// Use constants for the division and modulo for more efficient code.
// Switch cases ordered by popularity.
switch base {
case 10:
for u >= 10 {
i--
next := u / 10
buf[i] = byte('0' + u - next*10)
u = next
}
case 16:
for u >= 16 {
i--
buf[i] = digits[u&0xF]
u >>= 4
}
case 8:
for u >= 8 {
i--
buf[i] = byte('0' + u&7)
u >>= 3
}
case 2:
for u >= 2 {
i--
buf[i] = byte('0' + u&1)
u >>= 1
}
default:
panic("fmt: unknown base; can't happen")
}
i--
buf[i] = digits[u]
for i > 0 && prec > len(buf)-i {
i--
buf[i] = '0'
}
// Various prefixes: 0x, -, etc.
if f.sharp {
switch base {
case 2:
// Add a leading 0b.
i--
buf[i] = 'b'
i--
buf[i] = '0'
case 8:
if buf[i] != '0' {
i--
buf[i] = '0'
}
case 16:
// Add a leading 0x or 0X.
i--
buf[i] = digits[16]
i--
buf[i] = '0'
}
}
if verb == 'O' {
i--
buf[i] = 'o'
i--
buf[i] = '0'
}
if negative {
i--
buf[i] = '-'
} else if f.plus {
i--
buf[i] = '+'
} else if f.space {
i--
buf[i] = ' '
}
// Left padding with zeros has already been handled like precision earlier
// or the f.zero flag is ignored due to an explicitly set precision.
oldZero := f.zero
f.zero = false
f.pad(buf[i:])
f.zero = oldZero
}
// truncateString truncates the string s to the specified precision, if present.
func (f *fmt) truncateString(s string) string {
if f.precPresent {
n := f.prec
for i := range s {
n--
if n < 0 {
return s[:i]
}
}
}
return s
}
// truncate truncates the byte slice b as a string of the specified precision, if present.
func (f *fmt) truncate(b []byte) []byte {
if f.precPresent {
n := f.prec
for i := 0; i < len(b); {
n--
if n < 0 {
return b[:i]
}
wid := 1
if b[i] >= utf8.RuneSelf {
_, wid = utf8.DecodeRune(b[i:])
}
i += wid
}
}
return b
}
// fmtS formats a string.
func (f *fmt) fmtS(s string) {
s = f.truncateString(s)
f.padString(s)
}
// fmtBs formats the byte slice b as if it was formatted as string with fmtS.
func (f *fmt) fmtBs(b []byte) {
b = f.truncate(b)
f.pad(b)
}
// fmtSbx formats a string or byte slice as a hexadecimal encoding of its bytes.
func (f *fmt) fmtSbx(s string, b []byte, digits string) {
length := len(b)
if b == nil {
// No byte slice present. Assume string s should be encoded.
length = len(s)
}
// Set length to not process more bytes than the precision demands.
if f.precPresent && f.prec < length {
length = f.prec
}
// Compute width of the encoding taking into account the f.sharp and f.space flag.
width := 2 * length
if width > 0 {
if f.space {
// Each element encoded by two hexadecimals will get a leading 0x or 0X.
if f.sharp {
width *= 2
}
// Elements will be separated by a space.
width += length - 1
} else if f.sharp {
// Only a leading 0x or 0X will be added for the whole string.
width += 2
}
} else { // The byte slice or string that should be encoded is empty.
if f.widPresent {
f.writePadding(f.wid)
}
return
}
// Handle padding to the left.
if f.widPresent && f.wid > width && !f.minus {
f.writePadding(f.wid - width)
}
// Write the encoding directly into the output buffer.
buf := *f.buf
if f.sharp {
// Add leading 0x or 0X.
buf = append(buf, '0', digits[16])
}
var c byte
for i := 0; i < length; i++ {
if f.space && i > 0 {
// Separate elements with a space.
buf = append(buf, ' ')
if f.sharp {
// Add leading 0x or 0X for each element.
buf = append(buf, '0', digits[16])
}
}
if b != nil {
c = b[i] // Take a byte from the input byte slice.
} else {
c = s[i] // Take a byte from the input string.
}
// Encode each byte as two hexadecimal digits.
buf = append(buf, digits[c>>4], digits[c&0xF])
}
*f.buf = buf
// Handle padding to the right.
if f.widPresent && f.wid > width && f.minus {
f.writePadding(f.wid - width)
}
}
// fmtSx formats a string as a hexadecimal encoding of its bytes.
func (f *fmt) fmtSx(s, digits string) {
f.fmtSbx(s, nil, digits)
}
// fmtBx formats a byte slice as a hexadecimal encoding of its bytes.
func (f *fmt) fmtBx(b []byte, digits string) {
f.fmtSbx("", b, digits)
}
// fmtQ formats a string as a double-quoted, escaped Go string constant.
// If f.sharp is set a raw (backquoted) string may be returned instead
// if the string does not contain any control characters other than tab.
func (f *fmt) fmtQ(s string) {
s = f.truncateString(s)
if f.sharp && strconv.CanBackquote(s) {
f.padString("`" + s + "`")
return
}
buf := f.intbuf[:0]
if f.plus {
f.pad(strconv.AppendQuoteToASCII(buf, s))
} else {
f.pad(strconv.AppendQuote(buf, s))
}
}
// fmtC formats an integer as a Unicode character.
// If the character is not valid Unicode, it will print '\ufffd'.
func (f *fmt) fmtC(c uint64) {
// Explicitly check whether c exceeds utf8.MaxRune since the conversion
// of a uint64 to a rune may lose precision that indicates an overflow.
r := rune(c)
if c > utf8.MaxRune {
r = utf8.RuneError
}
buf := f.intbuf[:0]
f.pad(utf8.AppendRune(buf, r))
}
// fmtQc formats an integer as a single-quoted, escaped Go character constant.
// If the character is not valid Unicode, it will print '\ufffd'.
func (f *fmt) fmtQc(c uint64) {
r := rune(c)
if c > utf8.MaxRune {
r = utf8.RuneError
}
buf := f.intbuf[:0]
if f.plus {
f.pad(strconv.AppendQuoteRuneToASCII(buf, r))
} else {
f.pad(strconv.AppendQuoteRune(buf, r))
}
}
// fmtFloat formats a float64. It assumes that verb is a valid format specifier
// for strconv.AppendFloat and therefore fits into a byte.
func (f *fmt) fmtFloat(v float64, size int, verb rune, prec int) {
// Explicit precision in format specifier overrules default precision.
if f.precPresent {
prec = f.prec
}
// Format number, reserving space for leading + sign if needed.
num := strconv.AppendFloat(f.intbuf[:1], v, byte(verb), prec, size)
if num[1] == '-' || num[1] == '+' {
num = num[1:]
} else {
num[0] = '+'
}
// f.space means to add a leading space instead of a "+" sign unless
// the sign is explicitly asked for by f.plus.
if f.space && num[0] == '+' && !f.plus {
num[0] = ' '
}
// Special handling for infinities and NaN,
// which don't look like a number so shouldn't be padded with zeros.
if num[1] == 'I' || num[1] == 'N' {
oldZero := f.zero
f.zero = false
// Remove sign before NaN if not asked for.
if num[1] == 'N' && !f.space && !f.plus {
num = num[1:]
}
f.pad(num)
f.zero = oldZero
return
}
// The sharp flag forces printing a decimal point for non-binary formats
// and retains trailing zeros, which we may need to restore.
if f.sharp && verb != 'b' {
digits := 0
switch verb {
case 'v', 'g', 'G', 'x':
digits = prec
// If no precision is set explicitly use a precision of 6.
if digits == -1 {
digits = 6
}
}
// Buffer pre-allocated with enough room for
// exponent notations of the form "e+123" or "p-1023".
var tailBuf [6]byte
tail := tailBuf[:0]
hasDecimalPoint := false
sawNonzeroDigit := false
// Starting from i = 1 to skip sign at num[0].
for i := 1; i < len(num); i++ {
switch num[i] {
case '.':
hasDecimalPoint = true
case 'p', 'P':
tail = append(tail, num[i:]...)
num = num[:i]
case 'e', 'E':
if verb != 'x' && verb != 'X' {
tail = append(tail, num[i:]...)
num = num[:i]
break
}
fallthrough
default:
if num[i] != '0' {
sawNonzeroDigit = true
}
// Count significant digits after the first non-zero digit.
if sawNonzeroDigit {
digits--
}
}
}
if !hasDecimalPoint {
// Leading digit 0 should contribute once to digits.
if len(num) == 2 && num[1] == '0' {
digits--
}
num = append(num, '.')
}
for digits > 0 {
num = append(num, '0')
digits--
}
num = append(num, tail...)
}
// We want a sign if asked for and if the sign is not positive.
if f.plus || num[0] != '+' {
// If we're zero padding to the left we want the sign before the leading zeros.
// Achieve this by writing the sign out and then padding the unsigned number.
if f.zero && f.widPresent && f.wid > len(num) {
f.buf.writeByte(num[0])
f.writePadding(f.wid - len(num))
f.buf.write(num[1:])
return
}
f.pad(num)
return
}
// No sign to show and the number is positive; just print the unsigned number.
f.pad(num[1:])
}

File diff suppressed because it is too large Load Diff

View File

@@ -16,7 +16,6 @@
package crc32
// llgo:skipall
import (
"hash"
@@ -24,6 +23,9 @@ import (
"github.com/goplus/llgo/runtime/internal/clite/zlib"
)
// llgo:skipall
type _crc32 struct{}
// The size of a CRC-32 checksum in bytes.
const Size = 4

View File

@@ -16,16 +16,28 @@
package abi
// llgo:skipall
import (
"unsafe"
"github.com/goplus/llgo/runtime/abi"
)
// llgo:skipall
type _abi struct{}
type InterfaceType = abi.InterfaceType
func NoEscape(p unsafe.Pointer) unsafe.Pointer {
x := uintptr(p)
return unsafe.Pointer(x ^ 0)
}
func FuncPCABI0(f interface{}) uintptr {
words := (*[2]unsafe.Pointer)(unsafe.Pointer(&f))
return *(*uintptr)(unsafe.Pointer(words[1]))
}
func FuncPCABIInternal(f interface{}) uintptr {
words := (*[2]unsafe.Pointer)(unsafe.Pointer(&f))
return *(*uintptr)(unsafe.Pointer(words[1]))
}

View File

@@ -16,7 +16,6 @@
package bytealg
// llgo:skip init CompareString
import (
"unsafe"
@@ -24,6 +23,9 @@ import (
"github.com/goplus/llgo/runtime/internal/runtime"
)
// llgo:skip init CompareString
type _bytealg struct{}
func IndexByte(b []byte, ch byte) int {
ptr := unsafe.Pointer(unsafe.SliceData(b))
ret := c.Memchr(ptr, c.Int(ch), uintptr(len(b)))

View File

@@ -0,0 +1 @@
package cpu

View File

@@ -0,0 +1,40 @@
//go:build 386 || amd64
package cpu
/*
#if defined(__GNUC__) || defined(__clang__)
static void getcpuid(unsigned int eax, unsigned int ecx,
unsigned int *a, unsigned int *b,
unsigned int *c, unsigned int *d) {
#if defined(__i386__) || defined(__x86_64__)
__asm__ __volatile__(
"pushq %%rbp\n\t"
"movq %%rsp, %%rbp\n\t"
"andq $-16, %%rsp\n\t" // 16-byte align stack
"cpuid\n\t"
"movq %%rbp, %%rsp\n\t"
"popq %%rbp\n\t"
: "=a"(*a), "=b"(*b), "=c"(*c), "=d"(*d)
: "a"(eax), "c"(ecx)
: "memory"
);
#endif
}
#else
#error This code requires GCC or Clang
#endif
*/
import "C"
func cpuid(eaxArg, ecxArg uint32) (eax, ebx, ecx, edx uint32) {
C.getcpuid(
C.uint(eaxArg),
C.uint(ecxArg),
(*C.uint)(&eax),
(*C.uint)(&ebx),
(*C.uint)(&ecx),
(*C.uint)(&edx),
)
return
}

View File

@@ -8,12 +8,14 @@
// that are valid map keys.
package fmtsort
// llgo:skipall
import (
"reflect"
"sort"
)
// llgo:skipall
type _fmtsort struct{}
// Note: Throughout this package we avoid calling reflect.Value.Interface as
// it is not always legal to do so and it's easier to avoid the issue than to face it.

View File

@@ -0,0 +1,13 @@
package godebug
func setUpdate(update func(string, string)) {
println("todo: godebug.setUpdate")
}
func registerMetric(name string, read func() uint64) {
println("todo: godebug.registerMetric")
}
func setNewIncNonDefault(newIncNonDefault func(string) func()) {
println("todo: godebug.setNewIncNonDefault")
}

View File

@@ -4,9 +4,11 @@
// Simple conversions to avoid depending on strconv.
// llgo:skipall
package itoa
// llgo:skipall
type _itoa struct{}
// Itoa converts val to a decimal string.
func Itoa(val int) string {
if val < 0 {

View File

@@ -7,9 +7,11 @@
// These types are defined here to permit the syscall package to reference them.
package oserror
// llgo:skipall
import "errors"
// llgo:skipall
type _oserror struct{}
var (
ErrInvalid = errors.New("invalid argument")
ErrPermission = errors.New("permission denied")

View File

@@ -0,0 +1,45 @@
package poll
func runtime_Semacquire(sema *uint32) {
panic("todo: poll.runtime_Semacquire")
}
func runtime_Semrelease(sema *uint32) {
panic("todo: poll.runtime_Semrelease")
}
func runtime_pollServerInit() {
panic("todo: poll.runtime_pollServerInit")
}
func runtime_pollOpen(fd uintptr) (uintptr, int) {
panic("todo: poll.runtime_pollOpen")
}
func runtime_pollClose(ctx uintptr) {
panic("todo: poll.runtime_pollClose")
}
func runtime_pollWait(ctx uintptr, mode int) int {
panic("todo: poll.runtime_pollWait")
}
func runtime_pollWaitCanceled(ctx uintptr, mode int) {
panic("todo: poll.runtime_pollWaitCanceled")
}
func runtime_pollReset(ctx uintptr, mode int) int {
panic("todo: poll.runtime_pollReset")
}
func runtime_pollSetDeadline(ctx uintptr, d int64, mode int) {
panic("todo: poll.runtime_pollSetDeadline")
}
func runtime_pollUnblock(ctx uintptr) {
panic("todo: poll.runtime_pollUnblock")
}
func runtime_isPollServerDescriptor(fd uintptr) bool {
panic("todo: poll.runtime_isPollServerDescriptor")
}

View File

@@ -6,9 +6,11 @@
package execenv
// llgo:skipall
import "syscall"
// llgo:skipall
type _execenv struct{}
// Default will return the default environment
// variables based on the process attributes
// provided.

View File

@@ -6,13 +6,15 @@
package execenv
// llgo:skipall
import (
"internal/syscall/windows"
"syscall"
"unsafe"
)
// llgo:skipall
type _execenv struct{}
// Default will return the default environment
// variables based on the process attributes
// provided.

View File

@@ -0,0 +1,33 @@
#include <errno.h>
#include <stdint.h>
#include <fcntl.h>
#include <unistd.h>
struct fcntl_ret
{
int32_t r1; // Return value
int32_t err; // Error code
};
// llgo_fcntl implements the fcntl system call wrapper for Go
// fd: file descriptor
// cmd: fcntl command
// arg: command argument
struct fcntl_ret llgo_fcntl2(int32_t fd, int32_t cmd, int32_t arg)
{
struct fcntl_ret ret = {0};
int result = fcntl(fd, cmd, arg);
if (result == -1)
{
ret.err = errno;
ret.r1 = -1;
}
else
{
ret.err = 0;
ret.r1 = result;
}
return ret;
}

View File

@@ -2,14 +2,13 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build js && wasm
package unix
func IsNonblock(fd int) (nonblocking bool, err error) {
return false, nil
}
const (
AT_EACCESS = 0x10
AT_FDCWD = -0x2
AT_REMOVEDIR = 0x80
AT_SYMLINK_NOFOLLOW = 0x0020
func HasNonblockFlag(flag int) bool {
return false
}
UTIME_OMIT = -0x2
)

View File

@@ -0,0 +1,19 @@
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package unix
import "syscall"
const unlinkatTrap uintptr = syscall.SYS_UNLINKAT
const openatTrap uintptr = syscall.SYS_OPENAT
const (
AT_EACCESS = 0x200
AT_FDCWD = -0x64
AT_REMOVEDIR = 0x200
AT_SYMLINK_NOFOLLOW = 0x100
UTIME_OMIT = 0x3ffffffe
)

View File

@@ -0,0 +1,18 @@
// Copyright 2022 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build unix
package unix
const (
R_OK = 0x4
W_OK = 0x2
X_OK = 0x1
// NoFollowErrno is the error returned from open/openat called with
// O_NOFOLLOW flag, when the trailing component (basename) of the path
// is a symbolic link.
NoFollowErrno = noFollowErrno
)

View File

@@ -0,0 +1,23 @@
package unix
import (
"syscall"
_ "unsafe"
)
// llgo:skip fcntl
const (
LLGoPackage = "link"
LLGoFiles = "_unix/fcntl_unix.c"
)
//go:linkname fcntl C.llgo_fcntl2
func fcntl(fd int32, cmd int32, arg int32) (int32, int32)
func Fcntl(fd int, cmd int, arg int) (int, error) {
val, errno := fcntl(int32(fd), int32(cmd), int32(arg))
if val == -1 {
return int(val), syscall.Errno(errno)
}
return int(val), nil
}

View File

@@ -0,0 +1,14 @@
// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build dragonfly || freebsd
package unix
import "syscall"
// References:
// - https://man.freebsd.org/cgi/man.cgi?open(2)
// - https://man.dragonflybsd.org/?command=open&section=2
const noFollowErrno = syscall.EMLINK

View File

@@ -0,0 +1,10 @@
// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package unix
import "syscall"
// Reference: https://man.netbsd.org/open.2
const noFollowErrno = syscall.EFTYPE

View File

@@ -0,0 +1,22 @@
// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build unix && !dragonfly && !freebsd && !netbsd
package unix
import "syscall"
// POSIX.1-2008 says it's ELOOP. Most platforms follow:
//
// - aix: O_NOFOLLOW not documented (https://www.ibm.com/docs/ssw_aix_73/o_bostechref/open.html), assuming ELOOP
// - android: see linux
// - darwin: https://github.com/apple/darwin-xnu/blob/main/bsd/man/man2/open.2
// - hurd: who knows if it works at all (https://www.gnu.org/software/hurd/open_issues/open_symlink.html)
// - illumos: https://illumos.org/man/2/open
// - ios: see darwin
// - linux: https://man7.org/linux/man-pages/man2/openat.2.html
// - openbsd: https://man.openbsd.org/open.2
// - solaris: https://docs.oracle.com/cd/E23824_01/html/821-1463/open-2.html
const noFollowErrno = syscall.ELOOP

View File

@@ -1,23 +0,0 @@
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build unix
package unix
import "github.com/goplus/llgo/runtime/internal/clite/syscall"
/* TODO(xsw):
func IsNonblock(fd int) (nonblocking bool, err error) {
flag, e1 := Fcntl(fd, syscall.F_GETFL, 0)
if e1 != nil {
return false, e1
}
return flag&syscall.O_NONBLOCK != 0, nil
}
*/
func HasNonblockFlag(flag int) bool {
return flag&syscall.O_NONBLOCK != 0
}

View File

@@ -1,37 +0,0 @@
// Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build wasip1
package unix
import "github.com/goplus/llgo/runtime/internal/clite/syscall"
/* TODO(xsw):
import (
"syscall"
_ "unsafe" // for go:linkname
)
func IsNonblock(fd int) (nonblocking bool, err error) {
flags, e1 := fd_fdstat_get_flags(fd)
if e1 != nil {
return false, e1
}
return flags&syscall.FDFLAG_NONBLOCK != 0, nil
}
*/
func HasNonblockFlag(flag int) bool {
return flag&syscall.FDFLAG_NONBLOCK != 0
}
/* TODO(xsw):
// This helper is implemented in the syscall package. It means we don't have
// to redefine the fd_fdstat_get host import or the fdstat struct it
// populates.
//
//-go:linkname fd_fdstat_get_flags syscall.fd_fdstat_get_flags
func fd_fdstat_get_flags(fd int) (uint32, error)
*/

View File

@@ -17,8 +17,10 @@
package unix
import (
"syscall"
_ "unsafe"
)
// llgo:skipall
type _unix struct{}
func HasNonblockFlag(flag int) bool {
return flag&syscall.O_NONBLOCK != 0
}

View File

@@ -16,7 +16,6 @@
package big
// llgo:skipall
import (
"math/rand"
@@ -24,6 +23,9 @@ import (
"github.com/goplus/llgo/runtime/internal/clite/openssl"
)
// llgo:skipall
type _big struct{}
// A Word represents a single digit of a multi-precision unsigned integer.
type Word openssl.BN_ULONG

View File

@@ -16,13 +16,13 @@
package math
// llgo:skip sin cos
import (
_ "unsafe"
c "github.com/goplus/llgo/runtime/internal/clite"
)
// llgo:skip sin cos
const (
LLGoPackage = true
)

View File

@@ -17,7 +17,6 @@
// crypto/rand package.
package rand
// llgo:skipall
import (
"sync"
"sync/atomic"
@@ -28,6 +27,9 @@ import (
"github.com/goplus/llgo/runtime/internal/clite/time"
)
// llgo:skipall
type _rand struct{}
// A Source represents a source of uniformly-distributed
// pseudo-random int64 values in the range [0, 1<<63).
//

View File

@@ -0,0 +1,180 @@
package os
import (
"io/fs"
"sort"
origSyscall "syscall"
"unsafe"
c "github.com/goplus/llgo/runtime/internal/clite"
"github.com/goplus/llgo/runtime/internal/clite/os"
"github.com/goplus/llgo/runtime/internal/lib/internal/bytealg"
"github.com/goplus/llgo/runtime/internal/lib/syscall"
)
type readdirMode int
const (
readdirName readdirMode = iota
readdirDirEntry
readdirFileInfo
)
type DirEntry = fs.DirEntry
func (f *File) Readdirnames(n int) (names []string, err error) {
if f == nil {
return nil, ErrInvalid
}
entries, err := f.ReadDir(n)
if err != nil {
return nil, err
}
names = make([]string, len(entries))
for i, entry := range entries {
names[i] = entry.Name()
}
return names, err
}
func open(path string, flag int, perm uint32) (int, error) {
fd, err := syscall.Open(path, flag, perm)
return fd, err
}
func openDirNolog(name string) (*File, error) {
var (
r int
e error
)
ignoringEINTR(func() error {
r, e = open(name, O_RDONLY|origSyscall.O_CLOEXEC, 0)
return e
})
if e != nil {
return nil, &PathError{Op: "open", Path: name, Err: e}
}
if !supportsCloseOnExec {
origSyscall.CloseOnExec(r)
}
f := newFile(r, name, kindNoPoll)
return f, nil
}
func openDir(name string) (*File, error) {
return openDirNolog(name)
}
func ReadDir(name string) ([]DirEntry, error) {
f, err := openDir(name)
if err != nil {
return nil, err
}
defer f.Close()
dirs, err := f.ReadDir(-1)
sort.Slice(dirs, func(i, j int) bool {
return bytealg.CompareString(dirs[i].Name(), dirs[j].Name()) < 0
})
return dirs, err
}
//go:linkname c_fdopendir C.fdopendir
func c_fdopendir(fd c.Int) uintptr
func fdopendir(fd int) (dir uintptr, err error) {
return c_fdopendir(c.Int(fd)), nil
}
//go:linkname c_closedir C.closedir
func c_closedir(dir uintptr) c.Int
func closedir(dir uintptr) error {
if c_closedir(dir) != 0 {
return syscall.Errno(os.Errno())
}
return nil
}
//go:linkname c_readdir C.readdir
func c_readdir(dir uintptr) ([]syscall.Dirent, error)
func readdir(dir uintptr) ([]syscall.Dirent, error) {
return c_readdir(dir)
}
func (f *File) ReadDir(n int) (dirents []DirEntry, err error) {
if f == nil {
return nil, ErrInvalid
}
// Open directory using file descriptor
dir, err := fdopendir(int(f.fd))
if err != nil {
return nil, err
}
defer closedir(dir)
// Match Readdir and Readdirnames: don't return nil slices.
dirents = []DirEntry{}
// Read directory entries
for n < 0 || len(dirents) < n {
entries, err := readdir(dir)
if err != nil {
return dirents, err
}
if len(entries) == 0 {
break
}
for _, entry := range entries {
// Convert syscall.Dirent to fs.DirEntry
name := bytesToString((*[1024]byte)(unsafe.Pointer(&entry.Name[0]))[:])
if name == "." || name == ".." {
continue
}
typ := fs.FileMode(0)
switch entry.Type {
case origSyscall.DT_REG:
typ = 0
case origSyscall.DT_DIR:
typ = fs.ModeDir
case origSyscall.DT_LNK:
typ = fs.ModeSymlink
case origSyscall.DT_SOCK:
typ = fs.ModeSocket
case origSyscall.DT_FIFO:
typ = fs.ModeNamedPipe
case origSyscall.DT_CHR:
typ = fs.ModeCharDevice
case origSyscall.DT_BLK:
typ = fs.ModeDevice
}
dirents = append(dirents, &unixDirent{
parent: f.name,
name: name,
typ: typ,
})
if n > 0 && len(dirents) >= n {
break
}
}
}
return dirents, nil
}
// bytesToString converts byte slice to string without allocation.
func bytesToString(b []byte) string {
var i int
for i = 0; i < len(b) && b[i] != 0; i++ {
}
return string(b[0:i])
}

View File

@@ -16,7 +16,6 @@
package exec
// llgo:skipall
import (
"bytes"
"context"
@@ -33,6 +32,9 @@ import (
"github.com/goplus/llgo/runtime/internal/lib/internal/syscall/execenv"
)
// llgo:skipall
type _exec struct{}
// Error is returned by LookPath when it fails to classify a file as an
// executable.
type Error struct {

View File

@@ -59,8 +59,12 @@ func startProcess(name string, argv []string, attr *ProcAttr) (p *Process, err e
// runtime.KeepAlive(attr)
if e != nil {
return nil, &PathError{Op: "fork/exec", Path: name, Err: e}
// TODO(lijie): workaround with type assertion
if r, ok := e.(syscall.Errno); !ok || r != 0 {
return nil, &PathError{Op: "fork/exec", Path: name, Err: e}
}
}
println("StartProcess", pid, h, e)
return newProcess(pid, h), nil
}

View File

@@ -16,7 +16,6 @@
package os
// llgo:skipall
import (
"errors"
"runtime"
@@ -27,6 +26,9 @@ import (
"github.com/goplus/llgo/runtime/internal/clite/os"
)
// llgo:skipall
type _os struct{}
const (
LLGoPackage = true
)

View File

@@ -53,3 +53,7 @@ func MkdirAll(path string, perm FileMode) error {
}
return nil
}
func RemoveAll(path string) error {
return removeAll(path)
}

View File

@@ -0,0 +1,222 @@
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build unix
package os
import (
"io"
"syscall"
origSyscall "syscall"
"unsafe"
c "github.com/goplus/llgo/runtime/internal/clite"
"github.com/goplus/llgo/runtime/internal/clite/os"
"github.com/goplus/llgo/runtime/internal/lib/internal/syscall/unix"
)
func removeAll(path string) error {
if path == "" {
// fail silently to retain compatibility with previous behavior
// of RemoveAll. See issue 28830.
return nil
}
// The rmdir system call does not permit removing ".",
// so we don't permit it either.
if endsWithDot(path) {
return &PathError{Op: "RemoveAll", Path: path, Err: origSyscall.EINVAL}
}
// Simple case: if Remove works, we're done.
err := Remove(path)
if err == nil || IsNotExist(err) {
return nil
}
// RemoveAll recurses by deleting the path base from
// its parent directory
parentDir, base := splitPath(path)
parent, err := Open(parentDir)
if IsNotExist(err) {
// If parent does not exist, base cannot exist. Fail silently
return nil
}
if err != nil {
return err
}
defer parent.Close()
if err := removeAllFrom(parent, base); err != nil {
if pathErr, ok := err.(*PathError); ok {
pathErr.Path = parentDir + string(PathSeparator) + pathErr.Path
err = pathErr
}
return err
}
return nil
}
func removeAllFrom(parent *File, base string) error {
p, err := syscall.BytePtrFromString(base)
if err != nil {
return err
}
parentFd := int(parent.Fd())
// Simple case: if Unlink (aka remove) works, we're done.
err = ignoringEINTR(func() error {
if os.Unlinkat(c.Int(parentFd), (*c.Char)(unsafe.Pointer(p)), 0) < 0 {
return origSyscall.Errno(os.Errno())
}
return nil
})
if err == nil || IsNotExist(err) {
return nil
}
// EISDIR means that we have a directory, and we need to
// remove its contents.
// EPERM or EACCES means that we don't have write permission on
// the parent directory, but this entry might still be a directory
// whose contents need to be removed.
// Otherwise just return the error.
if err != origSyscall.EISDIR && err != origSyscall.EPERM && err != origSyscall.EACCES {
return &PathError{Op: "unlinkat", Path: base, Err: err}
}
uErr := err
// Remove the directory's entries.
var recurseErr error
for {
const reqSize = 1024
var respSize int
// Open the directory to recurse into
file, err := openDirAt(parentFd, base)
if err != nil {
if IsNotExist(err) {
return nil
}
if err == origSyscall.ENOTDIR || err == unix.NoFollowErrno {
// Not a directory; return the error from the unix.Unlinkat.
return &PathError{Op: "unlinkat", Path: base, Err: uErr}
}
recurseErr = &PathError{Op: "openfdat", Path: base, Err: err}
break
}
for {
numErr := 0
names, readErr := file.Readdirnames(reqSize)
// Errors other than EOF should stop us from continuing.
if readErr != nil && readErr != io.EOF {
file.Close()
if IsNotExist(readErr) {
return nil
}
return &PathError{Op: "readdirnames", Path: base, Err: readErr}
}
respSize = len(names)
for _, name := range names {
err := removeAllFrom(file, name)
if err != nil {
if pathErr, ok := err.(*PathError); ok {
pathErr.Path = base + string(PathSeparator) + pathErr.Path
}
numErr++
if recurseErr == nil {
recurseErr = err
}
}
}
// If we can delete any entry, break to start new iteration.
// Otherwise, we discard current names, get next entries and try deleting them.
if numErr != reqSize {
break
}
}
// Removing files from the directory may have caused
// the OS to reshuffle it. Simply calling Readdirnames
// again may skip some entries. The only reliable way
// to avoid this is to close and re-open the
// directory. See issue 20841.
file.Close()
// Finish when the end of the directory is reached
if respSize < reqSize {
break
}
}
// Remove the directory itself.
unlinkError := ignoringEINTR(func() error {
if os.Unlinkat(c.Int(parentFd), (*c.Char)(unsafe.Pointer(p)), unix.AT_REMOVEDIR) < 0 {
return origSyscall.Errno(os.Errno())
}
return nil
})
if unlinkError == nil || IsNotExist(unlinkError) {
return nil
}
if recurseErr != nil {
return recurseErr
}
return &PathError{Op: "unlinkat", Path: base, Err: unlinkError}
}
// openDirAt opens a directory name relative to the directory referred to by
// the file descriptor dirfd. If name is anything but a directory (this
// includes a symlink to one), it should return an error. Other than that this
// should act like openFileNolog.
//
// This acts like openFileNolog rather than OpenFile because
// we are going to (try to) remove the file.
// The contents of this file are not relevant for test caching.
func openDirAt(dirfd int, name string) (*File, error) {
p, err := syscall.BytePtrFromString(name)
if err != nil {
return nil, err
}
var r int
for {
var e error
r = int(os.Openat(c.Int(dirfd), (*c.Char)(unsafe.Pointer(p)), origSyscall.O_RDONLY|origSyscall.O_CLOEXEC|origSyscall.O_DIRECTORY|origSyscall.O_NOFOLLOW, 0))
if r >= 0 {
break
}
e = origSyscall.Errno(r)
// See comment in openFileNolog.
if e == origSyscall.EINTR {
continue
}
return nil, e
}
if !supportsCloseOnExec {
origSyscall.CloseOnExec(r)
}
// We use kindNoPoll because we know that this is a directory.
return newFile(r, name, kindNoPoll), nil
}
// endsWithDot reports whether the final component of path is ".".
func endsWithDot(path string) bool {
if path == "." {
return true
}
if len(path) >= 2 && path[len(path)-1] == '.' && IsPathSeparator(path[len(path)-2]) {
return true
}
return false
}

View File

@@ -0,0 +1,142 @@
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build !unix
package os
import (
"io"
"runtime"
"syscall"
)
func removeAll(path string) error {
if path == "" {
// fail silently to retain compatibility with previous behavior
// of RemoveAll. See issue 28830.
return nil
}
// The rmdir system call permits removing "." on Plan 9,
// so we don't permit it to remain consistent with the
// "at" implementation of RemoveAll.
if endsWithDot(path) {
return &PathError{Op: "RemoveAll", Path: path, Err: syscall.EINVAL}
}
// Simple case: if Remove works, we're done.
err := Remove(path)
if err == nil || IsNotExist(err) {
return nil
}
// Otherwise, is this a directory we need to recurse into?
dir, serr := Lstat(path)
if serr != nil {
if serr, ok := serr.(*PathError); ok && (IsNotExist(serr.Err) || serr.Err == syscall.ENOTDIR) {
return nil
}
return serr
}
if !dir.IsDir() {
// Not a directory; return the error from Remove.
return err
}
// Remove contents & return first error.
err = nil
for {
fd, err := Open(path)
if err != nil {
if IsNotExist(err) {
// Already deleted by someone else.
return nil
}
return err
}
const reqSize = 1024
var names []string
var readErr error
for {
numErr := 0
names, readErr = fd.Readdirnames(reqSize)
for _, name := range names {
err1 := RemoveAll(path + string(PathSeparator) + name)
if err == nil {
err = err1
}
if err1 != nil {
numErr++
}
}
// If we can delete any entry, break to start new iteration.
// Otherwise, we discard current names, get next entries and try deleting them.
if numErr != reqSize {
break
}
}
// Removing files from the directory may have caused
// the OS to reshuffle it. Simply calling Readdirnames
// again may skip some entries. The only reliable way
// to avoid this is to close and re-open the
// directory. See issue 20841.
fd.Close()
if readErr == io.EOF {
break
}
// If Readdirnames returned an error, use it.
if err == nil {
err = readErr
}
if len(names) == 0 {
break
}
// We don't want to re-open unnecessarily, so if we
// got fewer than request names from Readdirnames, try
// simply removing the directory now. If that
// succeeds, we are done.
if len(names) < reqSize {
err1 := Remove(path)
if err1 == nil || IsNotExist(err1) {
return nil
}
if err != nil {
// We got some error removing the
// directory contents, and since we
// read fewer names than we requested
// there probably aren't more files to
// remove. Don't loop around to read
// the directory again. We'll probably
// just get the same error.
return err
}
}
}
// Remove directory.
err1 := Remove(path)
if err1 == nil || IsNotExist(err1) {
return nil
}
if runtime.GOOS == "windows" && IsPermission(err1) {
if fs, err := Stat(path); err == nil {
if err = Chmod(path, FileMode(0200|int(fs.Mode()))); err == nil {
err1 = Remove(path)
}
}
}
if err == nil {
err = err1
}
return err
}

View File

@@ -0,0 +1,25 @@
package signal
func signal_disable(uint32) {
panic("signal_disable not implemented")
}
func signal_enable(uint32) {
panic("signal_enable not implemented")
}
func signal_ignore(uint32) {
panic("signal_ignore not implemented")
}
func signal_ignored(uint32) bool {
panic("signal_ignored not implemented")
}
func signal_recv() uint32 {
panic("signal_recv not implemented")
}
func signalWaitUntilIdle() {
panic("signalWaitUntilIdle not implemented")
}

View File

@@ -9,7 +9,7 @@ package os
import (
"time"
"github.com/goplus/llgo/runtime/internal/lib/syscall"
"github.com/goplus/llgo/runtime/internal/clite/syscall"
)
// A fileStat is the implementation of FileInfo returned by Stat and Lstat.

View File

@@ -0,0 +1,5 @@
package debug
func SetTraceback(level string) {
panic("todo: runtime/debug.SetTraceback")
}

View File

@@ -7,3 +7,7 @@ package runtime
func Caller(skip int) (pc uintptr, file string, line int, ok bool) {
panic("todo: runtime.Caller")
}
func Callers(skip int, pc []uintptr) int {
panic("todo: runtime.Callers")
}

View File

@@ -0,0 +1,6 @@
package syscall
import _ "unsafe"
// llgo:skipall
type _syscall6 struct{}

View File

@@ -0,0 +1,24 @@
package pprof
import (
"io"
)
// llgo:skipall
type Profile struct{}
func (p *Profile) WriteTo(w io.Writer, verbose bool) (int, error) {
panic("WriteTo not implemented")
}
func StartCPUProfile(w io.Writer) error {
panic("StartCPUProfile not implemented")
}
func StopCPUProfile() {
panic("StopCPUProfile not implemented")
}
func Lookup(name string) *Profile {
panic("Lookup not implemented")
}

View File

@@ -16,8 +16,22 @@
package runtime
/*
#include <unistd.h>
int llgo_maxprocs() {
#ifdef _SC_NPROCESSORS_ONLN
return (int)sysconf(_SC_NPROCESSORS_ONLN);
#else
return 1;
#endif
}
*/
import "C"
import (
_ "unsafe"
"unsafe"
"github.com/goplus/llgo/runtime/internal/clite/pthread"
)
// llgo:skipall
@@ -27,12 +41,23 @@ type _runtime struct{}
// GOROOT environment variable, if set at process start,
// or else the root used during the Go build.
func GOROOT() string {
/*
s := gogetenv("GOROOT")
if s != "" {
return s
}
return defaultGOROOT
*/
panic("todo: GOROOT")
return ""
}
//go:linkname c_maxprocs C.llgo_maxprocs
func c_maxprocs() int32
func GOMAXPROCS(n int) int {
return int(c_maxprocs())
}
func Goexit() {
pthread.Exit(nil)
}
func KeepAlive(x any) {
}
func write(fd uintptr, p unsafe.Pointer, n int32) int32 {
return int32(C.write(C.int(fd), p, C.size_t(n)))
}

View File

@@ -4,6 +4,10 @@
package runtime
import (
"runtime"
)
// Layout of in-memory per-function information prepared by linker
// See https://golang.org/s/go12symtab.
// Keep in sync with linker (../cmd/link/internal/ld/pcln.go:/pclntab)
@@ -11,3 +15,33 @@ package runtime
type _func struct {
unused [8]byte
}
func Stack(buf []byte, all bool) int {
panic("todo: runtime.Stack")
}
func StartTrace() error {
panic("todo: runtime.StartTrace")
}
func ReadTrace() []byte {
panic("todo: runtime.ReadTrace")
}
func StopTrace() {
panic("todo: runtime.StopTrace")
}
func ReadMemStats(m *runtime.MemStats) {
panic("todo: runtime.ReadMemStats")
}
func SetMutexProfileFraction(rate int) int {
panic("todo: runtime.SetMutexProfileFraction")
}
func SetBlockProfileRate(rate int) {
panic("todo: runtime.SetBlockProfileRate")
}
var MemProfileRate int = 512 * 1024

View File

@@ -0,0 +1,9 @@
//go:build !nogc
package runtime
import "github.com/goplus/llgo/runtime/internal/clite/bdwgc"
func GC() {
bdwgc.Gcollect()
}

View File

@@ -0,0 +1,7 @@
//go:build nogc
package runtime
func GC() {
}

View File

@@ -0,0 +1,5 @@
package trace
func userTaskEnd(id uint64) {
panic("todo: runtime/trace.userTaskEnd")
}

View File

@@ -0,0 +1,103 @@
package atomic
// An Int64 is an atomic int64. The zero value is zero.
type Int64 struct {
_ noCopy
_ align64
v int64
}
// Load atomically loads and returns the value stored in x.
func (x *Int64) Load() int64 { return LoadInt64(&x.v) }
// Store atomically stores val into x.
func (x *Int64) Store(val int64) { StoreInt64(&x.v, val) }
// Swap atomically stores new into x and returns the previous value.
func (x *Int64) Swap(new int64) (old int64) { return SwapInt64(&x.v, new) }
// CompareAndSwap executes the compare-and-swap operation for x.
func (x *Int64) CompareAndSwap(old, new int64) (swapped bool) {
return CompareAndSwapInt64(&x.v, old, new)
}
// Add atomically adds delta to x and returns the new value.
func (x *Int64) Add(delta int64) (new int64) { return AddInt64(&x.v, delta) }
// And atomically performs a bitwise AND operation on x using the bitmask
// provided as mask and returns the old value.
func (x *Int64) And(mask int64) (old int64) { return AndInt64(&x.v, mask) }
// Or atomically performs a bitwise OR operation on x using the bitmask
// provided as mask and returns the old value.
func (x *Int64) Or(mask int64) (old int64) { return OrInt64(&x.v, mask) }
// A Uint64 is an atomic uint64. The zero value is zero.
type Uint64 struct {
_ noCopy
_ align64
v uint64
}
// Load atomically loads and returns the value stored in x.
func (x *Uint64) Load() uint64 { return LoadUint64(&x.v) }
// Store atomically stores val into x.
func (x *Uint64) Store(val uint64) { StoreUint64(&x.v, val) }
// Swap atomically stores new into x and returns the previous value.
func (x *Uint64) Swap(new uint64) (old uint64) { return SwapUint64(&x.v, new) }
// CompareAndSwap executes the compare-and-swap operation for x.
func (x *Uint64) CompareAndSwap(old, new uint64) (swapped bool) {
return CompareAndSwapUint64(&x.v, old, new)
}
// Add atomically adds delta to x and returns the new value.
func (x *Uint64) Add(delta uint64) (new uint64) { return AddUint64(&x.v, delta) }
// And atomically performs a bitwise AND operation on x using the bitmask
// provided as mask and returns the old value.
func (x *Uint64) And(mask uint64) (old uint64) { return AndUint64(&x.v, mask) }
// Or atomically performs a bitwise OR operation on x using the bitmask
// provided as mask and returns the old value.
func (x *Uint64) Or(mask uint64) (old uint64) { return OrUint64(&x.v, mask) }
// noCopy may be added to structs which must not be copied
// after the first use.
//
// See https://golang.org/issues/8005#issuecomment-190753527
// for details.
//
// Note that it must not be embedded, due to the Lock and Unlock methods.
type noCopy struct{}
// Lock is a no-op used by -copylocks checker from `go vet`.
func (*noCopy) Lock() {}
func (*noCopy) Unlock() {}
// align64 may be added to structs that must be 64-bit aligned.
// This struct is recognized by a special case in the compiler
// and will not work if copied to any other package.
type align64 struct{}
// llgo:link AndInt64 llgo.atomicAnd
func AndInt64(addr *int64, mask int64) (old int64) {
panic("implement by llgo instruction")
}
// llgo:link AndUint64 llgo.atomicAnd
func AndUint64(addr *uint64, mask uint64) (old uint64) {
panic("implement by llgo instruction")
}
// llgo:link OrInt64 llgo.atomicOr
func OrInt64(addr *int64, mask int64) (old int64) {
panic("implement by llgo instruction")
}
// llgo:link OrUint64 llgo.atomicOr
func OrUint64(addr *uint64, mask uint64) (old uint64) {
panic("implement by llgo instruction")
}

View File

@@ -16,7 +16,6 @@
package sync
// llgo:skipall
import (
gosync "sync"
"unsafe"
@@ -25,6 +24,9 @@ import (
"github.com/goplus/llgo/runtime/internal/clite/pthread/sync"
)
// llgo:skipall
type _sync struct{}
// -----------------------------------------------------------------------------
type Mutex sync.Mutex

View File

@@ -17,16 +17,20 @@
package syscall
import (
origSyscall "syscall"
"unsafe"
c "github.com/goplus/llgo/runtime/internal/clite"
"github.com/goplus/llgo/runtime/internal/clite/os"
"github.com/goplus/llgo/runtime/internal/clite/syscall"
"github.com/goplus/llgo/runtime/internal/lib/internal/bytealg"
)
// llgo:skipall
type _syscall struct{}
type Iovec syscall.Iovec
type Timespec syscall.Timespec
type Timeval syscall.Timeval
@@ -192,3 +196,24 @@ func setrlimit(which int, lim *Rlimit) (err error) {
}
return Errno(ret)
}
func BytePtrFromString(s string) (*byte, error) {
a, err := ByteSliceFromString(s)
if err != nil {
return nil, err
}
return &a[0], nil
}
func ByteSliceFromString(s string) ([]byte, error) {
if bytealg.IndexByteString(s, 0) != -1 {
return nil, Errno(syscall.EINVAL)
}
a := make([]byte, len(s)+1)
copy(a, s)
return a, nil
}
func Accept(fd int) (nfd int, sa origSyscall.Sockaddr, err error) {
panic("todo: syscall.Accept")
}

View File

@@ -0,0 +1,5 @@
package syscall
func Sysctl(key string) (string, error) {
panic("todo: syscall.Sysctl")
}

View File

@@ -12,6 +12,7 @@
package syscall
import (
origSyscall "syscall"
_ "unsafe"
c "github.com/goplus/llgo/runtime/internal/clite"
@@ -126,3 +127,11 @@ func Faccessat(dirfd int, path string, mode uint32, flags int) (err error) {
func faccessat(dirfd c.Int, path *c.Char, mode c.Int, flags c.Int) c.Int
// -----------------------------------------------------------------------------
func Accept4(fd int, flags int) (nfd int, sa origSyscall.Sockaddr, err error) {
panic("todo: syscall.Accept4")
}
func Uname(buf *origSyscall.Utsname) (err error) {
panic("todo: syscall.Uname")
}

View File

@@ -22,6 +22,8 @@ var (
type Errno uintptr
type Dirent = syscall.Dirent
func (e Errno) Error() string {
ret := c.Strerror(c.Int(e))
return unsafe.String((*byte)(unsafe.Pointer(ret)), c.Strlen(ret))
@@ -69,3 +71,11 @@ func (s Signal) String() string {
*/
panic("todo: syscall.Signal.String")
}
func Mmap(fd int, offset int64, length int, prot int, flags int) (data []byte, err error) {
panic("todo: syscall.Mmap")
}
func Munmap(b []byte) (err error) {
panic("todo: syscall.Munmap")
}

View File

@@ -4,23 +4,27 @@
package time
import (
"sync"
"unsafe"
c "github.com/goplus/llgo/runtime/internal/clite"
"github.com/goplus/llgo/runtime/internal/clite/libuv"
)
// Sleep pauses the current goroutine for at least the duration d.
// A negative or zero duration causes Sleep to return immediately.
func Sleep(d Duration) {
panic("todo: time.Sleep")
c.Usleep(c.Uint(d.Nanoseconds()))
}
// Interface to timers implemented in package runtime.
// Must be in sync with ../runtime/time.go:/^type timer
type runtimeTimer struct {
pp uintptr
when int64
period int64
f func(any, uintptr) // NOTE: must not be closure
arg any
seq uintptr
nextwhen int64
status uint32
libuv.Timer
when int64
f func(any, uintptr)
arg any
}
// when is a helper function for setting the 'when' field of a runtimeTimer.
@@ -40,10 +44,6 @@ func when(d Duration) int64 {
return t
}
func startTimer(*runtimeTimer) { panic("todo: time.startTimer") }
func stopTimer(*runtimeTimer) bool { panic("todo: time.stopTimer") }
func resetTimer(*runtimeTimer, int64) bool { panic("todo: time.resetTimer") }
/* TODO(xsw):
func modTimer(t *runtimeTimer, when, period int64, f func(any, uintptr), arg any, seq uintptr) {
panic("todo: time.modTimer")
@@ -82,9 +82,6 @@ type Timer struct {
// If the caller needs to know whether f is completed, it must coordinate
// with f explicitly.
func (t *Timer) Stop() bool {
if t.r.f == nil {
panic("time: Stop called on uninitialized Timer")
}
return stopTimer(&t.r)
}
@@ -139,9 +136,6 @@ func NewTimer(d Duration) *Timer {
// one. If the caller needs to know whether the prior execution of
// f is completed, it must coordinate with f explicitly.
func (t *Timer) Reset(d Duration) bool {
if t.r.f == nil {
panic("time: Reset called on uninitialized Timer")
}
w := when(d)
return resetTimer(&t.r, w)
}
@@ -182,3 +176,64 @@ func AfterFunc(d Duration, f func()) *Timer {
func goFunc(arg any, seq uintptr) {
go arg.(func())()
}
var (
timerLoop *libuv.Loop
timerOnce sync.Once
)
func init() {
timerOnce.Do(func() {
timerLoop = libuv.LoopNew()
})
go func() {
timerLoop.Run(libuv.RUN_DEFAULT)
}()
}
// cross thread
func timerEvent(async *libuv.Async) {
a := (*asyncTimerEvent)(unsafe.Pointer(async))
a.cb()
a.Close(nil)
}
type asyncTimerEvent struct {
libuv.Async
cb func()
}
func timerCallback(t *libuv.Timer) {
}
func startTimer(r *runtimeTimer) {
asyncTimer := &asyncTimerEvent{
cb: func() {
libuv.InitTimer(timerLoop, &r.Timer)
r.Start(timerCallback, uint64(r.when), 0)
},
}
timerLoop.Async(&asyncTimer.Async, timerEvent)
asyncTimer.Send()
}
func stopTimer(r *runtimeTimer) bool {
asyncTimer := &asyncTimerEvent{
cb: func() {
r.Stop()
},
}
timerLoop.Async(&asyncTimer.Async, timerEvent)
return asyncTimer.Send() == 0
}
func resetTimer(r *runtimeTimer, when int64) bool {
asyncTimer := &asyncTimerEvent{
cb: func() {
r.Stop()
r.Start(timerCallback, uint64(when), 0)
},
}
timerLoop.Async(&asyncTimer.Async, timerEvent)
return asyncTimer.Send() == 0
}

View File

@@ -0,0 +1,36 @@
package time
import "unsafe"
type Ticker struct {
C <-chan Time // The channel on which the ticks are delivered.
}
func NewTicker(d Duration) *Ticker {
if d <= 0 {
panic("non-positive interval for NewTicker")
}
c := make(chan Time, 1)
t := &Timer{C: c}
t.C = c
startTimer(&t.r)
return (*Ticker)(unsafe.Pointer(t))
}
func (t *Ticker) Stop() {
stopTimer(&(*Timer)(unsafe.Pointer(t)).r)
}
func (t *Ticker) Reset(d Duration) {
if d <= 0 {
panic("non-positive interval for Ticker.Reset")
}
resetTimer(&(*Timer)(unsafe.Pointer(t)).r, when(d))
}
func Tick(d Duration) <-chan Time {
if d <= 0 {
return nil
}
return NewTicker(d).C
}

View File

@@ -187,6 +187,10 @@ func (t Time) Compare(u Time) int {
return 0
}
func (t Time) UnixNano() int64 {
return (t.unixSec())*1e9 + int64(t.nsec())
}
// Equal reports whether t and u represent the same time instant.
// Two times can be equal even if they are in different locations.
// For example, 6:00 +0200 and 4:00 UTC are Equal.