Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dev Tools: Source map decoder #335

Merged
merged 26 commits into from
Jul 20, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
efae689
Merge branch 'release/v1.14.1'
algojack Mar 29, 2022
6b6dfc0
Merge branch 'release/v1.15.0'
algobarb Apr 28, 2022
7c7bab4
Merge branch 'release/v1.16.0'
onetechnical Jun 3, 2022
fae5b08
Merge branch 'release/v1.17.0'
egieseke Jun 21, 2022
d4964bb
adding source map decoder
barnjamin Jun 23, 2022
f4d6c30
adding tests for source map
barnjamin Jun 24, 2022
364a52e
Revert docker shell script
barnjamin Jun 24, 2022
f6775ba
Merge branch 'develop' into src-map-decoder
barnjamin Jun 27, 2022
7b0d13b
Update logic/source_map.go
barnjamin Jun 27, 2022
d12ab29
adding correct name for mappings with bbackwards commpat option
barnjamin Jun 28, 2022
ec833c0
adding check for version and empty mappings
barnjamin Jun 28, 2022
6d872ec
updated to new format for mappings
barnjamin Jul 5, 2022
a6de1cf
dont export decoder
barnjamin Jul 5, 2022
0583896
rename test args to be more clear
barnjamin Jul 6, 2022
ad440ed
add test
barnjamin Jul 6, 2022
b4e37dc
Merge branch 'develop' into src-map-decoder
barnjamin Jul 6, 2022
d94d3a8
adding integration test, hack for teal compile with source map
barnjamin Jul 6, 2022
da90c91
use goquery to encode the get params
barnjamin Jul 11, 2022
c0444fc
tweak line to pc logic for new deltas
barnjamin Jul 11, 2022
4348498
adding tealCompile change generated from generator
barnjamin Jul 12, 2022
2c673b2
add new tests
barnjamin Jul 12, 2022
78767cd
revert to master
barnjamin Jul 13, 2022
c76b0ac
Adding extra param for get params on post
barnjamin Jul 15, 2022
763e945
swap arguments to make it cleaner
barnjamin Jul 15, 2022
8a4964a
fix post
barnjamin Jul 15, 2022
848ba91
fix arg names in client
barnjamin Jul 15, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@ test:

unit:
go test $(TEST_SOURCES_NO_CUCUMBER)
cd test && go test -timeout 0s --godog.strict=true --godog.format=pretty --godog.tags="@unit.offline,@unit.algod,@unit.indexer,@unit.transactions.keyreg,@unit.rekey,@unit.tealsign,@unit.dryrun,@unit.responses,@unit.applications,@unit.transactions,@unit.indexer.rekey,@unit.responses.messagepack,@unit.responses.231,@unit.responses.messagepack.231,@unit.responses.genesis,@unit.feetest,@unit.indexer.logs,@unit.abijson,@unit.abijson.byname,@unit.transactions.payment,@unit.atomic_transaction_composer,@unit.responses.unlimited_assets,@unit.indexer.ledger_refactoring,@unit.algod.ledger_refactoring,@unit.dryrun.trace.application" --test.v .
cd test && go test -timeout 0s --godog.strict=true --godog.format=pretty --godog.tags="@unit.sourcemap,@unit.offline,@unit.algod,@unit.indexer,@unit.transactions.keyreg,@unit.rekey,@unit.tealsign,@unit.dryrun,@unit.responses,@unit.applications,@unit.transactions,@unit.indexer.rekey,@unit.responses.messagepack,@unit.responses.231,@unit.responses.messagepack.231,@unit.responses.genesis,@unit.feetest,@unit.indexer.logs,@unit.abijson,@unit.abijson.byname,@unit.transactions.payment,@unit.atomic_transaction_composer,@unit.responses.unlimited_assets,@unit.indexer.ledger_refactoring,@unit.algod.ledger_refactoring,@unit.dryrun.trace.application" --test.v .

integration:
go test $(TEST_SOURCES_NO_CUCUMBER)
cd test && go test -timeout 0s --godog.strict=true --godog.format=pretty --godog.tags="@algod,@assets,@auction,@kmd,@send,@indexer,@rekey,@send.keyregtxn,@dryrun,@compile,@applications.verified,@indexer.applications,@indexer.231,@abi,@c2c" --test.v .
cd test && go test -timeout 0s --godog.strict=true --godog.format=pretty --godog.tags="@algod,@assets,@auction,@kmd,@send,@indexer,@rekey,@send.keyregtxn,@dryrun,@compile,@applications.verified,@indexer.applications,@indexer.231,@abi,@c2c,@compile.sourcemap" --test.v .

docker-test:
./test/docker/run_docker.sh
Expand Down
4 changes: 2 additions & 2 deletions client/v2/algod/algod.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ func (c *Client) getRaw(ctx context.Context, path string, body interface{}, head
// post sends a POST request to the given path with the given request object.
// No query parameters will be sent if request is nil.
// response must be a pointer to an object as post writes the response there.
func (c *Client) post(ctx context.Context, response interface{}, path string, body interface{}, headers []*common.Header) error {
return (*common.Client)(c).Post(ctx, response, path, body, headers)
func (c *Client) post(ctx context.Context, response interface{}, path string, params interface{}, headers []*common.Header, body interface{}) error {
return (*common.Client)(c).Post(ctx, response, path, params, headers, body)
}

// MakeClient is the factory for constructing a ClientV2 for a given endpoint.
Expand Down
3 changes: 1 addition & 2 deletions client/v2/algod/dryrun.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ func (s *TealDryRun) Do(
ctx context.Context,
headers ...*common.Header,
) (response models.DryrunResponse, err error) {
err = s.c.post(ctx, &response,
"/v2/teal/dryrun", s.rawobj, headers)
err = s.c.post(ctx, &response, "/v2/teal/dryrun", nil, headers, s.rawobj)
return
}
2 changes: 1 addition & 1 deletion client/v2/algod/rawTransaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ func (s *SendRawTransaction) Do(ctx context.Context, headers ...*common.Header)
if addContentType {
headers = append(headers, &common.Header{"Content-Type", "application/x-binary"})
}
err = s.c.post(ctx, &response, "/v2/transactions", s.rawtxn, headers)
err = s.c.post(ctx, &response, "/v2/transactions", nil, headers, s.rawtxn)
txid = response.Txid
return
}
2 changes: 1 addition & 1 deletion client/v2/algod/tealCompile.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,6 @@ func (s *TealCompile) Sourcemap(Sourcemap bool) *TealCompile {

// Do performs the HTTP request
func (s *TealCompile) Do(ctx context.Context, headers ...*common.Header) (response models.CompileResponse, err error) {
err = s.c.post(ctx, &response, "/v2/teal/compile", s.source, headers)
err = s.c.post(ctx, &response, "/v2/teal/compile", s.p, headers, s.source)
return
}
2 changes: 1 addition & 1 deletion client/v2/algod/tealDryrun.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,6 @@ type TealDryrun struct {

// Do performs the HTTP request
func (s *TealDryrun) Do(ctx context.Context, headers ...*common.Header) (response models.DryrunResponse, err error) {
err = s.c.post(ctx, &response, "/v2/teal/dryrun", msgpack.Encode(&s.request), headers)
err = s.c.post(ctx, &response, "/v2/teal/dryrun", nil, headers, msgpack.Encode(&s.request))
return
}
59 changes: 30 additions & 29 deletions client/v2/common/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,33 +103,34 @@ func mergeRawQueries(q1, q2 string) string {
}

// submitFormRaw is a helper used for submitting (ex.) GETs and POSTs to the server
func (client *Client) submitFormRaw(ctx context.Context, path string, body interface{}, requestMethod string, encodeJSON bool, headers []*Header) (resp *http.Response, err error) {
func (client *Client) submitFormRaw(ctx context.Context, path string, params interface{}, requestMethod string, encodeJSON bool, headers []*Header, body interface{}) (resp *http.Response, err error) {
queryURL := client.serverURL
queryURL.Path += path

var req *http.Request
var bodyReader io.Reader
if body != nil {
if requestMethod == "POST" && rawRequestPaths[path] {
reqBytes, ok := body.([]byte)
if !ok {
return nil, fmt.Errorf("couldn't decode raw body as bytes")
}
bodyReader = bytes.NewBuffer(reqBytes)
} else {
v, err := query.Values(body)
if err != nil {
return nil, err
}

queryURL.RawQuery = mergeRawQueries(queryURL.RawQuery, v.Encode())
if encodeJSON {
jsonValue := json.Encode(body)
bodyReader = bytes.NewBuffer(jsonValue)
}
var v url.Values

if params != nil {
v, err = query.Values(params)
if err != nil {
return nil, err
}
}

if requestMethod == "POST" && rawRequestPaths[path] {
michaeldiamant marked this conversation as resolved.
Show resolved Hide resolved
reqBytes, ok := body.([]byte)
if !ok {
return nil, fmt.Errorf("couldn't decode raw body as bytes")
}
bodyReader = bytes.NewBuffer(reqBytes)
} else if encodeJSON {
jsonValue := json.Encode(params)
bodyReader = bytes.NewBuffer(jsonValue)
}

queryURL.RawQuery = mergeRawQueries(queryURL.RawQuery, v.Encode())

req, err = http.NewRequest(requestMethod, queryURL.String(), bodyReader)
if err != nil {
return nil, err
Expand Down Expand Up @@ -161,8 +162,8 @@ func (client *Client) submitFormRaw(ctx context.Context, path string, body inter
return resp, nil
}

func (client *Client) submitForm(ctx context.Context, response interface{}, path string, body interface{}, requestMethod string, encodeJSON bool, headers []*Header) error {
resp, err := client.submitFormRaw(ctx, path, body, requestMethod, encodeJSON, headers)
func (client *Client) submitForm(ctx context.Context, response interface{}, path string, params interface{}, requestMethod string, encodeJSON bool, headers []*Header, body interface{}) error {
resp, err := client.submitFormRaw(ctx, path, params, requestMethod, encodeJSON, headers, body)
if err != nil {
return err
}
Expand Down Expand Up @@ -192,14 +193,14 @@ func (client *Client) submitForm(ctx context.Context, response interface{}, path
}

// Get performs a GET request to the specific path against the server
func (client *Client) Get(ctx context.Context, response interface{}, path string, body interface{}, headers []*Header) error {
return client.submitForm(ctx, response, path, body, "GET", false /* encodeJSON */, headers)
func (client *Client) Get(ctx context.Context, response interface{}, path string, params interface{}, headers []*Header) error {
return client.submitForm(ctx, response, path, params, "GET", false /* encodeJSON */, headers, nil)
}

// GetRaw performs a GET request to the specific path against the server and returns the raw body bytes.
func (client *Client) GetRaw(ctx context.Context, path string, body interface{}, headers []*Header) (response []byte, err error) {
func (client *Client) GetRaw(ctx context.Context, path string, params interface{}, headers []*Header) (response []byte, err error) {
var resp *http.Response
resp, err = client.submitFormRaw(ctx, path, body, "GET", false /* encodeJSON */, headers)
resp, err = client.submitFormRaw(ctx, path, params, "GET", false /* encodeJSON */, headers, nil)
if err != nil {
return nil, err
}
Expand All @@ -213,8 +214,8 @@ func (client *Client) GetRaw(ctx context.Context, path string, body interface{},
}

// GetRawMsgpack performs a GET request to the specific path against the server and returns the decoded messagepack response.
func (client *Client) GetRawMsgpack(ctx context.Context, response interface{}, path string, body interface{}, headers []*Header) error {
resp, err := client.submitFormRaw(ctx, path, body, "GET", false /* encodeJSON */, headers)
func (client *Client) GetRawMsgpack(ctx context.Context, response interface{}, path string, params interface{}, headers []*Header) error {
resp, err := client.submitFormRaw(ctx, path, params, "GET", false /* encodeJSON */, headers, nil)
if err != nil {
return err
}
Expand All @@ -238,8 +239,8 @@ func (client *Client) GetRawMsgpack(ctx context.Context, response interface{}, p
// Post sends a POST request to the given path with the given body object.
// No query parameters will be sent if body is nil.
// response must be a pointer to an object as post writes the response there.
func (client *Client) Post(ctx context.Context, response interface{}, path string, body interface{}, headers []*Header) error {
return client.submitForm(ctx, response, path, body, "POST", true /* encodeJSON */, headers)
func (client *Client) Post(ctx context.Context, response interface{}, path string, params interface{}, headers []*Header, body interface{}) error {
return client.submitForm(ctx, response, path, params, "POST", true /* encodeJSON */, headers, body)
}

// Helper function for correctly formatting and escaping URL path parameters.
Expand Down
4 changes: 2 additions & 2 deletions client/v2/indexer/indexer.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ func (c *Client) getRaw(ctx context.Context, path string, body interface{}, head
// post sends a POST request to the given path with the given request object.
// No query parameters will be sent if request is nil.
// response must be a pointer to an object as post writes the response there.
func (c *Client) post(ctx context.Context, response interface{}, path string, body interface{}, headers []*common.Header) error {
return (*common.Client)(c).Post(ctx, response, path, body, headers)
func (c *Client) post(ctx context.Context, response interface{}, path string, params interface{}, headers []*common.Header, body interface{}) error {
return (*common.Client)(c).Post(ctx, response, path, params, headers, body)
}

// MakeClient is the factory for constructing a ClientV2 for a given endpoint.
Expand Down
133 changes: 133 additions & 0 deletions logic/source_map.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
package logic

import (
"fmt"
"strings"
)

// SourceMap provides a mapping of the source to assembled program
type SourceMap struct {
Version int `json:"version"`
File string `json:"file,omitempty"`
SourceRoot string `json:"sourceRoot,omitempty"`
Sources []string `json:"sources"`
Names []string `json:"names"`
Mappings string `json:"mappings"`
// Decoded mapping results
LineToPc map[int][]int
PcToLine map[int]int
}

func DecodeSourceMap(ism map[string]interface{}) (SourceMap, error) {
sm := SourceMap{}

if v, ok := ism["version"]; ok {
sm.Version = int(v.(float64))
}

if sm.Version != 3 {
return sm, fmt.Errorf("only version 3 is supported")
}

if f, ok := ism["file"]; ok {
sm.File = f.(string)
}

if sr, ok := ism["sourceRoot"]; ok {
sm.SourceRoot = sr.(string)
}

if srcs, ok := ism["sources"]; ok {
srcSlice := srcs.([]interface{})
sm.Sources = make([]string, len(srcSlice))
for idx, s := range srcSlice {
sm.Sources[idx] = s.(string)
}
}

if names, ok := ism["names"]; ok {
nameSlice := names.([]interface{})
sm.Names = make([]string, len(nameSlice))
for idx, n := range nameSlice {
sm.Names[idx] = n.(string)
}
}

if m, ok := ism["mappings"]; ok {
sm.Mappings = m.(string)
}

if sm.Mappings == "" {
return sm, fmt.Errorf("no mappings defined")
}

sm.PcToLine = map[int]int{}
sm.LineToPc = map[int][]int{}

lastLine := 0
for idx, chunk := range strings.Split(sm.Mappings, ";") {
vals := decodeSourceMapLine(chunk)
// If the vals length >= 3 the lineDelta
if len(vals) >= 3 {
lastLine = lastLine + vals[2] // Add the line delta
}

if _, ok := sm.LineToPc[lastLine]; !ok {
sm.LineToPc[lastLine] = []int{}
}

sm.LineToPc[lastLine] = append(sm.LineToPc[lastLine], idx)
sm.PcToLine[idx] = lastLine
}

return sm, nil
}

func (s *SourceMap) GetLineForPc(pc int) (int, bool) {
line, ok := s.PcToLine[pc]
return line, ok
}

func (s *SourceMap) GetPcsForLine(line int) []int {
return s.LineToPc[line]
}

const (
// consts used for vlq encoding/decoding
b64table string = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
vlqShiftSize = 5
vlqFlag = 1 << vlqShiftSize
vlqMask = vlqFlag - 1
)

func decodeSourceMapLine(vlq string) []int {

var (
results []int
value, shift int
)

for i := 0; i < len(vlq); i++ {
digit := strings.Index(b64table, string(vlq[i]))

value |= (digit & int(vlqMask)) << shift

if digit&vlqFlag > 0 {
shift += vlqShiftSize
continue
}

if value&1 > 0 {
value = (value >> 1) * -1
} else {
value = value >> 1
}

results = append(results, value)

// Reset
value, shift = 0, 0
}

return results
}
1 change: 1 addition & 0 deletions test/algodclientv2_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ func AlgodClientV2Context(s *godog.Suite) {
s.Step(`^we make an Account Information call against account "([^"]*)" with exclude "([^"]*)"$`, weMakeAnAccountInformationCallAgainstAccountWithExclude)
s.Step(`^we make an Account Asset Information call against account "([^"]*)" assetID (\d+)$`, weMakeAnAccountAssetInformationCallAgainstAccountAssetID)
s.Step(`^we make an Account Application Information call against account "([^"]*)" applicationID (\d+)$`, weMakeAnAccountApplicationInformationCallAgainstAccountApplicationID)

s.BeforeScenario(func(interface{}) {
globalErrForExamination = nil
})
Expand Down
Loading