Compare commits

...

89 Commits

Author SHA1 Message Date
Marc Vertes
d64563edee interp: improve handling values and comparisons in interfaces
Fixes #1347.
2022-05-23 10:30:08 +02:00
Marc Vertes
07039262a0 interp: implements detection of packages with no Go files
This avoids panic during import, and print a proper diagnostic
instead.

Fixes #1394.
2022-05-19 18:34:08 +02:00
Marc Vertes
4ed9ccb5c4 interp: fix retrieving the string value of an interface
Thanks to @bailsman for providing first insight, in addition to
raising the issue.

Fixes #1342.
2022-05-19 18:20:09 +02:00
Marc Vertes
25edcfee7a interp: fix handling of empty interfaces in map index expressions
There should be no need now to wrap empty interfaces in order to
retrieve its value.

Fixes #1344.
2022-05-19 18:08:09 +02:00
Marc Vertes
d183f4205e interp: improve handling of empty interface values (#1393)
At variable, function parameter, slice, map or field element assign,
if the destination type is an empty interface, the value was never
wrapped into a valueInterface (to preserve type mutability in case
of re-assign). Now we wrap it in a valueInterface if the source
type has a non empty set of methods, to allow a future use as a non
empty interface.

There are still corner cases, but it extends notably the support
of interfaces within the interpreter.

Fixes #1355.
2022-05-19 17:53:56 +02:00
Marc Vertes
821e9ee006 interp: recover interpreter internal panics in EvalWithContext 2022-05-19 17:30:09 +02:00
Marc Vertes
00e3f924c1 interp: fix the behaviour of goto, continue and break (#1392)
The logic of goto was false due to mixing break label and goto
label, despite being opposite. In case of break, jump to node (the
exit point) instead of node.start. Also always define label symbols
before their use is parsed.

* Fix continue label, detect invalid labels for break and continue

* fix multiple goto, break, continue to the same label

Fixes #1354.
2022-05-19 11:23:30 +02:00
Marc Vertes
2248851d77 interp: fix creation of binary composite types (#1391)
* interp: fix creation of binary composite types

Use direct assignment instead of reflect.Value Set method to
initialize a binary composite type, as for non binary types.
It ensures that a new reflect.Value is stored in the frame
instead of modifying a possibly existing one, which can defeat
the purpose of initializing variables in a body loop.

While there, remove the need to have and use a mutex on types.

Fixes #1381.

* review: rework a bit the test

Co-authored-by: mpl <mathieu.lonjaret@gmail.com>
2022-05-05 21:31:10 +02:00
Marc Vertes
f74d1ea6d8 interp: detect invalid uses of _ as value
We now detect the use of special identifier _ (blank) during parsing in order to abort compiling early. It allows to not panic later during execution. We must catch all the cases where blank is used as a value, but still preserve the cases where it is assigned, used as a struct field or for import side effects.

Fixes #1386.
2022-05-04 18:51:09 +02:00
Marc Vertes
606b4c3a37 interp: fix import of binary type symbols in current scope (#1380)
Fixes #1360.
2022-05-04 17:27:11 +02:00
Ethan Reesor
4e77fc9436 interp: delete incomplete type on pkg import
When a package is imported, it creates a symbol with a name like "errors/_.go". If a statement such as x := errors.New(...) is executed before import "errors", it creates an incomplete type symbol with a name like "errors". Importing the package after the incomplete type symbol has been created does not fix the compile issue because the incomplete type still exists.

To fix this, this PR deletes the incomplete type symbol, if one exists.

Closes #1388.
2022-04-26 11:04:13 +02:00
Ethan Reesor
ad9db379e7 interp: add a function to get globals (#1387)
Co-authored-by: Marc Vertes <mvertes@free.fr>
2022-04-25 16:11:18 +02:00
Ethan Reesor
7be17d393f interp: expose fset to fix CompileAST issue
The interpreter has its own internal fileset and expects all code to have been parsed using that fileset. If a user creates a fileset, calls `go/parser.Parse*`, then passes the result to `interp.CompileAST`, strange things can happen.

The solutions I can see are:

1. Expose the fileset so the user can use it when parsing source.
2. Add the fileset as an option (input to New) so that the user can tell the interpreter to use a specific fileset.
3. Pass the fileset into `CompileAST`

There are two ways to implement option 3. One is to add a field to nodes and update every reference to `interp.fset` to use `node.fset`. The other is to add a parameter to every function that references `interp.fset` or calls a function that does. Both of these are significant changes and involve an extra pointer for every node or most function calls.

Options 1 and 2 are easy. Option 2 involves adding an option so I went with option 1. I can imagine situations where option 2 could be necessary, but I can open another issue/PR if and when I need that.

Fixes #1383
2022-04-22 11:48:08 +02:00
cclerget
5665c9a410 interp: fix redeclaration scope issue
Fixes #1378
2022-04-09 15:28:07 +02:00
cclerget
1cf9d345aa Ignore private methods for binary types during type assertion
Fixes #1373
2022-04-07 18:16:08 +02:00
Marc Vertes
f07f25f1ba interp: handle struct with multiple recursive fields (#1372)
* interp: handle struct with multiple recursive fields

In case of a recursive struct with several recursive fields of
different type, only the first one was properly fixed when
constructing the corresponding reflect type. We now memorize and
process all fields at the same depth level.

Fixes #1371.

* Update interp/type.go

Co-authored-by: mpl <mathieu.lonjaret@gmail.com>

* fix lint

* fix comment

Co-authored-by: mpl <mathieu.lonjaret@gmail.com>
2022-04-07 14:53:23 +02:00
cclerget
c93b836c77 Prevent variadic arguments from being wrapped as function
Fixes #1375
2022-04-07 14:24:07 +02:00
Marc Vertes
371103f0d1 interp: fix switch expression (#1370)
The control flow graph was incorrect for the initial clause.

Fixes #1368.
2022-04-06 22:07:51 +02:00
Marc Vertes
8bd7afbe62 interp: fix handling of redeclaration in multi-assign expression (#1367)
* interp: fix handling of redeclaration in multi-assign expression

In a statement like `a, b := f()` if `a` was previously declared,
its symbol must be reused, a new symbol must not override its
previous value. This is now fixed.

* In case of redeclaration, reuse the existing only if the redeclared
variable has the same type. Add _test/var16.go to check this use
case.

Fixes #1365.
2022-04-06 20:01:25 +02:00
Marc Vertes
8ea3a493f4 interp: fix interface conversion from binary call (#1366)
Fixes #1364.
2022-04-06 19:51:12 +02:00
Marc Vertes
f2abd346c0 interp: fix passing binary function as parameter
Wrap binary function values in a node if passing it
as a parameter to an interperter defined function.

Fixes #1361.
2022-04-05 17:34:08 +02:00
Marc Vertes
c784713aca interp: make methods passed as value preserve receiver
Fixes #1332.
2022-04-05 16:58:09 +02:00
Marc Vertes
14acf618af interp: improve type switch on binary interfaces
Fixes #1337.
2022-01-04 10:50:08 +01:00
Marc Vertes
fbee2baf9d interp: fix wrapping of returned closure passed to runtime
Fixes #1333.
2021-12-21 17:44:06 +01:00
Marc Vertes
2819b4167b interp: fix derivation of type of slice expression of binary object
Fixes #1328.
2021-12-20 15:46:05 +01:00
Marc Vertes
2af660cb1f interp: improve method resolution on embedded fields
The capability to dereference pointers has been added to
methodByName(), improving method lookup on binary values.

Wrapping to valueInterface is performed in a missing use case at
return of function calls. It was done in the nested call, but not
at assign.

Fixes #1330.
2021-12-20 15:06:11 +01:00
Marc Vertes
8323068414 interp: fix nested calls with variadic interfaces
Fixes #1326
2021-12-08 17:16:05 +01:00
Marc Vertes
5b62f9fdb6 interp: fix setting of interface value from nested function calls
Fixes #1320 and #1294.
2021-11-30 15:44:05 +01:00
Marc Vertes
4f66e3fe6c interp: fix type switch when the case type is an interpreted interface
Fixes #1315.

With this PR, the package gopkg.in/yaml.v3 works in yaegi (fixes #1296).
2021-11-26 12:24:07 +01:00
Marc Vertes
1335b4c64f stdlib: add wrappers for go/build/constraint package 2021-11-26 12:10:08 +01:00
Marc Vertes
da03c922ca interp: get rid of deprecated stdlib package ioutil
No functional change.
2021-11-26 11:58:07 +01:00
Marc Vertes
9620116c30 interp: replace value.Type().Kind() by value.Kind()
No functional change. Just simpler and faster.
2021-11-26 11:42:07 +01:00
Marc Vertes
dd7197f2a2 interp: fix assign of multiple return function call
A runtime builtin assignFromCall is added to handle multiple values returned at once. It is necessary if some of the values require to be set to interface values in the caller space, which is performed by reflect.Set in assignFromCall.
 
Fixes #1311.
2021-11-19 15:30:05 +01:00
Marc Vertes
9214806342 interp: fix populating array of interfaces
Fixes #1308.
2021-11-09 11:26:11 +01:00
Marc Vertes
348e713a8e interp: avoid collision between type and variable names in assign
Fixes #1306
2021-11-09 11:14:09 +01:00
Marc Vertes
cb81fe41ab interp: fix type processing to support multiple recursive fields
Fixes #1304
2021-11-08 20:46:12 +01:00
Marc Vertes
a876bb3673 interp: virtualize environment in restricted mode
In restricted mode, replace environment related symbols in
stdlib os package by a version which operates on a private copy
per interpreter context.

It allows to have concurrent interpreters in the same process
operating each in their own environment without affecting each
other or the host.

If unrestricted opt is set, this behaviour is disabled, and the
default symbols from stdlib are used.

Note also that no modification is done for syscall package, as it
should be not used in restricted mode.
2021-11-08 09:58:10 +01:00
Marc Vertes
afa46daccd interp: fix assignment to a dereferenced struct pointer
disable the optimization of skipping assign operation in that case,
as this step is necessary in case of writing to a pointer.

Fixes #1300.
2021-11-02 15:56:13 +01:00
Marc Vertes
3c00da291e stdlib: add wrappers for standard lib runtime/metrics package
This package was forgotten. No reason to not include it.
2021-11-02 15:20:06 +01:00
Marc Vertes
c847481184 interp: fix comparison operators in if statement
At generation of operator closures, the comparison expression
was hard-coded instead of being derived from the operator name,
leading to a wrong result.

Fixes #1297.
2021-11-02 15:02:07 +01:00
Marc Vertes
f46ef67180 interp: fix computation of ellipsis array length
Fixes #1287.
2021-10-20 14:36:05 +02:00
Marc Vertes
229ddfdae1 interp: fix use of builtins in type definitions
Make len() and cap() work on pointers. Preserve scope in case of
nested calls of cfg.

Fixes #1285.
2021-10-18 10:50:14 +02:00
Marc Vertes
aa7f0849e3 interp: fix goto to a label with no statement
Fixes #1288.
2021-10-18 10:36:12 +02:00
Bai-Yingjie
7617b8a090 interp: add args option to let each interpreter have seperate args
In some cases, we use many yaegi instances in one process, each interpreter will expect its own os.Args. This is done in interp.go as an option in interp.Options besides Stdin Stdout and Stderr.
2021-10-11 16:58:07 +02:00
Marc Vertes
4e06abe002 interp: fix unsafe2 to work on 32 bits architectures
Fixes #1279.
2021-10-11 11:38:12 +02:00
Bai-Yingjie
b1a758dd5a extract: add an option to be able to set build tag to the created file
Add a tag option in the extract program to optionally give an opportunity to tailor some packages in for example the stdlib, for people who is interested in keeping the final binary size as smaller as possible.
Below go generate
```
//go:generate ../cmd/extract/extract -name stdlib -tag stdmime mime mime/multipart mime/quotedprintable
```
produces a header to stdlib/mime-multipart.go
```
// Code generated by 'yaegi extract mime/multipart'. DO NOT EDIT.

// +build go1.16,!go1.17,stdmime
```
2021-10-11 10:50:11 +02:00
Marc Vertes
5bf4daef2d interp: fix type check of methods with a receiver of interface kind
Fixes #1280.
2021-10-08 17:44:09 +02:00
Marc Vertes
e56db3b82e interp: improve interface wrappers when used by reflect
When an interpreter type implementing an interface is
used by the runtime, the runtime can extract its type
and create new values using reflect, and call methods
on it. The problem is that there will be no interpreted
method counterpart in this case, which makes wrapper panic.

Allow the String() method wrapper to always succeed and
return an empty string if no interpreted method is present.

This allows scripts to define custom flag.Value types on
which the runtime internally instantiates values using
reflect (see isZeroValue in Go src/flag/flag.go).

This workaround could be generalized to all wrappers if
necessary. At this moment, it is convenient to keep the
default behavior of expecting instantiated interpreter
methods, in order to catch interpreter bugs related to
interfaces.

Fixes #1276.
2021-10-08 15:56:06 +02:00
Marc Vertes
d3bbe01d5c interp: improve support of composed interfaces
Fixes #1260.
2021-10-07 16:28:05 +02:00
Nicholas Wiersma
286d6c6359 interp: handle recursive and incomplete seen types in nodeType
This comes from experiments looking into #1259 where incomplete twice seen types are marked as complete. To mitigate the problem instead of a map of seen types in `nodeType` a slice is used as a cheap way to keep track of our current path through the node tree.
2021-09-27 10:20:13 +02:00
Nicholas Wiersma
84424b52bc interp: handle alias of an alias
When dealing with an alias of an alias, the actual underlying type of the source alias should be used.
2021-09-27 10:08:11 +02:00
Marc Vertes
98c2dcd3e5 interp: fix interface wrapper for struct types
This change fixes a regression introduced by PR #1192 in a program using
https://github.com/NYTimes/gziphandler which defines several types
implementing stdlib interfaces. We do not implement a wrapper
if we see that a type already implements an interface, except that
it can be falsly reported by reflect in case of a struct with
embedded interface field. We need to force the wrapper generation
in this case.
The problem occurs only for wrappers on struct, not on pointers or
other indirection types.
2021-09-24 13:20:12 +02:00
Ethan Reesor
808f0bde9d interp: add a function to directly compile Go AST
Adds CompileAST, which can be used to compile Go AST directly. This
allows users to delegate parsing of source to their own code instead of
relying on the interpreter.

CLoses #1251
2021-09-23 12:34:12 +02:00
Nicholas Wiersma
c5c6012947 interp: support type spec assign
This supports type spec assign types which disallows the attachment of methods onto the alias type as per the Go spec.

Fixes #1154
2021-09-23 12:16:11 +02:00
Nicholas Wiersma
836060c8ad interp: allow GTA revisit for unresolved define statements
When a define statement relies on a selector or type that may exist in another file it should revisit once GTA is complete. This allows that revisit.

**Note:** In order to keep the original GTA error for the define statement, so the error received is correct and meaningful, I have added a node `meta` property. I decided to make it generic as it may be useful in future. There may be a better way to stash errors across the GTA runs and am open to suggestion here.

Fixes #1253
2021-09-20 12:04:06 +02:00
Nicholas Wiersma
7a54353c7b chore: update linter to v1.42.1
* chore: update linter to v1.42.1

* fix: linter issues
2021-09-20 10:18:14 +02:00
Marc Vertes
b591ba0e78 interp: do not export RealFS, used internally only 2021-09-15 16:22:07 +02:00
Nicholas Wiersma
5af51aefe6 interp: fix type selector precedence
The current `nodeType` selector precedence is heavy handed in favour of package type. It seems to often create `typeSym` symbols as variable types in the scope will never be found. To fix this if the ancestor node is a field expression, the package type is searched for. After this, if the type is still `nil` the normal scope is searched using `nodeType2`.

Fixes #1158
2021-09-15 10:42:08 +02:00
mpl
e7c0f68bab interp: fix Clone documentation 2021-09-14 12:30:13 +02:00
Marc Vertes
bd9a6a4f8a interp: improve processing of recursive types
Make sure to keep always a single copy of incomplete type structures.
Remove remnants of recursive types processing.

Now `import "go.uber.org/zap"` works again (see #1172), fixing regressions
introduced since #1236.
2021-09-13 18:24:10 +02:00
Nicholas Wiersma
3eb2c79fd8 interp: fix nil funcs in composite literals
When a nil are used in a func composite literal, the nil type is a `func` not a `*node`. This handles this case.

Fixes #1249
2021-09-13 16:32:12 +02:00
Nicholas Wiersma
4653d87298 interp: types should not recover data for aliases
When `nodeType` recovers names and methods, it can overwrite the data if the type is an aliasT. When aliasing a type, do not recover the methods, this will be done in the GTA typeSpec pass.

Related to #1158
2021-09-06 18:24:04 +02:00
Nicholas Wiersma
45d569c215 interp: fix parsing of late binding consts
When a const is late binding and specified with a type, the GTA defineStmt was creating the symbol with the current scopes `iota` which is incorrect. The symbol should be created with the source nodes `rval`.

Related to #1158
2021-09-06 17:30:12 +02:00
Nicholas Wiersma
c33caeb573 interp: improve handling of wrapped interface values
This test (assert2.go) display 2 separate issues:
1. assert2.go L28: Type assert tries to set an `interface{}` to a `valueInterface`. The typing here is complex, we have a valueT(strings.Builder) wrapped in a ptrT wrapped in a src iface wrapped in a valueT(interface{}). Type assert fails to realise that the `valueT` `interface{}` is wrapping the `valueInterface`.
2. assert2.go L29: `genValueBinMethodOnInterface` does not try and get the bin method, as the `typ.node` (`ptrT` or a `valueT`(`string.Builder`)) is set. In this case the src iface is called with a receiver argument. To fix this the method is looked for first if possible, and only if not found does it fall back to the `defaultGen`.

Fixes #1227
2021-09-06 17:16:11 +02:00
Ethan Reesor
91a55cc4c5 interp: add debug interface
Adds an interface to `interp` that can be used to build an interactive debugger, such as those used by IDEs.

Closes #1188

All basic debugger features work, with one exception: breakpoints in some locations don't work, due to `setExec` creating a temporary `bltn` (https://github.com/traefik/yaegi/issues/1188#issuecomment-886107905).

Example, using a Debug Adapter implementation with VSCode:

![image](https://user-images.githubusercontent.com/879055/128620736-94b6efde-2e5f-43ad-82c9-d919c3fe401f.png)
2021-09-06 15:32:10 +02:00
Nicholas Wiersma
05f08d776a interp: complete type constructors
This completes the types constructors, cleaning up `nodeType`.
2021-09-01 14:46:08 +02:00
Nicholas Wiersma
d2569a85a6 interp: fix default types for runes
When using an untyped rune in an interface, the default type was blindly untyping it. This fixes this issue.

Fixes #1238
2021-08-31 12:32:10 +02:00
Nicholas Wiersma
772cd68fea interp: make use of type constructors
It was initially assumed that `nodeType` needed to rebuild the type from scratch, but this is not the case anymore. The existing type constructors are now used in `nodeType` to make it more readable. In doing this some bugs in type strings were found and fixed, along with adding the real package name to the type.

As `ptrOf` is now the only place that pointer types are constructed, it is feasible to cache the pointer type on the value type. To ensures that we have a consistent pointer type for any value type.
2021-08-31 10:34:12 +02:00
Nicholas Wiersma
4af992bccb interp: create real recursive types with unsafe type swapping
As the unsafe and pointer methods in `reflect` are to be depreciated, and seeing no replacement functions, it is now forced that some unsafe is needed to replace this as when and interface is dereferenced it is made unsettable by reflect.

With this in mind, this adds real recursive types by hot swapping the struct field type on the fly. This removes a lot of compensation code, simplifying all previous cases.

**Note:** While the struct field type is swapped for the real type, the type string is not changed. Due to this, unsafe will recreate the same type.
2021-08-30 18:38:12 +02:00
Nicholas Wiersma
da922ce90b interp: build type strings on the fly
This adds `itype.str` which is a string representation of the type built when the type is built. The goal is to make type comparison simpler and centralise the creation of types just to constructors and `nodeType`. `nodeType` continues to build types in parts so to reuse underlying types better.
2021-08-27 19:44:05 +02:00
Aloïs Micard
7b77b0fa22 test: only enable race detector when supported
Closes: #1228
2021-08-27 15:50:13 +02:00
Marc Vertes
b7f9a39eff interp: fix support of 32 bits and big-endian arch
Force recompute of bits.UintSize, make related tests portable.

Fixes #1230.
2021-08-27 14:54:05 +02:00
Marc Vertes
d2b25a7426 interp: fix append with 1 argument
Fixes #1224.
2021-08-24 12:16:08 +02:00
Johnny
b5bf4ef31a interp: allow for reading source files from diverse filesystems
Make use of fs.FS (new to go 1.16) to allow for reading source files from diverse filesystems (local, embed, custom).

* `Options` has a new field `SourcecodeFilesystem fs.FS` so users can supply their own read-only filesystem containing source code.
* Defaults to the local filesystems (via `RealFS` - a thin `os.Open` wrapper complying with `fs.FS`) so regular users should see no change in behaviour.
* When no filesystem is available (e.g. WASM, or if you want to embed files to retain single binary distribution) an alternative filesystem is preferable to using `Eval(string)` as that requires the stringy code to be a single file monolith instead of multiple files. By using an `fs.FS` we can use `EvalPath()` and gain the ability to handle multiple files and packages.
* You can make use of embed filesystems (https://pkg.go.dev/embed) and custom filesystems obeying the `fs.FS` interface (I use one for http served zip files when targeting wasm as there is no local filesystem on wasm). Tests can make use of `fstest.Map`.
* NOTE: This does NOT affect what the running yaegi code considers its local filesystem, this is only for the interpreter finding the source code.

See `example/fs/fs_test.go` for an example.

Fixes #1200.
2021-08-19 11:28:13 +02:00
Nicholas Wiersma
a69b9bc2dc stdlib: support go1.17 unsafe functions
This adds support for go1.17 `unsafe.Add` and `unsafe.Slice`.
2021-08-19 10:38:05 +02:00
Marc Vertes
b84278dcc6 stdlib: remove wrapper of runtime/cgo
Temporarily removing this wrapper which causes a failure
on freebsd system at build.

Fixes #1221.
2021-08-17 16:38:12 +02:00
Nicholas Wiersma
32cbcfb412 feat: update stdlib mapping for go1.17
* Drop go1.15
* Generate go1.17
* Update minimum extract version
* Update the CI config
2021-08-17 11:42:07 +02:00
Nicholas Wiersma
5c73f30f36 interp: fix handling of forward function declaration
Set node address in `val` field at creation of `funcDecl` node so it can be used correctly at closure generation, even in the case of forward function declarations, where the value was zero.

Fixes #1214
2021-08-16 16:24:05 +02:00
Nicholas Wiersma
5cc6fa42e4 feat: fix zero instance issue
When calling a function, if the input param is a "zero" instance, it is not set on the input. This is an issue where the param is an `interface{}` as a `nil` value is set instead of the zero value.

The actual solution for this is to remove the `if !val.IsZero()`, this however runs into an issue on `_test/struct48.go` where we have a zero recursive struct instance (`*interface{}`) and we have no way to get its real type. Should a way be figured out to keep tabs on the original type, the `if` can go away, and in the zero case of `genValueRecursiveInterfacePtrValue`, the actual zero type can be returned. 

Fixes #1215
2021-08-16 10:34:09 +02:00
Marc Vertes
37fe3422d8 interp: improve handling of interface struct field
Wrap non empty interface value  in struct field, to allow method
lookup.

Fixes #1208.
2021-08-06 12:16:10 +02:00
Dan Kortschak
d4e25f0259 interp: fix package lookup for type analysis of local types
Fixes #1203.
2021-08-02 12:14:11 +02:00
Marc Vertes
32ff3fb9b0 interp: fix handling of interface values in variadic calls
Fixes #1205.
2021-07-29 14:36:10 +02:00
Marc Vertes
b41fa6eb9d stdlib: add missing wrappers of io/fs for go1.16
Fixes #1195
2021-07-27 10:58:06 +02:00
Marc Vertes
c80c605ab9 interp: support wrapping of nested interfaces
Add getConcreteType to retrieve the concrete type of a nested interface
value implementing a specific interface for which a wrapper exists.

If method resolution fails at runtime, a panic is now issued instead
of an error message and continue.

Fixes #1187.
2021-07-26 19:12:11 +02:00
Marc Vertes
bf843fc09e interp: fix handling returned func values
Fixes #1202.
2021-07-26 17:42:05 +02:00
Marc Vertes
a913a4ea8b interp: fix handling map of interfaces
Map handling builtins getIndexMap and rangeMap had some leftover
code of previous way of emulating interfaces, which was modified
following changes in #1017.

Specific code for interfaceT is removed, as not necessary anymore.
Map builtins are now simplified and more robust.

Fixes #1189.
2021-07-26 17:30:12 +02:00
Marc Vertes
2f8493c405 interp: fix map update by assign operators
When operating on map elements, result of assign operators were not
written to the map entry. Now check if the destination of an assign
operator is in a map, and if so, set the result to it.

Fixes #1194.
2021-07-21 11:44:04 +02:00
Marc Vertes
c7fcfa8534 interp: fix interface wrapper generation
Add early detection of cases where no wrapper is necessary because
the value type already implements the target interface.

It should both increase performances by avoiding the wrapper overhead,
and fix errors due to replacing valid values by incomplete wrappers,
caused by the presence of private methods in the interface definition,
as in #1191.

Fixes #1191.
2021-07-19 15:38:11 +02:00
Ludovic Fernandez
aa012b992e chore: update linter
- update golangci-lint to v1.41.1
- fix GitHub action cache configuration
2021-07-17 12:14:06 +02:00
636 changed files with 18631 additions and 3781 deletions

View File

@@ -17,7 +17,7 @@ jobs:
strategy:
matrix:
go-version: [ 1.15, 1.16 ]
go-version: [ 1.16, 1.17 ]
os: [ubuntu-latest, macos-latest, windows-latest]
include:
@@ -45,11 +45,16 @@ jobs:
- name: Cache Go modules
uses: actions/cache@v2
with:
# In order:
# * Module download cache
# * Build cache (Linux)
# * Build cache (Mac)
# * Build cache (Windows)
path: |
~/go/pkg/mod # Module download cache
~/.cache/go-build # Build cache (Linux)
~/Library/Caches/go-build # Build cache (Mac)
'%LocalAppData%\go-build' # Build cache (Windows)
~/go/pkg/mod
~/.cache/go-build
~/Library/Caches/go-build
%LocalAppData%\go-build
key: ${{ runner.os }}-${{ matrix.go-version }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-${{ matrix.go-version }}-go-

View File

@@ -7,8 +7,8 @@ on:
pull_request:
env:
GO_VERSION: 1.16
GOLANGCI_LINT_VERSION: v1.36.0
GO_VERSION: 1.17
GOLANGCI_LINT_VERSION: v1.42.1
jobs:
@@ -45,7 +45,7 @@ jobs:
needs: linting
strategy:
matrix:
go-version: [ 1.15, 1.16 ]
go-version: [ 1.16, 1.17 ]
steps:
- name: Set up Go ${{ matrix.go-version }}
uses: actions/setup-go@v2
@@ -75,7 +75,7 @@ jobs:
working-directory: ${{ github.workspace }}/go/src/github.com/traefik/yaegi
strategy:
matrix:
go-version: [ 1.15, 1.16 ]
go-version: [ 1.16, 1.17 ]
steps:
- name: Set up Go ${{ matrix.go-version }}

View File

@@ -6,7 +6,7 @@ on:
- v[0-9]+.[0-9]+*
env:
GO_VERSION: 1.16
GO_VERSION: 1.17
jobs:

View File

@@ -23,13 +23,16 @@
[linters]
enable-all = true
disable = [
"maligned",
"golint", # deprecated
"scopelint", # deprecated
"interfacer", # deprecated
"maligned", # deprecated
"lll",
"gas",
"dupl",
"prealloc",
"scopelint",
"gocyclo",
"cyclop",
"gochecknoinits",
"gochecknoglobals",
"wsl",
@@ -49,6 +52,7 @@
"exhaustivestruct",
"forbidigo",
"ifshort",
"forcetypeassert",
"errorlint", # TODO: must be reactivate before fixes
]
@@ -59,8 +63,11 @@
exclude = []
[[issues.exclude-rules]]
path = "interp/.+_test\\.go"
path = ".+_test\\.go"
linters = ["goconst"]
[[issues.exclude-rules]]
path = ".+_test\\.go"
text = "var-declaration:"
[[issues.exclude-rules]]
path = "interp/interp.go"
@@ -68,6 +75,9 @@
[[issues.exclude-rules]]
path = "interp/interp.go"
text = "`out` can be `io.Writer`"
[[issues.exclude-rules]]
path = "interp/interp.go"
text = "`Panic` should conform to the `XxxError` format"
[[issues.exclude-rules]]
path = "interp/interp_eval_test.go"
linters = ["thelper"]

View File

@@ -18,7 +18,7 @@ It powers executable Go scripts and plugins, in embedded interpreters or interac
* Works everywhere Go works
* All Go & runtime resources accessible from script (with control)
* Security: `unsafe` and `syscall` packages neither used nor exported by default
* Support Go 1.15 and Go 1.16 (the latest 2 major releases)
* Support Go 1.16 and Go 1.17 (the latest 2 major releases)
## Install

View File

@@ -6,16 +6,18 @@ const (
zero = iota
one
two
three
)
func main() {
a := [...]string{
zero: "zero",
one: "one",
two: "two",
three: "three",
three + 2: "five",
}
fmt.Printf("%v %T\n", a, a)
}
// Output:
// [zero one two] [3]string
// [zero one three five] [6]string

18
_test/alias2.go Normal file
View File

@@ -0,0 +1,18 @@
package main
import "fmt"
func (t MyT) Test() string {
return "hello"
}
type MyT int
func main() {
t := MyT(1)
fmt.Println(t.Test())
}
// Output:
// hello

22
_test/alias3.go Normal file
View File

@@ -0,0 +1,22 @@
package main
import "github.com/traefik/yaegi/_test/alias3"
var globalT *T
func init() {
globalT = &T{A: "test"}
}
type T alias3.T
func (t *T) PrintT() {
(*alias3.T)(t).Print()
}
func main() {
globalT.PrintT()
}
// Output:
// test

9
_test/alias3/alias3.go Normal file
View File

@@ -0,0 +1,9 @@
package alias3
type T struct {
A string
}
func (t *T) Print() {
println(t.A)
}

27
_test/alias4.go Normal file
View File

@@ -0,0 +1,27 @@
package main
import (
"fmt"
"net/http"
)
type A http.Header
func (a A) Test1() {
fmt.Println("test1")
}
type B A
func (b B) Test2() {
fmt.Println("test2")
}
func main() {
b := B{}
b.Test2()
}
// Output:
// test2

37
_test/assert2.go Normal file
View File

@@ -0,0 +1,37 @@
package main
import (
"strings"
"sync"
)
// Defined an interface of stringBuilder that compatible with
// strings.Builder(go 1.10) and bytes.Buffer(< go 1.10)
type stringBuilder interface {
WriteRune(r rune) (n int, err error)
WriteString(s string) (int, error)
Reset()
Grow(n int)
String() string
}
var builderPool = sync.Pool{New: func() interface{} {
return newStringBuilder()
}}
func newStringBuilder() stringBuilder {
return &strings.Builder{}
}
func main() {
i := builderPool.Get()
sb := i.(stringBuilder)
_, _ = sb.WriteString("hello")
println(sb.String())
builderPool.Put(i)
}
// Output:
// hello

24
_test/break0.go Normal file
View File

@@ -0,0 +1,24 @@
package main
func main() {
n := 2
m := 2
foo := true
OuterLoop:
println("Boo")
for i := 0; i < n; i++ {
println("I: ", i)
for j := 0; j < m; j++ {
switch foo {
case true:
println(foo)
break OuterLoop
case false:
println(foo)
}
}
}
}
// Error:
// 15:5: invalid break label OuterLoop

24
_test/break1.go Normal file
View File

@@ -0,0 +1,24 @@
package main
func main() {
n := 2
m := 2
foo := true
OuterLoop:
for i := 0; i < n; i++ {
println("I: ", i)
for j := 0; j < m; j++ {
switch foo {
case true:
println(foo)
break OuterLoop
case false:
println(foo)
}
}
}
}
// Output:
// I: 0
// true

25
_test/break2.go Normal file
View File

@@ -0,0 +1,25 @@
package main
func main() {
n := 2
m := 2
foo := true
OuterLoop:
for i := 0; i < n; i++ {
println("I: ", i)
for j := 0; j < m; j++ {
switch foo {
case true:
println(foo)
break OuterLoop
case false:
println(foo)
continue OuterLoop
}
}
}
}
// Output:
// I: 0
// true

27
_test/break3.go Normal file
View File

@@ -0,0 +1,27 @@
package main
func main() {
n := 2
m := 2
foo := true
goto OuterLoop
println("Boo")
OuterLoop:
for i := 0; i < n; i++ {
println("I: ", i)
for j := 0; j < m; j++ {
switch foo {
case true:
println(foo)
break OuterLoop
case false:
println(foo)
goto OuterLoop
}
}
}
}
// Output:
// I: 0
// true

View File

@@ -1,13 +0,0 @@
// A test program
// +build darwin,linux !arm
// +build go1.12 !go1.13
package main
func main() {
println("hello world")
}
// Output:
// hello world

View File

@@ -2,7 +2,7 @@ package main
import (
"fmt"
"io/ioutil"
"io"
"log"
"net"
"net/http"
@@ -13,7 +13,7 @@ func client(uri string) {
if err != nil {
log.Fatal(err)
}
body, err := ioutil.ReadAll(resp.Body)
body, err := io.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}

View File

@@ -2,7 +2,7 @@ package main
import (
"fmt"
"io/ioutil"
"io"
"log"
"net"
"net/http"
@@ -21,7 +21,7 @@ func client(uri string) {
if err != nil {
log.Fatal(err)
}
body, err := ioutil.ReadAll(resp.Body)
body, err := io.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}

View File

@@ -2,7 +2,7 @@ package main
import (
"fmt"
"io/ioutil"
"io"
"log"
"net/http"
"net/http/httptest"
@@ -13,7 +13,7 @@ func client(uri string) {
if err != nil {
log.Fatal(err)
}
body, err := ioutil.ReadAll(resp.Body)
body, err := io.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}

View File

@@ -2,7 +2,7 @@ package main
import (
"fmt"
"io/ioutil"
"io"
"log"
"net/http"
"net/http/httptest"
@@ -40,7 +40,7 @@ func client(uri string) {
if err != nil {
log.Fatal(err)
}
body, err := ioutil.ReadAll(resp.Body)
body, err := io.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}

View File

@@ -2,7 +2,7 @@ package main
import (
"fmt"
"io/ioutil"
"io"
"log"
"net/http"
"net/http/httptest"
@@ -40,7 +40,7 @@ func client(uri string) {
if err != nil {
log.Fatal(err)
}
body, err := ioutil.ReadAll(resp.Body)
body, err := io.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}

View File

@@ -2,7 +2,7 @@ package main
import (
"fmt"
"io/ioutil"
"io"
"log"
"net/http"
"net/http/httptest"
@@ -41,7 +41,7 @@ func client(uri string) {
if err != nil {
log.Fatal(err)
}
body, err := ioutil.ReadAll(resp.Body)
body, err := io.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}

32
_test/composite18.go Normal file
View File

@@ -0,0 +1,32 @@
package main
import "fmt"
type fn func(string, string) bool
var funcs = []fn{
cmpLessFn,
cmpGreaterFn,
nil,
}
func cmpLessFn(a string, b string) bool {
return a < b
}
func cmpGreaterFn(a string, b string) bool {
return a > b
}
func main() {
for _, f := range funcs {
if f == nil {
continue
}
fmt.Println(f("a", "b"))
}
}
// Output:
// true
// false

33
_test/composite19.go Normal file
View File

@@ -0,0 +1,33 @@
package main
import "fmt"
type fn func(string, string) bool
var funcs = map[string]fn{
"less": cmpLessFn,
"greater": cmpGreaterFn,
"none": nil,
}
func cmpLessFn(a string, b string) bool {
return a < b
}
func cmpGreaterFn(a string, b string) bool {
return a > b
}
func main() {
for _, n := range []string{"less", "greater", "none"} {
f := funcs[n]
if f == nil {
continue
}
fmt.Println(f("a", "b"))
}
}
// Output:
// true
// false

View File

@@ -9,7 +9,7 @@ func main() {
const huge = 1 << 100
const large = huge >> 38
fmt.Println(large)
fmt.Println(int64(large))
}
// Output:

View File

@@ -2,7 +2,7 @@ package main
import "fmt"
const maxLen = int64(int(^uint(0) >> 1))
const maxLen = int64(int64(^uint64(0) >> 1))
func main() {
fmt.Println(maxLen)

28
_test/const26.go Normal file
View File

@@ -0,0 +1,28 @@
package main
import (
"fmt"
)
func init() {
fmt.Println(constString)
fmt.Println(const2)
fmt.Println(varString)
}
const constString string = "hello"
const (
const1 = iota + 10
const2
const3
)
var varString string = "test"
func main() {}
// Output:
// hello
// 11
// test

26
_test/cont2.go Normal file
View File

@@ -0,0 +1,26 @@
package main
func main() {
n := 2
m := 2
foo := true
OuterLoop:
for i := 0; i < n; i++ {
println("I: ", i)
for j := 0; j < m; j++ {
switch foo {
case true:
println(foo)
continue OuterLoop
case false:
println(foo)
}
}
}
}
// Output:
// I: 0
// true
// I: 1
// true

24
_test/cont3.go Normal file
View File

@@ -0,0 +1,24 @@
package main
func main() {
n := 2
m := 2
foo := true
OuterLoop:
println("boo")
for i := 0; i < n; i++ {
println("I: ", i)
for j := 0; j < m; j++ {
switch foo {
case true:
println(foo)
continue OuterLoop
case false:
println(foo)
}
}
}
}
// Error:
// 15:5: invalid continue label OuterLoop

View File

@@ -2,12 +2,11 @@ package main
import (
"fmt"
"io/ioutil"
"os"
)
func main() {
file, err := ioutil.TempFile("", "yeagibench")
file, err := os.CreateTemp("", "yeagibench")
if err != nil {
panic(err)
}
@@ -23,7 +22,7 @@ func main() {
panic(err)
}
b, err := ioutil.ReadFile(file.Name())
b, err := os.ReadFile(file.Name())
if err != nil {
panic(err)
}

25
_test/fun27.go Normal file
View File

@@ -0,0 +1,25 @@
package main
import (
"fmt"
"sync"
)
func main() {
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
print("test")
}()
wg.Wait()
}
func print(state string) {
fmt.Println(state)
}
// Output:
// test

17
_test/interface52.go Normal file
View File

@@ -0,0 +1,17 @@
package main
import "testing"
func main() {
t := testing.T{}
var tb testing.TB
tb = &t
if tb.TempDir() == "" {
println("FAIL")
return
}
println("PASS")
}
// Output:
// PASS

View File

@@ -2,7 +2,7 @@ package main
import (
"fmt"
"io/ioutil"
"io"
"log"
"strings"
)
@@ -10,7 +10,7 @@ import (
func main() {
r := strings.NewReader("Go is a general-purpose language designed with systems programming in mind.")
b, err := ioutil.ReadAll(r)
b, err := io.ReadAll(r)
if err != nil {
log.Fatal(err)
}

View File

@@ -10,4 +10,4 @@ func main() {
}
// Error:
// 9:6: cannot use type string as type int in assignment
// 9:6: cannot use type untyped string as type int in assignment

View File

@@ -7,4 +7,5 @@ func main() {
println(len(c))
}
// Output: 1
// Output:
// 1

46
_test/issue-1187.go Normal file
View File

@@ -0,0 +1,46 @@
package main
import (
"io"
"os"
)
type sink interface {
io.Writer
io.Closer
}
func newSink() sink {
// return os.Stdout // Stdout is special in yaegi tests
file, err := os.CreateTemp("", "yaegi-test.*")
if err != nil {
panic(err)
}
return file
}
func main() {
s := newSink()
n, err := s.Write([]byte("Hello\n"))
if err != nil {
panic(err)
}
var writer io.Writer = s
m, err := writer.Write([]byte("Hello\n"))
if err != nil {
panic(err)
}
var closer io.Closer = s
err = closer.Close()
if err != nil {
panic(err)
}
err = os.Remove(s.(*os.File).Name())
if err != nil {
panic(err)
}
println(m, n)
}
// Output:
// 6 6

31
_test/issue-1189.go Normal file
View File

@@ -0,0 +1,31 @@
package main
type I interface {
Foo() int
}
type S1 struct {
i int
}
func (s S1) Foo() int { return s.i }
type S2 struct{}
func (s *S2) Foo() int { return 42 }
func main() {
Is := map[string]I{
"foo": S1{21},
"bar": &S2{},
}
n := 0
for _, s := range Is {
n += s.Foo()
}
bar := "bar"
println(n, Is["foo"].Foo(), Is[bar].Foo())
}
// Output:
// 63 21 42

34
_test/issue-1202.go Normal file
View File

@@ -0,0 +1,34 @@
package main
import "fmt"
type foobar struct {
callback func(string) func()
}
func cb(text string) func() {
return func() {
fmt.Println(text)
}
}
func main() {
// These ways of invoking it all work...
cb("Hi from inline callback!")()
asVarTest1 := cb("Hi from asVarTest1 callback!")
asVarTest1()
asVarTest2 := cb
asVarTest2("Hi from asVarTest2 callback!")()
// But inside a struct panics in yaegi...
asStructField := &foobar{callback: cb}
asStructField.callback("Hi from struct field callback!")() // <--- panics here
}
// Output:
// Hi from inline callback!
// Hi from asVarTest1 callback!
// Hi from asVarTest2 callback!
// Hi from struct field callback!

25
_test/issue-1205.go Normal file
View File

@@ -0,0 +1,25 @@
package main
type Option interface {
apply()
}
func f(opts ...Option) {
for _, opt := range opts {
opt.apply()
}
}
type T struct{}
func (t *T) apply() { println("in apply") }
func main() {
opt := []Option{&T{}}
f(opt[0]) // works
f(opt...) // fails
}
// Output:
// in apply
// in apply

23
_test/issue-1208.go Normal file
View File

@@ -0,0 +1,23 @@
package main
type Enabler interface {
Enabled() bool
}
type Logger struct {
core Enabler
}
func (log *Logger) GetCore() Enabler { return log.core }
type T struct{}
func (t *T) Enabled() bool { return true }
func main() {
base := &Logger{&T{}}
println(base.GetCore().Enabled())
}
// Output:
// true

67
_test/issue-1260.go Normal file
View File

@@ -0,0 +1,67 @@
package main
import (
"fmt"
"io"
"os"
)
type WriteSyncer interface {
io.Writer
Sync() error
}
type Sink interface {
WriteSyncer
io.Closer
}
func newFileSink(path string) (Sink, error) {
return os.OpenFile(path, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0666)
}
type Sink1 struct{ name string }
func (s Sink1) Write(b []byte) (int, error) { println("in Write"); return 0, nil }
func (s Sink1) Sync() error { println("in Sync"); return nil }
func (s Sink1) Close() error { println("in Close", s.name); return nil }
func newS1(name string) Sink { return Sink1{name} }
func newS1p(name string) Sink { return &Sink1{name} }
type Sink2 struct{ name string }
func (s *Sink2) Write(b []byte) (int, error) { println("in Write"); return 0, nil }
func (s *Sink2) Sync() error { println("in Sync"); return nil }
func (s *Sink2) Close() error { println("in Close", s.name); return nil }
func newS2(name string) Sink { return Sink1{name} }
func main() {
tmpfile, err := os.CreateTemp("", "xxx")
if err != nil {
panic(err)
}
defer os.Remove(tmpfile.Name())
closers := []io.Closer{}
sink, err := newFileSink(tmpfile.Name())
if err != nil {
panic(err)
}
closers = append(closers, sink)
s1p := newS1p("ptr")
s1 := newS1("struct")
s2 := newS2("ptr2")
closers = append(closers, s1p, s1, s2)
for _, closer := range closers {
fmt.Println(closer.Close())
}
}
// Output:
// <nil>
// in Close ptr
// <nil>
// in Close struct
// <nil>
// in Close ptr2
// <nil>

24
_test/issue-1276.go Normal file
View File

@@ -0,0 +1,24 @@
package main
import (
"flag"
)
type customFlag struct{}
func (cf customFlag) String() string {
return "custom flag"
}
func (cf customFlag) Set(string) error {
return nil
}
func main() {
flag.Var(customFlag{}, "cf", "custom flag")
flag.Parse()
println("Hello, playground")
}
// Output:
// Hello, playground

30
_test/issue-1280.go Normal file
View File

@@ -0,0 +1,30 @@
package main
import (
"io"
"log"
"os"
)
type DBReader interface {
io.ReadCloser
io.ReaderAt
}
type DB struct {
f DBReader
}
func main() {
f, err := os.Open("/dev/null")
if err != nil {
log.Fatal(err)
}
d := &DB{f}
data := make([]byte, 1)
_, _ = d.f.ReadAt(data, 0)
println("bye")
}
// Output:
// bye

25
_test/issue-1285.go Normal file
View File

@@ -0,0 +1,25 @@
package main
type (
T1 struct{ Path [12]int8 }
T2 struct{ Path *[12]int8 }
)
var (
t11 = &T1{}
t21 = &T2{}
)
func main() {
b := [12]byte{}
t12 := &T1{}
t22 := &T2{}
b11 := (*[len(t11.Path)]byte)(&b)
b12 := (*[len(t12.Path)]byte)(&b)
b21 := (*[len(t21.Path)]byte)(&b)
b22 := (*[len(t22.Path)]byte)(&b)
println(len(b11), len(b12), len(b21), len(b22))
}
// Output:
// 12 12 12 12

10
_test/issue-1288.go Normal file
View File

@@ -0,0 +1,10 @@
package main
func main() {
println("Hi")
goto done
done:
}
// Output:
// Hi

20
_test/issue-1300.go Normal file
View File

@@ -0,0 +1,20 @@
package main
const buflen = 512
type T struct {
buf []byte
}
func f(t *T) { *t = T{buf: make([]byte, 0, buflen)} }
func main() {
s := T{}
println(cap(s.buf))
f(&s)
println(cap(s.buf))
}
// Output:
// 0
// 512

16
_test/issue-1304.go Normal file
View File

@@ -0,0 +1,16 @@
package main
type Node struct {
Name string
Alias *Node
Child []*Node
}
func main() {
n := &Node{Name: "parent"}
n.Child = append(n.Child, &Node{Name: "child"})
println(n.Name, n.Child[0].Name)
}
// Output:
// parent child

15
_test/issue-1306.go Normal file
View File

@@ -0,0 +1,15 @@
package main
import "fmt"
func check() (result bool, err error) {
return true, nil
}
func main() {
result, error := check()
fmt.Println(result, error)
}
// Output:
// true <nil>

27
_test/issue-1308.go Normal file
View File

@@ -0,0 +1,27 @@
package main
import "fmt"
type test struct {
v interface{}
s string
}
type T struct {
name string
}
func main() {
t := []test{
{
v: []interface{}{
T{"hello"},
},
s: "world",
},
}
fmt.Println(t)
}
// Output:
// [{[{hello}] world}]

19
_test/issue-1311.go Normal file
View File

@@ -0,0 +1,19 @@
package main
type T struct {
v interface{}
}
func f() (ret int64, err error) {
ret += 2
return
}
func main() {
t := &T{}
t.v, _ = f()
println(t.v.(int64))
}
// Output:
// 2

32
_test/issue-1315.go Normal file
View File

@@ -0,0 +1,32 @@
package main
type Intf interface {
M()
}
type T struct {
s string
}
func (t *T) M() { println("in M") }
func f(i interface{}) {
switch j := i.(type) {
case Intf:
j.M()
default:
println("default")
}
}
func main() {
var i Intf
var k interface{} = 1
i = &T{"hello"}
f(i)
f(k)
}
// Output:
// in M
// default

29
_test/issue-1320.go Normal file
View File

@@ -0,0 +1,29 @@
package main
type Pooler interface {
Get() string
}
type baseClient struct {
connPool Pooler
}
type connPool struct {
name string
}
func (c *connPool) Get() string { return c.name }
func newBaseClient(i int, p Pooler) *baseClient {
return &baseClient{connPool: p}
}
func newConnPool() *connPool { return &connPool{name: "connPool"} }
func main() {
b := newBaseClient(0, newConnPool())
println(b.connPool.(*connPool).name)
}
// Output:
// connPool

42
_test/issue-1326.go Normal file
View File

@@ -0,0 +1,42 @@
package main
type Option interface {
apply(*T)
}
type T struct {
s string
}
type opt struct {
name string
}
func (o *opt) apply(t *T) {
println(o.name)
}
func BuildOptions() []Option {
return []Option{
&opt{"opt1"},
&opt{"opt2"},
}
}
func NewT(name string, options ...Option) *T {
t := &T{name}
for _, opt := range options {
opt.apply(t)
}
return t
}
func main() {
t := NewT("hello", BuildOptions()...)
println(t.s)
}
// Output:
// opt1
// opt2
// hello

16
_test/issue-1328.go Normal file
View File

@@ -0,0 +1,16 @@
package main
import (
"crypto/sha1"
"encoding/hex"
)
func main() {
script := "hello"
sumRaw := sha1.Sum([]byte(script))
sum := hex.EncodeToString(sumRaw[:])
println(sum)
}
// Output:
// aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d

42
_test/issue-1330.go Normal file
View File

@@ -0,0 +1,42 @@
package main
import (
"fmt"
"io"
"net"
)
type wrappedConn struct {
net.Conn
}
func main() {
_, err := net.Listen("tcp", "127.0.0.1:49153")
if err != nil {
panic(err)
}
dialer := &net.Dialer{
LocalAddr: &net.TCPAddr{
IP: net.ParseIP("127.0.0.1"),
Port: 0,
},
}
conn, err := dialer.Dial("tcp", "127.0.0.1:49153")
if err != nil {
panic(err)
}
defer conn.Close()
t := &wrappedConn{conn}
var w io.Writer = t
if n, err := w.Write([]byte("hello")); err != nil {
fmt.Println(err)
} else {
fmt.Println(n)
}
}
// Output:
// 5

17
_test/issue-1332.go Normal file
View File

@@ -0,0 +1,17 @@
package main
func run(fn func(name string)) { fn("test") }
type T2 struct {
name string
}
func (t *T2) f(s string) { println(s, t.name) }
func main() {
t2 := &T2{"foo"}
run(t2.f)
}
// Output:
// test foo

37
_test/issue-1333.go Normal file
View File

@@ -0,0 +1,37 @@
package main
import (
"fmt"
"io"
"net/http"
"net/http/httptest"
)
func mock(name string) http.HandlerFunc {
return func(rw http.ResponseWriter, req *http.Request) {
fmt.Fprint(rw, "Hello ", name)
}
}
func client(uri string) {
resp, err := http.Get(uri)
if err != nil {
panic(err)
}
body, err := io.ReadAll(resp.Body)
if err != nil {
panic(err)
}
fmt.Println(string(body))
}
func main() {
mux := http.NewServeMux()
server := httptest.NewServer(mux)
defer server.Close()
mux.Handle("/", mock("foo"))
client(server.URL)
}
// Output:
// Hello foo

26
_test/issue-1337.go Normal file
View File

@@ -0,0 +1,26 @@
package main
import (
"io"
"os"
)
func f(i interface{}) {
switch at := i.(type) {
case int, int8:
println("integer", at)
case io.Reader:
println("reader")
}
println("bye")
}
func main() {
var fd *os.File
var r io.Reader = fd
f(r)
}
// Output:
// reader
// bye

12
_test/issue-1342.go Normal file
View File

@@ -0,0 +1,12 @@
package main
import "fmt"
func main() {
var a interface{}
a = "a"
fmt.Println(a, a == "a")
}
// Output:
// a true

13
_test/issue-1344.go Normal file
View File

@@ -0,0 +1,13 @@
package main
import "fmt"
func main() {
var m = map[string]interface{}{"a": "a"}
a, _ := m["a"]
b, ok := a.(string)
fmt.Println("a:", a, ", b:", b, ", ok:", ok)
}
// Output:
// a: a , b: a , ok: true

21
_test/issue-1354.go Normal file
View File

@@ -0,0 +1,21 @@
package main
func main() {
println(test()) // Go prints true, Yaegi false
}
func test() bool {
if true {
goto label
}
goto label
label:
println("Go continues here")
return true
println("Yaegi goes straight to this return (this line is never printed)")
return false
}
// Output:
// Go continues here
// true

22
_test/issue-1355.go Normal file
View File

@@ -0,0 +1,22 @@
package main
import "github.com/traefik/yaegi/_test/p2"
func f(i interface{}) {
_, ok := i.(p2.I)
println("ok:", ok)
}
func main() {
var v *p2.T
var i interface{}
i = v
_, ok := i.(p2.I)
println("ok:", ok)
f(v)
}
// Output:
// ok: true
// ok: true

14
_test/issue-1360.go Normal file
View File

@@ -0,0 +1,14 @@
package main
import (
"fmt"
. "net"
)
func main() {
v := IP{}
fmt.Println(v)
}
// Output:
// <nil>

27
_test/issue-1361.go Normal file
View File

@@ -0,0 +1,27 @@
package main
import (
"fmt"
"math"
)
type obj struct {
num float64
}
type Fun func(o *obj) (r *obj, err error)
func numFun(fn func(f float64) float64) Fun {
return func(o *obj) (*obj, error) {
return &obj{fn(o.num)}, nil
}
}
func main() {
f := numFun(math.Cos)
r, err := f(&obj{})
fmt.Println(r, err)
}
// Output:
// &{1} <nil>

16
_test/issue-1364.go Normal file
View File

@@ -0,0 +1,16 @@
package main
import (
"fmt"
"strconv"
)
func main() {
var value interface{}
var err error
value, err = strconv.ParseFloat("123", 64)
fmt.Println(value, err)
}
// Output:
// 123 <nil>

18
_test/issue-1365.go Normal file
View File

@@ -0,0 +1,18 @@
package main
func genInt() (int, error) { return 3, nil }
func getInt() (value int) {
value, err := genInt()
if err != nil {
panic(err)
}
return
}
func main() {
println(getInt())
}
// Output:
// 3

16
_test/issue-1368.go Normal file
View File

@@ -0,0 +1,16 @@
package main
const dollar byte = 36
func main() {
var c byte = 36
switch true {
case c == dollar:
println("ok")
default:
println("not ok")
}
}
// Output:
// ok

18
_test/issue-1371.go Normal file
View File

@@ -0,0 +1,18 @@
package main
import "fmt"
type node struct {
parent *node
child []*node
key string
}
func main() {
root := &node{key: "root"}
root.child = nil
fmt.Println("root:", root)
}
// Output:
// root: &{<nil> [] root}

17
_test/issue-1373.go Normal file
View File

@@ -0,0 +1,17 @@
package main
import (
"fmt"
"go/ast"
)
func NewBadExpr() ast.Expr {
return &ast.BadExpr{}
}
func main() {
fmt.Printf("%T\n", NewBadExpr().(*ast.BadExpr))
}
// Output:
// *ast.BadExpr

38
_test/issue-1375.go Normal file
View File

@@ -0,0 +1,38 @@
package main
import "fmt"
type Option func(*Struct)
func WithOption(opt string) Option {
return func(s *Struct) {
s.opt = opt
}
}
type Struct struct {
opt string
}
func New(opts ...Option) *Struct {
s := new(Struct)
for _, opt := range opts {
opt(s)
}
return s
}
func (s *Struct) ShowOption() {
fmt.Println(s.opt)
}
func main() {
opts := []Option{
WithOption("test"),
}
s := New(opts...)
s.ShowOption()
}
// Output:
// test

21
_test/issue-1378.go Normal file
View File

@@ -0,0 +1,21 @@
package main
import (
"fmt"
"time"
)
func main() {
t, err := time.Parse(time.RFC3339, "2006-01-02T15:04:05Z")
if err != nil {
panic(err)
}
fn := func() error {
_, err := t.GobEncode()
return err
}
fmt.Println(fn())
}
// Output:
// <nil>

58
_test/issue-1381.go Normal file
View File

@@ -0,0 +1,58 @@
package main
import (
"bytes"
"fmt"
)
func main() {
var bufPtrOne *bytes.Buffer
var bufPtrTwo *bytes.Buffer
var bufPtrThree *bytes.Buffer
var bufPtrFour *bytes.Buffer
for i := 0; i < 2; i++ {
bufOne := bytes.Buffer{}
bufTwo := &bytes.Buffer{}
var bufThree bytes.Buffer
bufFour := new(bytes.Buffer)
if bufPtrOne == nil {
bufPtrOne = &bufOne
} else if bufPtrOne == &bufOne {
fmt.Println("bufOne was not properly redeclared")
} else {
fmt.Println("bufOne is properly redeclared")
}
if bufPtrTwo == nil {
bufPtrTwo = bufTwo
} else if bufPtrTwo == bufTwo {
fmt.Println("bufTwo was not properly redeclared")
} else {
fmt.Println("bufTwo is properly redeclared")
}
if bufPtrThree == nil {
bufPtrThree = &bufThree
} else if bufPtrThree == &bufThree {
fmt.Println("bufThree was not properly redeclared")
} else {
fmt.Println("bufThree is properly redeclared")
}
if bufPtrFour == nil {
bufPtrFour = bufFour
} else if bufPtrFour == bufFour {
fmt.Println("bufFour was not properly redeclared")
} else {
fmt.Println("bufFour is properly redeclared")
}
}
}
// Output:
// bufOne is properly redeclared
// bufTwo is properly redeclared
// bufThree is properly redeclared
// bufFour is properly redeclared

View File

@@ -3,7 +3,6 @@ package main
import (
"fmt"
"io"
"io/ioutil"
"log"
"strings"
)
@@ -36,7 +35,7 @@ type pipe struct {
func newReadAutoCloser(r io.Reader) readAutoCloser {
if _, ok := r.(io.Closer); !ok {
return readAutoCloser{ioutil.NopCloser(r)}
return readAutoCloser{io.NopCloser(r)}
}
return readAutoCloser{r.(io.ReadCloser)}
}
@@ -44,7 +43,7 @@ func newReadAutoCloser(r io.Reader) readAutoCloser {
func main() {
p := &pipe{}
p.Reader = newReadAutoCloser(strings.NewReader("test"))
b, err := ioutil.ReadAll(p.Reader)
b, err := io.ReadAll(p.Reader)
if err != nil {
log.Fatal(err)
}

14
_test/method38.go Normal file
View File

@@ -0,0 +1,14 @@
package main
import (
"fmt"
"github.com/traefik/yaegi/_test/method38"
)
func main() {
fmt.Println(method38.Get())
}
// Output:
// &{[] {<nil>}}

19
_test/method38/a.go Normal file
View File

@@ -0,0 +1,19 @@
package method38
import "sync"
func NewPool() Pool { return Pool{} }
type Buffer struct {
bs []byte
pool Pool
}
type Pool struct {
p *sync.Pool
}
var (
_pool = NewPool()
Get = _pool.Get
)

3
_test/method38/b.go Normal file
View File

@@ -0,0 +1,3 @@
package method38
func (p Pool) Get() *Buffer { return &Buffer{} }

32
_test/method39.go Normal file
View File

@@ -0,0 +1,32 @@
package main
import (
"fmt"
"sync"
"github.com/traefik/yaegi/_test/method38"
)
func NewPool() Pool { return Pool{} }
type Buffer struct {
bs []byte
pool Pool
}
type Pool struct {
p *sync.Pool
}
var (
_pool = NewPool()
Get = _pool.Get
)
func main() {
fmt.Println(Get())
}
// Error:
// 17:11: undefined selector Get

View File

@@ -7,4 +7,4 @@ func main() {
}
// Error:
// 5:2: invalid operation: mismatched types int and float64
// 5:2: invalid operation: mismatched types int and untyped float

View File

@@ -2,11 +2,11 @@ package main
import (
"fmt"
"io/ioutil"
"os"
)
func main() {
_, err := ioutil.ReadFile("__NotExisting__")
_, err := os.ReadFile("__NotExisting__")
if err != nil {
fmt.Println(err.Error())
}

9
_test/p2/p2.go Normal file
View File

@@ -0,0 +1,9 @@
package p2
type I interface {
isI()
}
type T struct{}
func (t *T) isI() {}

0
_test/p3/empty Normal file
View File

22
_test/rune2.go Normal file
View File

@@ -0,0 +1,22 @@
package main
import "fmt"
const majorVersion = '2'
type hashed struct {
major byte
}
func main() {
fmt.Println(majorVersion)
p := new(hashed)
p.major = majorVersion
fmt.Println(p)
}
// Output:
// 50
// &{50}

View File

@@ -9,10 +9,20 @@ func test(time string, t time.Time) string {
return time
}
var zero = time.Time{}
func test2(time string) time.Time {
return zero
}
func main() {
str := test("test", time.Now())
fmt.Println(str)
str2 := test2("test2")
fmt.Println(str2)
}
// Output:
// test
// 0001-01-01 00:00:00 +0000 UTC

View File

@@ -8,6 +8,7 @@ type A struct {
}
type D struct {
F *A
E *A
}

20
_test/struct60.go Normal file
View File

@@ -0,0 +1,20 @@
package main
import (
"fmt"
)
type data struct {
S string
}
func render(v interface{}) {
fmt.Println(v)
}
func main() {
render(data{})
}
// Output:
// {}

22
_test/struct61.go Normal file
View File

@@ -0,0 +1,22 @@
package main
import "fmt"
type A struct {
B string
D
}
type D struct {
*A
E *A
}
func main() {
a := &A{B: "b"}
a.D = D{E: a}
fmt.Println(a.D.E.B)
}
// Output:
// b

11
_test/struct62.go Normal file
View File

@@ -0,0 +1,11 @@
package main
func main() {
type A struct{ *A }
v := &A{}
v.A = v
println("v.A.A = v", v.A.A == v)
}
// Output:
// v.A.A = v true

17
_test/type27.go Normal file
View File

@@ -0,0 +1,17 @@
package main
import "fmt"
type Foo = int
func (f Foo) Bar() int {
return f * f
}
func main() {
x := Foo(1)
fmt.Println(x.Bar())
}
// Error:
// 7:1: cannot define new methods on non-local type int

17
_test/type28.go Normal file
View File

@@ -0,0 +1,17 @@
package main
import "fmt"
type Foo = int
func (f *Foo) Bar() int {
return *f * *f
}
func main() {
x := Foo(1)
fmt.Println(x.Bar())
}
// Error:
// 7:1: cannot define new methods on non-local type int

12
_test/type29.go Normal file
View File

@@ -0,0 +1,12 @@
package main
var Foo int
func (f Foo) Bar() int {
return 1
}
func main() {}
// Error:
// 5:1: cannot define new methods on non-local type int

12
_test/type30.go Normal file
View File

@@ -0,0 +1,12 @@
package main
var Foo *int
func (f Foo) Bar() int {
return 1
}
func main() {}
// Error:
// 5:1: cannot define new methods on non-local type int

17
_test/type31.go Normal file
View File

@@ -0,0 +1,17 @@
package main
import "fmt"
func (f Foo) Bar() int {
return f * f
}
type Foo = int
func main() {
x := Foo(1)
fmt.Println(x.Bar())
}
// Error:
// 5:1: cannot define new methods on non-local type int

17
_test/type32.go Normal file
View File

@@ -0,0 +1,17 @@
package main
import "fmt"
func (f *Foo) Bar() int {
return *f * *f
}
type Foo = int
func main() {
x := Foo(1)
fmt.Println(x.Bar())
}
// Error:
// 5:1: cannot define new methods on non-local type int

11
_test/type33.go Normal file
View File

@@ -0,0 +1,11 @@
package main
func (f *Foo) Bar() int {
return *f * *f
}
func main() {
}
// Error:
// 3:1: undefined: Foo

View File

@@ -1,11 +1,14 @@
package main
import (
"fmt"
"math/bits"
"unsafe"
)
const SSize = 16
const (
SSize = 16
WSize = bits.UintSize / 8
)
type S struct {
X int
@@ -13,14 +16,19 @@ type S struct {
}
func main() {
bigEndian := (*(*[2]uint8)(unsafe.Pointer(&[]uint16{1}[0])))[0] == 0
var sBuf [SSize]byte
s := (*S)(unsafe.Pointer(&sBuf[0]))
s.X = 2
s.Y = 4
fmt.Println(sBuf)
if bigEndian {
println(sBuf[0+WSize-1], sBuf[WSize+WSize-1])
} else {
println(sBuf[0], sBuf[WSize])
}
}
// Output:
// [2 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0]
// 2 4

View File

@@ -2,10 +2,11 @@ package main
import (
"fmt"
"math/bits"
"unsafe"
)
const SSize = 24
const WSize = bits.UintSize / 8
type S struct {
X int
@@ -20,7 +21,8 @@ func main() {
{X: 3},
}
addr := unsafe.Pointer(&arr[0])
s := *(*S)(unsafe.Pointer(uintptr(addr) + SSize*2))
// s := *(*S)(unsafe.Pointer(uintptr(addr) + SSize*2))
s := *(*S)(unsafe.Pointer(uintptr(addr) + WSize*6))
fmt.Println(s.X)
}

View File

@@ -1,10 +1,12 @@
package main
import (
"fmt"
"math/bits"
"unsafe"
)
const WSize = bits.UintSize / 8
type S struct {
X int
Y int
@@ -13,12 +15,12 @@ type S struct {
func main() {
x := S{}
size := unsafe.Sizeof(x)
align := unsafe.Alignof(x.Y)
offset := unsafe.Offsetof(x.Z)
size := unsafe.Sizeof(x) / WSize
align := unsafe.Alignof(x.Y) / WSize
offset := unsafe.Offsetof(x.Z) / WSize
fmt.Println(size, align, offset)
println(size, align, offset)
}
// Output:
// 24 8 16
// 3 1 2

23
_test/unsafe6.go Normal file
View File

@@ -0,0 +1,23 @@
package main
import (
"fmt"
"unsafe"
)
type S struct {
X int
Y int
Z int
}
func main() {
x := S{Z: 5}
ptr := unsafe.Pointer(&x)
offset := int(unsafe.Offsetof(x.Z))
p := unsafe.Add(ptr, offset)
i := *(*int)(p)
fmt.Println(i)
}

20
_test/unsafe7.go Normal file
View File

@@ -0,0 +1,20 @@
package main
import (
"fmt"
"unsafe"
)
type S struct {
X int
Y int
Z int
}
func main() {
x := [2]S{{Z: 5}, {Z: 10}}
s := unsafe.Slice(&x[0], 2)
fmt.Println(s)
}

19
_test/var16.go Normal file
View File

@@ -0,0 +1,19 @@
package main
func getArray() ([]int, error) { println("getArray"); return []int{1, 2}, nil }
func getNum() (int, error) { println("getNum"); return 3, nil }
func main() {
if a, err := getNum(); err != nil {
println("#1", a)
} else if a, err := getArray(); err != nil {
println("#2", a)
}
println("#3")
}
// Output:
// getNum
// getArray
// #3

Some files were not shown because too many files have changed in this diff Show More