Compare commits

...

79 Commits

Author SHA1 Message Date
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
Marc Vertes
538182e12c interp: fix append of values to an array of interface objects
Interface wrappers were not properly generated.

Fixes #1185
2021-07-15 11:38:12 +02:00
Marc Vertes
13d554acbe interp: fix receiver for exported method objects
When a function variable is assigned from an exported method,
make sure the receiver is updated from the coresponding symbol.

Fixes #1182.
2021-07-13 11:00:14 +02:00
Marc Vertes
4fcf90edae interp: fix swap assign with extra operation
The optimization in unary operaror bypasses the necessary temporary
variable in that case and must be disabled.

Fixes #1181.
2021-07-12 18:00:09 +02:00
Marc Vertes
fc970799a1 interp: add support of interface wrappers to type assertions
Store the interpreter value of the interface object to wrap as
a field called IValue, at offset 0 in wrapper structures.

Update extract to include IValue field.

In typeAssert, detect interface wrapper, and dereference the
interpreter value from IValue wrapper field.

Fixes #1166.
2021-07-12 17:10:13 +02:00
Marc Vertes
78d7e85352 interp: fix method resolution on pointer to binary values
In that case, the value must be derefenced before extracting
the method by index, otherwise an incorrect method is returned.

Fixes #1167.
2021-07-09 12:08:14 +02:00
Marc Vertes
c503855262 interp: fix handling of interface value in forwarding return calls
Special wrapping of interface value at return is no longer necessary and
must be avoided now.

Fixes #1179.
2021-07-08 16:04:08 +02:00
Marc Vertes
77acfb4593 interp: fix type checking of address of multi dimension array
Fixes #1177.
2021-07-08 12:40:09 +02:00
Marc Vertes
f6d0cf95fd interp: fix array declaration with a typed constant size
In parsing array type declaration, The type check of array size was
restricted to `int`. Broaden the test to accept any valid integer
kind.

Fixes #1175.
2021-07-08 12:30:12 +02:00
Marc Vertes
25b570d7e9 interp: fix the detection of builtin calls during parsing
Avoid shortcuts, and apply regular scoping rules for symbol
resolution when checking for builtin calls.

Fixes #1173.
2021-07-08 11:26:05 +02:00
Marc Vertes
297b40d526 interp: improve field and method resolution in presence of collisions.
The resolution method was not compliant with the Go specification which
requires to retain the object where the field or method is the most
shallowed.

The detection of ambiguous fields or methods (same depth in different
objects) has also been added.

Fixes #1163.
2021-07-06 12:12:05 +02:00
Daniel Tomcej
bd2cb06789 Update README.md
This PR:

- Updates the Readme to reflect the current supported Go versions 😄
2021-07-06 08:16:13 +02:00
Ludovic Fernandez
3c5682150d chore: improve issue templates
- use new GitHub issue forms
- update readme badges and links
2021-07-02 11:00:08 +02:00
Marc Vertes
e32b2ab6bd interp: improve handling of interface values
In selector resolution, struct field matching now precedes
method matching. Before struct field matching could be skipped
in case of a matching method, which is incorrect, as demontrated
by _test/issue-1156.go.

Field lookup has been fixed to operate on recursive structures.

Concrete type values are derived when filling a receiver for
interface methods.

LookupBinField has been fixed to skip non struct values.

LookupMethod has been fixed to iterate on interface values as
well as concrete type values.

Fixes #1156.
2021-06-24 16:20:13 +02:00
Ethan Reesor
1df5dc2e93 Update Interpreter.Use API
### Background

#1102 changed how `Interpreter.Use` interprets export paths such that the last path component is stripped and used as the package name. This resulted in #1139 - attempting to Use an export with only one path component, such as `foo`, would result in the import path being `.`.

### Breaking API Change

This PR changes the signature of `Interpreter.Use` from `Use(Exports)` to `Use(Exports) error`.

### Fix for #1139

With this PR, if Use is called with an incomplete export path, such as `foo`, Use will return an error.
2021-06-24 10:00:05 +02:00
Ethan Reesor
36594014c9 Fix handling of ImportUsed packages
Fixes #1151

If I add a package with `Use` and import it with `ImportUsed`, the package is added to the universe scope as `<pkg>`. If I import with `Eval`, the package is added as `<pkg>/_.go`. However, `(*node).isType` (in cfg.go) only checks for `<pkg>/_.go`. Thus, packages imported with `ImportUsed` can be inaccessible.

This MR updates `(*node).isType` to fall back to `<pkg>` if `<pkg>/_.go` does not exist.
2021-06-21 11:24:10 +02:00
Marc Vertes
8f9eccdd61 interp: fix assign of function values with binary methods
In typecheck.go, detect binary methods so we know when to skip the receiver as first parameter when checking function signatures. The signature check is not yet performed, we just avoid a false error.

In cfg.go, take care to label types with isBinMethod field to true whenever a binary method is resolved.

Also, do not attempt to wrap node in functions if the node value is already a binary function.

Fixes #1145.
2021-06-18 12:56:05 +02:00
Marc Vertes
dd03989709 interp: fix handing nil when appending to a slice
Fixes #1147.
2021-06-18 12:44:07 +02:00
Marc Vertes
b2a11eaf2a interp: preserve concrete type when converting type to interface
This allows to fix the reassignment of an non empty interface value.
Before, reassignment was limited to empty interfaces.
    
Fixes #1138.
2021-06-18 10:32:13 +02:00
Ethan Reesor
25c2a435f5 Correctly resolve type aliases when building array literals
Fixes #1150

1. When resolving a selector expression involving an aliased type, resolve the aliased type
2. When building an array literal, resolve the aliased type

Aliases of named array and slice types were the only ones that didn't work, but I added the other test cases for the sake of completeness and through testing.
2021-06-18 10:10:10 +02:00
Ethan Reesor
3d1a21094a Avoid unnecessary calls to FieldByIndex
Fixes #1149

Because of how aliases are handled, `n.gen` is set to `getIndexSeqMethod` or `getIndexSeqPtrMethod` in cases like the one described in #1149. As a result, `FieldByIndex` can be called on a value that is not a struct, which causes a panic. This MR updates those two methods to avoid that call if the index array is empty.
2021-06-18 09:40:13 +02:00
Ethan Reesor
2a0d29a390 Add a newline after src in wrapInMain
Fixes #1142
2021-06-16 15:14:17 +02:00
Marc Vertes
db955e671f interp: fix append a slice on binary slice
Fixes #1128.
2021-06-15 14:34:08 +02:00
Ethan Reesor
ab44c38298 interp: handle binary array literals
This is a small change that allows use of composite array literals, such as:

```go
type Vec3 [3]float32

var foo = []Vec3{
	{1, 0, 0},
	{6, 0, 0},
	{6, 2, 0},
	{2, 2, 0},
	{1, 1, 0},
}
```
2021-06-14 14:48:05 +02:00
Marc Vertes
93e2db7085 interp: fix a wrong control flow in switch
In switch case expressions, the condition on case clause was
not always properly evaluated. Reverse the order of case clause
evaluations (as already done for if-else-if fashion), and fix the
wiring to false-next and true-next nodes.

Fixes #1126.
2021-06-14 12:18:07 +02:00
Marc Vertes
b1ef9251d4 interp: fix detection of binary types implementing interfaces
Fixes #1136.
2021-06-11 17:58:11 +02:00
Marc Vertes
b19afbfe93 interp: avoid panic in output value generation for recursive types
Fixes #1134.
2021-06-11 17:44:12 +02:00
Marc Vertes
a6762d500c interp: fix use of unrestricted symbols
This is fixing a regression introduced by #1102.

Fixes #1129.
2021-06-11 16:34:11 +02:00
Marc Vertes
c4174a7167 interp: fix assign of function call with implicit type assert.
De-optimize a use case to avoid skipping an assign operation with an implicit type assertion at function call return.

Fixes #1122.
2021-05-27 13:38:07 +02:00
Marc Vertes
2f9fe7003a interp: fix relative import path detection on MS-Windows
Fixes #1106.
2021-05-27 12:34:09 +02:00
Marc Vertes
c86436afa6 interp: fix litteral map containing binary functions
The case of assigning a binary function to a funcT object was
solved elsewhere. Factor the case in genDestValue to apply it
at multiple places.

Fixes #1100.
2021-05-27 12:04:11 +02:00
Marc Vertes
29e912e90b interp: fix method lookup on pointers to binary types
This case was missing in the selector expression processing.

Fixes #1083.
2021-05-27 11:54:04 +02:00
Marc Vertes
e29de04513 interp: fix constant types from imported packages
In binary packages, constants are wrapped in constant.Values, to
support arbitrary precision. Their type must therefore be converted
back to a regular type at import.

Fixes #1101.
2021-05-27 11:40:06 +02:00
Marc Vertes
c6945514cb interp: sandbox to preserve type of os.Stdin os.Stdout and os.Stderr
Use YAEGI_SPECIAL_STDIO env boolean to overwrite os.Stdxxx by
non file descriptors io.Writer / io.Reader interfaces. It is set
to true when testing to allow redirection to byte buffers.

The default behaviour is now to preserve the original concrete type
when sandboxing stdio, which maintains compatibility.

Fixes #1092.
2021-05-25 09:36:08 +02:00
Marc Vertes
847cd7ed2b interp: fix calling of variadic binary methods
Fixes #1046.
2021-05-22 11:48:07 +02:00
Marc Vertes
fbf897b047 interp: improve type check of binary methods
Some binary method calls were wrongly rejected. There is still
some ambiguous cases as binary method signature may include or
not the receiver as first argument, depending on how the method
was resolved.

With this fix, `import "golang.org/x/net/html"` doesn't panic
anymore, but not all tests are passing yet, i.e.
`yaegi test golang.org/x/net/html` still has failures, to be
investigated.

Fixes #1107.
2021-05-20 17:12:10 +02:00
Marc Vertes
45c7b8008a interp: fix special range on string
In this range variant "for k, v := range aString", k must
be the byte position of the rune in the byte array, rather than
the index of the rune in the rune array.

Fixes #1088.
2021-05-20 17:02:13 +02:00
Marc Vertes
4788775f8c interp: improve type checking for "+" operator
Fixes #1093.
2021-05-20 15:50:08 +02:00
Marc Vertes
bcb8546e91 interp: fix goto label by not overwriting destination
Fixes #1115
2021-05-20 11:38:04 +02:00
Marc Vertes
befa5a2b54 interp: fix return of function call involving a type conversion
In that case, direct propagation of result can not be attempted,
as the frame types will be different between the source and destination.
Disabling the optimisation and using The regular case involves an intermediate
frame entry, which enables the type conversion.

Fixes #1091.
2021-05-17 15:12:07 +02:00
Dan Kortschak
0ba64fc318 interp: add example to show use of Self
This example clarifies how `interp.Self` can be accessed.

Please take a look.
2021-05-10 14:06:09 +02:00
Marc Vertes
d16bd4bcdb interp: fix package name extraction from import path
The heuristic to generate a package name identifier was incorrect. Now for binary packages, the package identifier is obtained by a symbol, generated by extract, which contains the string argument of package statement in source file. This should ensure an always correct default package identifier. 

Fixes #1095.
2021-05-10 11:20:07 +02:00
Marc Vertes
33a532ee01 interp: fix a bug when assigning to an empty interface
The concrete type was not forwarded propertly in case of a binary
expression involving a valueT. The corresponding part in type.go
has been refactored and the now the multi-assign case should be
handled as well.

Fixes #1094.
2021-05-07 16:30:09 +02:00
Marc Vertes
cdc6b773c2 interp: add ImportUsed method to pre-import compiled packages
This feature was already present, but part of REPL only.
It's now also possible to apply it when evaluating a string
(-e flag). Default package names collision handling is no
longer hard-coded.

With -e flag, the eval result is now printed if valid, allowing
simpler commands:

     yaegi -e 'reflect.TypeOf(fmt.Printf)'

instead of:

     yaegi -e 'println(reflect.TypeOf(fmt.Printf))'

Fixes #1084.
2021-04-30 11:36:05 +02:00
Marc Vertes
17d5f1814a interp: fix support of interpreted functions in map values
Interpreted functions were represented in an inconsistent way in the frame: as a node pointer by default, and wrapped in a function wrapper for maps. 

We now simply use the default (*node) representation, as elsewhere, so values can be assigned, passed and called as for the other types. The alternative (generating a function wrapper) is more complex, costly and reserved for cases where the interpreted function can be called from the runtime.

Test that a map of functions can store both binary functions from used packages and interpreted ones.

Fixes #1090.
2021-04-30 11:26:04 +02:00
Marc Vertes
5f8be70066 interp: fix type string extraction for wrapped constants
Fixes #1089.
2021-04-29 18:08:03 +02:00
Marc Vertes
5530eca17d interp: give a self to the interpreter
The interpreter is exposed to itself through a "Self" var which
is set on "Use" of the interpreter package.

It allows meta-programming features, for example using "Eval" in
the current interpreter context, or enabling self-inspection
capabilities.
2021-04-21 16:26:03 +02:00
Marc Vertes
c8d9e25085 stdlib: add wrapper for testing/fstest package in go1.16 2021-04-19 09:38:03 +02:00
Marc Vertes
a241119bf7 interp: improve handling of aliased types
Avoid to test directly for a type category, as it may give wrong
results for aliased types, where the interesting category remains
masked.  Instead, use some property helpers, such as isFuncSrc,
isPtrSrc and isInterfaceSrc to check if a type is of source function,
source pointer or source interface respectively (versus runtime
defined function, pointer or interface).

Fixes #1068.
2021-04-14 17:52:04 +02:00
Marc Vertes
3e3f8d5c2f interp: implement unsafe.Offsetof
Offsetof returns the offset of a field in a struct. It is computed
during parsing at CFG, due to the constraint of operating on a
struct selector expression.

With this function, the support of 'unsafe' package is now
complete in yaegi.

Fixes #1062.
2021-04-14 14:28:03 +02:00
Marc Vertes
9aeb78fc36 interp: fix method lookup for aliased types
Fixes #1058.
2021-04-13 18:10:04 +02:00
Marc Vertes
7863456d52 interp: refactor slice type management
Add missing `sliceT` type category for consistency. Remove
`sizedef` field in `itype` struct. Rename field `size` to `length`.
Clean the various hacks used to cope with the absence of `sliceT`.
2021-04-13 18:00:12 +02:00
Ethan Reesor
428b658160 Allow colon-equal to override variable type
If I execute the following:

```
I := interp.New(interp.Options{})
I.Eval(`x := 1`)
I.Eval(`x := "foobar"`)
```

I expect the second declaration to override the first. `var x string` will override the previous type, and redeclaring a type, function, const, etc will override it, but the `:=` operator will not.

Currently, the result is: `reflect.Set: value of type string is not assignable to type int`
2021-04-02 09:08:04 +02:00
Ludovic Fernandez
350cf80bbf chore: restore macos env in GitHub Actions. 2021-04-02 08:48:04 +02:00
Ethan Reesor
d92051d40f interp: treat var declaration within a block as a define statement
This PR:

- Treats a `varDecl` within a block as a `defineStmt`
  - More specifically, any `varDecl` with a grandparent that is *not* a `fileStmt`
- Adds an extra condition to the handler for implicit const assignment
- Adds a tests to cover the changes
- Closes #1071
2021-04-01 15:22:03 +02:00
Marc Vertes
aa2621f6c6 interp: fix a panic when embedding an error interface
This patch brings the following modifications:
- consider that an interface is assignable to another if the former
  implements the latter
- call TypeOf() method instead of rtype field when resolving methods, to
  handle first met types
- unwrap error interface inplace rather than embedding it in an
  interface definition, as lower case named embbeded interface may
  not be handled by reflect when lookup for a method.

Fixes #1063. Partially improves #1058.
2021-04-01 10:38:05 +02:00
Marc Vertes
2b1d6f0e7a interp: fix append on variadic recursive struct
Fixes #1065. Improves #1058.
2021-03-26 09:34:03 +01:00
Ethan Reesor
992676722d interp: allow redeclaration of imports
This PR adds an interpreter option, `AllowRedeclaration`. If this option is set, `(*Interpreter).Eval` will allow package imports to be redeclared. That is, no error will be raised and the package symbol will be overwritten.

I would like to use Yaegi to power a Go notebook (VSCode extension), somewhat like Jupyter. A notebook can have multiple Go 'cells' which can be evaluated (using Yaegi). As much as is possible, evaluating cells should be idempotent - that is, evaluating a cell multiple times should have the same effect as evaluating it once, ideally. Cells that are not idempotent can degrade the user experience.

Specifically, Go files tend to declare all imports in a single block. In a notebook, I'd put all imports in a single block, in their own cell. When I decide I need to import an additional package, I want to add that import to the existing cell and evaluate it. Without this MR, reevaluating that block usually causes an error.
2021-03-25 23:34:03 +01:00
エガオノキミヘ
451c754068 extract: process interface wrapper method with variadic parameter
Fixes #1055
2021-03-24 11:48:03 +01:00
エガオノキミヘ
84ad46751a fix #1056, extract: replace the code for get package name.
fix #1056
2021-03-23 10:30:04 +01:00
Marc Vertes
ec5392d566 interp: do not skip use of tmp frame in multi-assign
Fixes #1052.
2021-03-19 11:24:04 +01:00
Marc Vertes
7d8fdbc1fc interp: fix closure in a struct field
Functions in a struct fields are always wrapped (as potentially
used by the runtime), so generate a function wrapper also for
closure when assigned to a struct field.

When such a function is called from the interpreter, ensure that
interface arguments are also wrapped so method and receiver resolution
can be performed.

Fixes partially #1043.
2021-03-11 15:26:04 +01:00
Marc Vertes
fdfcb9c1df interp: do not check properties of incomplete types
Fixes #1042.
2021-03-09 11:58:04 +01:00
Marc Vertes
a988459dcd interp: fix a memory management issue causing wrong closure context
The first change forces a variable definition to reallocate a
new memory slot to avoid corrupting a previously defined one in
a loop block.

The second change ensures that the frame clone operations obtains
a copy of the original data slice, to preserve the original context
set in a loop.

Fixes #1035.
2021-03-09 09:30:04 +01:00
Sylvain Muller
51e0b46256 Use fmt.Fprintln instead of fmt.Println in runCfg
As mentioned in #1030, when an Eval panic, it is print with `fmt.Println()` and not to the configured `interp.Options.Stderr`. According to https://github.com/traefik/yaegi/blob/master/interp/interp.go#L210, it should be removed in future version so I'm not sure if this pull request is necessary. However, it could fix the issue in the meanwhile.

Fixes #1030.
2021-02-20 12:08:03 +01:00
Chris Waldon
eb06aeeb26 fix: use portable path manipulation for choosing package names
This *should* fix the generated names of packages on windows, which currently take the form `C:\kdfjslfj\jkfsjldkfjsf\sjdkfldjf` (this was reported to me by someone attempting to generate yaegi symbols for one of my projects). That being said, I don't have a windows machine to test on.
2021-02-17 10:12:08 +01:00
Ludovic Fernandez
8bb5daf60e feat: update stdlib mapping for go1.16
- drop stdlib for go1.14
- generate stdlib for go1.16
-  update CI configuration.
2021-02-17 10:02:03 +01:00
635 changed files with 22189 additions and 8726 deletions

View File

@@ -1,27 +0,0 @@
---
name: Bug report
about: Create a report to help us improve
---
The following program `sample.go` triggers a panic:
```go
package main
func main() {
// add a sample
}
```
Expected result:
```console
$ go run ./sample.go
// output
```
Got:
```console
$ yaegi ./sample.go
// output
```

66
.github/ISSUE_TEMPLATE/bug_report.yml vendored Normal file
View File

@@ -0,0 +1,66 @@
name: Bug Report
description: Create a report to help us improve
body:
- type: markdown
attributes:
value: |
⚠️ Make sure to browse the opened and closed issues before submit your issue.
- type: textarea
id: sample
attributes:
label: "The following program `sample.go` triggers an unexpected result"
value: |
package main
func main() {
// add a sample
}
render: go
validations:
required: true
- type: textarea
id: expected
attributes:
label: Expected result
description: |-
```console
$ go run ./sample.go
// output
```
placeholder: $ go run ./sample.go
render: console
validations:
required: true
- type: textarea
id: got
attributes:
label: Got
description: |-
```console
$ yaegi ./sample.go
// output
```
placeholder: $ yaegi ./sample.go
render: console
validations:
required: true
- type: input
id: version
attributes:
label: Yaegi Version
description: Can be a tag or a hash.
validations:
required: true
- type: textarea
id: additional
attributes:
label: Additional Notes
description: Use [Markdown syntax](https://help.github.com/articles/github-flavored-markdown) if needed.
validations:
required: false

8
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@@ -0,0 +1,8 @@
blank_issues_enabled: false
contact_links:
- name: Questions
url: https://community.traefik.io/c/yaegi
about: If you have a question, or are looking for advice, please post on our discussions forum!
- name: Documentation
url: https://pkg.go.dev/github.com/traefik/yaegi
about: Please take a look to our documenation.

View File

@@ -1,19 +0,0 @@
---
name: Feature request
about: Propose a change to Yaegi!
---
<!-- ⚠️ If you do not respect this template your issue will be closed. -->
<!-- ⚠️ Make sure to browse the opened and closed issues before submit your issue. -->
#### Proposal
<!-- Write your feature request in the form of a proposal to be considered for implementation -->
#### Background
<!-- Describe the background problem or need that led to this feature request -->
#### Workarounds
<!-- Are there any current workarounds that you're using that others in similar positions should know about? -->

View File

@@ -0,0 +1,32 @@
name: Feature request
description: Propose a change to Yaegi
body:
- type: markdown
attributes:
value: |
⚠️ Make sure to browse the opened and closed issues before submit your issue.
- type: textarea
id: proposal
attributes:
label: Proposal
description: Write your feature request in the form of a proposal to be considered for implementation.
validations:
required: true
- type: textarea
id: background
attributes:
label: Background
description: Describe the background problem or need that led to this feature request.
validations:
required: true
- type: textarea
id: workarounds
attributes:
label: Workarounds
description: Are there any current workarounds that you're using that others in similar positions should know about?
validations:
required: true

View File

@@ -17,7 +17,7 @@ jobs:
strategy:
matrix:
go-version: [ 1.14, 1.15 ]
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.15
GOLANGCI_LINT_VERSION: v1.36.0
GO_VERSION: 1.17
GOLANGCI_LINT_VERSION: v1.41.1
jobs:
@@ -45,7 +45,7 @@ jobs:
needs: linting
strategy:
matrix:
go-version: [ 1.14, 1.15 ]
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.14, 1.15 ]
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.15
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
]
@@ -61,6 +65,9 @@
[[issues.exclude-rules]]
path = "interp/.+_test\\.go"
linters = ["goconst"]
[[issues.exclude-rules]]
path = "interp/.+_test\\.go"
text = "var-declaration:"
[[issues.exclude-rules]]
path = "interp/interp.go"

View File

@@ -3,9 +3,9 @@
</p>
[![release](https://img.shields.io/github/tag-date/traefik/yaegi.svg?label=alpha)](https://github.com/traefik/yaegi/releases)
[![Build Status](https://travis-ci.com/traefik/yaegi.svg?branch=master)](https://travis-ci.com/traefik/yaegi)
[![GoDoc](https://godoc.org/github.com/traefik/yaegi?status.svg)](https://godoc.org/github.com/traefik/yaegi)
[![Discourse status](https://img.shields.io/discourse/https/community.containo.us/status?label=Community&style=social)](https://community.containo.us/c/yaegi)
[![Build Status](https://github.com/traefik/yaegi/actions/workflows/main.yml/badge.svg)](https://github.com/traefik/yaegi/actions/workflows/main.yml)
[![GoDoc](https://godoc.org/github.com/traefik/yaegi?status.svg)](https://pkg.go.dev/mod/github.com/traefik/yaegi)
[![Discourse status](https://img.shields.io/discourse/https/community.traefik.io/status?label=Community&style=social)](https://community.traefik.io/c/yaegi)
Yaegi is Another Elegant Go Interpreter.
It powers executable Go scripts and plugins, in embedded interpreters or interactive shells, on top of the Go runtime.
@@ -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.13 and Go 1.14 (the latest 2 major releases)
* Support Go 1.16 and Go 1.17 (the latest 2 major releases)
## Install
@@ -186,7 +186,7 @@ Beside the known [bugs] which are supposed to be fixed in the short term, there
[Apache 2.0][License].
[specs]: https://golang.org/ref/spec
[docs]: https://godoc.org/github.com/traefik/yaegi
[docs]: https://pkg.go.dev/github.com/traefik/yaegi
[license]: https://github.com/traefik/yaegi/blob/master/LICENSE
[github]: https://github.com/traefik/yaegi
[bugs]: https://github.com/traefik/yaegi/issues?q=is%3Aissue+is%3Aopen+label%3Abug

View File

@@ -22,6 +22,12 @@ func main() {
var g int = 2
a = 10 + g
println(a.(int))
// multiple assignment
var foo interface{}
foo, a = "hello", 11 + g
println(a.(int))
println(foo.(string))
}
// Output:
@@ -31,3 +37,5 @@ func main() {
// 10
// 11
// 12
// 13
// hello

View File

@@ -57,7 +57,7 @@ func main() {
fmt.Println(err, vvv)
}
// Ouput:
// Output:
// <nil> {work bob@work.com}
// <nil> {work bob@work.com}
// <nil> {work bob@work.com}

View File

@@ -103,7 +103,7 @@ func main() {
intoMap()
}
// Ouput:
// Output:
// 0 : foo
// 1 : bar
// 0 : foo

14
_test/append3.go Normal file
View File

@@ -0,0 +1,14 @@
package main
import "fmt"
func main() {
a := []int{1, 2}
b := [2]int{3, 4}
fmt.Println(append(a, b[:]...))
fmt.Println(append(a, []int{5, 6}...))
}
// Output:
// [1 2 3 4]
// [1 2 5 6]

12
_test/append4.go Normal file
View File

@@ -0,0 +1,12 @@
package main
import "fmt"
func main() {
a := []*int{}
a = append(a, nil)
fmt.Println(a)
}
// Output:
// [<nil>]

18
_test/closure10.go Normal file
View File

@@ -0,0 +1,18 @@
package main
func main() {
foos := []func(){}
for i := 0; i < 3; i++ {
a, b := i, i
foos = append(foos, func() { println(i, a, b) })
}
foos[0]()
foos[1]()
foos[2]()
}
// Output:
// 3 0 0
// 3 1 1
// 3 2 2

22
_test/closure11.go Normal file
View File

@@ -0,0 +1,22 @@
package main
type T struct {
F func()
}
func main() {
foos := []T{}
for i := 0; i < 3; i++ {
a := i
foos = append(foos, T{func() { println(i, a) }})
}
foos[0].F()
foos[1].F()
foos[2].F()
}
// Output:
// 3 0
// 3 1
// 3 2

25
_test/closure12.go Normal file
View File

@@ -0,0 +1,25 @@
package main
import "fmt"
type T struct {
F func()
}
func main() {
foos := []T{}
for i := 0; i < 3; i++ {
a := i
n := fmt.Sprintf("i=%d", i)
foos = append(foos, T{func() { println(i, a, n) }})
}
foos[0].F()
foos[1].F()
foos[2].F()
}
// Output:
// 3 0 i=0
// 3 1 i=1
// 3 2 i=2

18
_test/closure9.go Normal file
View File

@@ -0,0 +1,18 @@
package main
func main() {
foos := []func(){}
for i := 0; i < 3; i++ {
a := i
foos = append(foos, func() { println(i, a) })
}
foos[0]()
foos[1]()
foos[2]()
}
// Output:
// 3 0
// 3 1
// 3 2

11
_test/const25.go Normal file
View File

@@ -0,0 +1,11 @@
package main
const (
FGBlack Attribute = iota + 30
)
type Attribute int
func main() {
println(FGBlack)
}

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

@@ -0,0 +1,9 @@
package d1
type T struct {
Name string
}
func (t *T) F() { println(t.Name) }
func NewT(s string) *T { return &T{s} }

8
_test/d2/d2.go Normal file
View File

@@ -0,0 +1,8 @@
package d2
import "github.com/traefik/yaegi/_test/d1"
var (
X = d1.NewT("test")
F = X.F
)

11
_test/d3.go Normal file
View File

@@ -0,0 +1,11 @@
package main
import "github.com/traefik/yaegi/_test/d2"
func main() {
f := d2.F
f()
}
// Output:
// test

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

23
_test/interface51.go Normal file
View File

@@ -0,0 +1,23 @@
package main
type Error interface {
error
Message() string
}
type T struct {
Msg string
}
func (t *T) Error() string { return t.Msg }
func (t *T) Message() string { return "message:" + t.Msg }
func newError() Error { return &T{"test"} }
func main() {
e := newError()
println(e.Error())
}
// 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

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

@@ -0,0 +1,23 @@
package main
import "fmt"
func main() {
a, b := 1, 1
for i := 0; i < 10; i++ {
fmt.Println(a)
a, b = b, a+b
}
}
// Output:
// 1
// 1
// 2
// 3
// 5
// 8
// 13
// 21
// 34
// 55

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

@@ -0,0 +1,20 @@
package main
import "fmt"
type AST struct {
Num int
Children []AST
}
func newAST(num int, root AST, children ...AST) AST {
return AST{num, append([]AST{root}, children...)}
}
func main() {
ast := newAST(1, AST{}, AST{})
fmt.Println(ast)
}
// Output:
// {1 [{0 []} {0 []}]}

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

@@ -0,0 +1,19 @@
package main
type I interface {
Hello()
}
type T struct{}
func (t T) Hello() { println("hello") }
type I2 I
func main() {
var i I2 = T{}
i.Hello()
}
// Output:
// hello

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

@@ -0,0 +1,14 @@
package main
import "fmt"
func main() {
for i, ch := range "日本語" {
fmt.Printf("%#U starts at byte position %d\n", ch, i)
}
}
// Output:
// U+65E5 '日' starts at byte position 0
// U+672C '本' starts at byte position 3
// U+8A9E '語' starts at byte position 6

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

@@ -0,0 +1,13 @@
package main
import (
"fmt"
"time"
)
func main() {
fmt.Println(`"` + time.RFC3339Nano + `"`)
}
// Output:
// "2006-01-02T15:04:05.999999999Z07:00"

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

@@ -0,0 +1,13 @@
package main
func b() string {
return "b"
}
func main() {
var x int
x = "a" + b()
}
// Error:
// 9:6: cannot use type string as type int in assignment

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

@@ -0,0 +1,12 @@
package main
import "fmt"
func main() {
var x interface{}
x = "a" + fmt.Sprintf("b")
fmt.Printf("%v %T\n", x, x)
}
// Output:
// ab string

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

@@ -0,0 +1,17 @@
package main
import (
"fmt"
"net/http"
)
func main() {
method := "POST"
switch method {
case http.MethodPost:
fmt.Println("It's a post!")
}
}
// Output:
// It's a post!

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

@@ -0,0 +1,24 @@
package main
import "fmt"
func main() {
outer:
for y := 0; y < 10; y++ {
for x := 0; x < 10; x++ {
if x == 5 && y == 5 {
break outer
}
}
fmt.Println(y)
}
fmt.Println("Yay! I finished!")
}
// Output:
// 0
// 1
// 2
// 3
// 4
// Yay! I finished!

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

@@ -0,0 +1,23 @@
package main
import (
"errors"
"fmt"
"strings"
)
func main() {
err := errors.New("hello there")
switch true {
case err == nil:
break
case strings.Contains(err.Error(), "hello"):
fmt.Println("True!")
default:
fmt.Println("False!")
}
}
// Output:
// True!

11
_test/issue-1128.go Normal file
View File

@@ -0,0 +1,11 @@
package main
import "net"
func main() {
c := append(net.Buffers{}, []byte{})
println(len(c))
}
// Output:
// 1

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

@@ -0,0 +1,20 @@
package main
type I interface {
Hello()
}
type T struct {
Name string
Child []*T
}
func (t *T) Hello() { println("Hello", t.Name) }
func main() {
var i I = new(T)
i.Hello()
}
// Output:
// Hello

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

@@ -0,0 +1,22 @@
package main
import (
"fmt"
"io"
)
type T struct {
r io.Reader
}
func (t *T) Read(p []byte) (n int, err error) { n, err = t.r.Read(p); return }
func main() {
x := io.LimitedReader{}
y := io.Reader(&x)
y = &T{y}
fmt.Println(y.Read([]byte("")))
}
// Output:
// 0 EOF

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

@@ -0,0 +1,14 @@
package main
import "sync"
type F func()
func main() {
var wg sync.WaitGroup
var f F = wg.Done
println(f != nil)
}
// Output:
// true

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

@@ -0,0 +1,25 @@
package main
type myInterface interface {
myFunc() string
}
type V struct{}
func (v *V) myFunc() string { return "hello" }
type U struct {
v myInterface
}
func (u *U) myFunc() string { return u.v.myFunc() }
func main() {
x := V{}
y := myInterface(&x)
y = &U{y}
println(y.myFunc())
}
// Output:
// hello

41
_test/issue-1163.go Normal file
View File

@@ -0,0 +1,41 @@
package main
import "fmt"
type WidgetEvent struct {
Nothing string
}
type WidgetControl interface {
HandleEvent(e *WidgetEvent)
}
type Button struct{}
func (b *Button) HandleEvent(e *WidgetEvent) {
}
type WindowEvent struct {
Something int
}
type Window struct {
Widget WidgetControl
}
func (w *Window) HandleEvent(e *WindowEvent) {
}
func main() {
window := &Window{
Widget: &Button{},
}
windowevent := &WindowEvent{}
// The next line uses the signature from the wrong method, resulting in an error.
// Renaming one of the clashing method names fixes the problem.
window.HandleEvent(windowevent)
fmt.Println("OK!")
}
// Output:
// OK!

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

@@ -0,0 +1,24 @@
package main
import (
"fmt"
"io"
)
type T []byte
func (t *T) Write(p []byte) (n int, err error) { *t = append(*t, p...); return len(p), nil }
func foo(w io.Writer) {
a := w.(*T)
fmt.Fprint(a, "test")
fmt.Printf("%s\n", *a)
}
func main() {
x := T{}
foo(&x)
}
// Output:
// test

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

@@ -0,0 +1,19 @@
package main
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
)
func main() {
key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
panic(err)
}
pub := key.Public().(*ecdsa.PublicKey)
println(pub.Params().Name)
}
// Output:
// P-256

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

@@ -0,0 +1,10 @@
package main
var real = func() { println("Hello") }
func main() {
real()
}
// Output:
// Hello

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

@@ -0,0 +1,18 @@
package main
type Level int8
const (
a Level = -1
b Level = 5
d = b - a + 1
)
type counters [d]int
func main() {
println(len(counters{}))
}
// Output:
// 7

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

@@ -0,0 +1,13 @@
package main
type counters [3][16]int
func main() {
cs := &counters{}
p := &cs[0][1]
*p = 2
println(cs[0][1])
}
// Output:
// 2

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

@@ -0,0 +1,23 @@
package main
type I interface {
F()
}
type T struct {
Name string
}
func (t *T) F() { println("in F", t.Name) }
func NewI(s string) I { return newT(s) }
func newT(s string) *T { return &T{s} }
func main() {
i := NewI("test")
i.F()
}
// Output:
// in F test

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

@@ -0,0 +1,10 @@
package main
func main() {
a, b := 1, 2
a, b = b, -a
println(a, b)
}
// Output:
// 2 -1

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

@@ -0,0 +1,20 @@
package main
import "io"
type B []byte
func (b B) Write(p []byte) (n int, err error) {
b = p
return len(p), nil
}
func main() {
b := B{}
a := make([]io.Writer, 0)
a = append(a, b)
println(len(a))
}
// Output:
// 1

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

@@ -0,0 +1,47 @@
package main
import (
"io"
"io/ioutil"
"os"
)
type sink interface {
io.Writer
io.Closer
}
func newSink() sink {
// return os.Stdout // Stdout is special in yaegi tests
file, err := ioutil.TempFile("", "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

27
_test/map30.go Normal file
View File

@@ -0,0 +1,27 @@
package main
import "strings"
func f(s string) string { return "hello " + s }
func g(s string) string { return "hi " + s }
var methods = map[string]func(string) string{
"f": f,
"h": strings.ToLower,
}
func main() {
methods["i"] = strings.ToUpper
methods["g"] = g
println(methods["f"]("test"))
println(methods["g"]("test"))
println(methods["i"]("test"))
println(methods["h"]("TEST"))
}
// Output:
// hello test
// hi test
// TEST
// test

26
_test/method36.go Normal file
View File

@@ -0,0 +1,26 @@
package main
type I interface{ Hello() }
type T struct{ Name string }
func (t *T) Hello() { println("Hello", t.Name) }
type FT func(i I)
type ST struct{ Handler FT }
func newF() FT {
return func(i I) {
i.Hello()
}
}
func main() {
st := &ST{}
st.Handler = newF()
st.Handler(&T{"test"})
}
// Output:
// Hello test

19
_test/method37.go Normal file
View File

@@ -0,0 +1,19 @@
package main
import (
"net"
"os"
)
func writeBufs(bufs ...[]byte) error {
b := net.Buffers(bufs)
_, err := b.WriteTo(os.Stdout)
return err
}
func main() {
writeBufs([]byte("hello"))
}
// Output:
// hello

28
_test/ret8.go Normal file
View File

@@ -0,0 +1,28 @@
package main
import "fmt"
type CustomError string
func (s CustomError) Error() string {
return string(s)
}
func NewCustomError(errorText string) CustomError {
return CustomError(errorText)
}
func fail() (err error) {
return NewCustomError("Everything is going wrong!")
}
func main() {
fmt.Println(fail())
var myError error
myError = NewCustomError("ok")
fmt.Println(myError)
}
// Output:
// Everything is going wrong!
// ok

View File

@@ -7,7 +7,7 @@ import (
const (
period = 100 * time.Millisecond
precision = 5 * time.Millisecond
precision = 7 * time.Millisecond
)
func main() {

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:
// {}

View File

@@ -12,11 +12,13 @@ type S struct {
}
func main() {
size := unsafe.Sizeof(S{})
align := unsafe.Alignof(S{})
x := S{}
size := unsafe.Sizeof(x)
align := unsafe.Alignof(x.Y)
offset := unsafe.Offsetof(x.Z)
fmt.Println(size, align)
fmt.Println(size, align, offset)
}
// Output:
// 24 8
// 24 8 16

View File

@@ -7,7 +7,7 @@ import (
"fmt"
"io"
"os"
"path"
"path/filepath"
"strings"
"github.com/traefik/yaegi/extract"
@@ -50,7 +50,7 @@ func extractCmd(arg []string) error {
}
if name == "" {
name = path.Base(wd)
name = filepath.Base(wd)
}
ext := extract.Extractor{
Dest: name,

View File

@@ -6,6 +6,7 @@ import (
"go/build"
"io/ioutil"
"os"
"reflect"
"strconv"
"strings"
@@ -18,6 +19,7 @@ import (
func run(arg []string) error {
var interactive bool
var noAutoImport bool
var tags string
var cmd string
var err error
@@ -33,6 +35,7 @@ func run(arg []string) error {
rflag.BoolVar(&useUnrestricted, "unrestricted", useUnrestricted, "include unrestricted symbols")
rflag.StringVar(&tags, "tags", "", "set a list of build tags")
rflag.BoolVar(&useUnsafe, "unsafe", useUnsafe, "include unsafe symbols")
rflag.BoolVar(&noAutoImport, "noautoimport", false, "do not auto import pre-compiled packages. Import names that would result in collisions (e.g. rand from crypto/rand and rand from math/rand) are automatically renamed (crypto_rand and math_rand)")
rflag.StringVar(&cmd, "e", "", "set the command to be executed (instead of script or/and shell)")
rflag.Usage = func() {
fmt.Println("Usage: yaegi run [options] [path] [args]")
@@ -45,48 +48,68 @@ func run(arg []string) error {
args := rflag.Args()
i := interp.New(interp.Options{GoPath: build.Default.GOPATH, BuildTags: strings.Split(tags, ",")})
i.Use(stdlib.Symbols)
i.Use(interp.Symbols)
if err := i.Use(stdlib.Symbols); err != nil {
return err
}
if err := i.Use(interp.Symbols); err != nil {
return err
}
if useSyscall {
i.Use(syscall.Symbols)
if err := i.Use(syscall.Symbols); err != nil {
return err
}
// Using a environment var allows a nested interpreter to import the syscall package.
if err := os.Setenv("YAEGI_SYSCALL", "1"); err != nil {
return err
}
}
if useUnsafe {
i.Use(unsafe.Symbols)
if err := i.Use(unsafe.Symbols); err != nil {
return err
}
if err := os.Setenv("YAEGI_UNSAFE", "1"); err != nil {
return err
}
}
if useUnrestricted {
// Use of unrestricted symbols should always follow stdlib and syscall symbols, to update them.
i.Use(unrestricted.Symbols)
if err := i.Use(unrestricted.Symbols); err != nil {
return err
}
if err := os.Setenv("YAEGI_UNRESTRICTED", "1"); err != nil {
return err
}
}
if cmd != "" {
_, err = i.Eval(cmd)
if !noAutoImport {
i.ImportUsed()
}
var v reflect.Value
v, err = i.Eval(cmd)
if len(args) == 0 && v.IsValid() {
fmt.Println(v)
}
}
if len(args) == 0 {
if interactive || cmd == "" {
if cmd == "" || interactive {
showError(err)
if !noAutoImport {
i.ImportUsed()
}
_, err = i.REPL()
}
return err
}
// Skip first os arg to set command line as expected by interpreted main
// Skip first os arg to set command line as expected by interpreted main.
path := args[0]
os.Args = arg
flag.CommandLine = flag.NewFlagSet(path, flag.ExitOnError)
if isFile(path) {
err = runFile(i, path)
err = runFile(i, path, noAutoImport)
} else {
_, err = i.EvalPath(path)
}
@@ -106,7 +129,7 @@ func isFile(path string) bool {
return err == nil && fi.Mode().IsRegular()
}
func runFile(i *interp.Interpreter, path string) error {
func runFile(i *interp.Interpreter, path string, noAutoImport bool) error {
b, err := ioutil.ReadFile(path)
if err != nil {
return err
@@ -115,6 +138,9 @@ func runFile(i *interp.Interpreter, path string) error {
if s := string(b); strings.HasPrefix(s, "#!") {
// Allow executable go scripts, Have the same behavior as in interactive mode.
s = strings.Replace(s, "#!", "//", 1)
if !noAutoImport {
i.ImportUsed()
}
_, err = i.Eval(s)
return err
}

View File

@@ -117,23 +117,33 @@ func test(arg []string) (err error) {
}
i := interp.New(interp.Options{GoPath: build.Default.GOPATH, BuildTags: strings.Split(tags, ",")})
i.Use(stdlib.Symbols)
i.Use(interp.Symbols)
if err := i.Use(stdlib.Symbols); err != nil {
return err
}
if err := i.Use(interp.Symbols); err != nil {
return err
}
if useSyscall {
i.Use(syscall.Symbols)
if err := i.Use(syscall.Symbols); err != nil {
return err
}
// Using a environment var allows a nested interpreter to import the syscall package.
if err := os.Setenv("YAEGI_SYSCALL", "1"); err != nil {
return err
}
}
if useUnrestricted {
i.Use(unrestricted.Symbols)
if err := i.Use(unrestricted.Symbols); err != nil {
return err
}
if err := os.Setenv("YAEGI_UNRESTRICTED", "1"); err != nil {
return err
}
}
if useUnsafe {
i.Use(unsafe.Symbols)
if err := i.Use(unsafe.Symbols); err != nil {
return err
}
if err := os.Setenv("YAEGI_UNSAFE", "1"); err != nil {
return err
}

View File

@@ -82,7 +82,7 @@ func TestYaegiCmdCancel(t *testing.T) {
if err != nil {
t.Errorf("failed pipe test source to yaegi command: %v", err)
}
Sleep(200 * time.Millisecond)
Sleep(500 * time.Millisecond)
err = cmd.Process.Signal(os.Interrupt)
if err != nil {
t.Errorf("failed to send os.Interrupt to yaegi command: %v", err)

View File

@@ -9,7 +9,9 @@ import (
func TestFunctionCall(t *testing.T) {
i := interp.New(interp.Options{GoPath: "./_pkg"})
i.Use(stdlib.Symbols)
if err := i.Use(stdlib.Symbols); err != nil {
t.Fatal(err)
}
_, err := i.Eval(`import "foo/bar"`)
if err != nil {

View File

@@ -10,7 +10,9 @@ import (
func TestGetFunc(t *testing.T) {
i := interp.New(interp.Options{GoPath: "./_gopath/"})
i.Use(stdlib.Symbols)
if err := i.Use(stdlib.Symbols); err != nil {
t.Fatal(err)
}
if _, err := i.Eval(`import "github.com/foo/bar"`); err != nil {
t.Fatal(err)

View File

@@ -111,7 +111,10 @@ func TestPackages(t *testing.T) {
var stdout, stderr bytes.Buffer
i := interp.New(interp.Options{GoPath: goPath, Stdout: &stdout, Stderr: &stderr})
i.Use(stdlib.Symbols) // Use binary standard library
// Use binary standard library
if err := i.Use(stdlib.Symbols); err != nil {
t.Fatal(err)
}
var msg string
if test.evalFile != "" {
@@ -170,7 +173,10 @@ func TestPackagesError(t *testing.T) {
t.Run(test.desc, func(t *testing.T) {
// Init go interpreter
i := interp.New(interp.Options{GoPath: test.goPath})
i.Use(stdlib.Symbols) // Use binary standard library
// Use binary standard library
if err := i.Use(stdlib.Symbols); err != nil {
t.Fatal(err)
}
// Load pkg from sources
_, err := i.Eval(`import "github.com/foo/pkg"`)

View File

@@ -25,7 +25,7 @@ import (
"text/template"
)
const model = `// Code generated by 'yaegi extract {{.PkgName}}'. DO NOT EDIT.
const model = `// Code generated by 'yaegi extract {{.ImportPath}}'. DO NOT EDIT.
{{.License}}
@@ -39,7 +39,7 @@ import (
"{{$key}}"
{{- end}}
{{- end}}
"{{.PkgName}}"
"{{.ImportPath}}"
"reflect"
)
@@ -74,6 +74,7 @@ func init() {
{{range $key, $value := .Wrap -}}
// {{$value.Name}} is an interface wrapper for {{$key}} type
type {{$value.Name}} struct {
IValue interface{}
{{range $m := $value.Method -}}
W{{$m.Name}} func{{$m.Param}} {{$m.Result}}
{{end}}
@@ -167,8 +168,8 @@ func (e *Extractor) genContent(importPath string, p *types.Package) ([]byte, err
continue
}
pname := path.Base(importPath) + "." + name
if rname := path.Base(importPath) + name; restricted[rname] {
pname := p.Name() + "." + name
if rname := p.Name() + name; restricted[rname] {
// Restricted symbol, locally provided by stdlib wrapper.
pname = rname
}
@@ -203,7 +204,15 @@ func (e *Extractor) genContent(importPath string, p *types.Package) ([]byte, err
if args[j] = v.Name(); args[j] == "" {
args[j] = fmt.Sprintf("a%d", j)
}
params[j] = args[j] + " " + types.TypeString(v.Type(), qualify)
// process interface method variadic parameter
if sign.Variadic() && j == len(args)-1 { // check is last arg
// only replace the first "[]" to "..."
at := types.TypeString(v.Type(), qualify)[2:]
params[j] = args[j] + " ..." + at
args[j] += "..."
} else {
params[j] = args[j] + " " + types.TypeString(v.Type(), qualify)
}
}
arg := "(" + strings.Join(args, ", ") + ")"
param := "(" + strings.Join(params, ", ") + ")"
@@ -263,14 +272,15 @@ func (e *Extractor) genContent(importPath string, p *types.Package) ([]byte, err
b := new(bytes.Buffer)
data := map[string]interface{}{
"Dest": e.Dest,
"Imports": imports,
"PkgName": importPath,
"Val": val,
"Typ": typ,
"Wrap": wrap,
"BuildTags": buildTags,
"License": e.License,
"Dest": e.Dest,
"Imports": imports,
"ImportPath": importPath,
"PkgName": path.Join(importPath, p.Name()),
"Val": val,
"Typ": typ,
"Wrap": wrap,
"BuildTags": buildTags,
"License": e.License,
}
err = parse.Execute(b, data)
if err != nil {
@@ -436,7 +446,7 @@ func GetMinor(part string) string {
return minor
}
const defaultMinorVersion = 15
const defaultMinorVersion = 17
func genBuildTags() (string, error) {
version := runtime.Version()

View File

@@ -18,7 +18,7 @@ import (
)
func init() {
Symbols["guthib.com/baz"] = map[string]reflect.Value{
Symbols["guthib.com/baz/baz"] = map[string]reflect.Value{
// function, constant and variable definitions
"Hello": reflect.ValueOf(baz.Hello),
}
@@ -43,7 +43,7 @@ func TestPackages(t *testing.T) {
// We check this one because it shows both defects when we break it: the value
// gets corrupted, and the type becomes token.INT
// TODO(mpl): if the ident between key and value becomes annoying, be smarter about it.
contains: `"MaxFloat64": reflect.ValueOf(constant.MakeFromLiteral("179769313486231570814527423731704356798100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", token.FLOAT, 0)),`,
contains: `"MaxFloat32": reflect.ValueOf(constant.MakeFromLiteral("340282346638528859811704183484516925440", token.FLOAT, 0)),`,
},
{
desc: "using relative path, using go.mod",
@@ -78,6 +78,49 @@ func TestPackages(t *testing.T) {
importPath: "guthib.com/baz",
expected: expectedOutput,
},
{
desc: "using relative path, package name is not same as import path",
wd: "./testdata/6/src/guthib.com/bar",
arg: "../baz-baz",
importPath: "guthib.com/baz",
expected: expectedOutput,
},
{
desc: "using relative path, interface method parameter is variadic",
wd: "./testdata/7/src/guthib.com/variadic",
arg: "../variadic",
importPath: "guthib.com/variadic",
expected: `
// Code generated by 'yaegi extract guthib.com/variadic'. DO NOT EDIT.
package variadic
import (
"guthib.com/variadic"
"reflect"
)
func init() {
Symbols["guthib.com/variadic/variadic"] = map[string]reflect.Value{
// type definitions
"Variadic": reflect.ValueOf((*variadic.Variadic)(nil)),
// interface wrapper definitions
"_Variadic": reflect.ValueOf((*_guthib_com_variadic_Variadic)(nil)),
}
}
// _guthib_com_variadic_Variadic is an interface wrapper for Variadic type
type _guthib_com_variadic_Variadic struct {
IValue interface{}
WCall func(method string, args ...[]interface{}) (interface{}, error)
}
func (W _guthib_com_variadic_Variadic) Call(method string, args ...[]interface{}) (interface{}, error) {
return W.WCall(method, args...)
}
`[1:],
},
}
for _, test := range testCases {

View File

@@ -0,0 +1,9 @@
package main
import (
"guthib.com/baz"
)
func main() {
baz.Hello()
}

View File

@@ -0,0 +1,5 @@
package baz
func Hello() {
println("HELLO")
}

View File

@@ -0,0 +1 @@
module guthib.com/baz-baz

View File

@@ -0,0 +1 @@
module guthib.com/baz-baz/variadic

View File

@@ -0,0 +1,5 @@
package variadic
type Variadic interface {
Call(method string, args ...[]interface{}) (interface{}, error)
}

View File

@@ -289,6 +289,13 @@ func {{$name}}Assign(n *node) {
next := getExec(n.tnext)
typ := n.typ.TypeOf()
c0, c1 := n.child[0], n.child[1]
setMap := isMapEntry(c0)
var mapValue, indexValue func(*frame) reflect.Value
if setMap {
mapValue = genValue(c0.child[0])
indexValue = genValue(c0.child[1])
}
if c1.rval.IsValid() {
switch typ.Kind() {
@@ -299,6 +306,9 @@ func {{$name}}Assign(n *node) {
n.exec = func(f *frame) bltn {
v, s := v0(f)
v.SetString(s {{$op.Name}} v1)
if setMap {
mapValue(f).SetMapIndex(indexValue(f), v)
}
return next
}
{{- end}}
@@ -312,6 +322,9 @@ func {{$name}}Assign(n *node) {
n.exec = func(f *frame) bltn {
v, i := v0(f)
v.SetInt(i {{$op.Name}} j)
if setMap {
mapValue(f).SetMapIndex(indexValue(f), v)
}
return next
}
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
@@ -320,6 +333,9 @@ func {{$name}}Assign(n *node) {
n.exec = func(f *frame) bltn {
v, i := v0(f)
v.SetUint(i {{$op.Name}} j)
if setMap {
mapValue(f).SetMapIndex(indexValue(f), v)
}
return next
}
{{- if $op.Float}}
@@ -329,6 +345,9 @@ func {{$name}}Assign(n *node) {
n.exec = func(f *frame) bltn {
v, i := v0(f)
v.SetFloat(i {{$op.Name}} j)
if setMap {
mapValue(f).SetMapIndex(indexValue(f), v)
}
return next
}
case reflect.Complex64, reflect.Complex128:
@@ -337,6 +356,9 @@ func {{$name}}Assign(n *node) {
n.exec = func(f *frame) bltn {
v := v0(f)
v.SetComplex(v.Complex() {{$op.Name}} v1)
if setMap {
mapValue(f).SetMapIndex(indexValue(f), v)
}
return next
}
{{- end}}
@@ -350,6 +372,9 @@ func {{$name}}Assign(n *node) {
n.exec = func(f *frame) bltn {
v, s := v0(f)
v.SetString(s {{$op.Name}} v1(f).String())
if setMap {
mapValue(f).SetMapIndex(indexValue(f), v)
}
return next
}
{{- end}}
@@ -364,6 +389,9 @@ func {{$name}}Assign(n *node) {
v, i := v0(f)
_, j := v1(f)
v.SetInt(i {{$op.Name}} j)
if setMap {
mapValue(f).SetMapIndex(indexValue(f), v)
}
return next
}
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
@@ -373,6 +401,9 @@ func {{$name}}Assign(n *node) {
v, i := v0(f)
_, j := v1(f)
v.SetUint(i {{$op.Name}} j)
if setMap {
mapValue(f).SetMapIndex(indexValue(f), v)
}
return next
}
{{- if $op.Float}}
@@ -383,6 +414,9 @@ func {{$name}}Assign(n *node) {
v, i := v0(f)
_, j := v1(f)
v.SetFloat(i {{$op.Name}} j)
if setMap {
mapValue(f).SetMapIndex(indexValue(f), v)
}
return next
}
case reflect.Complex64, reflect.Complex128:
@@ -391,6 +425,9 @@ func {{$name}}Assign(n *node) {
n.exec = func(f *frame) bltn {
v := v0(f)
v.SetComplex(v.Complex() {{$op.Name}} v1(f).Complex())
if setMap {
mapValue(f).SetMapIndex(indexValue(f), v)
}
return next
}
{{- end}}
@@ -402,34 +439,54 @@ func {{$name}}Assign(n *node) {
func {{$name}}(n *node) {
next := getExec(n.tnext)
typ := n.typ.TypeOf()
c0 := n.child[0]
setMap := isMapEntry(c0)
var mapValue, indexValue func(*frame) reflect.Value
if setMap {
mapValue = genValue(c0.child[0])
indexValue = genValue(c0.child[1])
}
switch typ.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
v0 := genValueInt(n.child[0])
v0 := genValueInt(c0)
n.exec = func(f *frame) bltn {
v, i := v0(f)
v.SetInt(i {{$op.Name}} 1)
if setMap {
mapValue(f).SetMapIndex(indexValue(f), v)
}
return next
}
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
v0 := genValueUint(n.child[0])
v0 := genValueUint(c0)
n.exec = func(f *frame) bltn {
v, i := v0(f)
v.SetUint(i {{$op.Name}} 1)
if setMap {
mapValue(f).SetMapIndex(indexValue(f), v)
}
return next
}
case reflect.Float32, reflect.Float64:
v0 := genValueFloat(n.child[0])
v0 := genValueFloat(c0)
n.exec = func(f *frame) bltn {
v, i := v0(f)
v.SetFloat(i {{$op.Name}} 1)
if setMap {
mapValue(f).SetMapIndex(indexValue(f), v)
}
return next
}
case reflect.Complex64, reflect.Complex128:
v0 := genValue(n.child[0])
v0 := genValue(c0)
n.exec = func(f *frame) bltn {
v := v0(f)
v.SetComplex(v.Complex() {{$op.Name}} 1)
if setMap {
mapValue(f).SetMapIndex(indexValue(f), v)
}
return next
}
}

View File

@@ -357,7 +357,7 @@ func ignoreError(err error, src string) bool {
}
func wrapInMain(src string) string {
return fmt.Sprintf("package main; func main() {%s}", src)
return fmt.Sprintf("package main; func main() {%s\n}", src)
}
// Note: no type analysis is performed at this stage, it is done in pre-order
@@ -680,6 +680,7 @@ func (interp *Interpreter) ast(src, name string, inc bool) (string, *node, error
case *ast.FuncDecl:
n := addChild(&root, anc, pos, funcDecl, aNop)
n.val = n
if a.Recv == nil {
// function is not a method, create an empty receiver list
addChild(&root, astNode{n, nod}, pos, fieldList, aNop)
@@ -716,7 +717,7 @@ func (interp *Interpreter) ast(src, name string, inc bool) (string, *node, error
n := addChild(&root, anc, pos, identExpr, aNop)
n.ident = a.Name
st.push(n, nod)
if n.anc.kind == defineStmt && n.anc.nright == 0 {
if n.anc.kind == defineStmt && n.anc.anc.kind == constDecl && n.anc.nright == 0 {
// Implicit assign expression (in a ConstDecl block).
// Clone assign source and type from previous
a := n.anc
@@ -858,7 +859,8 @@ func (interp *Interpreter) ast(src, name string, inc bool) (string, *node, error
case *ast.ValueSpec:
kind := valueSpec
act := aNop
if a.Values != nil {
switch {
case a.Values != nil:
if len(a.Names) > 1 && len(a.Values) == 1 {
if anc.node.kind == constDecl || anc.node.kind == varDecl {
kind = defineXStmt
@@ -874,7 +876,9 @@ func (interp *Interpreter) ast(src, name string, inc bool) (string, *node, error
}
act = aAssign
}
} else if anc.node.kind == constDecl {
case anc.node.kind == constDecl:
kind, act = defineStmt, aAssign
case anc.node.kind == varDecl && anc.node.anc.kind != fileStmt:
kind, act = defineStmt, aAssign
}
n := addChild(&root, anc, pos, kind, act)

View File

@@ -163,9 +163,9 @@ var knownOs = map[string]bool{
"dragonfly": true,
"freebsd": true,
"illumos": true,
"ios": true,
"js": true,
"linux": true,
"nacl": true,
"netbsd": true,
"openbsd": true,
"plan9": true,
@@ -176,9 +176,9 @@ var knownOs = map[string]bool{
var knownArch = map[string]bool{
"386": true,
"amd64": true,
"amd64p32": true,
"arm": true,
"arm64": true,
"loong64": true,
"mips": true,
"mips64": true,
"mips64le": true,

View File

@@ -7,7 +7,6 @@ import (
"math"
"path/filepath"
"reflect"
"regexp"
"strings"
"unicode"
)
@@ -44,8 +43,6 @@ var constBltn = map[string]func(*node){
bltnReal: realConst,
}
var identifier = regexp.MustCompile(`([\pL_][\pL_\d]*)$`)
const nilIdent = "nil"
// cfg generates a control flow graph (CFG) from AST (wiring successors in AST)
@@ -82,7 +79,14 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
i--
}
dest := a.child[i]
if dest.typ != nil && !isInterface(dest.typ) {
if dest.typ == nil {
break
}
if dest.typ.incomplete {
err = n.cfgErrorf("invalid type declaration")
return false
}
if !isInterface(dest.typ) {
// Interface type are not propagated, and will be resolved at post-order.
n.typ = dest.typ
}
@@ -147,6 +151,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
vtyp = &itype{cat: valueT, rtype: typ.Elem()}
case reflect.String:
sc.add(sc.getType("int")) // Add a dummy type to store array shallow copy for range
sc.add(sc.getType("int")) // Add a dummy type to store index for range
ktyp = sc.getType("int")
vtyp = sc.getType("rune")
case reflect.Array, reflect.Slice:
@@ -170,9 +175,10 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
}
case stringT:
sc.add(sc.getType("int")) // Add a dummy type to store array shallow copy for range
sc.add(sc.getType("int")) // Add a dummy type to store index for range
ktyp = sc.getType("int")
vtyp = sc.getType("rune")
case arrayT, variadicT:
case arrayT, sliceT, variadicT:
sc.add(sc.getType("int")) // Add a dummy type to store array shallow copy for range
ktyp = sc.getType("int")
vtyp = o.typ.val
@@ -518,10 +524,6 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
if dest.typ.incomplete {
return
}
if dest.typ.sizedef {
dest.typ.size = arrayTypeLen(src)
dest.typ.rtype = nil
}
if sc.global {
// Do not overload existing symbols (defined in GTA) in global scope
sym, _, _ = sc.lookup(dest.ident)
@@ -555,9 +557,6 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
n.findex = dest.findex
n.level = dest.level
// Propagate type.
// TODO: Check that existing destination type matches source type.
// In the following, we attempt to optimize by skipping the assign
// operation and setting the source location directly to the destination
// location in the frame.
@@ -571,8 +570,14 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
// Setting a map entry requires an additional step, do not optimize.
// As we only write, skip the default useless getIndexMap dest action.
dest.gen = nop
case isCall(src) && dest.typ.cat != interfaceT && !isRecursiveField(dest):
case isFuncField(dest):
// Setting a struct field of function type requires an extra step. Do not optimize.
case isCall(src) && !isInterfaceSrc(dest.typ) && !isRecursiveField(dest) && n.kind != defineStmt:
// Call action may perform the assignment directly.
if dest.typ.id() != src.typ.id() {
// Skip optimitization if returned type doesn't match assigned one.
break
}
n.gen = nop
src.level = level
src.findex = dest.findex
@@ -605,6 +610,8 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
case src.kind == basicLit:
// Assign to nil.
src.rval = reflect.New(dest.typ.TypeOf()).Elem()
case n.nright == 0:
n.gen = reset
}
n.typ = dest.typ
@@ -724,7 +731,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
// by constOp and available in n.rval. Nothing else to do at execution.
n.gen = nop
n.findex = notInFrame
case n.anc.kind == assignStmt && n.anc.action == aAssign:
case n.anc.kind == assignStmt && n.anc.action == aAssign && n.anc.nleft == 1:
// To avoid a copy in frame, if the result is to be assigned, store it directly
// at the frame location of destination.
dest := n.anc.child[childPos(n)-n.anc.nright]
@@ -857,7 +864,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
case callExpr:
wireChild(n)
switch {
case interp.isBuiltinCall(n):
case isBuiltinCall(n, sc):
c0 := n.child[0]
bname := c0.ident
err = check.builtin(bname, n, n.child[1:], n.action == aCallSlice)
@@ -923,9 +930,12 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
if !c1.typ.implements(c0.typ) {
err = n.cfgErrorf("type %v does not implement interface %v", c1.typ.id(), c0.typ.id())
}
// Pass value as is
// Convert type to interface while keeping a reference to the original concrete type.
// besides type, the node value remains preserved.
n.gen = nop
n.typ = c1.typ
t := *c0.typ
n.typ = &t
n.typ.val = c1.typ
n.findex = c1.findex
n.level = c1.level
n.val = c1.val
@@ -974,6 +984,20 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
}
}
}
case isOffsetof(n):
if len(n.child) != 2 || n.child[1].kind != selectorExpr || !isStruct(n.child[1].child[0].typ) {
err = n.cfgErrorf("Offsetof argument: invalid expression")
break
}
c1 := n.child[1]
field, ok := c1.child[0].typ.rtype.FieldByName(c1.child[1].ident)
if !ok {
err = n.cfgErrorf("struct does not contain field: %s", c1.child[1].ident)
break
}
n.typ = &itype{cat: valueT, rtype: reflect.TypeOf(field.Offset)}
n.rval = reflect.ValueOf(field.Offset)
n.gen = nop
default:
err = check.arguments(n, n.child[1:], n.child[0], n.action == aCallSlice)
if err != nil {
@@ -986,7 +1010,9 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
}
if typ := n.child[0].typ; len(typ.ret) > 0 {
n.typ = typ.ret[0]
if n.anc.kind == returnStmt {
if n.anc.kind == returnStmt && n.typ.id() == sc.def.typ.ret[0].id() {
// Store the result directly to the return value area of frame.
// It can be done only if no type conversion at return is involved.
n.findex = childPos(n)
} else {
n.findex = sc.add(n.typ)
@@ -1043,8 +1069,8 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
}
switch n.typ.cat {
case arrayT:
err = check.arrayLitExpr(child, n.typ.val, n.typ.size)
case arrayT, sliceT:
err = check.arrayLitExpr(child, n.typ)
case mapT:
err = check.mapLitExpr(child, n.typ.key, n.typ.val)
case structT:
@@ -1234,11 +1260,10 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
}
}
// Found symbol, populate node info
n.typ, n.findex, n.level = sym.typ, sym.index, level
n.sym, n.typ, n.findex, n.level = sym, sym.typ, sym.index, level
if n.findex < 0 {
n.val = sym.node
} else {
n.sym = sym
switch {
case sym.kind == constSym && sym.rval.IsValid():
n.rval = sym.rval
@@ -1426,7 +1451,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
// TODO(mpl): move any of that code to typecheck?
c.typ.node = c
if !c.typ.assignableTo(typ) {
err = fmt.Errorf("cannot use %v (type %v) as type %v in return argument", c.ident, c.typ.cat, typ.cat)
err = c.cfgErrorf("cannot use %v (type %v) as type %v in return argument", c.ident, c.typ.cat, typ.cat)
return
}
if c.typ.cat == nilT {
@@ -1448,72 +1473,8 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
err = n.cfgErrorf("undefined type")
break
}
if n.typ.cat == valueT || n.typ.cat == errorT {
// Handle object defined in runtime, try to find field or method
// Search for method first, as it applies both to types T and *T
// Search for field must then be performed on type T only (not *T)
switch method, ok := n.typ.rtype.MethodByName(n.child[1].ident); {
case ok:
hasRecvType := n.typ.rtype.Kind() != reflect.Interface
n.val = method.Index
n.gen = getIndexBinMethod
n.action = aGetMethod
n.recv = &receiver{node: n.child[0]}
n.typ = &itype{cat: valueT, rtype: method.Type, isBinMethod: true}
if hasRecvType {
n.typ.recv = n.typ
}
case n.typ.rtype.Kind() == reflect.Ptr:
if field, ok := n.typ.rtype.Elem().FieldByName(n.child[1].ident); ok {
n.typ = &itype{cat: valueT, rtype: field.Type}
n.val = field.Index
n.gen = getPtrIndexSeq
} else {
err = n.cfgErrorf("undefined field or method: %s", n.child[1].ident)
}
case n.typ.rtype.Kind() == reflect.Struct:
if field, ok := n.typ.rtype.FieldByName(n.child[1].ident); ok {
n.typ = &itype{cat: valueT, rtype: field.Type}
n.val = field.Index
n.gen = getIndexSeq
} else {
// method lookup failed on type, now lookup on pointer to type
pt := reflect.PtrTo(n.typ.rtype)
if m2, ok2 := pt.MethodByName(n.child[1].ident); ok2 {
n.val = m2.Index
n.gen = getIndexBinPtrMethod
n.typ = &itype{cat: valueT, rtype: m2.Type, recv: &itype{cat: valueT, rtype: pt}}
n.recv = &receiver{node: n.child[0]}
n.action = aGetMethod
} else {
err = n.cfgErrorf("undefined field or method: %s", n.child[1].ident)
}
}
default:
err = n.cfgErrorf("undefined field or method: %s", n.child[1].ident)
}
} else if n.typ.cat == ptrT && (n.typ.val.cat == valueT || n.typ.val.cat == errorT) {
// Handle pointer on object defined in runtime
if method, ok := n.typ.val.rtype.MethodByName(n.child[1].ident); ok {
n.val = method.Index
n.typ = &itype{cat: valueT, rtype: method.Type, recv: n.typ}
n.recv = &receiver{node: n.child[0]}
n.gen = getIndexBinMethod
n.action = aGetMethod
} else if method, ok := reflect.PtrTo(n.typ.val.rtype).MethodByName(n.child[1].ident); ok {
n.val = method.Index
n.gen = getIndexBinMethod
n.typ = &itype{cat: valueT, rtype: method.Type, recv: &itype{cat: valueT, rtype: reflect.PtrTo(n.typ.val.rtype)}}
n.recv = &receiver{node: n.child[0]}
n.action = aGetMethod
} else if field, ok := n.typ.val.rtype.FieldByName(n.child[1].ident); ok {
n.typ = &itype{cat: valueT, rtype: field.Type}
n.val = field.Index
n.gen = getPtrIndexSeq
} else {
err = n.cfgErrorf("undefined selector: %s", n.child[1].ident)
}
} else if n.typ.cat == binPkgT {
switch {
case n.typ.cat == binPkgT:
// Resolve binary package symbol: a type or a value
name := n.child[1].ident
pkg := n.child[0].sym.typ.path
@@ -1521,7 +1482,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
if isBinType(s) {
n.typ = &itype{cat: valueT, rtype: s.Type().Elem()}
} else {
n.typ = &itype{cat: valueT, rtype: s.Type(), untyped: isValueUntyped(s)}
n.typ = &itype{cat: valueT, rtype: fixPossibleConstType(s.Type()), untyped: isValueUntyped(s)}
n.rval = s
}
n.action = aGetSym
@@ -1529,7 +1490,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
} else {
err = n.cfgErrorf("package %s \"%s\" has no symbol %s", n.child[0].ident, pkg, name)
}
} else if n.typ.cat == srcPkgT {
case n.typ.cat == srcPkgT:
pkg, name := n.child[0].sym.typ.path, n.child[1].ident
// Resolve source package symbol
if sym, ok := interp.srcPkg[pkg][name]; ok {
@@ -1542,70 +1503,172 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
n.action = aGetSym
n.typ = sym.typ
n.sym = sym
n.recv = sym.recv
n.rval = sym.rval
} else {
err = n.cfgErrorf("undefined selector: %s.%s", pkg, name)
}
} else if m, lind := n.typ.lookupMethod(n.child[1].ident); m != nil {
n.action = aGetMethod
if n.child[0].isType(sc) {
// Handle method as a function with receiver in 1st argument
n.val = m
n.findex = notInFrame
n.gen = nop
n.typ = &itype{}
*n.typ = *m.typ
n.typ.arg = append([]*itype{n.child[0].typ}, m.typ.arg...)
} else {
// Handle method with receiver
n.gen = getMethod
n.val = m
n.typ = m.typ
case isStruct(n.typ) || isInterfaceSrc(n.typ):
// Find a matching field.
if ti := n.typ.lookupField(n.child[1].ident); len(ti) > 0 {
if isStruct(n.typ) {
// If a method of the same name exists, use it if it is shallower than the struct field.
// if method's depth is the same as field's, this is an error.
d := n.typ.methodDepth(n.child[1].ident)
if d >= 0 && d < len(ti) {
goto tryMethods
}
if d == len(ti) {
err = n.cfgErrorf("ambiguous selector: %s", n.child[1].ident)
break
}
}
n.val = ti
switch {
case isInterfaceSrc(n.typ):
n.typ = n.typ.fieldSeq(ti)
n.gen = getMethodByName
n.action = aMethod
case n.typ.cat == ptrT:
n.typ = n.typ.fieldSeq(ti)
n.gen = getPtrIndexSeq
if n.typ.cat == funcT {
// Function in a struct field is always wrapped in reflect.Value.
rtype := n.typ.TypeOf()
n.typ = &itype{cat: valueT, rtype: rtype, val: n.typ}
}
default:
n.gen = getIndexSeq
n.typ = n.typ.fieldSeq(ti)
if n.typ.cat == funcT {
// Function in a struct field is always wrapped in reflect.Value.
rtype := n.typ.TypeOf()
n.typ = &itype{cat: valueT, rtype: rtype, val: n.typ}
}
}
break
}
if s, lind, ok := n.typ.lookupBinField(n.child[1].ident); ok {
// Handle an embedded binary field into a struct field.
n.gen = getIndexSeqField
lind = append(lind, s.Index...)
if isStruct(n.typ) {
// If a method of the same name exists, use it if it is shallower than the struct field.
// if method's depth is the same as field's, this is an error.
d := n.typ.methodDepth(n.child[1].ident)
if d >= 0 && d < len(lind) {
goto tryMethods
}
if d == len(lind) {
err = n.cfgErrorf("ambiguous selector: %s", n.child[1].ident)
break
}
}
n.val = lind
n.typ = &itype{cat: valueT, rtype: s.Type}
break
}
// No field (embedded or not) matched. Try to match a method.
tryMethods:
fallthrough
default:
// Find a matching method.
// TODO (marc): simplify the following if/elseif blocks.
if n.typ.cat == valueT || n.typ.cat == errorT {
switch method, ok := n.typ.rtype.MethodByName(n.child[1].ident); {
case ok:
hasRecvType := n.typ.rtype.Kind() != reflect.Interface
n.val = method.Index
n.gen = getIndexBinMethod
n.action = aGetMethod
n.recv = &receiver{node: n.child[0]}
n.typ = &itype{cat: valueT, rtype: method.Type, isBinMethod: true}
if hasRecvType {
n.typ.recv = n.typ
}
case n.typ.rtype.Kind() == reflect.Ptr:
if field, ok := n.typ.rtype.Elem().FieldByName(n.child[1].ident); ok {
n.typ = &itype{cat: valueT, rtype: field.Type}
n.val = field.Index
n.gen = getPtrIndexSeq
break
}
err = n.cfgErrorf("undefined field or method: %s", n.child[1].ident)
case n.typ.rtype.Kind() == reflect.Struct:
if field, ok := n.typ.rtype.FieldByName(n.child[1].ident); ok {
n.typ = &itype{cat: valueT, rtype: field.Type}
n.val = field.Index
n.gen = getIndexSeq
break
}
fallthrough
default:
// method lookup failed on type, now lookup on pointer to type
pt := reflect.PtrTo(n.typ.rtype)
if m2, ok2 := pt.MethodByName(n.child[1].ident); ok2 {
n.val = m2.Index
n.gen = getIndexBinPtrMethod
n.typ = &itype{cat: valueT, rtype: m2.Type, recv: &itype{cat: valueT, rtype: pt}, isBinMethod: true}
n.recv = &receiver{node: n.child[0]}
n.action = aGetMethod
break
}
err = n.cfgErrorf("undefined field or method: %s", n.child[1].ident)
}
} else if n.typ.cat == ptrT && (n.typ.val.cat == valueT || n.typ.val.cat == errorT) {
// Handle pointer on object defined in runtime
if method, ok := n.typ.val.rtype.MethodByName(n.child[1].ident); ok {
n.val = method.Index
n.typ = &itype{cat: valueT, rtype: method.Type, recv: n.typ, isBinMethod: true}
n.recv = &receiver{node: n.child[0]}
n.gen = getIndexBinElemMethod
n.action = aGetMethod
} else if method, ok := reflect.PtrTo(n.typ.val.rtype).MethodByName(n.child[1].ident); ok {
n.val = method.Index
n.gen = getIndexBinMethod
n.typ = &itype{cat: valueT, rtype: method.Type, recv: &itype{cat: valueT, rtype: reflect.PtrTo(n.typ.val.rtype)}, isBinMethod: true}
n.recv = &receiver{node: n.child[0]}
n.action = aGetMethod
} else if field, ok := n.typ.val.rtype.FieldByName(n.child[1].ident); ok {
n.typ = &itype{cat: valueT, rtype: field.Type}
n.val = field.Index
n.gen = getPtrIndexSeq
} else {
err = n.cfgErrorf("undefined selector: %s", n.child[1].ident)
}
} else if m, lind := n.typ.lookupMethod(n.child[1].ident); m != nil {
n.action = aGetMethod
if n.child[0].isType(sc) {
// Handle method as a function with receiver in 1st argument
n.val = m
n.findex = notInFrame
n.gen = nop
n.typ = &itype{}
*n.typ = *m.typ
n.typ.arg = append([]*itype{n.child[0].typ}, m.typ.arg...)
} else {
// Handle method with receiver
n.gen = getMethod
n.val = m
n.typ = m.typ
n.recv = &receiver{node: n.child[0], index: lind}
}
} else if m, lind, isPtr, ok := n.typ.lookupBinMethod(n.child[1].ident); ok {
n.action = aGetMethod
switch {
case isPtr && n.typ.fieldSeq(lind).cat != ptrT:
n.gen = getIndexSeqPtrMethod
case isInterfaceSrc(n.typ):
n.gen = getMethodByName
default:
n.gen = getIndexSeqMethod
}
n.recv = &receiver{node: n.child[0], index: lind}
}
} else if m, lind, isPtr, ok := n.typ.lookupBinMethod(n.child[1].ident); ok {
n.action = aGetMethod
if isPtr && n.typ.fieldSeq(lind).cat != ptrT {
n.gen = getIndexSeqPtrMethod
n.val = append([]int{m.Index}, lind...)
n.typ = &itype{cat: valueT, rtype: m.Type, recv: n.child[0].typ, isBinMethod: true}
} else {
n.gen = getIndexSeqMethod
err = n.cfgErrorf("undefined selector: %s", n.child[1].ident)
}
n.recv = &receiver{node: n.child[0], index: lind}
n.val = append([]int{m.Index}, lind...)
n.typ = &itype{cat: valueT, rtype: m.Type, recv: n.child[0].typ}
} else if ti := n.typ.lookupField(n.child[1].ident); len(ti) > 0 {
// Handle struct field
n.val = ti
switch {
case isInterfaceSrc(n.typ):
n.typ = n.typ.fieldSeq(ti)
n.gen = getMethodByName
n.action = aMethod
case n.typ.cat == ptrT:
n.typ = n.typ.fieldSeq(ti)
n.gen = getPtrIndexSeq
if n.typ.cat == funcT {
// function in a struct field is always wrapped in reflect.Value
rtype := n.typ.TypeOf()
n.typ = &itype{cat: valueT, rtype: rtype, val: n.typ}
}
default:
n.gen = getIndexSeq
n.typ = n.typ.fieldSeq(ti)
if n.typ.cat == funcT {
// function in a struct field is always wrapped in reflect.Value
rtype := n.typ.TypeOf()
n.typ = &itype{cat: valueT, rtype: rtype, val: n.typ}
}
}
} else if s, lind, ok := n.typ.lookupBinField(n.child[1].ident); ok {
// Handle an embedded binary field into a struct field
n.gen = getIndexSeqField
lind = append(lind, s.Index...)
n.val = lind
n.typ = &itype{cat: valueT, rtype: s.Type}
} else {
err = n.cfgErrorf("undefined selector: %s", n.child[1].ident)
}
if err == nil && n.findex != -1 {
n.findex = sc.add(n.typ)
@@ -1714,15 +1777,17 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
break
}
// Chain case clauses.
for i, c := range clauses[:l-1] {
// Chain to next clause.
setFNext(c, clauses[i+1])
for i := l - 1; i >= 0; i-- {
c := clauses[i]
if len(c.child) == 0 {
c.tnext = n // Clause body is empty, exit.
} else {
body := c.lastChild()
c.tnext = body.start
if len(body.child) > 0 && body.lastChild().kind == fallthroughtStmt {
c.child[0].tnext = c
c.start = c.child[0].start
if i < l-1 && len(body.child) > 0 && body.lastChild().kind == fallthroughtStmt {
if n.kind == typeSwitch {
err = body.lastChild().cfgErrorf("cannot fallthrough in type switch")
}
@@ -1735,15 +1800,16 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
body.tnext = n // Exit switch at end of clause body.
}
}
}
c := clauses[l-1] // Last clause.
c.fnext = n
if len(c.child) == 0 {
c.tnext = n // Clause body is empty, exit.
} else {
body := c.lastChild()
c.tnext = body.start
body.tnext = n
if i == l-1 {
setFNext(clauses[i], n)
continue
}
if len(clauses[i+1].child) > 1 {
setFNext(c, clauses[i+1].start)
} else {
setFNext(c, clauses[i+1])
}
}
n.start = n.child[0].start
n.child[0].tnext = sbn.start
@@ -1865,7 +1931,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
case n.rval.IsValid():
n.gen = nop
n.findex = notInFrame
case n.anc.kind == assignStmt && n.anc.action == aAssign:
case n.anc.kind == assignStmt && n.anc.action == aAssign && n.anc.nright == 1:
dest := n.anc.child[childPos(n)-n.anc.nright]
n.typ = dest.typ
n.findex = dest.findex
@@ -2160,7 +2226,10 @@ func (n *node) isType(sc *scope) bool {
suffixedPkg := filepath.Join(pkg, baseName)
sym, _, ok := sc.lookup(suffixedPkg)
if !ok {
return false
sym, _, ok = sc.lookup(pkg)
if !ok {
return false
}
}
if sym.kind != pkgSym {
return false
@@ -2388,6 +2457,10 @@ func isMethod(n *node) bool {
return len(n.child[0].child) > 0 // receiver defined
}
func isFuncField(n *node) bool {
return isField(n) && isFunc(n.typ)
}
func isMapEntry(n *node) bool {
return n.action == aGetIndex && isMap(n.child[0].typ)
}
@@ -2400,6 +2473,10 @@ func isBinCall(n *node) bool {
return isCall(n) && n.child[0].typ.cat == valueT && n.child[0].typ.rtype.Kind() == reflect.Func
}
func isOffsetof(n *node) bool {
return isCall(n) && n.child[0].typ.cat == valueT && n.child[0].rval.String() == "Offsetof"
}
func mustReturnValue(n *node) bool {
if len(n.child) < 2 {
return false
@@ -2490,7 +2567,9 @@ func gotoLabel(s *symbol) {
return
}
for _, c := range s.from {
c.tnext = s.node.start
if c.tnext == nil {
c.tnext = s.node.start
}
}
}
@@ -2498,7 +2577,7 @@ func compositeGenerator(n *node, typ *itype, rtyp reflect.Type) (gen bltnGenerat
switch typ.cat {
case aliasT, ptrT:
gen = compositeGenerator(n, n.typ.val, rtyp)
case arrayT:
case arrayT, sliceT:
gen = arrayLit
case mapT:
gen = mapLit
@@ -2539,7 +2618,7 @@ func compositeGenerator(n *node, typ *itype, rtyp reflect.Type) (gen bltnGenerat
gen = compositeBinMap
case reflect.Ptr:
gen = compositeGenerator(n, typ, n.typ.val.rtype)
case reflect.Slice:
case reflect.Slice, reflect.Array:
gen = compositeBinSlice
default:
log.Panic(n.cfgErrorf("compositeGenerator not implemented for type kind: %s", k))
@@ -2552,8 +2631,8 @@ func compositeGenerator(n *node, typ *itype, rtyp reflect.Type) (gen bltnGenerat
// array variable it is determined from the value's type, otherwise it is
// computed from the source definition.
func arrayTypeLen(n *node) int {
if n.typ != nil && n.typ.sizedef {
return n.typ.size
if n.typ != nil && n.typ.cat == arrayT {
return n.typ.length
}
max := -1
for i, c := range n.child[1:] {

View File

@@ -1,6 +1,7 @@
package interp
import (
"path"
"path/filepath"
"reflect"
)
@@ -80,9 +81,7 @@ func (interp *Interpreter) gta(root *node, rpath, importPath string) ([]*node, e
if typ.isBinMethod {
typ = &itype{cat: valueT, rtype: typ.methodCallType(), isBinMethod: true, scope: sc}
}
if sc.sym[dest.ident] == nil || sc.sym[dest.ident].typ.incomplete {
sc.sym[dest.ident] = &symbol{kind: varSym, global: true, index: sc.add(typ), typ: typ, rval: val, node: n}
}
sc.sym[dest.ident] = &symbol{kind: varSym, global: true, index: sc.add(typ), typ: typ, rval: val, node: n}
if n.anc.kind == constDecl {
sc.sym[dest.ident].kind = constSym
if childPos(n) == len(n.anc.child)-1 {
@@ -189,11 +188,14 @@ func (interp *Interpreter) gta(root *node, rpath, importPath string) ([]*node, e
}
// Try to import a binary package first, or a source package
var pkgName string
if interp.binPkg[ipath] != nil {
if packageName := path.Base(ipath); path.Dir(ipath) == packageName {
ipath = packageName
}
if pkg := interp.binPkg[ipath]; pkg != nil {
switch name {
case "_": // no import of symbols
case ".": // import symbols in current scope
for n, v := range interp.binPkg[ipath] {
for n, v := range pkg {
typ := v.Type()
if isBinType(v) {
typ = typ.Elem()
@@ -202,16 +204,19 @@ func (interp *Interpreter) gta(root *node, rpath, importPath string) ([]*node, e
}
default: // import symbols in package namespace
if name == "" {
name = identifier.FindString(ipath)
name = interp.pkgNames[ipath]
}
// imports of a same package are all mapped in the same scope, so we cannot just
// Imports of a same package are all mapped in the same scope, so we cannot just
// map them by their names, otherwise we could have collisions from same-name
// imports in different source files of the same package. Therefore, we suffix
// the key with the basename of the source file.
name = filepath.Join(name, baseName)
if _, exists := sc.sym[name]; !exists {
if sym, exists := sc.sym[name]; !exists {
sc.sym[name] = &symbol{kind: pkgSym, typ: &itype{cat: binPkgT, path: ipath, scope: sc}}
break
} else if sym.kind == pkgSym && sym.typ.cat == srcPkgT && sym.typ.path == ipath {
// ignore re-import of identical package
break
}
// redeclaration error. Not caught by the parser.
@@ -233,9 +238,12 @@ func (interp *Interpreter) gta(root *node, rpath, importPath string) ([]*node, e
name = pkgName
}
name = filepath.Join(name, baseName)
if _, exists := sc.sym[name]; !exists {
if sym, exists := sc.sym[name]; !exists {
sc.sym[name] = &symbol{kind: pkgSym, typ: &itype{cat: srcPkgT, path: ipath, scope: sc}}
break
} else if sym.kind == pkgSym && sym.typ.cat == srcPkgT && sym.typ.path == ipath {
// ignore re-import of identical package
break
}
// redeclaration error

View File

@@ -14,6 +14,8 @@ import (
"log"
"os"
"os/signal"
"path"
"path/filepath"
"reflect"
"runtime"
"runtime/debug"
@@ -92,21 +94,29 @@ func newFrame(anc *frame, length int, id uint64) *frame {
func (f *frame) runid() uint64 { return atomic.LoadUint64(&f.id) }
func (f *frame) setrunid(id uint64) { atomic.StoreUint64(&f.id, id) }
func (f *frame) clone() *frame {
func (f *frame) clone(fork bool) *frame {
f.mutex.RLock()
defer f.mutex.RUnlock()
return &frame{
nf := &frame{
anc: f.anc,
root: f.root,
data: f.data,
deferred: f.deferred,
recovered: f.recovered,
id: f.runid(),
done: f.done,
}
if fork {
nf.data = make([]reflect.Value, len(f.data))
copy(nf.data, f.data)
} else {
nf.data = f.data
}
return nf
}
// Exports stores the map of binary packages per package path.
// The package path is the path joined from the import path and the package name
// as specified in source files by the "package" statement.
type Exports map[string]map[string]reflect.Value
// imports stores the map of source packages per package path.
@@ -118,13 +128,14 @@ type opt struct {
cfgDot bool // display CFG graph (debug)
// dotCmd is the command to process the dot graph produced when astDot and/or
// cfgDot is enabled. It defaults to 'dot -Tdot -o <filename>.dot'.
dotCmd string
noRun bool // compile, but do not run
fastChan bool // disable cancellable chan operations
context build.Context // build context: GOPATH, build constraints
stdin io.Reader // standard input
stdout io.Writer // standard output
stderr io.Writer // standard error
dotCmd string
noRun bool // compile, but do not run
fastChan bool // disable cancellable chan operations
context build.Context // build context: GOPATH, build constraints
specialStdio bool // Allows os.Stdin, os.Stdout, os.Stderr to not be file descriptors
stdin io.Reader // standard input
stdout io.Writer // standard output
stderr io.Writer // standard error
}
// Interpreter contains global resources and state.
@@ -162,7 +173,7 @@ type Interpreter struct {
const (
mainID = "main"
selfPrefix = "github.com/traefik/yaegi"
selfPath = selfPrefix + "/interp"
selfPath = selfPrefix + "/interp/interp"
// DefaultSourceName is the name used by default when the name of the input
// source file has not been specified for an Eval.
// TODO(mpl): something even more special as a name?
@@ -174,6 +185,9 @@ const (
NoTest = true
)
// Self points to the current interpreter if accessed from within itself, or is nil.
var Self *Interpreter
// Symbols exposes interpreter values.
var Symbols = Exports{
selfPath: map[string]reflect.Value{
@@ -189,6 +203,7 @@ func init() { Symbols[selfPath]["Symbols"] = reflect.ValueOf(Symbols) }
// _error is a wrapper of error interface type.
type _error struct {
IValue interface{}
WError func() string
}
@@ -208,7 +223,7 @@ type Panic struct {
}
// TODO: Capture interpreter stack frames also and remove
// fmt.Println(n.cfgErrorf("panic")) in runCfg.
// fmt.Fprintln(n.interp.stderr, oNode.cfgErrorf("panic")) in runCfg.
func (e Panic) Error() string { return fmt.Sprint(e.Value) }
@@ -235,7 +250,7 @@ type Options struct {
BuildTags []string
// Standard input, output and error streams.
// They default to os.Stding, os.Stdout and os.Stderr respectively.
// They default to os.Stdin, os.Stdout and os.Stderr respectively.
Stdin io.Reader
Stdout, Stderr io.Writer
}
@@ -288,6 +303,11 @@ func New(options Options) *Interpreter {
// fastChan disables the cancellable version of channel operations in evalWithContext
i.opt.fastChan, _ = strconv.ParseBool(os.Getenv("YAEGI_FAST_CHAN"))
// specialStdio allows to assign directly io.Writer and io.Reader to os.Stdxxx,
// even if they are not file descriptors.
i.opt.specialStdio, _ = strconv.ParseBool(os.Getenv("YAEGI_SPECIAL_STDIO"))
return &i
}
@@ -635,33 +655,45 @@ func (interp *Interpreter) getWrapper(t reflect.Type) reflect.Type {
// Use loads binary runtime symbols in the interpreter context so
// they can be used in interpreted code.
func (interp *Interpreter) Use(values Exports) {
func (interp *Interpreter) Use(values Exports) error {
for k, v := range values {
if k == selfPrefix {
importPath := path.Dir(k)
packageName := path.Base(k)
if importPath == "." {
return fmt.Errorf("export path %[1]q is missing a package name; did you mean '%[1]s/%[1]s'?", k)
}
if importPath == selfPrefix {
interp.hooks.Parse(v)
continue
}
if interp.binPkg[k] == nil {
interp.binPkg[k] = make(map[string]reflect.Value)
if interp.binPkg[importPath] == nil {
interp.binPkg[importPath] = make(map[string]reflect.Value)
interp.pkgNames[importPath] = packageName
}
for s, sym := range v {
interp.binPkg[k][s] = sym
interp.binPkg[importPath][s] = sym
}
if k == selfPath {
interp.binPkg[importPath]["Self"] = reflect.ValueOf(interp)
}
}
// Checks if input values correspond to stdlib packages by looking for one
// well known stdlib package path.
if _, ok := values["fmt"]; ok {
if _, ok := values["fmt/fmt"]; ok {
fixStdio(interp)
}
return nil
}
// fixStdio redefines interpreter stdlib symbols to use the standard input,
// output and errror assigned to the interpreter. The changes are limited to
// the interpreter only. Global values os.Stdin, os.Stdout and os.Stderr are
// not changed. Note that it is possible to escape the virtualized stdio by
// the interpreter only.
// Note that it is possible to escape the virtualized stdio by
// read/write directly to file descriptors 0, 1, 2.
func fixStdio(interp *Interpreter) {
p := interp.binPkg["fmt"]
@@ -708,9 +740,23 @@ func fixStdio(interp *Interpreter) {
}
if p = interp.binPkg["os"]; p != nil {
p["Stdin"] = reflect.ValueOf(&stdin).Elem()
p["Stdout"] = reflect.ValueOf(&stdout).Elem()
p["Stderr"] = reflect.ValueOf(&stderr).Elem()
if interp.specialStdio {
// Inherit streams from interpreter even if they do not have a file descriptor.
p["Stdin"] = reflect.ValueOf(&stdin).Elem()
p["Stdout"] = reflect.ValueOf(&stdout).Elem()
p["Stderr"] = reflect.ValueOf(&stderr).Elem()
} else {
// Inherits streams from interpreter only if they have a file descriptor and preserve original type.
if s, ok := stdin.(*os.File); ok {
p["Stdin"] = reflect.ValueOf(&s).Elem()
}
if s, ok := stdout.(*os.File); ok {
p["Stdout"] = reflect.ValueOf(&s).Elem()
}
if s, ok := stderr.(*os.File); ok {
p["Stderr"] = reflect.ValueOf(&s).Elem()
}
}
}
}
@@ -730,24 +776,47 @@ func ignoreScannerError(e *scanner.Error, s string) bool {
return false
}
// ImportUsed automatically imports pre-compiled packages included by Use().
// This is mainly useful for REPLs, or single command lines. In case of an ambiguous default
// package name, for example "rand" for crypto/rand and math/rand, the package name is
// constructed by replacing the last "/" by a "_", producing crypto_rand and math_rand.
// ImportUsed should not be called more than once, and not after a first Eval, as it may
// rename packages.
func (interp *Interpreter) ImportUsed() {
sc := interp.universe
for k := range interp.binPkg {
// By construction, the package name is the last path element of the key.
name := path.Base(k)
if sym, ok := sc.sym[name]; ok {
// Handle collision by renaming old and new entries.
name2 := key2name(fixKey(sym.typ.path))
sc.sym[name2] = sym
if name2 != name {
delete(sc.sym, name)
}
name = key2name(fixKey(k))
}
sc.sym[name] = &symbol{kind: pkgSym, typ: &itype{cat: binPkgT, path: k, scope: sc}}
}
}
func key2name(name string) string {
return filepath.Join(name, DefaultSourceName)
}
func fixKey(k string) string {
i := strings.LastIndex(k, "/")
if i >= 0 {
k = k[:i] + "_" + k[i+1:]
}
return k
}
// REPL performs a Read-Eval-Print-Loop on input reader.
// Results are printed to the output writer of the Interpreter, provided as option
// at creation time. Errors are printed to the similarly defined errors writer.
// The last interpreter result value and error are returned.
func (interp *Interpreter) REPL() (reflect.Value, error) {
// Preimport used bin packages, to avoid having to import these packages manually
// in REPL mode. These packages are already loaded anyway.
sc := interp.universe
for k := range interp.binPkg {
name := identifier.FindString(k)
if name == "" || name == "rand" || name == "scanner" || name == "template" || name == "pprof" {
// Skip any package with an ambiguous name (i.e crypto/rand vs math/rand).
// Those will have to be imported explicitly.
continue
}
sc.sym[name] = &symbol{kind: pkgSym, typ: &itype{cat: binPkgT, path: k, scope: sc}}
}
in, out, errs := interp.stdin, interp.stdout, interp.stderr
ctx, cancel := context.WithCancel(context.Background())
end := make(chan struct{}) // channel to terminate the REPL

View File

@@ -50,6 +50,7 @@ func TestInterpConsistencyBuild(t *testing.T) {
file.Name() == "import6.go" || // expect error
file.Name() == "init1.go" || // expect error
file.Name() == "io0.go" || // use random number
file.Name() == "issue-1093.go" || // expect error
file.Name() == "op1.go" || // expect error
file.Name() == "op7.go" || // expect error
file.Name() == "op9.go" || // expect error
@@ -114,9 +115,15 @@ func TestInterpConsistencyBuild(t *testing.T) {
os.Stdout = w
i := interp.New(interp.Options{GoPath: build.Default.GOPATH})
i.Use(stdlib.Symbols)
i.Use(interp.Symbols)
i.Use(unsafe.Symbols)
if err := i.Use(stdlib.Symbols); err != nil {
t.Fatal(err)
}
if err := i.Use(interp.Symbols); err != nil {
t.Fatal(err)
}
if err := i.Use(unsafe.Symbols); err != nil {
t.Fatal(err)
}
_, err = i.EvalPath(filePath)
if err != nil {
@@ -209,6 +216,11 @@ func TestInterpErrorConsistency(t *testing.T) {
expectedInterp: "3:17: too many arguments to return",
expectedExec: "3:17: too many arguments to return",
},
{
fileName: "issue-1093.go",
expectedInterp: "9:6: cannot use type string as type int in assignment",
expectedExec: `9:4: cannot use "a" + b() (type string) as type int in assignment`,
},
{
fileName: "op1.go",
expectedInterp: "5:2: invalid operation: mismatched types int and float64",
@@ -254,7 +266,9 @@ func TestInterpErrorConsistency(t *testing.T) {
filePath := filepath.Join("..", "_test", test.fileName)
i := interp.New(interp.Options{GoPath: build.Default.GOPATH})
i.Use(stdlib.Symbols)
if err := i.Use(stdlib.Symbols); err != nil {
t.Fatal(err)
}
_, errEval := i.EvalPath(filePath)
if errEval == nil {

View File

@@ -12,6 +12,7 @@ import (
"os"
"path/filepath"
"reflect"
"runtime"
"strconv"
"strings"
"sync"
@@ -103,11 +104,14 @@ func TestEvalStar(t *testing.T) {
func TestEvalAssign(t *testing.T) {
i := interp.New(interp.Options{})
i.Use(interp.Exports{
"testpkg": {
if err := i.Use(interp.Exports{
"testpkg/testpkg": {
"val": reflect.ValueOf(int64(11)),
},
})
}); err != nil {
t.Fatal(err)
}
_, e := i.Eval(`import "testpkg"`)
if e != nil {
t.Fatal(e)
@@ -123,6 +127,7 @@ func TestEvalAssign(t *testing.T) {
{src: "h := 1; h >>= 8", res: "0"},
{src: "i := 1; j := &i; (*j) = 2", res: "2"},
{src: "i64 := testpkg.val; i64 == 11", res: "true"},
{pre: func() { eval(t, i, "k := 1") }, src: `k := "Hello world"`, res: "Hello world"}, // allow reassignment in subsequent evaluations
})
}
@@ -139,8 +144,8 @@ func TestEvalBuiltin(t *testing.T) {
{src: `g := cap(a)`, res: "1"},
{src: `g := len("test")`, res: "4"},
{src: `g := len(map[string]string{"a": "b"})`, res: "1"},
{src: `a := len()`, err: "not enough arguments in call to len"},
{src: `a := len([]int, 0)`, err: "too many arguments for len"},
{src: `n := len()`, err: "not enough arguments in call to len"},
{src: `n := len([]int, 0)`, err: "too many arguments for len"},
{src: `g := cap("test")`, err: "1:37: invalid argument for cap"},
{src: `g := cap(map[string]string{"a": "b"})`, err: "1:37: invalid argument for cap"},
{src: `h := make(chan int, 1); close(h); len(h)`, res: "0"},
@@ -167,6 +172,10 @@ func TestEvalBuiltin(t *testing.T) {
{src: `imag("test")`, err: "1:33: cannot convert \"test\" to complex128"},
{src: `imag(a)`, err: "1:33: invalid argument type []int for imag"},
{src: `real(a)`, err: "1:33: invalid argument type []int for real"},
{src: `t := map[int]int{}; t[123]++; t`, res: "map[123:1]"},
{src: `t := map[int]int{}; t[123]--; t`, res: "map[123:-1]"},
{src: `t := map[int]int{}; t[123] += 1; t`, res: "map[123:1]"},
{src: `t := map[int]int{}; t[123] -= 1; t`, res: "map[123:-1]"},
})
}
@@ -182,6 +191,15 @@ func TestEvalDecl(t *testing.T) {
})
}
func TestEvalDeclWithExpr(t *testing.T) {
i := interp.New(interp.Options{})
runTests(t, i, []testCase{
{src: `a1 := ""; var a2 int; a2 = 2`, res: "2"},
{src: `b1 := ""; const b2 = 2; b2`, res: "2"},
{src: `c1 := ""; var c2, c3 [8]byte; c3[3]`, res: "0"},
})
}
func TestEvalFunc(t *testing.T) {
i := interp.New(interp.Options{})
runTests(t, i, []testCase{
@@ -195,7 +213,9 @@ func TestEvalFunc(t *testing.T) {
func TestEvalImport(t *testing.T) {
i := interp.New(interp.Options{})
i.Use(stdlib.Symbols)
if err := i.Use(stdlib.Symbols); err != nil {
t.Fatal(err)
}
runTests(t, i, []testCase{
{pre: func() { eval(t, i, `import "time"`) }, src: "2 * time.Second", res: "2s"},
})
@@ -204,7 +224,9 @@ func TestEvalImport(t *testing.T) {
func TestEvalStdout(t *testing.T) {
var out, err bytes.Buffer
i := interp.New(interp.Options{Stdout: &out, Stderr: &err})
i.Use(stdlib.Symbols)
if err := i.Use(stdlib.Symbols); err != nil {
t.Fatal(err)
}
_, e := i.Eval(`import "fmt"; func main() { fmt.Println("hello") }`)
if e != nil {
t.Fatal(e)
@@ -217,7 +239,9 @@ func TestEvalStdout(t *testing.T) {
func TestEvalNil(t *testing.T) {
i := interp.New(interp.Options{})
i.Use(stdlib.Symbols)
if err := i.Use(stdlib.Symbols); err != nil {
t.Fatal(err)
}
runTests(t, i, []testCase{
{desc: "assign nil", src: "a := nil", err: "1:33: use of untyped nil"},
{desc: "return nil", pre: func() { eval(t, i, "func getNil() error {return nil}") }, src: "getNil()", res: "<nil>"},
@@ -365,7 +389,9 @@ var a = T{
func TestEvalCompositeBin0(t *testing.T) {
i := interp.New(interp.Options{})
i.Use(stdlib.Symbols)
if err := i.Use(stdlib.Symbols); err != nil {
t.Fatal(err)
}
eval(t, i, `
import (
"fmt"
@@ -626,7 +652,9 @@ func TestEvalCall(t *testing.T) {
func TestEvalBinCall(t *testing.T) {
i := interp.New(interp.Options{})
i.Use(stdlib.Symbols)
if err := i.Use(stdlib.Symbols); err != nil {
t.Fatal(err)
}
if _, err := i.Eval(`import "fmt"`); err != nil {
t.Fatal(err)
}
@@ -652,9 +680,11 @@ func TestEvalMissingSymbol(t *testing.T) {
F S2
}
i := interp.New(interp.Options{})
i.Use(interp.Exports{"p": map[string]reflect.Value{
if err := i.Use(interp.Exports{"p/p": map[string]reflect.Value{
"S1": reflect.Zero(reflect.TypeOf(&S1{})),
}})
}}); err != nil {
t.Fatal(err)
}
_, err := i.Eval(`import "p"`)
if err != nil {
t.Fatalf("failed to import package: %v", err)
@@ -723,7 +753,9 @@ func TestEvalWithContext(t *testing.T) {
go func() {
defer close(done)
i := interp.New(interp.Options{})
i.Use(stdlib.Symbols)
if err := i.Use(stdlib.Symbols); err != nil {
t.Error(err)
}
_, err := i.Eval(`import "sync"`)
if err != nil {
t.Errorf(`failed to import "sync": %v`, err)
@@ -816,6 +848,7 @@ func assertEval(t *testing.T, i *interp.Interpreter, src, expectedError, expecte
}
func TestMultiEval(t *testing.T) {
t.Skip("fail in CI only ?")
// catch stdout
backupStdout := os.Stdout
defer func() {
@@ -825,8 +858,9 @@ func TestMultiEval(t *testing.T) {
os.Stdout = w
i := interp.New(interp.Options{})
i.Use(stdlib.Symbols)
var err error
if err := i.Use(stdlib.Symbols); err != nil {
t.Fatal(err)
}
f, err := os.Open(filepath.Join("testdata", "multi", "731"))
if err != nil {
@@ -862,9 +896,11 @@ func TestMultiEval(t *testing.T) {
}
func TestMultiEvalNoName(t *testing.T) {
t.Skip("fail in CI only ?")
i := interp.New(interp.Options{})
i.Use(stdlib.Symbols)
var err error
if err := i.Use(stdlib.Symbols); err != nil {
t.Fatal(err)
}
f, err := os.Open(filepath.Join("testdata", "multi", "731"))
if err != nil {
@@ -893,10 +929,90 @@ func TestMultiEvalNoName(t *testing.T) {
}
}
const goMinorVersionTest = 16
func TestHasIOFS(t *testing.T) {
code := `
// +build go1.16
package main
import (
"errors"
"io/fs"
)
func main() {
pe := fs.PathError{}
pe.Op = "nothing"
pe.Path = "/nowhere"
pe.Err = errors.New("an error")
println(pe.Error())
}
// Output:
// nothing /nowhere: an error
`
var buf bytes.Buffer
i := interp.New(interp.Options{Stdout: &buf})
if err := i.Use(interp.Symbols); err != nil {
t.Fatal(err)
}
if err := i.Use(stdlib.Symbols); err != nil {
t.Fatal(err)
}
if _, err := i.Eval(code); err != nil {
t.Fatal(err)
}
var expectedOutput string
var minor int
var err error
version := runtime.Version()
fields := strings.Fields(version)
// Go stable
if len(fields) == 1 {
v := strings.Split(version, ".")
if len(v) < 2 {
t.Fatalf("unexpected: %v", version)
}
minor, err = strconv.Atoi(v[1])
if err != nil {
t.Fatal(err)
}
} else {
// Go devel
if fields[0] != "devel" {
t.Fatalf("unexpected: %v", fields[0])
}
parts := strings.Split(fields[1], "-")
if len(parts) != 2 {
t.Fatalf("unexpected: %v", fields[1])
}
minor, err = strconv.Atoi(strings.TrimPrefix(parts[0], "go1."))
if err != nil {
t.Fatal(err)
}
}
if minor >= goMinorVersionTest {
expectedOutput = "nothing /nowhere: an error\n"
}
output := buf.String()
if buf.String() != expectedOutput {
t.Fatalf("got: %v, wanted: %v", output, expectedOutput)
}
}
func TestImportPathIsKey(t *testing.T) {
// No need to check the results of Eval, as TestFile already does it.
i := interp.New(interp.Options{GoPath: filepath.FromSlash("../_test/testdata/redeclaration-global7")})
i.Use(stdlib.Symbols)
if err := i.Use(stdlib.Symbols); err != nil {
t.Fatal(err)
}
filePath := filepath.Join("..", "_test", "ipp_as_key.go")
if _, err := i.EvalPath(filePath); err != nil {
@@ -949,9 +1065,6 @@ func TestImportPathIsKey(t *testing.T) {
}
packages := i.Packages()
if len(packages) != len(wantPackages) {
t.Fatalf("want %d, got %d", len(wantPackages), len(packages))
}
for k, v := range wantPackages {
pkg := packages[k]
if pkg != v {
@@ -978,7 +1091,9 @@ func TestConcurrentEvals(t *testing.T) {
_ = pout.Close()
}()
interpr := interp.New(interp.Options{Stdout: pout})
interpr.Use(stdlib.Symbols)
if err := interpr.Use(stdlib.Symbols); err != nil {
t.Fatal(err)
}
if _, err := interpr.EvalPath("testdata/concurrent/hello1.go"); err != nil {
t.Fatal(err)
@@ -1037,7 +1152,9 @@ func TestConcurrentEvals2(t *testing.T) {
_ = pout.Close()
}()
interpr := interp.New(interp.Options{Stdout: pout})
interpr.Use(stdlib.Symbols)
if err := interpr.Use(stdlib.Symbols); err != nil {
t.Fatal(err)
}
done := make(chan error)
go func() {
@@ -1099,7 +1216,9 @@ func TestConcurrentEvals3(t *testing.T) {
pinin, poutin := io.Pipe()
pinout, poutout := io.Pipe()
i := interp.New(interp.Options{Stdin: pinin, Stdout: poutout})
i.Use(stdlib.Symbols)
if err := i.Use(stdlib.Symbols); err != nil {
t.Fatal(err)
}
go func() {
_, _ = i.REPL()
@@ -1178,7 +1297,9 @@ func testConcurrentComposite(t *testing.T, filePath string) {
}
pin, pout := io.Pipe()
i := interp.New(interp.Options{Stdout: pout})
i.Use(stdlib.Symbols)
if err := i.Use(stdlib.Symbols); err != nil {
t.Fatal(err)
}
errc := make(chan error)
var output string
@@ -1388,11 +1509,11 @@ func applyCIMultiplier(timeout time.Duration) time.Duration {
return time.Duration(float64(timeout) * CITimeoutMultiplier)
}
func TestREPLDivision(t *testing.T) {
func TestREPLCommands(t *testing.T) {
if testing.Short() {
return
}
_ = os.Setenv("YAEGI_PROMPT", "1")
_ = os.Setenv("YAEGI_PROMPT", "1") // To force prompts over non-tty streams
defer func() {
_ = os.Setenv("YAEGI_PROMPT", "0")
}()
@@ -1402,7 +1523,9 @@ func TestREPLDivision(t *testing.T) {
pinin, poutin := io.Pipe()
pinout, poutout := io.Pipe()
i := interp.New(interp.Options{Stdin: pinin, Stdout: poutout})
i.Use(stdlib.Symbols)
if err := i.Use(stdlib.Symbols); err != nil {
t.Fatal(err)
}
go func() {
_, _ = i.REPL()
@@ -1421,12 +1544,16 @@ func TestREPLDivision(t *testing.T) {
`7/3`,
`16/5`,
`3./2`, // float
`reflect.TypeOf(math_rand.Int)`,
`reflect.TypeOf(crypto_rand.Int)`,
}
output := []string{
`1`,
`2`,
`3`,
`1.5`,
`func() int`,
`func(io.Reader, *big.Int) (*big.Int, error)`,
}
go func() {
@@ -1478,3 +1605,108 @@ func TestREPLDivision(t *testing.T) {
t.Fatal("timeout")
}
}
func TestStdio(t *testing.T) {
i := interp.New(interp.Options{})
if err := i.Use(stdlib.Symbols); err != nil {
t.Fatal(err)
}
i.ImportUsed()
if _, err := i.Eval(`var x = os.Stdout`); err != nil {
t.Fatal(err)
}
v, _ := i.Eval(`x`)
if _, ok := v.Interface().(*os.File); !ok {
t.Fatalf("%v not *os.file", v.Interface())
}
}
func TestIssue1142(t *testing.T) {
i := interp.New(interp.Options{})
runTests(t, i, []testCase{
{src: "a := 1; // foo bar", res: "1"},
})
}
type Issue1149Array [3]float32
func (v Issue1149Array) Foo() string { return "foo" }
func (v *Issue1149Array) Bar() string { return "foo" }
func TestIssue1149(t *testing.T) {
i := interp.New(interp.Options{})
if err := i.Use(interp.Exports{
"pkg/pkg": map[string]reflect.Value{
"Type": reflect.ValueOf((*Issue1149Array)(nil)),
},
}); err != nil {
t.Fatal(err)
}
i.ImportUsed()
_, err := i.Eval(`
type Type = pkg.Type
`)
if err != nil {
t.Fatal(err)
}
runTests(t, i, []testCase{
{src: "Type{1, 2, 3}.Foo()", res: "foo"},
{src: "Type{1, 2, 3}.Bar()", res: "foo"},
})
}
func TestIssue1150(t *testing.T) {
i := interp.New(interp.Options{})
_, err := i.Eval(`
type ArrayT [3]float32
type SliceT []float32
type StructT struct { A, B, C float32 }
type StructT2 struct { A, B, C float32 }
type FooerT interface { Foo() string }
func (v ArrayT) Foo() string { return "foo" }
func (v SliceT) Foo() string { return "foo" }
func (v StructT) Foo() string { return "foo" }
func (v *StructT2) Foo() string { return "foo" }
type Array = ArrayT
type Slice = SliceT
type Struct = StructT
type Struct2 = StructT2
type Fooer = FooerT
`)
if err != nil {
t.Fatal(err)
}
runTests(t, i, []testCase{
{desc: "array", src: "Array{1, 2, 3}.Foo()", res: "foo"},
{desc: "slice", src: "Slice{1, 2, 3}.Foo()", res: "foo"},
{desc: "struct", src: "Struct{1, 2, 3}.Foo()", res: "foo"},
{desc: "*struct", src: "Struct2{1, 2, 3}.Foo()", res: "foo"},
{desc: "interface", src: "v := Fooer(Array{1, 2, 3}); v.Foo()", res: "foo"},
})
}
func TestIssue1151(t *testing.T) {
type pkgStruct struct{ X int }
type pkgArray [1]int
i := interp.New(interp.Options{})
if err := i.Use(interp.Exports{
"pkg/pkg": map[string]reflect.Value{
"Struct": reflect.ValueOf((*pkgStruct)(nil)),
"Array": reflect.ValueOf((*pkgArray)(nil)),
},
}); err != nil {
t.Fatal(err)
}
i.ImportUsed()
runTests(t, i, []testCase{
{src: "x := pkg.Struct{1}", res: "{1}"},
{src: "x := pkg.Array{1}", res: "[1]"},
})
}

View File

@@ -41,14 +41,53 @@ type Wrap struct {
func (w Wrap) Hello() { w.DoHello() }
func TestExportsSemantics(t *testing.T) {
Foo := &struct{}{}
t.Run("Correct", func(t *testing.T) {
t.Skip()
i := interp.New(interp.Options{})
err := i.Use(interp.Exports{
"foo/foo": {"Foo": reflect.ValueOf(Foo)},
})
if err != nil {
t.Fatal(err)
}
i.ImportUsed()
res, err := i.Eval("foo.Foo")
if err != nil {
t.Fatal(err)
}
if res.Interface() != Foo {
t.Fatalf("expected foo.Foo to equal local Foo")
}
})
t.Run("Incorrect", func(t *testing.T) {
i := interp.New(interp.Options{})
err := i.Use(interp.Exports{
"foo": {"Foo": reflect.ValueOf(Foo)},
})
if err == nil {
t.Fatal("expected error for incorrect Use semantics")
}
})
}
func TestInterface(t *testing.T) {
i := interp.New(interp.Options{})
// export the Wrap type to the interpreter under virtual "wrap" package
i.Use(interp.Exports{
"wrap": {
err := i.Use(interp.Exports{
"wrap/wrap": {
"Wrap": reflect.ValueOf((*Wrap)(nil)),
},
})
if err != nil {
t.Fatal(err)
}
eval(t, i, `
import "wrap"
@@ -66,3 +105,29 @@ func NewMyInt(i int) wrap.Wrap {
w := NewMyInt(4)
Hi(w)
}
type T struct{}
func (t T) Bar(s ...string) {}
func TestCallBinVariadicMethod(t *testing.T) {
i := interp.New(interp.Options{})
err := i.Use(interp.Exports{
"mypkg/mypkg": {
"T": reflect.ValueOf((*T)(nil)),
},
})
if err != nil {
t.Fatal(err)
}
eval(t, i, `
package p
import "mypkg"
func Foo(x mypkg.T) { x.Bar("s") }
`)
v := eval(t, i, "p.Foo")
bar := v.Interface().(func(t T))
bar(T{})
}

View File

@@ -20,6 +20,11 @@ func TestFile(t *testing.T) {
filePath := "../_test/str.go"
runCheck(t, filePath)
defer func() {
_ = os.Setenv("YAEGI_SPECIAL_STDIO", "0")
}()
_ = os.Setenv("YAEGI_SPECIAL_STDIO", "1")
baseDir := filepath.Join("..", "_test")
files, err := ioutil.ReadDir(baseDir)
if err != nil {
@@ -50,9 +55,15 @@ func runCheck(t *testing.T, p string) {
}
var stdout, stderr bytes.Buffer
i := interp.New(interp.Options{GoPath: goPath, Stdout: &stdout, Stderr: &stderr})
i.Use(interp.Symbols)
i.Use(stdlib.Symbols)
i.Use(unsafe.Symbols)
if err := i.Use(interp.Symbols); err != nil {
t.Fatal(err)
}
if err := i.Use(stdlib.Symbols); err != nil {
t.Fatal(err)
}
if err := i.Use(unsafe.Symbols); err != nil {
t.Fatal(err)
}
_, err := i.EvalPath(p)
if errWanted {

View File

@@ -1490,6 +1490,13 @@ func addAssign(n *node) {
next := getExec(n.tnext)
typ := n.typ.TypeOf()
c0, c1 := n.child[0], n.child[1]
setMap := isMapEntry(c0)
var mapValue, indexValue func(*frame) reflect.Value
if setMap {
mapValue = genValue(c0.child[0])
indexValue = genValue(c0.child[1])
}
if c1.rval.IsValid() {
switch typ.Kind() {
@@ -1499,6 +1506,9 @@ func addAssign(n *node) {
n.exec = func(f *frame) bltn {
v, s := v0(f)
v.SetString(s + v1)
if setMap {
mapValue(f).SetMapIndex(indexValue(f), v)
}
return next
}
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
@@ -1507,6 +1517,9 @@ func addAssign(n *node) {
n.exec = func(f *frame) bltn {
v, i := v0(f)
v.SetInt(i + j)
if setMap {
mapValue(f).SetMapIndex(indexValue(f), v)
}
return next
}
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
@@ -1515,6 +1528,9 @@ func addAssign(n *node) {
n.exec = func(f *frame) bltn {
v, i := v0(f)
v.SetUint(i + j)
if setMap {
mapValue(f).SetMapIndex(indexValue(f), v)
}
return next
}
case reflect.Float32, reflect.Float64:
@@ -1523,6 +1539,9 @@ func addAssign(n *node) {
n.exec = func(f *frame) bltn {
v, i := v0(f)
v.SetFloat(i + j)
if setMap {
mapValue(f).SetMapIndex(indexValue(f), v)
}
return next
}
case reflect.Complex64, reflect.Complex128:
@@ -1531,6 +1550,9 @@ func addAssign(n *node) {
n.exec = func(f *frame) bltn {
v := v0(f)
v.SetComplex(v.Complex() + v1)
if setMap {
mapValue(f).SetMapIndex(indexValue(f), v)
}
return next
}
}
@@ -1542,6 +1564,9 @@ func addAssign(n *node) {
n.exec = func(f *frame) bltn {
v, s := v0(f)
v.SetString(s + v1(f).String())
if setMap {
mapValue(f).SetMapIndex(indexValue(f), v)
}
return next
}
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
@@ -1551,6 +1576,9 @@ func addAssign(n *node) {
v, i := v0(f)
_, j := v1(f)
v.SetInt(i + j)
if setMap {
mapValue(f).SetMapIndex(indexValue(f), v)
}
return next
}
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
@@ -1560,6 +1588,9 @@ func addAssign(n *node) {
v, i := v0(f)
_, j := v1(f)
v.SetUint(i + j)
if setMap {
mapValue(f).SetMapIndex(indexValue(f), v)
}
return next
}
case reflect.Float32, reflect.Float64:
@@ -1569,6 +1600,9 @@ func addAssign(n *node) {
v, i := v0(f)
_, j := v1(f)
v.SetFloat(i + j)
if setMap {
mapValue(f).SetMapIndex(indexValue(f), v)
}
return next
}
case reflect.Complex64, reflect.Complex128:
@@ -1577,6 +1611,9 @@ func addAssign(n *node) {
n.exec = func(f *frame) bltn {
v := v0(f)
v.SetComplex(v.Complex() + v1(f).Complex())
if setMap {
mapValue(f).SetMapIndex(indexValue(f), v)
}
return next
}
}
@@ -1587,6 +1624,13 @@ func andAssign(n *node) {
next := getExec(n.tnext)
typ := n.typ.TypeOf()
c0, c1 := n.child[0], n.child[1]
setMap := isMapEntry(c0)
var mapValue, indexValue func(*frame) reflect.Value
if setMap {
mapValue = genValue(c0.child[0])
indexValue = genValue(c0.child[1])
}
if c1.rval.IsValid() {
switch typ.Kind() {
@@ -1596,6 +1640,9 @@ func andAssign(n *node) {
n.exec = func(f *frame) bltn {
v, i := v0(f)
v.SetInt(i & j)
if setMap {
mapValue(f).SetMapIndex(indexValue(f), v)
}
return next
}
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
@@ -1604,6 +1651,9 @@ func andAssign(n *node) {
n.exec = func(f *frame) bltn {
v, i := v0(f)
v.SetUint(i & j)
if setMap {
mapValue(f).SetMapIndex(indexValue(f), v)
}
return next
}
}
@@ -1616,6 +1666,9 @@ func andAssign(n *node) {
v, i := v0(f)
_, j := v1(f)
v.SetInt(i & j)
if setMap {
mapValue(f).SetMapIndex(indexValue(f), v)
}
return next
}
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
@@ -1625,6 +1678,9 @@ func andAssign(n *node) {
v, i := v0(f)
_, j := v1(f)
v.SetUint(i & j)
if setMap {
mapValue(f).SetMapIndex(indexValue(f), v)
}
return next
}
}
@@ -1635,6 +1691,13 @@ func andNotAssign(n *node) {
next := getExec(n.tnext)
typ := n.typ.TypeOf()
c0, c1 := n.child[0], n.child[1]
setMap := isMapEntry(c0)
var mapValue, indexValue func(*frame) reflect.Value
if setMap {
mapValue = genValue(c0.child[0])
indexValue = genValue(c0.child[1])
}
if c1.rval.IsValid() {
switch typ.Kind() {
@@ -1644,6 +1707,9 @@ func andNotAssign(n *node) {
n.exec = func(f *frame) bltn {
v, i := v0(f)
v.SetInt(i &^ j)
if setMap {
mapValue(f).SetMapIndex(indexValue(f), v)
}
return next
}
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
@@ -1652,6 +1718,9 @@ func andNotAssign(n *node) {
n.exec = func(f *frame) bltn {
v, i := v0(f)
v.SetUint(i &^ j)
if setMap {
mapValue(f).SetMapIndex(indexValue(f), v)
}
return next
}
}
@@ -1664,6 +1733,9 @@ func andNotAssign(n *node) {
v, i := v0(f)
_, j := v1(f)
v.SetInt(i &^ j)
if setMap {
mapValue(f).SetMapIndex(indexValue(f), v)
}
return next
}
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
@@ -1673,6 +1745,9 @@ func andNotAssign(n *node) {
v, i := v0(f)
_, j := v1(f)
v.SetUint(i &^ j)
if setMap {
mapValue(f).SetMapIndex(indexValue(f), v)
}
return next
}
}
@@ -1683,6 +1758,13 @@ func mulAssign(n *node) {
next := getExec(n.tnext)
typ := n.typ.TypeOf()
c0, c1 := n.child[0], n.child[1]
setMap := isMapEntry(c0)
var mapValue, indexValue func(*frame) reflect.Value
if setMap {
mapValue = genValue(c0.child[0])
indexValue = genValue(c0.child[1])
}
if c1.rval.IsValid() {
switch typ.Kind() {
@@ -1692,6 +1774,9 @@ func mulAssign(n *node) {
n.exec = func(f *frame) bltn {
v, i := v0(f)
v.SetInt(i * j)
if setMap {
mapValue(f).SetMapIndex(indexValue(f), v)
}
return next
}
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
@@ -1700,6 +1785,9 @@ func mulAssign(n *node) {
n.exec = func(f *frame) bltn {
v, i := v0(f)
v.SetUint(i * j)
if setMap {
mapValue(f).SetMapIndex(indexValue(f), v)
}
return next
}
case reflect.Float32, reflect.Float64:
@@ -1708,6 +1796,9 @@ func mulAssign(n *node) {
n.exec = func(f *frame) bltn {
v, i := v0(f)
v.SetFloat(i * j)
if setMap {
mapValue(f).SetMapIndex(indexValue(f), v)
}
return next
}
case reflect.Complex64, reflect.Complex128:
@@ -1716,6 +1807,9 @@ func mulAssign(n *node) {
n.exec = func(f *frame) bltn {
v := v0(f)
v.SetComplex(v.Complex() * v1)
if setMap {
mapValue(f).SetMapIndex(indexValue(f), v)
}
return next
}
}
@@ -1728,6 +1822,9 @@ func mulAssign(n *node) {
v, i := v0(f)
_, j := v1(f)
v.SetInt(i * j)
if setMap {
mapValue(f).SetMapIndex(indexValue(f), v)
}
return next
}
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
@@ -1737,6 +1834,9 @@ func mulAssign(n *node) {
v, i := v0(f)
_, j := v1(f)
v.SetUint(i * j)
if setMap {
mapValue(f).SetMapIndex(indexValue(f), v)
}
return next
}
case reflect.Float32, reflect.Float64:
@@ -1746,6 +1846,9 @@ func mulAssign(n *node) {
v, i := v0(f)
_, j := v1(f)
v.SetFloat(i * j)
if setMap {
mapValue(f).SetMapIndex(indexValue(f), v)
}
return next
}
case reflect.Complex64, reflect.Complex128:
@@ -1754,6 +1857,9 @@ func mulAssign(n *node) {
n.exec = func(f *frame) bltn {
v := v0(f)
v.SetComplex(v.Complex() * v1(f).Complex())
if setMap {
mapValue(f).SetMapIndex(indexValue(f), v)
}
return next
}
}
@@ -1764,6 +1870,13 @@ func orAssign(n *node) {
next := getExec(n.tnext)
typ := n.typ.TypeOf()
c0, c1 := n.child[0], n.child[1]
setMap := isMapEntry(c0)
var mapValue, indexValue func(*frame) reflect.Value
if setMap {
mapValue = genValue(c0.child[0])
indexValue = genValue(c0.child[1])
}
if c1.rval.IsValid() {
switch typ.Kind() {
@@ -1773,6 +1886,9 @@ func orAssign(n *node) {
n.exec = func(f *frame) bltn {
v, i := v0(f)
v.SetInt(i | j)
if setMap {
mapValue(f).SetMapIndex(indexValue(f), v)
}
return next
}
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
@@ -1781,6 +1897,9 @@ func orAssign(n *node) {
n.exec = func(f *frame) bltn {
v, i := v0(f)
v.SetUint(i | j)
if setMap {
mapValue(f).SetMapIndex(indexValue(f), v)
}
return next
}
}
@@ -1793,6 +1912,9 @@ func orAssign(n *node) {
v, i := v0(f)
_, j := v1(f)
v.SetInt(i | j)
if setMap {
mapValue(f).SetMapIndex(indexValue(f), v)
}
return next
}
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
@@ -1802,6 +1924,9 @@ func orAssign(n *node) {
v, i := v0(f)
_, j := v1(f)
v.SetUint(i | j)
if setMap {
mapValue(f).SetMapIndex(indexValue(f), v)
}
return next
}
}
@@ -1812,6 +1937,13 @@ func quoAssign(n *node) {
next := getExec(n.tnext)
typ := n.typ.TypeOf()
c0, c1 := n.child[0], n.child[1]
setMap := isMapEntry(c0)
var mapValue, indexValue func(*frame) reflect.Value
if setMap {
mapValue = genValue(c0.child[0])
indexValue = genValue(c0.child[1])
}
if c1.rval.IsValid() {
switch typ.Kind() {
@@ -1821,6 +1953,9 @@ func quoAssign(n *node) {
n.exec = func(f *frame) bltn {
v, i := v0(f)
v.SetInt(i / j)
if setMap {
mapValue(f).SetMapIndex(indexValue(f), v)
}
return next
}
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
@@ -1829,6 +1964,9 @@ func quoAssign(n *node) {
n.exec = func(f *frame) bltn {
v, i := v0(f)
v.SetUint(i / j)
if setMap {
mapValue(f).SetMapIndex(indexValue(f), v)
}
return next
}
case reflect.Float32, reflect.Float64:
@@ -1837,6 +1975,9 @@ func quoAssign(n *node) {
n.exec = func(f *frame) bltn {
v, i := v0(f)
v.SetFloat(i / j)
if setMap {
mapValue(f).SetMapIndex(indexValue(f), v)
}
return next
}
case reflect.Complex64, reflect.Complex128:
@@ -1845,6 +1986,9 @@ func quoAssign(n *node) {
n.exec = func(f *frame) bltn {
v := v0(f)
v.SetComplex(v.Complex() / v1)
if setMap {
mapValue(f).SetMapIndex(indexValue(f), v)
}
return next
}
}
@@ -1857,6 +2001,9 @@ func quoAssign(n *node) {
v, i := v0(f)
_, j := v1(f)
v.SetInt(i / j)
if setMap {
mapValue(f).SetMapIndex(indexValue(f), v)
}
return next
}
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
@@ -1866,6 +2013,9 @@ func quoAssign(n *node) {
v, i := v0(f)
_, j := v1(f)
v.SetUint(i / j)
if setMap {
mapValue(f).SetMapIndex(indexValue(f), v)
}
return next
}
case reflect.Float32, reflect.Float64:
@@ -1875,6 +2025,9 @@ func quoAssign(n *node) {
v, i := v0(f)
_, j := v1(f)
v.SetFloat(i / j)
if setMap {
mapValue(f).SetMapIndex(indexValue(f), v)
}
return next
}
case reflect.Complex64, reflect.Complex128:
@@ -1883,6 +2036,9 @@ func quoAssign(n *node) {
n.exec = func(f *frame) bltn {
v := v0(f)
v.SetComplex(v.Complex() / v1(f).Complex())
if setMap {
mapValue(f).SetMapIndex(indexValue(f), v)
}
return next
}
}
@@ -1893,6 +2049,13 @@ func remAssign(n *node) {
next := getExec(n.tnext)
typ := n.typ.TypeOf()
c0, c1 := n.child[0], n.child[1]
setMap := isMapEntry(c0)
var mapValue, indexValue func(*frame) reflect.Value
if setMap {
mapValue = genValue(c0.child[0])
indexValue = genValue(c0.child[1])
}
if c1.rval.IsValid() {
switch typ.Kind() {
@@ -1902,6 +2065,9 @@ func remAssign(n *node) {
n.exec = func(f *frame) bltn {
v, i := v0(f)
v.SetInt(i % j)
if setMap {
mapValue(f).SetMapIndex(indexValue(f), v)
}
return next
}
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
@@ -1910,6 +2076,9 @@ func remAssign(n *node) {
n.exec = func(f *frame) bltn {
v, i := v0(f)
v.SetUint(i % j)
if setMap {
mapValue(f).SetMapIndex(indexValue(f), v)
}
return next
}
}
@@ -1922,6 +2091,9 @@ func remAssign(n *node) {
v, i := v0(f)
_, j := v1(f)
v.SetInt(i % j)
if setMap {
mapValue(f).SetMapIndex(indexValue(f), v)
}
return next
}
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
@@ -1931,6 +2103,9 @@ func remAssign(n *node) {
v, i := v0(f)
_, j := v1(f)
v.SetUint(i % j)
if setMap {
mapValue(f).SetMapIndex(indexValue(f), v)
}
return next
}
}
@@ -1941,6 +2116,13 @@ func shlAssign(n *node) {
next := getExec(n.tnext)
typ := n.typ.TypeOf()
c0, c1 := n.child[0], n.child[1]
setMap := isMapEntry(c0)
var mapValue, indexValue func(*frame) reflect.Value
if setMap {
mapValue = genValue(c0.child[0])
indexValue = genValue(c0.child[1])
}
if c1.rval.IsValid() {
switch typ.Kind() {
@@ -1950,6 +2132,9 @@ func shlAssign(n *node) {
n.exec = func(f *frame) bltn {
v, i := v0(f)
v.SetInt(i << j)
if setMap {
mapValue(f).SetMapIndex(indexValue(f), v)
}
return next
}
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
@@ -1958,6 +2143,9 @@ func shlAssign(n *node) {
n.exec = func(f *frame) bltn {
v, i := v0(f)
v.SetUint(i << j)
if setMap {
mapValue(f).SetMapIndex(indexValue(f), v)
}
return next
}
}
@@ -1970,6 +2158,9 @@ func shlAssign(n *node) {
v, i := v0(f)
_, j := v1(f)
v.SetInt(i << j)
if setMap {
mapValue(f).SetMapIndex(indexValue(f), v)
}
return next
}
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
@@ -1979,6 +2170,9 @@ func shlAssign(n *node) {
v, i := v0(f)
_, j := v1(f)
v.SetUint(i << j)
if setMap {
mapValue(f).SetMapIndex(indexValue(f), v)
}
return next
}
}
@@ -1989,6 +2183,13 @@ func shrAssign(n *node) {
next := getExec(n.tnext)
typ := n.typ.TypeOf()
c0, c1 := n.child[0], n.child[1]
setMap := isMapEntry(c0)
var mapValue, indexValue func(*frame) reflect.Value
if setMap {
mapValue = genValue(c0.child[0])
indexValue = genValue(c0.child[1])
}
if c1.rval.IsValid() {
switch typ.Kind() {
@@ -1998,6 +2199,9 @@ func shrAssign(n *node) {
n.exec = func(f *frame) bltn {
v, i := v0(f)
v.SetInt(i >> j)
if setMap {
mapValue(f).SetMapIndex(indexValue(f), v)
}
return next
}
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
@@ -2006,6 +2210,9 @@ func shrAssign(n *node) {
n.exec = func(f *frame) bltn {
v, i := v0(f)
v.SetUint(i >> j)
if setMap {
mapValue(f).SetMapIndex(indexValue(f), v)
}
return next
}
}
@@ -2018,6 +2225,9 @@ func shrAssign(n *node) {
v, i := v0(f)
_, j := v1(f)
v.SetInt(i >> j)
if setMap {
mapValue(f).SetMapIndex(indexValue(f), v)
}
return next
}
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
@@ -2027,6 +2237,9 @@ func shrAssign(n *node) {
v, i := v0(f)
_, j := v1(f)
v.SetUint(i >> j)
if setMap {
mapValue(f).SetMapIndex(indexValue(f), v)
}
return next
}
}
@@ -2037,6 +2250,13 @@ func subAssign(n *node) {
next := getExec(n.tnext)
typ := n.typ.TypeOf()
c0, c1 := n.child[0], n.child[1]
setMap := isMapEntry(c0)
var mapValue, indexValue func(*frame) reflect.Value
if setMap {
mapValue = genValue(c0.child[0])
indexValue = genValue(c0.child[1])
}
if c1.rval.IsValid() {
switch typ.Kind() {
@@ -2046,6 +2266,9 @@ func subAssign(n *node) {
n.exec = func(f *frame) bltn {
v, i := v0(f)
v.SetInt(i - j)
if setMap {
mapValue(f).SetMapIndex(indexValue(f), v)
}
return next
}
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
@@ -2054,6 +2277,9 @@ func subAssign(n *node) {
n.exec = func(f *frame) bltn {
v, i := v0(f)
v.SetUint(i - j)
if setMap {
mapValue(f).SetMapIndex(indexValue(f), v)
}
return next
}
case reflect.Float32, reflect.Float64:
@@ -2062,6 +2288,9 @@ func subAssign(n *node) {
n.exec = func(f *frame) bltn {
v, i := v0(f)
v.SetFloat(i - j)
if setMap {
mapValue(f).SetMapIndex(indexValue(f), v)
}
return next
}
case reflect.Complex64, reflect.Complex128:
@@ -2070,6 +2299,9 @@ func subAssign(n *node) {
n.exec = func(f *frame) bltn {
v := v0(f)
v.SetComplex(v.Complex() - v1)
if setMap {
mapValue(f).SetMapIndex(indexValue(f), v)
}
return next
}
}
@@ -2082,6 +2314,9 @@ func subAssign(n *node) {
v, i := v0(f)
_, j := v1(f)
v.SetInt(i - j)
if setMap {
mapValue(f).SetMapIndex(indexValue(f), v)
}
return next
}
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
@@ -2091,6 +2326,9 @@ func subAssign(n *node) {
v, i := v0(f)
_, j := v1(f)
v.SetUint(i - j)
if setMap {
mapValue(f).SetMapIndex(indexValue(f), v)
}
return next
}
case reflect.Float32, reflect.Float64:
@@ -2100,6 +2338,9 @@ func subAssign(n *node) {
v, i := v0(f)
_, j := v1(f)
v.SetFloat(i - j)
if setMap {
mapValue(f).SetMapIndex(indexValue(f), v)
}
return next
}
case reflect.Complex64, reflect.Complex128:
@@ -2108,6 +2349,9 @@ func subAssign(n *node) {
n.exec = func(f *frame) bltn {
v := v0(f)
v.SetComplex(v.Complex() - v1(f).Complex())
if setMap {
mapValue(f).SetMapIndex(indexValue(f), v)
}
return next
}
}
@@ -2118,6 +2362,13 @@ func xorAssign(n *node) {
next := getExec(n.tnext)
typ := n.typ.TypeOf()
c0, c1 := n.child[0], n.child[1]
setMap := isMapEntry(c0)
var mapValue, indexValue func(*frame) reflect.Value
if setMap {
mapValue = genValue(c0.child[0])
indexValue = genValue(c0.child[1])
}
if c1.rval.IsValid() {
switch typ.Kind() {
@@ -2127,6 +2378,9 @@ func xorAssign(n *node) {
n.exec = func(f *frame) bltn {
v, i := v0(f)
v.SetInt(i ^ j)
if setMap {
mapValue(f).SetMapIndex(indexValue(f), v)
}
return next
}
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
@@ -2135,6 +2389,9 @@ func xorAssign(n *node) {
n.exec = func(f *frame) bltn {
v, i := v0(f)
v.SetUint(i ^ j)
if setMap {
mapValue(f).SetMapIndex(indexValue(f), v)
}
return next
}
}
@@ -2147,6 +2404,9 @@ func xorAssign(n *node) {
v, i := v0(f)
_, j := v1(f)
v.SetInt(i ^ j)
if setMap {
mapValue(f).SetMapIndex(indexValue(f), v)
}
return next
}
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
@@ -2156,6 +2416,9 @@ func xorAssign(n *node) {
v, i := v0(f)
_, j := v1(f)
v.SetUint(i ^ j)
if setMap {
mapValue(f).SetMapIndex(indexValue(f), v)
}
return next
}
}
@@ -2165,34 +2428,54 @@ func xorAssign(n *node) {
func dec(n *node) {
next := getExec(n.tnext)
typ := n.typ.TypeOf()
c0 := n.child[0]
setMap := isMapEntry(c0)
var mapValue, indexValue func(*frame) reflect.Value
if setMap {
mapValue = genValue(c0.child[0])
indexValue = genValue(c0.child[1])
}
switch typ.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
v0 := genValueInt(n.child[0])
v0 := genValueInt(c0)
n.exec = func(f *frame) bltn {
v, i := v0(f)
v.SetInt(i - 1)
if setMap {
mapValue(f).SetMapIndex(indexValue(f), v)
}
return next
}
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
v0 := genValueUint(n.child[0])
v0 := genValueUint(c0)
n.exec = func(f *frame) bltn {
v, i := v0(f)
v.SetUint(i - 1)
if setMap {
mapValue(f).SetMapIndex(indexValue(f), v)
}
return next
}
case reflect.Float32, reflect.Float64:
v0 := genValueFloat(n.child[0])
v0 := genValueFloat(c0)
n.exec = func(f *frame) bltn {
v, i := v0(f)
v.SetFloat(i - 1)
if setMap {
mapValue(f).SetMapIndex(indexValue(f), v)
}
return next
}
case reflect.Complex64, reflect.Complex128:
v0 := genValue(n.child[0])
v0 := genValue(c0)
n.exec = func(f *frame) bltn {
v := v0(f)
v.SetComplex(v.Complex() - 1)
if setMap {
mapValue(f).SetMapIndex(indexValue(f), v)
}
return next
}
}
@@ -2201,34 +2484,54 @@ func dec(n *node) {
func inc(n *node) {
next := getExec(n.tnext)
typ := n.typ.TypeOf()
c0 := n.child[0]
setMap := isMapEntry(c0)
var mapValue, indexValue func(*frame) reflect.Value
if setMap {
mapValue = genValue(c0.child[0])
indexValue = genValue(c0.child[1])
}
switch typ.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
v0 := genValueInt(n.child[0])
v0 := genValueInt(c0)
n.exec = func(f *frame) bltn {
v, i := v0(f)
v.SetInt(i + 1)
if setMap {
mapValue(f).SetMapIndex(indexValue(f), v)
}
return next
}
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
v0 := genValueUint(n.child[0])
v0 := genValueUint(c0)
n.exec = func(f *frame) bltn {
v, i := v0(f)
v.SetUint(i + 1)
if setMap {
mapValue(f).SetMapIndex(indexValue(f), v)
}
return next
}
case reflect.Float32, reflect.Float64:
v0 := genValueFloat(n.child[0])
v0 := genValueFloat(c0)
n.exec = func(f *frame) bltn {
v, i := v0(f)
v.SetFloat(i + 1)
if setMap {
mapValue(f).SetMapIndex(indexValue(f), v)
}
return next
}
case reflect.Complex64, reflect.Complex128:
v0 := genValue(n.child[0])
v0 := genValue(c0)
n.exec = func(f *frame) bltn {
v := v0(f)
v.SetComplex(v.Complex() + 1)
if setMap {
mapValue(f).SetMapIndex(indexValue(f), v)
}
return next
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,49 @@
package interp_test
import (
"log"
"github.com/traefik/yaegi/interp"
"github.com/traefik/yaegi/stdlib"
)
func ExampleInterpreter_self() {
i := interp.New(interp.Options{})
if err := i.Use(stdlib.Symbols); err != nil {
log.Fatal(err)
}
if err := i.Use(interp.Symbols); err != nil {
log.Fatal(err)
}
_, err := i.Eval(`import (
"fmt"
"log"
// Import interp to gain access to Self.
"github.com/traefik/yaegi/interp"
)`)
if err != nil {
log.Fatal(err)
}
_, err = i.Eval(`
// Evaluate code directly.
fmt.Println("Hello Yaegi from Go")
// Evaluate code indirectly via the Self access point.
_, err := interp.Self.Eval("fmt.Println(\"Hello Yaegi from Yaegi\")")
if err != nil {
log.Fatal(err)
}
`)
if err != nil {
log.Fatal(err)
}
// Output:
//
// Hello Yaegi from Go
// Hello Yaegi from Yaegi
}

View File

@@ -303,7 +303,7 @@ func effectivePkg(root, path string) string {
}
// isPathRelative returns true if path starts with "./" or "../".
// It is intended for use on import paths, where "/" is always the directory separator.
func isPathRelative(s string) bool {
p := "." + string(filepath.Separator)
return strings.HasPrefix(s, p) || strings.HasPrefix(s, "."+p)
return strings.HasPrefix(s, "./") || strings.HasPrefix(s, "../")
}

View File

@@ -38,6 +38,7 @@ const (
int64T
mapT
ptrT
sliceT
srcPkgT
stringT
structT
@@ -75,6 +76,7 @@ var cats = [...]string{
int64T: "int64T",
mapT: "mapT",
ptrT: "ptrT",
sliceT: "sliceT",
srcPkgT: "srcPkgT",
stringT: "stringT",
structT: "structT",
@@ -109,19 +111,18 @@ type itype struct {
cat tcat // Type category
field []structField // Array of struct fields if structT or interfaceT
key *itype // Type of key element if MapT or nil
val *itype // Type of value element if chanT, chanSendT, chanRecvT, mapT, ptrT, aliasT, arrayT or variadicT
val *itype // Type of value element if chanT, chanSendT, chanRecvT, mapT, ptrT, aliasT, arrayT, sliceT or variadicT
recv *itype // Receiver type for funcT or nil
arg []*itype // Argument types if funcT or nil
ret []*itype // Return types if funcT or nil
method []*node // Associated methods or nil
name string // name of type within its package for a defined type
path string // for a defined type, the package import path
size int // Size of array if ArrayT
length int // length of array if ArrayT
rtype reflect.Type // Reflection type if ValueT, or nil
incomplete bool // true if type must be parsed again (out of order declarations)
recursive bool // true if the type has an element which refer to itself
untyped bool // true for a literal value (string or number)
sizedef bool // true if array size is computed from type definition
isBinMethod bool // true if the type refers to a bin method function
node *node // root AST node of type definition
scope *scope // type declaration scope (in case of re-parse incomplete type)
@@ -134,12 +135,13 @@ func untypedInt() *itype { return &itype{cat: intT, name: "int", untyped: tr
func untypedFloat() *itype { return &itype{cat: float64T, name: "float64", untyped: true} }
func untypedComplex() *itype { return &itype{cat: complex128T, name: "complex128", untyped: true} }
func errorMethodType(sc *scope) *itype {
return &itype{cat: funcT, ret: []*itype{sc.getType("string")}}
}
// nodeType returns a type definition for the corresponding AST subtree.
func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
if n.typ != nil && !n.typ.incomplete {
if n.kind == sliceExpr {
n.typ.sizedef = false
}
return n.typ, nil
}
@@ -165,10 +167,10 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
t.incomplete = t.val.incomplete
case arrayType:
t.cat = arrayT
c0 := n.child[0]
if len(n.child) == 1 {
// Array size is not defined.
t.cat = sliceT
if t.val, err = nodeType(interp, sc, c0); err != nil {
return nil, err
}
@@ -176,18 +178,19 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
break
}
// Array size is defined.
t.cat = arrayT
switch v := c0.rval; {
case v.IsValid():
// Size if defined by a constant litteral value.
if isConstantValue(v.Type()) {
c := v.Interface().(constant.Value)
t.size = constToInt(c)
t.length = constToInt(c)
} else {
t.size = int(v.Int())
t.length = int(v.Int())
}
case c0.kind == ellipsisExpr:
// [...]T expression, get size from the length of composite array.
t.size = arrayTypeLen(n.anc)
t.length = arrayTypeLen(n.anc)
case c0.kind == identExpr:
sym, _, ok := sc.lookup(c0.ident)
if !ok {
@@ -198,19 +201,11 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
if sym.kind != constSym {
return nil, c0.cfgErrorf("non-constant array bound %q", c0.ident)
}
if sym.typ == nil || sym.typ.cat != intT || !sym.rval.IsValid() {
if sym.typ == nil || !isInt(sym.typ.TypeOf()) || !sym.rval.IsValid() {
t.incomplete = true
break
}
if v, ok := sym.rval.Interface().(int); ok {
t.size = v
break
}
if c, ok := sym.rval.Interface().(constant.Value); ok {
t.size = constToInt(c)
break
}
t.incomplete = true
t.length = int(vInt(sym.rval))
default:
// Size is defined by a numeric constant expression.
if _, err = interp.cfg(c0, sc.pkgID); err != nil {
@@ -221,12 +216,11 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
t.incomplete = true
break
}
t.size = constToInt(v)
t.length = constToInt(v)
}
if t.val, err = nodeType(interp, sc, n.child[1]); err != nil {
return nil, err
}
t.sizedef = true
t.incomplete = t.incomplete || t.val.incomplete
case basicLit:
@@ -275,47 +269,33 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
}
}
// Because an empty interface concrete type "mutates" as different values are
// assigned to it, we need to make a new itype from scratch everytime a new
// assignment is made, and not let different nodes (of the same variable) share the
// same itype. Otherwise they would overwrite each other.
if n.anc.kind == assignStmt && isInterface(n.anc.child[0].typ) && len(n.anc.child[0].typ.field) == 0 {
// TODO(mpl): do the indexes properly for multiple assignments on the same line.
// Also, maybe we should use nodeType to figure out dt.cat? but isn't it always
// gonna be an interfaceT anyway?
dt := new(itype)
dt.cat = interfaceT
val := new(itype)
val.cat = t.cat
dt.val = val
// TODO(mpl): do the indexes properly for multiple assignments on the same line.
// Also, maybe we should use nodeType to figure out dt.cat? but isn't it always
// gonna be an interfaceT anyway?
n.anc.child[0].typ = dt
// TODO(mpl): not sure yet whether we should do that last step. It doesn't seem
// to change anything either way though.
// t = dt
break
}
// If the node is to be assigned or returned, the node type is the destination type.
dt := t
switch a := n.anc; {
case a.kind == assignStmt && isEmptyInterface(a.child[0].typ):
// Because an empty interface concrete type "mutates" as different values are
// assigned to it, we need to make a new itype from scratch everytime a new
// assignment is made, and not let different nodes (of the same variable) share the
// same itype. Otherwise they would overwrite each other.
a.child[0].typ = &itype{cat: interfaceT, val: dt}
case a.kind == defineStmt && len(a.child) > a.nleft+a.nright:
if dt, err = nodeType(interp, sc, a.child[a.nleft]); err != nil {
return nil, err
}
case a.kind == returnStmt:
dt = sc.def.typ.ret[childPos(n)]
}
if isInterface(dt) {
dt.val = t
}
t = dt
case callExpr:
if interp.isBuiltinCall(n) {
if isBuiltinCall(n, sc) {
// Builtin types are special and may depend from their input arguments.
t.cat = builtinT
switch n.child[0].ident {
@@ -499,7 +479,7 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
break
}
switch lt.cat {
case arrayT, mapT:
case arrayT, mapT, sliceT, variadicT:
t = lt.val
}
@@ -512,21 +492,28 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
}
}
for _, field := range n.child[0].child {
f0 := field.child[0]
if len(field.child) == 1 {
typ, err := nodeType(interp, sc, field.child[0])
if f0.ident == "error" {
// Unwrap error interface inplace rather than embedding it, because
// "error" is lower case which may cause problems with reflect for method lookup.
t.field = append(t.field, structField{name: "Error", typ: errorMethodType(sc)})
continue
}
typ, err := nodeType(interp, sc, f0)
if err != nil {
return nil, err
}
t.field = append(t.field, structField{name: fieldName(field.child[0]), embed: true, typ: typ})
incomplete = incomplete || typ.incomplete
} else {
typ, err := nodeType(interp, sc, field.child[1])
if err != nil {
return nil, err
}
t.field = append(t.field, structField{name: field.child[0].ident, typ: typ})
t.field = append(t.field, structField{name: fieldName(f0), embed: true, typ: typ})
incomplete = incomplete || typ.incomplete
continue
}
typ, err := nodeType(interp, sc, field.child[1])
if err != nil {
return nil, err
}
t.field = append(t.field, structField{name: f0.ident, typ: typ})
incomplete = incomplete || typ.incomplete
}
t.incomplete = incomplete
@@ -615,15 +602,14 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
case sliceExpr:
t, err = nodeType(interp, sc, n.child[0])
if err != nil {
return nil, err
}
if t.cat == ptrT {
t = t.val
}
if err == nil && t.size != 0 {
t1 := *t
t1.size = 0
t1.sizedef = false
t1.rtype = nil
t = &t1
if t.cat == arrayT {
t = &itype{cat: sliceT, val: t.val, incomplete: t.incomplete, node: n, scope: sc}
}
case structType:
@@ -681,11 +667,16 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
return t, err
}
func (interp *Interpreter) isBuiltinCall(n *node) bool {
func isBuiltinCall(n *node, sc *scope) bool {
if n.kind != callExpr {
return false
}
s := interp.universe.sym[n.child[0].ident]
s := n.child[0].sym
if s == nil {
if sym, _, found := sc.lookup(n.child[0].ident); found {
s = sym
}
}
return s != nil && s.kind == bltnSym
}
@@ -775,7 +766,7 @@ func (t *itype) referTo(name string, seen map[*itype]bool) bool {
}
seen[t] = true
switch t.cat {
case aliasT, arrayT, chanT, chanRecvT, chanSendT, ptrT:
case aliasT, arrayT, chanT, chanRecvT, chanSendT, ptrT, sliceT, variadicT:
return t.val.referTo(name, seen)
case funcT:
for _, a := range t.arg {
@@ -883,6 +874,10 @@ func (t *itype) isRecursive() bool {
return false
}
func (t *itype) isIndirectRecursive() bool {
return t.isRecursive() || t.val != nil && t.val.isIndirectRecursive()
}
// isVariadic returns true if the function type is variadic.
// If the type is not a function or is not variadic, it will
// return false.
@@ -919,7 +914,7 @@ func isComplete(t *itype, visited map[string]bool) bool {
return true
}
fallthrough
case arrayT, chanT, chanRecvT, chanSendT, ptrT:
case arrayT, chanT, chanRecvT, chanSendT, ptrT, sliceT, variadicT:
return isComplete(t.val, visited)
case funcT:
complete := true
@@ -957,7 +952,7 @@ func (t *itype) assignableTo(o *itype) bool {
return true
}
if t.cat == aliasT && o.cat == aliasT {
// if alias types are not identical, it is not assignable.
// If alias types are not identical, it is not assignable.
return false
}
if t.isNil() && o.hasNil() || o.isNil() && t.hasNil() {
@@ -968,6 +963,15 @@ func (t *itype) assignableTo(o *itype) bool {
return true
}
if isInterface(o) && t.implements(o) {
return true
}
if t.isBinMethod && isFunc(o) {
// TODO (marc): check that t without receiver as first parameter is equivalent to o.
return true
}
n := t.node
if n == nil || !n.rval.IsValid() {
return false
@@ -1053,6 +1057,10 @@ func (t *itype) methods() methodSet {
seen[typ] = true
switch typ.cat {
case aliasT:
for k, v := range getMethods(typ.val) {
res[k] = v
}
case interfaceT:
// Get methods from recursive analysis of interface fields.
for _, f := range typ.field {
@@ -1066,7 +1074,7 @@ func (t *itype) methods() methodSet {
}
case valueT, errorT:
// Get method from corresponding reflect.Type.
for i := typ.rtype.NumMethod() - 1; i >= 0; i-- {
for i := typ.TypeOf().NumMethod() - 1; i >= 0; i-- {
m := typ.rtype.Method(i)
res[m.Name] = m.Type.String()
}
@@ -1114,11 +1122,7 @@ func (t *itype) id() (res string) {
case nilT:
res = "nil"
case arrayT:
if t.size == 0 {
res = "[]" + t.val.id()
} else {
res = "[" + strconv.Itoa(t.size) + "]" + t.val.id()
}
res = "[" + strconv.Itoa(t.length) + "]" + t.val.id()
case chanT:
res = "chan " + t.val.id()
case chanSendT:
@@ -1151,6 +1155,8 @@ func (t *itype) id() (res string) {
res = "map[" + t.key.id() + "]" + t.val.id()
case ptrT:
res = "*" + t.val.id()
case sliceT:
res = "[]" + t.val.id()
case structT:
res = "struct{"
for _, t := range t.field {
@@ -1158,15 +1164,47 @@ func (t *itype) id() (res string) {
}
res += "}"
case valueT:
if isConstantValue(t.rtype) {
res = fixPossibleConstType(t.rtype).String()
break
}
res = ""
if t.rtype.PkgPath() != "" {
res += t.rtype.PkgPath() + "."
}
res += t.rtype.Name()
case variadicT:
res = "..." + t.val.id()
}
if res == "" {
res = t.TypeOf().String()
}
return res
}
// fixPossibleConstType returns the input type if it not a constant value,
// otherwise, it returns the default Go type corresponding to the
// constant.Value.
func fixPossibleConstType(t reflect.Type) (r reflect.Type) {
cv, ok := reflect.New(t).Elem().Interface().(constant.Value)
if !ok {
return t
}
switch cv.Kind() {
case constant.Bool:
r = reflect.TypeOf(true)
case constant.Int:
r = reflect.TypeOf(0)
case constant.String:
r = reflect.TypeOf("")
case constant.Float:
r = reflect.TypeOf(float64(0))
case constant.Complex:
r = reflect.TypeOf(complex128(0))
}
return r
}
// zero instantiates and return a zero value object for the given type during execution.
func (t *itype) zero() (v reflect.Value, err error) {
if t, err = t.finalize(); err != nil {
@@ -1176,7 +1214,7 @@ func (t *itype) zero() (v reflect.Value, err error) {
case aliasT:
v, err = t.val.zero()
case arrayT, ptrT, structT:
case arrayT, ptrT, structT, sliceT:
v = reflect.New(t.frameType()).Elem()
case valueT:
@@ -1216,24 +1254,36 @@ func (t *itype) fieldSeq(seq []int) *itype {
// lookupField returns a list of indices, i.e. a path to access a field in a struct object.
func (t *itype) lookupField(name string) []int {
switch t.cat {
case aliasT, ptrT:
return t.val.lookupField(name)
}
if fi := t.fieldIndex(name); fi >= 0 {
return []int{fi}
}
seen := map[*itype]bool{}
var lookup func(*itype) []int
for i, f := range t.field {
switch f.typ.cat {
case ptrT, structT, interfaceT, aliasT:
if index2 := f.typ.lookupField(name); len(index2) > 0 {
return append([]int{i}, index2...)
lookup = func(typ *itype) []int {
if seen[typ] {
return nil
}
seen[typ] = true
switch typ.cat {
case aliasT, ptrT:
return lookup(typ.val)
}
if fi := typ.fieldIndex(name); fi >= 0 {
return []int{fi}
}
for i, f := range typ.field {
switch f.typ.cat {
case ptrT, structT, interfaceT, aliasT:
if index2 := lookup(f.typ); len(index2) > 0 {
return append([]int{i}, index2...)
}
}
}
return nil
}
return nil
return lookup(t)
}
// lookupBinField returns a structfield and a path to access an embedded binary field in a struct object.
@@ -1244,10 +1294,13 @@ func (t *itype) lookupBinField(name string) (s reflect.StructField, index []int,
if !isStruct(t) {
return
}
rt := t.rtype
if t.cat == valueT && rt.Kind() == reflect.Ptr {
rt := t.TypeOf()
for t.cat == valueT && rt.Kind() == reflect.Ptr {
rt = rt.Elem()
}
if rt.Kind() != reflect.Struct {
return
}
s, ok = rt.FieldByName(name)
if !ok {
for i, f := range t.field {
@@ -1278,6 +1331,13 @@ func (t *itype) methodCallType() reflect.Type {
return reflect.FuncOf(it, ot, t.rtype.IsVariadic())
}
func (t *itype) resolveAlias() *itype {
for t.cat == aliasT {
t = t.val
}
return t
}
// GetMethod returns a pointer to the method definition.
func (t *itype) getMethod(name string) *node {
for _, m := range t.method {
@@ -1305,10 +1365,24 @@ func (t *itype) lookupMethod(name string) (*node, []int) {
}
}
}
if t.cat == aliasT || isInterfaceSrc(t) && t.val != nil {
return t.val.lookupMethod(name)
}
}
return m, index
}
// methodDepth returns a depth greater or equal to 0, or -1 if no match.
func (t *itype) methodDepth(name string) int {
if m, lint := t.lookupMethod(name); m != nil {
return len(lint)
}
if _, lint, _, ok := t.lookupBinMethod(name); ok {
return len(lint)
}
return -1
}
// LookupBinMethod returns a method and a path to access a field in a struct object (the receiver).
func (t *itype) lookupBinMethod(name string) (m reflect.Method, index []int, isPtr, ok bool) {
if t.cat == ptrT {
@@ -1423,12 +1497,10 @@ func (t *itype) refType(defined map[string]*itype, wrapRecursive bool) reflect.T
switch t.cat {
case aliasT:
t.rtype = t.val.refType(defined, wrapRecursive)
case arrayT, variadicT:
if t.sizedef {
t.rtype = reflect.ArrayOf(t.size, t.val.refType(defined, wrapRecursive))
} else {
t.rtype = reflect.SliceOf(t.val.refType(defined, wrapRecursive))
}
case arrayT:
t.rtype = reflect.ArrayOf(t.length, t.val.refType(defined, wrapRecursive))
case sliceT, variadicT:
t.rtype = reflect.SliceOf(t.val.refType(defined, wrapRecursive))
case chanT:
t.rtype = reflect.ChanOf(reflect.BothDir, t.val.refType(defined, wrapRecursive))
case chanRecvT:
@@ -1503,12 +1575,10 @@ func (t *itype) frameType() (r reflect.Type) {
switch t.cat {
case aliasT:
r = t.val.frameType()
case arrayT, variadicT:
if t.sizedef {
r = reflect.ArrayOf(t.size, t.val.frameType())
} else {
r = reflect.SliceOf(t.val.frameType())
}
case arrayT:
r = reflect.ArrayOf(t.length, t.val.frameType())
case sliceT, variadicT:
r = reflect.SliceOf(t.val.frameType())
case funcT:
r = reflect.TypeOf((*node)(nil))
case interfaceT:
@@ -1518,6 +1588,8 @@ func (t *itype) frameType() (r reflect.Type) {
break
}
r = reflect.TypeOf((*valueInterface)(nil)).Elem()
case mapT:
r = reflect.MapOf(t.key.frameType(), t.val.frameType())
case ptrT:
r = reflect.PtrTo(t.val.frameType())
default:
@@ -1527,7 +1599,7 @@ func (t *itype) frameType() (r reflect.Type) {
}
func (t *itype) implements(it *itype) bool {
if t.cat == valueT {
if isBin(t) {
return t.TypeOf().Implements(it.TypeOf())
}
return t.methods().contains(it.methods())
@@ -1570,6 +1642,13 @@ func (t *itype) hasNil() bool {
return false
}
func (t *itype) elem() *itype {
if t.cat == valueT {
return &itype{cat: valueT, rtype: t.rtype.Elem()}
}
return t.val
}
func copyDefined(m map[string]*itype) map[string]*itype {
n := make(map[string]*itype, len(m))
for k, v := range m {
@@ -1631,6 +1710,13 @@ func defRecvType(n *node) *itype {
return nil
}
func wrappedType(n *node) *itype {
if n.typ.cat != valueT {
return nil
}
return n.typ.val
}
func isShiftNode(n *node) bool {
switch n.action {
case aShl, aShr, aShlAssign, aShrAssign:
@@ -1658,12 +1744,27 @@ func isFunc(t *itype) bool { return t.TypeOf().Kind() == reflect.Func }
func isMap(t *itype) bool { return t.TypeOf().Kind() == reflect.Map }
func isPtr(t *itype) bool { return t.TypeOf().Kind() == reflect.Ptr }
func isEmptyInterface(t *itype) bool {
return t.cat == interfaceT && len(t.field) == 0
}
func isFuncSrc(t *itype) bool {
return t.cat == funcT || (t.cat == aliasT && isFuncSrc(t.val))
}
func isPtrSrc(t *itype) bool {
return t.cat == ptrT || (t.cat == aliasT && isPtrSrc(t.val))
}
func isSendChan(t *itype) bool {
rt := t.TypeOf()
return rt.Kind() == reflect.Chan && rt.ChanDir() == reflect.SendDir
}
func isArray(t *itype) bool {
if t.cat == nilT {
return false
}
k := t.TypeOf().Kind()
return k == reflect.Array || k == reflect.Slice
}
@@ -1673,13 +1774,24 @@ func isInterfaceSrc(t *itype) bool {
}
func isInterfaceBin(t *itype) bool {
return t.cat == valueT && t.rtype.Kind() == reflect.Interface
return t.cat == valueT && t.rtype.Kind() == reflect.Interface || t.cat == errorT
}
func isInterface(t *itype) bool {
return isInterfaceSrc(t) || t.TypeOf() != nil && t.TypeOf().Kind() == reflect.Interface
}
func isBin(t *itype) bool {
switch t.cat {
case valueT:
return true
case aliasT, ptrT:
return isBin(t.val)
default:
return false
}
}
func isStruct(t *itype) bool {
// Test first for a struct category, because a recursive interpreter struct may be
// represented by an interface{} at reflect level.

View File

@@ -32,6 +32,9 @@ func (check typecheck) op(p opPredicates, a action, n, c *node, t reflect.Type)
//
// Use typ == nil to indicate assignment to an untyped blank identifier.
func (check typecheck) assignment(n *node, typ *itype, context string) error {
if n.typ == nil {
return n.cfgErrorf("invalid type in %s", context)
}
if n.typ.untyped {
if typ == nil || isInterface(typ) {
if typ == nil && n.typ.cat == nilT {
@@ -48,6 +51,10 @@ func (check typecheck) assignment(n *node, typ *itype, context string) error {
return nil
}
if typ.isIndirectRecursive() || n.typ.isIndirectRecursive() {
return nil
}
if !n.typ.assignableTo(typ) {
if context == "" {
return n.cfgErrorf("cannot use type %s as type %s", n.typ.id(), typ.id())
@@ -98,6 +105,7 @@ func (check typecheck) addressExpr(n *node) error {
c := c0.child[0]
if isArray(c.typ) || isMap(c.typ) {
c0 = c
found = true
continue
}
case compositeLitExpr, identExpr:
@@ -139,10 +147,7 @@ func (check typecheck) unaryExpr(n *node) error {
return nil
}
if err := check.op(unaryOpPredicates, n.action, n, c0, t0); err != nil {
return err
}
return nil
return check.op(unaryOpPredicates, n.action, n, c0, t0)
}
// shift type checks a shift binary expression.
@@ -228,6 +233,15 @@ func (check typecheck) binaryExpr(n *node) error {
}
switch n.action {
case aAdd:
if n.typ == nil {
break
}
// Catch mixing string and number for "+" operator use.
k, k0, k1 := isNumber(n.typ.TypeOf()), isNumber(c0.typ.TypeOf()), isNumber(c1.typ.TypeOf())
if k != k0 || k != k1 {
return n.cfgErrorf("cannot use type %s as type %s in assignment", c0.typ.id(), n.typ.id())
}
case aRem:
if zeroConst(c1) {
return n.cfgErrorf("invalid operation: division by zero")
@@ -254,10 +268,8 @@ func (check typecheck) binaryExpr(n *node) error {
}
t0 := c0.typ.TypeOf()
if err := check.op(binaryOpPredicates, a, n, c0, t0); err != nil {
return err
}
return nil
return check.op(binaryOpPredicates, a, n, c0, t0)
}
func zeroConst(n *node) bool {
@@ -285,7 +297,10 @@ func (check typecheck) index(n *node, max int) error {
}
// arrayLitExpr type checks an array composite literal expression.
func (check typecheck) arrayLitExpr(child []*node, typ *itype, length int) error {
func (check typecheck) arrayLitExpr(child []*node, typ *itype) error {
cat := typ.cat
length := typ.length
typ = typ.val
visited := make(map[int]bool, len(child))
index := 0
for _, c := range child {
@@ -297,7 +312,7 @@ func (check typecheck) arrayLitExpr(child []*node, typ *itype, length int) error
}
n = c.child[1]
index = int(vInt(c.child[0].rval))
case length > 0 && index >= length:
case cat == arrayT && index >= length:
return c.cfgErrorf("index %d is out of bounds (>= %d)", index, length)
}
@@ -900,7 +915,7 @@ func arrayDeref(typ *itype) *itype {
return typ
}
if typ.cat == ptrT && typ.val.cat == arrayT && typ.val.sizedef {
if typ.cat == ptrT && typ.val.cat == arrayT {
return typ.val
}
return typ
@@ -956,7 +971,7 @@ func (check typecheck) argument(p param, ftyp *itype, i, l int, ellipsis bool) e
}
t := p.Type().TypeOf()
if t.Kind() != reflect.Slice || !(&itype{cat: valueT, rtype: t.Elem()}).assignableTo(atyp) {
return p.nod.cfgErrorf("cannot use %s as type %s", p.nod.typ.id(), (&itype{cat: arrayT, val: atyp}).id())
return p.nod.cfgErrorf("cannot use %s as type %s", p.nod.typ.id(), (&itype{cat: sliceT, val: atyp}).id())
}
return nil
}
@@ -978,6 +993,8 @@ func getArg(ftyp *itype, i int) *itype {
return arg
case i < l:
return ftyp.in(i)
case ftyp.cat == valueT && i < ftyp.rtype.NumIn():
return &itype{cat: valueT, rtype: ftyp.rtype.In(i)}
default:
return nil
}

View File

@@ -45,21 +45,35 @@ func genValueBinMethodOnInterface(n *node, defaultGen func(*frame) reflect.Value
n.child[0].child == nil || n.child[0].child[0] == nil {
return defaultGen
}
if n.child[0].child[1] == nil || n.child[0].child[1].ident == "" {
c0 := n.child[0]
if c0.child[1] == nil || c0.child[1].ident == "" {
return defaultGen
}
value0 := genValue(n.child[0].child[0])
value0 := genValue(c0.child[0])
return func(f *frame) reflect.Value {
val, ok := value0(f).Interface().(valueInterface)
if !ok {
v := value0(f)
var nod *node
for v.IsValid() {
// Traverse interface indirections to find out concrete type.
vi, ok := v.Interface().(valueInterface)
if !ok {
break
}
v = vi.value
nod = vi.node
}
if nod == nil {
return defaultGen(f)
}
typ := val.node.typ
typ := nod.typ
if typ.node != nil || typ.cat != valueT {
return defaultGen(f)
}
meth, _ := typ.rtype.MethodByName(n.child[0].child[1].ident)
meth, _ := typ.rtype.MethodByName(c0.child[1].ident)
return meth.Func
}
}
@@ -143,14 +157,27 @@ func genValueAsFunctionWrapper(n *node) func(*frame) reflect.Value {
if v.IsNil() {
return reflect.New(typ).Elem()
}
return genFunctionWrapper(v.Interface().(*node))(f)
vn, ok := v.Interface().(*node)
if ok && vn.rval.IsValid() && vn.rval.Type().Kind() == reflect.Func {
// The node value is already a callable func, no need to wrap it.
return vn.rval
}
return genFunctionWrapper(vn)(f)
}
}
func genValueAs(n *node, t reflect.Type) func(*frame) reflect.Value {
v := genValue(n)
value := genValue(n)
return func(f *frame) reflect.Value {
return v(f).Convert(t)
v := value(f)
switch v.Type().Kind() {
case reflect.Chan, reflect.Func, reflect.Interface, reflect.Ptr, reflect.Map, reflect.Slice, reflect.UnsafePointer:
if v.IsNil() {
return reflect.New(t).Elem()
}
}
return v.Convert(t)
}
}
@@ -200,6 +227,31 @@ func genValue(n *node) func(*frame) reflect.Value {
}
}
func genDestValue(typ *itype, n *node) func(*frame) reflect.Value {
convertLiteralValue(n, typ.TypeOf())
switch {
case isInterfaceSrc(typ) && !isEmptyInterface(typ):
return genValueInterface(n)
case isFuncSrc(typ) && n.typ.cat == valueT:
return genValueNode(n)
case typ.cat == valueT && isFuncSrc(n.typ):
return genFunctionWrapper(n)
case isInterfaceBin(typ):
return genInterfaceWrapper(n, typ.rtype)
case n.kind == basicLit && n.val == nil:
return func(*frame) reflect.Value { return reflect.New(typ.rtype).Elem() }
case isRecursiveType(typ, typ.rtype):
return genValueRecursiveInterface(n, typ.rtype)
case isRecursiveType(n.typ, n.typ.rtype):
return genValueRecursiveInterfacePtrValue(n)
case n.typ.untyped && isComplex(typ.TypeOf()):
return genValueComplex(n)
case n.typ.untyped && !typ.untyped:
return genValueAs(n, typ.TypeOf())
}
return genValue(n)
}
func genValueArray(n *node) func(*frame) reflect.Value {
value := genValue(n)
// dereference array pointer, to support array operations on array pointer
@@ -289,6 +341,34 @@ func genValueInterface(n *node) func(*frame) reflect.Value {
}
}
func getConcreteValue(val reflect.Value) reflect.Value {
v := val
for {
vi, ok := v.Interface().(valueInterface)
if !ok {
break
}
v = vi.value
}
if v.NumMethod() > 0 {
return v
}
if v.Type().Kind() != reflect.Struct {
return v
}
// Search a concrete value in fields of an emulated interface.
for i := v.NumField() - 1; i >= 0; i-- {
vv := v.Field(i)
if vv.Type().Kind() == reflect.Interface {
vv = vv.Elem()
}
if vv.IsValid() {
return vv
}
}
return v
}
func zeroInterfaceValue() reflect.Value {
n := &node{kind: basicLit, typ: &itype{cat: nilT, untyped: true}}
v := reflect.New(interf).Elem()
@@ -296,7 +376,7 @@ func zeroInterfaceValue() reflect.Value {
}
func wantEmptyInterface(n *node) bool {
return n.typ.cat == interfaceT && len(n.typ.field) == 0 ||
return isEmptyInterface(n.typ) ||
n.anc.action == aAssign && n.anc.typ.cat == interfaceT && len(n.anc.typ.field) == 0 ||
n.anc.kind == returnStmt && n.anc.val.(*node).typ.ret[0].cat == interfaceT && len(n.anc.val.(*node).typ.ret[0].field) == 0
}
@@ -311,7 +391,7 @@ func genValueOutput(n *node, t reflect.Type) func(*frame) reflect.Value {
}
fallthrough
case n.anc.kind == returnStmt && n.anc.val.(*node).typ.ret[0].cat == interfaceT:
if len(n.anc.val.(*node).typ.ret[0].field) == 0 {
if nod, ok := n.anc.val.(*node); !ok || len(nod.typ.ret[0].field) == 0 {
// empty interface, do not wrap
return value
}

View File

@@ -1,74 +0,0 @@
// Code generated by 'yaegi extract debug/pe'. DO NOT EDIT.
// +build go1.14,!go1.15
package stdlib
import (
"debug/pe"
"go/constant"
"go/token"
"reflect"
)
func init() {
Symbols["debug/pe"] = map[string]reflect.Value{
// function, constant and variable definitions
"COFFSymbolSize": reflect.ValueOf(constant.MakeFromLiteral("18", token.INT, 0)),
"IMAGE_DIRECTORY_ENTRY_ARCHITECTURE": reflect.ValueOf(constant.MakeFromLiteral("7", token.INT, 0)),
"IMAGE_DIRECTORY_ENTRY_BASERELOC": reflect.ValueOf(constant.MakeFromLiteral("5", token.INT, 0)),
"IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT": reflect.ValueOf(constant.MakeFromLiteral("11", token.INT, 0)),
"IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR": reflect.ValueOf(constant.MakeFromLiteral("14", token.INT, 0)),
"IMAGE_DIRECTORY_ENTRY_DEBUG": reflect.ValueOf(constant.MakeFromLiteral("6", token.INT, 0)),
"IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT": reflect.ValueOf(constant.MakeFromLiteral("13", token.INT, 0)),
"IMAGE_DIRECTORY_ENTRY_EXCEPTION": reflect.ValueOf(constant.MakeFromLiteral("3", token.INT, 0)),
"IMAGE_DIRECTORY_ENTRY_EXPORT": reflect.ValueOf(constant.MakeFromLiteral("0", token.INT, 0)),
"IMAGE_DIRECTORY_ENTRY_GLOBALPTR": reflect.ValueOf(constant.MakeFromLiteral("8", token.INT, 0)),
"IMAGE_DIRECTORY_ENTRY_IAT": reflect.ValueOf(constant.MakeFromLiteral("12", token.INT, 0)),
"IMAGE_DIRECTORY_ENTRY_IMPORT": reflect.ValueOf(constant.MakeFromLiteral("1", token.INT, 0)),
"IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG": reflect.ValueOf(constant.MakeFromLiteral("10", token.INT, 0)),
"IMAGE_DIRECTORY_ENTRY_RESOURCE": reflect.ValueOf(constant.MakeFromLiteral("2", token.INT, 0)),
"IMAGE_DIRECTORY_ENTRY_SECURITY": reflect.ValueOf(constant.MakeFromLiteral("4", token.INT, 0)),
"IMAGE_DIRECTORY_ENTRY_TLS": reflect.ValueOf(constant.MakeFromLiteral("9", token.INT, 0)),
"IMAGE_FILE_MACHINE_AM33": reflect.ValueOf(constant.MakeFromLiteral("467", token.INT, 0)),
"IMAGE_FILE_MACHINE_AMD64": reflect.ValueOf(constant.MakeFromLiteral("34404", token.INT, 0)),
"IMAGE_FILE_MACHINE_ARM": reflect.ValueOf(constant.MakeFromLiteral("448", token.INT, 0)),
"IMAGE_FILE_MACHINE_ARM64": reflect.ValueOf(constant.MakeFromLiteral("43620", token.INT, 0)),
"IMAGE_FILE_MACHINE_ARMNT": reflect.ValueOf(constant.MakeFromLiteral("452", token.INT, 0)),
"IMAGE_FILE_MACHINE_EBC": reflect.ValueOf(constant.MakeFromLiteral("3772", token.INT, 0)),
"IMAGE_FILE_MACHINE_I386": reflect.ValueOf(constant.MakeFromLiteral("332", token.INT, 0)),
"IMAGE_FILE_MACHINE_IA64": reflect.ValueOf(constant.MakeFromLiteral("512", token.INT, 0)),
"IMAGE_FILE_MACHINE_M32R": reflect.ValueOf(constant.MakeFromLiteral("36929", token.INT, 0)),
"IMAGE_FILE_MACHINE_MIPS16": reflect.ValueOf(constant.MakeFromLiteral("614", token.INT, 0)),
"IMAGE_FILE_MACHINE_MIPSFPU": reflect.ValueOf(constant.MakeFromLiteral("870", token.INT, 0)),
"IMAGE_FILE_MACHINE_MIPSFPU16": reflect.ValueOf(constant.MakeFromLiteral("1126", token.INT, 0)),
"IMAGE_FILE_MACHINE_POWERPC": reflect.ValueOf(constant.MakeFromLiteral("496", token.INT, 0)),
"IMAGE_FILE_MACHINE_POWERPCFP": reflect.ValueOf(constant.MakeFromLiteral("497", token.INT, 0)),
"IMAGE_FILE_MACHINE_R4000": reflect.ValueOf(constant.MakeFromLiteral("358", token.INT, 0)),
"IMAGE_FILE_MACHINE_SH3": reflect.ValueOf(constant.MakeFromLiteral("418", token.INT, 0)),
"IMAGE_FILE_MACHINE_SH3DSP": reflect.ValueOf(constant.MakeFromLiteral("419", token.INT, 0)),
"IMAGE_FILE_MACHINE_SH4": reflect.ValueOf(constant.MakeFromLiteral("422", token.INT, 0)),
"IMAGE_FILE_MACHINE_SH5": reflect.ValueOf(constant.MakeFromLiteral("424", token.INT, 0)),
"IMAGE_FILE_MACHINE_THUMB": reflect.ValueOf(constant.MakeFromLiteral("450", token.INT, 0)),
"IMAGE_FILE_MACHINE_UNKNOWN": reflect.ValueOf(constant.MakeFromLiteral("0", token.INT, 0)),
"IMAGE_FILE_MACHINE_WCEMIPSV2": reflect.ValueOf(constant.MakeFromLiteral("361", token.INT, 0)),
"NewFile": reflect.ValueOf(pe.NewFile),
"Open": reflect.ValueOf(pe.Open),
// type definitions
"COFFSymbol": reflect.ValueOf((*pe.COFFSymbol)(nil)),
"DataDirectory": reflect.ValueOf((*pe.DataDirectory)(nil)),
"File": reflect.ValueOf((*pe.File)(nil)),
"FileHeader": reflect.ValueOf((*pe.FileHeader)(nil)),
"FormatError": reflect.ValueOf((*pe.FormatError)(nil)),
"ImportDirectory": reflect.ValueOf((*pe.ImportDirectory)(nil)),
"OptionalHeader32": reflect.ValueOf((*pe.OptionalHeader32)(nil)),
"OptionalHeader64": reflect.ValueOf((*pe.OptionalHeader64)(nil)),
"Reloc": reflect.ValueOf((*pe.Reloc)(nil)),
"Section": reflect.ValueOf((*pe.Section)(nil)),
"SectionHeader": reflect.ValueOf((*pe.SectionHeader)(nil)),
"SectionHeader32": reflect.ValueOf((*pe.SectionHeader32)(nil)),
"StringTable": reflect.ValueOf((*pe.StringTable)(nil)),
"Symbol": reflect.ValueOf((*pe.Symbol)(nil)),
}
}

View File

@@ -1,156 +0,0 @@
// Code generated by 'yaegi extract os'. DO NOT EDIT.
// +build go1.14,!go1.15
package stdlib
import (
"go/constant"
"go/token"
"os"
"reflect"
"time"
)
func init() {
Symbols["os"] = map[string]reflect.Value{
// function, constant and variable definitions
"Args": reflect.ValueOf(&os.Args).Elem(),
"Chdir": reflect.ValueOf(os.Chdir),
"Chmod": reflect.ValueOf(os.Chmod),
"Chown": reflect.ValueOf(os.Chown),
"Chtimes": reflect.ValueOf(os.Chtimes),
"Clearenv": reflect.ValueOf(os.Clearenv),
"Create": reflect.ValueOf(os.Create),
"DevNull": reflect.ValueOf(constant.MakeFromLiteral("\"/dev/null\"", token.STRING, 0)),
"Environ": reflect.ValueOf(os.Environ),
"ErrClosed": reflect.ValueOf(&os.ErrClosed).Elem(),
"ErrExist": reflect.ValueOf(&os.ErrExist).Elem(),
"ErrInvalid": reflect.ValueOf(&os.ErrInvalid).Elem(),
"ErrNoDeadline": reflect.ValueOf(&os.ErrNoDeadline).Elem(),
"ErrNotExist": reflect.ValueOf(&os.ErrNotExist).Elem(),
"ErrPermission": reflect.ValueOf(&os.ErrPermission).Elem(),
"Executable": reflect.ValueOf(os.Executable),
"Exit": reflect.ValueOf(osExit),
"Expand": reflect.ValueOf(os.Expand),
"ExpandEnv": reflect.ValueOf(os.ExpandEnv),
"FindProcess": reflect.ValueOf(osFindProcess),
"Getegid": reflect.ValueOf(os.Getegid),
"Getenv": reflect.ValueOf(os.Getenv),
"Geteuid": reflect.ValueOf(os.Geteuid),
"Getgid": reflect.ValueOf(os.Getgid),
"Getgroups": reflect.ValueOf(os.Getgroups),
"Getpagesize": reflect.ValueOf(os.Getpagesize),
"Getpid": reflect.ValueOf(os.Getpid),
"Getppid": reflect.ValueOf(os.Getppid),
"Getuid": reflect.ValueOf(os.Getuid),
"Getwd": reflect.ValueOf(os.Getwd),
"Hostname": reflect.ValueOf(os.Hostname),
"Interrupt": reflect.ValueOf(&os.Interrupt).Elem(),
"IsExist": reflect.ValueOf(os.IsExist),
"IsNotExist": reflect.ValueOf(os.IsNotExist),
"IsPathSeparator": reflect.ValueOf(os.IsPathSeparator),
"IsPermission": reflect.ValueOf(os.IsPermission),
"IsTimeout": reflect.ValueOf(os.IsTimeout),
"Kill": reflect.ValueOf(&os.Kill).Elem(),
"Lchown": reflect.ValueOf(os.Lchown),
"Link": reflect.ValueOf(os.Link),
"LookupEnv": reflect.ValueOf(os.LookupEnv),
"Lstat": reflect.ValueOf(os.Lstat),
"Mkdir": reflect.ValueOf(os.Mkdir),
"MkdirAll": reflect.ValueOf(os.MkdirAll),
"ModeAppend": reflect.ValueOf(os.ModeAppend),
"ModeCharDevice": reflect.ValueOf(os.ModeCharDevice),
"ModeDevice": reflect.ValueOf(os.ModeDevice),
"ModeDir": reflect.ValueOf(os.ModeDir),
"ModeExclusive": reflect.ValueOf(os.ModeExclusive),
"ModeIrregular": reflect.ValueOf(os.ModeIrregular),
"ModeNamedPipe": reflect.ValueOf(os.ModeNamedPipe),
"ModePerm": reflect.ValueOf(os.ModePerm),
"ModeSetgid": reflect.ValueOf(os.ModeSetgid),
"ModeSetuid": reflect.ValueOf(os.ModeSetuid),
"ModeSocket": reflect.ValueOf(os.ModeSocket),
"ModeSticky": reflect.ValueOf(os.ModeSticky),
"ModeSymlink": reflect.ValueOf(os.ModeSymlink),
"ModeTemporary": reflect.ValueOf(os.ModeTemporary),
"ModeType": reflect.ValueOf(os.ModeType),
"NewFile": reflect.ValueOf(os.NewFile),
"NewSyscallError": reflect.ValueOf(os.NewSyscallError),
"O_APPEND": reflect.ValueOf(os.O_APPEND),
"O_CREATE": reflect.ValueOf(os.O_CREATE),
"O_EXCL": reflect.ValueOf(os.O_EXCL),
"O_RDONLY": reflect.ValueOf(os.O_RDONLY),
"O_RDWR": reflect.ValueOf(os.O_RDWR),
"O_SYNC": reflect.ValueOf(os.O_SYNC),
"O_TRUNC": reflect.ValueOf(os.O_TRUNC),
"O_WRONLY": reflect.ValueOf(os.O_WRONLY),
"Open": reflect.ValueOf(os.Open),
"OpenFile": reflect.ValueOf(os.OpenFile),
"PathListSeparator": reflect.ValueOf(constant.MakeFromLiteral("58", token.INT, 0)),
"PathSeparator": reflect.ValueOf(constant.MakeFromLiteral("47", token.INT, 0)),
"Pipe": reflect.ValueOf(os.Pipe),
"Readlink": reflect.ValueOf(os.Readlink),
"Remove": reflect.ValueOf(os.Remove),
"RemoveAll": reflect.ValueOf(os.RemoveAll),
"Rename": reflect.ValueOf(os.Rename),
"SEEK_CUR": reflect.ValueOf(os.SEEK_CUR),
"SEEK_END": reflect.ValueOf(os.SEEK_END),
"SEEK_SET": reflect.ValueOf(os.SEEK_SET),
"SameFile": reflect.ValueOf(os.SameFile),
"Setenv": reflect.ValueOf(os.Setenv),
"StartProcess": reflect.ValueOf(os.StartProcess),
"Stat": reflect.ValueOf(os.Stat),
"Stderr": reflect.ValueOf(&os.Stderr).Elem(),
"Stdin": reflect.ValueOf(&os.Stdin).Elem(),
"Stdout": reflect.ValueOf(&os.Stdout).Elem(),
"Symlink": reflect.ValueOf(os.Symlink),
"TempDir": reflect.ValueOf(os.TempDir),
"Truncate": reflect.ValueOf(os.Truncate),
"Unsetenv": reflect.ValueOf(os.Unsetenv),
"UserCacheDir": reflect.ValueOf(os.UserCacheDir),
"UserConfigDir": reflect.ValueOf(os.UserConfigDir),
"UserHomeDir": reflect.ValueOf(os.UserHomeDir),
// type definitions
"File": reflect.ValueOf((*os.File)(nil)),
"FileInfo": reflect.ValueOf((*os.FileInfo)(nil)),
"FileMode": reflect.ValueOf((*os.FileMode)(nil)),
"LinkError": reflect.ValueOf((*os.LinkError)(nil)),
"PathError": reflect.ValueOf((*os.PathError)(nil)),
"ProcAttr": reflect.ValueOf((*os.ProcAttr)(nil)),
"Process": reflect.ValueOf((*os.Process)(nil)),
"ProcessState": reflect.ValueOf((*os.ProcessState)(nil)),
"Signal": reflect.ValueOf((*os.Signal)(nil)),
"SyscallError": reflect.ValueOf((*os.SyscallError)(nil)),
// interface wrapper definitions
"_FileInfo": reflect.ValueOf((*_os_FileInfo)(nil)),
"_Signal": reflect.ValueOf((*_os_Signal)(nil)),
}
}
// _os_FileInfo is an interface wrapper for FileInfo type
type _os_FileInfo struct {
WIsDir func() bool
WModTime func() time.Time
WMode func() os.FileMode
WName func() string
WSize func() int64
WSys func() interface{}
}
func (W _os_FileInfo) IsDir() bool { return W.WIsDir() }
func (W _os_FileInfo) ModTime() time.Time { return W.WModTime() }
func (W _os_FileInfo) Mode() os.FileMode { return W.WMode() }
func (W _os_FileInfo) Name() string { return W.WName() }
func (W _os_FileInfo) Size() int64 { return W.WSize() }
func (W _os_FileInfo) Sys() interface{} { return W.WSys() }
// _os_Signal is an interface wrapper for Signal type
type _os_Signal struct {
WSignal func()
WString func() string
}
func (W _os_Signal) Signal() { W.WSignal() }
func (W _os_Signal) String() string { return W.WString() }

View File

@@ -1,21 +0,0 @@
// Code generated by 'yaegi extract os/signal'. DO NOT EDIT.
// +build go1.14,!go1.15
package stdlib
import (
"os/signal"
"reflect"
)
func init() {
Symbols["os/signal"] = map[string]reflect.Value{
// function, constant and variable definitions
"Ignore": reflect.ValueOf(signal.Ignore),
"Ignored": reflect.ValueOf(signal.Ignored),
"Notify": reflect.ValueOf(signal.Notify),
"Reset": reflect.ValueOf(signal.Reset),
"Stop": reflect.ValueOf(signal.Stop),
}
}

View File

@@ -1,82 +0,0 @@
// Code generated by 'yaegi extract testing'. DO NOT EDIT.
// +build go1.14,!go1.15
package stdlib
import (
"reflect"
"testing"
)
func init() {
Symbols["testing"] = map[string]reflect.Value{
// function, constant and variable definitions
"AllocsPerRun": reflect.ValueOf(testing.AllocsPerRun),
"Benchmark": reflect.ValueOf(testing.Benchmark),
"CoverMode": reflect.ValueOf(testing.CoverMode),
"Coverage": reflect.ValueOf(testing.Coverage),
"Init": reflect.ValueOf(testing.Init),
"Main": reflect.ValueOf(testing.Main),
"MainStart": reflect.ValueOf(testing.MainStart),
"RegisterCover": reflect.ValueOf(testing.RegisterCover),
"RunBenchmarks": reflect.ValueOf(testing.RunBenchmarks),
"RunExamples": reflect.ValueOf(testing.RunExamples),
"RunTests": reflect.ValueOf(testing.RunTests),
"Short": reflect.ValueOf(testing.Short),
"Verbose": reflect.ValueOf(testing.Verbose),
// type definitions
"B": reflect.ValueOf((*testing.B)(nil)),
"BenchmarkResult": reflect.ValueOf((*testing.BenchmarkResult)(nil)),
"Cover": reflect.ValueOf((*testing.Cover)(nil)),
"CoverBlock": reflect.ValueOf((*testing.CoverBlock)(nil)),
"InternalBenchmark": reflect.ValueOf((*testing.InternalBenchmark)(nil)),
"InternalExample": reflect.ValueOf((*testing.InternalExample)(nil)),
"InternalTest": reflect.ValueOf((*testing.InternalTest)(nil)),
"M": reflect.ValueOf((*testing.M)(nil)),
"PB": reflect.ValueOf((*testing.PB)(nil)),
"T": reflect.ValueOf((*testing.T)(nil)),
"TB": reflect.ValueOf((*testing.TB)(nil)),
// interface wrapper definitions
"_TB": reflect.ValueOf((*_testing_TB)(nil)),
}
}
// _testing_TB is an interface wrapper for TB type
type _testing_TB struct {
WCleanup func(a0 func())
WError func(args []interface{})
WErrorf func(format string, args []interface{})
WFail func()
WFailNow func()
WFailed func() bool
WFatal func(args []interface{})
WFatalf func(format string, args []interface{})
WHelper func()
WLog func(args []interface{})
WLogf func(format string, args []interface{})
WName func() string
WSkip func(args []interface{})
WSkipNow func()
WSkipf func(format string, args []interface{})
WSkipped func() bool
}
func (W _testing_TB) Cleanup(a0 func()) { W.WCleanup(a0) }
func (W _testing_TB) Error(args []interface{}) { W.WError(args) }
func (W _testing_TB) Errorf(format string, args []interface{}) { W.WErrorf(format, args) }
func (W _testing_TB) Fail() { W.WFail() }
func (W _testing_TB) FailNow() { W.WFailNow() }
func (W _testing_TB) Failed() bool { return W.WFailed() }
func (W _testing_TB) Fatal(args []interface{}) { W.WFatal(args) }
func (W _testing_TB) Fatalf(format string, args []interface{}) { W.WFatalf(format, args) }
func (W _testing_TB) Helper() { W.WHelper() }
func (W _testing_TB) Log(args []interface{}) { W.WLog(args) }
func (W _testing_TB) Logf(format string, args []interface{}) { W.WLogf(format, args) }
func (W _testing_TB) Name() string { return W.WName() }
func (W _testing_TB) Skip(args []interface{}) { W.WSkip(args) }
func (W _testing_TB) SkipNow() { W.WSkipNow() }
func (W _testing_TB) Skipf(format string, args []interface{}) { W.WSkipf(format, args) }
func (W _testing_TB) Skipped() bool { return W.WSkipped() }

View File

@@ -1,30 +0,0 @@
// Code generated by 'yaegi extract go/parser'. DO NOT EDIT.
// +build go1.15
package stdlib
import (
"go/parser"
"reflect"
)
func init() {
Symbols["go/parser"] = map[string]reflect.Value{
// function, constant and variable definitions
"AllErrors": reflect.ValueOf(parser.AllErrors),
"DeclarationErrors": reflect.ValueOf(parser.DeclarationErrors),
"ImportsOnly": reflect.ValueOf(parser.ImportsOnly),
"PackageClauseOnly": reflect.ValueOf(parser.PackageClauseOnly),
"ParseComments": reflect.ValueOf(parser.ParseComments),
"ParseDir": reflect.ValueOf(parser.ParseDir),
"ParseExpr": reflect.ValueOf(parser.ParseExpr),
"ParseExprFrom": reflect.ValueOf(parser.ParseExprFrom),
"ParseFile": reflect.ValueOf(parser.ParseFile),
"SpuriousErrors": reflect.ValueOf(parser.SpuriousErrors),
"Trace": reflect.ValueOf(parser.Trace),
// type definitions
"Mode": reflect.ValueOf((*parser.Mode)(nil)),
}
}

View File

@@ -1,65 +0,0 @@
// Code generated by 'yaegi extract image/draw'. DO NOT EDIT.
// +build go1.15
package stdlib
import (
"image"
"image/color"
"image/draw"
"reflect"
)
func init() {
Symbols["image/draw"] = map[string]reflect.Value{
// function, constant and variable definitions
"Draw": reflect.ValueOf(draw.Draw),
"DrawMask": reflect.ValueOf(draw.DrawMask),
"FloydSteinberg": reflect.ValueOf(&draw.FloydSteinberg).Elem(),
"Over": reflect.ValueOf(draw.Over),
"Src": reflect.ValueOf(draw.Src),
// type definitions
"Drawer": reflect.ValueOf((*draw.Drawer)(nil)),
"Image": reflect.ValueOf((*draw.Image)(nil)),
"Op": reflect.ValueOf((*draw.Op)(nil)),
"Quantizer": reflect.ValueOf((*draw.Quantizer)(nil)),
// interface wrapper definitions
"_Drawer": reflect.ValueOf((*_image_draw_Drawer)(nil)),
"_Image": reflect.ValueOf((*_image_draw_Image)(nil)),
"_Quantizer": reflect.ValueOf((*_image_draw_Quantizer)(nil)),
}
}
// _image_draw_Drawer is an interface wrapper for Drawer type
type _image_draw_Drawer struct {
WDraw func(dst draw.Image, r image.Rectangle, src image.Image, sp image.Point)
}
func (W _image_draw_Drawer) Draw(dst draw.Image, r image.Rectangle, src image.Image, sp image.Point) {
W.WDraw(dst, r, src, sp)
}
// _image_draw_Image is an interface wrapper for Image type
type _image_draw_Image struct {
WAt func(x int, y int) color.Color
WBounds func() image.Rectangle
WColorModel func() color.Model
WSet func(x int, y int, c color.Color)
}
func (W _image_draw_Image) At(x int, y int) color.Color { return W.WAt(x, y) }
func (W _image_draw_Image) Bounds() image.Rectangle { return W.WBounds() }
func (W _image_draw_Image) ColorModel() color.Model { return W.WColorModel() }
func (W _image_draw_Image) Set(x int, y int, c color.Color) { W.WSet(x, y, c) }
// _image_draw_Quantizer is an interface wrapper for Quantizer type
type _image_draw_Quantizer struct {
WQuantize func(p color.Palette, m image.Image) color.Palette
}
func (W _image_draw_Quantizer) Quantize(p color.Palette, m image.Image) color.Palette {
return W.WQuantize(p, m)
}

View File

@@ -1,21 +0,0 @@
// Code generated by 'yaegi extract os/signal'. DO NOT EDIT.
// +build go1.15
package stdlib
import (
"os/signal"
"reflect"
)
func init() {
Symbols["os/signal"] = map[string]reflect.Value{
// function, constant and variable definitions
"Ignore": reflect.ValueOf(signal.Ignore),
"Ignored": reflect.ValueOf(signal.Ignored),
"Notify": reflect.ValueOf(signal.Notify),
"Reset": reflect.ValueOf(signal.Reset),
"Stop": reflect.ValueOf(signal.Stop),
}
}

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