From e0ff4df39941b8b43aeb0359473f5bba0eee7df6 Mon Sep 17 00:00:00 2001 From: Ilan Date: Thu, 4 Aug 2022 16:10:46 -0400 Subject: [PATCH 01/15] define directive --- data/transactions/logic/assembler.go | 125 ++++++++++++++++------ data/transactions/logic/assembler_test.go | 98 +++++++++++++---- 2 files changed, 173 insertions(+), 50 deletions(-) diff --git a/data/transactions/logic/assembler.go b/data/transactions/logic/assembler.go index fcd56d2c7c..12680d7433 100644 --- a/data/transactions/logic/assembler.go +++ b/data/transactions/logic/assembler.go @@ -250,6 +250,8 @@ type OpStream struct { // Need new copy for each opstream versionedPseudoOps map[string]map[int]OpSpec + + macros map[string][]string } // newOpStream constructs OpStream instances ready to invoke assemble. A new @@ -260,6 +262,7 @@ func newOpStream(version uint64) OpStream { OffsetToLine: make(map[int]int), typeTracking: true, Version: version, + macros: make(map[string][]string), } for i := range o.known.scratchSpace { @@ -1531,6 +1534,38 @@ func (ops *OpStream) trackStack(args StackTypes, returns StackTypes, instruction } } +func processFields(ops *OpStream, fields []string) (current, next []string) { + for i := 0; i < len(fields); i++ { + field := fields[i] + replacement, ok := ops.macros[field] + if ok { + fields = append(fields[0:i], append(replacement, fields[i+1:]...)...) + i-- + continue + } + if string(field[len(field)-1]) == ";" { + field = field[0 : len(field)-1] + replacement, ok = ops.macros[field] + if ok { + last := replacement[len(replacement)-1] + fields = append(fields[0:i], append(replacement, fields[i+1:]...)...) + fields[i+len(replacement)-1] = last + ";" + i-- + continue + } + current = fields[:i] + if len(field) > 0 { + current = append(current, field) + } + if i+1 == len(fields) { + return current, nil + } + return current, fields[i+1:] + } + } + return fields, nil +} + // assemble reads text from an input and accumulates the program func (ops *OpStream) assemble(text string) error { fin := strings.NewReader(text) @@ -1567,41 +1602,71 @@ func (ops *OpStream) assemble(text string) error { if ops.versionedPseudoOps == nil { ops.versionedPseudoOps = prepareVersionedPseudoTable(ops.Version) } - opstring := fields[0] - if opstring[len(opstring)-1] == ':' { - ops.createLabel(opstring[:len(opstring)-1]) - fields = fields[1:] - if len(fields) == 0 { - ops.trace("%3d: label only\n", ops.sourceLine) - continue + if fields[0] == "#define" { + if len(fields) < 3 { + ops.error("Not enough arguments to define directive") + } else { + if fields[1][len(fields[1])-1] == ';' { + ops.error("Macro name not allowed to terminate with semicolon: " + fields[1]) + } + ops.macros[fields[1]] = make([]string, len(fields[2:])) + copy(ops.macros[fields[1]], fields[2:]) } - opstring = fields[0] + continue } - spec, expandedName, ok := getSpec(ops, opstring, fields[1:]) - if ok { - ops.trace("%3d: %s\t", ops.sourceLine, opstring) - ops.recordSourceLine() - if spec.Modes == modeApp { - ops.HasStatefulOps = true + var current []string + var next []string + next = fields + for current, next = processFields(ops, next); len(current) > 0; current, next = processFields(ops, next) { + opstring := current[0] + if len(opstring) == 0 { + continue } - args, returns := spec.Arg.Types, spec.Return.Types - if spec.refine != nil { - nargs, nreturns := spec.refine(&ops.known, fields[1:]) - if nargs != nil { - args = nargs - } - if nreturns != nil { - returns = nreturns - } + if strings.HasPrefix(opstring, "//") { + // semicolon inside comment is not counted as newline + break + } + currentLine := strings.Join(current, " ") + if strings.HasPrefix(currentLine, "#pragma") { + ops.trace("%3d: #pragma line\n", ops.sourceLine) + ops.pragma(currentLine) + continue } - ops.trackStack(args, returns, append([]string{expandedName}, fields[1:]...)) - spec.asm(ops, &spec, fields[1:]) - if spec.deadens() { // An unconditional branch deadens the following code - ops.known.deaden() + if opstring[len(opstring)-1] == ':' { + ops.createLabel(opstring[:len(opstring)-1]) + current = current[1:] + if len(current) == 0 { + ops.trace("%3d: label only\n", ops.sourceLine) + continue + } + opstring = current[0] } - if spec.Name == "callsub" { - // since retsub comes back to the callsub, it is an entry point like a label - ops.known.label() + spec, expandedName, ok := getSpec(ops, opstring, current[1:]) + if ok { + ops.trace("%3d: %s\t", ops.sourceLine, opstring) + ops.recordSourceLine() + if spec.Modes == modeApp { + ops.HasStatefulOps = true + } + args, returns := spec.Arg.Types, spec.Return.Types + if spec.refine != nil { + nargs, nreturns := spec.refine(&ops.known, current[1:]) + if nargs != nil { + args = nargs + } + if nreturns != nil { + returns = nreturns + } + } + ops.trackStack(args, returns, append([]string{expandedName}, current[1:]...)) + spec.asm(ops, &spec, current[1:]) + if spec.deadens() { // An unconditional branch deadens the following code + ops.known.deaden() + } + if spec.Name == "callsub" { + // since retsub comes back to the callsub, it is an entry point like a label + ops.known.label() + } } ops.trace("\n") continue diff --git a/data/transactions/logic/assembler_test.go b/data/transactions/logic/assembler_test.go index 10c7d13f48..b5320b298c 100644 --- a/data/transactions/logic/assembler_test.go +++ b/data/transactions/logic/assembler_test.go @@ -17,6 +17,7 @@ package logic import ( + "bufio" "encoding/hex" "fmt" "strings" @@ -434,6 +435,29 @@ func pseudoOp(opcode string) bool { strings.HasPrefix(opcode, "arg") } +func addSemis(s string) (ret string) { + scanner := bufio.NewScanner(strings.NewReader(s)) +scanLoop: + for scanner.Scan() { + line := strings.TrimSpace(scanner.Text()) + fields := fieldsFromLine(line) + for i, field := range fields { + if i == 0 && strings.HasPrefix(field, "#") { + ret += "\n" + line + "\n" + continue scanLoop + } + if strings.HasPrefix(field, "//") { + ret += line + "\n" + continue scanLoop + } + } + if len(fields) > 0 { + ret += strings.Join(fields, " ") + "; " + } + } + return +} + // Check that assembly output is stable across time. func TestAssemble(t *testing.T) { partitiontest.PartitionTest(t) @@ -529,28 +553,37 @@ func testProg(t testing.TB, source string, ver uint64, expected ...Expect) *OpSt t.Helper() program := strings.ReplaceAll(source, ";", "\n") ops, err := AssembleStringWithVersion(program, ver) + semiOps, semiErr := AssembleStringWithVersion(addSemis(program), ver) + opstreams := [2]*OpStream{ops, semiOps} + errors := [2]error{err, semiErr} if len(expected) == 0 { - if len(ops.Errors) > 0 || err != nil || ops == nil || ops.Program == nil { - t.Log(assemblyTrace(program, ver)) - } - require.Empty(t, ops.Errors) - require.NoError(t, err) - require.NotNil(t, ops) - require.NotNil(t, ops.Program) - // It should always be possible to Disassemble - dis, err := Disassemble(ops.Program) - require.NoError(t, err, program) - // And, while the disassembly may not match input - // exactly, the assembly of the disassembly should - // give the same bytecode - ops2, err := AssembleStringWithVersion(notrack(dis), ver) - if len(ops2.Errors) > 0 || err != nil || ops2 == nil || ops2.Program == nil { - t.Log(program) - t.Log(dis) + for i := range opstreams { + ops = opstreams[i] + err = errors[i] + if len(ops.Errors) > 0 || err != nil || ops == nil || ops.Program == nil { + t.Log(assemblyTrace(program, ver)) + } + require.Empty(t, ops.Errors) + require.NoError(t, err) + require.NotNil(t, ops) + require.NotNil(t, ops.Program) + // It should always be possible to Disassemble + dis, err := Disassemble(ops.Program) + require.NoError(t, err, program) + // And, while the disassembly may not match input + // exactly, the assembly of the disassembly should + // give the same bytecode + ops2, err := AssembleStringWithVersion(notrack(dis), ver) + if len(ops2.Errors) > 0 || err != nil || ops2 == nil || ops2.Program == nil { + t.Log(program) + t.Log(dis) + } + require.Empty(t, ops2.Errors) + require.NoError(t, err) + require.Equal(t, ops.Program, ops2.Program) } - require.Empty(t, ops2.Errors) - require.NoError(t, err) - require.Equal(t, ops.Program, ops2.Program) + ops = opstreams[0] + err = errors[0] } else { if err == nil { t.Log(program) @@ -2687,3 +2720,28 @@ func TestReplacePseudo(t *testing.T) { testProg(t, "byte 0x0000; int 0; byte 0x1234; replace 0", v, Expect{4, "replace 0 arg 0 wanted type []byte got uint64"}) } } + +func TestSemiColon(t *testing.T) { + partitiontest.PartitionTest(t) + t.Parallel() + // Space for weird semicolon cases that might not be checked by rest of tests + checkSame(t, "pushint 0 ; pushint 1 ; +; int 3 ; *", "pushint 0\npushint 1\n+\nint 3\n*") +} + +func checkSame(t *testing.T, weird string, normal string) { + ops, _ := AssembleStringWithVersion(weird, 7) + otherOps, _ := AssembleStringWithVersion(normal, 7) + require.Equal(t, otherOps.Program, ops.Program) +} + +func TestMacros(t *testing.T) { + partitiontest.PartitionTest(t) + t.Parallel() + ops, _ := AssembleStringWithVersion("#define none 0\n#define one 1\npushint none; pushint one; +", 7) + otherOps, _ := AssembleStringWithVersion("pushint 0\npushint 1\n+", 7) + require.Equal(t, otherOps.Program, ops.Program) + + checkSame(t, "#define ==? ==; bnz\n #define one 1\n #define two 2\npushint one; pushint two; ==? label1; err; label1:; pushint one", "pushint 1\npushint 2\n==\nbnz label1\nerr\nlabel1:\npushint 1") + checkSame(t, "#define rowSize 3\n#define columnSize 5\n#define tableDimensions rowSize columnSize\npushbytes 0x100000000000; substring tableDimensions\n#define rowSize 0\n#define columnSize 1\nsubstring tableDimensions", "pushbytes 0x100000000000; substring 3 5; substring 0 1") + checkSame(t, "#define &x 0\n#define x load &x;\n#define &y 1\n#define y load &y;\n#define -> ; store\nint 3 -> &x; int 4 -> &y\nx y <", "int 3\nstore 0\nint 4\nstore 1\nload 0\nload 1\n<") +} From 14baa47fa843e0fa478c65828640e4add9aafa6d Mon Sep 17 00:00:00 2001 From: Ilan Date: Fri, 5 Aug 2022 13:54:19 -0400 Subject: [PATCH 02/15] addressed comments --- data/transactions/logic/assembler.go | 6 +++ data/transactions/logic/assembler_test.go | 47 +++++++++++++++++++++-- 2 files changed, 50 insertions(+), 3 deletions(-) diff --git a/data/transactions/logic/assembler.go b/data/transactions/logic/assembler.go index 12680d7433..70eff5f2dd 100644 --- a/data/transactions/logic/assembler.go +++ b/data/transactions/logic/assembler.go @@ -1534,6 +1534,12 @@ func (ops *OpStream) trackStack(args StackTypes, returns StackTypes, instruction } } +// processFields walks through the input fields and expands any macros along the way +// until it gets to a semi-colon at the end of a field at which point it returns +// everything prior as current and everything following the semicolon as next + +// fields should not be used after as processFields mangles it +// current and next do share the same array if next is not nil func processFields(ops *OpStream, fields []string) (current, next []string) { for i := 0; i < len(fields); i++ { field := fields[i] diff --git a/data/transactions/logic/assembler_test.go b/data/transactions/logic/assembler_test.go index b5320b298c..762c0d6a61 100644 --- a/data/transactions/logic/assembler_test.go +++ b/data/transactions/logic/assembler_test.go @@ -2741,7 +2741,48 @@ func TestMacros(t *testing.T) { otherOps, _ := AssembleStringWithVersion("pushint 0\npushint 1\n+", 7) require.Equal(t, otherOps.Program, ops.Program) - checkSame(t, "#define ==? ==; bnz\n #define one 1\n #define two 2\npushint one; pushint two; ==? label1; err; label1:; pushint one", "pushint 1\npushint 2\n==\nbnz label1\nerr\nlabel1:\npushint 1") - checkSame(t, "#define rowSize 3\n#define columnSize 5\n#define tableDimensions rowSize columnSize\npushbytes 0x100000000000; substring tableDimensions\n#define rowSize 0\n#define columnSize 1\nsubstring tableDimensions", "pushbytes 0x100000000000; substring 3 5; substring 0 1") - checkSame(t, "#define &x 0\n#define x load &x;\n#define &y 1\n#define y load &y;\n#define -> ; store\nint 3 -> &x; int 4 -> &y\nx y <", "int 3\nstore 0\nint 4\nstore 1\nload 0\nload 1\n<") + checkSame(t, ` +#define ==? ==; bnz +#define one 1 +#define two 2 +pushint one; pushint two; ==? label1 +err +label1: +pushint one`, ` + +pushint 1 +pushint 2 +== +bnz label1 +err +label1: +pushint 1`) + + checkSame(t, ` +#define rowSize 3 +#define columnSize 5 +#define tableDimensions rowSize columnSize +pushbytes 0x100000000000; substring tableDimensions +#define rowSize 0 +#define columnSize 1 +substring tableDimensions`, ` + +pushbytes 0x100000000000; substring 3 5; substring 0 1`) + + checkSame(t, ` +#define &x 0 +#define x load &x; +#define &y 1 +#define y load &y; +#define -> ; store +int 3 -> &x; int 4 -> &y +x y <`, ` + +int 3 +store 0 +int 4 +store 1 +load 0 +load 1 +<`) } From bea64d95e16cc85822f44a68bf310311873e6a30 Mon Sep 17 00:00:00 2001 From: Ilan Date: Fri, 5 Aug 2022 15:49:34 -0400 Subject: [PATCH 03/15] Fixed tests --- data/transactions/logic/assembler.go | 42 +--- data/transactions/logic/assembler_test.go | 228 +++++++----------- .../transactions/logic/backwardCompat_test.go | 6 +- data/transactions/logic/evalAppTxn_test.go | 98 ++++---- data/transactions/logic/evalStateful_test.go | 10 +- data/transactions/logic/eval_test.go | 126 +++++----- data/transactions/logic/fields_test.go | 2 +- 7 files changed, 213 insertions(+), 299 deletions(-) diff --git a/data/transactions/logic/assembler.go b/data/transactions/logic/assembler.go index 70eff5f2dd..f0177dc8df 100644 --- a/data/transactions/logic/assembler.go +++ b/data/transactions/logic/assembler.go @@ -250,8 +250,6 @@ type OpStream struct { // Need new copy for each opstream versionedPseudoOps map[string]map[int]OpSpec - - macros map[string][]string } // newOpStream constructs OpStream instances ready to invoke assemble. A new @@ -262,7 +260,6 @@ func newOpStream(version uint64) OpStream { OffsetToLine: make(map[int]int), typeTracking: true, Version: version, - macros: make(map[string][]string), } for i := range o.known.scratchSpace { @@ -1534,31 +1531,17 @@ func (ops *OpStream) trackStack(args StackTypes, returns StackTypes, instruction } } -// processFields walks through the input fields and expands any macros along the way -// until it gets to a semi-colon at the end of a field at which point it returns -// everything prior as current and everything following the semicolon as next +// processFields walks through the input fields until it gets to a semi-colon +// at the end of a field at which point it returns everything prior as current +// and everything following the semicolon as next // fields should not be used after as processFields mangles it // current and next do share the same array if next is not nil -func processFields(ops *OpStream, fields []string) (current, next []string) { +func processFields(fields []string) (current, next []string) { for i := 0; i < len(fields); i++ { field := fields[i] - replacement, ok := ops.macros[field] - if ok { - fields = append(fields[0:i], append(replacement, fields[i+1:]...)...) - i-- - continue - } if string(field[len(field)-1]) == ";" { field = field[0 : len(field)-1] - replacement, ok = ops.macros[field] - if ok { - last := replacement[len(replacement)-1] - fields = append(fields[0:i], append(replacement, fields[i+1:]...)...) - fields[i+len(replacement)-1] = last + ";" - i-- - continue - } current = fields[:i] if len(field) > 0 { current = append(current, field) @@ -1608,22 +1591,13 @@ func (ops *OpStream) assemble(text string) error { if ops.versionedPseudoOps == nil { ops.versionedPseudoOps = prepareVersionedPseudoTable(ops.Version) } - if fields[0] == "#define" { - if len(fields) < 3 { - ops.error("Not enough arguments to define directive") - } else { - if fields[1][len(fields[1])-1] == ';' { - ops.error("Macro name not allowed to terminate with semicolon: " + fields[1]) - } - ops.macros[fields[1]] = make([]string, len(fields[2:])) - copy(ops.macros[fields[1]], fields[2:]) - } - continue - } var current []string var next []string next = fields - for current, next = processFields(ops, next); len(current) > 0; current, next = processFields(ops, next) { + for current, next = processFields(next); len(current) > 0 || len(next) > 0; current, next = processFields(next) { + if len(current) == 0 { + continue + } opstring := current[0] if len(opstring) == 0 { continue diff --git a/data/transactions/logic/assembler_test.go b/data/transactions/logic/assembler_test.go index 762c0d6a61..9ebd2d27b2 100644 --- a/data/transactions/logic/assembler_test.go +++ b/data/transactions/logic/assembler_test.go @@ -484,6 +484,7 @@ func TestAssemble(t *testing.T) { } ops := testProg(t, nonsense[v], v) + opsWithSemiColons := testProg(t, addSemis(nonsense[v]), v) // check that compilation is stable over // time. we must assemble to the same bytes // this month that we did last month. @@ -491,6 +492,7 @@ func TestAssemble(t *testing.T) { // the hex is for convenience if the program has been changed. the // hex string can be copy pasted back in as a new expected result. require.Equal(t, expectedBytes, ops.Program, hex.EncodeToString(ops.Program)) + require.Equal(t, expectedBytes, opsWithSemiColons.Program, hex.EncodeToString(ops.Program)) }) } } @@ -551,39 +553,30 @@ func assemblyTrace(text string, ver uint64) string { func testProg(t testing.TB, source string, ver uint64, expected ...Expect) *OpStream { t.Helper() - program := strings.ReplaceAll(source, ";", "\n") + program := source ops, err := AssembleStringWithVersion(program, ver) - semiOps, semiErr := AssembleStringWithVersion(addSemis(program), ver) - opstreams := [2]*OpStream{ops, semiOps} - errors := [2]error{err, semiErr} if len(expected) == 0 { - for i := range opstreams { - ops = opstreams[i] - err = errors[i] - if len(ops.Errors) > 0 || err != nil || ops == nil || ops.Program == nil { - t.Log(assemblyTrace(program, ver)) - } - require.Empty(t, ops.Errors) - require.NoError(t, err) - require.NotNil(t, ops) - require.NotNil(t, ops.Program) - // It should always be possible to Disassemble - dis, err := Disassemble(ops.Program) - require.NoError(t, err, program) - // And, while the disassembly may not match input - // exactly, the assembly of the disassembly should - // give the same bytecode - ops2, err := AssembleStringWithVersion(notrack(dis), ver) - if len(ops2.Errors) > 0 || err != nil || ops2 == nil || ops2.Program == nil { - t.Log(program) - t.Log(dis) - } - require.Empty(t, ops2.Errors) - require.NoError(t, err) - require.Equal(t, ops.Program, ops2.Program) + if len(ops.Errors) > 0 || err != nil || ops == nil || ops.Program == nil { + t.Log(assemblyTrace(program, ver)) } - ops = opstreams[0] - err = errors[0] + require.Empty(t, ops.Errors) + require.NoError(t, err) + require.NotNil(t, ops) + require.NotNil(t, ops.Program) + // It should always be possible to Disassemble + dis, err := Disassemble(ops.Program) + require.NoError(t, err, program) + // And, while the disassembly may not match input + // exactly, the assembly of the disassembly should + // give the same bytecode + ops2, err := AssembleStringWithVersion(notrack(dis), ver) + if len(ops2.Errors) > 0 || err != nil || ops2 == nil || ops2.Program == nil { + t.Log(program) + t.Log(dis) + } + require.Empty(t, ops2.Errors) + require.NoError(t, err) + require.Equal(t, ops.Program, ops2.Program) } else { if err == nil { t.Log(program) @@ -706,9 +699,9 @@ func TestAssembleGlobal(t *testing.T) { testProg(t, "global MinTxnFee; int 2; +", AssemblerMaxVersion) testProg(t, "global ZeroAddress; byte 0x12; concat; len", AssemblerMaxVersion) testProg(t, "global MinTxnFee; byte 0x12; concat", AssemblerMaxVersion, - Expect{3, "concat arg 0 wanted type []byte..."}) + Expect{1, "concat arg 0 wanted type []byte..."}) testProg(t, "int 2; global ZeroAddress; +", AssemblerMaxVersion, - Expect{3, "+ arg 1 wanted type uint64..."}) + Expect{1, "+ arg 1 wanted type uint64..."}) } func TestAssembleDefault(t *testing.T) { @@ -1803,22 +1796,22 @@ func TestAssembleAsset(t *testing.T) { testProg(t, "asset_holding_get ABC 1", v, Expect{1, "asset_holding_get ABC 1 expects 2 stack arguments..."}) testProg(t, "int 1; asset_holding_get ABC 1", v, - Expect{2, "asset_holding_get ABC 1 expects 2 stack arguments..."}) + Expect{1, "asset_holding_get ABC 1 expects 2 stack arguments..."}) testProg(t, "int 1; int 1; asset_holding_get ABC 1", v, - Expect{3, "asset_holding_get expects 1 immediate argument"}) + Expect{1, "asset_holding_get expects 1 immediate argument"}) testProg(t, "int 1; int 1; asset_holding_get ABC", v, - Expect{3, "asset_holding_get unknown field: \"ABC\""}) + Expect{1, "asset_holding_get unknown field: \"ABC\""}) testProg(t, "byte 0x1234; asset_params_get ABC 1", v, - Expect{2, "asset_params_get ABC 1 arg 0 wanted type uint64..."}) + Expect{1, "asset_params_get ABC 1 arg 0 wanted type uint64..."}) // Test that AssetUnitName is known to return bytes testProg(t, "int 1; asset_params_get AssetUnitName; pop; int 1; +", v, - Expect{5, "+ arg 0 wanted type uint64..."}) + Expect{1, "+ arg 0 wanted type uint64..."}) // Test that AssetTotal is known to return uint64 testProg(t, "int 1; asset_params_get AssetTotal; pop; byte 0x12; concat", v, - Expect{5, "concat arg 0 wanted type []byte..."}) + Expect{1, "concat arg 0 wanted type []byte..."}) testLine(t, "asset_params_get ABC 1", v, "asset_params_get expects 1 immediate argument") testLine(t, "asset_params_get ABC", v, "asset_params_get unknown field: \"ABC\"") @@ -2427,29 +2420,29 @@ func TestSwapTypeCheck(t *testing.T) { t.Parallel() /* reconfirm that we detect this type error */ - testProg(t, "int 1; byte 0x1234; +", AssemblerMaxVersion, Expect{3, "+ arg 1..."}) + testProg(t, "int 1; byte 0x1234; +", AssemblerMaxVersion, Expect{1, "+ arg 1..."}) /* despite swap, we track types */ - testProg(t, "int 1; byte 0x1234; swap; +", AssemblerMaxVersion, Expect{4, "+ arg 0..."}) - testProg(t, "byte 0x1234; int 1; swap; +", AssemblerMaxVersion, Expect{4, "+ arg 1..."}) + testProg(t, "int 1; byte 0x1234; swap; +", AssemblerMaxVersion, Expect{1, "+ arg 0..."}) + testProg(t, "byte 0x1234; int 1; swap; +", AssemblerMaxVersion, Expect{1, "+ arg 1..."}) } func TestDigAsm(t *testing.T) { partitiontest.PartitionTest(t) t.Parallel() - testProg(t, "int 1; dig; +", AssemblerMaxVersion, Expect{2, "dig expects 1 immediate..."}) - testProg(t, "int 1; dig junk; +", AssemblerMaxVersion, Expect{2, "dig unable to parse..."}) + testProg(t, "int 1; dig; +", AssemblerMaxVersion, Expect{1, "dig expects 1 immediate..."}) + testProg(t, "int 1; dig junk; +", AssemblerMaxVersion, Expect{1, "dig unable to parse..."}) testProg(t, "int 1; byte 0x1234; int 2; dig 2; +", AssemblerMaxVersion) testProg(t, "byte 0x32; byte 0x1234; int 2; dig 2; +", AssemblerMaxVersion, - Expect{5, "+ arg 1..."}) + Expect{1, "+ arg 1..."}) testProg(t, "byte 0x32; byte 0x1234; int 2; dig 3; +", AssemblerMaxVersion, - Expect{4, "dig 3 expects 4..."}) + Expect{1, "dig 3 expects 4..."}) testProg(t, "int 1; byte 0x1234; int 2; dig 12; +", AssemblerMaxVersion, - Expect{4, "dig 12 expects 13..."}) + Expect{1, "dig 12 expects 13..."}) // Confirm that digging something out does not ruin our knowledge about the types in the middle testProg(t, "int 1; byte 0x1234; byte 0x1234; dig 2; dig 3; +; pop; +", AssemblerMaxVersion, - Expect{8, "+ arg 1..."}) + Expect{1, "+ arg 1..."}) testProg(t, "int 3; pushbytes \"123456\"; int 1; dig 2; substring3", AssemblerMaxVersion) } @@ -2457,39 +2450,39 @@ func TestDigAsm(t *testing.T) { func TestEqualsTypeCheck(t *testing.T) { partitiontest.PartitionTest(t) t.Parallel() - testProg(t, "int 1; byte 0x1234; ==", AssemblerMaxVersion, Expect{3, "== arg 0..."}) - testProg(t, "int 1; byte 0x1234; !=", AssemblerMaxVersion, Expect{3, "!= arg 0..."}) - testProg(t, "byte 0x1234; int 1; ==", AssemblerMaxVersion, Expect{3, "== arg 0..."}) - testProg(t, "byte 0x1234; int 1; !=", AssemblerMaxVersion, Expect{3, "!= arg 0..."}) + testProg(t, "int 1; byte 0x1234; ==", AssemblerMaxVersion, Expect{1, "== arg 0..."}) + testProg(t, "int 1; byte 0x1234; !=", AssemblerMaxVersion, Expect{1, "!= arg 0..."}) + testProg(t, "byte 0x1234; int 1; ==", AssemblerMaxVersion, Expect{1, "== arg 0..."}) + testProg(t, "byte 0x1234; int 1; !=", AssemblerMaxVersion, Expect{1, "!= arg 0..."}) } func TestDupTypeCheck(t *testing.T) { partitiontest.PartitionTest(t) t.Parallel() - testProg(t, "byte 0x1234; dup; int 1; +", AssemblerMaxVersion, Expect{4, "+ arg 0..."}) + testProg(t, "byte 0x1234; dup; int 1; +", AssemblerMaxVersion, Expect{1, "+ arg 0..."}) testProg(t, "byte 0x1234; int 1; dup; +", AssemblerMaxVersion) - testProg(t, "byte 0x1234; int 1; dup2; +", AssemblerMaxVersion, Expect{4, "+ arg 0..."}) - testProg(t, "int 1; byte 0x1234; dup2; +", AssemblerMaxVersion, Expect{4, "+ arg 1..."}) + testProg(t, "byte 0x1234; int 1; dup2; +", AssemblerMaxVersion, Expect{1, "+ arg 0..."}) + testProg(t, "int 1; byte 0x1234; dup2; +", AssemblerMaxVersion, Expect{1, "+ arg 1..."}) - testProg(t, "byte 0x1234; int 1; dup; dig 1; len", AssemblerMaxVersion, Expect{5, "len arg 0..."}) - testProg(t, "int 1; byte 0x1234; dup; dig 1; !", AssemblerMaxVersion, Expect{5, "! arg 0..."}) + testProg(t, "byte 0x1234; int 1; dup; dig 1; len", AssemblerMaxVersion, Expect{1, "len arg 0..."}) + testProg(t, "int 1; byte 0x1234; dup; dig 1; !", AssemblerMaxVersion, Expect{1, "! arg 0..."}) - testProg(t, "byte 0x1234; int 1; dup2; dig 2; len", AssemblerMaxVersion, Expect{5, "len arg 0..."}) - testProg(t, "int 1; byte 0x1234; dup2; dig 2; !", AssemblerMaxVersion, Expect{5, "! arg 0..."}) + testProg(t, "byte 0x1234; int 1; dup2; dig 2; len", AssemblerMaxVersion, Expect{1, "len arg 0..."}) + testProg(t, "int 1; byte 0x1234; dup2; dig 2; !", AssemblerMaxVersion, Expect{1, "! arg 0..."}) } func TestSelectTypeCheck(t *testing.T) { partitiontest.PartitionTest(t) t.Parallel() - testProg(t, "int 1; int 2; int 3; select; len", AssemblerMaxVersion, Expect{5, "len arg 0..."}) - testProg(t, "byte 0x1234; byte 0x5678; int 3; select; !", AssemblerMaxVersion, Expect{5, "! arg 0..."}) + testProg(t, "int 1; int 2; int 3; select; len", AssemblerMaxVersion, Expect{1, "len arg 0..."}) + testProg(t, "byte 0x1234; byte 0x5678; int 3; select; !", AssemblerMaxVersion, Expect{1, "! arg 0..."}) } func TestSetBitTypeCheck(t *testing.T) { partitiontest.PartitionTest(t) t.Parallel() - testProg(t, "int 1; int 2; int 3; setbit; len", AssemblerMaxVersion, Expect{5, "len arg 0..."}) - testProg(t, "byte 0x1234; int 2; int 3; setbit; !", AssemblerMaxVersion, Expect{5, "! arg 0..."}) + testProg(t, "int 1; int 2; int 3; setbit; len", AssemblerMaxVersion, Expect{1, "len arg 0..."}) + testProg(t, "byte 0x1234; int 2; int 3; setbit; !", AssemblerMaxVersion, Expect{1, "! arg 0..."}) } func TestScratchTypeCheck(t *testing.T) { @@ -2498,13 +2491,13 @@ func TestScratchTypeCheck(t *testing.T) { // All scratch slots should start as uint64 testProg(t, "load 0; int 1; +", AssemblerMaxVersion) // Check load and store accurately using the scratch space - testProg(t, "byte 0x01; store 0; load 0; int 1; +", AssemblerMaxVersion, Expect{5, "+ arg 0..."}) + testProg(t, "byte 0x01; store 0; load 0; int 1; +", AssemblerMaxVersion, Expect{1, "+ arg 0..."}) // Loads should know the type it's loading if all the slots are the same type - testProg(t, "int 0; loads; btoi", AssemblerMaxVersion, Expect{3, "btoi arg 0..."}) + testProg(t, "int 0; loads; btoi", AssemblerMaxVersion, Expect{1, "btoi arg 0..."}) // Loads doesn't know the type when slot types vary testProg(t, "byte 0x01; store 0; int 1; loads; btoi", AssemblerMaxVersion) // Stores should only set slots to StackAny if they are not the same type as what is being stored - testProg(t, "byte 0x01; store 0; int 3; byte 0x01; stores; load 0; int 1; +", AssemblerMaxVersion, Expect{8, "+ arg 0..."}) + testProg(t, "byte 0x01; store 0; int 3; byte 0x01; stores; load 0; int 1; +", AssemblerMaxVersion, Expect{1, "+ arg 0..."}) // ScratchSpace should reset after hitting label in deadcode testProg(t, "byte 0x01; store 0; b label1; label1:; load 0; int 1; +", AssemblerMaxVersion) // But it should reset to StackAny not uint64 @@ -2512,7 +2505,7 @@ func TestScratchTypeCheck(t *testing.T) { // Callsubs should also reset the scratch space testProg(t, "callsub A; load 0; btoi; return; A: byte 0x01; store 0; retsub", AssemblerMaxVersion) // But the scratchspace should still be tracked after the callsub - testProg(t, "callsub A; int 1; store 0; load 0; btoi; return; A: retsub", AssemblerMaxVersion, Expect{5, "btoi arg 0..."}) + testProg(t, "callsub A; int 1; store 0; load 0; btoi; return; A: retsub", AssemblerMaxVersion, Expect{1, "btoi arg 0..."}) } func TestCoverAsm(t *testing.T) { @@ -2520,9 +2513,9 @@ func TestCoverAsm(t *testing.T) { t.Parallel() testProg(t, `int 4; byte "john"; int 5; cover 2; pop; +`, AssemblerMaxVersion) testProg(t, `int 4; byte "ayush"; int 5; cover 1; pop; +`, AssemblerMaxVersion) - testProg(t, `int 4; byte "john"; int 5; cover 2; +`, AssemblerMaxVersion, Expect{5, "+ arg 1..."}) + testProg(t, `int 4; byte "john"; int 5; cover 2; +`, AssemblerMaxVersion, Expect{1, "+ arg 1..."}) - testProg(t, `int 4; cover junk`, AssemblerMaxVersion, Expect{2, "cover unable to parse n ..."}) + testProg(t, `int 4; cover junk`, AssemblerMaxVersion, Expect{1, "cover unable to parse n ..."}) } func TestUncoverAsm(t *testing.T) { @@ -2531,38 +2524,38 @@ func TestUncoverAsm(t *testing.T) { testProg(t, `int 4; byte "john"; int 5; uncover 2; +`, AssemblerMaxVersion) testProg(t, `int 4; byte "ayush"; int 5; uncover 1; pop; +`, AssemblerMaxVersion) testProg(t, `int 1; byte "jj"; byte "ayush"; byte "john"; int 5; uncover 4; +`, AssemblerMaxVersion) - testProg(t, `int 4; byte "ayush"; int 5; uncover 1; +`, AssemblerMaxVersion, Expect{5, "+ arg 1..."}) + testProg(t, `int 4; byte "ayush"; int 5; uncover 1; +`, AssemblerMaxVersion, Expect{1, "+ arg 1..."}) } func TestTxTypes(t *testing.T) { partitiontest.PartitionTest(t) t.Parallel() - testProg(t, "itxn_begin; itxn_field Sender", 5, Expect{2, "itxn_field Sender expects 1 stack argument..."}) - testProg(t, "itxn_begin; int 1; itxn_field Sender", 5, Expect{3, "...wanted type []byte got uint64"}) + testProg(t, "itxn_begin; itxn_field Sender", 5, Expect{1, "itxn_field Sender expects 1 stack argument..."}) + testProg(t, "itxn_begin; int 1; itxn_field Sender", 5, Expect{1, "...wanted type []byte got uint64"}) testProg(t, "itxn_begin; byte 0x56127823; itxn_field Sender", 5) - testProg(t, "itxn_begin; itxn_field Amount", 5, Expect{2, "itxn_field Amount expects 1 stack argument..."}) - testProg(t, "itxn_begin; byte 0x87123376; itxn_field Amount", 5, Expect{3, "...wanted type uint64 got []byte"}) + testProg(t, "itxn_begin; itxn_field Amount", 5, Expect{1, "itxn_field Amount expects 1 stack argument..."}) + testProg(t, "itxn_begin; byte 0x87123376; itxn_field Amount", 5, Expect{1, "...wanted type uint64 got []byte"}) testProg(t, "itxn_begin; int 1; itxn_field Amount", 5) } func TestBadInnerFields(t *testing.T) { partitiontest.PartitionTest(t) t.Parallel() - testProg(t, "itxn_begin; int 1000; itxn_field FirstValid", 5, Expect{3, "...is not allowed."}) - testProg(t, "itxn_begin; int 1000; itxn_field FirstValidTime", 5, Expect{3, "...is not allowed."}) - testProg(t, "itxn_begin; int 1000; itxn_field LastValid", 5, Expect{3, "...is not allowed."}) - testProg(t, "itxn_begin; int 32; bzero; itxn_field Lease", 5, Expect{4, "...is not allowed."}) - testProg(t, "itxn_begin; byte 0x7263; itxn_field Note", 5, Expect{3, "...Note field was introduced in v6..."}) - testProg(t, "itxn_begin; byte 0x7263; itxn_field VotePK", 5, Expect{3, "...VotePK field was introduced in v6..."}) - testProg(t, "itxn_begin; int 32; bzero; itxn_field TxID", 5, Expect{4, "...is not allowed."}) - - testProg(t, "itxn_begin; int 1000; itxn_field FirstValid", 6, Expect{3, "...is not allowed."}) - testProg(t, "itxn_begin; int 1000; itxn_field LastValid", 6, Expect{3, "...is not allowed."}) - testProg(t, "itxn_begin; int 32; bzero; itxn_field Lease", 6, Expect{4, "...is not allowed."}) + testProg(t, "itxn_begin; int 1000; itxn_field FirstValid", 5, Expect{1, "...is not allowed."}) + testProg(t, "itxn_begin; int 1000; itxn_field FirstValidTime", 5, Expect{1, "...is not allowed."}) + testProg(t, "itxn_begin; int 1000; itxn_field LastValid", 5, Expect{1, "...is not allowed."}) + testProg(t, "itxn_begin; int 32; bzero; itxn_field Lease", 5, Expect{1, "...is not allowed."}) + testProg(t, "itxn_begin; byte 0x7263; itxn_field Note", 5, Expect{1, "...Note field was introduced in v6..."}) + testProg(t, "itxn_begin; byte 0x7263; itxn_field VotePK", 5, Expect{1, "...VotePK field was introduced in v6..."}) + testProg(t, "itxn_begin; int 32; bzero; itxn_field TxID", 5, Expect{1, "...is not allowed."}) + + testProg(t, "itxn_begin; int 1000; itxn_field FirstValid", 6, Expect{1, "...is not allowed."}) + testProg(t, "itxn_begin; int 1000; itxn_field LastValid", 6, Expect{1, "...is not allowed."}) + testProg(t, "itxn_begin; int 32; bzero; itxn_field Lease", 6, Expect{1, "...is not allowed."}) testProg(t, "itxn_begin; byte 0x7263; itxn_field Note", 6) testProg(t, "itxn_begin; byte 0x7263; itxn_field VotePK", 6) - testProg(t, "itxn_begin; int 32; bzero; itxn_field TxID", 6, Expect{4, "...is not allowed."}) + testProg(t, "itxn_begin; int 32; bzero; itxn_field TxID", 6, Expect{1, "...is not allowed."}) } func TestTypeTracking(t *testing.T) { @@ -2578,7 +2571,7 @@ func TestTypeTracking(t *testing.T) { // but we do want to ensure we're not just treating the code after callsub as dead testProg(t, "callsub A; int 1; concat; return; A: int 1; int 2; retsub", LogicVersion, - Expect{3, "concat arg 1 wanted..."}) + Expect{1, "concat arg 1 wanted..."}) // retsub deadens code, like any unconditional branch testProg(t, "callsub A; +; return; A: int 1; int 2; retsub; concat", LogicVersion) @@ -2716,73 +2709,20 @@ func TestReplacePseudo(t *testing.T) { for v := uint64(replaceVersion); v <= AssemblerMaxVersion; v++ { testProg(t, "byte 0x0000; byte 0x1234; replace 0", v) testProg(t, "byte 0x0000; int 0; byte 0x1234; replace", v) - testProg(t, "byte 0x0000; byte 0x1234; replace", v, Expect{3, "replace without immediates expects 3 stack arguments but stack height is 2"}) - testProg(t, "byte 0x0000; int 0; byte 0x1234; replace 0", v, Expect{4, "replace 0 arg 0 wanted type []byte got uint64"}) + testProg(t, "byte 0x0000; byte 0x1234; replace", v, Expect{1, "replace without immediates expects 3 stack arguments but stack height is 2"}) + testProg(t, "byte 0x0000; int 0; byte 0x1234; replace 0", v, Expect{1, "replace 0 arg 0 wanted type []byte got uint64"}) } } -func TestSemiColon(t *testing.T) { - partitiontest.PartitionTest(t) - t.Parallel() - // Space for weird semicolon cases that might not be checked by rest of tests - checkSame(t, "pushint 0 ; pushint 1 ; +; int 3 ; *", "pushint 0\npushint 1\n+\nint 3\n*") -} - func checkSame(t *testing.T, weird string, normal string) { ops, _ := AssembleStringWithVersion(weird, 7) otherOps, _ := AssembleStringWithVersion(normal, 7) require.Equal(t, otherOps.Program, ops.Program) } -func TestMacros(t *testing.T) { +func TestSemiColon(t *testing.T) { partitiontest.PartitionTest(t) t.Parallel() - ops, _ := AssembleStringWithVersion("#define none 0\n#define one 1\npushint none; pushint one; +", 7) - otherOps, _ := AssembleStringWithVersion("pushint 0\npushint 1\n+", 7) - require.Equal(t, otherOps.Program, ops.Program) - - checkSame(t, ` -#define ==? ==; bnz -#define one 1 -#define two 2 -pushint one; pushint two; ==? label1 -err -label1: -pushint one`, ` - -pushint 1 -pushint 2 -== -bnz label1 -err -label1: -pushint 1`) - - checkSame(t, ` -#define rowSize 3 -#define columnSize 5 -#define tableDimensions rowSize columnSize -pushbytes 0x100000000000; substring tableDimensions -#define rowSize 0 -#define columnSize 1 -substring tableDimensions`, ` - -pushbytes 0x100000000000; substring 3 5; substring 0 1`) - - checkSame(t, ` -#define &x 0 -#define x load &x; -#define &y 1 -#define y load &y; -#define -> ; store -int 3 -> &x; int 4 -> &y -x y <`, ` - -int 3 -store 0 -int 4 -store 1 -load 0 -load 1 -<`) + // Space for weird semicolon cases that might not be checked by rest of tests + checkSame(t, "pushint 0 ; pushint 1 ; +; int 3 ; *", "pushint 0\npushint 1\n+\nint 3\n*") } diff --git a/data/transactions/logic/backwardCompat_test.go b/data/transactions/logic/backwardCompat_test.go index 086741dcde..13a19ddaa7 100644 --- a/data/transactions/logic/backwardCompat_test.go +++ b/data/transactions/logic/backwardCompat_test.go @@ -467,15 +467,15 @@ func TestBackwardCompatAssemble(t *testing.T) { source := "int 1; int 1; bnz done; done:" t.Run("v=default", func(t *testing.T) { - testProg(t, source, assemblerNoVersion, Expect{4, "label \"done\" is too far away"}) + testProg(t, source, assemblerNoVersion, Expect{1, "label \"done\" is too far away"}) }) t.Run("v=default", func(t *testing.T) { - testProg(t, source, 0, Expect{4, "label \"done\" is too far away"}) + testProg(t, source, 0, Expect{1, "label \"done\" is too far away"}) }) t.Run("v=default", func(t *testing.T) { - testProg(t, source, 1, Expect{4, "label \"done\" is too far away"}) + testProg(t, source, 1, Expect{1, "label \"done\" is too far away"}) }) for v := uint64(2); v <= AssemblerMaxVersion; v++ { diff --git a/data/transactions/logic/evalAppTxn_test.go b/data/transactions/logic/evalAppTxn_test.go index cd0229321a..de9aada851 100644 --- a/data/transactions/logic/evalAppTxn_test.go +++ b/data/transactions/logic/evalAppTxn_test.go @@ -456,25 +456,25 @@ func TestNumInnerShallow(t *testing.T) { ep.Reset() ledger.NewApp(tx.Receiver, 888, basics.AppParams{}) ledger.NewAccount(appAddr(888), 1000000) - TestApp(t, pay+";int 1", ep) - TestApp(t, pay+pay+";int 1", ep) - TestApp(t, pay+pay+pay+";int 1", ep) - TestApp(t, pay+pay+pay+pay+";int 1", ep) + TestApp(t, pay+"; int 1", ep) + TestApp(t, pay+pay+"; int 1", ep) + TestApp(t, pay+pay+pay+"; int 1", ep) + TestApp(t, pay+pay+pay+pay+"; int 1", ep) // In the sample proto, MaxInnerTransactions = 4 - TestApp(t, pay+pay+pay+pay+pay+";int 1", ep, "too many inner transactions") + TestApp(t, pay+pay+pay+pay+pay+"; int 1", ep, "too many inner transactions") ep, tx, ledger = MakeSampleEnv() ledger.NewApp(tx.Receiver, 888, basics.AppParams{}) ledger.NewAccount(appAddr(888), 1000000) - TestApp(t, pay+";int 1", ep) - TestApp(t, pay+pay+";int 1", ep) - TestApp(t, pay+pay+pay+";int 1", ep) - TestApp(t, pay+pay+pay+pay+";int 1", ep) + TestApp(t, pay+"; int 1", ep) + TestApp(t, pay+pay+"; int 1", ep) + TestApp(t, pay+pay+pay+"; int 1", ep) + TestApp(t, pay+pay+pay+pay+"; int 1", ep) // In the sample proto, MaxInnerTransactions = 4, but when pooling you get // MaxTxGroupSize (here, 8) * that. - TestApp(t, pay+pay+pay+pay+pay+";int 1", ep) - TestApp(t, strings.Repeat(pay, 32)+";int 1", ep) - TestApp(t, strings.Repeat(pay, 33)+";int 1", ep, "too many inner transactions") + TestApp(t, pay+pay+pay+pay+pay+"; int 1", ep) + TestApp(t, strings.Repeat(pay, 32)+"; int 1", ep) + TestApp(t, strings.Repeat(pay, 33)+"; int 1", ep, "too many inner transactions") } // TestNumInnerPooled ensures that inner call limits are pooled across app calls @@ -499,8 +499,8 @@ func TestNumInnerPooled(t *testing.T) { ledger := MakeLedger(nil) ledger.NewApp(tx.Txn.Receiver, 888, basics.AppParams{}) ledger.NewAccount(appAddr(888), 1000000) - short := pay + ";int 1" - long := strings.Repeat(pay, 17) + ";int 1" // More than half allowed + short := pay + "; int 1" + long := strings.Repeat(pay, 17) + "; int 1" // More than half allowed grp := MakeSampleTxnGroup(tx) TestApps(t, []string{short, ""}, grp, LogicVersion, ledger) @@ -812,7 +812,7 @@ txn Sender; itxn_field Receiver; // Force the first fee to 3, but the second will default to 2*fee-3 = 2002-3 TestApp(t, "itxn_begin"+ pay+ - "int 3; itxn_field Fee;"+ + "int 3; itxn_field Fee; "+ "itxn_next"+ pay+ "itxn_submit; itxn Fee; int 1999; ==", ep) @@ -820,16 +820,16 @@ txn Sender; itxn_field Receiver; // Same as first, but force the second too low TestApp(t, "itxn_begin"+ pay+ - "int 3; itxn_field Fee;"+ + "int 3; itxn_field Fee; "+ "itxn_next"+ pay+ - "int 1998; itxn_field Fee;"+ + "int 1998; itxn_field Fee; "+ "itxn_submit; int 1", ep, "fee too small") // Overpay in first itxn, the second will default to less TestApp(t, "itxn_begin"+ pay+ - "int 2000; itxn_field Fee;"+ + "int 2000; itxn_field Fee; "+ "itxn_next"+ pay+ "itxn_submit; itxn Fee; int 2; ==", ep) @@ -837,25 +837,25 @@ txn Sender; itxn_field Receiver; // Same first, but force the second too low TestApp(t, "itxn_begin"+ pay+ - "int 2000; itxn_field Fee;"+ + "int 2000; itxn_field Fee; "+ "itxn_next"+ pay+ - "int 1; itxn_field Fee;"+ + "int 1; itxn_field Fee; "+ "itxn_submit; itxn Fee; int 1", ep, "fee too small") // Test that overpay in first inner group is available in second inner group // also ensure only exactly the _right_ amount of credit is available. TestApp(t, "itxn_begin"+ pay+ - "int 2002; itxn_field Fee;"+ // double pay + "int 2002; itxn_field Fee; "+ // double pay "itxn_next"+ pay+ - "int 1001; itxn_field Fee;"+ // regular pay - "itxn_submit;"+ + "int 1001; itxn_field Fee; "+ // regular pay + "itxn_submit; "+ // At beginning of second group, we should have 1 minfee of credit "itxn_begin"+ pay+ - "int 0; itxn_field Fee;"+ // free, due to credit + "int 0; itxn_field Fee; "+ // free, due to credit "itxn_next"+ pay+ "itxn_submit; itxn Fee; int 1001; ==", // second one should have to pay @@ -871,7 +871,7 @@ func TestApplCreation(t *testing.T) { ep, tx, _ := MakeSampleEnv() - p := "itxn_begin;" + p := "itxn_begin; " s := "; int 1" TestApp(t, p+"int 31; itxn_field ApplicationID"+s, ep, @@ -899,31 +899,31 @@ func TestApplCreation(t *testing.T) { TestApp(t, p+"int 401; bzero; dup; itxn_field ApplicationArgs; itxn_field ApplicationArgs", ep, "length too long") - TestApp(t, p+strings.Repeat("byte 0x11; itxn_field ApplicationArgs;", 12)+s, ep) - TestApp(t, p+strings.Repeat("byte 0x11; itxn_field ApplicationArgs;", 13)+s, ep, + TestApp(t, p+strings.Repeat("byte 0x11; itxn_field ApplicationArgs; ", 12)+s, ep) + TestApp(t, p+strings.Repeat("byte 0x11; itxn_field ApplicationArgs; ", 13)+s, ep, "too many application args") - TestApp(t, p+strings.Repeat("int 32; bzero; itxn_field Accounts;", 3)+s, ep, + TestApp(t, p+strings.Repeat("int 32; bzero; itxn_field Accounts; ", 3)+s, ep, "invalid Account reference") tx.Accounts = append(tx.Accounts, basics.Address{}) TestApp(t, fmt.Sprintf(p+"%s"+s, - strings.Repeat("int 32; bzero; itxn_field Accounts;", 3)), ep) + strings.Repeat("int 32; bzero; itxn_field Accounts; ", 3)), ep) TestApp(t, fmt.Sprintf(p+"%s"+s, - strings.Repeat("int 32; bzero; itxn_field Accounts;", 4)), ep, + strings.Repeat("int 32; bzero; itxn_field Accounts; ", 4)), ep, "too many foreign accounts") - TestApp(t, p+strings.Repeat("int 621; itxn_field Applications;", 5)+s, ep, + TestApp(t, p+strings.Repeat("int 621; itxn_field Applications; ", 5)+s, ep, "invalid App reference") tx.ForeignApps = append(tx.ForeignApps, basics.AppIndex(621)) - TestApp(t, p+strings.Repeat("int 621; itxn_field Applications;", 5)+s, ep) - TestApp(t, p+strings.Repeat("int 621; itxn_field Applications;", 6)+s, ep, + TestApp(t, p+strings.Repeat("int 621; itxn_field Applications; ", 5)+s, ep) + TestApp(t, p+strings.Repeat("int 621; itxn_field Applications; ", 6)+s, ep, "too many foreign apps") - TestApp(t, p+strings.Repeat("int 621; itxn_field Assets;", 6)+s, ep, + TestApp(t, p+strings.Repeat("int 621; itxn_field Assets; ", 6)+s, ep, "invalid Asset reference") tx.ForeignAssets = append(tx.ForeignAssets, basics.AssetIndex(621)) - TestApp(t, p+strings.Repeat("int 621; itxn_field Assets;", 6)+s, ep) - TestApp(t, p+strings.Repeat("int 621; itxn_field Assets;", 7)+s, ep, + TestApp(t, p+strings.Repeat("int 621; itxn_field Assets; ", 6)+s, ep) + TestApp(t, p+strings.Repeat("int 621; itxn_field Assets; ", 7)+s, ep, "too many foreign assets") TestApp(t, p+"int 2700; bzero; itxn_field ApprovalProgram"+s, ep) @@ -955,7 +955,7 @@ func TestBigApplCreation(t *testing.T) { ep, _, _ := MakeSampleEnv() - p := "itxn_begin;" + p := "itxn_begin; " s := "; int 1" // Recall that in test proto, max possible program size is 2700, because @@ -1014,39 +1014,39 @@ func TestApplSubmission(t *testing.T) { ops := TestProg(t, "int 1", AssemblerMaxVersion) approve := hex.EncodeToString(ops.Program) - a := fmt.Sprintf("byte 0x%s; itxn_field ApprovalProgram;", approve) + a := fmt.Sprintf("byte 0x%s; itxn_field ApprovalProgram; ", approve) - p := "itxn_begin; int appl; itxn_field TypeEnum;" - s := ";itxn_submit; int 1" + p := "itxn_begin; int appl; itxn_field TypeEnum; " + s := "itxn_submit; int 1" TestApp(t, p+a+s, ep, "ClearStateProgram: invalid program (empty)") - a += fmt.Sprintf("byte 0x%s; itxn_field ClearStateProgram;", approve) + a += fmt.Sprintf("byte 0x%s; itxn_field ClearStateProgram; ", approve) // All zeros is v0, so we get a complaint, but that means lengths were ok when set. TestApp(t, p+a+`int 600; bzero; itxn_field ApprovalProgram; - int 600; bzero; itxn_field ClearStateProgram;`+s, ep, + int 600; bzero; itxn_field ClearStateProgram; `+s, ep, "inner app call with version v0 < v4") TestApp(t, p+`int 601; bzero; itxn_field ApprovalProgram; - int 600; bzero; itxn_field ClearStateProgram;`+s, ep, "too long") + int 600; bzero; itxn_field ClearStateProgram; `+s, ep, "too long") // WellFormed does the math based on the supplied ExtraProgramPages TestApp(t, p+a+`int 1; itxn_field ExtraProgramPages int 1200; bzero; itxn_field ApprovalProgram; - int 1200; bzero; itxn_field ClearStateProgram;`+s, ep, + int 1200; bzero; itxn_field ClearStateProgram; `+s, ep, "inner app call with version v0 < v4") TestApp(t, p+`int 1; itxn_field ExtraProgramPages int 1200; bzero; itxn_field ApprovalProgram; - int 1201; bzero; itxn_field ClearStateProgram;`+s, ep, "too long") + int 1201; bzero; itxn_field ClearStateProgram; `+s, ep, "too long") // Can't set epp when app id is given tx.ForeignApps = append(tx.ForeignApps, basics.AppIndex(7)) TestApp(t, p+`int 1; itxn_field ExtraProgramPages; - int 7; itxn_field ApplicationID`+s, ep, "immutable") + int 7; itxn_field ApplicationID; `+s, ep, "immutable") - TestApp(t, p+a+"int 20; itxn_field GlobalNumUint; int 11; itxn_field GlobalNumByteSlice"+s, + TestApp(t, p+a+"int 20; itxn_field GlobalNumUint; int 11; itxn_field GlobalNumByteSlice; "+s, ep, "too large") - TestApp(t, p+a+"int 7; itxn_field LocalNumUint; int 7; itxn_field LocalNumByteSlice"+s, + TestApp(t, p+a+"int 7; itxn_field LocalNumUint; int 7; itxn_field LocalNumByteSlice; "+s, ep, "too large") } @@ -1261,7 +1261,7 @@ func TestInnerBudgetIncrement(t *testing.T) { ApprovalProgram: gasup.Program, }) - waste := `global CurrentApplicationAddress; keccak256; pop;` + waste := `global CurrentApplicationAddress; keccak256; pop; ` buy := `itxn_begin int appl; itxn_field TypeEnum int 222; itxn_field ApplicationID diff --git a/data/transactions/logic/evalStateful_test.go b/data/transactions/logic/evalStateful_test.go index 820f52ff49..d19e1e93c4 100644 --- a/data/transactions/logic/evalStateful_test.go +++ b/data/transactions/logic/evalStateful_test.go @@ -313,7 +313,7 @@ func TestBalance(t *testing.T) { text = `txn Accounts 1; balance; int 177; ==;` // won't assemble in old version teal - testProg(t, text, directRefEnabledVersion-1, Expect{2, "balance arg 0 wanted type uint64..."}) + testProg(t, text, directRefEnabledVersion-1, Expect{1, "balance arg 0 wanted type uint64..."}) // but legal after that testApp(t, text, ep) @@ -474,7 +474,7 @@ func TestMinBalance(t *testing.T) { testApp(t, "int 1; min_balance; int 1001; ==", ep) // 1 == Accounts[0] testProg(t, "txn Accounts 1; min_balance; int 1001; ==", directRefEnabledVersion-1, - Expect{2, "min_balance arg 0 wanted type uint64..."}) + Expect{1, "min_balance arg 0 wanted type uint64..."}) testProg(t, "txn Accounts 1; min_balance; int 1001; ==", directRefEnabledVersion) testApp(t, "txn Accounts 1; min_balance; int 1001; ==", ep) // 1 == Accounts[0] // Receiver opts in @@ -527,7 +527,7 @@ func TestAppCheckOptedIn(t *testing.T) { testApp(t, "int 1; int 2; app_opted_in; int 0; ==", pre) // in pre, int 2 is an actual app id testApp(t, "byte \"aoeuiaoeuiaoeuiaoeuiaoeuiaoeui01\"; int 2; app_opted_in; int 1; ==", now) testProg(t, "byte \"aoeuiaoeuiaoeuiaoeuiaoeuiaoeui01\"; int 2; app_opted_in; int 1; ==", directRefEnabledVersion-1, - Expect{3, "app_opted_in arg 0 wanted type uint64..."}) + Expect{1, "app_opted_in arg 0 wanted type uint64..."}) // Receiver opts into 888, the current app in testApp ledger.NewLocals(txn.Txn.Receiver, 888) @@ -938,7 +938,7 @@ func testAssetsByVersion(t *testing.T, assetsTestProgram string, version uint64) // it wasn't legal to use a direct ref for account testProg(t, `byte "aoeuiaoeuiaoeuiaoeuiaoeuiaoeui00"; int 54; asset_holding_get AssetBalance`, - directRefEnabledVersion-1, Expect{3, "asset_holding_get AssetBalance arg 0 wanted type uint64..."}) + directRefEnabledVersion-1, Expect{1, "asset_holding_get AssetBalance arg 0 wanted type uint64..."}) // but it is now (empty asset yields 0,0 on stack) testApp(t, `byte "aoeuiaoeuiaoeuiaoeuiaoeuiaoeui00"; int 55; asset_holding_get AssetBalance; ==`, now) // This is receiver, who is in Assets array @@ -2576,7 +2576,7 @@ func TestAppLoop(t *testing.T) { t.Parallel() ep, _, _ := makeSampleEnv() - stateful := "global CurrentApplicationID; pop;" + stateful := "global CurrentApplicationID; pop; " // Double until > 10. Should be 16 testApp(t, stateful+"int 1; loop: int 2; *; dup; int 10; <; bnz loop; int 16; ==", ep) diff --git a/data/transactions/logic/eval_test.go b/data/transactions/logic/eval_test.go index 59393792fa..d5ccf865c2 100644 --- a/data/transactions/logic/eval_test.go +++ b/data/transactions/logic/eval_test.go @@ -337,12 +337,12 @@ func TestSimpleMath(t *testing.T) { partitiontest.PartitionTest(t) t.Parallel() - testAccepts(t, "int 2; int 3; + ;int 5;==", 1) - testAccepts(t, "int 22; int 3; - ;int 19;==", 1) - testAccepts(t, "int 8; int 7; * ;int 56;==", 1) - testAccepts(t, "int 21; int 7; / ;int 3;==", 1) + testAccepts(t, "int 2; int 3; + ; int 5; ==", 1) + testAccepts(t, "int 22; int 3; - ; int 19; ==", 1) + testAccepts(t, "int 8; int 7; * ; int 56; ==", 1) + testAccepts(t, "int 21; int 7; / ; int 3; ==", 1) - testPanics(t, "int 1; int 2; - ;int 0; ==", 1) + testPanics(t, "int 1; int 2; - ; int 0; ==", 1) } func TestSha256EqArg(t *testing.T) { @@ -959,7 +959,7 @@ func TestArg(t *testing.T) { t.Parallel() for v := uint64(1); v <= AssemblerMaxVersion; v++ { t.Run(fmt.Sprintf("v=%d", v), func(t *testing.T) { - source := "arg 0; arg 1; ==; arg 2; arg 3; !=; &&; arg 4; len; int 9; <; &&;" + source := "arg 0; arg 1; ==; arg 2; arg 3; !=; &&; arg 4; len; int 9; <; &&; " if v >= 5 { source += "int 0; args; int 1; args; ==; assert; int 2; args; int 3; args; !=; assert" } @@ -2824,16 +2824,16 @@ func TestSlowLogic(t *testing.T) { t.Parallel() fragment := `byte 0x666E6F7264; keccak256 - byte 0xc195eca25a6f4c82bfba0287082ddb0d602ae9230f9cf1f1a40b68f8e2c41567; ==;` + byte 0xc195eca25a6f4c82bfba0287082ddb0d602ae9230f9cf1f1a40b68f8e2c41567; ==; ` // Sanity check. Running a short sequence of these fragments passes in all versions. - source := fragment + strings.Repeat(fragment+"&&;", 5) + source := fragment + strings.Repeat(fragment+"&&; ", 5) testAccepts(t, source, 1) // in v1, each repeat costs 30 - v1overspend := fragment + strings.Repeat(fragment+"&&;", 20000/30) + v1overspend := fragment + strings.Repeat(fragment+"&&; ", 20000/30) // in v2,v3 each repeat costs 134 - v2overspend := fragment + strings.Repeat(fragment+"&&;", 20000/134) + v2overspend := fragment + strings.Repeat(fragment+"&&; ", 20000/134) // v1overspend fails (on v1) ops := testProg(t, v1overspend, 1) @@ -3846,9 +3846,9 @@ func TestStackOverflow(t *testing.T) { partitiontest.PartitionTest(t) t.Parallel() - source := "int 1; int 2;" + source := "int 1; int 2; " for i := 1; i < maxStackDepth/2; i++ { - source += "dup2;" + source += "dup2; " } testAccepts(t, source+"return", 2) testPanics(t, source+"dup2; return", 2) @@ -4252,11 +4252,11 @@ func TestAssert(t *testing.T) { partitiontest.PartitionTest(t) t.Parallel() - testAccepts(t, "int 1;assert;int 1", 3) - testRejects(t, "int 1;assert;int 0", 3) - testPanics(t, "int 0;assert;int 1", 3) - testPanics(t, notrack("assert;int 1"), 3) - testPanics(t, notrack(`byte "john";assert;int 1`), 3) + testAccepts(t, "int 1; assert; int 1", 3) + testRejects(t, "int 1; assert; int 0", 3) + testPanics(t, "int 0; assert; int 1", 3) + testPanics(t, notrack("assert; int 1"), 3) + testPanics(t, notrack(`byte "john"; assert; int 1`), 3) } func TestBits(t *testing.T) { @@ -4752,7 +4752,7 @@ func TestLog(t *testing.T) { loglen: 2, }, { - source: fmt.Sprintf(`%s int 1`, strings.Repeat(`byte "a logging message"; log;`, maxLogCalls)), + source: fmt.Sprintf(`%s int 1`, strings.Repeat(`byte "a logging message"; log; `, maxLogCalls)), loglen: maxLogCalls, }, { @@ -4797,7 +4797,7 @@ func TestLog(t *testing.T) { runMode: modeApp, }, { - source: fmt.Sprintf(`%s; int 1`, strings.Repeat(`byte "a"; log;`, maxLogCalls+1)), + source: fmt.Sprintf(`%s; int 1`, strings.Repeat(`byte "a"; log; `, maxLogCalls+1)), errContains: "too many log calls", runMode: modeApp, }, @@ -5109,7 +5109,7 @@ func TestOpJSONRef(t *testing.T) { json_ref JSONUint64; int 0; ==`, - previousVersErrors: []Expect{{5, "unknown opcode: json_ref"}}, + previousVersErrors: []Expect{{3, "unknown opcode: json_ref"}}, }, { source: `byte "{\"key0\": 0,\"key1\": \"algo\",\"key2\":{\"key3\": \"teal\", \"key4\": 3}, \"key5\": 18446744073709551615 }"; @@ -5117,7 +5117,7 @@ func TestOpJSONRef(t *testing.T) { json_ref JSONUint64; int 18446744073709551615; //max uint64 value ==`, - previousVersErrors: []Expect{{5, "unknown opcode: json_ref"}}, + previousVersErrors: []Expect{{3, "unknown opcode: json_ref"}}, }, { source: `byte "{\"key0\": 0,\"key1\": \"algo\",\"key2\":{\"key3\": \"teal\", \"key4\": 3}, \"key5\": 18446744073709551615 }"; @@ -5125,7 +5125,7 @@ func TestOpJSONRef(t *testing.T) { json_ref JSONString; byte "algo"; ==`, - previousVersErrors: []Expect{{5, "unknown opcode: json_ref"}}, + previousVersErrors: []Expect{{3, "unknown opcode: json_ref"}}, }, { source: `byte "{\"key0\": 0,\"key1\": \"\\u0061\\u006C\\u0067\\u006F\",\"key2\":{\"key3\": \"teal\", \"key4\": 3}, \"key5\": 18446744073709551615 }"; @@ -5133,7 +5133,7 @@ func TestOpJSONRef(t *testing.T) { json_ref JSONString; byte "algo"; ==`, - previousVersErrors: []Expect{{5, "unknown opcode: json_ref"}}, + previousVersErrors: []Expect{{3, "unknown opcode: json_ref"}}, }, { source: `byte "{\"key0\": 0,\"key1\": \"algo\",\"key2\":{\"key3\": \"teal\", \"key4\": {\"key40\": 10}}, \"key5\": 18446744073709551615 }"; @@ -5145,7 +5145,7 @@ func TestOpJSONRef(t *testing.T) { json_ref JSONUint64 int 10 ==`, - previousVersErrors: []Expect{{5, "unknown opcode: json_ref"}, {9, "unknown opcode: json_ref"}}, + previousVersErrors: []Expect{{3, "unknown opcode: json_ref"}, {5, "unknown opcode: json_ref"}}, }, { source: `byte "{\"key0\": 0,\"key1\": \"algo\",\"key2\":{\"key3\": \"teal\", \"key4\": {\"key40\": 10}}, \"key5\": 18446744073709551615 }"; @@ -5155,7 +5155,7 @@ func TestOpJSONRef(t *testing.T) { json_ref JSONString; byte "teal" ==`, - previousVersErrors: []Expect{{5, "unknown opcode: json_ref"}, {9, "unknown opcode: json_ref"}}, + previousVersErrors: []Expect{{3, "unknown opcode: json_ref"}, {5, "unknown opcode: json_ref"}}, }, { source: `byte "{\"key0\": 0,\"key1\": \"algo\",\"key2\":{\"key3\": \"\\"teal\\"\", \"key4\": {\"key40\": 10}}, \"key5\": 18446744073709551615 }"; @@ -5165,7 +5165,7 @@ func TestOpJSONRef(t *testing.T) { json_ref JSONString; byte ""teal"" // quotes match ==`, - previousVersErrors: []Expect{{5, "unknown opcode: json_ref"}, {9, "unknown opcode: json_ref"}}, + previousVersErrors: []Expect{{3, "unknown opcode: json_ref"}, {5, "unknown opcode: json_ref"}}, }, { source: `byte "{\"key0\": 0,\"key1\": \"algo\",\"key2\":{\"key3\": \" teal \", \"key4\": {\"key40\": 10}}, \"key5\": 18446744073709551615 }"; @@ -5175,7 +5175,7 @@ func TestOpJSONRef(t *testing.T) { json_ref JSONString; byte " teal " // spaces match ==`, - previousVersErrors: []Expect{{5, "unknown opcode: json_ref"}, {9, "unknown opcode: json_ref"}}, + previousVersErrors: []Expect{{3, "unknown opcode: json_ref"}, {5, "unknown opcode: json_ref"}}, }, { source: `byte "{\"key0\": 0,\"key1\": \"algo\",\"key2\":{\"key3\": \"teal\", \"key4\": {\"key40\": 10, \"key40\": \"10\"}}, \"key5\": 18446744073709551615 }"; @@ -5186,7 +5186,7 @@ func TestOpJSONRef(t *testing.T) { byte "{\"key40\": 10, \"key40\": \"10\"}" == `, - previousVersErrors: []Expect{{5, "unknown opcode: json_ref"}}, + previousVersErrors: []Expect{{3, "unknown opcode: json_ref"}}, }, { source: `byte "{\"rawId\": \"responseId\",\"id\": \"0\",\"response\": {\"attestationObject\": \"based64url_encoded_buffer\",\"clientDataJSON\": \" based64url_encoded_client_data\"},\"getClientExtensionResults\": {},\"type\": \"public-key\"}"; @@ -5194,7 +5194,7 @@ func TestOpJSONRef(t *testing.T) { json_ref JSONObject; byte "{\"attestationObject\": \"based64url_encoded_buffer\",\"clientDataJSON\": \" based64url_encoded_client_data\"}" // object as it appeared in input ==`, - previousVersErrors: []Expect{{5, "unknown opcode: json_ref"}}, + previousVersErrors: []Expect{{3, "unknown opcode: json_ref"}}, }, { source: `byte "{\"rawId\": \"responseId\",\"id\": \"0\",\"response\": {\"attestationObject\": \"based64url_encoded_buffer\",\"clientD\\u0061taJSON\": \" based64url_encoded_client_data\"},\"getClientExtensionResults\": {},\"type\": \"public-key\"}"; @@ -5202,7 +5202,7 @@ func TestOpJSONRef(t *testing.T) { json_ref JSONObject; byte "{\"attestationObject\": \"based64url_encoded_buffer\",\"clientD\\u0061taJSON\": \" based64url_encoded_client_data\"}" // object as it appeared in input ==`, - previousVersErrors: []Expect{{5, "unknown opcode: json_ref"}}, + previousVersErrors: []Expect{{3, "unknown opcode: json_ref"}}, }, { source: `byte "{\"rawId\": \"responseId\",\"id\": \"0\",\"response\": {\"attestationObject\": \"based64url_encoded_buffer\",\"clientDataJSON\": \" based64url_encoded_client_data\"},\"getClientExtensionResults\": {},\"type\": \"public-key\"}"; @@ -5212,7 +5212,7 @@ func TestOpJSONRef(t *testing.T) { json_ref JSONString; byte " based64url_encoded_client_data"; ==`, - previousVersErrors: []Expect{{5, "unknown opcode: json_ref"}, {9, "unknown opcode: json_ref"}}, + previousVersErrors: []Expect{{3, "unknown opcode: json_ref"}, {5, "unknown opcode: json_ref"}}, }, { source: `byte "{\"\\u0072\\u0061\\u0077\\u0049\\u0044\": \"responseId\",\"id\": \"0\",\"response\": {\"attestationObject\": \"based64url_encoded_buffer\",\"clientDataJSON\": \" based64url_encoded_client_data\"},\"getClientExtensionResults\": {},\"type\": \"public-key\"}"; @@ -5220,7 +5220,7 @@ func TestOpJSONRef(t *testing.T) { json_ref JSONString; byte "responseId" ==`, - previousVersErrors: []Expect{{5, "unknown opcode: json_ref"}}, + previousVersErrors: []Expect{{3, "unknown opcode: json_ref"}}, }, // JavaScript MAX_SAFE_INTEGER { @@ -5229,7 +5229,7 @@ func TestOpJSONRef(t *testing.T) { json_ref JSONUint64; int 9007199254740991; ==`, - previousVersErrors: []Expect{{5, "unknown opcode: json_ref"}}, + previousVersErrors: []Expect{{3, "unknown opcode: json_ref"}}, }, // maximum uint64 { @@ -5238,7 +5238,7 @@ func TestOpJSONRef(t *testing.T) { json_ref JSONUint64; int 18446744073709551615; ==`, - previousVersErrors: []Expect{{5, "unknown opcode: json_ref"}}, + previousVersErrors: []Expect{{3, "unknown opcode: json_ref"}}, }, // larger-than-uint64s are allowed if not requested { @@ -5247,7 +5247,7 @@ func TestOpJSONRef(t *testing.T) { json_ref JSONUint64; int 0; ==`, - previousVersErrors: []Expect{{5, "unknown opcode: json_ref"}}, + previousVersErrors: []Expect{{3, "unknown opcode: json_ref"}}, }, } @@ -5287,52 +5287,52 @@ func TestOpJSONRef(t *testing.T) { { source: `byte "{\"key0\": 1 }"; byte "key0"; json_ref JSONString;`, error: "json: cannot unmarshal number into Go value of type string", - previousVersErrors: []Expect{{3, "unknown opcode: json_ref"}}, + previousVersErrors: []Expect{{1, "unknown opcode: json_ref"}}, }, { source: `byte "{\"key0\": [1] }"; byte "key0"; json_ref JSONString;`, error: "json: cannot unmarshal array into Go value of type string", - previousVersErrors: []Expect{{3, "unknown opcode: json_ref"}}, + previousVersErrors: []Expect{{1, "unknown opcode: json_ref"}}, }, { source: `byte "{\"key0\": {\"key1\":1} }"; byte "key0"; json_ref JSONString;`, error: "json: cannot unmarshal object into Go value of type string", - previousVersErrors: []Expect{{3, "unknown opcode: json_ref"}}, + previousVersErrors: []Expect{{1, "unknown opcode: json_ref"}}, }, { source: `byte "{\"key0\": \"1\" }"; byte "key0"; json_ref JSONUint64;`, error: "json: cannot unmarshal string into Go value of type uint64", - previousVersErrors: []Expect{{3, "unknown opcode: json_ref"}}, + previousVersErrors: []Expect{{1, "unknown opcode: json_ref"}}, }, { source: `byte "{\"key0\": [\"1\"] }"; byte "key0"; json_ref JSONUint64;`, error: "json: cannot unmarshal array into Go value of type uint64", - previousVersErrors: []Expect{{3, "unknown opcode: json_ref"}}, + previousVersErrors: []Expect{{1, "unknown opcode: json_ref"}}, }, { source: `byte "{\"key0\": {\"key1\":1} }"; byte "key0"; json_ref JSONUint64;`, error: "json: cannot unmarshal object into Go value of type uint64", - previousVersErrors: []Expect{{3, "unknown opcode: json_ref"}}, + previousVersErrors: []Expect{{1, "unknown opcode: json_ref"}}, }, { source: `byte "{\"key0\": [1]}"; byte "key0"; json_ref JSONObject;`, error: "json: cannot unmarshal array into Go value of type map[string]json.RawMessage", - previousVersErrors: []Expect{{3, "unknown opcode: json_ref"}}, + previousVersErrors: []Expect{{1, "unknown opcode: json_ref"}}, }, { source: `byte "{\"key0\": 1}"; byte "key0"; json_ref JSONObject;`, error: "json: cannot unmarshal number into Go value of type map[string]json.RawMessage", - previousVersErrors: []Expect{{3, "unknown opcode: json_ref"}}, + previousVersErrors: []Expect{{1, "unknown opcode: json_ref"}}, }, { source: `byte "{\"key0\": \"1\"}"; byte "key0"; json_ref JSONObject;`, error: "json: cannot unmarshal string into Go value of type map[string]json.RawMessage", - previousVersErrors: []Expect{{3, "unknown opcode: json_ref"}}, + previousVersErrors: []Expect{{1, "unknown opcode: json_ref"}}, }, { source: `byte "{\"key0\": 1,\"key1\": \"algo\",\"key2\":{\"key3\": \"teal\", \"key4\": [1,2,3]} }"; byte "key3"; json_ref JSONString;`, error: "key key3 not found in JSON text", - previousVersErrors: []Expect{{3, "unknown opcode: json_ref"}}, + previousVersErrors: []Expect{{1, "unknown opcode: json_ref"}}, }, { source: `byte "{\"key0\": 1,\"key1\": \"algo\",\"key2\":{\"key3\": \"teal\", \"key4\": [1,2,3]}}"; @@ -5342,52 +5342,52 @@ func TestOpJSONRef(t *testing.T) { json_ref JSONString `, error: "key key5 not found in JSON text", - previousVersErrors: []Expect{{5, "unknown opcode: json_ref"}, {9, "unknown opcode: json_ref"}}, + previousVersErrors: []Expect{{3, "unknown opcode: json_ref"}, {5, "unknown opcode: json_ref"}}, }, { source: `byte "{\"key0\": -0,\"key1\": 2.5,\"key2\": -3}"; byte "key0"; json_ref JSONUint64;`, error: "json: cannot unmarshal number -0 into Go value of type uint64", - previousVersErrors: []Expect{{3, "unknown opcode: json_ref"}}, + previousVersErrors: []Expect{{1, "unknown opcode: json_ref"}}, }, { source: `byte "{\"key0\": 1e10,\"key1\": 2.5,\"key2\": -3}"; byte "key0"; json_ref JSONUint64;`, error: "json: cannot unmarshal number 1e10 into Go value of type uint64", - previousVersErrors: []Expect{{3, "unknown opcode: json_ref"}}, + previousVersErrors: []Expect{{1, "unknown opcode: json_ref"}}, }, { source: `byte "{\"key0\": 0.2e-2,\"key1\": 2.5,\"key2\": -3}"; byte "key0"; json_ref JSONUint64;`, error: "json: cannot unmarshal number 0.2e-2 into Go value of type uint64", - previousVersErrors: []Expect{{3, "unknown opcode: json_ref"}}, + previousVersErrors: []Expect{{1, "unknown opcode: json_ref"}}, }, { source: `byte "{\"key0\": 1.0,\"key1\": 2.5,\"key2\": -3}"; byte "key0"; json_ref JSONUint64;`, error: "json: cannot unmarshal number 1.0 into Go value of type uint64", - previousVersErrors: []Expect{{3, "unknown opcode: json_ref"}}, + previousVersErrors: []Expect{{1, "unknown opcode: json_ref"}}, }, { source: `byte "{\"key0\": 1.0,\"key1\": 2.5,\"key2\": -3}"; byte "key1"; json_ref JSONUint64;`, error: "json: cannot unmarshal number 2.5 into Go value of type uint64", - previousVersErrors: []Expect{{3, "unknown opcode: json_ref"}}, + previousVersErrors: []Expect{{1, "unknown opcode: json_ref"}}, }, { source: `byte "{\"key0\": 1.0,\"key1\": 2.5,\"key2\": -3}"; byte "key2"; json_ref JSONUint64;`, error: "json: cannot unmarshal number -3 into Go value of type uint64", - previousVersErrors: []Expect{{3, "unknown opcode: json_ref"}}, + previousVersErrors: []Expect{{1, "unknown opcode: json_ref"}}, }, { source: `byte "{\"key0\": 18446744073709551616}"; byte "key0"; json_ref JSONUint64;`, error: "json: cannot unmarshal number 18446744073709551616 into Go value of type uint64", - previousVersErrors: []Expect{{3, "unknown opcode: json_ref"}}, + previousVersErrors: []Expect{{1, "unknown opcode: json_ref"}}, }, { source: `byte "{\"key0\": 1,}"; byte "key0"; json_ref JSONString;`, error: "error while parsing JSON text, invalid json text", - previousVersErrors: []Expect{{3, "unknown opcode: json_ref"}}, + previousVersErrors: []Expect{{1, "unknown opcode: json_ref"}}, }, { source: `byte "{\"key0\": 1, \"key0\": \"3\"}"; byte "key0"; json_ref JSONString;`, error: "error while parsing JSON text, invalid json text, duplicate keys not allowed", - previousVersErrors: []Expect{{3, "unknown opcode: json_ref"}}, + previousVersErrors: []Expect{{1, "unknown opcode: json_ref"}}, }, { source: `byte "{\"key0\": 0,\"key1\": \"algo\",\"key2\":{\"key3\": \"teal\", \"key4\": {\"key40\": 10, \"key40\": \"should fail!\"}}}"; @@ -5399,7 +5399,7 @@ func TestOpJSONRef(t *testing.T) { json_ref JSONString `, error: "error while parsing JSON text, invalid json text, duplicate keys not allowed", - previousVersErrors: []Expect{{5, "unknown opcode: json_ref"}, {9, "unknown opcode: json_ref"}, {13, "unknown opcode: json_ref"}}, + previousVersErrors: []Expect{{3, "unknown opcode: json_ref"}, {5, "unknown opcode: json_ref"}, {7, "unknown opcode: json_ref"}}, }, { source: `byte "[1,2,3]"; @@ -5407,7 +5407,7 @@ func TestOpJSONRef(t *testing.T) { json_ref JSONUint64 `, error: "error while parsing JSON text, invalid json text, only json object is allowed", - previousVersErrors: []Expect{{5, "unknown opcode: json_ref"}}, + previousVersErrors: []Expect{{3, "unknown opcode: json_ref"}}, }, { source: `byte "2"; @@ -5415,7 +5415,7 @@ func TestOpJSONRef(t *testing.T) { json_ref JSONUint64 `, error: "error while parsing JSON text, invalid json text, only json object is allowed", - previousVersErrors: []Expect{{5, "unknown opcode: json_ref"}}, + previousVersErrors: []Expect{{3, "unknown opcode: json_ref"}}, }, { source: `byte "null"; @@ -5423,7 +5423,7 @@ func TestOpJSONRef(t *testing.T) { json_ref JSONUint64 `, error: "error while parsing JSON text, invalid json text, only json object is allowed", - previousVersErrors: []Expect{{5, "unknown opcode: json_ref"}}, + previousVersErrors: []Expect{{3, "unknown opcode: json_ref"}}, }, { source: `byte "true"; @@ -5431,7 +5431,7 @@ func TestOpJSONRef(t *testing.T) { json_ref JSONUint64 `, error: "error while parsing JSON text, invalid json text, only json object is allowed", - previousVersErrors: []Expect{{5, "unknown opcode: json_ref"}}, + previousVersErrors: []Expect{{3, "unknown opcode: json_ref"}}, }, { source: `byte "\"sometext\""; @@ -5439,7 +5439,7 @@ func TestOpJSONRef(t *testing.T) { json_ref JSONUint64 `, error: "error while parsing JSON text, invalid json text, only json object is allowed", - previousVersErrors: []Expect{{5, "unknown opcode: json_ref"}}, + previousVersErrors: []Expect{{3, "unknown opcode: json_ref"}}, }, { source: `byte "{noquotes: \"shouldn't work\"}"; @@ -5448,7 +5448,7 @@ func TestOpJSONRef(t *testing.T) { byte "shouldn't work"; ==`, error: "error while parsing JSON text, invalid json text", - previousVersErrors: []Expect{{5, "unknown opcode: json_ref"}}, + previousVersErrors: []Expect{{3, "unknown opcode: json_ref"}}, }, // max uint64 + 1 should fail { @@ -5458,7 +5458,7 @@ func TestOpJSONRef(t *testing.T) { int 1; return`, error: "json: cannot unmarshal number 18446744073709551616 into Go value of type uint64", - previousVersErrors: []Expect{{5, "unknown opcode: json_ref"}}, + previousVersErrors: []Expect{{3, "unknown opcode: json_ref"}}, }, } diff --git a/data/transactions/logic/fields_test.go b/data/transactions/logic/fields_test.go index 0a29288134..2b5008f5c8 100644 --- a/data/transactions/logic/fields_test.go +++ b/data/transactions/logic/fields_test.go @@ -225,7 +225,7 @@ func TestAssetParamsFieldsVersions(t *testing.T) { ep, _, _ := makeSampleEnv() ep.Proto.LogicSigVersion = v if field.version > v { - testProg(t, text, v, Expect{3, "...was introduced in..."}) + testProg(t, text, v, Expect{1, "...was introduced in..."}) ops := testProg(t, text, field.version) // assemble in the future ops.Program[0] = byte(v) testAppBytes(t, ops.Program, ep, "invalid asset_params_get field") From 58b8d60751b8b14e75377a358d582540afeee04e Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Sun, 7 Aug 2022 08:01:36 -0400 Subject: [PATCH 04/15] Simplify things by lexing ";" in fieldsFromLine --- data/transactions/logic/assembler.go | 122 +++++++++------------- data/transactions/logic/assembler_test.go | 92 ++++++++++------ data/transactions/logic/eval_test.go | 3 +- 3 files changed, 112 insertions(+), 105 deletions(-) diff --git a/data/transactions/logic/assembler.go b/data/transactions/logic/assembler.go index f0177dc8df..044b31e705 100644 --- a/data/transactions/logic/assembler.go +++ b/data/transactions/logic/assembler.go @@ -545,7 +545,7 @@ func asmPushBytes(ops *OpStream, spec *OpSpec, args []string) error { return nil } -func base32DecdodeAnyPadding(x string) (val []byte, err error) { +func base32DecodeAnyPadding(x string) (val []byte, err error) { val, err = base32.StdEncoding.WithPadding(base32.NoPadding).DecodeString(x) if err != nil { // try again with standard padding @@ -567,7 +567,7 @@ func parseBinaryArgs(args []string) (val []byte, consumed int, err error) { err = errors.New("byte base32 arg lacks close paren") return } - val, err = base32DecdodeAnyPadding(arg[open+1 : close]) + val, err = base32DecodeAnyPadding(arg[open+1 : close]) if err != nil { return } @@ -595,7 +595,7 @@ func parseBinaryArgs(args []string) (val []byte, consumed int, err error) { err = fmt.Errorf("need literal after 'byte %s'", arg) return } - val, err = base32DecdodeAnyPadding(args[1]) + val, err = base32DecodeAnyPadding(args[1]) if err != nil { return } @@ -1399,25 +1399,26 @@ func typecheck(expected, got StackType) bool { return expected == got } -var spaces = [256]uint8{'\t': 1, ' ': 1} +// semi-colon is quite space-like, so include it +var spaces = [256]bool{'\t': true, ' ': true, ';': true} func fieldsFromLine(line string) []string { var fields []string i := 0 - for i < len(line) && spaces[line[i]] != 0 { + for i < len(line) && spaces[line[i]] { i++ } start := i - inString := false - inBase64 := false + inString := false // tracked to allow spaces and comments inside + inBase64 := false // tracked to allow '//' inside for i < len(line) { - if spaces[line[i]] == 0 { // if not space + if !spaces[line[i]] { // if not space switch line[i] { case '"': // is a string literal? if !inString { - if i == 0 || i > 0 && spaces[line[i-1]] != 0 { + if i == 0 || i > 0 && spaces[line[i-1]] { inString = true } } else { @@ -1446,19 +1447,29 @@ func fieldsFromLine(line string) []string { i++ continue } + + // we've hit a space, end last token unless inString + if !inString { field := line[start:i] fields = append(fields, field) - if field == "base64" || field == "b64" { - inBase64 = true - } else if inBase64 { + if line[i] == ';' { + fields = append(fields, ";") + } + if inBase64 { inBase64 = false + } else if field == "base64" || field == "b64" { + inBase64 = true } } i++ + // gooble up consecutive whitespace (but notice semis) if !inString { - for i < len(line) && spaces[line[i]] != 0 { + for i < len(line) && spaces[line[i]] { + if line[i] == ';' { + fields = append(fields, ";") + } i++ } start = i @@ -1531,25 +1542,12 @@ func (ops *OpStream) trackStack(args StackTypes, returns StackTypes, instruction } } -// processFields walks through the input fields until it gets to a semi-colon -// at the end of a field at which point it returns everything prior as current -// and everything following the semicolon as next - -// fields should not be used after as processFields mangles it -// current and next do share the same array if next is not nil -func processFields(fields []string) (current, next []string) { - for i := 0; i < len(fields); i++ { - field := fields[i] - if string(field[len(field)-1]) == ";" { - field = field[0 : len(field)-1] - current = fields[:i] - if len(field) > 0 { - current = append(current, field) - } - if i+1 == len(fields) { - return current, nil - } - return current, fields[i+1:] +// processFields breaks fields into a slice of tokens up to the first +// semi-colon, and the rest. +func processFields(fields []string) (current, rest []string) { + for i, field := range fields { + if field == ";" { + return fields[:i], fields[i+1:] } } return fields, nil @@ -1565,52 +1563,28 @@ func (ops *OpStream) assemble(text string) error { for scanner.Scan() { ops.sourceLine++ line := scanner.Text() - line = strings.TrimSpace(line) - if len(line) == 0 { - ops.trace("%3d: 0 line\n", ops.sourceLine) - continue - } - if strings.HasPrefix(line, "//") { - ops.trace("%3d: // line\n", ops.sourceLine) - continue - } - if strings.HasPrefix(line, "#pragma") { - ops.trace("%3d: #pragma line\n", ops.sourceLine) - ops.pragma(line) - continue - } fields := fieldsFromLine(line) - if len(fields) == 0 { - ops.trace("%3d: no fields\n", ops.sourceLine) - continue - } - // we're about to begin processing opcodes, so settle the Version - if ops.Version == assemblerNoVersion { - ops.Version = AssemblerDefaultVersion - } - if ops.versionedPseudoOps == nil { - ops.versionedPseudoOps = prepareVersionedPseudoTable(ops.Version) - } - var current []string - var next []string - next = fields - for current, next = processFields(next); len(current) > 0 || len(next) > 0; current, next = processFields(next) { + for current, next := processFields(fields); len(current) > 0 || len(next) > 0; current, next = processFields(next) { if len(current) == 0 { continue } opstring := current[0] - if len(opstring) == 0 { - continue - } if strings.HasPrefix(opstring, "//") { - // semicolon inside comment is not counted as newline + ops.trace("%3d: comment\n", ops.sourceLine) break } - currentLine := strings.Join(current, " ") - if strings.HasPrefix(currentLine, "#pragma") { + if opstring == "#pragma" { ops.trace("%3d: #pragma line\n", ops.sourceLine) - ops.pragma(currentLine) - continue + // pragma get the rest of the tokens + ops.pragma(append(current, next...)) + break + } + // we're about to begin processing opcodes, so settle the Version + if ops.Version == assemblerNoVersion { + ops.Version = AssemblerDefaultVersion + } + if ops.versionedPseudoOps == nil { + ops.versionedPseudoOps = prepareVersionedPseudoTable(ops.Version) } if opstring[len(opstring)-1] == ':' { ops.createLabel(opstring[:len(opstring)-1]) @@ -1653,6 +1627,13 @@ func (ops *OpStream) assemble(text string) error { } } + if err := scanner.Err(); err != nil { + if errors.Is(err, bufio.ErrTooLong) { + err = errors.New("line too long") + } + ops.error(err) + } + // backward compatibility: do not allow jumps behind last instruction in v1 if ops.Version <= 1 { for label, dest := range ops.labels { @@ -1680,8 +1661,7 @@ func (ops *OpStream) assemble(text string) error { return nil } -func (ops *OpStream) pragma(line string) error { - fields := strings.Split(line, " ") +func (ops *OpStream) pragma(fields []string) error { if fields[0] != "#pragma" { return ops.errorf("invalid syntax: %s", fields[0]) } diff --git a/data/transactions/logic/assembler_test.go b/data/transactions/logic/assembler_test.go index 9ebd2d27b2..2178c536be 100644 --- a/data/transactions/logic/assembler_test.go +++ b/data/transactions/logic/assembler_test.go @@ -17,7 +17,6 @@ package logic import ( - "bufio" "encoding/hex" "fmt" "strings" @@ -435,29 +434,6 @@ func pseudoOp(opcode string) bool { strings.HasPrefix(opcode, "arg") } -func addSemis(s string) (ret string) { - scanner := bufio.NewScanner(strings.NewReader(s)) -scanLoop: - for scanner.Scan() { - line := strings.TrimSpace(scanner.Text()) - fields := fieldsFromLine(line) - for i, field := range fields { - if i == 0 && strings.HasPrefix(field, "#") { - ret += "\n" + line + "\n" - continue scanLoop - } - if strings.HasPrefix(field, "//") { - ret += line + "\n" - continue scanLoop - } - } - if len(fields) > 0 { - ret += strings.Join(fields, " ") + "; " - } - } - return -} - // Check that assembly output is stable across time. func TestAssemble(t *testing.T) { partitiontest.PartitionTest(t) @@ -484,7 +460,6 @@ func TestAssemble(t *testing.T) { } ops := testProg(t, nonsense[v], v) - opsWithSemiColons := testProg(t, addSemis(nonsense[v]), v) // check that compilation is stable over // time. we must assemble to the same bytes // this month that we did last month. @@ -492,7 +467,6 @@ func TestAssemble(t *testing.T) { // the hex is for convenience if the program has been changed. the // hex string can be copy pasted back in as a new expected result. require.Equal(t, expectedBytes, ops.Program, hex.EncodeToString(ops.Program)) - require.Equal(t, expectedBytes, opsWithSemiColons.Program, hex.EncodeToString(ops.Program)) }) } } @@ -1128,6 +1102,29 @@ func TestFieldsFromLine(t *testing.T) { require.Equal(t, "base64", fields[1]) require.Equal(t, "ABC//==", fields[2]) + line = "op base64 base64" + fields = fieldsFromLine(line) + require.Equal(t, 3, len(fields)) + require.Equal(t, "op", fields[0]) + require.Equal(t, "base64", fields[1]) + require.Equal(t, "base64", fields[2]) + + line = "op base64 base64 //comment" + fields = fieldsFromLine(line) + require.Equal(t, 3, len(fields)) + require.Equal(t, "op", fields[0]) + require.Equal(t, "base64", fields[1]) + require.Equal(t, "base64", fields[2]) + + line = "op base64 base64; op2 //done" + fields = fieldsFromLine(line) + require.Equal(t, 5, len(fields)) + require.Equal(t, "op", fields[0]) + require.Equal(t, "base64", fields[1]) + require.Equal(t, "base64", fields[2]) + require.Equal(t, ";", fields[3]) + require.Equal(t, "op2", fields[4]) + line = "op base64 ABC/==" fields = fieldsFromLine(line) require.Equal(t, 3, len(fields)) @@ -1312,6 +1309,15 @@ func TestFieldsFromLine(t *testing.T) { fields = fieldsFromLine(line) require.Equal(t, 1, len(fields)) require.Equal(t, `""`, fields[0]) + + line = "int 1; int 2" + fields = fieldsFromLine(line) + require.Equal(t, 5, len(fields)) + require.Equal(t, "int", fields[0]) + require.Equal(t, "1", fields[1]) + require.Equal(t, ";", fields[2]) + require.Equal(t, "int", fields[3]) + require.Equal(t, "2", fields[4]) } func TestAssembleRejectNegJump(t *testing.T) { @@ -2714,15 +2720,37 @@ func TestReplacePseudo(t *testing.T) { } } -func checkSame(t *testing.T, weird string, normal string) { - ops, _ := AssembleStringWithVersion(weird, 7) - otherOps, _ := AssembleStringWithVersion(normal, 7) - require.Equal(t, otherOps.Program, ops.Program) +func checkSame(t *testing.T, first string, compares ...string) { + t.Helper() + ops, err := AssembleStringWithVersion(first, 7) + require.NoError(t, err, first) + for _, compare := range compares { + other, err := AssembleStringWithVersion(compare, 7) + assert.NoError(t, err, compare) + assert.Equal(t, other.Program, ops.Program, "%s unlike %s", first, compare) + } } func TestSemiColon(t *testing.T) { partitiontest.PartitionTest(t) t.Parallel() - // Space for weird semicolon cases that might not be checked by rest of tests - checkSame(t, "pushint 0 ; pushint 1 ; +; int 3 ; *", "pushint 0\npushint 1\n+\nint 3\n*") + + checkSame(t, + "pushint 0 ; pushint 1 ; +; int 3 ; *", + "pushint 0\npushint 1\n+\nint 3\n*", + "pushint 0; pushint 1; +; int 3; *; // comment; int 2", + "pushint 0; ; ; pushint 1 ; +; int 3 ; *//check", + ) + + checkSame(t, + "#pragma version 7\nint 1", + "// junk;\n#pragma version 7\nint 1", + "// junk;\n #pragma version 7\nint 1", + ) + + checkSame(t, + `byte "test;this"; pop;`, + `byte "test;this"; ; pop;`, + `byte "test;this";;pop;`, + ) } diff --git a/data/transactions/logic/eval_test.go b/data/transactions/logic/eval_test.go index d5ccf865c2..e34815bf9b 100644 --- a/data/transactions/logic/eval_test.go +++ b/data/transactions/logic/eval_test.go @@ -3532,8 +3532,7 @@ func benchmarkBasicProgram(b *testing.B, source string) { func benchmarkOperation(b *testing.B, prefix string, operation string, suffix string) { runs := 1 + b.N/2000 inst := strings.Count(operation, ";") + strings.Count(operation, "\n") - source := prefix + ";" + strings.Repeat(operation+";", 2000) + ";" + suffix - source = strings.ReplaceAll(source, ";", "\n") + source := prefix + ";" + strings.Repeat(operation+"\n", 2000) + ";" + suffix ops := testProg(b, source, AssemblerMaxVersion) evalLoop(b, runs, ops.Program) b.ReportMetric(float64(inst), "extra/op") From 655484d0d21b016b0700802f3aafb9d84ba18c4d Mon Sep 17 00:00:00 2001 From: Ilan Date: Tue, 9 Aug 2022 14:47:59 -0400 Subject: [PATCH 05/15] Small change --- data/transactions/logic/assembler.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/data/transactions/logic/assembler.go b/data/transactions/logic/assembler.go index 044b31e705..738909c8fa 100644 --- a/data/transactions/logic/assembler.go +++ b/data/transactions/logic/assembler.go @@ -1563,16 +1563,16 @@ func (ops *OpStream) assemble(text string) error { for scanner.Scan() { ops.sourceLine++ line := scanner.Text() + if strings.HasPrefix(line, "//") { + ops.trace("%3d: comment\n", ops.sourceLine) + continue + } fields := fieldsFromLine(line) for current, next := processFields(fields); len(current) > 0 || len(next) > 0; current, next = processFields(next) { if len(current) == 0 { continue } opstring := current[0] - if strings.HasPrefix(opstring, "//") { - ops.trace("%3d: comment\n", ops.sourceLine) - break - } if opstring == "#pragma" { ops.trace("%3d: #pragma line\n", ops.sourceLine) // pragma get the rest of the tokens From edacf40be67b3fc3e47092701d8c0e47032dccf1 Mon Sep 17 00:00:00 2001 From: Ilan Date: Tue, 9 Aug 2022 15:14:52 -0400 Subject: [PATCH 06/15] small change --- data/transactions/logic/assembler_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/transactions/logic/assembler_test.go b/data/transactions/logic/assembler_test.go index 9f9146a6b4..27424a5382 100644 --- a/data/transactions/logic/assembler_test.go +++ b/data/transactions/logic/assembler_test.go @@ -2728,7 +2728,7 @@ func TestAddPseudoDocTags(t *testing.T) { delete(opDocByName, "any") }() - pseudoOps["tests"] = map[int]OpSpec{2: OpSpec{Name: "multiple"}, 1: OpSpec{Name: "single"}, 0: OpSpec{Name: "none"}, anyImmediates: OpSpec{Name: "any"}} + pseudoOps["tests"] = map[int]OpSpec{2: {Name: "multiple"}, 1: {Name: "single"}, 0: {Name: "none"}, anyImmediates: {Name: "any"}} addPseudoDocTags() require.Equal(t, "`multiple` can be called using `tests` with 2 immediates.", opDocByName["multiple"]) require.Equal(t, "`single` can be called using `tests` with 1 immediate.", opDocByName["single"]) From 45617ca5ace14e528f769dcbf563b6cd81a73355 Mon Sep 17 00:00:00 2001 From: Ilan Date: Tue, 9 Aug 2022 17:06:02 -0400 Subject: [PATCH 07/15] Pragma change and revert space changes --- data/transactions/logic/assembler.go | 3 +- data/transactions/logic/evalAppTxn_test.go | 98 ++++++++++---------- data/transactions/logic/evalStateful_test.go | 2 +- 3 files changed, 52 insertions(+), 51 deletions(-) diff --git a/data/transactions/logic/assembler.go b/data/transactions/logic/assembler.go index 59a31473eb..7a26e11644 100644 --- a/data/transactions/logic/assembler.go +++ b/data/transactions/logic/assembler.go @@ -1576,7 +1576,8 @@ func (ops *OpStream) assemble(text string) error { if opstring == "#pragma" { ops.trace("%3d: #pragma line\n", ops.sourceLine) // pragma get the rest of the tokens - ops.pragma(append(current, next...)) + pragmaFields := current[:cap(current)] + ops.pragma(pragmaFields) break } // we're about to begin processing opcodes, so settle the Version diff --git a/data/transactions/logic/evalAppTxn_test.go b/data/transactions/logic/evalAppTxn_test.go index a5e9250e3e..35d8f28e3d 100644 --- a/data/transactions/logic/evalAppTxn_test.go +++ b/data/transactions/logic/evalAppTxn_test.go @@ -456,25 +456,25 @@ func TestNumInnerShallow(t *testing.T) { ep.Reset() ledger.NewApp(tx.Receiver, 888, basics.AppParams{}) ledger.NewAccount(appAddr(888), 1000000) - TestApp(t, pay+"; int 1", ep) - TestApp(t, pay+pay+"; int 1", ep) - TestApp(t, pay+pay+pay+"; int 1", ep) - TestApp(t, pay+pay+pay+pay+"; int 1", ep) + TestApp(t, pay+";int 1", ep) + TestApp(t, pay+pay+";int 1", ep) + TestApp(t, pay+pay+pay+";int 1", ep) + TestApp(t, pay+pay+pay+pay+";int 1", ep) // In the sample proto, MaxInnerTransactions = 4 - TestApp(t, pay+pay+pay+pay+pay+"; int 1", ep, "too many inner transactions") + TestApp(t, pay+pay+pay+pay+pay+";int 1", ep, "too many inner transactions") ep, tx, ledger = MakeSampleEnv() ledger.NewApp(tx.Receiver, 888, basics.AppParams{}) ledger.NewAccount(appAddr(888), 1000000) - TestApp(t, pay+"; int 1", ep) - TestApp(t, pay+pay+"; int 1", ep) - TestApp(t, pay+pay+pay+"; int 1", ep) - TestApp(t, pay+pay+pay+pay+"; int 1", ep) + TestApp(t, pay+";int 1", ep) + TestApp(t, pay+pay+";int 1", ep) + TestApp(t, pay+pay+pay+";int 1", ep) + TestApp(t, pay+pay+pay+pay+";int 1", ep) // In the sample proto, MaxInnerTransactions = 4, but when pooling you get // MaxTxGroupSize (here, 8) * that. - TestApp(t, pay+pay+pay+pay+pay+"; int 1", ep) - TestApp(t, strings.Repeat(pay, 32)+"; int 1", ep) - TestApp(t, strings.Repeat(pay, 33)+"; int 1", ep, "too many inner transactions") + TestApp(t, pay+pay+pay+pay+pay+";int 1", ep) + TestApp(t, strings.Repeat(pay, 32)+";int 1", ep) + TestApp(t, strings.Repeat(pay, 33)+";int 1", ep, "too many inner transactions") } // TestNumInnerPooled ensures that inner call limits are pooled across app calls @@ -499,8 +499,8 @@ func TestNumInnerPooled(t *testing.T) { ledger := MakeLedger(nil) ledger.NewApp(tx.Txn.Receiver, 888, basics.AppParams{}) ledger.NewAccount(appAddr(888), 1000000) - short := pay + "; int 1" - long := strings.Repeat(pay, 17) + "; int 1" // More than half allowed + short := pay + ";int 1" + long := strings.Repeat(pay, 17) + ";int 1" // More than half allowed grp := MakeSampleTxnGroup(tx) TestApps(t, []string{short, ""}, grp, LogicVersion, ledger) @@ -812,7 +812,7 @@ txn Sender; itxn_field Receiver; // Force the first fee to 3, but the second will default to 2*fee-3 = 2002-3 TestApp(t, "itxn_begin"+ pay+ - "int 3; itxn_field Fee; "+ + "int 3; itxn_field Fee;"+ "itxn_next"+ pay+ "itxn_submit; itxn Fee; int 1999; ==", ep) @@ -820,16 +820,16 @@ txn Sender; itxn_field Receiver; // Same as first, but force the second too low TestApp(t, "itxn_begin"+ pay+ - "int 3; itxn_field Fee; "+ + "int 3; itxn_field Fee;"+ "itxn_next"+ pay+ - "int 1998; itxn_field Fee; "+ + "int 1998; itxn_field Fee;"+ "itxn_submit; int 1", ep, "fee too small") // Overpay in first itxn, the second will default to less TestApp(t, "itxn_begin"+ pay+ - "int 2000; itxn_field Fee; "+ + "int 2000; itxn_field Fee;"+ "itxn_next"+ pay+ "itxn_submit; itxn Fee; int 2; ==", ep) @@ -837,25 +837,25 @@ txn Sender; itxn_field Receiver; // Same first, but force the second too low TestApp(t, "itxn_begin"+ pay+ - "int 2000; itxn_field Fee; "+ + "int 2000; itxn_field Fee;"+ "itxn_next"+ pay+ - "int 1; itxn_field Fee; "+ + "int 1; itxn_field Fee;"+ "itxn_submit; itxn Fee; int 1", ep, "fee too small") // Test that overpay in first inner group is available in second inner group // also ensure only exactly the _right_ amount of credit is available. TestApp(t, "itxn_begin"+ pay+ - "int 2002; itxn_field Fee; "+ // double pay + "int 2002; itxn_field Fee;"+ // double pay "itxn_next"+ pay+ - "int 1001; itxn_field Fee; "+ // regular pay - "itxn_submit; "+ + "int 1001; itxn_field Fee;"+ // regular pay + "itxn_submit;"+ // At beginning of second group, we should have 1 minfee of credit "itxn_begin"+ pay+ - "int 0; itxn_field Fee; "+ // free, due to credit + "int 0; itxn_field Fee;"+ // free, due to credit "itxn_next"+ pay+ "itxn_submit; itxn Fee; int 1001; ==", // second one should have to pay @@ -871,7 +871,7 @@ func TestApplCreation(t *testing.T) { ep, tx, _ := MakeSampleEnv() - p := "itxn_begin; " + p := "itxn_begin;" s := "; int 1" TestApp(t, p+"int 31; itxn_field ApplicationID"+s, ep, @@ -899,31 +899,31 @@ func TestApplCreation(t *testing.T) { TestApp(t, p+"int 401; bzero; dup; itxn_field ApplicationArgs; itxn_field ApplicationArgs", ep, "length too long") - TestApp(t, p+strings.Repeat("byte 0x11; itxn_field ApplicationArgs; ", 12)+s, ep) - TestApp(t, p+strings.Repeat("byte 0x11; itxn_field ApplicationArgs; ", 13)+s, ep, + TestApp(t, p+strings.Repeat("byte 0x11; itxn_field ApplicationArgs;", 12)+s, ep) + TestApp(t, p+strings.Repeat("byte 0x11; itxn_field ApplicationArgs;", 13)+s, ep, "too many application args") - TestApp(t, p+strings.Repeat("int 32; bzero; itxn_field Accounts; ", 3)+s, ep, + TestApp(t, p+strings.Repeat("int 32; bzero; itxn_field Accounts;", 3)+s, ep, "invalid Account reference") tx.Accounts = append(tx.Accounts, basics.Address{}) TestApp(t, fmt.Sprintf(p+"%s"+s, - strings.Repeat("int 32; bzero; itxn_field Accounts; ", 3)), ep) + strings.Repeat("int 32; bzero; itxn_field Accounts;", 3)), ep) TestApp(t, fmt.Sprintf(p+"%s"+s, - strings.Repeat("int 32; bzero; itxn_field Accounts; ", 4)), ep, + strings.Repeat("int 32; bzero; itxn_field Accounts;", 4)), ep, "too many foreign accounts") - TestApp(t, p+strings.Repeat("int 621; itxn_field Applications; ", 5)+s, ep, + TestApp(t, p+strings.Repeat("int 621; itxn_field Applications;", 5)+s, ep, "invalid App reference") tx.ForeignApps = append(tx.ForeignApps, basics.AppIndex(621)) - TestApp(t, p+strings.Repeat("int 621; itxn_field Applications; ", 5)+s, ep) - TestApp(t, p+strings.Repeat("int 621; itxn_field Applications; ", 6)+s, ep, + TestApp(t, p+strings.Repeat("int 621; itxn_field Applications;", 5)+s, ep) + TestApp(t, p+strings.Repeat("int 621; itxn_field Applications;", 6)+s, ep, "too many foreign apps") - TestApp(t, p+strings.Repeat("int 621; itxn_field Assets; ", 6)+s, ep, + TestApp(t, p+strings.Repeat("int 621; itxn_field Assets;", 6)+s, ep, "invalid Asset reference") tx.ForeignAssets = append(tx.ForeignAssets, basics.AssetIndex(621)) - TestApp(t, p+strings.Repeat("int 621; itxn_field Assets; ", 6)+s, ep) - TestApp(t, p+strings.Repeat("int 621; itxn_field Assets; ", 7)+s, ep, + TestApp(t, p+strings.Repeat("int 621; itxn_field Assets;", 6)+s, ep) + TestApp(t, p+strings.Repeat("int 621; itxn_field Assets;", 7)+s, ep, "too many foreign assets") TestApp(t, p+"int 2700; bzero; itxn_field ApprovalProgram"+s, ep) @@ -955,7 +955,7 @@ func TestBigApplCreation(t *testing.T) { ep, _, _ := MakeSampleEnv() - p := "itxn_begin; " + p := "itxn_begin;" s := "; int 1" // Recall that in test proto, max possible program size is 2700, because @@ -1014,39 +1014,39 @@ func TestApplSubmission(t *testing.T) { ops := TestProg(t, "int 1", AssemblerMaxVersion) approve := hex.EncodeToString(ops.Program) - a := fmt.Sprintf("byte 0x%s; itxn_field ApprovalProgram; ", approve) + a := fmt.Sprintf("byte 0x%s; itxn_field ApprovalProgram;", approve) - p := "itxn_begin; int appl; itxn_field TypeEnum; " - s := "itxn_submit; int 1" + p := "itxn_begin; int appl; itxn_field TypeEnum;" + s := ";itxn_submit; int 1" TestApp(t, p+a+s, ep, "ClearStateProgram: invalid program (empty)") - a += fmt.Sprintf("byte 0x%s; itxn_field ClearStateProgram; ", approve) + a += fmt.Sprintf("byte 0x%s; itxn_field ClearStateProgram;", approve) // All zeros is v0, so we get a complaint, but that means lengths were ok when set. TestApp(t, p+a+`int 600; bzero; itxn_field ApprovalProgram; - int 600; bzero; itxn_field ClearStateProgram; `+s, ep, + int 600; bzero; itxn_field ClearStateProgram;`+s, ep, "inner app call with version v0 < v4") TestApp(t, p+`int 601; bzero; itxn_field ApprovalProgram; - int 600; bzero; itxn_field ClearStateProgram; `+s, ep, "too long") + int 600; bzero; itxn_field ClearStateProgram;`+s, ep, "too long") // WellFormed does the math based on the supplied ExtraProgramPages TestApp(t, p+a+`int 1; itxn_field ExtraProgramPages int 1200; bzero; itxn_field ApprovalProgram; - int 1200; bzero; itxn_field ClearStateProgram; `+s, ep, + int 1200; bzero; itxn_field ClearStateProgram;`+s, ep, "inner app call with version v0 < v4") TestApp(t, p+`int 1; itxn_field ExtraProgramPages int 1200; bzero; itxn_field ApprovalProgram; - int 1201; bzero; itxn_field ClearStateProgram; `+s, ep, "too long") + int 1201; bzero; itxn_field ClearStateProgram;`+s, ep, "too long") // Can't set epp when app id is given tx.ForeignApps = append(tx.ForeignApps, basics.AppIndex(7)) TestApp(t, p+`int 1; itxn_field ExtraProgramPages; - int 7; itxn_field ApplicationID; `+s, ep, "immutable") + int 7; itxn_field ApplicationID`+s, ep, "immutable") - TestApp(t, p+a+"int 20; itxn_field GlobalNumUint; int 11; itxn_field GlobalNumByteSlice; "+s, + TestApp(t, p+a+"int 20; itxn_field GlobalNumUint; int 11; itxn_field GlobalNumByteSlice"+s, ep, "too large") - TestApp(t, p+a+"int 7; itxn_field LocalNumUint; int 7; itxn_field LocalNumByteSlice; "+s, + TestApp(t, p+a+"int 7; itxn_field LocalNumUint; int 7; itxn_field LocalNumByteSlice"+s, ep, "too large") } @@ -1261,7 +1261,7 @@ func TestInnerBudgetIncrement(t *testing.T) { ApprovalProgram: gasup.Program, }) - waste := `global CurrentApplicationAddress; keccak256; pop; ` + waste := `global CurrentApplicationAddress; keccak256; pop;` buy := `itxn_begin int appl; itxn_field TypeEnum int 222; itxn_field ApplicationID diff --git a/data/transactions/logic/evalStateful_test.go b/data/transactions/logic/evalStateful_test.go index 1e88764881..bb2f1e0f1d 100644 --- a/data/transactions/logic/evalStateful_test.go +++ b/data/transactions/logic/evalStateful_test.go @@ -2586,7 +2586,7 @@ func TestAppLoop(t *testing.T) { t.Parallel() ep, _, _ := makeSampleEnv() - stateful := "global CurrentApplicationID; pop; " + stateful := "global CurrentApplicationID; pop;" // Double until > 10. Should be 16 testApp(t, stateful+"int 1; loop: int 2; *; dup; int 10; <; bnz loop; int 16; ==", ep) From 170b810431418f5fa73dde1b248d376fb732d2f6 Mon Sep 17 00:00:00 2001 From: Ilan Date: Tue, 9 Aug 2022 18:02:49 -0400 Subject: [PATCH 08/15] ci decided to just never run so making unimportant change to get ci running hopefully --- data/transactions/logic/assembler_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/transactions/logic/assembler_test.go b/data/transactions/logic/assembler_test.go index 27424a5382..8691c64a40 100644 --- a/data/transactions/logic/assembler_test.go +++ b/data/transactions/logic/assembler_test.go @@ -2778,6 +2778,6 @@ func TestSemiColon(t *testing.T) { checkSame(t, `byte "test;this"; pop;`, `byte "test;this"; ; pop;`, - `byte "test;this";;pop;`, + `byte "test;this";;;pop;`, ) } From 5fd22fe053e8d068e447a9823e029d30fac9c242 Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Tue, 9 Aug 2022 22:13:14 -0400 Subject: [PATCH 09/15] more concise token tests --- data/transactions/logic/assembler_test.go | 277 ++++------------------ 1 file changed, 42 insertions(+), 235 deletions(-) diff --git a/data/transactions/logic/assembler_test.go b/data/transactions/logic/assembler_test.go index 8691c64a40..498a7034ed 100644 --- a/data/transactions/logic/assembler_test.go +++ b/data/transactions/logic/assembler_test.go @@ -1110,241 +1110,48 @@ func TestFieldsFromLine(t *testing.T) { partitiontest.PartitionTest(t) t.Parallel() - line := "op arg" - fields := fieldsFromLine(line) - require.Equal(t, 2, len(fields)) - require.Equal(t, "op", fields[0]) - require.Equal(t, "arg", fields[1]) - - line = "op arg // test" - fields = fieldsFromLine(line) - require.Equal(t, 2, len(fields)) - require.Equal(t, "op", fields[0]) - require.Equal(t, "arg", fields[1]) - - line = "op base64 ABC//==" - fields = fieldsFromLine(line) - require.Equal(t, 3, len(fields)) - require.Equal(t, "op", fields[0]) - require.Equal(t, "base64", fields[1]) - require.Equal(t, "ABC//==", fields[2]) - - line = "op base64 base64" - fields = fieldsFromLine(line) - require.Equal(t, 3, len(fields)) - require.Equal(t, "op", fields[0]) - require.Equal(t, "base64", fields[1]) - require.Equal(t, "base64", fields[2]) - - line = "op base64 base64 //comment" - fields = fieldsFromLine(line) - require.Equal(t, 3, len(fields)) - require.Equal(t, "op", fields[0]) - require.Equal(t, "base64", fields[1]) - require.Equal(t, "base64", fields[2]) - - line = "op base64 base64; op2 //done" - fields = fieldsFromLine(line) - require.Equal(t, 5, len(fields)) - require.Equal(t, "op", fields[0]) - require.Equal(t, "base64", fields[1]) - require.Equal(t, "base64", fields[2]) - require.Equal(t, ";", fields[3]) - require.Equal(t, "op2", fields[4]) - - line = "op base64 ABC/==" - fields = fieldsFromLine(line) - require.Equal(t, 3, len(fields)) - require.Equal(t, "op", fields[0]) - require.Equal(t, "base64", fields[1]) - require.Equal(t, "ABC/==", fields[2]) - - line = "op base64 ABC/== /" - fields = fieldsFromLine(line) - require.Equal(t, 4, len(fields)) - require.Equal(t, "op", fields[0]) - require.Equal(t, "base64", fields[1]) - require.Equal(t, "ABC/==", fields[2]) - require.Equal(t, "/", fields[3]) - - line = "op base64 ABC/== //" - fields = fieldsFromLine(line) - require.Equal(t, 3, len(fields)) - require.Equal(t, "op", fields[0]) - require.Equal(t, "base64", fields[1]) - require.Equal(t, "ABC/==", fields[2]) - - line = "op base64 ABC//== //" - fields = fieldsFromLine(line) - require.Equal(t, 3, len(fields)) - require.Equal(t, "op", fields[0]) - require.Equal(t, "base64", fields[1]) - require.Equal(t, "ABC//==", fields[2]) - - line = "op b64 ABC//== //" - fields = fieldsFromLine(line) - require.Equal(t, 3, len(fields)) - require.Equal(t, "op", fields[0]) - require.Equal(t, "b64", fields[1]) - require.Equal(t, "ABC//==", fields[2]) - - line = "op b64(ABC//==) // comment" - fields = fieldsFromLine(line) - require.Equal(t, 2, len(fields)) - require.Equal(t, "op", fields[0]) - require.Equal(t, "b64(ABC//==)", fields[1]) - - line = "op base64(ABC//==) // comment" - fields = fieldsFromLine(line) - require.Equal(t, 2, len(fields)) - require.Equal(t, "op", fields[0]) - require.Equal(t, "base64(ABC//==)", fields[1]) - - line = "op b64(ABC/==) // comment" - fields = fieldsFromLine(line) - require.Equal(t, 2, len(fields)) - require.Equal(t, "op", fields[0]) - require.Equal(t, "b64(ABC/==)", fields[1]) - - line = "op base64(ABC/==) // comment" - fields = fieldsFromLine(line) - require.Equal(t, 2, len(fields)) - require.Equal(t, "op", fields[0]) - require.Equal(t, "base64(ABC/==)", fields[1]) - - line = "base64(ABC//==)" - fields = fieldsFromLine(line) - require.Equal(t, 1, len(fields)) - require.Equal(t, "base64(ABC//==)", fields[0]) - - line = "b(ABC//==)" - fields = fieldsFromLine(line) - require.Equal(t, 1, len(fields)) - require.Equal(t, "b(ABC", fields[0]) - - line = "b(ABC//==) //" - fields = fieldsFromLine(line) - require.Equal(t, 1, len(fields)) - require.Equal(t, "b(ABC", fields[0]) - - line = "b(ABC ==) //" - fields = fieldsFromLine(line) - require.Equal(t, 2, len(fields)) - require.Equal(t, "b(ABC", fields[0]) - require.Equal(t, "==)", fields[1]) - - line = "op base64 ABC)" - fields = fieldsFromLine(line) - require.Equal(t, 3, len(fields)) - require.Equal(t, "op", fields[0]) - require.Equal(t, "base64", fields[1]) - require.Equal(t, "ABC)", fields[2]) - - line = "op base64 ABC) // comment" - fields = fieldsFromLine(line) - require.Equal(t, 3, len(fields)) - require.Equal(t, "op", fields[0]) - require.Equal(t, "base64", fields[1]) - require.Equal(t, "ABC)", fields[2]) - - line = "op base64 ABC//) // comment" - fields = fieldsFromLine(line) - require.Equal(t, 3, len(fields)) - require.Equal(t, "op", fields[0]) - require.Equal(t, "base64", fields[1]) - require.Equal(t, "ABC//)", fields[2]) - - line = `op "test"` - fields = fieldsFromLine(line) - require.Equal(t, 2, len(fields)) - require.Equal(t, "op", fields[0]) - require.Equal(t, `"test"`, fields[1]) - - line = `op "test1 test2"` - fields = fieldsFromLine(line) - require.Equal(t, 2, len(fields)) - require.Equal(t, "op", fields[0]) - require.Equal(t, `"test1 test2"`, fields[1]) - - line = `op "test1 test2" // comment` - fields = fieldsFromLine(line) - require.Equal(t, 2, len(fields)) - require.Equal(t, "op", fields[0]) - require.Equal(t, `"test1 test2"`, fields[1]) - - line = `op "test1 test2 // not a comment"` - fields = fieldsFromLine(line) - require.Equal(t, 2, len(fields)) - require.Equal(t, "op", fields[0]) - require.Equal(t, `"test1 test2 // not a comment"`, fields[1]) - - line = `op "test1 test2 // not a comment" // comment` - fields = fieldsFromLine(line) - require.Equal(t, 2, len(fields)) - require.Equal(t, "op", fields[0]) - require.Equal(t, `"test1 test2 // not a comment"`, fields[1]) - - line = `op "test1 test2 // not a comment" // comment` - fields = fieldsFromLine(line) - require.Equal(t, 2, len(fields)) - require.Equal(t, "op", fields[0]) - require.Equal(t, `"test1 test2 // not a comment"`, fields[1]) - - line = `op "test1 test2" //` - fields = fieldsFromLine(line) - require.Equal(t, 2, len(fields)) - require.Equal(t, "op", fields[0]) - require.Equal(t, `"test1 test2"`, fields[1]) - - line = `op "test1 test2"//` - fields = fieldsFromLine(line) - require.Equal(t, 2, len(fields)) - require.Equal(t, "op", fields[0]) - require.Equal(t, `"test1 test2"`, fields[1]) - - line = `op "test1 test2` // non-terminated string literal - fields = fieldsFromLine(line) - require.Equal(t, 2, len(fields)) - require.Equal(t, "op", fields[0]) - require.Equal(t, `"test1 test2`, fields[1]) - - line = `op "test1 test2\"` // non-terminated string literal - fields = fieldsFromLine(line) - require.Equal(t, 2, len(fields)) - require.Equal(t, "op", fields[0]) - require.Equal(t, `"test1 test2\"`, fields[1]) - - line = `op \"test1 test2\"` // not a string literal - fields = fieldsFromLine(line) - require.Equal(t, 3, len(fields)) - require.Equal(t, "op", fields[0]) - require.Equal(t, `\"test1`, fields[1]) - require.Equal(t, `test2\"`, fields[2]) - - line = `"test1 test2"` - fields = fieldsFromLine(line) - require.Equal(t, 1, len(fields)) - require.Equal(t, `"test1 test2"`, fields[0]) - - line = `\"test1 test2"` - fields = fieldsFromLine(line) - require.Equal(t, 2, len(fields)) - require.Equal(t, `\"test1`, fields[0]) - require.Equal(t, `test2"`, fields[1]) - - line = `"" // test` - fields = fieldsFromLine(line) - require.Equal(t, 1, len(fields)) - require.Equal(t, `""`, fields[0]) - - line = "int 1; int 2" - fields = fieldsFromLine(line) - require.Equal(t, 5, len(fields)) - require.Equal(t, "int", fields[0]) - require.Equal(t, "1", fields[1]) - require.Equal(t, ";", fields[2]) - require.Equal(t, "int", fields[3]) - require.Equal(t, "2", fields[4]) + check := func(line string, tokens ...string) { + t.Helper() + assert.Equal(t, fieldsFromLine(line), tokens) + } + + check("op arg", "op", "arg") + check("op arg // test", "op", "arg") + check("op base64 ABC//==", "op", "base64", "ABC//==") + check("op base64 base64", "op", "base64", "base64") + check("op base64 base64 //comment", "op", "base64", "base64") + check("op base64 base64; op2 //done", "op", "base64", "base64", ";", "op2") + check("op base64 ABC/==", "op", "base64", "ABC/==") + check("op base64 ABC/== /", "op", "base64", "ABC/==", "/") + check("op base64 ABC/== //", "op", "base64", "ABC/==") + check("op base64 ABC//== //", "op", "base64", "ABC//==") + check("op b64 ABC//== //", "op", "b64", "ABC//==") + check("op b64(ABC//==) // comment", "op", "b64(ABC//==)") + check("op base64(ABC//==) // comment", "op", "base64(ABC//==)") + check("op b64(ABC/==) // comment", "op", "b64(ABC/==)") + check("op base64(ABC/==) // comment", "op", "base64(ABC/==)") + check("base64(ABC//==)", "base64(ABC//==)") + check("b(ABC//==)", "b(ABC") + check("b(ABC//==) //", "b(ABC") + check("b(ABC ==) //", "b(ABC", "==)") + check("op base64 ABC)", "op", "base64", "ABC)") + check("op base64 ABC) // comment", "op", "base64", "ABC)") + check("op base64 ABC//) // comment", "op", "base64", "ABC//)") + check(`op "test"`, "op", `"test"`) + check(`op "test1 test2"`, "op", `"test1 test2"`) + check(`op "test1 test2" // comment`, "op", `"test1 test2"`) + check(`op "test1 test2 // not a comment"`, "op", `"test1 test2 // not a comment"`) + check(`op "test1 test2 // not a comment" // comment`, "op", `"test1 test2 // not a comment"`) + check(`op "test1 test2" //`, "op", `"test1 test2"`) + check(`op "test1 test2"//`, "op", `"test1 test2"`) + check(`op "test1 test2`, "op", `"test1 test2`) // non-terminated string literal + check(`op "test1 test2\"`, "op", `"test1 test2\"`) // non-terminated string literal + check(`op \"test1 test2\"`, "op", `\"test1`, `test2\"`) // not a string literal + check(`"test1 test2"`, `"test1 test2"`) + check(`\"test1 test2"`, `\"test1`, `test2"`) + check(`"" // test`, `""`) + check("int 1; int 2", "int", "1", ";", "int", "2") + check("int 1;;;int 2", "int", "1", ";", ";", ";", "int", "2") } func TestAssembleRejectNegJump(t *testing.T) { From c0ab6acb3c1e55c479ff290791c9fe8e63c7a6ea Mon Sep 17 00:00:00 2001 From: Ilan Date: Tue, 9 Aug 2022 23:11:08 -0400 Subject: [PATCH 10/15] pragma fields construction change --- data/transactions/logic/assembler.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/data/transactions/logic/assembler.go b/data/transactions/logic/assembler.go index 7a26e11644..3311e2d526 100644 --- a/data/transactions/logic/assembler.go +++ b/data/transactions/logic/assembler.go @@ -1576,7 +1576,13 @@ func (ops *OpStream) assemble(text string) error { if opstring == "#pragma" { ops.trace("%3d: #pragma line\n", ops.sourceLine) // pragma get the rest of the tokens - pragmaFields := current[:cap(current)] + pragmaFields := make([]string, len(current)+len(next)+1) + if copy(pragmaFields, current) < len(pragmaFields)-1 { + pragmaFields[len(current)] = ";" + copy(pragmaFields[len(current)+1:], next) + } else { + pragmaFields = pragmaFields[0:len(current)] + } ops.pragma(pragmaFields) break } From 1eebe6970614b871171155c0d6cbf9ae053e6d73 Mon Sep 17 00:00:00 2001 From: Ilan Date: Wed, 10 Aug 2022 13:36:59 -0400 Subject: [PATCH 11/15] Addressed feedback --- data/transactions/logic/assembler.go | 101 +++++++++++----------- data/transactions/logic/assembler_test.go | 17 ++-- 2 files changed, 62 insertions(+), 56 deletions(-) diff --git a/data/transactions/logic/assembler.go b/data/transactions/logic/assembler.go index 3311e2d526..5db1f26693 100644 --- a/data/transactions/logic/assembler.go +++ b/data/transactions/logic/assembler.go @@ -1402,8 +1402,8 @@ func typecheck(expected, got StackType) bool { // semi-colon is quite space-like, so include it var spaces = [256]bool{'\t': true, ' ': true, ';': true} -func fieldsFromLine(line string) []string { - var fields []string +func tokensFromLine(line string) []string { + var tokens []string i := 0 for i < len(line) && spaces[line[i]] { @@ -1429,9 +1429,9 @@ func fieldsFromLine(line string) []string { case '/': // is a comment? if i < len(line)-1 && line[i+1] == '/' && !inBase64 && !inString { if start != i { // if a comment without whitespace - fields = append(fields, line[start:i]) + tokens = append(tokens, line[start:i]) } - return fields + return tokens } case '(': // is base64( seq? prefix := line[start:i] @@ -1451,14 +1451,14 @@ func fieldsFromLine(line string) []string { // we've hit a space, end last token unless inString if !inString { - field := line[start:i] - fields = append(fields, field) + token := line[start:i] + tokens = append(tokens, token) if line[i] == ';' { - fields = append(fields, ";") + tokens = append(tokens, ";") } if inBase64 { inBase64 = false - } else if field == "base64" || field == "b64" { + } else if token == "base64" || token == "b64" { inBase64 = true } } @@ -1468,7 +1468,7 @@ func fieldsFromLine(line string) []string { if !inString { for i < len(line) && spaces[line[i]] { if line[i] == ';' { - fields = append(fields, ";") + tokens = append(tokens, ";") } i++ } @@ -1478,10 +1478,10 @@ func fieldsFromLine(line string) []string { // add rest of the string if any if start < len(line) { - fields = append(fields, line[start:i]) + tokens = append(tokens, line[start:i]) } - return fields + return tokens } func (ops *OpStream) trace(format string, args ...interface{}) { @@ -1542,15 +1542,14 @@ func (ops *OpStream) trackStack(args StackTypes, returns StackTypes, instruction } } -// processFields breaks fields into a slice of tokens up to the first -// semi-colon, and the rest. -func processFields(fields []string) (current, rest []string) { - for i, field := range fields { - if field == ";" { - return fields[:i], fields[i+1:] +// splitTokens breaks tokens into two slices at the first semicolon. +func splitTokens(tokens []string) (current, rest []string) { + for i, token := range tokens { + if token == ";" { + return tokens[:i], tokens[i+1:] } } - return fields, nil + return tokens, nil } // assemble reads text from an input and accumulates the program @@ -1563,36 +1562,40 @@ func (ops *OpStream) assemble(text string) error { for scanner.Scan() { ops.sourceLine++ line := scanner.Text() + line = strings.TrimSpace(line) + if len(line) == 0 { + ops.trace("%3d: empty line\n", ops.sourceLine) + continue + } if strings.HasPrefix(line, "//") { ops.trace("%3d: comment\n", ops.sourceLine) continue } - fields := fieldsFromLine(line) - for current, next := processFields(fields); len(current) > 0 || len(next) > 0; current, next = processFields(next) { + tokens := tokensFromLine(line) + first := tokens[0] + if strings.HasPrefix(first, "#") { + directive := first[1:] + switch directive { + case "pragma": + ops.pragma(tokens) + ops.trace("%3d: #pragma line\n", ops.sourceLine) + default: + ops.errorf("Unknown directive: %s", directive) + } + continue + } + // we're about to begin processing opcodes, so settle the Version + if ops.Version == assemblerNoVersion { + ops.Version = AssemblerDefaultVersion + } + if ops.versionedPseudoOps == nil { + ops.versionedPseudoOps = prepareVersionedPseudoTable(ops.Version) + } + for current, next := splitTokens(tokens); len(current) > 0 || len(next) > 0; current, next = splitTokens(next) { if len(current) == 0 { continue } opstring := current[0] - if opstring == "#pragma" { - ops.trace("%3d: #pragma line\n", ops.sourceLine) - // pragma get the rest of the tokens - pragmaFields := make([]string, len(current)+len(next)+1) - if copy(pragmaFields, current) < len(pragmaFields)-1 { - pragmaFields[len(current)] = ";" - copy(pragmaFields[len(current)+1:], next) - } else { - pragmaFields = pragmaFields[0:len(current)] - } - ops.pragma(pragmaFields) - break - } - // we're about to begin processing opcodes, so settle the Version - if ops.Version == assemblerNoVersion { - ops.Version = AssemblerDefaultVersion - } - if ops.versionedPseudoOps == nil { - ops.versionedPseudoOps = prepareVersionedPseudoTable(ops.Version) - } if opstring[len(opstring)-1] == ':' { ops.createLabel(opstring[:len(opstring)-1]) current = current[1:] @@ -1668,20 +1671,20 @@ func (ops *OpStream) assemble(text string) error { return nil } -func (ops *OpStream) pragma(fields []string) error { - if fields[0] != "#pragma" { - return ops.errorf("invalid syntax: %s", fields[0]) +func (ops *OpStream) pragma(tokens []string) error { + if tokens[0] != "#pragma" { + return ops.errorf("invalid syntax: %s", tokens[0]) } - if len(fields) < 2 { + if len(tokens) < 2 { return ops.error("empty pragma") } - key := fields[1] + key := tokens[1] switch key { case "version": - if len(fields) < 3 { + if len(tokens) < 3 { return ops.error("no version value") } - value := fields[2] + value := tokens[2] var ver uint64 if ops.pending.Len() > 0 { return ops.error("#pragma version is only allowed before instructions") @@ -1706,10 +1709,10 @@ func (ops *OpStream) pragma(fields []string) error { } return nil case "typetrack": - if len(fields) < 3 { + if len(tokens) < 3 { return ops.error("no typetrack value") } - value := fields[2] + value := tokens[2] on, err := strconv.ParseBool(value) if err != nil { return ops.errorf("bad #pragma typetrack: %#v", value) diff --git a/data/transactions/logic/assembler_test.go b/data/transactions/logic/assembler_test.go index 498a7034ed..8817c013ea 100644 --- a/data/transactions/logic/assembler_test.go +++ b/data/transactions/logic/assembler_test.go @@ -1112,7 +1112,7 @@ func TestFieldsFromLine(t *testing.T) { check := func(line string, tokens ...string) { t.Helper() - assert.Equal(t, fieldsFromLine(line), tokens) + assert.Equal(t, tokensFromLine(line), tokens) } check("op arg", "op", "arg") @@ -2554,12 +2554,15 @@ func TestReplacePseudo(t *testing.T) { } } -func checkSame(t *testing.T, first string, compares ...string) { +func checkSame(t *testing.T, version uint64, first string, compares ...string) { t.Helper() - ops, err := AssembleStringWithVersion(first, 7) + if version == 0 { + version = assemblerNoVersion + } + ops, err := AssembleStringWithVersion(first, version) require.NoError(t, err, first) for _, compare := range compares { - other, err := AssembleStringWithVersion(compare, 7) + other, err := AssembleStringWithVersion(compare, version) assert.NoError(t, err, compare) assert.Equal(t, other.Program, ops.Program, "%s unlike %s", first, compare) } @@ -2569,20 +2572,20 @@ func TestSemiColon(t *testing.T) { partitiontest.PartitionTest(t) t.Parallel() - checkSame(t, + checkSame(t, AssemblerMaxVersion, "pushint 0 ; pushint 1 ; +; int 3 ; *", "pushint 0\npushint 1\n+\nint 3\n*", "pushint 0; pushint 1; +; int 3; *; // comment; int 2", "pushint 0; ; ; pushint 1 ; +; int 3 ; *//check", ) - checkSame(t, + checkSame(t, 0, "#pragma version 7\nint 1", "// junk;\n#pragma version 7\nint 1", "// junk;\n #pragma version 7\nint 1", ) - checkSame(t, + checkSame(t, AssemblerMaxVersion, `byte "test;this"; pop;`, `byte "test;this"; ; pop;`, `byte "test;this";;;pop;`, From 38b80eeef024c95c5efcee2bb69588a7c00940c4 Mon Sep 17 00:00:00 2001 From: Ilan Date: Wed, 10 Aug 2022 14:56:03 -0400 Subject: [PATCH 12/15] fixed bug --- data/transactions/logic/assembler.go | 47 +++++++++++++--------------- 1 file changed, 21 insertions(+), 26 deletions(-) diff --git a/data/transactions/logic/assembler.go b/data/transactions/logic/assembler.go index 5db1f26693..b6cb39b2e8 100644 --- a/data/transactions/logic/assembler.go +++ b/data/transactions/logic/assembler.go @@ -1407,6 +1407,9 @@ func tokensFromLine(line string) []string { i := 0 for i < len(line) && spaces[line[i]] { + if line[i] == ';' { + tokens = append(tokens, ";") + } i++ } @@ -1562,39 +1565,31 @@ func (ops *OpStream) assemble(text string) error { for scanner.Scan() { ops.sourceLine++ line := scanner.Text() - line = strings.TrimSpace(line) - if len(line) == 0 { - ops.trace("%3d: empty line\n", ops.sourceLine) - continue - } - if strings.HasPrefix(line, "//") { - ops.trace("%3d: comment\n", ops.sourceLine) - continue - } tokens := tokensFromLine(line) - first := tokens[0] - if strings.HasPrefix(first, "#") { - directive := first[1:] - switch directive { - case "pragma": - ops.pragma(tokens) - ops.trace("%3d: #pragma line\n", ops.sourceLine) - default: - ops.errorf("Unknown directive: %s", directive) + if len(tokens) > 0 { + if first := tokens[0]; first[0] == '#' { + directive := first[1:] + switch directive { + case "pragma": + ops.pragma(tokens) + ops.trace("%3d: #pragma line\n", ops.sourceLine) + default: + ops.errorf("Unknown directive: %s", directive) + } + continue } - continue - } - // we're about to begin processing opcodes, so settle the Version - if ops.Version == assemblerNoVersion { - ops.Version = AssemblerDefaultVersion - } - if ops.versionedPseudoOps == nil { - ops.versionedPseudoOps = prepareVersionedPseudoTable(ops.Version) } for current, next := splitTokens(tokens); len(current) > 0 || len(next) > 0; current, next = splitTokens(next) { if len(current) == 0 { continue } + // we're about to begin processing opcodes, so settle the Version + if ops.Version == assemblerNoVersion { + ops.Version = AssemblerDefaultVersion + } + if ops.versionedPseudoOps == nil { + ops.versionedPseudoOps = prepareVersionedPseudoTable(ops.Version) + } opstring := current[0] if opstring[len(opstring)-1] == ':' { ops.createLabel(opstring[:len(opstring)-1]) From 095f4d320b755c29cf2fe51dc410f8f278fb4755 Mon Sep 17 00:00:00 2001 From: Ilan Date: Thu, 11 Aug 2022 12:52:27 -0400 Subject: [PATCH 13/15] More tests --- data/transactions/logic/assembler.go | 2 +- data/transactions/logic/assembler_test.go | 39 +++++++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/data/transactions/logic/assembler.go b/data/transactions/logic/assembler.go index b6cb39b2e8..af2f663410 100644 --- a/data/transactions/logic/assembler.go +++ b/data/transactions/logic/assembler.go @@ -1467,7 +1467,7 @@ func tokensFromLine(line string) []string { } i++ - // gooble up consecutive whitespace (but notice semis) + // gobble up consecutive whitespace (but notice semis) if !inString { for i < len(line) && spaces[line[i]] { if line[i] == ';' { diff --git a/data/transactions/logic/assembler_test.go b/data/transactions/logic/assembler_test.go index 8817c013ea..16e26daf25 100644 --- a/data/transactions/logic/assembler_test.go +++ b/data/transactions/logic/assembler_test.go @@ -1152,6 +1152,45 @@ func TestFieldsFromLine(t *testing.T) { check(`"" // test`, `""`) check("int 1; int 2", "int", "1", ";", "int", "2") check("int 1;;;int 2", "int", "1", ";", ";", ";", "int", "2") + check("int 1; ;int 2;; ; ;; ", "int", "1", ";", ";", "int", "2", ";", ";", ";", ";", ";") +} + +func TestSplitTokens(t *testing.T) { + partitiontest.PartitionTest(t) + t.Parallel() + + check := func(tokens []string, left []string, right []string) { + t.Helper() + current, next := splitTokens(tokens) + assert.Equal(t, left, current) + assert.Equal(t, right, next) + } + + check([]string{"hey,", "how's", ";", ";", "it", "going", ";"}, + []string{"hey,", "how's"}, + []string{";", "it", "going", ";"}, + ) + + check([]string{";"}, + []string{}, + []string{}, + ) + + check([]string{";", "it", "going"}, + []string{}, + []string{"it", "going"}, + ) + + check([]string{"hey,", "how's"}, + []string{"hey,", "how's"}, + nil, + ) + + check([]string{`"hey in quotes;"`, "getting", `";"`, ";", "tricky"}, + []string{`"hey in quotes;"`, "getting", `";"`}, + []string{"tricky"}, + ) + } func TestAssembleRejectNegJump(t *testing.T) { From b25e2628275d64d77db1d0a1157a5e7c8a93ba56 Mon Sep 17 00:00:00 2001 From: Ilan Date: Thu, 11 Aug 2022 13:00:36 -0400 Subject: [PATCH 14/15] change spaces to tokenSeparators --- data/transactions/logic/assembler.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/data/transactions/logic/assembler.go b/data/transactions/logic/assembler.go index af2f663410..f99c480834 100644 --- a/data/transactions/logic/assembler.go +++ b/data/transactions/logic/assembler.go @@ -1399,14 +1399,14 @@ func typecheck(expected, got StackType) bool { return expected == got } -// semi-colon is quite space-like, so include it -var spaces = [256]bool{'\t': true, ' ': true, ';': true} +// newline not included since handled in scanner +var tokenSeparators = [256]bool{'\t': true, ' ': true, ';': true} func tokensFromLine(line string) []string { var tokens []string i := 0 - for i < len(line) && spaces[line[i]] { + for i < len(line) && tokenSeparators[line[i]] { if line[i] == ';' { tokens = append(tokens, ";") } @@ -1417,11 +1417,11 @@ func tokensFromLine(line string) []string { inString := false // tracked to allow spaces and comments inside inBase64 := false // tracked to allow '//' inside for i < len(line) { - if !spaces[line[i]] { // if not space + if !tokenSeparators[line[i]] { // if not space switch line[i] { case '"': // is a string literal? if !inString { - if i == 0 || i > 0 && spaces[line[i-1]] { + if i == 0 || i > 0 && tokenSeparators[line[i-1]] { inString = true } } else { @@ -1469,7 +1469,7 @@ func tokensFromLine(line string) []string { // gobble up consecutive whitespace (but notice semis) if !inString { - for i < len(line) && spaces[line[i]] { + for i < len(line) && tokenSeparators[line[i]] { if line[i] == ';' { tokens = append(tokens, ";") } From 8b9b51688ec25fd27f99b51497fb9882e7857ec4 Mon Sep 17 00:00:00 2001 From: Ilan Date: Thu, 11 Aug 2022 13:11:38 -0400 Subject: [PATCH 15/15] more tests --- data/transactions/logic/assembler_test.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/data/transactions/logic/assembler_test.go b/data/transactions/logic/assembler_test.go index 16e26daf25..f1afb9cc25 100644 --- a/data/transactions/logic/assembler_test.go +++ b/data/transactions/logic/assembler_test.go @@ -1153,6 +1153,10 @@ func TestFieldsFromLine(t *testing.T) { check("int 1; int 2", "int", "1", ";", "int", "2") check("int 1;;;int 2", "int", "1", ";", ";", ";", "int", "2") check("int 1; ;int 2;; ; ;; ", "int", "1", ";", ";", "int", "2", ";", ";", ";", ";", ";") + check(";", ";") + check("; ; ;;;;", ";", ";", ";", ";", ";", ";") + check(" ;", ";") + check(" ; ", ";") } func TestSplitTokens(t *testing.T) {