diff --git a/CHANGELOG.md b/CHANGELOG.md index 6ebd2e243..d51bccf67 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,13 +2,14 @@ ### v0.7.1 (NOT YET RELEASED) * Additions - * ... + * Added a function that prints information about the heap for debugging purposes (`debugHeap` in cx/utilities.go). * Changes * ... * Libraries * ... * Fixed issues - * ... + * #286: Compilation error when struct field is named 'input' or 'output' + * #323: Installation issues on Windows after merging #320 * Documentation * ... * Miscellaneous diff --git a/Gopkg.lock b/Gopkg.lock index 42c463990..e324d19f1 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -295,6 +295,17 @@ pruneopts = "UT" revision = "b86a26479c3e25ac0f13e195bd2c0554bcf2f837" +[[projects]] + branch = "master" + digest = "1:a1aa53cb80d9a13149672fb12151b85f6f7cf9acc5a95647fbb9963f2ae961b4" + name = "github.com/sn-srdjan/cx-tracker-cli" + packages = [ + "src/api", + "src/cli/provider", + ] + pruneopts = "UT" + revision = "ea7ea4c8f02584620dcc13523f5b9050e6b1d1f9" + [[projects]] digest = "1:bb495ec276ab82d3dd08504bbc0594a65de8c3b22c6f2aaa92d05b73fbf3a82e" name = "github.com/spf13/afero" @@ -449,6 +460,7 @@ "github.com/amherag/skycoin/src/wallet", "github.com/satori/go.uuid", "github.com/skycoin/gltext", + "github.com/sn-srdjan/cx-tracker-cli/src/cli/provider", "github.com/theherk/viper", "github.com/urfave/cli", ] diff --git a/Gopkg.toml b/Gopkg.toml index 08e253e75..6ed1336f0 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -53,3 +53,7 @@ ignored = ["github.com/go-gl/*"] [prune] go-tests = true unused-packages = true + +[[constraint]] + branch = "master" + name = "github.com/sn-srdjan/cx-tracker-cli" diff --git a/cx-setup.bat b/cx-setup.bat index 80000d923..a5aa2b043 100644 --- a/cx-setup.bat +++ b/cx-setup.bat @@ -109,7 +109,7 @@ if %ERRORLEVEL% neq 0 ( rem run CX tests -%BIN_PATH%\cx.exe %SKYCOIN_PATH%\cx\tests +%BIN_PATH%\cx.exe %SKYCOIN_PATH%\cx\tests\main.cx ++wdir=tests\ ++disable-tests=gui,issue call :showResults cx\tests "Tested" "ERROR while testing" call :echoWithColor yellow "You can re-run CX tests with:" @@ -235,10 +235,10 @@ exit /b %BIN_PATH%\goyacc -o %CXGO_PATH%\cxgo0\cxgo0.go %CXGO_PATH%\cxgo0\cxgo0.y call :showResults "goyacc cxgo0" "1st pass -" "ERROR in 1st pass -" - %BIN_PATH%\nex -e %CXGO_PATH%\cxgo.nex + %BIN_PATH%\nex -e %CXGO_PATH%\parser\cxgo.nex call :showResults "nex cxgo" "2nd pass -" "ERROR in 2nd pass -" - %BIN_PATH%\goyacc -o %CXGO_PATH%\cxgo.go %CXGO_PATH%\cxgo.y + %BIN_PATH%\goyacc -o %CXGO_PATH%\parser\cxgo.go %CXGO_PATH%\parser\cxgo.y call :showResults "goyacc cxgo" "2nd pass -" "ERROR in 2nd pass -" diff --git a/cx.sh b/cx.sh index 05cc82708..3ad23818f 100755 --- a/cx.sh +++ b/cx.sh @@ -154,13 +154,13 @@ if [ ! $? -eq 0 ]; then exit 0 fi -$INSTALLATION_PATH/bin/nex -e $INSTALLATION_PATH/src/github.com/skycoin/cx/cxgo/cxgo.nex +$INSTALLATION_PATH/bin/nex -e $INSTALLATION_PATH/src/github.com/skycoin/cx/cxgo/parser/cxgo.nex if [ ! $? -eq 0 ]; then echo "FAIL:\tThere was a problem compiling CX's lexical analyzer" exit 0 fi -$INSTALLATION_PATH/bin/goyacc -o $INSTALLATION_PATH/src/github.com/skycoin/cx/cxgo/cxgo.go $INSTALLATION_PATH/src/github.com/skycoin/cx/cxgo/cxgo.y +$INSTALLATION_PATH/bin/goyacc -o $INSTALLATION_PATH/src/github.com/skycoin/cx/cxgo/parser/cxgo.go $INSTALLATION_PATH/src/github.com/skycoin/cx/cxgo/parser/cxgo.y if [ ! $? -eq 0 ]; then echo "FAIL:\tThere was a problem compiling CX's parser" exit 0 diff --git a/cx/utilities.go b/cx/utilities.go index b1a9ed160..11ba97592 100644 --- a/cx/utilities.go +++ b/cx/utilities.go @@ -6,6 +6,7 @@ import ( "os" "runtime/debug" "strconv" + "text/tabwriter" "github.com/amherag/skycoin/src/cipher/encoder" ) @@ -994,3 +995,85 @@ func mustDeserializeRaw(byts []byte, item interface{}) { panic(err) } } + +// DebugHeap prints the symbols that are acting as pointers in a CX program at certain point during the execution of the program along with the addresses they are pointing. Additionally, a list of the objects in the heap is printed, which shows their address in the heap, if they are marked as alive or as dead by the garbage collector, the address where they used to live after a garbage collector call, the full size of the object, the object itself as a slice of bytes and the pointers that are pointing to that object. +func DebugHeap() { + // symsToAddrs will hold a list of symbols that are pointing to an address. + symsToAddrs := make(map[int32][]string) + + // Processing global variables. Adding the address they are pointing to. + for _, pkg := range PROGRAM.Packages { + for _, glbl := range pkg.Globals { + if glbl.IsPointer || glbl.IsSlice { + var heapOffset int32 + _, err := encoder.DeserializeAtomic(PROGRAM.Memory[glbl.Offset:glbl.Offset+TYPE_POINTER_SIZE], &heapOffset) + if err != nil { + panic(err) + } + + symsToAddrs[heapOffset] = append(symsToAddrs[heapOffset], glbl.Name) + } + } + } + + // Processing local variables in every active function call in the `CallStack`. + // Adding the address they are pointing to. + var fp int + for c := 0; c <= PROGRAM.CallCounter; c++ { + op := PROGRAM.CallStack[c].Operator + + // TODO: Some standard library functions "manually" add a function + // call (callbacks) to `PRGRM.CallStack`. These functions do not have an + // operator associated to them. This can be considered as a bug or as an + // undesirable mechanic. + // [2019-06-24 Mon 22:39] Actually, if the GC is triggered in the middle + // of a callback, things will certainly break. + if op == nil { + continue + } + + for _, ptr := range op.ListOfPointers { + var heapOffset int32 + _, err := encoder.DeserializeAtomic(PROGRAM.Memory[fp+ptr.Offset:fp+ptr.Offset+TYPE_POINTER_SIZE], &heapOffset) + if err != nil { + panic(err) + } + + symsToAddrs[heapOffset] = append(symsToAddrs[heapOffset], ptr.Name) + } + + fp += op.Size + } + + // Printing all the details. + w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, '.', 0) + + for off, symNames := range symsToAddrs { + addrB := encoder.Serialize(off) + fmt.Fprintln(w, "Addr:\t", addrB, "\tPtr:\t", symNames) + } + + // Just a newline. + fmt.Fprintln(w) + w.Flush() + + w = tabwriter.NewWriter(os.Stdout, 0, 0, 2, '.', 0) + + for c := PROGRAM.HeapStartsAt + NULL_HEAP_ADDRESS_OFFSET; c < PROGRAM.HeapStartsAt+PROGRAM.HeapPointer; { + var objSize int32 + _, err := encoder.DeserializeAtomic(PROGRAM.Memory[c+MARK_SIZE+FORWARDING_ADDRESS_SIZE:c+MARK_SIZE+FORWARDING_ADDRESS_SIZE+OBJECT_SIZE], &objSize) + if err != nil { + panic(err) + } + + addrB := encoder.Serialize(int32(c)) + + fmt.Fprintln(w, "Addr:\t", addrB, "\tMark:\t", PROGRAM.Memory[c:c+MARK_SIZE], "\tFwd:\t", PROGRAM.Memory[c+MARK_SIZE:c+MARK_SIZE+FORWARDING_ADDRESS_SIZE], "\tSize:\t", objSize, "\tObj:\t", PROGRAM.Memory[c+OBJECT_HEADER_SIZE:c+int(objSize)], "\tPtrs:", symsToAddrs[int32(c)]) + + c += int(objSize) + } + + // Just a newline. + fmt.Fprintln(w) + w.Flush() +} diff --git a/cxgo/cxgo0/cxgo0.nex b/cxgo/cxgo0/cxgo0.nex index a7eac7b6a..c85d13ffb 100644 --- a/cxgo/cxgo0/cxgo0.nex +++ b/cxgo/cxgo0/cxgo0.nex @@ -121,8 +121,6 @@ /clauses/ { return f(CLAUSES) } /def/ { return f(DEF) } /field/ { return f(FIELD) } -/input/ { return f(INPUT) } -/output/ { return f(OUTPUT) } /import/ { return f(IMPORT) } /var/ { return f(VAR) } /"([^"]*)"/ { /* " */ diff --git a/cxgo/cxgo0/cxgo0.y b/cxgo/cxgo0/cxgo0.y index c47910dd6..5d2c97be4 100644 --- a/cxgo/cxgo0/cxgo0.y +++ b/cxgo/cxgo0/cxgo0.y @@ -98,7 +98,7 @@ /* Selectors */ SPACKAGE SSTRUCT SFUNC /* Removers */ - REM DEF EXPR FIELD INPUT OUTPUT CLAUSES OBJECT OBJECTS + REM DEF EXPR FIELD CLAUSES OBJECT OBJECTS /* Stepping */ STEP PSTEP TSTEP /* Debugging */ diff --git a/cxgo/main.go b/cxgo/main.go index 5e0073dff..db7898aab 100644 --- a/cxgo/main.go +++ b/cxgo/main.go @@ -1,57 +1,63 @@ package main import ( - "os" - "os/user" - "os/exec" - "fmt" + "bufio" "bytes" - "time" + "encoding/json" + "fmt" "io" "io/ioutil" - "strings" + "os" + "os/exec" + "os/user" "strconv" - "bufio" - "encoding/json" + "strings" + "time" + // "encoding/hex" "runtime" "regexp" - + "flag" "path/filepath" "net" "net/http" - + . "github.com/skycoin/cx/cx" - . "github.com/skycoin/cx/cxgo/parser" . "github.com/skycoin/cx/cxgo/actions" "github.com/skycoin/cx/cxgo/cxgo0" + . "github.com/skycoin/cx/cxgo/parser" "github.com/theherk/viper" "github.com/amherag/skycoin/src/cipher" // "github.com/amherag/skycoin/src/cipher/encoder" + "github.com/amherag/skycoin/src/api" + "github.com/amherag/skycoin/src/cli" "github.com/amherag/skycoin/src/coin" + "github.com/amherag/skycoin/src/fiber" "github.com/amherag/skycoin/src/readable" "github.com/amherag/skycoin/src/skycoin" - "github.com/amherag/skycoin/src/fiber" "github.com/amherag/skycoin/src/util/logging" "github.com/amherag/skycoin/src/visor" - "github.com/amherag/skycoin/src/api" - "github.com/amherag/skycoin/src/cli" "github.com/amherag/skycoin/src/wallet" + "github.com/sn-srdjan/cx-tracker-cli/src/cli/provider" + "errors" ) const VERSION = "0.7.0" +const configPath = "./cx-config.json" + var ( log = logging.MustGetLogger("newcoin") apiClient = &http.Client{Timeout: 10 * time.Second} + cxTracker = provider.TrackerProvider{} genesisBlockURL = "http://127.0.0.1:%d/api/v1/block?seq=0" ) @@ -74,9 +80,9 @@ func getJSON(url string, target interface{}) error { return json.NewDecoder(r.Body).Decode(target) } -func initCXBlockchain (initPrgrm []byte, coinname, seckey string) error { +func initCXBlockchain(initPrgrm []byte, coinname, seckey string) error { var err error - + // check that data.db does not exist // if it does, delete it userHome := UserHome() @@ -155,7 +161,7 @@ func initCXBlockchain (initPrgrm []byte, coinname, seckey string) error { // get genesis block err = getJSON(fmt.Sprintf(genesisBlockURL, params.Node.WebInterfacePort), &genesisBlock) - + return } } @@ -266,7 +272,7 @@ func runNode(mode string, options cxCmdFlags) *exec.Cmd { switch mode { case "publisher": return exec.Command("cxcoin", "-enable-all-api-sets", - "-block-publisher=true", + "-block-publisher=true", "-localhost-only", "-disable-default-peers", "-custom-peers-file=localhost-peers.txt", @@ -277,31 +283,31 @@ func runNode(mode string, options cxCmdFlags) *exec.Cmd { fmt.Sprintf("-genesis-signature=%s", options.genesisSignature), fmt.Sprintf("-blockchain-public-key=%s", options.pubKey), "-max-txn-size-unconfirmed=134217728", - "-max-txn-size-create-block=134217728", + "-max-txn-size-create-block=134217728", "-max-block-size=134217728", "-max-in-msg-len=134217929", "-max-out-msg-len=134217929", // I don't know why this value, but the logger stated a value >= than this is needed ) case "peer": - return exec.Command("cxcoin", "-enable-all-api-sets", - "-localhost-only", - "-disable-default-peers", - "-custom-peers-file=localhost-peers.txt", - "-download-peerlist=false", - "-launch-browser=false", - fmt.Sprintf("-genesis-address=%s", options.genesisAddress), - fmt.Sprintf("-genesis-signature=%s", options.genesisSignature), - fmt.Sprintf("-blockchain-public-key=%s", options.pubKey), - // "-web-interface-port=$(expr $2 + 420)", - fmt.Sprintf("-web-interface-port=%d", options.port + 420), - fmt.Sprintf("-port=%d", options.port), - fmt.Sprintf("-data-dir=/tmp/%d", options.port), - "-max-txn-size-unconfirmed=134217728", - "-max-txn-size-create-block=134217728", - "-max-block-size=134217728", - "-max-in-msg-len=134217929", - "-max-out-msg-len=134217929", // I don't know why this value, but the logger stated a value >= than this is needed - ) + return exec.Command("cxcoin", "-enable-all-api-sets", + "-localhost-only", + "-disable-default-peers", + "-custom-peers-file=localhost-peers.txt", + "-download-peerlist=false", + "-launch-browser=false", + fmt.Sprintf("-genesis-address=%s", options.genesisAddress), + fmt.Sprintf("-genesis-signature=%s", options.genesisSignature), + fmt.Sprintf("-blockchain-public-key=%s", options.pubKey), + // "-web-interface-port=$(expr $2 + 420)", + fmt.Sprintf("-web-interface-port=%d", options.port+420), + fmt.Sprintf("-port=%d", options.port), + fmt.Sprintf("-data-dir=/tmp/%d", options.port), + "-max-txn-size-unconfirmed=134217728", + "-max-txn-size-create-block=134217728", + "-max-block-size=134217728", + "-max-in-msg-len=134217929", + "-max-out-msg-len=134217929", // I don't know why this value, but the logger stated a value >= than this is needed + ) default: return nil } @@ -320,7 +326,7 @@ func main() { flag.Parse() // Propagate some options out to other packages - DebugLexer = options.debugLexer // in package parser + DebugLexer = options.debugLexer // in package parser if options.publisherMode || options.peerMode { var cmd *exec.Cmd @@ -352,6 +358,24 @@ func main() { log.Error(scanner.Text()) } }() + + //TODO use startup flag instead of file existence here + if _, err := os.Stat(configPath); err == nil { + log.Info("Initiating up CX Tracker ping") + pingTracker() // TODO should we ping on startup or after first period of 5 min is over and we're sure instance is running correctly? + trackerTicker := time.NewTicker(5 * time.Minute) + go func() { + defer trackerTicker.Stop() + + for { + select { + case <-trackerTicker.C: + pingTracker() + } + } + + }() + } } // Generate a CX chain address. @@ -360,7 +384,7 @@ func main() { seed := cli.MakeAlphanumericSeed() wltOpts := wallet.Options{ Label: "cxcoin", - Seed: seed, + Seed: seed, } // Generate temporary wallet. @@ -381,7 +405,7 @@ func main() { return } - + if options.walletMode { if options.walletSeed == "" { fmt.Println("creating a wallet requires a seed provided with --wallet-seed") @@ -396,9 +420,9 @@ func main() { wltOpts := wallet.Options{ Label: "cxcoin", - Seed: options.walletSeed, + Seed: options.walletSeed, } - + wlt, err := cli.GenerateWallet(options.walletId, wltOpts, 1) if err != nil { panic(err) @@ -416,10 +440,10 @@ func main() { // Printing JSON with wallet information fmt.Println(string(wltJSON)) - + return } - + if options.printHelp { printHelp() return @@ -499,7 +523,7 @@ func main() { w = os.Stdout } else { tokenFilename := options.compileOutput - w, err = os. Create(tokenFilename) + w, err = os.Create(tokenFilename) if err != nil { fmt.Fprintln(os.Stderr, "Error writing:", tokenFilename, err) return @@ -537,12 +561,12 @@ func main() { sPrgrmSH = append(sPrgrmSH, sPrgrm[memOff:]...) // Appending new heap sPrgrmSH = append(sPrgrmSH, make([]byte, INIT_HEAP_SIZE)...) - + // Deserialize(sPrgrm).RunCompiled(0, cxArgs) // bcPrgrm = Deserialize(sPrgrm) // prevMemSize := len(PRGRM.Memory) PRGRM = Deserialize(sPrgrmSH) - // // Adding + // // Adding // PRGRM.Memory = append(make([]byte, STACK_SIZE), PRGRM.Memory...) // PRGRM.Memory // PRGRM.Memory = append(PRGRM.Memory, make([]byte, prevMemSize - len(PRGRM.Memory))...) @@ -567,26 +591,26 @@ func main() { var prePkg *CXPackage parseErrors := 0 - + // We need to traverse the elements by hierarchy first add all the // packages and structs at the same time then add globals, as these // can be of a custom type (and it could be imported) the signatures // of functions and methods are added in the cxgo0.y pass if len(sourceCode) > 0 { - - reMultiCommentOpen := regexp.MustCompile(`/\*`) + + reMultiCommentOpen := regexp.MustCompile(`/\*`) reMultiCommentClose := regexp.MustCompile(`\*/`) reComment := regexp.MustCompile("//") - rePkg := regexp.MustCompile("package") - rePkgName := regexp.MustCompile("(^|[\\s])package\\s+([_a-zA-Z][_a-zA-Z0-9]*)") - reStrct := regexp.MustCompile("type") + rePkg := regexp.MustCompile("package") + rePkgName := regexp.MustCompile("(^|[\\s])package\\s+([_a-zA-Z][_a-zA-Z0-9]*)") + reStrct := regexp.MustCompile("type") reStrctName := regexp.MustCompile("(^|[\\s])type\\s+([_a-zA-Z][_a-zA-Z0-9]*)?\\s") - reGlbl := regexp.MustCompile("var") + reGlbl := regexp.MustCompile("var") reGlblName := regexp.MustCompile("(^|[\\s])var\\s([_a-zA-Z][_a-zA-Z0-9]*)") - reBodyOpen := regexp.MustCompile("{") + reBodyOpen := regexp.MustCompile("{") reBodyClose := regexp.MustCompile("}") // 1. Identify all the packages and structs @@ -628,9 +652,9 @@ func main() { } if match := rePkgName.FindStringSubmatch(string(line)); match != nil { - if pkg, err := cxgo0.PRGRM0.GetPackage(match[len(match) - 1]); err != nil { + if pkg, err := cxgo0.PRGRM0.GetPackage(match[len(match)-1]); err != nil { // then it hasn't been added - newPkg := MakePackage(match[len(match) - 1]) + newPkg := MakePackage(match[len(match)-1]) cxgo0.PRGRM0.AddPackage(newPkg) prePkg = newPkg } else { @@ -651,10 +675,10 @@ func main() { if match := reStrctName.FindStringSubmatch(string(line)); match != nil { if prePkg == nil { println(CompilationError(filename, lineno), - "No package defined") - } else if _, err := cxgo0.PRGRM0.GetStruct(match[len(match) - 1], prePkg.Name); err != nil { + "No package defined") + } else if _, err := cxgo0.PRGRM0.GetStruct(match[len(match)-1], prePkg.Name); err != nil { // then it hasn't been added - strct := MakeStruct(match[len(match) - 1]) + strct := MakeStruct(match[len(match)-1]) prePkg.AddStruct(strct) } } @@ -673,12 +697,12 @@ func main() { scanner := bufio.NewScanner(strings.NewReader(source)) for scanner.Scan() { line := scanner.Bytes() - + // we need to ignore function bodies // it'll also ignore struct declaration's bodies, but this doesn't matter commentLoc := reComment.FindIndex(line) - - multiCommentOpenLoc := reMultiCommentOpen.FindIndex(line) + + multiCommentOpenLoc := reMultiCommentOpen.FindIndex(line) multiCommentCloseLoc := reMultiCommentClose.FindIndex(line) if commentedCode && multiCommentCloseLoc != nil { @@ -702,18 +726,18 @@ func main() { // then it's commented out continue } - + if match := rePkgName.FindStringSubmatch(string(line)); match != nil { - if pkg, err := cxgo0.PRGRM0.GetPackage(match[len(match) - 1]); err != nil { + if pkg, err := cxgo0.PRGRM0.GetPackage(match[len(match)-1]); err != nil { // then it hasn't been added - prePkg = MakePackage(match[len(match) - 1]) + prePkg = MakePackage(match[len(match)-1]) cxgo0.PRGRM0.AddPackage(prePkg) } else { prePkg = pkg } } } - + if locs := reBodyOpen.FindAllIndex(line, -1); locs != nil { for _, loc := range locs { if !(multiCommentCloseLoc != nil && multiCommentCloseLoc[0] > loc[0]) { @@ -745,7 +769,7 @@ func main() { // we could have this situation: {var local i32} // but we don't care about this, as the later passes will throw an error as it's invalid syntax - + if loc := rePkg.FindIndex(line); loc != nil { if (commentLoc != nil && commentLoc[0] < loc[0]) || (multiCommentOpenLoc != nil && multiCommentOpenLoc[0] < loc[0]) || @@ -753,9 +777,9 @@ func main() { // then it's commented out continue } - + if match := rePkgName.FindStringSubmatch(string(line)); match != nil { - if pkg, err := cxgo0.PRGRM0.GetPackage(match[len(match) - 1]); err != nil { + if pkg, err := cxgo0.PRGRM0.GetPackage(match[len(match)-1]); err != nil { // it should be already present panic(err) } else { @@ -775,9 +799,9 @@ func main() { } if match := reGlblName.FindStringSubmatch(string(line)); match != nil { - if _, err := prePkg.GetGlobal(match[len(match) - 1]); err != nil { + if _, err := prePkg.GetGlobal(match[len(match)-1]); err != nil { // then it hasn't been added - arg := MakeArgument(match[len(match) - 1], "", 0) + arg := MakeArgument(match[len(match)-1], "", 0) arg.Offset = -1 arg.Package = prePkg prePkg.AddGlobal(arg) @@ -811,7 +835,6 @@ func main() { arg1 = DeclarationSpecifiers(arg1, 0, DECL_BASIC) arg1 = DeclarationSpecifiers(arg1, 0, DECL_SLICE) - DeclareGlobalInPackage(osPkg, arg0, arg1, nil, false) } @@ -847,7 +870,7 @@ func main() { // adding *init function that initializes all the global variables addInitFunction(PRGRM) - + LineNo = 0 if FoundCompileErrors { @@ -863,28 +886,28 @@ func main() { if err != nil { panic(err) } - + PRGRM.RemovePackage(MAIN_FUNC) - + s := Serialize(PRGRM, 1) s = ExtractBlockchainProgram(s, s) configDir := os.Getenv("GOPATH") + "/src/github.com/skycoin/cx/" configFile := "fiber" - + cmd := exec.Command("go", "install", "./cmd/newcoin/...") cmd.Start() cmd.Wait() - + cmd = exec.Command("newcoin", "createcoin", fmt.Sprintf("--coin=%s", options.programName), fmt.Sprintf("--template-dir=%s%s", os.Getenv("GOPATH"), "/src/github.com/skycoin/cx/template"), - "--config-file=" + configFile + ".toml", - "--config-dir=" + configDir, + "--config-file="+configFile+".toml", + "--config-dir="+configDir, ) cmd.Start() cmd.Wait() - + cmd = exec.Command("go", "install", "./cmd/cxcoin/...") cmd.Start() cmd.Wait() @@ -896,20 +919,20 @@ func main() { fmt.Println("\ngenesis signature:", genesisSignature) viper.SetConfigName(configFile) // name of config file (without extension) - viper.AddConfigPath(".") // optionally look for config in the working directory - err = viper.ReadInConfig() // Find and read the config file - if err != nil { // Handle errors reading the config file + viper.AddConfigPath(".") // optionally look for config in the working directory + err = viper.ReadInConfig() // Find and read the config file + if err != nil { // Handle errors reading the config file panic(err) } viper.Set("node.genesis_signature_str", genesisSignature) viper.WriteConfig() - + cmd = exec.Command("newcoin", "createcoin", fmt.Sprintf("--coin=%s", options.programName), fmt.Sprintf("--template-dir=%s%s", os.Getenv("GOPATH"), "/src/github.com/skycoin/cx/template"), - "--config-file=" + configFile + ".toml", - "--config-dir=" + configDir, + "--config-file="+configFile+".toml", + "--config-dir="+configDir, ) cmd.Start() cmd.Wait() @@ -924,20 +947,20 @@ func main() { dsPrgrm := Deserialize(sPrgrm) PRGRM.StackPointer = 0 PRGRM.HeapPointer = dsPrgrm.HeapPointer - + s := Serialize(PRGRM, 1) txnCode := ExtractTransactionProgram(sPrgrm, s) // All these HTTP requests need to be dropped in favor of calls to calls to functions // from the `cli` or `api` Skycoin packages - addr := fmt.Sprintf("http://127.0.0.1:%d", options.port + 420) + addr := fmt.Sprintf("http://127.0.0.1:%d", options.port+420) skycoinClient := api.NewClient(addr) csrfToken, err := skycoinClient.CSRF() if err != nil { panic(err) } - url := fmt.Sprintf("http://127.0.0.1:%d/api/v1/wallet/transaction", options.port + 420) + url := fmt.Sprintf("http://127.0.0.1:%d/api/v1/wallet/transaction", options.port+420) var dataMap map[string]interface{} dataMap = make(map[string]interface{}, 0) @@ -947,7 +970,6 @@ func main() { dataMap["wallet_id"] = string(options.walletId) dataMap["to"] = []interface{}{map[string]string{"address": "2PBcLADETphmqWV7sujRZdh3UcabssgKAEB", "coins": "1", "hours": "0"}} - jsonStr, err := json.Marshal(dataMap) if err != nil { panic(err) @@ -976,7 +998,7 @@ func main() { return } - url = fmt.Sprintf("http://127.0.0.1:%d/api/v1/injectTransaction", options.port + 420) + url = fmt.Sprintf("http://127.0.0.1:%d/api/v1/injectTransaction", options.port+420) dataMap = make(map[string]interface{}, 0) dataMap["rawtx"] = respBody["encoded_transaction"] @@ -1004,12 +1026,12 @@ func main() { if err != nil { panic(err) } - + if AssertFailed() { os.Exit(CX_ASSERT) } } - + if BaseOutput { //PRGRM.Compile(true) } @@ -1023,12 +1045,11 @@ func main() { } } - // Used for the -heap-initial, -heap-max and -stack-size flags. // This function parses, for example, "1M" to 1048576 (the corresponding number of bytes) // Possible suffixes are: G or g (gigabytes), M or m (megabytes), K or k (kilobytes) -func parseMemoryString (s string) int { - suffix := s[len(s) - 1] +func parseMemoryString(s string) int { + suffix := s[len(s)-1] _, notSuffix := strconv.ParseFloat(string(suffix), 64) if notSuffix == nil { @@ -1049,7 +1070,7 @@ func parseMemoryString (s string) int { // malformed size return -1 } - + switch suffix { case 'G': return int(num * 1073741824) @@ -1065,20 +1086,20 @@ func parseMemoryString (s string) int { // var PRGRM *CXProgram -func unsafeEval (code string) (out string) { +func unsafeEval(code string) (out string) { var lexer *Lexer defer func() { if r := recover(); r != nil { out = fmt.Sprintf("%v", r) lexer.Stop() - } + } }() - + // storing strings sent to standard output old := os.Stdout r, w, _ := os.Pipe() os.Stdout = w - + LineNo = 0 PRGRM = MakeProgram() @@ -1105,7 +1126,7 @@ func unsafeEval (code string) (out string) { io.Copy(&buf, r) outC <- buf.String() }() - + w.Close() os.Stdout = old // restoring the real stdout out = <-outC @@ -1114,12 +1135,12 @@ func unsafeEval (code string) (out string) { return out } -func Eval (code string) string { +func Eval(code string) string { runtime.GOMAXPROCS(2) ch := make(chan string, 1) var result string - + go func() { result = unsafeEval(code) ch <- result @@ -1141,11 +1162,11 @@ type SourceCode struct { Code string } -func ServiceMode () { +func ServiceMode() { host := ":5336" mux := http.NewServeMux() - + mux.Handle("/", http.FileServer(http.Dir("./dist"))) mux.HandleFunc("/eval", func(w http.ResponseWriter, r *http.Request) { defer r.Body.Close() @@ -1155,7 +1176,7 @@ func ServiceMode () { http.Error(w, err.Error(), 500) return } - + var source SourceCode if err := json.Unmarshal(b, &source); err != nil { http.Error(w, err.Error(), 500) @@ -1163,7 +1184,7 @@ func ServiceMode () { } if err := r.ParseForm(); err == nil { - fmt.Fprintf(w, "%s", Eval(source.Code + "\n")) + fmt.Fprintf(w, "%s", Eval(source.Code+"\n")) } }) @@ -1196,14 +1217,14 @@ func PersistentServiceMode() { for { var inp string var ok bool - + printPrompt() if inp, ok = readline(fi); ok { if isJSON(inp) { var err error client := &http.Client{} - body := bytes.NewBufferString(inp) + body := bytes.NewBufferString(inp) req, err := http.NewRequest("GET", "http://127.0.0.1:5336/eval", body) if err != nil { fmt.Println(err) @@ -1220,19 +1241,19 @@ func PersistentServiceMode() { } } -func getWorkingDirectory (file string) string { +func getWorkingDirectory(file string) string { file = filepath.FromSlash(file) var c int = len(file) - 1 for ; c > 0; c-- { - if file[c - 1] == os.PathSeparator { + if file[c-1] == os.PathSeparator { break } } - + return file[:c] } -func printPrompt () { +func printPrompt() { if ReplTargetMod != "" { fmt.Println(fmt.Sprintf(":package %s ...", ReplTargetMod)) fmt.Printf("* ") @@ -1247,7 +1268,7 @@ func printPrompt () { } } -func repl () { +func repl() { fmt.Println("CX", VERSION) fmt.Println("More information about CX is available at http://cx.skycoin.net/ and https://github.com/skycoin/cx/") @@ -1262,7 +1283,7 @@ func repl () { var ok bool printPrompt() - + if inp, ok = readline(fi); ok { if ReplTargetFn != "" { inp = fmt.Sprintf(":func %s {\n%s\n}\n", ReplTargetFn, inp) @@ -1300,22 +1321,22 @@ func repl () { } } -func initNewProject () { +func initNewProject() { var name string - + reader := bufio.NewReader(os.Stdin) fmt.Print("Name of the project: ") name, _ = reader.ReadString('\n') fmt.Printf("Creating project %s%s/", SRCPATH, name) - os.MkdirAll(fmt.Sprintf("%s%s", SRCPATH, name[:len(name) - 1]), 0751) + os.MkdirAll(fmt.Sprintf("%s%s", SRCPATH, name[:len(name)-1]), 0751) } -func checkCXPathSet () { +func checkCXPathSet() { if os.Getenv("CXPATH") == "" { usr, err := user.Current() - if err != nil { + if err != nil { panic(err) } @@ -1333,7 +1354,7 @@ func checkCXPathSet () { COREPATH = filepath.Dir(ex) } -func addInitFunction (PRGRM *CXProgram) { +func addInitFunction(PRGRM *CXProgram) { if main, err := PRGRM.GetPackage(MAIN_PKG); err == nil { initFn := MakeFunction(SYS_INIT_FUNC, CurrentFile, LineNo) main.AddFunction(initFn) @@ -1348,7 +1369,7 @@ func addInitFunction (PRGRM *CXProgram) { // ---------------------------------------------------------------- // Utility functions -func readline (fi *bufio.Reader) (string, bool) { +func readline(fi *bufio.Reader) (string, bool) { s, err := fi.ReadString('\n') s = strings.Replace(s, "\n", "", -1) @@ -1364,7 +1385,7 @@ func readline (fi *bufio.Reader) (string, bool) { if err != nil { return "", false } - + return s, true } @@ -1373,3 +1394,9 @@ func isJSON(str string) bool { err := json.Unmarshal([]byte(str), &js) return err == nil } + +func pingTracker() { + if err := cxTracker.SaveToTrackerService(configPath); err != nil { + log.Error("Unable to ping CX Tracker due to error", err) + } +} diff --git a/cxgo/parser/cxgo.nex b/cxgo/parser/cxgo.nex index 8fcf9ed11..426979681 100644 --- a/cxgo/parser/cxgo.nex +++ b/cxgo/parser/cxgo.nex @@ -117,8 +117,6 @@ /clauses/ { return f(CLAUSES) } /def/ { return f(DEF) } /field/ { return f(FIELD) } -/input/ { return f(INPUT) } -/output/ { return f(OUTPUT) } /import/ { return f(IMPORT) } /var/ { return f(VAR) } /"([^"]*)"/ { /* " */ @@ -397,8 +395,6 @@ var tokenNames = map[int]string { DEF: "DEF", EXPR: "EXPR", FIELD: "FIELD", - INPUT: "INPUT", - OUTPUT: "OUTPUT", CLAUSES: "CLAUSES", OBJECT: "OBJECT", OBJECTS: "OBJECTS", diff --git a/cxgo/parser/cxgo.y b/cxgo/parser/cxgo.y index 9a53bd86c..b4697e7f6 100644 --- a/cxgo/parser/cxgo.y +++ b/cxgo/parser/cxgo.y @@ -81,7 +81,7 @@ /* Selectors */ SPACKAGE SSTRUCT SFUNC /* Removers */ - REM DEF EXPR FIELD INPUT OUTPUT CLAUSES OBJECT OBJECTS + REM DEF EXPR FIELD CLAUSES OBJECT OBJECTS /* Stepping */ STEP PSTEP TSTEP /* Debugging */ diff --git a/cxgo/parser/cxtok.go b/cxgo/parser/cxtok.go index f6ee90207..5caad34cd 100644 --- a/cxgo/parser/cxtok.go +++ b/cxgo/parser/cxtok.go @@ -181,8 +181,6 @@ func tokenName(token int) string { return " INCOP" case INFER: return " INFER" - case INPUT: - return " INPUT" case INT_LITERAL: return "INTLIT" case LBRACE: @@ -247,8 +245,6 @@ func tokenName(token int) string { return " ORSET" case OR_OP: return " OROP" - case OUTPUT: - return "OUTPUT" case PACKAGE: return "PACKAG" case PERIOD: diff --git a/tests/issue-283b.cx b/tests/issue-283b.cx new file mode 100644 index 000000000..dab3c7784 --- /dev/null +++ b/tests/issue-283b.cx @@ -0,0 +1,10 @@ +package main + +func main()() { + var b bool = true + var i i32 = 3 + for b { + a := 1 + 2 + //printf("true\n") + } +} diff --git a/tests/issue-415.cx b/tests/issue-415.cx new file mode 100644 index 000000000..5400b6223 --- /dev/null +++ b/tests/issue-415.cx @@ -0,0 +1,8 @@ +package main + +func main() { + cat(1) +} + +func cat() { +} diff --git a/tests/main.cx b/tests/main.cx index a82a9362d..d50431742 100644 --- a/tests/main.cx +++ b/tests/main.cx @@ -425,7 +425,7 @@ func main ()() { runTestEx("issue-283.cx", cx.SUCCESS, "for loop using boolean value is not compiling", TEST_ISSUE, 0) runTest("issue-284.cx", cx.SUCCESS, "Concatenation of str variables with + operator doesn't work") runTest("issue-285.cx", cx.SUCCESS, "Short declaration doesn't compile with opcode return value") - runTestEx("issue-286.cx", cx.SUCCESS, "Compilation error when struct field is named 'input' or 'output'", TEST_ISSUE, 0) + runTest("issue-286.cx", cx.SUCCESS, "Compilation error when struct field is named 'input' or 'output'") runTestEx("issue-287.cx", cx.COMPILATION_ERROR, "No compilation error when using empty argument list after function call", TEST_ISSUE, 0) runTest("issue-288.cx", cx.COMPILATION_ERROR, "No compilation error when using float value in place of boolean expression") runTest("issue-289.cx", cx.COMPILATION_ERROR, "Panic when package contains duplicate function signature") @@ -444,6 +444,7 @@ func main ()() { runTestEx("issue-360.cx", cx.COMPILATION_ERROR, "Panic when package keyword is misspelled", TEST_ISSUE, 0) runTestEx("issue-361.cx", cx.COMPILATION_ERROR, "No compilation error when assigning an literal which overflow the receiving type", TEST_ISSUE, 0) runTestEx("issue-362.cx", cx.SUCCESS, "Cx is not supporting short-circuit evaluation", TEST_ISSUE, 0) + runTestEx("issue-415.cx", cx.SUCCESS, "func defined with no arguments, called WITH arguments causes PANIC", TEST_ISSUE, 0) // We need to fix serialization and deserialization as user-callable functions // runTestEx("issue-309.cx", cx.SUCCESS, "Serialization is not taking into account non-default stack sizes.", TEST_ISSUE, 0) diff --git a/tests/testdata/tokens/issue-283b.cx.txt b/tests/testdata/tokens/issue-283b.cx.txt new file mode 100644 index 000000000..d224b51d8 --- /dev/null +++ b/tests/testdata/tokens/issue-283b.cx.txt @@ -0,0 +1,35 @@ +PACKAG + IDENT main +SCOLON + FUNC + IDENT main +LPAREN +RPAREN +LPAREN +RPAREN +LBRACE + VAR + IDENT b + BOOL bool + ASGN = +BOOLLT true +SCOLON + VAR + IDENT i + I32 i32 + ASGN = +INTLIT 3 +SCOLON + FOR + IDENT b +LBRACE + IDENT a +CASSGN := +INTLIT 1 + ADDOP + +INTLIT 2 +SCOLON +RBRACE +SCOLON +RBRACE +SCOLON diff --git a/tests/testdata/tokens/issue-286.cx.txt b/tests/testdata/tokens/issue-286.cx.txt index 951bad8d3..d9c9ec5b1 100644 --- a/tests/testdata/tokens/issue-286.cx.txt +++ b/tests/testdata/tokens/issue-286.cx.txt @@ -5,10 +5,10 @@ SCOLON IDENT too STRUCT LBRACE - INPUT + IDENT input I32 i32 SCOLON -OUTPUT + IDENT output I32 i32 SCOLON RBRACE diff --git a/tests/testdata/tokens/issue-415.cx.txt b/tests/testdata/tokens/issue-415.cx.txt new file mode 100644 index 000000000..f1b6a05b2 --- /dev/null +++ b/tests/testdata/tokens/issue-415.cx.txt @@ -0,0 +1,22 @@ +PACKAG + IDENT main +SCOLON + FUNC + IDENT main +LPAREN +RPAREN +LBRACE + IDENT cat +LPAREN +INTLIT 1 +RPAREN +SCOLON +RBRACE +SCOLON + FUNC + IDENT cat +LPAREN +RPAREN +LBRACE +RBRACE +SCOLON diff --git a/tests/testdata/tokens/main.cx.txt b/tests/testdata/tokens/main.cx.txt index 8348b17ed..901a4155f 100644 --- a/tests/testdata/tokens/main.cx.txt +++ b/tests/testdata/tokens/main.cx.txt @@ -3408,7 +3408,7 @@ PERIOD STRLIT Short declaration doesn't compile with opcode return value RPAREN SCOLON - IDENT runTestEx + IDENT runTest LPAREN STRLIT issue-286.cx COMMA @@ -3417,10 +3417,6 @@ PERIOD IDENT SUCCESS COMMA STRLIT Compilation error when struct field is named 'input' or 'output' - COMMA - IDENT TEST_ISSUE - COMMA -INTLIT 0 RPAREN SCOLON IDENT runTestEx @@ -3680,6 +3676,21 @@ STRLIT Cx is not supporting short-circuit evaluation COMMA INTLIT 0 RPAREN +SCOLON + IDENT runTestEx +LPAREN +STRLIT issue-415.cx + COMMA + IDENT cx +PERIOD + IDENT SUCCESS + COMMA +STRLIT func defined with no arguments, called WITH arguments causes PANIC + COMMA + IDENT TEST_ISSUE + COMMA +INTLIT 0 +RPAREN SCOLON VAR IDENT end diff --git a/vendor/github.com/sn-srdjan/cx-tracker-cli/src/api/client.go b/vendor/github.com/sn-srdjan/cx-tracker-cli/src/api/client.go new file mode 100644 index 000000000..30304d5f8 --- /dev/null +++ b/vendor/github.com/sn-srdjan/cx-tracker-cli/src/api/client.go @@ -0,0 +1,176 @@ +package api + +import ( + "encoding/json" + "io" + "io/ioutil" + "net" + "net/http" + "strings" + "time" +) + +const ( + dialTimeout = 60 * time.Second + httpClientTimeout = 120 * time.Second + tlsHandshakeTimeout = 60 * time.Second + + // ContentTypeJSON json content type header + ContentTypeJSON = "application/json" + // ContentTypeForm form data content type header + ContentTypeForm = "application/x-www-form-urlencoded" +) + +type Client struct { + HTTPClient *http.Client + Addr string + Username string + Password string +} + +//TODo use existing api.ErrorResponse here? +// ClientError is used for non-200 API responses +type ClientError struct { + Status string + StatusCode int + Message string +} + +//TODo replace this +// HTTPError is included in an HTTPResponse +type HTTPError struct { + Message string `json:"message"` + Code int `json:"code"` +} + +// NewClientError creates a ClientError +func NewClientError(status string, statusCode int, message string) ClientError { + return ClientError{ + Status: status, + StatusCode: statusCode, + Message: strings.TrimRight(message, "\n"), + } +} + +func (e ClientError) Error() string { + return e.Message +} + +// ReceivedHTTPResponse parsed a HTTPResponse received by the Client, for the V2 API +type ReceivedHTTPResponse struct { + Error *HTTPError `json:"error,omitempty"` + Data json.RawMessage `json:"data"` +} + +// NewClient creates a Client +func NewClient(addr string) *Client { + transport := &http.Transport{ + Dial: (&net.Dialer{ + Timeout: dialTimeout, + }).Dial, + TLSHandshakeTimeout: tlsHandshakeTimeout, + } + httpClient := &http.Client{ + Transport: transport, + Timeout: httpClientTimeout, + } + addr = strings.TrimRight(addr, "/") + addr += "/" + + return &Client{ + Addr: addr, + HTTPClient: httpClient, + } +} + +// Get makes a GET request to an endpoint and unmarshals the response to obj. +// If the response is not 200 OK, returns an error +func (c *Client) Get(endpoint string, obj interface{}) error { + resp, err := c.get(endpoint) + if err != nil { + return err + } + + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return err + } + + return NewClientError(resp.Status, resp.StatusCode, string(body)) + } + + if obj == nil { + return nil + } + + d := json.NewDecoder(resp.Body) + d.DisallowUnknownFields() + return d.Decode(obj) +} + +// Post makes a POST request with provided body to an endpoint and unmarshals the response to obj. +// If the response is not 200 OK, returns an error +func (c *Client) Post(endpoint string, body, obj interface{}) error { + resp, err := c.post(endpoint, body) + if err != nil { + return err + } + + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return err + } + + return NewClientError(resp.Status, resp.StatusCode, string(body)) + } + + if obj == nil { + return nil + } + + d := json.NewDecoder(resp.Body) + d.DisallowUnknownFields() + return d.Decode(obj) +} + +// get makes a GET request to an endpoint. Caller must close response body. +func (c *Client) get(endpoint string) (*http.Response, error) { + return c.makeRequestWithoutBody(endpoint, http.MethodGet) +} + +// post makes a POST request to an endpoint with provided body. Caller must close response body. +func (c *Client) post(endpoint string, body interface{}) (*http.Response, error) { + return c.makeRequestWithBody(body, endpoint, http.MethodPost) +} + +// makeRequestWithoutBody makes a `method` request to an endpoint. Caller must close response body. +func (c *Client) makeRequestWithoutBody(endpoint, method string) (*http.Response, error) { + endpoint = strings.TrimLeft(endpoint, "/") + endpoint = c.Addr + endpoint + + req, err := http.NewRequest(method, endpoint, nil) + if err != nil { + return nil, err + } + + return c.HTTPClient.Do(req) +} + +// makeRequestWithBody makes a `method` request to an endpoint. Caller must close response body. +func (c *Client) makeRequestWithBody(body interface{}, endpoint, method string) (*http.Response, error) { + endpoint = strings.TrimLeft(endpoint, "/") + endpoint = c.Addr + endpoint + + req, err := http.NewRequest(method, endpoint, body.(io.Reader)) + if err != nil { + return nil, err + } + + return c.HTTPClient.Do(req) +} diff --git a/vendor/github.com/sn-srdjan/cx-tracker-cli/src/cli/provider/tracker_provider.go b/vendor/github.com/sn-srdjan/cx-tracker-cli/src/cli/provider/tracker_provider.go new file mode 100644 index 000000000..e7e25fa94 --- /dev/null +++ b/vendor/github.com/sn-srdjan/cx-tracker-cli/src/cli/provider/tracker_provider.go @@ -0,0 +1,47 @@ +package provider + +import ( + "fmt" + "io/ioutil" + "os" + + "github.com/sn-srdjan/cx-tracker-cli/src/api" +) + +// TrackerProvider - provider object +type TrackerProvider struct { + ServiceURL string + apiClient *api.Client +} + +// DefaultCxTrackerURL - default cx tracker url +const DefaultCxTrackerURL = "https://tracker.skycoin.net" + +// SaveToTrackerService - persist config on tracker service +func (t *TrackerProvider) SaveToTrackerService(configFilePath string) error { + t.init() + if _, err := os.Stat(configFilePath); os.IsNotExist(err) { + return fmt.Errorf("config %s doesn't exist", configFilePath) + } + + bs, err := ioutil.ReadFile(configFilePath) + if err != nil { + return fmt.Errorf("error while reading config %s", configFilePath) + } + + if err := t.apiClient.Post(t.ServiceURL, bs, nil); err != nil { + return fmt.Errorf("error while persisting config %s on service %s", configFilePath, t.ServiceURL) + } + + return nil +} + +func (t *TrackerProvider) init() { + if len(t.ServiceURL) == 0 { + t.ServiceURL = DefaultCxTrackerURL + } + + if t.apiClient == nil { + t.apiClient = api.NewClient(t.ServiceURL) + } +}