-
Notifications
You must be signed in to change notification settings - Fork 20
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
tools: TypeScript implementation, tests
- Loading branch information
Showing
7 changed files
with
1,164 additions
and
86 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,164 @@ | ||
package parsers | ||
|
||
import ( | ||
"fmt" | ||
"log" | ||
"os" | ||
"path/filepath" | ||
"regexp" | ||
"strings" | ||
|
||
"github.com/oasisprotocol/oasis-sdk/tools/extract-runtime-txs/types" | ||
) | ||
|
||
var TypeScriptWarnings = []error{} | ||
|
||
type TypeScriptParser struct { | ||
filename string | ||
txs map[string]string | ||
txParams map[string]string | ||
txResults map[string]string | ||
} | ||
|
||
func NewTypeScriptParser(filename string) TypeScriptParser { | ||
return TypeScriptParser{ | ||
filename: filename, | ||
txs: map[string]string{}, | ||
txParams: map[string]string{}, | ||
txResults: map[string]string{}, | ||
} | ||
} | ||
|
||
func PopulateTypeScriptRefs(transactions map[string]types.Tx, searchDir string) error { | ||
err := filepath.Walk(searchDir, func(path string, f os.FileInfo, err error) error { | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
if f.IsDir() { | ||
return nil | ||
} | ||
// Ts source files only, ignore types.ts, because we parse it indirectly. | ||
if !strings.HasSuffix(f.Name(), ".ts") || f.Name() == "types.ts" { | ||
return nil | ||
} | ||
tsParser := NewTypeScriptParser(path) | ||
e := tsParser.populateTransactionRefs(transactions) | ||
if e != nil { | ||
return e | ||
} | ||
|
||
return nil | ||
}) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func (p *TypeScriptParser) populateTransactionRefs(txs map[string]types.Tx) error { | ||
text, err := readFile(p.filename) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
// export const METHOD_SOME_METHOD = 'some_module.SomeMethod'; | ||
regMethodMatch, _ := regexp.Compile("export const METHOD_.+ = '([a-zA-Z_\\.]+)'") | ||
// callSomeMethod() { or querySomeMethod() { | ||
regCallQueryMatch, _ := regexp.Compile(" (call|query)(.+)\\(\\) \\{") | ||
// callSomeMethod() { or querySomeMethod() { | ||
regTxTypesMatch, _ := regexp.Compile(" return this\\.(call|query)<(.+), (.+)>") | ||
|
||
// Collect name -> fullName of transactions in this file. | ||
// Collect TxTypeName -> fullName of transaction params and results in the file. | ||
for lineIdx := 0; lineIdx < len(text); lineIdx += 1 { | ||
methodMatch := regMethodMatch.FindStringSubmatch(text[lineIdx]) | ||
if len(methodMatch) > 0 { | ||
fullNameSplit := strings.Split(methodMatch[1], ".") | ||
_, found := txs[methodMatch[1]] | ||
if !found { | ||
TypeScriptWarnings = append(TypeScriptWarnings, fmt.Errorf("unknown method %s in file %s:%d", methodMatch[1], p.filename, lineIdx+1)) | ||
} | ||
p.txs[fullNameSplit[1]] = methodMatch[1] | ||
} | ||
|
||
callQueryMatch := regCallQueryMatch.FindStringSubmatch(text[lineIdx]) | ||
if len(callQueryMatch) == 3 { | ||
fullName, valid := p.txs[callQueryMatch[2]] | ||
if !valid { | ||
TypeScriptWarnings = append(TypeScriptWarnings, fmt.Errorf("implementation of %s not defined as method in the beginning of %s", callQueryMatch[2], p.filename)) | ||
continue | ||
} | ||
if _, valid = txs[fullName]; !valid { | ||
continue | ||
} | ||
|
||
txTypesMatch := regTxTypesMatch.FindStringSubmatch(text[lineIdx+1]) | ||
if len(txTypesMatch) == 4 { | ||
if strings.HasPrefix(txTypesMatch[2], "types.") { | ||
name := strings.TrimPrefix(txTypesMatch[2], "types.") | ||
p.txParams[name] = fullName | ||
} | ||
if strings.HasPrefix(txTypesMatch[3], "types.") { | ||
name := strings.TrimPrefix(txTypesMatch[3], "types.") | ||
p.txResults[name] = fullName | ||
} | ||
} | ||
|
||
_, lineFrom := findComment(text, lineIdx, " ") | ||
lineTo, err := findEndBlock(text, lineIdx, " ", fullName) | ||
if err != nil { | ||
return err | ||
} | ||
txs[fullName].Ref[types.TypeScript] = types.Snippet{ | ||
Path: p.filename, | ||
LineFrom: lineFrom, | ||
LineTo: lineTo, | ||
} | ||
} | ||
} | ||
|
||
// Open types.ts of the same module and collect parameters and result snippets. | ||
if len(p.txs) > 0 { | ||
if err := p.populateParamsResultRefs(txs); err != nil { | ||
return err | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// populateParamsResultRefs opens types.ts file, finds corresponding parameters and results snippets | ||
// for the provided transactions and populates the refs of global transactions. | ||
func (p *TypeScriptParser) populateParamsResultRefs(txs map[string]types.Tx) error { | ||
typesPath := filepath.Join(filepath.Dir(p.filename), "types.ts") | ||
text, err := readFile(typesPath) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
regTypeMatch, _ := regexp.Compile("export interface ([a-zA-Z]+)") | ||
for lineIdx := 0; lineIdx < len(text); lineIdx += 1 { | ||
typeMatch := regTypeMatch.FindStringSubmatch(text[lineIdx]) | ||
if len(typeMatch) > 0 { | ||
_, lineFrom := findComment(text, lineIdx, "") | ||
lineTo, err := findEndBlock(text, lineIdx, "", typeMatch[1]) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
snippet := types.Snippet{ | ||
Path: typesPath, | ||
LineFrom: lineFrom, | ||
LineTo: lineTo, | ||
} | ||
if fullName, valid := p.txParams[typeMatch[1]]; valid { | ||
txs[fullName].ParametersRef[types.TypeScript] = snippet | ||
} else if fullName, valid := p.txResults[typeMatch[1]]; valid { | ||
txs[fullName].ResultRef[types.TypeScript] = snippet | ||
} | ||
} | ||
} | ||
|
||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
package parsers | ||
|
||
import ( | ||
"fmt" | ||
"testing" | ||
|
||
"github.com/oasisprotocol/oasis-sdk/tools/extract-runtime-txs/types" | ||
|
||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
func TestPopulateTypeScriptRefs(t *testing.T) { | ||
require := require.New(t) | ||
|
||
txs := map[string]types.Tx{ | ||
"contracts.Upload": { | ||
Module: "contracts", | ||
Name: "Upload", | ||
Comment: "", | ||
Type: types.Call, | ||
Ref: map[types.Lang]types.Snippet{}, | ||
Parameters: []types.Parameter{}, | ||
ParametersRef: make(map[types.Lang]types.Snippet), | ||
Result: []types.Parameter{}, | ||
ResultRef: map[types.Lang]types.Snippet{}, | ||
}, | ||
} | ||
|
||
tsParser := NewTypeScriptParser("../tests/typescript/contracts.ts") | ||
err := tsParser.populateTransactionRefs(txs) | ||
require.NoError(err) | ||
fmt.Println(txs["contracts.Upload"].Ref) | ||
require.Equal( | ||
types.Snippet{ | ||
Path: "../tests/typescript/contracts.ts", | ||
LineFrom: 50, | ||
LineTo: 52, | ||
}, | ||
txs["contracts.Upload"].Ref[types.TypeScript], | ||
"check implementation reference from TypeScript source file", | ||
) | ||
require.Equal( | ||
types.Snippet{ | ||
Path: "../tests/typescript/types.ts", | ||
LineFrom: 474, | ||
LineTo: 490, | ||
}, | ||
txs["contracts.Upload"].ParametersRef[types.TypeScript], | ||
"check parameters reference from TypeScript source file", | ||
) | ||
require.Equal( | ||
types.Snippet{ | ||
Path: "../tests/typescript/types.ts", | ||
LineFrom: 492, | ||
LineTo: 500, | ||
}, | ||
txs["contracts.Upload"].ResultRef[types.TypeScript], | ||
"check result reference from TypeScript source file", | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
import * as transaction from './transaction'; | ||
import * as types from './types'; | ||
import * as wrapper from './wrapper'; | ||
|
||
/** | ||
* Unique module name. | ||
*/ | ||
export const MODULE_NAME = 'contracts'; | ||
|
||
export const ERR_INVALID_ARGUMENT_CODE = 1; | ||
export const ERR_CODE_TOO_LARGE_CODE = 2; | ||
export const ERR_CODE_MALFORMED_CODE = 3; | ||
export const ERR_UNSUPPORTED_ABI_CODE = 4; | ||
export const ERR_CODE_MISSING_REQUIRED_EXPORT_CODE = 5; | ||
export const ERR_CODE_DECLARES_RESERVED_EXPORT_CODE = 6; | ||
export const ERR_CODE_DECLARES_START_FUNCTION_CODE = 7; | ||
export const ERR_CODE_DECLARES_TOO_MANY_MEMORIES_CODE = 8; | ||
export const ERR_CODE_NOT_FOUND_CODE = 9; | ||
export const ERR_INSTANCE_NOT_FOUND_CODE = 10; | ||
export const ERR_MODULE_LOADING_FAILED_CODE = 11; | ||
export const ERR_EXECUTION_FAILED_CODE = 12; | ||
export const ERR_FORBIDDEN_CODE = 13; | ||
export const ERR_UNSUPPORTED_CODE = 14; | ||
export const ERR_INSUFFICIENT_CALLER_BALANCE_CODE = 15; | ||
export const ERR_CALL_DEPTH_EXCEEDED_CODE = 16; | ||
export const ERR_RESULT_TOO_LARGE_CODE = 17; | ||
export const ERR_TOO_MANY_SUBCALLS_CODE = 18; | ||
export const ERR_CODE_ALREADY_UPGRADED_CODE = 19; | ||
|
||
// Callable methods. | ||
export const METHOD_UPLOAD = 'contracts.Upload'; | ||
export const METHOD_INSTANTIATE = 'contracts.Instantiate'; | ||
export const METHOD_CALL = 'contracts.Call'; | ||
export const METHOD_UPGRADE = 'contracts.Upgrade'; | ||
// Queries. | ||
export const METHOD_CODE = 'contracts.Code'; | ||
export const METHOD_INSTANCE = 'contracts.Instance'; | ||
export const METHOD_INSTANCE_STORAGE = 'contracts.InstanceStorage'; | ||
export const METHOD_PUBLIC_KEY = 'contracts.PublicKey'; | ||
export const METHOD_CUSTOM = 'contracts.Custom'; | ||
|
||
// Public key kind. | ||
export const PUBLIC_KEY_KIND_TRANSACTION = 1; | ||
|
||
export class Wrapper extends wrapper.Base { | ||
constructor(runtimeID: Uint8Array) { | ||
super(runtimeID); | ||
} | ||
|
||
callUpload() { | ||
return this.call<types.ContractsUpload, types.ContractsUploadResult>(METHOD_UPLOAD); | ||
} | ||
} | ||
|
||
/** | ||
* Use this as a part of a {@link transaction.CallHandlers}. | ||
*/ | ||
export type TransactionCallHandlers = { | ||
[METHOD_UPLOAD]?: transaction.CallHandler<types.ContractsUpload>; | ||
[METHOD_INSTANTIATE]?: transaction.CallHandler<types.ContractsInstantiate>; | ||
[METHOD_CALL]?: transaction.CallHandler<types.ContractsCall>; | ||
[METHOD_UPGRADE]?: transaction.CallHandler<types.ContractsUpgrade>; | ||
}; |
Oops, something went wrong.