diff --git a/cmd/compile/main.go b/cmd/compile/main.go deleted file mode 100644 index 59c5de9eba..0000000000 --- a/cmd/compile/main.go +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Cadence - The resource-oriented smart contract programming language - * - * Copyright Flow Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package main - -import ( - "os" - - "github.com/onflow/cadence/ast" - "github.com/onflow/cadence/cmd" - "github.com/onflow/cadence/common" - "github.com/onflow/cadence/compiler" - "github.com/onflow/cadence/compiler/ir" - "github.com/onflow/cadence/compiler/wasm" - "github.com/onflow/cadence/stdlib" -) - -func main() { - args := os.Args - - if len(args) < 2 { - cmd.ExitWithError("no input file") - } - - path := args[1] - - location := common.NewStringLocation(nil, path) - - codes := map[common.Location][]byte{} - - program, must := cmd.PrepareProgramFromFile(location, codes) - - // standard library handler is only needed for execution, but we're only checking - standardLibraryValues := stdlib.DefaultScriptStandardLibraryValues(nil) - - checker, must := cmd.PrepareChecker( - program, - location, - codes, - nil, - standardLibraryValues, - must, - ) - - must(checker.Check()) - - // Compile all functions - - comp := compiler.NewCompiler(checker) - - functionDeclarations := checker.Program.FunctionDeclarations() - - funcs := make([]*ir.Func, len(functionDeclarations)) - - for i, functionDeclaration := range functionDeclarations { - funcs[i] = ast.AcceptDeclaration[ir.Stmt](functionDeclaration, comp).(*ir.Func) - } - - // Generate a WebAssembly module for the functions - - module := compiler.GenerateWasm(funcs) - - // Export all public functions - - for i, functionDeclaration := range functionDeclarations { - if functionDeclaration.Access != ast.AccessAll { - continue - } - - module.Exports = append(module.Exports, - &wasm.Export{ - Name: functionDeclaration.Identifier.Identifier, - Descriptor: wasm.FunctionExport{ - FunctionIndex: uint32(i), - }, - }, - ) - } - - // Generate WASM binary - - var buf wasm.Buffer - w := wasm.NewWASMWriter(&buf) - err := w.WriteModule(module) - if err != nil { - panic(nil) - } - - // Write WASM binary to stdout - - _, err = os.Stdout.Write(buf.Bytes()) - if err != nil { - panic(nil) - } -} diff --git a/compiler/codegen.go b/compiler/codegen.go deleted file mode 100644 index 9e67e0f9f0..0000000000 --- a/compiler/codegen.go +++ /dev/null @@ -1,277 +0,0 @@ -/* - * Cadence - The resource-oriented smart contract programming language - * - * Copyright Flow Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package compiler - -import ( - "fmt" - - "github.com/onflow/cadence/compiler/ir" - "github.com/onflow/cadence/compiler/wasm" - "github.com/onflow/cadence/errors" -) - -const RuntimeModuleName = "crt" - -type wasmCodeGen struct { - mod *wasm.ModuleBuilder - code *wasm.Code - runtimeFunctionIndexInt uint32 - runtimeFunctionIndexString uint32 - runtimeFunctionIndexAdd uint32 -} - -func (codeGen *wasmCodeGen) VisitInt(i ir.Int) ir.Repr { - codeGen.emitConstantCall( - codeGen.runtimeFunctionIndexInt, - i.Value, - ) - return nil -} - -func (codeGen *wasmCodeGen) VisitString(s ir.String) ir.Repr { - codeGen.emitConstantCall( - codeGen.runtimeFunctionIndexString, - []byte(s.Value), - ) - return nil -} - -func (codeGen *wasmCodeGen) VisitSequence(sequence *ir.Sequence) ir.Repr { - for _, stmt := range sequence.Stmts { - stmt.Accept(codeGen) - } - return nil -} - -func (codeGen *wasmCodeGen) VisitBlock(_ *ir.Block) ir.Repr { - // TODO - panic(errors.NewUnreachableError()) -} - -func (codeGen *wasmCodeGen) VisitLoop(_ *ir.Loop) ir.Repr { - // TODO - panic(errors.NewUnreachableError()) -} - -func (codeGen *wasmCodeGen) VisitIf(_ *ir.If) ir.Repr { - // TODO - panic(errors.NewUnreachableError()) -} - -func (codeGen *wasmCodeGen) VisitBranch(_ *ir.Branch) ir.Repr { - // TODO - panic(errors.NewUnreachableError()) -} - -func (codeGen *wasmCodeGen) VisitBranchIf(_ *ir.BranchIf) ir.Repr { - // TODO - panic(errors.NewUnreachableError()) -} - -func (codeGen *wasmCodeGen) VisitStoreLocal(storeLocal *ir.StoreLocal) ir.Repr { - storeLocal.Exp.Accept(codeGen) - codeGen.emit(wasm.InstructionLocalSet{ - LocalIndex: storeLocal.LocalIndex, - }) - return nil -} - -func (codeGen *wasmCodeGen) VisitDrop(_ *ir.Drop) ir.Repr { - // TODO - panic(errors.NewUnreachableError()) -} - -func (codeGen *wasmCodeGen) VisitReturn(r *ir.Return) ir.Repr { - r.Exp.Accept(codeGen) - codeGen.emit(wasm.InstructionReturn{}) - return nil -} - -func (codeGen *wasmCodeGen) VisitConst(c *ir.Const) ir.Repr { - c.Constant.Accept(codeGen) - return nil -} - -func (codeGen *wasmCodeGen) VisitCopyLocal(c *ir.CopyLocal) ir.Repr { - // TODO: copy - codeGen.emit(wasm.InstructionLocalGet{ - LocalIndex: c.LocalIndex, - }) - return nil -} - -func (codeGen *wasmCodeGen) VisitMoveLocal(_ *ir.MoveLocal) ir.Repr { - // TODO - panic(errors.NewUnreachableError()) -} - -func (codeGen *wasmCodeGen) VisitUnOpExpr(_ *ir.UnOpExpr) ir.Repr { - // TODO - panic(errors.NewUnreachableError()) -} - -func (codeGen *wasmCodeGen) VisitBinOpExpr(expr *ir.BinOpExpr) ir.Repr { - expr.Left.Accept(codeGen) - expr.Right.Accept(codeGen) - // TODO: add remaining operations, take types into account - switch expr.Op { - case ir.BinOpPlus: - codeGen.emit(wasm.InstructionCall{ - FuncIndex: codeGen.runtimeFunctionIndexAdd, - }) - return nil - } - panic(errors.NewUnreachableError()) -} - -func (codeGen *wasmCodeGen) VisitCall(_ *ir.Call) ir.Repr { - // TODO - panic(errors.NewUnreachableError()) -} - -func (codeGen *wasmCodeGen) VisitFunc(f *ir.Func) ir.Repr { - codeGen.code = &wasm.Code{} - codeGen.code.Locals = generateWasmLocalTypes(f.Locals) - f.Statement.Accept(codeGen) - functionType := generateWasmFunctionType(f.Type) - funcIndex := codeGen.mod.AddFunction(f.Name, functionType, codeGen.code) - // TODO: make export dependent on visibility modifier - codeGen.mod.AddExport(&wasm.Export{ - Name: f.Name, - Descriptor: wasm.FunctionExport{ - FunctionIndex: funcIndex, - }, - }) - return nil -} - -func (codeGen *wasmCodeGen) emit(inst wasm.Instruction) { - codeGen.code.Instructions = append(codeGen.code.Instructions, inst) -} - -func (codeGen *wasmCodeGen) addConstant(value []byte) uint32 { - offset := codeGen.mod.RequireMemory(uint32(len(value))) - // TODO: optimize: - // let module builder generate one data entry of all constants, - // instead of one data entry for each constant - codeGen.mod.AddData(offset, value) - return offset -} - -func (codeGen *wasmCodeGen) emitConstantCall(funcIndex uint32, value []byte) { - memoryOffset := codeGen.addConstant(value) - codeGen.emit(wasm.InstructionI32Const{Value: int32(memoryOffset)}) - - length := int32(len(value)) - codeGen.emit(wasm.InstructionI32Const{Value: length}) - - codeGen.emit(wasm.InstructionCall{FuncIndex: funcIndex}) -} - -var constantFunctionType = &wasm.FunctionType{ - Params: []wasm.ValueType{ - // memory offset - wasm.ValueTypeI32, - // length - wasm.ValueTypeI32, - }, - Results: []wasm.ValueType{ - wasm.ValueTypeExternRef, - }, -} - -var addFunctionType = &wasm.FunctionType{ - Params: []wasm.ValueType{ - wasm.ValueTypeExternRef, - wasm.ValueTypeExternRef, - }, - Results: []wasm.ValueType{ - wasm.ValueTypeExternRef, - }, -} - -func (codeGen *wasmCodeGen) addRuntimeImports() { - // NOTE: ensure to update the imports in the vm - codeGen.runtimeFunctionIndexInt = codeGen.addRuntimeImport("Int", constantFunctionType) - codeGen.runtimeFunctionIndexString = codeGen.addRuntimeImport("String", constantFunctionType) - codeGen.runtimeFunctionIndexAdd = codeGen.addRuntimeImport("add", addFunctionType) -} - -func (codeGen *wasmCodeGen) addRuntimeImport(name string, funcType *wasm.FunctionType) uint32 { - funcIndex, err := codeGen.mod.AddFunctionImport(RuntimeModuleName, name, funcType) - if err != nil { - panic(fmt.Errorf("failed to add runtime import of function %s: %w", name, err)) - } - return funcIndex -} - -func GenerateWasm(funcs []*ir.Func) *wasm.Module { - g := &wasmCodeGen{ - mod: &wasm.ModuleBuilder{}, - } - - g.addRuntimeImports() - - for _, f := range funcs { - f.Accept(g) - } - - g.mod.ExportMemory("mem") - - return g.mod.Build() -} - -func generateWasmLocalTypes(locals []ir.Local) []wasm.ValueType { - result := make([]wasm.ValueType, len(locals)) - for i, local := range locals { - result[i] = generateWasmValType(local.Type) - } - return result -} - -func generateWasmValType(valType ir.ValType) wasm.ValueType { - // TODO: add remaining types - switch valType { - case ir.ValTypeInt, - ir.ValTypeString: - - return wasm.ValueTypeExternRef - } - - panic(errors.NewUnreachableError()) -} - -func generateWasmFunctionType(funcType ir.FuncType) *wasm.FunctionType { - // generate parameter types - params := make([]wasm.ValueType, len(funcType.Params)) - for i, param := range funcType.Params { - params[i] = generateWasmValType(param) - } - - // generate result types - results := make([]wasm.ValueType, len(funcType.Results)) - for i, result := range funcType.Results { - results[i] = generateWasmValType(result) - } - - return &wasm.FunctionType{ - Params: params, - Results: results, - } -} diff --git a/compiler/codegen_test.go b/compiler/codegen_test.go deleted file mode 100644 index ad1bd9fea8..0000000000 --- a/compiler/codegen_test.go +++ /dev/null @@ -1,200 +0,0 @@ -/* - * Cadence - The resource-oriented smart contract programming language - * - * Copyright Flow Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package compiler - -import ( - "testing" - - "github.com/stretchr/testify/require" - - "github.com/onflow/cadence/compiler/ir" - "github.com/onflow/cadence/compiler/wasm" -) - -func TestWasmCodeGenSimple(t *testing.T) { - - t.Skip("WIP") - - mod := GenerateWasm([]*ir.Func{ - { - Name: "inc", - Type: ir.FuncType{ - Params: []ir.ValType{ - ir.ValTypeInt, - }, - Results: []ir.ValType{ - ir.ValTypeInt, - }, - }, - Locals: []ir.Local{ - {Type: ir.ValTypeInt}, - {Type: ir.ValTypeInt}, - }, - Statement: &ir.Sequence{ - Stmts: []ir.Stmt{ - &ir.StoreLocal{ - LocalIndex: 1, - Exp: &ir.Const{ - Constant: ir.Int{Value: []byte{1, 1}}, - }, - }, - &ir.Return{ - Exp: &ir.BinOpExpr{ - Op: ir.BinOpPlus, - Left: &ir.CopyLocal{ - LocalIndex: 0, - }, - Right: &ir.CopyLocal{ - LocalIndex: 1, - }, - }, - }, - }, - }, - }, - }) - - require.Equal(t, - &wasm.Module{ - Types: []*wasm.FunctionType{ - // function type of crt.Int - { - Params: []wasm.ValueType{ - wasm.ValueTypeI32, - wasm.ValueTypeI32, - }, - Results: []wasm.ValueType{ - wasm.ValueTypeExternRef, - }, - }, - // function type of crt.String - { - Params: []wasm.ValueType{ - wasm.ValueTypeI32, - wasm.ValueTypeI32, - }, - Results: []wasm.ValueType{ - wasm.ValueTypeExternRef, - }, - }, - // function type of add - { - Params: []wasm.ValueType{ - wasm.ValueTypeExternRef, - wasm.ValueTypeExternRef, - }, - Results: []wasm.ValueType{ - wasm.ValueTypeExternRef, - }, - }, - // function type of inc - { - Params: []wasm.ValueType{ - wasm.ValueTypeExternRef, - }, - Results: []wasm.ValueType{ - wasm.ValueTypeExternRef, - }, - }, - }, - Imports: []*wasm.Import{ - { - Module: RuntimeModuleName, - Name: "Int", - TypeIndex: 0, - }, - { - Module: RuntimeModuleName, - Name: "String", - TypeIndex: 1, - }, - { - Module: RuntimeModuleName, - Name: "add", - TypeIndex: 2, - }, - }, - Functions: []*wasm.Function{ - { - Name: "inc", - TypeIndex: 3, - Code: &wasm.Code{ - Locals: []wasm.ValueType{ - wasm.ValueTypeExternRef, - wasm.ValueTypeExternRef, - }, - Instructions: []wasm.Instruction{ - wasm.InstructionI32Const{Value: 0}, - wasm.InstructionI32Const{Value: 2}, - wasm.InstructionCall{FuncIndex: 0}, - wasm.InstructionLocalSet{LocalIndex: 1}, - wasm.InstructionLocalGet{LocalIndex: 0}, - wasm.InstructionLocalGet{LocalIndex: 1}, - wasm.InstructionCall{FuncIndex: 2}, - wasm.InstructionReturn{}, - }, - }, - }, - }, - Memories: []*wasm.Memory{ - { - Min: 1, - Max: nil, - }, - }, - Data: []*wasm.Data{ - // load [0x1, 0x1] at offset 0 - { - MemoryIndex: 0, - Offset: []wasm.Instruction{ - wasm.InstructionI32Const{Value: 0}, - }, - Init: []byte{ - // positive flag - 0x1, - // integer 1 - 0x1, - }, - }, - }, - Exports: []*wasm.Export{ - { - Name: "inc", - Descriptor: wasm.FunctionExport{ - FunctionIndex: 3, - }, - }, - { - Name: "mem", - Descriptor: wasm.MemoryExport{ - MemoryIndex: 0, - }, - }, - }, - }, - mod, - ) - - var buf wasm.Buffer - w := wasm.NewWASMWriter(&buf) - err := w.WriteModule(mod) - require.NoError(t, err) - - _ = wasm.WASM2WAT(buf.Bytes()) -} diff --git a/compiler/compiler.go b/compiler/compiler.go deleted file mode 100644 index d7fefc2a14..0000000000 --- a/compiler/compiler.go +++ /dev/null @@ -1,464 +0,0 @@ -/* - * Cadence - The resource-oriented smart contract programming language - * - * Copyright Flow Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package compiler - -import ( - "github.com/onflow/cadence/activations" - "github.com/onflow/cadence/ast" - "github.com/onflow/cadence/compiler/ir" - "github.com/onflow/cadence/errors" - "github.com/onflow/cadence/sema" -) - -type Compiler struct { - Checker *sema.Checker - activations *activations.Activations[*Local] - locals []*Local -} - -var _ ast.DeclarationVisitor[ir.Stmt] = &Compiler{} -var _ ast.StatementVisitor[ir.Stmt] = &Compiler{} -var _ ast.ExpressionVisitor[ir.Expr] = &Compiler{} - -func NewCompiler(checker *sema.Checker) *Compiler { - return &Compiler{ - Checker: checker, - activations: activations.NewActivations[*Local](nil), - } -} - -// declareLocal declares a local -func (compiler *Compiler) declareLocal(identifier string, valType ir.ValType) *Local { - // NOTE: semantic analysis already checked possible invalid redeclaration - index := uint32(len(compiler.locals)) - local := NewLocal(index, valType) - compiler.locals = append(compiler.locals, local) - compiler.setLocal(identifier, local) - return local -} - -func (compiler *Compiler) findLocal(name string) *Local { - return compiler.activations.Find(name) -} - -func (compiler *Compiler) setLocal(name string, variable *Local) { - compiler.activations.Set(name, variable) -} - -func (compiler *Compiler) VisitReturnStatement(statement *ast.ReturnStatement) ir.Stmt { - exp := ast.AcceptExpression[ir.Expr](statement.Expression, compiler) - return &ir.Return{ - Exp: exp, - } -} - -func (compiler *Compiler) VisitBreakStatement(_ *ast.BreakStatement) ir.Stmt { - // TODO - panic(errors.NewUnreachableError()) -} - -func (compiler *Compiler) VisitContinueStatement(_ *ast.ContinueStatement) ir.Stmt { - // TODO - panic(errors.NewUnreachableError()) -} - -func (compiler *Compiler) VisitIfStatement(_ *ast.IfStatement) ir.Stmt { - // TODO - panic(errors.NewUnreachableError()) -} - -func (compiler *Compiler) VisitWhileStatement(_ *ast.WhileStatement) ir.Stmt { - // TODO - panic(errors.NewUnreachableError()) -} - -func (compiler *Compiler) VisitForStatement(_ *ast.ForStatement) ir.Stmt { - // TODO - panic(errors.NewUnreachableError()) -} - -func (compiler *Compiler) VisitEmitStatement(_ *ast.EmitStatement) ir.Stmt { - // TODO - panic(errors.NewUnreachableError()) -} - -func (compiler *Compiler) VisitRemoveStatement(_ *ast.RemoveStatement) ir.Stmt { - // TODO - panic(errors.NewUnreachableError()) -} - -func (compiler *Compiler) VisitSwitchStatement(_ *ast.SwitchStatement) ir.Stmt { - // TODO - panic(errors.NewUnreachableError()) -} - -func (compiler *Compiler) VisitVariableDeclaration(declaration *ast.VariableDeclaration) ir.Stmt { - - // TODO: potential storage removal - // TODO: copy and convert - // TODO: second value - - identifier := declaration.Identifier.Identifier - targetType := compiler.Checker.Elaboration.VariableDeclarationTypes(declaration).TargetType - valType := compileValueType(targetType) - local := compiler.declareLocal(identifier, valType) - exp := ast.AcceptExpression[ir.Expr](declaration.Value, compiler) - - return &ir.StoreLocal{ - LocalIndex: local.Index, - Exp: exp, - } -} - -func (compiler *Compiler) VisitAssignmentStatement(_ *ast.AssignmentStatement) ir.Stmt { - // TODO - panic(errors.NewUnreachableError()) -} - -func (compiler *Compiler) VisitSwapStatement(_ *ast.SwapStatement) ir.Stmt { - // TODO - panic(errors.NewUnreachableError()) -} - -func (compiler *Compiler) VisitExpressionStatement(_ *ast.ExpressionStatement) ir.Stmt { - // TODO - panic(errors.NewUnreachableError()) -} - -func (compiler *Compiler) VisitVoidExpression(_ *ast.VoidExpression) ir.Expr { - panic(errors.NewUnreachableError()) -} - -func (compiler *Compiler) VisitBoolExpression(_ *ast.BoolExpression) ir.Expr { - // TODO - panic(errors.NewUnreachableError()) -} - -func (compiler *Compiler) VisitNilExpression(_ *ast.NilExpression) ir.Expr { - // TODO - panic(errors.NewUnreachableError()) -} - -func (compiler *Compiler) VisitIntegerExpression(expression *ast.IntegerExpression) ir.Expr { - var value []byte - - if expression.Value.Sign() < 0 { - value = append(value, 0) - } else { - value = append(value, 1) - } - - value = append(value, - expression.Value.Bytes()..., - ) - - return &ir.Const{ - Constant: ir.Int{ - Value: value, - }, - } -} - -func (compiler *Compiler) VisitFixedPointExpression(_ *ast.FixedPointExpression) ir.Expr { - // TODO - panic(errors.NewUnreachableError()) -} - -func (compiler *Compiler) VisitArrayExpression(_ *ast.ArrayExpression) ir.Expr { - // TODO - panic(errors.NewUnreachableError()) -} - -func (compiler *Compiler) VisitDictionaryExpression(_ *ast.DictionaryExpression) ir.Expr { - // TODO - panic(errors.NewUnreachableError()) -} - -func (compiler *Compiler) VisitIdentifierExpression(expression *ast.IdentifierExpression) ir.Expr { - // TODO - local := compiler.findLocal(expression.Identifier.Identifier) - // TODO: moves - return &ir.CopyLocal{ - LocalIndex: local.Index, - } -} - -func (compiler *Compiler) VisitInvocationExpression(_ *ast.InvocationExpression) ir.Expr { - // TODO - panic(errors.NewUnreachableError()) -} - -func (compiler *Compiler) VisitMemberExpression(_ *ast.MemberExpression) ir.Expr { - // TODO - panic(errors.NewUnreachableError()) -} - -func (compiler *Compiler) VisitIndexExpression(_ *ast.IndexExpression) ir.Expr { - // TODO - panic(errors.NewUnreachableError()) -} - -func (compiler *Compiler) VisitConditionalExpression(_ *ast.ConditionalExpression) ir.Expr { - // TODO - panic(errors.NewUnreachableError()) -} - -func (compiler *Compiler) VisitAttachExpression(_ *ast.AttachExpression) ir.Expr { - // TODO - panic(errors.NewUnreachableError()) -} - -func (compiler *Compiler) VisitUnaryExpression(_ *ast.UnaryExpression) ir.Expr { - // TODO - panic(errors.NewUnreachableError()) -} - -func (compiler *Compiler) VisitBinaryExpression(expression *ast.BinaryExpression) ir.Expr { - op := compileBinaryOperation(expression.Operation) - left := ast.AcceptExpression[ir.Expr](expression.Left, compiler) - right := ast.AcceptExpression[ir.Expr](expression.Right, compiler) - - return &ir.BinOpExpr{ - Op: op, - Left: left, - Right: right, - } -} - -func (compiler *Compiler) VisitFunctionExpression(_ *ast.FunctionExpression) ir.Expr { - // TODO - panic(errors.NewUnreachableError()) -} - -func (compiler *Compiler) VisitStringTemplateExpression(e *ast.StringTemplateExpression) ir.Expr { - // TODO - panic(errors.NewUnreachableError()) -} - -func (compiler *Compiler) VisitStringExpression(e *ast.StringExpression) ir.Expr { - return &ir.Const{ - Constant: ir.String{ - Value: e.Value, - }, - } -} - -func (compiler *Compiler) VisitCastingExpression(_ *ast.CastingExpression) ir.Expr { - // TODO - panic(errors.NewUnreachableError()) -} - -func (compiler *Compiler) VisitCreateExpression(_ *ast.CreateExpression) ir.Expr { - // TODO - panic(errors.NewUnreachableError()) -} - -func (compiler *Compiler) VisitDestroyExpression(_ *ast.DestroyExpression) ir.Expr { - // TODO - panic(errors.NewUnreachableError()) -} - -func (compiler *Compiler) VisitReferenceExpression(_ *ast.ReferenceExpression) ir.Expr { - // TODO - panic(errors.NewUnreachableError()) -} - -func (compiler *Compiler) VisitForceExpression(_ *ast.ForceExpression) ir.Expr { - // TODO - panic(errors.NewUnreachableError()) -} - -func (compiler *Compiler) VisitPathExpression(_ *ast.PathExpression) ir.Expr { - // TODO - panic(errors.NewUnreachableError()) -} - -func (compiler *Compiler) VisitProgram(_ *ast.Program) ir.Repr { - // TODO - panic(errors.NewUnreachableError()) -} - -func (compiler *Compiler) VisitSpecialFunctionDeclaration(declaration *ast.SpecialFunctionDeclaration) ir.Stmt { - return compiler.VisitFunctionDeclaration(declaration.FunctionDeclaration) -} - -func (compiler *Compiler) VisitFunctionDeclaration(declaration *ast.FunctionDeclaration) ir.Stmt { - - // TODO: declare function in current scope, use current scope in function - // TODO: conditions - - compiler.locals = nil - - block := declaration.FunctionBlock.Block - - // Declare a local for each parameter - - functionType := compiler.Checker.Elaboration.FunctionDeclarationFunctionType(declaration) - - parameters := declaration.ParameterList.Parameters - - for i, parameter := range parameters { - parameterType := functionType.Parameters[i].TypeAnnotation.Type - valType := compileValueType(parameterType) - name := parameter.Identifier.Identifier - compiler.declareLocal(name, valType) - } - - // Compile the function block - - stmt := compiler.visitBlock(block) - - // Important: compile locals after compiling function block, - // and don't include parameters in locals - locals := compileLocals(compiler.locals[len(parameters):]) - - compiledFunctionType := compileFunctionType(functionType) - - return &ir.Func{ - // TODO: fully qualify - Name: declaration.Identifier.Identifier, - Type: compiledFunctionType, - Locals: locals, - Statement: stmt, - } -} - -func (compiler *Compiler) visitBlock(block *ast.Block) ir.Stmt { - - // Block scope: each block gets an activation record - - compiler.activations.PushNewWithCurrent() - defer compiler.activations.Pop() - - // Compile each statement in the block - - stmts := make([]ir.Stmt, len(block.Statements)) - for i, statement := range block.Statements { - stmts[i] = ast.AcceptStatement[ir.Stmt](statement, compiler) - } - - // NOTE: just return an IR statement sequence, - // there is no need for an IR block - return &ir.Sequence{ - Stmts: stmts, - } -} - -func (compiler *Compiler) VisitCompositeDeclaration(_ *ast.CompositeDeclaration) ir.Stmt { - // TODO - panic(errors.NewUnreachableError()) -} - -func (compiler *Compiler) VisitAttachmentDeclaration(_ *ast.AttachmentDeclaration) ir.Stmt { - // TODO - panic(errors.NewUnreachableError()) -} - -func (compiler *Compiler) VisitInterfaceDeclaration(_ *ast.InterfaceDeclaration) ir.Stmt { - // TODO - panic(errors.NewUnreachableError()) -} - -func (compiler *Compiler) VisitFieldDeclaration(_ *ast.FieldDeclaration) ir.Stmt { - // TODO - panic(errors.NewUnreachableError()) -} - -func (compiler *Compiler) VisitPragmaDeclaration(_ *ast.PragmaDeclaration) ir.Stmt { - // TODO - panic(errors.NewUnreachableError()) -} - -func (compiler *Compiler) VisitImportDeclaration(_ *ast.ImportDeclaration) ir.Stmt { - // TODO - panic(errors.NewUnreachableError()) -} - -func (compiler *Compiler) VisitTransactionDeclaration(_ *ast.TransactionDeclaration) ir.Stmt { - // TODO - panic(errors.NewUnreachableError()) -} - -func (compiler *Compiler) VisitEntitlementDeclaration(_ *ast.EntitlementDeclaration) ir.Stmt { - // TODO - panic(errors.NewUnreachableError()) -} - -func (compiler *Compiler) VisitEntitlementMappingDeclaration(_ *ast.EntitlementMappingDeclaration) ir.Stmt { - // TODO - panic(errors.NewUnreachableError()) -} - -func (compiler *Compiler) VisitEnumCaseDeclaration(_ *ast.EnumCaseDeclaration) ir.Stmt { - // TODO - panic(errors.NewUnreachableError()) -} - -func compileBinaryOperation(operation ast.Operation) ir.BinOp { - // TODO: add remaining operations - switch operation { - case ast.OperationPlus: - return ir.BinOpPlus - } - - panic(errors.NewUnreachableError()) -} - -func compileValueType(ty sema.Type) ir.ValType { - // TODO: add remaining types - - switch ty { - case sema.StringType: - return ir.ValTypeString - case sema.IntType: - return ir.ValTypeInt - } - - panic(errors.NewUnreachableError()) -} - -func compileFunctionType(functionType *sema.FunctionType) ir.FuncType { - // compile parameter types - paramTypes := make([]ir.ValType, len(functionType.Parameters)) - for i, parameter := range functionType.Parameters { - paramTypes[i] = compileValueType(parameter.TypeAnnotation.Type) - } - - // compile return / result type - var resultTypes []ir.ValType - if functionType.ReturnTypeAnnotation.Type != sema.VoidType { - resultTypes = []ir.ValType{ - compileValueType(functionType.ReturnTypeAnnotation.Type), - } - } - return ir.FuncType{ - Params: paramTypes, - Results: resultTypes, - } -} - -func compileLocals(locals []*Local) []ir.Local { - result := make([]ir.Local, len(locals)) - for i, local := range locals { - result[i] = ir.Local{ - Type: local.Type, - } - } - return result -} diff --git a/compiler/compiler_test.go b/compiler/compiler_test.go deleted file mode 100644 index b9a0e2d866..0000000000 --- a/compiler/compiler_test.go +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Cadence - The resource-oriented smart contract programming language - * - * Copyright Flow Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package compiler - -import ( - "testing" - - "github.com/stretchr/testify/require" - - "github.com/onflow/cadence/compiler/ir" - . "github.com/onflow/cadence/test_utils/sema_utils" -) - -func TestCompilerSimple(t *testing.T) { - - checker, err := ParseAndCheck(t, ` - fun inc(a: Int): Int { - let mod = 1 - return a + mod - } - `) - - require.NoError(t, err) - - compiler := NewCompiler(checker) - - res := compiler.VisitFunctionDeclaration(checker.Program.FunctionDeclarations()[0]) - - require.Equal(t, - &ir.Func{ - Name: "inc", - Type: ir.FuncType{ - Params: []ir.ValType{ - ir.ValTypeInt, - }, - Results: []ir.ValType{ - ir.ValTypeInt, - }, - }, - Locals: []ir.Local{ - {Type: ir.ValTypeInt}, - }, - Statement: &ir.Sequence{ - Stmts: []ir.Stmt{ - &ir.StoreLocal{ - LocalIndex: 1, - Exp: &ir.Const{ - Constant: ir.Int{Value: []byte{1, 1}}, - }, - }, - &ir.Return{ - Exp: &ir.BinOpExpr{ - Op: ir.BinOpPlus, - Left: &ir.CopyLocal{ - LocalIndex: 0, - }, - Right: &ir.CopyLocal{ - LocalIndex: 1, - }, - }, - }, - }, - }, - }, - res, - ) -} diff --git a/compiler/ir/binop.go b/compiler/ir/binop.go deleted file mode 100644 index 361101b31a..0000000000 --- a/compiler/ir/binop.go +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Cadence - The resource-oriented smart contract programming language - * - * Copyright Flow Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package ir - -//go:generate go run golang.org/x/tools/cmd/stringer -type=BinOp - -type BinOp uint - -const ( - BinOpUnknown BinOp = iota - BinOpPlus -) diff --git a/compiler/ir/binop_string.go b/compiler/ir/binop_string.go deleted file mode 100644 index 6960586a1d..0000000000 --- a/compiler/ir/binop_string.go +++ /dev/null @@ -1,24 +0,0 @@ -// Code generated by "stringer -type=BinOp"; DO NOT EDIT. - -package ir - -import "strconv" - -func _() { - // An "invalid array index" compiler error signifies that the constant values have changed. - // Re-run the stringer command to generate them again. - var x [1]struct{} - _ = x[BinOpUnknown-0] - _ = x[BinOpPlus-1] -} - -const _BinOp_name = "BinOpUnknownBinOpPlus" - -var _BinOp_index = [...]uint8{0, 12, 21} - -func (i BinOp) String() string { - if i >= BinOp(len(_BinOp_index)-1) { - return "BinOp(" + strconv.FormatInt(int64(i), 10) + ")" - } - return _BinOp_name[_BinOp_index[i]:_BinOp_index[i+1]] -} diff --git a/compiler/ir/constant.go b/compiler/ir/constant.go deleted file mode 100644 index f4ebe49dc0..0000000000 --- a/compiler/ir/constant.go +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Cadence - The resource-oriented smart contract programming language - * - * Copyright Flow Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package ir - -type Constant interface { - isConstant() - Accept(Visitor) Repr -} - -type Int struct { - Value []byte -} - -func (Int) isConstant() {} - -func (c Int) Accept(v Visitor) Repr { - return v.VisitInt(c) -} - -type String struct { - Value string -} - -func (String) isConstant() {} - -func (c String) Accept(v Visitor) Repr { - return v.VisitString(c) -} diff --git a/compiler/ir/expr.go b/compiler/ir/expr.go deleted file mode 100644 index 094003957b..0000000000 --- a/compiler/ir/expr.go +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Cadence - The resource-oriented smart contract programming language - * - * Copyright Flow Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package ir - -type Expr interface { - isExpr() - Accept(Visitor) Repr -} - -type Const struct { - Constant Constant -} - -func (*Const) isExpr() {} - -func (e *Const) Accept(v Visitor) Repr { - return v.VisitConst(e) -} - -type CopyLocal struct { - LocalIndex uint32 -} - -func (*CopyLocal) isExpr() {} - -func (e *CopyLocal) Accept(v Visitor) Repr { - return v.VisitCopyLocal(e) -} - -type MoveLocal struct { - LocalIndex uint32 -} - -func (*MoveLocal) isExpr() {} - -func (e *MoveLocal) Accept(v Visitor) Repr { - return v.VisitMoveLocal(e) -} - -type UnOpExpr struct { - Expr Expr - Op UnOp -} - -func (*UnOpExpr) isExpr() {} - -func (e *UnOpExpr) Accept(v Visitor) Repr { - return v.VisitUnOpExpr(e) -} - -type BinOpExpr struct { - Left Expr - Right Expr - Op BinOp -} - -func (*BinOpExpr) isExpr() {} - -func (e *BinOpExpr) Accept(v Visitor) Repr { - return v.VisitBinOpExpr(e) -} - -type Call struct { - Arguments []Expr - FunctionIndex uint32 -} - -func (*Call) isExpr() {} - -func (e *Call) Accept(v Visitor) Repr { - return v.VisitCall(e) -} diff --git a/compiler/ir/func.go b/compiler/ir/func.go deleted file mode 100644 index 532190e4c5..0000000000 --- a/compiler/ir/func.go +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Cadence - The resource-oriented smart contract programming language - * - * Copyright Flow Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package ir - -type Func struct { - Statement Stmt - Name string - Type FuncType - Locals []Local -} - -func (*Func) isStmt() {} - -func (f *Func) Accept(v Visitor) Repr { - return v.VisitFunc(f) -} - -type FuncType struct { - Params []ValType - Results []ValType -} diff --git a/compiler/ir/local.go b/compiler/ir/local.go deleted file mode 100644 index 1c95b03df0..0000000000 --- a/compiler/ir/local.go +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Cadence - The resource-oriented smart contract programming language - * - * Copyright Flow Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package ir - -type Local struct { - Type ValType -} diff --git a/compiler/ir/stmt.go b/compiler/ir/stmt.go deleted file mode 100644 index f2ac1d453b..0000000000 --- a/compiler/ir/stmt.go +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Cadence - The resource-oriented smart contract programming language - * - * Copyright Flow Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package ir - -type Stmt interface { - isStmt() - Accept(Visitor) Repr -} - -type Sequence struct { - Stmts []Stmt -} - -func (*Sequence) isStmt() {} - -func (s *Sequence) Accept(v Visitor) Repr { - return v.VisitSequence(s) -} - -type Block struct { - Stmts []Stmt -} - -func (*Block) isStmt() {} - -func (s *Block) Accept(v Visitor) Repr { - return v.VisitBlock(s) -} - -type Loop struct { - Stmts []Stmt -} - -func (*Loop) isStmt() {} - -func (s *Loop) Accept(v Visitor) Repr { - return v.VisitLoop(s) -} - -type If struct { - Test Expr - Then Stmt - Else Stmt -} - -func (*If) isStmt() {} - -func (s *If) Accept(v Visitor) Repr { - return v.VisitIf(s) -} - -type Branch struct { - Index uint32 -} - -func (*Branch) isStmt() {} - -func (s *Branch) Accept(v Visitor) Repr { - return v.VisitBranch(s) -} - -type BranchIf struct { - Exp Expr - Index uint32 -} - -func (*BranchIf) isStmt() {} - -func (s *BranchIf) Accept(v Visitor) Repr { - return v.VisitBranchIf(s) -} - -type StoreLocal struct { - Exp Expr - LocalIndex uint32 -} - -func (*StoreLocal) isStmt() {} - -func (s *StoreLocal) Accept(v Visitor) Repr { - return v.VisitStoreLocal(s) -} - -type Drop struct { - Exp Expr -} - -func (*Drop) isStmt() {} - -func (s *Drop) Accept(v Visitor) Repr { - return v.VisitDrop(s) -} - -type Return struct { - Exp Expr -} - -func (*Return) isStmt() {} - -func (s *Return) Accept(v Visitor) Repr { - return v.VisitReturn(s) -} diff --git a/compiler/ir/unop.go b/compiler/ir/unop.go deleted file mode 100644 index 405a1af023..0000000000 --- a/compiler/ir/unop.go +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Cadence - The resource-oriented smart contract programming language - * - * Copyright Flow Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package ir - -//go:generate go run golang.org/x/tools/cmd/stringer -type=UnOp - -type UnOp uint - -const ( - UnOpUnknown UnOp = iota -) diff --git a/compiler/ir/unop_string.go b/compiler/ir/unop_string.go deleted file mode 100644 index 2c929f83a2..0000000000 --- a/compiler/ir/unop_string.go +++ /dev/null @@ -1,23 +0,0 @@ -// Code generated by "stringer -type=UnOp"; DO NOT EDIT. - -package ir - -import "strconv" - -func _() { - // An "invalid array index" compiler error signifies that the constant values have changed. - // Re-run the stringer command to generate them again. - var x [1]struct{} - _ = x[UnOpUnknown-0] -} - -const _UnOp_name = "UnOpUnknown" - -var _UnOp_index = [...]uint8{0, 11} - -func (i UnOp) String() string { - if i >= UnOp(len(_UnOp_index)-1) { - return "UnOp(" + strconv.FormatInt(int64(i), 10) + ")" - } - return _UnOp_name[_UnOp_index[i]:_UnOp_index[i+1]] -} diff --git a/compiler/ir/valtype.go b/compiler/ir/valtype.go deleted file mode 100644 index 35b0916a2d..0000000000 --- a/compiler/ir/valtype.go +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Cadence - The resource-oriented smart contract programming language - * - * Copyright Flow Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package ir - -//go:generate go run golang.org/x/tools/cmd/stringer -type=ValType - -type ValType uint - -const ( - ValTypeUnknown ValType = iota - ValTypeInt - ValTypeString -) diff --git a/compiler/ir/valtype_string.go b/compiler/ir/valtype_string.go deleted file mode 100644 index 0ca71428d0..0000000000 --- a/compiler/ir/valtype_string.go +++ /dev/null @@ -1,25 +0,0 @@ -// Code generated by "stringer -type=ValType"; DO NOT EDIT. - -package ir - -import "strconv" - -func _() { - // An "invalid array index" compiler error signifies that the constant values have changed. - // Re-run the stringer command to generate them again. - var x [1]struct{} - _ = x[ValTypeUnknown-0] - _ = x[ValTypeInt-1] - _ = x[ValTypeString-2] -} - -const _ValType_name = "ValTypeUnknownValTypeIntValTypeString" - -var _ValType_index = [...]uint8{0, 14, 24, 37} - -func (i ValType) String() string { - if i >= ValType(len(_ValType_index)-1) { - return "ValType(" + strconv.FormatInt(int64(i), 10) + ")" - } - return _ValType_name[_ValType_index[i]:_ValType_index[i+1]] -} diff --git a/compiler/ir/visitor.go b/compiler/ir/visitor.go deleted file mode 100644 index 61b24de0a3..0000000000 --- a/compiler/ir/visitor.go +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Cadence - The resource-oriented smart contract programming language - * - * Copyright Flow Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package ir - -type Repr any - -type ConstVisitor interface { - VisitInt(Int) Repr - VisitString(String) Repr -} - -type StmtVisitor interface { - VisitSequence(*Sequence) Repr - VisitBlock(*Block) Repr - VisitLoop(*Loop) Repr - VisitIf(*If) Repr - VisitBranch(*Branch) Repr - VisitBranchIf(*BranchIf) Repr - VisitStoreLocal(*StoreLocal) Repr - VisitDrop(*Drop) Repr - VisitReturn(*Return) Repr -} - -type ExprVisitor interface { - VisitConst(*Const) Repr - VisitCopyLocal(*CopyLocal) Repr - VisitMoveLocal(*MoveLocal) Repr - VisitUnOpExpr(*UnOpExpr) Repr - VisitBinOpExpr(*BinOpExpr) Repr - VisitCall(*Call) Repr -} - -type Visitor interface { - ConstVisitor - StmtVisitor - ExprVisitor - VisitFunc(f *Func) Repr -} diff --git a/compiler/local.go b/compiler/local.go deleted file mode 100644 index 8dc5ad765f..0000000000 --- a/compiler/local.go +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Cadence - The resource-oriented smart contract programming language - * - * Copyright Flow Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package compiler - -import ( - "github.com/onflow/cadence/compiler/ir" -) - -type Local struct { - Index uint32 - Type ir.ValType -} - -func NewLocal(index uint32, valType ir.ValType) *Local { - return &Local{ - Index: index, - Type: valType, - } -} diff --git a/compiler/wasm/block.go b/compiler/wasm/block.go deleted file mode 100644 index 127be91a29..0000000000 --- a/compiler/wasm/block.go +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Cadence - The resource-oriented smart contract programming language - * - * Copyright Flow Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package wasm - -type Block struct { - BlockType BlockType - Instructions1 []Instruction - Instructions2 []Instruction -} diff --git a/compiler/wasm/blocktype.go b/compiler/wasm/blocktype.go deleted file mode 100644 index d62a36d4ad..0000000000 --- a/compiler/wasm/blocktype.go +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Cadence - The resource-oriented smart contract programming language - * - * Copyright Flow Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package wasm - -const emptyBlockType byte = 0x40 - -type BlockType interface { - isBlockType() - write(writer *WASMWriter) error -} - -type TypeIndexBlockType struct { - TypeIndex uint32 -} - -func (t TypeIndexBlockType) write(w *WASMWriter) error { - // "the type index in a block type is encoded as a positive signed integer, - // so that its signed LEB128 bit pattern cannot collide with the encoding of value types or the special code 0x40, - // which correspond to the LEB128 encoding of negative integers. - // To avoid any loss in the range of allowed indices, it is treated as a 33 bit signed integer." - return w.buf.writeInt64LEB128(int64(t.TypeIndex)) -} - -func (TypeIndexBlockType) isBlockType() {} diff --git a/compiler/wasm/buf.go b/compiler/wasm/buf.go deleted file mode 100644 index d0483d165b..0000000000 --- a/compiler/wasm/buf.go +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Cadence - The resource-oriented smart contract programming language - * - * Copyright Flow Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package wasm - -import ( - "io" -) - -type offset int - -// Buffer is a byte buffer, which allows reading and writing. -type Buffer struct { - data []byte - offset offset -} - -func (buf *Buffer) WriteByte(b byte) error { - if buf.offset < offset(len(buf.data)) { - buf.data[buf.offset] = b - } else { - buf.data = append(buf.data, b) - } - buf.offset++ - return nil -} - -func (buf *Buffer) WriteBytes(data []byte) error { - for _, b := range data { - err := buf.WriteByte(b) - if err != nil { - return err - } - } - return nil -} - -func (buf *Buffer) Read(data []byte) (int, error) { - n := copy(data, buf.data[buf.offset:]) - if n == 0 && len(data) != 0 { - return 0, io.EOF - } - buf.offset += offset(n) - return n, nil -} - -func (buf *Buffer) ReadByte() (byte, error) { - if buf.offset >= offset(len(buf.data)) { - return 0, io.EOF - } - b := buf.data[buf.offset] - buf.offset++ - return b, nil -} - -func (buf *Buffer) PeekByte() (byte, error) { - if buf.offset >= offset(len(buf.data)) { - return 0, io.EOF - } - b := buf.data[buf.offset] - return b, nil -} - -func (buf *Buffer) ReadBytesEqual(expected []byte) (bool, error) { - off := buf.offset - for _, b := range expected { - if off >= offset(len(buf.data)) { - return false, io.EOF - } - if buf.data[off] != b { - return false, nil - } - off++ - } - buf.offset = off - return true, nil -} - -func (buf *Buffer) Bytes() []byte { - return buf.data -} diff --git a/compiler/wasm/data.go b/compiler/wasm/data.go deleted file mode 100644 index f5a051d452..0000000000 --- a/compiler/wasm/data.go +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Cadence - The resource-oriented smart contract programming language - * - * Copyright Flow Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package wasm - -// Data represents a data segment, which initializes a range of memory, -// at a given offset, with a static vector of bytes. -type Data struct { - // must be constant, as defined in the spec - // (https://webassembly.github.io/spec/core/valid/instructions.html#constant-expressions) - Offset []Instruction - Init []byte - MemoryIndex uint32 -} diff --git a/compiler/wasm/errors.go b/compiler/wasm/errors.go deleted file mode 100644 index 9317b89d7b..0000000000 --- a/compiler/wasm/errors.go +++ /dev/null @@ -1,928 +0,0 @@ -/* - * Cadence - The resource-oriented smart contract programming language - * - * Copyright Flow Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package wasm - -import ( - "fmt" -) - -// InvalidMagicError is returned when the WASM binary -// does not start with the magic byte sequence -type InvalidMagicError struct { - ReadError error - Offset int -} - -func (e InvalidMagicError) Error() string { - return fmt.Sprintf( - "invalid magic at offset %d", - e.Offset, - ) -} - -func (e InvalidMagicError) Unwrap() error { - return e.ReadError -} - -// InvalidMagicError is returned when the WASM binary -// does not have the expected version -type InvalidVersionError struct { - ReadError error - Offset int -} - -func (e InvalidVersionError) Error() string { - return fmt.Sprintf( - "invalid version at offset %d", - e.Offset, - ) -} - -func (e InvalidVersionError) Unwrap() error { - return e.ReadError -} - -// InvalidSectionIDError is returned when the WASM binary specifies -// an invalid section ID -type InvalidSectionIDError struct { - ReadError error - Offset int - SectionID sectionID -} - -func (e InvalidSectionIDError) Error() string { - return fmt.Sprintf( - "invalid section ID %d at offset %d", - e.SectionID, - e.Offset, - ) -} - -func (e InvalidSectionIDError) Unwrap() error { - return e.ReadError -} - -// InvalidDuplicateSectionError is returned when the WASM binary specifies -// a duplicate section -type InvalidDuplicateSectionError struct { - Offset int - SectionID sectionID -} - -func (e InvalidDuplicateSectionError) Error() string { - return fmt.Sprintf( - "invalid duplicate section with ID %d at offset %d", - e.SectionID, - e.Offset, - ) -} - -// InvalidSectionOrderError is returned when the WASM binary specifies -// a non-custom section out-of-order -type InvalidSectionOrderError struct { - Offset int - SectionID sectionID -} - -func (e InvalidSectionOrderError) Error() string { - return fmt.Sprintf( - "out-of-order section with ID %d at offset %d", - e.SectionID, - e.Offset, - ) -} - -// InvalidSectionSizeError is returned when the WASM binary specifies -// an invalid section size -type InvalidSectionSizeError struct { - ReadError error - Offset int -} - -func (e InvalidSectionSizeError) Error() string { - return fmt.Sprintf( - "invalid section size at offset %d: %s", - e.Offset, - e.ReadError, - ) -} - -func (e InvalidSectionSizeError) Unwrap() error { - return e.ReadError -} - -// InvalidValTypeError is returned when the WASM binary specifies -// an invalid value type -type InvalidValTypeError struct { - ReadError error - Offset int - ValType ValueType -} - -func (e InvalidValTypeError) Error() string { - return fmt.Sprintf( - "invalid value type %d at offset %d", - e.ValType, - e.Offset, - ) -} - -func (e InvalidValTypeError) Unwrap() error { - return e.ReadError -} - -// InvalidFuncTypeIndicatorError is returned when the WASM binary specifies -// an invalid function type indicator -type InvalidFuncTypeIndicatorError struct { - ReadError error - Offset int - FuncTypeIndicator byte -} - -func (e InvalidFuncTypeIndicatorError) Error() string { - return fmt.Sprintf( - "invalid function type indicator at offset %d: got %x, expected %x", - e.Offset, - e.FuncTypeIndicator, - functionTypeIndicator, - ) -} - -func (e InvalidFuncTypeIndicatorError) Unwrap() error { - return e.ReadError -} - -// InvalidFuncTypeParameterCountError is returned when the WASM binary specifies -// an invalid func type parameter count -type InvalidFuncTypeParameterCountError struct { - ReadError error - Offset int -} - -func (e InvalidFuncTypeParameterCountError) Error() string { - return fmt.Sprintf( - "invalid function type parameter count at offset %d", - e.Offset, - ) -} - -func (e InvalidFuncTypeParameterCountError) Unwrap() error { - return e.ReadError -} - -// InvalidFuncTypeParameterTypeError is returned when the WASM binary specifies -// an invalid function type parameter type -type InvalidFuncTypeParameterTypeError struct { - ReadError error - Index int -} - -func (e InvalidFuncTypeParameterTypeError) Error() string { - return fmt.Sprintf( - "invalid function type parameter type at index %d", - e.Index, - ) -} - -func (e InvalidFuncTypeParameterTypeError) Unwrap() error { - return e.ReadError -} - -// InvalidFuncTypeResultCountError is returned when the WASM binary specifies -// an invalid func type result count -type InvalidFuncTypeResultCountError struct { - ReadError error - Offset int -} - -func (e InvalidFuncTypeResultCountError) Error() string { - return fmt.Sprintf( - "invalid function type result count at offset %d", - e.Offset, - ) -} - -func (e InvalidFuncTypeResultCountError) Unwrap() error { - return e.ReadError -} - -// InvalidFuncTypeResultTypeError is returned when the WASM binary specifies -// an invalid function type result type -type InvalidFuncTypeResultTypeError struct { - ReadError error - Index int -} - -func (e InvalidFuncTypeResultTypeError) Error() string { - return fmt.Sprintf( - "invalid function type result type at index %d", - e.Index, - ) -} - -func (e InvalidFuncTypeResultTypeError) Unwrap() error { - return e.ReadError -} - -// InvalidTypeSectionTypeCountError is returned when the WASM binary specifies -// an invalid count in the type section -type InvalidTypeSectionTypeCountError struct { - ReadError error - Offset int -} - -func (e InvalidTypeSectionTypeCountError) Error() string { - return fmt.Sprintf( - "invalid type count in type section at offset %d", - e.Offset, - ) -} - -func (e InvalidTypeSectionTypeCountError) Unwrap() error { - return e.ReadError -} - -// InvalidImportSectionImportCountError is returned when the WASM binary specifies -// an invalid count in the import section -type InvalidImportSectionImportCountError struct { - ReadError error - Offset int -} - -func (e InvalidImportSectionImportCountError) Error() string { - return fmt.Sprintf( - "invalid import count in import section at offset %d", - e.Offset, - ) -} - -func (e InvalidImportSectionImportCountError) Unwrap() error { - return e.ReadError -} - -// InvalidImportError is returned when the WASM binary specifies -// invalid import in the import section -type InvalidImportError struct { - ReadError error - Index int -} - -func (e InvalidImportError) Error() string { - return fmt.Sprintf( - "invalid import at index %d", - e.Index, - ) -} - -func (e InvalidImportError) Unwrap() error { - return e.ReadError -} - -// InvalidImportIndicatorError is returned when the WASM binary specifies -// an invalid type indicator in the import section -type InvalidImportIndicatorError struct { - ReadError error - Offset int - ImportIndicator importIndicator -} - -func (e InvalidImportIndicatorError) Error() string { - return fmt.Sprintf( - "invalid import indicator %d at offset %d", - e.ImportIndicator, - e.Offset, - ) -} - -func (e InvalidImportIndicatorError) Unwrap() error { - return e.ReadError -} - -// InvalidImportSectionTypeIndexError is returned when the WASM binary specifies -// an invalid type index in the import section -type InvalidImportSectionTypeIndexError struct { - ReadError error - Offset int -} - -func (e InvalidImportSectionTypeIndexError) Error() string { - return fmt.Sprintf( - "invalid type index in import section at offset %d", - e.Offset, - ) -} - -func (e InvalidImportSectionTypeIndexError) Unwrap() error { - return e.ReadError -} - -// InvalidFunctionSectionFunctionCountError is returned when the WASM binary specifies -// an invalid count in the function section -type InvalidFunctionSectionFunctionCountError struct { - ReadError error - Offset int -} - -func (e InvalidFunctionSectionFunctionCountError) Error() string { - return fmt.Sprintf( - "invalid function count in function section at offset %d", - e.Offset, - ) -} - -func (e InvalidFunctionSectionFunctionCountError) Unwrap() error { - return e.ReadError -} - -// InvalidFunctionSectionTypeIndexError is returned when the WASM binary specifies -// an invalid type index in the function section -type InvalidFunctionSectionTypeIndexError struct { - ReadError error - Offset int - Index int -} - -func (e InvalidFunctionSectionTypeIndexError) Error() string { - return fmt.Sprintf( - "invalid type index in function section at index %d at offset %d", - e.Index, - e.Offset, - ) -} - -func (e InvalidFunctionSectionTypeIndexError) Unwrap() error { - return e.ReadError -} - -// FunctionCountMismatchError is returned when the WASM binary specifies -// information for a different number of functions than previously specified -type FunctionCountMismatchError struct { - Offset int -} - -func (e FunctionCountMismatchError) Error() string { - return fmt.Sprintf( - "function count mismatch at offset %d", - e.Offset, - ) -} - -// InvalidExportSectionExportCountError is returned when the WASM binary specifies -// an invalid count in the export section -type InvalidExportSectionExportCountError struct { - ReadError error - Offset int -} - -func (e InvalidExportSectionExportCountError) Error() string { - return fmt.Sprintf( - "invalid export count in export section at offset %d", - e.Offset, - ) -} - -func (e InvalidExportSectionExportCountError) Unwrap() error { - return e.ReadError -} - -// InvalidExportError is returned when the WASM binary specifies -// invalid export in the export section -type InvalidExportError struct { - ReadError error - Index int -} - -func (e InvalidExportError) Error() string { - return fmt.Sprintf( - "invalid export at index %d", - e.Index, - ) -} - -func (e InvalidExportError) Unwrap() error { - return e.ReadError -} - -// InvalidExportIndicatorError is returned when the WASM binary specifies -// an invalid type indicator in the export section -type InvalidExportIndicatorError struct { - ReadError error - Offset int - ExportIndicator exportIndicator -} - -func (e InvalidExportIndicatorError) Error() string { - return fmt.Sprintf( - "invalid export indicator %d at offset %d", - e.ExportIndicator, - e.Offset, - ) -} - -func (e InvalidExportIndicatorError) Unwrap() error { - return e.ReadError -} - -// InvalidExportSectionIndexError is returned when the WASM binary specifies -// an invalid index in the export section -type InvalidExportSectionIndexError struct { - ReadError error - Offset int -} - -func (e InvalidExportSectionIndexError) Error() string { - return fmt.Sprintf( - "invalid index in export section at offset %d", - e.Offset, - ) -} - -func (e InvalidExportSectionIndexError) Unwrap() error { - return e.ReadError -} - -// InvalidCodeSectionFunctionCountError is returned when the WASM binary specifies -// an invalid function count in the code section -type InvalidCodeSectionFunctionCountError struct { - ReadError error - Offset int -} - -func (e InvalidCodeSectionFunctionCountError) Error() string { - return fmt.Sprintf( - "invalid function count in code section at offset %d", - e.Offset, - ) -} - -func (e InvalidCodeSectionFunctionCountError) Unwrap() error { - return e.ReadError -} - -// InvalidFunctionCodeError is returned when the WASM binary specifies -// invalid code for a function in the code section -type InvalidFunctionCodeError struct { - ReadError error - Index int -} - -func (e InvalidFunctionCodeError) Error() string { - return fmt.Sprintf( - "invalid code for function at index %d", - e.Index, - ) -} - -func (e InvalidFunctionCodeError) Unwrap() error { - return e.ReadError -} - -// InvalidCodeSizeError is returned when the WASM binary specifies -// an invalid code size in the code section -type InvalidCodeSizeError struct { - ReadError error - Offset int -} - -func (e InvalidCodeSizeError) Error() string { - return fmt.Sprintf( - "invalid code size in code section at offset %d", - e.Offset, - ) -} - -// InvalidCodeSectionLocalsCountError is returned when the WASM binary specifies -// an invalid locals count in the code section -type InvalidCodeSectionLocalsCountError struct { - ReadError error - Offset int -} - -func (e InvalidCodeSectionLocalsCountError) Error() string { - return fmt.Sprintf( - "invalid locals count in code section at offset %d", - e.Offset, - ) -} - -func (e InvalidCodeSectionLocalsCountError) Unwrap() error { - return e.ReadError -} - -// InvalidCodeSectionCompressedLocalsCountError is returned when the WASM binary specifies -// an invalid local type in the code section -type InvalidCodeSectionCompressedLocalsCountError struct { - ReadError error - Offset int -} - -func (e InvalidCodeSectionCompressedLocalsCountError) Error() string { - return fmt.Sprintf( - "invalid compressed local type count in code section at offset %d", - e.Offset, - ) -} - -func (e InvalidCodeSectionCompressedLocalsCountError) Unwrap() error { - return e.ReadError -} - -// InvalidCodeSectionLocalTypeError is returned when the WASM binary specifies -// an invalid local type in the code section -type InvalidCodeSectionLocalTypeError struct { - ReadError error - Offset int -} - -func (e InvalidCodeSectionLocalTypeError) Error() string { - return fmt.Sprintf( - "invalid local type in code section at offset %d", - e.Offset, - ) -} - -func (e InvalidCodeSectionLocalTypeError) Unwrap() error { - return e.ReadError -} - -// CodeSectionLocalsCountMismatchError is returned when -// the sum of the compressed locals locals count in the code section does not match -// the number of locals in the code section of the WASM binary -type CodeSectionLocalsCountMismatchError struct { - Offset int - Expected uint32 - Actual uint32 -} - -func (e CodeSectionLocalsCountMismatchError) Error() string { - return fmt.Sprintf( - "local count mismatch in code section at offset %d: expected %d, got %d", - e.Offset, - e.Expected, - e.Actual, - ) -} - -// InvalidOpcodeError is returned when the WASM binary specifies -// an invalid opcode in the code section -type InvalidOpcodeError struct { - ReadError error - Offset int - Opcode opcode -} - -func (e InvalidOpcodeError) Error() string { - return fmt.Sprintf( - "invalid opcode in code section at offset %d: %x", - e.Offset, - e.Opcode, - ) -} - -func (e InvalidOpcodeError) Unwrap() error { - return e.ReadError -} - -// InvalidInstructionArgumentError is returned when the WASM binary specifies -// an invalid argument for an instruction in the code section -type InvalidInstructionArgumentError struct { - ReadError error - Offset int -} - -func (e InvalidInstructionArgumentError) Error() string { - return fmt.Sprintf( - "invalid argument in code section at offset %d", - e.Offset, - ) -} - -func (e InvalidInstructionArgumentError) Unwrap() error { - return e.ReadError -} - -// MissingEndInstructionError is returned when the WASM binary -// misses an end instruction for a function in the code section -type MissingEndInstructionError struct { - Offset int -} - -func (e MissingEndInstructionError) Error() string { - return fmt.Sprintf( - "missing end instruction in code section at offset %d", - e.Offset, - ) -} - -// InvalidNonUTF8NameError is returned when the WASM binary specifies -// or the writer is given a name which is not properly UTF-8 encoded -type InvalidNonUTF8NameError struct { - Name string - Offset int -} - -func (e InvalidNonUTF8NameError) Error() string { - return fmt.Sprintf( - "invalid non UTF-8 string at offset %d: %s", - e.Offset, - e.Name, - ) -} - -// InvalidNameLengthError is returned the WASM binary specifies -// an invalid name length -type InvalidNameLengthError struct { - ReadError error - Offset int -} - -func (e InvalidNameLengthError) Error() string { - return fmt.Sprintf( - "invalid name length at offset %d", - e.Offset, - ) -} - -func (e InvalidNameLengthError) Unwrap() error { - return e.ReadError -} - -// InvalidNameError is returned the WASM binary specifies -// an invalid name -type InvalidNameError struct { - ReadError error - Offset int -} - -func (e InvalidNameError) Error() string { - return fmt.Sprintf( - "invalid name at offset %d", - e.Offset, - ) -} - -func (e InvalidNameError) Unwrap() error { - return e.ReadError -} - -// IncompleteNameError is returned the WASM binary specifies -// an incomplete name -type IncompleteNameError struct { - Offset int - Expected uint32 - Actual uint32 -} - -func (e IncompleteNameError) Error() string { - return fmt.Sprintf( - "incomplete name at offset %d. expected %d bytes, got %d", - e.Offset, - e.Expected, - e.Actual, - ) -} - -// InvalidBlockSecondInstructionsError is returned when the WASM binary specifies -// or the writer is given a second set of instructions in a block that -// is not allowed to have it (only the 'if' instruction may have it) -type InvalidBlockSecondInstructionsError struct { - Offset int -} - -func (e InvalidBlockSecondInstructionsError) Error() string { - return fmt.Sprintf( - "invalid second set of instructions at offset %d", - e.Offset, - ) -} - -// InvalidInstructionVectorArgumentCountError is returned when the WASM binary specifies -// an invalid count for a vector argument of an instruction -type InvalidInstructionVectorArgumentCountError struct { - ReadError error - Offset int -} - -func (e InvalidInstructionVectorArgumentCountError) Error() string { - return fmt.Sprintf( - "invalid vector count for argument of instruction at offset %d", - e.Offset, - ) -} - -func (e InvalidInstructionVectorArgumentCountError) Unwrap() error { - return e.ReadError -} - -// InvalidBlockTypeTypeIndexError is returned when the WASM binary specifies -// an invalid type index as a block type -type InvalidBlockTypeTypeIndexError struct { - TypeIndex int64 - Offset int -} - -func (e InvalidBlockTypeTypeIndexError) Error() string { - return fmt.Sprintf( - "invalid type index in block type at offset %d: %d", - e.Offset, - e.TypeIndex, - ) -} - -// InvalidDataSectionSegmentCountError is returned when the WASM binary specifies -// an invalid count in the data section -type InvalidDataSectionSegmentCountError struct { - ReadError error - Offset int -} - -func (e InvalidDataSectionSegmentCountError) Error() string { - return fmt.Sprintf( - "invalid segment count in data section at offset %d", - e.Offset, - ) -} - -func (e InvalidDataSectionSegmentCountError) Unwrap() error { - return e.ReadError -} - -// InvalidDataSegmentError is returned when the WASM binary specifies -// invalid segment in the data section -type InvalidDataSegmentError struct { - ReadError error - Index int -} - -func (e InvalidDataSegmentError) Error() string { - return fmt.Sprintf( - "invalid data segment at index %d", - e.Index, - ) -} - -func (e InvalidDataSegmentError) Unwrap() error { - return e.ReadError -} - -// InvalidDataSectionMemoryIndexError is returned when the WASM binary specifies -// an invalid memory index in the data section -type InvalidDataSectionMemoryIndexError struct { - ReadError error - Offset int -} - -func (e InvalidDataSectionMemoryIndexError) Error() string { - return fmt.Sprintf( - "invalid memory index in data section at offset %d", - e.Offset, - ) -} - -func (e InvalidDataSectionMemoryIndexError) Unwrap() error { - return e.ReadError -} - -// InvalidDataSectionInitByteCountError is returned when the WASM binary specifies -// an invalid init byte count in the data section -type InvalidDataSectionInitByteCountError struct { - ReadError error - Offset int -} - -func (e InvalidDataSectionInitByteCountError) Error() string { - return fmt.Sprintf( - "invalid init byte count in data section at offset %d", - e.Offset, - ) -} - -func (e InvalidDataSectionInitByteCountError) Unwrap() error { - return e.ReadError -} - -// InvalidMemorySectionMemoryCountError is returned when the WASM binary specifies -// an invalid count in the memory section -type InvalidMemorySectionMemoryCountError struct { - ReadError error - Offset int -} - -func (e InvalidMemorySectionMemoryCountError) Error() string { - return fmt.Sprintf( - "invalid memories count in memory section at offset %d", - e.Offset, - ) -} - -func (e InvalidMemorySectionMemoryCountError) Unwrap() error { - return e.ReadError -} - -// InvalidMemoryError is returned when the WASM binary specifies -// invalid memory in the memory section -type InvalidMemoryError struct { - ReadError error - Index int -} - -func (e InvalidMemoryError) Error() string { - return fmt.Sprintf( - "invalid memory at index %d", - e.Index, - ) -} - -func (e InvalidMemoryError) Unwrap() error { - return e.ReadError -} - -// InvalidLimitIndicatorError is returned when the WASM binary specifies -// an invalid limit indicator -type InvalidLimitIndicatorError struct { - ReadError error - Offset int - LimitIndicator byte -} - -func (e InvalidLimitIndicatorError) Error() string { - return fmt.Sprintf( - "invalid limit indicator at offset %d: %x", - e.Offset, - e.LimitIndicator, - ) -} - -func (e InvalidLimitIndicatorError) Unwrap() error { - return e.ReadError -} - -// InvalidLimitMinError is returned when the WASM binary specifies -// an invalid limit minimum -type InvalidLimitMinError struct { - ReadError error - Offset int -} - -func (e InvalidLimitMinError) Error() string { - return fmt.Sprintf( - "invalid limit minimum at offset %d", - e.Offset, - ) -} - -func (e InvalidLimitMinError) Unwrap() error { - return e.ReadError -} - -// InvalidLimitMaxError is returned when the WASM binary specifies -// an invalid limit maximum -type InvalidLimitMaxError struct { - ReadError error - Offset int -} - -func (e InvalidLimitMaxError) Error() string { - return fmt.Sprintf( - "invalid limit maximum at offset %d", - e.Offset, - ) -} - -func (e InvalidLimitMaxError) Unwrap() error { - return e.ReadError -} - -// InvalidStartSectionFunctionIndexError is returned when the WASM binary specifies -// an invalid function index in the start section -type InvalidStartSectionFunctionIndexError struct { - ReadError error - Offset int -} - -func (e InvalidStartSectionFunctionIndexError) Error() string { - return fmt.Sprintf( - "invalid function index in start section at offset %d", - e.Offset, - ) -} - -func (e InvalidStartSectionFunctionIndexError) Unwrap() error { - return e.ReadError -} diff --git a/compiler/wasm/export.go b/compiler/wasm/export.go deleted file mode 100644 index 1f2e3e04ad..0000000000 --- a/compiler/wasm/export.go +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Cadence - The resource-oriented smart contract programming language - * - * Copyright Flow Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package wasm - -// Exports represents an export -type Export struct { - Descriptor ExportDescriptor - Name string -} - -// exportIndicator is the byte used to indicate the kind of export in the WASM binary -type exportIndicator byte - -const ( - // exportIndicatorFunction is the byte used to indicate the export of a function in the WASM binary - exportIndicatorFunction exportIndicator = 0x0 - // exportIndicatorMemory is the byte used to indicate the export of a memory in the WASM binary - exportIndicatorMemory exportIndicator = 0x2 -) - -// ExportDescriptor represents an export (e.g. a function, memory, etc.) -type ExportDescriptor interface { - isExportDescriptor() -} - -// FunctionExport represents the export of a function -type FunctionExport struct { - FunctionIndex uint32 -} - -func (FunctionExport) isExportDescriptor() {} - -// MemoryExport represents the export of a memory -type MemoryExport struct { - MemoryIndex uint32 -} - -func (MemoryExport) isExportDescriptor() {} diff --git a/compiler/wasm/function.go b/compiler/wasm/function.go deleted file mode 100644 index 0f22ff75d8..0000000000 --- a/compiler/wasm/function.go +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Cadence - The resource-oriented smart contract programming language - * - * Copyright Flow Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package wasm - -// Function represents a function -type Function struct { - Code *Code - Name string - TypeIndex uint32 -} - -// Code represents the code of a function -type Code struct { - Locals []ValueType - Instructions []Instruction -} diff --git a/compiler/wasm/functiontype.go b/compiler/wasm/functiontype.go deleted file mode 100644 index 86287073bd..0000000000 --- a/compiler/wasm/functiontype.go +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Cadence - The resource-oriented smart contract programming language - * - * Copyright Flow Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package wasm - -// functionTypeIndicator is the byte used to indicate a function type in the WASM binary -const functionTypeIndicator = 0x60 - -// FunctionType is the type of a function. -// It may have multiple parameters and return values -type FunctionType struct { - Params []ValueType - Results []ValueType -} diff --git a/compiler/wasm/gen/main.go b/compiler/wasm/gen/main.go deleted file mode 100644 index f052c85f8e..0000000000 --- a/compiler/wasm/gen/main.go +++ /dev/null @@ -1,959 +0,0 @@ -/* - * Cadence - The resource-oriented smart contract programming language - * - * Copyright Flow Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package main - -import ( - "fmt" - "os" - "regexp" - "strings" - "text/template" -) - -const fileTemplate = `// Code generated by utils/version. DO NOT EDIT. -/* - * Cadence - The resource-oriented smart contract programming language - * - * Copyright Flow Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -{{goGenerateComment}} - -package wasm - -import ( - "io" -) - -{{range .Instructions -}} -// Instruction{{.Identifier}} is the '{{.Name}}' instruction -// -type Instruction{{.Identifier}} struct{{if .Arguments}} { -{{- range .Arguments}}{{- if .Identifier}} - {{.Identifier}} {{.Type.FieldType}}{{end}}{{end}} -} -{{- else}}{}{{- end}} - -func (Instruction{{.Identifier}}) isInstruction() {} - -func (i Instruction{{.Identifier}}) write(w *WASMWriter) error { - err := w.writeOpcode({{.OpcodeList}}) - if err != nil { - return err - } -{{range .Arguments}} - {{.Variable}} := i.{{.Identifier}} - {{.Type.Write .Variable}} -{{end}} - return nil -} - -{{end -}} - -const ( -{{- range .Instructions }} - // {{.OpcodeIdentifier}} is the opcode for the '{{.Name}}' instruction - {{.OpcodeIdentifier}} opcode = {{.Opcode | printf "0x%x"}} -{{- end}} -) - -// readInstruction reads an instruction in the WASM binary -// -func (r *WASMReader) readInstruction() (Instruction, error) { - opcodeOffset := r.buf.offset - b, err := r.buf.ReadByte() - - c := opcode(b) - - if err != nil { - if err == io.EOF { - return nil, MissingEndInstructionError{ - Offset: int(opcodeOffset), - } - } else { - return nil, InvalidOpcodeError{ - Offset: int(opcodeOffset), - Opcode: c, - ReadError: err, - } - } - } -{{switch .}} -} -` - -const switchTemplate = ` -switch c { -{{- range $key, $group := . }} -case {{ $key }}: -{{- if (eq (len $group.Instructions) 1)}} -{{- with (index $group.Instructions 0) }} -{{- range .Arguments}} - {{.Type.Read .Variable}} -{{end}} - return Instruction{{.Identifier}}{{if .Arguments}}{ -{{- range .Arguments}} - {{.Identifier}}: {{.Variable}},{{end}} - } -{{- else}}{}{{- end}}, nil -{{end}} -{{- else}} -{{switch $group}} -{{- end}}{{end}} -default: - return nil, InvalidOpcodeError{ - Offset: int(opcodeOffset), - Opcode: c, - ReadError: err, - } -} -` - -type opcodes []byte - -type argumentType interface { - isArgumentType() - FieldType() string - Read(variable string) string - Write(variable string) string -} - -type ArgumentTypeUint32 struct{} - -func (t ArgumentTypeUint32) isArgumentType() {} - -func (t ArgumentTypeUint32) FieldType() string { - return "uint32" -} - -func (t ArgumentTypeUint32) Read(variable string) string { - return fmt.Sprintf( - `%s, err := r.readUint32LEB128InstructionArgument() - if err != nil { - return nil, err - }`, - variable, - ) -} - -func (t ArgumentTypeUint32) Write(variable string) string { - return fmt.Sprintf( - `err = w.buf.writeUint32LEB128(%s) - if err != nil { - return err - }`, - variable, - ) -} - -type ArgumentTypeInt32 struct{} - -func (t ArgumentTypeInt32) isArgumentType() {} - -func (t ArgumentTypeInt32) FieldType() string { - return "int32" -} - -func (t ArgumentTypeInt32) Read(variable string) string { - return fmt.Sprintf( - `%s, err := r.readInt32LEB128InstructionArgument() - if err != nil { - return nil, err - }`, - variable, - ) -} - -func (t ArgumentTypeInt32) Write(variable string) string { - return fmt.Sprintf( - `err = w.buf.writeInt32LEB128(%s) - if err != nil { - return err - }`, - variable, - ) -} - -type ArgumentTypeInt64 struct{} - -func (t ArgumentTypeInt64) isArgumentType() {} - -func (t ArgumentTypeInt64) FieldType() string { - return "int64" -} - -func (t ArgumentTypeInt64) Read(variable string) string { - return fmt.Sprintf( - `%s, err := r.readInt64LEB128InstructionArgument() - if err != nil { - return nil, err - }`, - variable, - ) -} - -func (t ArgumentTypeInt64) Write(variable string) string { - return fmt.Sprintf( - `err = w.buf.writeInt64LEB128(%s) - if err != nil { - return err - }`, - variable, - ) -} - -type ArgumentTypeBlock struct { - AllowElse bool -} - -func (t ArgumentTypeBlock) isArgumentType() {} - -func (t ArgumentTypeBlock) FieldType() string { - return "Block" -} - -func (t ArgumentTypeBlock) Read(variable string) string { - return fmt.Sprintf( - `%s, err := r.readBlockInstructionArgument(%v) - if err != nil { - return nil, err - }`, - variable, - t.AllowElse, - ) -} - -func (t ArgumentTypeBlock) Write(variable string) string { - return fmt.Sprintf( - `err = w.writeBlockInstructionArgument(%s, %v) - if err != nil { - return err - }`, - variable, - t.AllowElse, - ) -} - -type ArgumentTypeVector struct { - ArgumentType argumentType -} - -func (t ArgumentTypeVector) isArgumentType() {} - -func (t ArgumentTypeVector) FieldType() string { - return fmt.Sprintf("[]%s", t.ArgumentType.FieldType()) -} - -func (t ArgumentTypeVector) Read(variable string) string { - // TODO: improve error - - elementVariable := variable + "Element" - - return fmt.Sprintf( - `%[1]sCountOffset := r.buf.offset - %[1]sCount, err := r.buf.readUint32LEB128() - if err != nil { - return nil, InvalidInstructionVectorArgumentCountError{ - Offset: int(%[1]sCountOffset), - ReadError: err, - } - } - - %[1]s := make(%[2]s, %[1]sCount) - - for i := uint32(0); i < %[1]sCount; i++ { - %[3]s - %[1]s[i] = %[4]s - }`, - variable, - t.FieldType(), - t.ArgumentType.Read(elementVariable), - elementVariable, - ) -} - -func (t ArgumentTypeVector) Write(variable string) string { - // TODO: improve error - - elementVariable := variable + "Element" - - return fmt.Sprintf( - `%[1]sCount := len(%[1]s) - err = w.buf.writeUint32LEB128(uint32(%[1]sCount)) - if err != nil { - return err - } - - for i := 0; i < %[1]sCount; i++ { - %[3]s := %[1]s[i] - %[2]s - }`, - variable, - t.ArgumentType.Write(elementVariable), - elementVariable, - ) -} - -type argument struct { - Type argumentType - Identifier string -} - -func (a argument) Variable() string { - first := strings.ToLower(string(a.Identifier[0])) - rest := a.Identifier[1:] - return first + rest -} - -type arguments []argument - -type instruction struct { - Name string - Opcodes opcodes - Arguments arguments -} - -var identifierPartRegexp = regexp.MustCompile("(^|[._])[A-Za-z0-9]") - -func (ins instruction) Identifier() string { - return string(identifierPartRegexp.ReplaceAllFunc([]byte(ins.Name), func(bytes []byte) []byte { - return []byte(strings.ToUpper(string(bytes[len(bytes)-1]))) - })) -} -func (ins instruction) OpcodeList() string { - var b strings.Builder - - count := len(ins.Opcodes) - - // prefix - for i := 0; i < count-1; i++ { - if i > 0 { - b.WriteString(", ") - } - opcode := ins.Opcodes[i] - _, err := fmt.Fprintf(&b, "0x%x", opcode) - if err != nil { - panic(err) - } - } - - // final opcode - if count > 1 { - b.WriteString(", ") - } - _, err := b.WriteString(ins.OpcodeIdentifier()) - if err != nil { - panic(err) - } - - return b.String() -} - -func (ins instruction) Opcode() byte { - return ins.Opcodes[len(ins.Opcodes)-1] -} - -func (ins instruction) OpcodeIdentifier() string { - return fmt.Sprintf("opcode%s", ins.Identifier()) -} - -type instructionGroup struct { - Instructions []instruction - Depth int -} - -func (group instructionGroup) GroupByOpcode() map[string]instructionGroup { - result := map[string]instructionGroup{} - - for _, ins := range group.Instructions { - innerDepth := group.Depth + 1 - atEnd := len(ins.Opcodes) <= innerDepth - opcode := ins.Opcodes[group.Depth] - var key string - if atEnd { - key = ins.OpcodeIdentifier() - } else { - key = fmt.Sprintf("0x%x", opcode) - } - innerGroup := result[key] - innerGroup.Depth = innerDepth - innerGroup.Instructions = append(innerGroup.Instructions, ins) - result[key] = innerGroup - } - - return result -} - -var trailingWhitespaceRegexp = regexp.MustCompile("(?m:[ \t]+$)") - -const target = "instructions.go" - -var indexArgumentType = ArgumentTypeUint32{} - -func main() { - - f, err := os.Create(target) - if err != nil { - panic(fmt.Errorf("could not create %s: %w\n", target, err)) - } - defer func() { - _ = f.Close() - }() - - var generateSwitch func(group instructionGroup) (string, error) - - templateFuncs := map[string]any{ - "goGenerateComment": func() string { - // NOTE: must be templated/injected, as otherwise - // it will be detected itself as a go generate invocation itself - return "//go:generate go run ./gen/main.go\n//go:generate go fmt $GOFILE" - }, - "switch": func(group instructionGroup) (string, error) { - res, err := generateSwitch(group) - if err != nil { - return "", err - } - pad := strings.Repeat("\t", group.Depth+1) - padded := pad + strings.ReplaceAll(res, "\n", "\n"+pad) - trimmed := trailingWhitespaceRegexp.ReplaceAll([]byte(padded), nil) - return string(trimmed), nil - }, - } - - parsedSwitchTemplate := template.Must( - template.New("switch"). - Funcs(templateFuncs). - Parse(switchTemplate), - ) - - parsedFileTemplate := template.Must( - template.New("instructions"). - Funcs(templateFuncs). - Parse(fileTemplate), - ) - - generateSwitch = func(instructions instructionGroup) (string, error) { - var b strings.Builder - err := parsedSwitchTemplate.Execute(&b, instructions.GroupByOpcode()) - if err != nil { - return "", err - } - return b.String(), nil - } - - declare := func(instructions []instruction) { - err = parsedFileTemplate.Execute(f, - instructionGroup{ - Depth: 0, - Instructions: instructions, - }, - ) - if err != nil { - panic(err) - } - } - - declare([]instruction{ - // Control Instructions - { - Name: "unreachable", - Opcodes: opcodes{0x0}, - Arguments: arguments{}, - }, - { - Name: "nop", - Opcodes: opcodes{0x01}, - Arguments: arguments{}, - }, - { - Name: "block", - Opcodes: opcodes{0x02}, - Arguments: arguments{ - {Identifier: "Block", Type: ArgumentTypeBlock{AllowElse: false}}, - }, - }, - { - Name: "loop", - Opcodes: opcodes{0x03}, - Arguments: arguments{ - {Identifier: "Block", Type: ArgumentTypeBlock{AllowElse: false}}, - }, - }, - { - Name: "if", - Opcodes: opcodes{0x04}, - Arguments: arguments{ - {Identifier: "Block", Type: ArgumentTypeBlock{AllowElse: true}}, - }, - }, - { - Name: "end", - Opcodes: opcodes{0x0B}, - Arguments: arguments{}, - }, - { - Name: "br", - Opcodes: opcodes{0x0C}, - Arguments: arguments{ - {Identifier: "LabelIndex", Type: indexArgumentType}, - }, - }, - { - Name: "br_if", - Opcodes: opcodes{0x0D}, - Arguments: arguments{ - {Identifier: "LabelIndex", Type: indexArgumentType}, - }, - }, - { - Name: "br_table", - Opcodes: opcodes{0x0E}, - Arguments: arguments{ - {Identifier: "LabelIndices", Type: ArgumentTypeVector{ArgumentType: indexArgumentType}}, - {Identifier: "DefaultLabelIndex", Type: indexArgumentType}, - }, - }, - { - Name: "return", - Opcodes: opcodes{0x0F}, - Arguments: arguments{}, - }, - { - Name: "call", - Opcodes: opcodes{0x10}, - Arguments: arguments{ - {Identifier: "FuncIndex", Type: indexArgumentType}, - }, - }, - { - Name: "call_indirect", - Opcodes: opcodes{0x11}, - Arguments: arguments{ - {Identifier: "TypeIndex", Type: indexArgumentType}, - {Identifier: "TableIndex", Type: indexArgumentType}, - }, - }, - // Reference Instructions - { - Name: "ref.null", - Opcodes: opcodes{0xD0}, - Arguments: arguments{ - {Identifier: "TypeIndex", Type: indexArgumentType}, - }, - }, - { - Name: "ref.is_null", - Opcodes: opcodes{0xD1}, - Arguments: arguments{}, - }, - { - Name: "ref.func", - Opcodes: opcodes{0xD2}, - Arguments: arguments{ - {Identifier: "FuncIndex", Type: indexArgumentType}, - }, - }, - // Parametric Instructions - { - Name: "drop", - Opcodes: opcodes{0x1A}, - Arguments: arguments{}, - }, - { - Name: "select", - Opcodes: opcodes{0x1B}, - Arguments: arguments{}, - }, - // Variable Instructions - { - Name: "local.get", - Opcodes: opcodes{0x20}, - Arguments: arguments{ - {Identifier: "LocalIndex", Type: indexArgumentType}, - }, - }, - { - Name: "local.set", - Opcodes: opcodes{0x21}, - Arguments: arguments{ - {Identifier: "LocalIndex", Type: indexArgumentType}, - }, - }, - { - Name: "local.tee", - Opcodes: opcodes{0x22}, - Arguments: arguments{ - {Identifier: "LocalIndex", Type: indexArgumentType}, - }, - }, - { - Name: "global.get", - Opcodes: opcodes{0x23}, - Arguments: arguments{ - {Identifier: "GlobalIndex", Type: indexArgumentType}, - }, - }, - { - Name: "global.set", - Opcodes: opcodes{0x24}, - Arguments: arguments{ - {Identifier: "GlobalIndex", Type: indexArgumentType}, - }, - }, - // Numeric Instructions - // const instructions are followed by the respective literal - { - Name: "i32.const", - Opcodes: opcodes{0x41}, - Arguments: arguments{ - // i32, "Uninterpreted integers are encoded as signed integers." - {Identifier: "Value", Type: ArgumentTypeInt32{}}, - }, - }, - { - Name: "i64.const", - Opcodes: opcodes{0x42}, - Arguments: arguments{ - // i64, "Uninterpreted integers are encoded as signed integers." - {Identifier: "Value", Type: ArgumentTypeInt64{}}, - }, - }, - // All other numeric instructions are plain opcodes without any immediates. - { - Name: "i32.eqz", - Opcodes: opcodes{0x45}, - Arguments: arguments{}, - }, - { - Name: "i32.eq", - Opcodes: opcodes{0x46}, - Arguments: arguments{}, - }, - { - Name: "i32.ne", - Opcodes: opcodes{0x47}, - Arguments: arguments{}, - }, - { - Name: "i32.lt_s", - Opcodes: opcodes{0x48}, - Arguments: arguments{}, - }, - { - Name: "i32.lt_u", - Opcodes: opcodes{0x49}, - Arguments: arguments{}, - }, - { - Name: "i32.gt_s", - Opcodes: opcodes{0x4a}, - Arguments: arguments{}, - }, - { - Name: "i32.gt_u", - Opcodes: opcodes{0x4b}, - Arguments: arguments{}, - }, - { - Name: "i32.le_s", - Opcodes: opcodes{0x4c}, - Arguments: arguments{}, - }, - { - Name: "i32.le_u", - Opcodes: opcodes{0x4d}, - Arguments: arguments{}, - }, - { - Name: "i32.ge_s", - Opcodes: opcodes{0x4e}, - Arguments: arguments{}, - }, - { - Name: "i32.ge_u", - Opcodes: opcodes{0x4f}, - Arguments: arguments{}, - }, - { - Name: "i64.eqz", - Opcodes: opcodes{0x50}, - Arguments: arguments{}, - }, - { - Name: "i64.eq", - Opcodes: opcodes{0x51}, - Arguments: arguments{}, - }, - { - Name: "i64.ne", - Opcodes: opcodes{0x52}, - Arguments: arguments{}, - }, - { - Name: "i64.lt_s", - Opcodes: opcodes{0x53}, - Arguments: arguments{}, - }, - { - Name: "i64.lt_u", - Opcodes: opcodes{0x54}, - Arguments: arguments{}, - }, - { - Name: "i64.gt_s", - Opcodes: opcodes{0x55}, - Arguments: arguments{}, - }, - { - Name: "i64.gt_u", - Opcodes: opcodes{0x56}, - Arguments: arguments{}, - }, - { - Name: "i64.le_s", - Opcodes: opcodes{0x57}, - Arguments: arguments{}, - }, - { - Name: "i64.le_u", - Opcodes: opcodes{0x58}, - Arguments: arguments{}, - }, - { - Name: "i64.ge_s", - Opcodes: opcodes{0x59}, - Arguments: arguments{}, - }, - { - Name: "i64.ge_u", - Opcodes: opcodes{0x5a}, - Arguments: arguments{}, - }, - - { - Name: "i32.clz", - Opcodes: opcodes{0x67}, - Arguments: arguments{}, - }, - { - Name: "i32.ctz", - Opcodes: opcodes{0x68}, - Arguments: arguments{}, - }, - { - Name: "i32.popcnt", - Opcodes: opcodes{0x69}, - Arguments: arguments{}, - }, - { - Name: "i32.add", - Opcodes: opcodes{0x6a}, - Arguments: arguments{}, - }, - { - Name: "i32.sub", - Opcodes: opcodes{0x6b}, - Arguments: arguments{}, - }, - { - Name: "i32.mul", - Opcodes: opcodes{0x6c}, - Arguments: arguments{}, - }, - { - Name: "i32.div_s", - Opcodes: opcodes{0x6d}, - Arguments: arguments{}, - }, - { - Name: "i32.div_u", - Opcodes: opcodes{0x6e}, - Arguments: arguments{}, - }, - { - Name: "i32.rem_s", - Opcodes: opcodes{0x6f}, - Arguments: arguments{}, - }, - { - Name: "i32.rem_u", - Opcodes: opcodes{0x70}, - Arguments: arguments{}, - }, - { - Name: "i32.and", - Opcodes: opcodes{0x71}, - Arguments: arguments{}, - }, - { - Name: "i32.or", - Opcodes: opcodes{0x72}, - Arguments: arguments{}, - }, - { - Name: "i32.xor", - Opcodes: opcodes{0x73}, - Arguments: arguments{}, - }, - { - Name: "i32.shl", - Opcodes: opcodes{0x74}, - Arguments: arguments{}, - }, - { - Name: "i32.shr_s", - Opcodes: opcodes{0x75}, - Arguments: arguments{}, - }, - { - Name: "i32.shr_u", - Opcodes: opcodes{0x76}, - Arguments: arguments{}, - }, - { - Name: "i32.rotl", - Opcodes: opcodes{0x77}, - Arguments: arguments{}, - }, - { - Name: "i32.rotr", - Opcodes: opcodes{0x78}, - Arguments: arguments{}, - }, - { - Name: "i64.clz", - Opcodes: opcodes{0x79}, - Arguments: arguments{}, - }, - { - Name: "i64.ctz", - Opcodes: opcodes{0x7a}, - Arguments: arguments{}, - }, - { - Name: "i64.popcnt", - Opcodes: opcodes{0x7b}, - Arguments: arguments{}, - }, - { - Name: "i64.add", - Opcodes: opcodes{0x7c}, - Arguments: arguments{}, - }, - { - Name: "i64.sub", - Opcodes: opcodes{0x7d}, - Arguments: arguments{}, - }, - { - Name: "i64.mul", - Opcodes: opcodes{0x7e}, - Arguments: arguments{}, - }, - { - Name: "i64.div_s", - Opcodes: opcodes{0x7f}, - Arguments: arguments{}, - }, - { - Name: "i64.div_u", - Opcodes: opcodes{0x80}, - Arguments: arguments{}, - }, - { - Name: "i64.rem_s", - Opcodes: opcodes{0x81}, - Arguments: arguments{}, - }, - { - Name: "i64.rem_u", - Opcodes: opcodes{0x82}, - Arguments: arguments{}, - }, - { - Name: "i64.and", - Opcodes: opcodes{0x83}, - Arguments: arguments{}, - }, - { - Name: "i64.or", - Opcodes: opcodes{0x84}, - Arguments: arguments{}, - }, - { - Name: "i64.xor", - Opcodes: opcodes{0x85}, - Arguments: arguments{}, - }, - { - Name: "i64.shl", - Opcodes: opcodes{0x86}, - Arguments: arguments{}, - }, - { - Name: "i64.shr_s", - Opcodes: opcodes{0x87}, - Arguments: arguments{}, - }, - { - Name: "i64.shr_u", - Opcodes: opcodes{0x88}, - Arguments: arguments{}, - }, - { - Name: "i64.rotl", - Opcodes: opcodes{0x89}, - Arguments: arguments{}, - }, - { - Name: "i64.rotr", - Opcodes: opcodes{0x8a}, - Arguments: arguments{}, - }, - - { - Name: "i32.wrap_i64", - Opcodes: opcodes{0xa7}, - Arguments: arguments{}, - }, - - { - Name: "i64.extend_i32_s", - Opcodes: opcodes{0xac}, - Arguments: arguments{}, - }, - { - Name: "i64.extend_i32_u", - Opcodes: opcodes{0xad}, - Arguments: arguments{}, - }, - }) -} diff --git a/compiler/wasm/import.go b/compiler/wasm/import.go deleted file mode 100644 index 095f7dc0d6..0000000000 --- a/compiler/wasm/import.go +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Cadence - The resource-oriented smart contract programming language - * - * Copyright Flow Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package wasm - -import "fmt" - -// Import represents an import -type Import struct { - Module string - Name string - // TODO: add support for tables, memories, and globals. adjust name section! - TypeIndex uint32 -} - -func (imp Import) FullName() string { - return fmt.Sprintf("%s.%s", imp.Module, imp.Name) -} - -// importIndicator is the byte used to indicate the kind of import in the WASM binary -type importIndicator byte - -const ( - // importIndicatorFunction is the byte used to indicate the import of a function in the WASM binary - importIndicatorFunction importIndicator = 0x0 -) diff --git a/compiler/wasm/instruction.go b/compiler/wasm/instruction.go deleted file mode 100644 index 57d44c7132..0000000000 --- a/compiler/wasm/instruction.go +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Cadence - The resource-oriented smart contract programming language - * - * Copyright Flow Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package wasm - -// Instruction represents an instruction in the code of a WASM binary -type Instruction interface { - isInstruction() - write(*WASMWriter) error -} diff --git a/compiler/wasm/instructions.go b/compiler/wasm/instructions.go deleted file mode 100644 index e4cad104dc..0000000000 --- a/compiler/wasm/instructions.go +++ /dev/null @@ -1,1981 +0,0 @@ -// Code generated by utils/version. DO NOT EDIT. -/* - * Cadence - The resource-oriented smart contract programming language - * - * Copyright Flow Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -//go:generate go run ./gen/main.go -//go:generate go fmt $GOFILE - -package wasm - -import ( - "io" -) - -// InstructionUnreachable is the 'unreachable' instruction -type InstructionUnreachable struct{} - -func (InstructionUnreachable) isInstruction() {} - -func (i InstructionUnreachable) write(w *WASMWriter) error { - err := w.writeOpcode(opcodeUnreachable) - if err != nil { - return err - } - - return nil -} - -// InstructionNop is the 'nop' instruction -type InstructionNop struct{} - -func (InstructionNop) isInstruction() {} - -func (i InstructionNop) write(w *WASMWriter) error { - err := w.writeOpcode(opcodeNop) - if err != nil { - return err - } - - return nil -} - -// InstructionBlock is the 'block' instruction -type InstructionBlock struct { - Block Block -} - -func (InstructionBlock) isInstruction() {} - -func (i InstructionBlock) write(w *WASMWriter) error { - err := w.writeOpcode(opcodeBlock) - if err != nil { - return err - } - - block := i.Block - err = w.writeBlockInstructionArgument(block, false) - if err != nil { - return err - } - - return nil -} - -// InstructionLoop is the 'loop' instruction -type InstructionLoop struct { - Block Block -} - -func (InstructionLoop) isInstruction() {} - -func (i InstructionLoop) write(w *WASMWriter) error { - err := w.writeOpcode(opcodeLoop) - if err != nil { - return err - } - - block := i.Block - err = w.writeBlockInstructionArgument(block, false) - if err != nil { - return err - } - - return nil -} - -// InstructionIf is the 'if' instruction -type InstructionIf struct { - Block Block -} - -func (InstructionIf) isInstruction() {} - -func (i InstructionIf) write(w *WASMWriter) error { - err := w.writeOpcode(opcodeIf) - if err != nil { - return err - } - - block := i.Block - err = w.writeBlockInstructionArgument(block, true) - if err != nil { - return err - } - - return nil -} - -// InstructionEnd is the 'end' instruction -type InstructionEnd struct{} - -func (InstructionEnd) isInstruction() {} - -func (i InstructionEnd) write(w *WASMWriter) error { - err := w.writeOpcode(opcodeEnd) - if err != nil { - return err - } - - return nil -} - -// InstructionBr is the 'br' instruction -type InstructionBr struct { - LabelIndex uint32 -} - -func (InstructionBr) isInstruction() {} - -func (i InstructionBr) write(w *WASMWriter) error { - err := w.writeOpcode(opcodeBr) - if err != nil { - return err - } - - labelIndex := i.LabelIndex - err = w.buf.writeUint32LEB128(labelIndex) - if err != nil { - return err - } - - return nil -} - -// InstructionBrIf is the 'br_if' instruction -type InstructionBrIf struct { - LabelIndex uint32 -} - -func (InstructionBrIf) isInstruction() {} - -func (i InstructionBrIf) write(w *WASMWriter) error { - err := w.writeOpcode(opcodeBrIf) - if err != nil { - return err - } - - labelIndex := i.LabelIndex - err = w.buf.writeUint32LEB128(labelIndex) - if err != nil { - return err - } - - return nil -} - -// InstructionBrTable is the 'br_table' instruction -type InstructionBrTable struct { - LabelIndices []uint32 - DefaultLabelIndex uint32 -} - -func (InstructionBrTable) isInstruction() {} - -func (i InstructionBrTable) write(w *WASMWriter) error { - err := w.writeOpcode(opcodeBrTable) - if err != nil { - return err - } - - labelIndices := i.LabelIndices - labelIndicesCount := len(labelIndices) - err = w.buf.writeUint32LEB128(uint32(labelIndicesCount)) - if err != nil { - return err - } - - for i := 0; i < labelIndicesCount; i++ { - labelIndicesElement := labelIndices[i] - err = w.buf.writeUint32LEB128(labelIndicesElement) - if err != nil { - return err - } - } - - defaultLabelIndex := i.DefaultLabelIndex - err = w.buf.writeUint32LEB128(defaultLabelIndex) - if err != nil { - return err - } - - return nil -} - -// InstructionReturn is the 'return' instruction -type InstructionReturn struct{} - -func (InstructionReturn) isInstruction() {} - -func (i InstructionReturn) write(w *WASMWriter) error { - err := w.writeOpcode(opcodeReturn) - if err != nil { - return err - } - - return nil -} - -// InstructionCall is the 'call' instruction -type InstructionCall struct { - FuncIndex uint32 -} - -func (InstructionCall) isInstruction() {} - -func (i InstructionCall) write(w *WASMWriter) error { - err := w.writeOpcode(opcodeCall) - if err != nil { - return err - } - - funcIndex := i.FuncIndex - err = w.buf.writeUint32LEB128(funcIndex) - if err != nil { - return err - } - - return nil -} - -// InstructionCallIndirect is the 'call_indirect' instruction -type InstructionCallIndirect struct { - TypeIndex uint32 - TableIndex uint32 -} - -func (InstructionCallIndirect) isInstruction() {} - -func (i InstructionCallIndirect) write(w *WASMWriter) error { - err := w.writeOpcode(opcodeCallIndirect) - if err != nil { - return err - } - - typeIndex := i.TypeIndex - err = w.buf.writeUint32LEB128(typeIndex) - if err != nil { - return err - } - - tableIndex := i.TableIndex - err = w.buf.writeUint32LEB128(tableIndex) - if err != nil { - return err - } - - return nil -} - -// InstructionRefNull is the 'ref.null' instruction -type InstructionRefNull struct { - TypeIndex uint32 -} - -func (InstructionRefNull) isInstruction() {} - -func (i InstructionRefNull) write(w *WASMWriter) error { - err := w.writeOpcode(opcodeRefNull) - if err != nil { - return err - } - - typeIndex := i.TypeIndex - err = w.buf.writeUint32LEB128(typeIndex) - if err != nil { - return err - } - - return nil -} - -// InstructionRefIsNull is the 'ref.is_null' instruction -type InstructionRefIsNull struct{} - -func (InstructionRefIsNull) isInstruction() {} - -func (i InstructionRefIsNull) write(w *WASMWriter) error { - err := w.writeOpcode(opcodeRefIsNull) - if err != nil { - return err - } - - return nil -} - -// InstructionRefFunc is the 'ref.func' instruction -type InstructionRefFunc struct { - FuncIndex uint32 -} - -func (InstructionRefFunc) isInstruction() {} - -func (i InstructionRefFunc) write(w *WASMWriter) error { - err := w.writeOpcode(opcodeRefFunc) - if err != nil { - return err - } - - funcIndex := i.FuncIndex - err = w.buf.writeUint32LEB128(funcIndex) - if err != nil { - return err - } - - return nil -} - -// InstructionDrop is the 'drop' instruction -type InstructionDrop struct{} - -func (InstructionDrop) isInstruction() {} - -func (i InstructionDrop) write(w *WASMWriter) error { - err := w.writeOpcode(opcodeDrop) - if err != nil { - return err - } - - return nil -} - -// InstructionSelect is the 'select' instruction -type InstructionSelect struct{} - -func (InstructionSelect) isInstruction() {} - -func (i InstructionSelect) write(w *WASMWriter) error { - err := w.writeOpcode(opcodeSelect) - if err != nil { - return err - } - - return nil -} - -// InstructionLocalGet is the 'local.get' instruction -type InstructionLocalGet struct { - LocalIndex uint32 -} - -func (InstructionLocalGet) isInstruction() {} - -func (i InstructionLocalGet) write(w *WASMWriter) error { - err := w.writeOpcode(opcodeLocalGet) - if err != nil { - return err - } - - localIndex := i.LocalIndex - err = w.buf.writeUint32LEB128(localIndex) - if err != nil { - return err - } - - return nil -} - -// InstructionLocalSet is the 'local.set' instruction -type InstructionLocalSet struct { - LocalIndex uint32 -} - -func (InstructionLocalSet) isInstruction() {} - -func (i InstructionLocalSet) write(w *WASMWriter) error { - err := w.writeOpcode(opcodeLocalSet) - if err != nil { - return err - } - - localIndex := i.LocalIndex - err = w.buf.writeUint32LEB128(localIndex) - if err != nil { - return err - } - - return nil -} - -// InstructionLocalTee is the 'local.tee' instruction -type InstructionLocalTee struct { - LocalIndex uint32 -} - -func (InstructionLocalTee) isInstruction() {} - -func (i InstructionLocalTee) write(w *WASMWriter) error { - err := w.writeOpcode(opcodeLocalTee) - if err != nil { - return err - } - - localIndex := i.LocalIndex - err = w.buf.writeUint32LEB128(localIndex) - if err != nil { - return err - } - - return nil -} - -// InstructionGlobalGet is the 'global.get' instruction -type InstructionGlobalGet struct { - GlobalIndex uint32 -} - -func (InstructionGlobalGet) isInstruction() {} - -func (i InstructionGlobalGet) write(w *WASMWriter) error { - err := w.writeOpcode(opcodeGlobalGet) - if err != nil { - return err - } - - globalIndex := i.GlobalIndex - err = w.buf.writeUint32LEB128(globalIndex) - if err != nil { - return err - } - - return nil -} - -// InstructionGlobalSet is the 'global.set' instruction -type InstructionGlobalSet struct { - GlobalIndex uint32 -} - -func (InstructionGlobalSet) isInstruction() {} - -func (i InstructionGlobalSet) write(w *WASMWriter) error { - err := w.writeOpcode(opcodeGlobalSet) - if err != nil { - return err - } - - globalIndex := i.GlobalIndex - err = w.buf.writeUint32LEB128(globalIndex) - if err != nil { - return err - } - - return nil -} - -// InstructionI32Const is the 'i32.const' instruction -type InstructionI32Const struct { - Value int32 -} - -func (InstructionI32Const) isInstruction() {} - -func (i InstructionI32Const) write(w *WASMWriter) error { - err := w.writeOpcode(opcodeI32Const) - if err != nil { - return err - } - - value := i.Value - err = w.buf.writeInt32LEB128(value) - if err != nil { - return err - } - - return nil -} - -// InstructionI64Const is the 'i64.const' instruction -type InstructionI64Const struct { - Value int64 -} - -func (InstructionI64Const) isInstruction() {} - -func (i InstructionI64Const) write(w *WASMWriter) error { - err := w.writeOpcode(opcodeI64Const) - if err != nil { - return err - } - - value := i.Value - err = w.buf.writeInt64LEB128(value) - if err != nil { - return err - } - - return nil -} - -// InstructionI32Eqz is the 'i32.eqz' instruction -type InstructionI32Eqz struct{} - -func (InstructionI32Eqz) isInstruction() {} - -func (i InstructionI32Eqz) write(w *WASMWriter) error { - err := w.writeOpcode(opcodeI32Eqz) - if err != nil { - return err - } - - return nil -} - -// InstructionI32Eq is the 'i32.eq' instruction -type InstructionI32Eq struct{} - -func (InstructionI32Eq) isInstruction() {} - -func (i InstructionI32Eq) write(w *WASMWriter) error { - err := w.writeOpcode(opcodeI32Eq) - if err != nil { - return err - } - - return nil -} - -// InstructionI32Ne is the 'i32.ne' instruction -type InstructionI32Ne struct{} - -func (InstructionI32Ne) isInstruction() {} - -func (i InstructionI32Ne) write(w *WASMWriter) error { - err := w.writeOpcode(opcodeI32Ne) - if err != nil { - return err - } - - return nil -} - -// InstructionI32LtS is the 'i32.lt_s' instruction -type InstructionI32LtS struct{} - -func (InstructionI32LtS) isInstruction() {} - -func (i InstructionI32LtS) write(w *WASMWriter) error { - err := w.writeOpcode(opcodeI32LtS) - if err != nil { - return err - } - - return nil -} - -// InstructionI32LtU is the 'i32.lt_u' instruction -type InstructionI32LtU struct{} - -func (InstructionI32LtU) isInstruction() {} - -func (i InstructionI32LtU) write(w *WASMWriter) error { - err := w.writeOpcode(opcodeI32LtU) - if err != nil { - return err - } - - return nil -} - -// InstructionI32GtS is the 'i32.gt_s' instruction -type InstructionI32GtS struct{} - -func (InstructionI32GtS) isInstruction() {} - -func (i InstructionI32GtS) write(w *WASMWriter) error { - err := w.writeOpcode(opcodeI32GtS) - if err != nil { - return err - } - - return nil -} - -// InstructionI32GtU is the 'i32.gt_u' instruction -type InstructionI32GtU struct{} - -func (InstructionI32GtU) isInstruction() {} - -func (i InstructionI32GtU) write(w *WASMWriter) error { - err := w.writeOpcode(opcodeI32GtU) - if err != nil { - return err - } - - return nil -} - -// InstructionI32LeS is the 'i32.le_s' instruction -type InstructionI32LeS struct{} - -func (InstructionI32LeS) isInstruction() {} - -func (i InstructionI32LeS) write(w *WASMWriter) error { - err := w.writeOpcode(opcodeI32LeS) - if err != nil { - return err - } - - return nil -} - -// InstructionI32LeU is the 'i32.le_u' instruction -type InstructionI32LeU struct{} - -func (InstructionI32LeU) isInstruction() {} - -func (i InstructionI32LeU) write(w *WASMWriter) error { - err := w.writeOpcode(opcodeI32LeU) - if err != nil { - return err - } - - return nil -} - -// InstructionI32GeS is the 'i32.ge_s' instruction -type InstructionI32GeS struct{} - -func (InstructionI32GeS) isInstruction() {} - -func (i InstructionI32GeS) write(w *WASMWriter) error { - err := w.writeOpcode(opcodeI32GeS) - if err != nil { - return err - } - - return nil -} - -// InstructionI32GeU is the 'i32.ge_u' instruction -type InstructionI32GeU struct{} - -func (InstructionI32GeU) isInstruction() {} - -func (i InstructionI32GeU) write(w *WASMWriter) error { - err := w.writeOpcode(opcodeI32GeU) - if err != nil { - return err - } - - return nil -} - -// InstructionI64Eqz is the 'i64.eqz' instruction -type InstructionI64Eqz struct{} - -func (InstructionI64Eqz) isInstruction() {} - -func (i InstructionI64Eqz) write(w *WASMWriter) error { - err := w.writeOpcode(opcodeI64Eqz) - if err != nil { - return err - } - - return nil -} - -// InstructionI64Eq is the 'i64.eq' instruction -type InstructionI64Eq struct{} - -func (InstructionI64Eq) isInstruction() {} - -func (i InstructionI64Eq) write(w *WASMWriter) error { - err := w.writeOpcode(opcodeI64Eq) - if err != nil { - return err - } - - return nil -} - -// InstructionI64Ne is the 'i64.ne' instruction -type InstructionI64Ne struct{} - -func (InstructionI64Ne) isInstruction() {} - -func (i InstructionI64Ne) write(w *WASMWriter) error { - err := w.writeOpcode(opcodeI64Ne) - if err != nil { - return err - } - - return nil -} - -// InstructionI64LtS is the 'i64.lt_s' instruction -type InstructionI64LtS struct{} - -func (InstructionI64LtS) isInstruction() {} - -func (i InstructionI64LtS) write(w *WASMWriter) error { - err := w.writeOpcode(opcodeI64LtS) - if err != nil { - return err - } - - return nil -} - -// InstructionI64LtU is the 'i64.lt_u' instruction -type InstructionI64LtU struct{} - -func (InstructionI64LtU) isInstruction() {} - -func (i InstructionI64LtU) write(w *WASMWriter) error { - err := w.writeOpcode(opcodeI64LtU) - if err != nil { - return err - } - - return nil -} - -// InstructionI64GtS is the 'i64.gt_s' instruction -type InstructionI64GtS struct{} - -func (InstructionI64GtS) isInstruction() {} - -func (i InstructionI64GtS) write(w *WASMWriter) error { - err := w.writeOpcode(opcodeI64GtS) - if err != nil { - return err - } - - return nil -} - -// InstructionI64GtU is the 'i64.gt_u' instruction -type InstructionI64GtU struct{} - -func (InstructionI64GtU) isInstruction() {} - -func (i InstructionI64GtU) write(w *WASMWriter) error { - err := w.writeOpcode(opcodeI64GtU) - if err != nil { - return err - } - - return nil -} - -// InstructionI64LeS is the 'i64.le_s' instruction -type InstructionI64LeS struct{} - -func (InstructionI64LeS) isInstruction() {} - -func (i InstructionI64LeS) write(w *WASMWriter) error { - err := w.writeOpcode(opcodeI64LeS) - if err != nil { - return err - } - - return nil -} - -// InstructionI64LeU is the 'i64.le_u' instruction -type InstructionI64LeU struct{} - -func (InstructionI64LeU) isInstruction() {} - -func (i InstructionI64LeU) write(w *WASMWriter) error { - err := w.writeOpcode(opcodeI64LeU) - if err != nil { - return err - } - - return nil -} - -// InstructionI64GeS is the 'i64.ge_s' instruction -type InstructionI64GeS struct{} - -func (InstructionI64GeS) isInstruction() {} - -func (i InstructionI64GeS) write(w *WASMWriter) error { - err := w.writeOpcode(opcodeI64GeS) - if err != nil { - return err - } - - return nil -} - -// InstructionI64GeU is the 'i64.ge_u' instruction -type InstructionI64GeU struct{} - -func (InstructionI64GeU) isInstruction() {} - -func (i InstructionI64GeU) write(w *WASMWriter) error { - err := w.writeOpcode(opcodeI64GeU) - if err != nil { - return err - } - - return nil -} - -// InstructionI32Clz is the 'i32.clz' instruction -type InstructionI32Clz struct{} - -func (InstructionI32Clz) isInstruction() {} - -func (i InstructionI32Clz) write(w *WASMWriter) error { - err := w.writeOpcode(opcodeI32Clz) - if err != nil { - return err - } - - return nil -} - -// InstructionI32Ctz is the 'i32.ctz' instruction -type InstructionI32Ctz struct{} - -func (InstructionI32Ctz) isInstruction() {} - -func (i InstructionI32Ctz) write(w *WASMWriter) error { - err := w.writeOpcode(opcodeI32Ctz) - if err != nil { - return err - } - - return nil -} - -// InstructionI32Popcnt is the 'i32.popcnt' instruction -type InstructionI32Popcnt struct{} - -func (InstructionI32Popcnt) isInstruction() {} - -func (i InstructionI32Popcnt) write(w *WASMWriter) error { - err := w.writeOpcode(opcodeI32Popcnt) - if err != nil { - return err - } - - return nil -} - -// InstructionI32Add is the 'i32.add' instruction -type InstructionI32Add struct{} - -func (InstructionI32Add) isInstruction() {} - -func (i InstructionI32Add) write(w *WASMWriter) error { - err := w.writeOpcode(opcodeI32Add) - if err != nil { - return err - } - - return nil -} - -// InstructionI32Sub is the 'i32.sub' instruction -type InstructionI32Sub struct{} - -func (InstructionI32Sub) isInstruction() {} - -func (i InstructionI32Sub) write(w *WASMWriter) error { - err := w.writeOpcode(opcodeI32Sub) - if err != nil { - return err - } - - return nil -} - -// InstructionI32Mul is the 'i32.mul' instruction -type InstructionI32Mul struct{} - -func (InstructionI32Mul) isInstruction() {} - -func (i InstructionI32Mul) write(w *WASMWriter) error { - err := w.writeOpcode(opcodeI32Mul) - if err != nil { - return err - } - - return nil -} - -// InstructionI32DivS is the 'i32.div_s' instruction -type InstructionI32DivS struct{} - -func (InstructionI32DivS) isInstruction() {} - -func (i InstructionI32DivS) write(w *WASMWriter) error { - err := w.writeOpcode(opcodeI32DivS) - if err != nil { - return err - } - - return nil -} - -// InstructionI32DivU is the 'i32.div_u' instruction -type InstructionI32DivU struct{} - -func (InstructionI32DivU) isInstruction() {} - -func (i InstructionI32DivU) write(w *WASMWriter) error { - err := w.writeOpcode(opcodeI32DivU) - if err != nil { - return err - } - - return nil -} - -// InstructionI32RemS is the 'i32.rem_s' instruction -type InstructionI32RemS struct{} - -func (InstructionI32RemS) isInstruction() {} - -func (i InstructionI32RemS) write(w *WASMWriter) error { - err := w.writeOpcode(opcodeI32RemS) - if err != nil { - return err - } - - return nil -} - -// InstructionI32RemU is the 'i32.rem_u' instruction -type InstructionI32RemU struct{} - -func (InstructionI32RemU) isInstruction() {} - -func (i InstructionI32RemU) write(w *WASMWriter) error { - err := w.writeOpcode(opcodeI32RemU) - if err != nil { - return err - } - - return nil -} - -// InstructionI32And is the 'i32.and' instruction -type InstructionI32And struct{} - -func (InstructionI32And) isInstruction() {} - -func (i InstructionI32And) write(w *WASMWriter) error { - err := w.writeOpcode(opcodeI32And) - if err != nil { - return err - } - - return nil -} - -// InstructionI32Or is the 'i32.or' instruction -type InstructionI32Or struct{} - -func (InstructionI32Or) isInstruction() {} - -func (i InstructionI32Or) write(w *WASMWriter) error { - err := w.writeOpcode(opcodeI32Or) - if err != nil { - return err - } - - return nil -} - -// InstructionI32Xor is the 'i32.xor' instruction -type InstructionI32Xor struct{} - -func (InstructionI32Xor) isInstruction() {} - -func (i InstructionI32Xor) write(w *WASMWriter) error { - err := w.writeOpcode(opcodeI32Xor) - if err != nil { - return err - } - - return nil -} - -// InstructionI32Shl is the 'i32.shl' instruction -type InstructionI32Shl struct{} - -func (InstructionI32Shl) isInstruction() {} - -func (i InstructionI32Shl) write(w *WASMWriter) error { - err := w.writeOpcode(opcodeI32Shl) - if err != nil { - return err - } - - return nil -} - -// InstructionI32ShrS is the 'i32.shr_s' instruction -type InstructionI32ShrS struct{} - -func (InstructionI32ShrS) isInstruction() {} - -func (i InstructionI32ShrS) write(w *WASMWriter) error { - err := w.writeOpcode(opcodeI32ShrS) - if err != nil { - return err - } - - return nil -} - -// InstructionI32ShrU is the 'i32.shr_u' instruction -type InstructionI32ShrU struct{} - -func (InstructionI32ShrU) isInstruction() {} - -func (i InstructionI32ShrU) write(w *WASMWriter) error { - err := w.writeOpcode(opcodeI32ShrU) - if err != nil { - return err - } - - return nil -} - -// InstructionI32Rotl is the 'i32.rotl' instruction -type InstructionI32Rotl struct{} - -func (InstructionI32Rotl) isInstruction() {} - -func (i InstructionI32Rotl) write(w *WASMWriter) error { - err := w.writeOpcode(opcodeI32Rotl) - if err != nil { - return err - } - - return nil -} - -// InstructionI32Rotr is the 'i32.rotr' instruction -type InstructionI32Rotr struct{} - -func (InstructionI32Rotr) isInstruction() {} - -func (i InstructionI32Rotr) write(w *WASMWriter) error { - err := w.writeOpcode(opcodeI32Rotr) - if err != nil { - return err - } - - return nil -} - -// InstructionI64Clz is the 'i64.clz' instruction -type InstructionI64Clz struct{} - -func (InstructionI64Clz) isInstruction() {} - -func (i InstructionI64Clz) write(w *WASMWriter) error { - err := w.writeOpcode(opcodeI64Clz) - if err != nil { - return err - } - - return nil -} - -// InstructionI64Ctz is the 'i64.ctz' instruction -type InstructionI64Ctz struct{} - -func (InstructionI64Ctz) isInstruction() {} - -func (i InstructionI64Ctz) write(w *WASMWriter) error { - err := w.writeOpcode(opcodeI64Ctz) - if err != nil { - return err - } - - return nil -} - -// InstructionI64Popcnt is the 'i64.popcnt' instruction -type InstructionI64Popcnt struct{} - -func (InstructionI64Popcnt) isInstruction() {} - -func (i InstructionI64Popcnt) write(w *WASMWriter) error { - err := w.writeOpcode(opcodeI64Popcnt) - if err != nil { - return err - } - - return nil -} - -// InstructionI64Add is the 'i64.add' instruction -type InstructionI64Add struct{} - -func (InstructionI64Add) isInstruction() {} - -func (i InstructionI64Add) write(w *WASMWriter) error { - err := w.writeOpcode(opcodeI64Add) - if err != nil { - return err - } - - return nil -} - -// InstructionI64Sub is the 'i64.sub' instruction -type InstructionI64Sub struct{} - -func (InstructionI64Sub) isInstruction() {} - -func (i InstructionI64Sub) write(w *WASMWriter) error { - err := w.writeOpcode(opcodeI64Sub) - if err != nil { - return err - } - - return nil -} - -// InstructionI64Mul is the 'i64.mul' instruction -type InstructionI64Mul struct{} - -func (InstructionI64Mul) isInstruction() {} - -func (i InstructionI64Mul) write(w *WASMWriter) error { - err := w.writeOpcode(opcodeI64Mul) - if err != nil { - return err - } - - return nil -} - -// InstructionI64DivS is the 'i64.div_s' instruction -type InstructionI64DivS struct{} - -func (InstructionI64DivS) isInstruction() {} - -func (i InstructionI64DivS) write(w *WASMWriter) error { - err := w.writeOpcode(opcodeI64DivS) - if err != nil { - return err - } - - return nil -} - -// InstructionI64DivU is the 'i64.div_u' instruction -type InstructionI64DivU struct{} - -func (InstructionI64DivU) isInstruction() {} - -func (i InstructionI64DivU) write(w *WASMWriter) error { - err := w.writeOpcode(opcodeI64DivU) - if err != nil { - return err - } - - return nil -} - -// InstructionI64RemS is the 'i64.rem_s' instruction -type InstructionI64RemS struct{} - -func (InstructionI64RemS) isInstruction() {} - -func (i InstructionI64RemS) write(w *WASMWriter) error { - err := w.writeOpcode(opcodeI64RemS) - if err != nil { - return err - } - - return nil -} - -// InstructionI64RemU is the 'i64.rem_u' instruction -type InstructionI64RemU struct{} - -func (InstructionI64RemU) isInstruction() {} - -func (i InstructionI64RemU) write(w *WASMWriter) error { - err := w.writeOpcode(opcodeI64RemU) - if err != nil { - return err - } - - return nil -} - -// InstructionI64And is the 'i64.and' instruction -type InstructionI64And struct{} - -func (InstructionI64And) isInstruction() {} - -func (i InstructionI64And) write(w *WASMWriter) error { - err := w.writeOpcode(opcodeI64And) - if err != nil { - return err - } - - return nil -} - -// InstructionI64Or is the 'i64.or' instruction -type InstructionI64Or struct{} - -func (InstructionI64Or) isInstruction() {} - -func (i InstructionI64Or) write(w *WASMWriter) error { - err := w.writeOpcode(opcodeI64Or) - if err != nil { - return err - } - - return nil -} - -// InstructionI64Xor is the 'i64.xor' instruction -type InstructionI64Xor struct{} - -func (InstructionI64Xor) isInstruction() {} - -func (i InstructionI64Xor) write(w *WASMWriter) error { - err := w.writeOpcode(opcodeI64Xor) - if err != nil { - return err - } - - return nil -} - -// InstructionI64Shl is the 'i64.shl' instruction -type InstructionI64Shl struct{} - -func (InstructionI64Shl) isInstruction() {} - -func (i InstructionI64Shl) write(w *WASMWriter) error { - err := w.writeOpcode(opcodeI64Shl) - if err != nil { - return err - } - - return nil -} - -// InstructionI64ShrS is the 'i64.shr_s' instruction -type InstructionI64ShrS struct{} - -func (InstructionI64ShrS) isInstruction() {} - -func (i InstructionI64ShrS) write(w *WASMWriter) error { - err := w.writeOpcode(opcodeI64ShrS) - if err != nil { - return err - } - - return nil -} - -// InstructionI64ShrU is the 'i64.shr_u' instruction -type InstructionI64ShrU struct{} - -func (InstructionI64ShrU) isInstruction() {} - -func (i InstructionI64ShrU) write(w *WASMWriter) error { - err := w.writeOpcode(opcodeI64ShrU) - if err != nil { - return err - } - - return nil -} - -// InstructionI64Rotl is the 'i64.rotl' instruction -type InstructionI64Rotl struct{} - -func (InstructionI64Rotl) isInstruction() {} - -func (i InstructionI64Rotl) write(w *WASMWriter) error { - err := w.writeOpcode(opcodeI64Rotl) - if err != nil { - return err - } - - return nil -} - -// InstructionI64Rotr is the 'i64.rotr' instruction -type InstructionI64Rotr struct{} - -func (InstructionI64Rotr) isInstruction() {} - -func (i InstructionI64Rotr) write(w *WASMWriter) error { - err := w.writeOpcode(opcodeI64Rotr) - if err != nil { - return err - } - - return nil -} - -// InstructionI32WrapI64 is the 'i32.wrap_i64' instruction -type InstructionI32WrapI64 struct{} - -func (InstructionI32WrapI64) isInstruction() {} - -func (i InstructionI32WrapI64) write(w *WASMWriter) error { - err := w.writeOpcode(opcodeI32WrapI64) - if err != nil { - return err - } - - return nil -} - -// InstructionI64ExtendI32S is the 'i64.extend_i32_s' instruction -type InstructionI64ExtendI32S struct{} - -func (InstructionI64ExtendI32S) isInstruction() {} - -func (i InstructionI64ExtendI32S) write(w *WASMWriter) error { - err := w.writeOpcode(opcodeI64ExtendI32S) - if err != nil { - return err - } - - return nil -} - -// InstructionI64ExtendI32U is the 'i64.extend_i32_u' instruction -type InstructionI64ExtendI32U struct{} - -func (InstructionI64ExtendI32U) isInstruction() {} - -func (i InstructionI64ExtendI32U) write(w *WASMWriter) error { - err := w.writeOpcode(opcodeI64ExtendI32U) - if err != nil { - return err - } - - return nil -} - -const ( - // opcodeUnreachable is the opcode for the 'unreachable' instruction - opcodeUnreachable opcode = 0x0 - // opcodeNop is the opcode for the 'nop' instruction - opcodeNop opcode = 0x1 - // opcodeBlock is the opcode for the 'block' instruction - opcodeBlock opcode = 0x2 - // opcodeLoop is the opcode for the 'loop' instruction - opcodeLoop opcode = 0x3 - // opcodeIf is the opcode for the 'if' instruction - opcodeIf opcode = 0x4 - // opcodeEnd is the opcode for the 'end' instruction - opcodeEnd opcode = 0xb - // opcodeBr is the opcode for the 'br' instruction - opcodeBr opcode = 0xc - // opcodeBrIf is the opcode for the 'br_if' instruction - opcodeBrIf opcode = 0xd - // opcodeBrTable is the opcode for the 'br_table' instruction - opcodeBrTable opcode = 0xe - // opcodeReturn is the opcode for the 'return' instruction - opcodeReturn opcode = 0xf - // opcodeCall is the opcode for the 'call' instruction - opcodeCall opcode = 0x10 - // opcodeCallIndirect is the opcode for the 'call_indirect' instruction - opcodeCallIndirect opcode = 0x11 - // opcodeRefNull is the opcode for the 'ref.null' instruction - opcodeRefNull opcode = 0xd0 - // opcodeRefIsNull is the opcode for the 'ref.is_null' instruction - opcodeRefIsNull opcode = 0xd1 - // opcodeRefFunc is the opcode for the 'ref.func' instruction - opcodeRefFunc opcode = 0xd2 - // opcodeDrop is the opcode for the 'drop' instruction - opcodeDrop opcode = 0x1a - // opcodeSelect is the opcode for the 'select' instruction - opcodeSelect opcode = 0x1b - // opcodeLocalGet is the opcode for the 'local.get' instruction - opcodeLocalGet opcode = 0x20 - // opcodeLocalSet is the opcode for the 'local.set' instruction - opcodeLocalSet opcode = 0x21 - // opcodeLocalTee is the opcode for the 'local.tee' instruction - opcodeLocalTee opcode = 0x22 - // opcodeGlobalGet is the opcode for the 'global.get' instruction - opcodeGlobalGet opcode = 0x23 - // opcodeGlobalSet is the opcode for the 'global.set' instruction - opcodeGlobalSet opcode = 0x24 - // opcodeI32Const is the opcode for the 'i32.const' instruction - opcodeI32Const opcode = 0x41 - // opcodeI64Const is the opcode for the 'i64.const' instruction - opcodeI64Const opcode = 0x42 - // opcodeI32Eqz is the opcode for the 'i32.eqz' instruction - opcodeI32Eqz opcode = 0x45 - // opcodeI32Eq is the opcode for the 'i32.eq' instruction - opcodeI32Eq opcode = 0x46 - // opcodeI32Ne is the opcode for the 'i32.ne' instruction - opcodeI32Ne opcode = 0x47 - // opcodeI32LtS is the opcode for the 'i32.lt_s' instruction - opcodeI32LtS opcode = 0x48 - // opcodeI32LtU is the opcode for the 'i32.lt_u' instruction - opcodeI32LtU opcode = 0x49 - // opcodeI32GtS is the opcode for the 'i32.gt_s' instruction - opcodeI32GtS opcode = 0x4a - // opcodeI32GtU is the opcode for the 'i32.gt_u' instruction - opcodeI32GtU opcode = 0x4b - // opcodeI32LeS is the opcode for the 'i32.le_s' instruction - opcodeI32LeS opcode = 0x4c - // opcodeI32LeU is the opcode for the 'i32.le_u' instruction - opcodeI32LeU opcode = 0x4d - // opcodeI32GeS is the opcode for the 'i32.ge_s' instruction - opcodeI32GeS opcode = 0x4e - // opcodeI32GeU is the opcode for the 'i32.ge_u' instruction - opcodeI32GeU opcode = 0x4f - // opcodeI64Eqz is the opcode for the 'i64.eqz' instruction - opcodeI64Eqz opcode = 0x50 - // opcodeI64Eq is the opcode for the 'i64.eq' instruction - opcodeI64Eq opcode = 0x51 - // opcodeI64Ne is the opcode for the 'i64.ne' instruction - opcodeI64Ne opcode = 0x52 - // opcodeI64LtS is the opcode for the 'i64.lt_s' instruction - opcodeI64LtS opcode = 0x53 - // opcodeI64LtU is the opcode for the 'i64.lt_u' instruction - opcodeI64LtU opcode = 0x54 - // opcodeI64GtS is the opcode for the 'i64.gt_s' instruction - opcodeI64GtS opcode = 0x55 - // opcodeI64GtU is the opcode for the 'i64.gt_u' instruction - opcodeI64GtU opcode = 0x56 - // opcodeI64LeS is the opcode for the 'i64.le_s' instruction - opcodeI64LeS opcode = 0x57 - // opcodeI64LeU is the opcode for the 'i64.le_u' instruction - opcodeI64LeU opcode = 0x58 - // opcodeI64GeS is the opcode for the 'i64.ge_s' instruction - opcodeI64GeS opcode = 0x59 - // opcodeI64GeU is the opcode for the 'i64.ge_u' instruction - opcodeI64GeU opcode = 0x5a - // opcodeI32Clz is the opcode for the 'i32.clz' instruction - opcodeI32Clz opcode = 0x67 - // opcodeI32Ctz is the opcode for the 'i32.ctz' instruction - opcodeI32Ctz opcode = 0x68 - // opcodeI32Popcnt is the opcode for the 'i32.popcnt' instruction - opcodeI32Popcnt opcode = 0x69 - // opcodeI32Add is the opcode for the 'i32.add' instruction - opcodeI32Add opcode = 0x6a - // opcodeI32Sub is the opcode for the 'i32.sub' instruction - opcodeI32Sub opcode = 0x6b - // opcodeI32Mul is the opcode for the 'i32.mul' instruction - opcodeI32Mul opcode = 0x6c - // opcodeI32DivS is the opcode for the 'i32.div_s' instruction - opcodeI32DivS opcode = 0x6d - // opcodeI32DivU is the opcode for the 'i32.div_u' instruction - opcodeI32DivU opcode = 0x6e - // opcodeI32RemS is the opcode for the 'i32.rem_s' instruction - opcodeI32RemS opcode = 0x6f - // opcodeI32RemU is the opcode for the 'i32.rem_u' instruction - opcodeI32RemU opcode = 0x70 - // opcodeI32And is the opcode for the 'i32.and' instruction - opcodeI32And opcode = 0x71 - // opcodeI32Or is the opcode for the 'i32.or' instruction - opcodeI32Or opcode = 0x72 - // opcodeI32Xor is the opcode for the 'i32.xor' instruction - opcodeI32Xor opcode = 0x73 - // opcodeI32Shl is the opcode for the 'i32.shl' instruction - opcodeI32Shl opcode = 0x74 - // opcodeI32ShrS is the opcode for the 'i32.shr_s' instruction - opcodeI32ShrS opcode = 0x75 - // opcodeI32ShrU is the opcode for the 'i32.shr_u' instruction - opcodeI32ShrU opcode = 0x76 - // opcodeI32Rotl is the opcode for the 'i32.rotl' instruction - opcodeI32Rotl opcode = 0x77 - // opcodeI32Rotr is the opcode for the 'i32.rotr' instruction - opcodeI32Rotr opcode = 0x78 - // opcodeI64Clz is the opcode for the 'i64.clz' instruction - opcodeI64Clz opcode = 0x79 - // opcodeI64Ctz is the opcode for the 'i64.ctz' instruction - opcodeI64Ctz opcode = 0x7a - // opcodeI64Popcnt is the opcode for the 'i64.popcnt' instruction - opcodeI64Popcnt opcode = 0x7b - // opcodeI64Add is the opcode for the 'i64.add' instruction - opcodeI64Add opcode = 0x7c - // opcodeI64Sub is the opcode for the 'i64.sub' instruction - opcodeI64Sub opcode = 0x7d - // opcodeI64Mul is the opcode for the 'i64.mul' instruction - opcodeI64Mul opcode = 0x7e - // opcodeI64DivS is the opcode for the 'i64.div_s' instruction - opcodeI64DivS opcode = 0x7f - // opcodeI64DivU is the opcode for the 'i64.div_u' instruction - opcodeI64DivU opcode = 0x80 - // opcodeI64RemS is the opcode for the 'i64.rem_s' instruction - opcodeI64RemS opcode = 0x81 - // opcodeI64RemU is the opcode for the 'i64.rem_u' instruction - opcodeI64RemU opcode = 0x82 - // opcodeI64And is the opcode for the 'i64.and' instruction - opcodeI64And opcode = 0x83 - // opcodeI64Or is the opcode for the 'i64.or' instruction - opcodeI64Or opcode = 0x84 - // opcodeI64Xor is the opcode for the 'i64.xor' instruction - opcodeI64Xor opcode = 0x85 - // opcodeI64Shl is the opcode for the 'i64.shl' instruction - opcodeI64Shl opcode = 0x86 - // opcodeI64ShrS is the opcode for the 'i64.shr_s' instruction - opcodeI64ShrS opcode = 0x87 - // opcodeI64ShrU is the opcode for the 'i64.shr_u' instruction - opcodeI64ShrU opcode = 0x88 - // opcodeI64Rotl is the opcode for the 'i64.rotl' instruction - opcodeI64Rotl opcode = 0x89 - // opcodeI64Rotr is the opcode for the 'i64.rotr' instruction - opcodeI64Rotr opcode = 0x8a - // opcodeI32WrapI64 is the opcode for the 'i32.wrap_i64' instruction - opcodeI32WrapI64 opcode = 0xa7 - // opcodeI64ExtendI32S is the opcode for the 'i64.extend_i32_s' instruction - opcodeI64ExtendI32S opcode = 0xac - // opcodeI64ExtendI32U is the opcode for the 'i64.extend_i32_u' instruction - opcodeI64ExtendI32U opcode = 0xad -) - -// readInstruction reads an instruction in the WASM binary -func (r *WASMReader) readInstruction() (Instruction, error) { - opcodeOffset := r.buf.offset - b, err := r.buf.ReadByte() - - c := opcode(b) - - if err != nil { - if err == io.EOF { - return nil, MissingEndInstructionError{ - Offset: int(opcodeOffset), - } - } else { - return nil, InvalidOpcodeError{ - Offset: int(opcodeOffset), - Opcode: c, - ReadError: err, - } - } - } - - switch c { - case opcodeBlock: - block, err := r.readBlockInstructionArgument(false) - if err != nil { - return nil, err - } - - return InstructionBlock{ - Block: block, - }, nil - - case opcodeBr: - labelIndex, err := r.readUint32LEB128InstructionArgument() - if err != nil { - return nil, err - } - - return InstructionBr{ - LabelIndex: labelIndex, - }, nil - - case opcodeBrIf: - labelIndex, err := r.readUint32LEB128InstructionArgument() - if err != nil { - return nil, err - } - - return InstructionBrIf{ - LabelIndex: labelIndex, - }, nil - - case opcodeBrTable: - labelIndicesCountOffset := r.buf.offset - labelIndicesCount, err := r.buf.readUint32LEB128() - if err != nil { - return nil, InvalidInstructionVectorArgumentCountError{ - Offset: int(labelIndicesCountOffset), - ReadError: err, - } - } - - labelIndices := make([]uint32, labelIndicesCount) - - for i := uint32(0); i < labelIndicesCount; i++ { - labelIndicesElement, err := r.readUint32LEB128InstructionArgument() - if err != nil { - return nil, err - } - labelIndices[i] = labelIndicesElement - } - - defaultLabelIndex, err := r.readUint32LEB128InstructionArgument() - if err != nil { - return nil, err - } - - return InstructionBrTable{ - LabelIndices: labelIndices, - DefaultLabelIndex: defaultLabelIndex, - }, nil - - case opcodeCall: - funcIndex, err := r.readUint32LEB128InstructionArgument() - if err != nil { - return nil, err - } - - return InstructionCall{ - FuncIndex: funcIndex, - }, nil - - case opcodeCallIndirect: - typeIndex, err := r.readUint32LEB128InstructionArgument() - if err != nil { - return nil, err - } - - tableIndex, err := r.readUint32LEB128InstructionArgument() - if err != nil { - return nil, err - } - - return InstructionCallIndirect{ - TypeIndex: typeIndex, - TableIndex: tableIndex, - }, nil - - case opcodeDrop: - return InstructionDrop{}, nil - - case opcodeEnd: - return InstructionEnd{}, nil - - case opcodeGlobalGet: - globalIndex, err := r.readUint32LEB128InstructionArgument() - if err != nil { - return nil, err - } - - return InstructionGlobalGet{ - GlobalIndex: globalIndex, - }, nil - - case opcodeGlobalSet: - globalIndex, err := r.readUint32LEB128InstructionArgument() - if err != nil { - return nil, err - } - - return InstructionGlobalSet{ - GlobalIndex: globalIndex, - }, nil - - case opcodeI32Add: - return InstructionI32Add{}, nil - - case opcodeI32And: - return InstructionI32And{}, nil - - case opcodeI32Clz: - return InstructionI32Clz{}, nil - - case opcodeI32Const: - value, err := r.readInt32LEB128InstructionArgument() - if err != nil { - return nil, err - } - - return InstructionI32Const{ - Value: value, - }, nil - - case opcodeI32Ctz: - return InstructionI32Ctz{}, nil - - case opcodeI32DivS: - return InstructionI32DivS{}, nil - - case opcodeI32DivU: - return InstructionI32DivU{}, nil - - case opcodeI32Eq: - return InstructionI32Eq{}, nil - - case opcodeI32Eqz: - return InstructionI32Eqz{}, nil - - case opcodeI32GeS: - return InstructionI32GeS{}, nil - - case opcodeI32GeU: - return InstructionI32GeU{}, nil - - case opcodeI32GtS: - return InstructionI32GtS{}, nil - - case opcodeI32GtU: - return InstructionI32GtU{}, nil - - case opcodeI32LeS: - return InstructionI32LeS{}, nil - - case opcodeI32LeU: - return InstructionI32LeU{}, nil - - case opcodeI32LtS: - return InstructionI32LtS{}, nil - - case opcodeI32LtU: - return InstructionI32LtU{}, nil - - case opcodeI32Mul: - return InstructionI32Mul{}, nil - - case opcodeI32Ne: - return InstructionI32Ne{}, nil - - case opcodeI32Or: - return InstructionI32Or{}, nil - - case opcodeI32Popcnt: - return InstructionI32Popcnt{}, nil - - case opcodeI32RemS: - return InstructionI32RemS{}, nil - - case opcodeI32RemU: - return InstructionI32RemU{}, nil - - case opcodeI32Rotl: - return InstructionI32Rotl{}, nil - - case opcodeI32Rotr: - return InstructionI32Rotr{}, nil - - case opcodeI32Shl: - return InstructionI32Shl{}, nil - - case opcodeI32ShrS: - return InstructionI32ShrS{}, nil - - case opcodeI32ShrU: - return InstructionI32ShrU{}, nil - - case opcodeI32Sub: - return InstructionI32Sub{}, nil - - case opcodeI32WrapI64: - return InstructionI32WrapI64{}, nil - - case opcodeI32Xor: - return InstructionI32Xor{}, nil - - case opcodeI64Add: - return InstructionI64Add{}, nil - - case opcodeI64And: - return InstructionI64And{}, nil - - case opcodeI64Clz: - return InstructionI64Clz{}, nil - - case opcodeI64Const: - value, err := r.readInt64LEB128InstructionArgument() - if err != nil { - return nil, err - } - - return InstructionI64Const{ - Value: value, - }, nil - - case opcodeI64Ctz: - return InstructionI64Ctz{}, nil - - case opcodeI64DivS: - return InstructionI64DivS{}, nil - - case opcodeI64DivU: - return InstructionI64DivU{}, nil - - case opcodeI64Eq: - return InstructionI64Eq{}, nil - - case opcodeI64Eqz: - return InstructionI64Eqz{}, nil - - case opcodeI64ExtendI32S: - return InstructionI64ExtendI32S{}, nil - - case opcodeI64ExtendI32U: - return InstructionI64ExtendI32U{}, nil - - case opcodeI64GeS: - return InstructionI64GeS{}, nil - - case opcodeI64GeU: - return InstructionI64GeU{}, nil - - case opcodeI64GtS: - return InstructionI64GtS{}, nil - - case opcodeI64GtU: - return InstructionI64GtU{}, nil - - case opcodeI64LeS: - return InstructionI64LeS{}, nil - - case opcodeI64LeU: - return InstructionI64LeU{}, nil - - case opcodeI64LtS: - return InstructionI64LtS{}, nil - - case opcodeI64LtU: - return InstructionI64LtU{}, nil - - case opcodeI64Mul: - return InstructionI64Mul{}, nil - - case opcodeI64Ne: - return InstructionI64Ne{}, nil - - case opcodeI64Or: - return InstructionI64Or{}, nil - - case opcodeI64Popcnt: - return InstructionI64Popcnt{}, nil - - case opcodeI64RemS: - return InstructionI64RemS{}, nil - - case opcodeI64RemU: - return InstructionI64RemU{}, nil - - case opcodeI64Rotl: - return InstructionI64Rotl{}, nil - - case opcodeI64Rotr: - return InstructionI64Rotr{}, nil - - case opcodeI64Shl: - return InstructionI64Shl{}, nil - - case opcodeI64ShrS: - return InstructionI64ShrS{}, nil - - case opcodeI64ShrU: - return InstructionI64ShrU{}, nil - - case opcodeI64Sub: - return InstructionI64Sub{}, nil - - case opcodeI64Xor: - return InstructionI64Xor{}, nil - - case opcodeIf: - block, err := r.readBlockInstructionArgument(true) - if err != nil { - return nil, err - } - - return InstructionIf{ - Block: block, - }, nil - - case opcodeLocalGet: - localIndex, err := r.readUint32LEB128InstructionArgument() - if err != nil { - return nil, err - } - - return InstructionLocalGet{ - LocalIndex: localIndex, - }, nil - - case opcodeLocalSet: - localIndex, err := r.readUint32LEB128InstructionArgument() - if err != nil { - return nil, err - } - - return InstructionLocalSet{ - LocalIndex: localIndex, - }, nil - - case opcodeLocalTee: - localIndex, err := r.readUint32LEB128InstructionArgument() - if err != nil { - return nil, err - } - - return InstructionLocalTee{ - LocalIndex: localIndex, - }, nil - - case opcodeLoop: - block, err := r.readBlockInstructionArgument(false) - if err != nil { - return nil, err - } - - return InstructionLoop{ - Block: block, - }, nil - - case opcodeNop: - return InstructionNop{}, nil - - case opcodeRefFunc: - funcIndex, err := r.readUint32LEB128InstructionArgument() - if err != nil { - return nil, err - } - - return InstructionRefFunc{ - FuncIndex: funcIndex, - }, nil - - case opcodeRefIsNull: - return InstructionRefIsNull{}, nil - - case opcodeRefNull: - typeIndex, err := r.readUint32LEB128InstructionArgument() - if err != nil { - return nil, err - } - - return InstructionRefNull{ - TypeIndex: typeIndex, - }, nil - - case opcodeReturn: - return InstructionReturn{}, nil - - case opcodeSelect: - return InstructionSelect{}, nil - - case opcodeUnreachable: - return InstructionUnreachable{}, nil - - default: - return nil, InvalidOpcodeError{ - Offset: int(opcodeOffset), - Opcode: c, - ReadError: err, - } - } - -} diff --git a/compiler/wasm/leb128.go b/compiler/wasm/leb128.go deleted file mode 100644 index cbba8d7f74..0000000000 --- a/compiler/wasm/leb128.go +++ /dev/null @@ -1,279 +0,0 @@ -/* - * Cadence - The resource-oriented smart contract programming language - * - * Copyright Flow Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package wasm - -import ( - "fmt" -) - -// max32bitLEB128ByteCount is the maximum number of bytes a 32-bit integer -// (signed or unsigned) may be encoded as. From -// https://webassembly.github.io/spec/core/binary/values.html#binary-int: -// -// "the total number of bytes encoding a value of type uN must not exceed ceil(N/7) bytes" -// "the total number of bytes encoding a value of type sN must not exceed ceil(N/7) bytes" -const max32bitLEB128ByteCount = 5 - -// max64bitLEB128ByteCount is the maximum number of bytes a 64-bit integer -// (signed or unsigned) may be encoded as. From -// https://webassembly.github.io/spec/core/binary/values.html#binary-int: -// -// "the total number of bytes encoding a value of type uN must not exceed ceil(N/7) bytes" -// "the total number of bytes encoding a value of type sN must not exceed ceil(N/7) bytes" -const max64bitLEB128ByteCount = 10 - -// writeUint32LEB128 encodes and writes the given unsigned 32-bit integer -// in canonical (with the fewest bytes possible) unsigned little endian base 128 format -func (buf *Buffer) writeUint32LEB128(v uint32) error { - if v < 128 { - err := buf.WriteByte(uint8(v)) - if err != nil { - return err - } - return nil - } - more := true - for more { - // low order 7 bits of value - c := uint8(v & 0x7f) - v >>= 7 - // more bits to come? - more = v != 0 - if more { - // set high order bit of byte - c |= 0x80 - } - // emit byte - err := buf.WriteByte(c) - if err != nil { - return err - } - } - return nil -} - -// writeUint64LEB128 encodes and writes the given unsigned 64-bit integer -// in canonical (with the fewest bytes possible) unsigned little endian base 128 format -func (buf *Buffer) writeUint64LEB128(v uint64) error { - if v < 128 { - err := buf.WriteByte(uint8(v)) - if err != nil { - return err - } - return nil - } - more := true - for more { - // low order 7 bits of value - c := uint8(v & 0x7f) - v >>= 7 - // more bits to come? - more = v != 0 - if more { - // set high order bit of byte - c |= 0x80 - } - // emit byte - err := buf.WriteByte(c) - if err != nil { - return err - } - } - return nil -} - -// writeUint32LEB128FixedLength encodes and writes the given unsigned 32-bit integer -// in non-canonical (fixed-size, instead of with the fewest bytes possible) -// unsigned little endian base 128 format -func (buf *Buffer) writeUint32LEB128FixedLength(v uint32, length int) error { - for i := 0; i < length; i++ { - c := uint8(v & 0x7f) - v >>= 7 - if i < length-1 { - c |= 0x80 - } - err := buf.WriteByte(c) - if err != nil { - return err - } - } - if v != 0 { - return fmt.Errorf("writeUint32LEB128FixedLength: length too small: %d", length) - } - return nil -} - -// readUint32LEB128 reads and decodes an unsigned 32-bit integer -func (buf *Buffer) readUint32LEB128() (uint32, error) { - var result uint32 - var shift, i uint - // only read up to maximum number of bytes - for i < max32bitLEB128ByteCount { - b, err := buf.ReadByte() - if err != nil { - return 0, err - } - result |= (uint32(b & 0x7F)) << shift - // check high order bit of byte - if b&0x80 == 0 { - break - } - shift += 7 - i++ - } - return result, nil -} - -// readUint64LEB128 reads and decodes an unsigned 64-bit integer -func (buf *Buffer) readUint64LEB128() (uint64, error) { - var result uint64 - var shift, i uint - // only read up to maximum number of bytes - for i < max64bitLEB128ByteCount { - b, err := buf.ReadByte() - if err != nil { - return 0, err - } - result |= (uint64(b & 0x7F)) << shift - // check high order bit of byte - if b&0x80 == 0 { - break - } - shift += 7 - i++ - } - return result, nil -} - -// writeInt32LEB128 encodes and writes the given signed 32-bit integer -// in canonical (with the fewest bytes possible) signed little endian base 128 format -func (buf *Buffer) writeInt32LEB128(v int32) error { - more := true - for more { - // low order 7 bits of value - c := uint8(v & 0x7f) - sign := uint8(v & 0x40) - v >>= 7 - more = !((v == 0 && sign == 0) || (v == -1 && sign != 0)) - if more { - c |= 0x80 - } - err := buf.WriteByte(c) - if err != nil { - return err - } - } - return nil -} - -// writeInt64LEB128 encodes and writes the given signed 64-bit integer -// in canonical (with the fewest bytes possible) signed little endian base 128 format -func (buf *Buffer) writeInt64LEB128(v int64) error { - more := true - for more { - // low order 7 bits of value - c := uint8(v & 0x7f) - sign := uint8(v & 0x40) - v >>= 7 - more = !((v == 0 && sign == 0) || (v == -1 && sign != 0)) - if more { - c |= 0x80 - } - err := buf.WriteByte(c) - if err != nil { - return err - } - } - return nil -} - -// readInt32LEB128 reads and decodes a signed 32-bit integer -func (buf *Buffer) readInt32LEB128() (int32, error) { - var result int32 - var i uint - var b byte = 0x80 - var signBits int32 = -1 - var err error - for (b&0x80 == 0x80) && i < max32bitLEB128ByteCount { - b, err = buf.ReadByte() - if err != nil { - return 0, err - } - result += int32(b&0x7f) << (i * 7) - signBits <<= 7 - i++ - } - if ((signBits >> 1) & result) != 0 { - result += signBits - } - return result, nil -} - -// readInt64LEB128 reads and decodes a signed 64-bit integer -func (buf *Buffer) readInt64LEB128() (int64, error) { - var result int64 - var i uint - var b byte = 0x80 - var signBits int64 = -1 - var err error - for (b&0x80 == 0x80) && i < max64bitLEB128ByteCount { - b, err = buf.ReadByte() - if err != nil { - return 0, err - } - result += int64(b&0x7f) << (i * 7) - signBits <<= 7 - i++ - } - if ((signBits >> 1) & result) != 0 { - result += signBits - } - return result, nil -} - -// writeFixedUint32LEB128Space writes a non-canonical 5-byte fixed-size space -// (instead of the minimal size if canonical encoding would be used) -func (buf *Buffer) writeFixedUint32LEB128Space() (offset, error) { - off := buf.offset - for i := 0; i < max32bitLEB128ByteCount; i++ { - err := buf.WriteByte(0) - if err != nil { - return 0, err - } - } - return off, nil -} - -// writeUint32LEB128SizeAt writes the size, the number of bytes -// between the given offset and the current offset, -// as an uint32 in non-canonical 5-byte fixed-size format -// (instead of the minimal size if canonical encoding would be used) -// at the given offset -func (buf *Buffer) writeUint32LEB128SizeAt(off offset) error { - currentOff := buf.offset - if currentOff < max32bitLEB128ByteCount || currentOff-max32bitLEB128ByteCount < off { - return fmt.Errorf("writeUint32LEB128SizeAt: invalid offset: %d", off) - } - size := uint32(currentOff - off - max32bitLEB128ByteCount) - buf.offset = off - defer func() { - buf.offset = currentOff - }() - return buf.writeUint32LEB128FixedLength(size, max32bitLEB128ByteCount) -} diff --git a/compiler/wasm/leb128_test.go b/compiler/wasm/leb128_test.go deleted file mode 100644 index 6b21ae86a1..0000000000 --- a/compiler/wasm/leb128_test.go +++ /dev/null @@ -1,347 +0,0 @@ -/* - * Cadence - The resource-oriented smart contract programming language - * - * Copyright Flow Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package wasm - -import ( - "math" - "testing" - - "github.com/stretchr/testify/require" -) - -func TestBuf_Uint32LEB128(t *testing.T) { - - t.Parallel() - - t.Run("DWARF spec + more", func(t *testing.T) { - - t.Parallel() - - // DWARF Debugging Information Format, Version 3, page 140 - - for v, expected := range map[uint32][]byte{ - 0: {0x00}, - 1: {0x01}, - 2: {2}, - 63: {0x3f}, - 64: {0x40}, - 127: {127}, - 128: {0 + 0x80, 1}, - 129: {1 + 0x80, 1}, - 130: {2 + 0x80, 1}, - 0x90: {0x90, 0x01}, - 0x100: {0x80, 0x02}, - 0x101: {0x81, 0x02}, - 0xff: {0xff, 0x01}, - 12857: {57 + 0x80, 100}, - } { - var b Buffer - err := b.writeUint32LEB128(v) - require.NoError(t, err) - require.Equal(t, expected, b.data) - - b.offset = 0 - - actual, err := b.readUint32LEB128() - require.NoError(t, err) - require.Equal(t, v, actual) - } - }) - - t.Run("write: max byte count", func(t *testing.T) { - - t.Parallel() - - // This test ensures that only up to the maximum number of bytes are written - // when writing a LEB128-encoded 32-bit number (see max32bitLEB128ByteCount), - // i.e. test that only up to 5 bytes are written. - - var b Buffer - err := b.writeUint32LEB128(math.MaxUint32) - require.NoError(t, err) - require.GreaterOrEqual(t, max32bitLEB128ByteCount, len(b.data)) - }) - - t.Run("read: max byte count", func(t *testing.T) { - - t.Parallel() - - // This test ensures that only up to the maximum number of bytes are read - // when reading a LEB128-encoded 32-bit number (see max32bitLEB128ByteCount), - // i.e. test that only 5 of the 8 given bytes are read, - // to ensure the LEB128 parser doesn't keep reading infinitely. - - b := Buffer{data: []byte{0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88}} - _, err := b.readUint32LEB128() - require.NoError(t, err) - require.Equal(t, offset(max32bitLEB128ByteCount), b.offset) - }) -} - -func TestBuf_Uint64LEB128(t *testing.T) { - - t.Parallel() - - t.Run("DWARF spec + more", func(t *testing.T) { - - t.Parallel() - - // DWARF Debugging Information Format, Version 3, page 140 - - for v, expected := range map[uint64][]byte{ - 0: {0x00}, - 1: {0x01}, - 2: {2}, - 63: {0x3f}, - 64: {0x40}, - 127: {127}, - 128: {0 + 0x80, 1}, - 129: {1 + 0x80, 1}, - 130: {2 + 0x80, 1}, - 0x90: {0x90, 0x01}, - 0x100: {0x80, 0x02}, - 0x101: {0x81, 0x02}, - 0xff: {0xff, 0x01}, - 12857: {57 + 0x80, 100}, - } { - var b Buffer - err := b.writeUint64LEB128(v) - require.NoError(t, err) - require.Equal(t, expected, b.data) - - b.offset = 0 - - actual, err := b.readUint64LEB128() - require.NoError(t, err) - require.Equal(t, v, actual) - } - }) - - t.Run("write: max byte count", func(t *testing.T) { - - t.Parallel() - - var b Buffer - err := b.writeUint64LEB128(math.MaxUint64) - require.NoError(t, err) - require.GreaterOrEqual(t, max64bitLEB128ByteCount, len(b.data)) - }) - - t.Run("read: max byte count", func(t *testing.T) { - - t.Parallel() - - b := Buffer{data: []byte{ - 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, - 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, - }} - _, err := b.readUint64LEB128() - require.NoError(t, err) - require.Equal(t, offset(max64bitLEB128ByteCount), b.offset) - }) -} - -func TestBuf_Int32LEB128(t *testing.T) { - - t.Parallel() - - t.Run("DWARF spec + more", func(t *testing.T) { - - t.Parallel() - - // DWARF Debugging Information Format, Version 3, page 141 - - for v, expected := range map[int32][]byte{ - 0: {0x00}, - 1: {0x01}, - -1: {0x7f}, - 2: {2}, - -2: {0x7e}, - 63: {0x3f}, - -63: {0x41}, - 64: {0xc0, 0x00}, - -64: {0x40}, - -65: {0xbf, 0x7f}, - 127: {127 + 0x80, 0}, - -127: {1 + 0x80, 0x7f}, - 128: {0 + 0x80, 1}, - -128: {0 + 0x80, 0x7f}, - 129: {1 + 0x80, 1}, - -129: {0x7f + 0x80, 0x7e}, - -12345: {0xc7, 0x9f, 0x7f}, - } { - var b Buffer - err := b.writeInt32LEB128(v) - require.NoError(t, err) - require.Equal(t, expected, b.data) - - b.offset = 0 - - actual, err := b.readInt32LEB128() - require.NoError(t, err) - require.Equal(t, v, actual) - } - }) - - t.Run("write: max byte count", func(t *testing.T) { - - t.Parallel() - - // This test ensures that only up to the maximum number of bytes are written - // when writing a LEB128-encoded 32-bit number (see max32bitLEB128ByteCount), - // i.e. test that only up to 5 bytes are written. - - var b Buffer - err := b.writeInt32LEB128(math.MaxInt32) - require.NoError(t, err) - require.GreaterOrEqual(t, max32bitLEB128ByteCount, len(b.data)) - - var b2 Buffer - err = b2.writeInt32LEB128(math.MinInt32) - require.NoError(t, err) - require.GreaterOrEqual(t, max32bitLEB128ByteCount, len(b.data)) - }) - - t.Run("read: max byte count", func(t *testing.T) { - - t.Parallel() - - // This test ensures that only up to the maximum number of bytes are read - // when reading a LEB128-encoded 32-bit number (see max32bitLEB128ByteCount), - // i.e. test that only 5 of the 8 given bytes are read, - // to ensure the LEB128 parser doesn't keep reading infinitely. - - b := Buffer{data: []byte{0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88}} - _, err := b.readInt32LEB128() - require.NoError(t, err) - require.Equal(t, offset(max32bitLEB128ByteCount), b.offset) - }) -} - -func TestBuf_Int64LEB128(t *testing.T) { - - t.Parallel() - - t.Run("DWARF spec + more", func(t *testing.T) { - - t.Parallel() - - // DWARF Debugging Information Format, Version 3, page 141 - - for v, expected := range map[int64][]byte{ - 0: {0x00}, - 1: {0x01}, - -1: {0x7f}, - 2: {2}, - -2: {0x7e}, - 63: {0x3f}, - -63: {0x41}, - 64: {0xc0, 0x00}, - -64: {0x40}, - -65: {0xbf, 0x7f}, - 127: {127 + 0x80, 0}, - -127: {1 + 0x80, 0x7f}, - 128: {0 + 0x80, 1}, - -128: {0 + 0x80, 0x7f}, - 129: {1 + 0x80, 1}, - -129: {0x7f + 0x80, 0x7e}, - -12345: {0xc7, 0x9f, 0x7f}, - } { - var b Buffer - err := b.writeInt64LEB128(v) - require.NoError(t, err) - require.Equal(t, expected, b.data) - - b.offset = 0 - - actual, err := b.readInt64LEB128() - require.NoError(t, err) - require.Equal(t, v, actual) - } - }) - - t.Run("write: max byte count", func(t *testing.T) { - - t.Parallel() - - var b Buffer - err := b.writeInt64LEB128(math.MaxInt64) - require.NoError(t, err) - require.GreaterOrEqual(t, max64bitLEB128ByteCount, len(b.data)) - - var b2 Buffer - err = b2.writeInt64LEB128(math.MinInt64) - require.NoError(t, err) - require.GreaterOrEqual(t, max64bitLEB128ByteCount, len(b.data)) - }) - - t.Run("read: max byte count", func(t *testing.T) { - - t.Parallel() - - b := Buffer{data: []byte{ - 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, - 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, - }} - _, err := b.readInt64LEB128() - require.NoError(t, err) - require.Equal(t, offset(max64bitLEB128ByteCount), b.offset) - }) -} - -func TestBuf_WriteSpaceAndSize(t *testing.T) { - - t.Parallel() - - var b Buffer - - err := b.WriteByte(101) - require.NoError(t, err) - err = b.WriteByte(102) - require.NoError(t, err) - - off, err := b.writeFixedUint32LEB128Space() - require.NoError(t, err) - require.Equal(t, offset(2), off) - require.Equal(t, - []byte{ - 101, 102, - 0, 0, 0, 0, 0, - }, - b.data, - ) - - err = b.WriteByte(104) - require.NoError(t, err) - err = b.WriteByte(105) - require.NoError(t, err) - err = b.WriteByte(106) - require.NoError(t, err) - - err = b.writeUint32LEB128SizeAt(off) - require.NoError(t, err) - require.Equal(t, - []byte{ - 101, 102, - 0x83, 0x80, 0x80, 0x80, 0, - 104, 105, 106, - }, - b.data, - ) -} diff --git a/compiler/wasm/magic.go b/compiler/wasm/magic.go deleted file mode 100644 index 2fa2b82aba..0000000000 --- a/compiler/wasm/magic.go +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Cadence - The resource-oriented smart contract programming language - * - * Copyright Flow Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package wasm - -// wasmMagic is the magic byte sequence that appears at the start of the WASM binary. -// -// See https://webassembly.github.io/spec/core/binary/modules.html#binary-module: -// -// The encoding of a module starts with a preamble containing a 4-byte magic number (the string '\0asm') -// -// magic ::= 0x00 0x61 0x73 0x6d -var wasmMagic = []byte{0x00, 0x61, 0x73, 0x6d} - -// wasmVersion is the byte sequence that appears after wasmMagic -// and indicated the version of the WASM binary. -// -// See https://webassembly.github.io/spec/core/binary/modules.html#binary-module: -// -// The encoding of a module starts with [...] a version field. -// The current version of the WebAssembly binary format is 1. -// -// version ::= 0x01 0x00 0x00 0x00 -var wasmVersion = []byte{0x01, 0x00, 0x00, 0x00} diff --git a/compiler/wasm/memory.go b/compiler/wasm/memory.go deleted file mode 100644 index c8fa322286..0000000000 --- a/compiler/wasm/memory.go +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Cadence - The resource-oriented smart contract programming language - * - * Copyright Flow Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package wasm - -// MemoryPageSize is the size of a memory page: 64KiB -const MemoryPageSize = 64 * 1024 - -// Memory represents a memory -type Memory struct { - // maximum number of pages (each one is 64KiB in size). optional, unlimited if nil - Max *uint32 - // minimum number of pages (each one is 64KiB in size) - Min uint32 -} - -// limitIndicator is the byte used to indicate the kind of limit in the WASM binary -type limitIndicator byte - -const ( - // limitIndicatorNoMax is the byte used to indicate a limit with no maximum in the WASM binary - limitIndicatorNoMax limitIndicator = 0x0 - // limitIndicatorMax is the byte used to indicate a limit with no maximum in the WASM binary - limitIndicatorMax limitIndicator = 0x1 -) diff --git a/compiler/wasm/module.go b/compiler/wasm/module.go deleted file mode 100644 index fb818ad9c3..0000000000 --- a/compiler/wasm/module.go +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Cadence - The resource-oriented smart contract programming language - * - * Copyright Flow Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package wasm - -// Module represents a module -type Module struct { - Name string - Types []*FunctionType - Imports []*Import - Functions []*Function - Memories []*Memory - Exports []*Export - StartFunctionIndex *uint32 - Data []*Data -} diff --git a/compiler/wasm/modulebuilder.go b/compiler/wasm/modulebuilder.go deleted file mode 100644 index d0b4d0f11f..0000000000 --- a/compiler/wasm/modulebuilder.go +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Cadence - The resource-oriented smart contract programming language - * - * Copyright Flow Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package wasm - -import ( - "errors" - "math" -) - -// ModuleBuilder allows building modules -type ModuleBuilder struct { - functionImports []*Import - types []*FunctionType - functions []*Function - data []*Data - exports []*Export - requiredMemorySize uint32 -} - -func (b *ModuleBuilder) AddFunction(name string, functionType *FunctionType, code *Code) uint32 { - typeIndex := uint32(len(b.types)) - b.types = append(b.types, functionType) - // function indices include function imports - funcIndex := uint32(len(b.functionImports) + len(b.functions)) - b.functions = append( - b.functions, - &Function{ - Name: name, - TypeIndex: typeIndex, - Code: code, - }, - ) - return funcIndex -} - -func (b *ModuleBuilder) AddFunctionImport(module string, name string, functionType *FunctionType) (uint32, error) { - if len(b.functions) > 0 { - return 0, errors.New("cannot add function imports after adding functions") - } - - typeIndex := uint32(len(b.types)) - b.types = append(b.types, functionType) - funcIndex := uint32(len(b.functionImports)) - b.functionImports = append( - b.functionImports, - &Import{ - Module: module, - Name: name, - TypeIndex: typeIndex, - }, - ) - - return funcIndex, nil -} - -func (b *ModuleBuilder) RequireMemory(size uint32) uint32 { - offset := b.requiredMemorySize - b.requiredMemorySize += size - return offset -} - -func (b *ModuleBuilder) AddData(offset uint32, value []byte) { - b.data = append(b.data, &Data{ - // NOTE: currently only one memory is supported - MemoryIndex: 0, - Offset: []Instruction{ - InstructionI32Const{Value: int32(offset)}, - }, - Init: value, - }) -} - -func (b *ModuleBuilder) Build() *Module { - // NOTE: currently only one memory is supported - memories := []*Memory{ - { - Min: uint32(math.Ceil(float64(b.requiredMemorySize) / float64(MemoryPageSize))), - Max: nil, - }, - } - - return &Module{ - Types: b.types, - Imports: b.functionImports, - Functions: b.functions, - Memories: memories, - Data: b.data, - Exports: b.exports, - } -} - -func (b *ModuleBuilder) ExportMemory(name string) { - b.AddExport(&Export{ - Name: name, - Descriptor: MemoryExport{ - MemoryIndex: 0, - }, - }) -} - -func (b *ModuleBuilder) AddExport(export *Export) { - b.exports = append(b.exports, export) -} diff --git a/compiler/wasm/opcode.go b/compiler/wasm/opcode.go deleted file mode 100644 index 9a0766597d..0000000000 --- a/compiler/wasm/opcode.go +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Cadence - The resource-oriented smart contract programming language - * - * Copyright Flow Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package wasm - -// opcode is the byte used to indicate a certain instruction in the WASM binary -type opcode byte - -const opcodeElse opcode = 0x05 diff --git a/compiler/wasm/reader.go b/compiler/wasm/reader.go deleted file mode 100644 index a9163d4bb8..0000000000 --- a/compiler/wasm/reader.go +++ /dev/null @@ -1,1236 +0,0 @@ -/* - * Cadence - The resource-oriented smart contract programming language - * - * Copyright Flow Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package wasm - -import ( - "io" - "math" - "unicode/utf8" -) - -// WASMReader allows reading WASM binaries -type WASMReader struct { - buf *Buffer - Module Module - lastSectionID sectionID - didReadFunctions bool - didReadCode bool -} - -func NewWASMReader(buf *Buffer) *WASMReader { - return &WASMReader{ - buf: buf, - } -} - -// readMagicAndVersion reads the magic byte sequence and version at the beginning of the WASM binary -// -// See https://webassembly.github.io/spec/core/binary/modules.html#binary-module: -// -// The encoding of a module starts with a preamble containing a 4-byte magic number [...] and a version field. -func (r *WASMReader) readMagicAndVersion() error { - - // Read the magic - equal, err := r.buf.ReadBytesEqual(wasmMagic) - if err != nil || !equal { - return InvalidMagicError{ - Offset: int(r.buf.offset), - ReadError: err, - } - } - - // Read the version - equal, err = r.buf.ReadBytesEqual(wasmVersion) - if err != nil || !equal { - return InvalidVersionError{ - Offset: int(r.buf.offset), - ReadError: err, - } - } - - return nil -} - -// readSection reads a section in the WASM binary -func (r *WASMReader) readSection() error { - // read the section ID - sectionIDOffset := r.buf.offset - b, err := r.buf.ReadByte() - - sectionID := sectionID(b) - - if err != nil { - return InvalidSectionIDError{ - SectionID: sectionID, - Offset: int(sectionIDOffset), - ReadError: err, - } - } - - invalidDuplicateSectionError := func() error { - return InvalidDuplicateSectionError{ - SectionID: sectionID, - Offset: int(sectionIDOffset), - } - } - - // "Custom sections may be inserted at any place in this sequence, - // while other sections must occur at most once and in the prescribed order." - - if sectionID > 0 && sectionID <= r.lastSectionID { - return InvalidSectionOrderError{ - SectionID: sectionID, - Offset: int(sectionIDOffset), - } - } - - switch sectionID { - case sectionIDCustom: - err = r.readCustomSection() - if err != nil { - return err - } - - case sectionIDType: - if r.Module.Types != nil { - return invalidDuplicateSectionError() - } - - err = r.readTypeSection() - if err != nil { - return err - } - - case sectionIDImport: - if r.Module.Imports != nil { - return invalidDuplicateSectionError() - } - - err = r.readImportSection() - if err != nil { - return err - } - - case sectionIDFunction: - if r.didReadFunctions { - return invalidDuplicateSectionError() - } - - err = r.readFunctionSection() - if err != nil { - return err - } - - r.didReadFunctions = true - - case sectionIDMemory: - if r.Module.Memories != nil { - return invalidDuplicateSectionError() - } - - err = r.readMemorySection() - if err != nil { - return err - } - - case sectionIDExport: - if r.Module.Exports != nil { - return invalidDuplicateSectionError() - } - - err = r.readExportSection() - if err != nil { - return err - } - - case sectionIDStart: - if r.Module.StartFunctionIndex != nil { - return invalidDuplicateSectionError() - } - - err = r.readStartSection() - if err != nil { - return err - } - - case sectionIDCode: - if r.didReadCode { - return invalidDuplicateSectionError() - } - - err = r.readCodeSection() - if err != nil { - return err - } - - r.didReadCode = true - - case sectionIDData: - if r.Module.Data != nil { - return invalidDuplicateSectionError() - } - - err = r.readDataSection() - if err != nil { - return err - } - - default: - return InvalidSectionIDError{ - SectionID: sectionID, - Offset: int(sectionIDOffset), - } - } - - // Keep track of the last read non-custom section ID: - // non-custom sections must appear in order - - if sectionID > 0 { - r.lastSectionID = sectionID - } - - return nil -} - -// readSectionSize reads the content size of a section -func (r *WASMReader) readSectionSize() (uint32, error) { - // read the size - sizeOffset := r.buf.offset - size, err := r.buf.readUint32LEB128() - if err != nil { - return 0, InvalidSectionSizeError{ - Offset: int(sizeOffset), - ReadError: err, - } - } - - return size, nil -} - -// readTypeSection reads the section that declares all function types -// so they can be referenced by index -func (r *WASMReader) readTypeSection() error { - - _, err := r.readSectionSize() - if err != nil { - return err - } - - // read the number of types - countOffset := r.buf.offset - count, err := r.buf.readUint32LEB128() - if err != nil { - return InvalidTypeSectionTypeCountError{ - Offset: int(countOffset), - ReadError: err, - } - } - - funcTypes := make([]*FunctionType, count) - - // read each type - for i := uint32(0); i < count; i++ { - funcType, err := r.readFuncType() - if err != nil { - return err - } - funcTypes[i] = funcType - } - - r.Module.Types = funcTypes - - return nil -} - -// readFuncType reads a function type -func (r *WASMReader) readFuncType() (*FunctionType, error) { - // read the function type indicator - funcTypeIndicatorOffset := r.buf.offset - funcTypeIndicator, err := r.buf.ReadByte() - if err != nil || funcTypeIndicator != functionTypeIndicator { - return nil, InvalidFuncTypeIndicatorError{ - Offset: int(funcTypeIndicatorOffset), - FuncTypeIndicator: funcTypeIndicator, - ReadError: err, - } - } - - // read the number of parameters - parameterCountOffset := r.buf.offset - parameterCount, err := r.buf.readUint32LEB128() - if err != nil { - return nil, InvalidFuncTypeParameterCountError{ - Offset: int(parameterCountOffset), - ReadError: err, - } - } - - // read the type of each parameter - - var parameterTypes []ValueType - if parameterCount > 0 { - parameterTypes = make([]ValueType, parameterCount) - - for i := uint32(0); i < parameterCount; i++ { - parameterType, err := r.readValType() - if err != nil { - return nil, InvalidFuncTypeParameterTypeError{ - Index: int(i), - ReadError: err, - } - } - parameterTypes[i] = parameterType - } - } - - // read the number of results - resultCountOffset := r.buf.offset - resultCount, err := r.buf.readUint32LEB128() - if err != nil { - return nil, InvalidFuncTypeResultCountError{ - Offset: int(resultCountOffset), - ReadError: err, - } - } - - // read the type of each result - - var resultTypes []ValueType - if resultCount > 0 { - resultTypes = make([]ValueType, resultCount) - for i := uint32(0); i < resultCount; i++ { - resultType, err := r.readValType() - if err != nil { - return nil, InvalidFuncTypeResultTypeError{ - Index: int(i), - ReadError: err, - } - } - resultTypes[i] = resultType - } - } - - return &FunctionType{ - Params: parameterTypes, - Results: resultTypes, - }, nil -} - -// readValType reads a value type -func (r *WASMReader) readValType() (ValueType, error) { - valTypeOffset := r.buf.offset - b, err := r.buf.ReadByte() - - valType := ValueType(b) - - if err != nil { - return 0, InvalidValTypeError{ - Offset: int(valTypeOffset), - ValType: valType, - ReadError: err, - } - } - - switch valType { - case ValueTypeI32, ValueTypeI64: - return valType, nil - } - - return 0, InvalidValTypeError{ - Offset: int(valTypeOffset), - ValType: valType, - } -} - -// readImportSection reads the section that declares the imports -func (r *WASMReader) readImportSection() error { - - _, err := r.readSectionSize() - if err != nil { - return err - } - - // read the number of imports - countOffset := r.buf.offset - count, err := r.buf.readUint32LEB128() - if err != nil { - return InvalidImportSectionImportCountError{ - Offset: int(countOffset), - ReadError: err, - } - } - - imports := make([]*Import, count) - - // read each import - for i := uint32(0); i < count; i++ { - im, err := r.readImport() - if err != nil { - return InvalidImportError{ - Index: int(i), - ReadError: err, - } - } - imports[i] = im - } - - r.Module.Imports = imports - - return nil -} - -// readImport reads an import in the import section -func (r *WASMReader) readImport() (*Import, error) { - - // read the module - module, err := r.readName() - if err != nil { - return nil, err - } - - // read the name - name, err := r.readName() - if err != nil { - return nil, err - } - - // read the type indicator - indicatorOffset := r.buf.offset - b, err := r.buf.ReadByte() - - indicator := importIndicator(b) - - // TODO: add support for tables, memories, and globals. adjust name section! - if err != nil || indicator != importIndicatorFunction { - return nil, InvalidImportIndicatorError{ - ImportIndicator: indicator, - Offset: int(indicatorOffset), - ReadError: err, - } - } - - // read the type index - typeIndexOffset := r.buf.offset - typeIndex, err := r.buf.readUint32LEB128() - if err != nil { - return nil, InvalidImportSectionTypeIndexError{ - Offset: int(typeIndexOffset), - ReadError: err, - } - } - - return &Import{ - Module: module, - Name: name, - TypeIndex: typeIndex, - }, nil -} - -// readFunctionSection reads the section that declares the types of functions. -// The bodies of these functions will later be provided in the code section -func (r *WASMReader) readFunctionSection() error { - - _, err := r.readSectionSize() - if err != nil { - return err - } - - // read the number of functions - countOffset := r.buf.offset - count, err := r.buf.readUint32LEB128() - if err != nil { - return InvalidFunctionSectionFunctionCountError{ - Offset: int(countOffset), - ReadError: err, - } - } - - functionTypeIndices := make([]uint32, count) - - // read the type index for each function - for i := uint32(0); i < count; i++ { - typeIndexOffset := r.buf.offset - typeIndex, err := r.buf.readUint32LEB128() - if err != nil { - return InvalidFunctionSectionTypeIndexError{ - Index: int(i), - Offset: int(typeIndexOffset), - ReadError: err, - } - } - functionTypeIndices[i] = typeIndex - } - - if !r.ensureModuleFunctions(len(functionTypeIndices)) { - return FunctionCountMismatchError{ - Offset: int(r.buf.offset), - } - } - - for i, functionTypeIndex := range functionTypeIndices { - r.Module.Functions[i].TypeIndex = functionTypeIndex - } - - return nil -} - -func (r *WASMReader) ensureModuleFunctions(count int) bool { - if r.Module.Functions != nil { - return len(r.Module.Functions) == count - } - - r.Module.Functions = make([]*Function, count) - for i := 0; i < count; i++ { - r.Module.Functions[i] = &Function{} - } - - return true -} - -// readMemorySection reads the section that declares the memories -func (r *WASMReader) readMemorySection() error { - - _, err := r.readSectionSize() - if err != nil { - return err - } - - // read the number of memories - countOffset := r.buf.offset - count, err := r.buf.readUint32LEB128() - if err != nil { - return InvalidMemorySectionMemoryCountError{ - Offset: int(countOffset), - ReadError: err, - } - } - - memories := make([]*Memory, count) - - // read each memory - for i := uint32(0); i < count; i++ { - im, err := r.readMemory() - if err != nil { - return InvalidMemoryError{ - Index: int(i), - ReadError: err, - } - } - memories[i] = im - } - - r.Module.Memories = memories - - return nil -} - -// readMemory reads a memory in the memory section -func (r *WASMReader) readMemory() (*Memory, error) { - min, max, err := r.readLimit() - if err != nil { - return nil, err - } - - return &Memory{ - Min: min, - Max: max, - }, nil -} - -// readLimit reads a limit -func (r *WASMReader) readLimit() (min uint32, max *uint32, err error) { - // read the limit indicator - indicatorOffset := r.buf.offset - b, err := r.buf.ReadByte() - if err != nil { - return 0, nil, InvalidLimitIndicatorError{ - Offset: int(indicatorOffset), - LimitIndicator: b, - ReadError: err, - } - } - - indicator := limitIndicator(b) - - var readMax bool - - switch indicator { - case limitIndicatorNoMax: - readMax = false - case limitIndicatorMax: - readMax = true - default: - return 0, nil, InvalidLimitIndicatorError{ - Offset: int(indicatorOffset), - LimitIndicator: byte(indicator), - } - } - - // read the minimum - minOffset := r.buf.offset - min, err = r.buf.readUint32LEB128() - if err != nil { - return 0, nil, InvalidLimitMinError{ - Offset: int(minOffset), - ReadError: err, - } - } - - // read the maximum, if given - if readMax { - maxOffset := r.buf.offset - maximum, err := r.buf.readUint32LEB128() - if err != nil { - return 0, nil, InvalidLimitMaxError{ - Offset: int(maxOffset), - ReadError: err, - } - } - max = &maximum - } - - return min, max, nil -} - -// readExportSection reads the section that declares the exports -func (r *WASMReader) readExportSection() error { - - _, err := r.readSectionSize() - if err != nil { - return err - } - - // read the number of exports - countOffset := r.buf.offset - count, err := r.buf.readUint32LEB128() - if err != nil { - return InvalidExportSectionExportCountError{ - Offset: int(countOffset), - ReadError: err, - } - } - - exports := make([]*Export, count) - - // read each export - for i := uint32(0); i < count; i++ { - im, err := r.readExport() - if err != nil { - return InvalidExportError{ - Index: int(i), - ReadError: err, - } - } - exports[i] = im - } - - r.Module.Exports = exports - - return nil -} - -// readExport reads an export in the export section -func (r *WASMReader) readExport() (*Export, error) { - - // read the name - name, err := r.readName() - if err != nil { - return nil, err - } - - // read the type indicator - indicatorOffset := r.buf.offset - b, err := r.buf.ReadByte() - - indicator := exportIndicator(b) - - // TODO: add support for tables, and globals. adjust name section! - if err != nil { - return nil, InvalidExportIndicatorError{ - ExportIndicator: indicator, - Offset: int(indicatorOffset), - ReadError: err, - } - } - - // read the index - indexOffset := r.buf.offset - index, err := r.buf.readUint32LEB128() - if err != nil { - return nil, InvalidExportSectionIndexError{ - Offset: int(indexOffset), - ReadError: err, - } - } - - var descriptor ExportDescriptor - - switch indicator { - case exportIndicatorFunction: - descriptor = FunctionExport{ - FunctionIndex: index, - } - - case exportIndicatorMemory: - descriptor = MemoryExport{ - MemoryIndex: index, - } - - default: - return nil, InvalidExportIndicatorError{ - ExportIndicator: indicator, - Offset: int(indicatorOffset), - } - } - - return &Export{ - Name: name, - Descriptor: descriptor, - }, nil -} - -// readStartSection reads the section that declares the types of functions. -// The bodies of these functions will later be provided in the code section -func (r *WASMReader) readStartSection() error { - - _, err := r.readSectionSize() - if err != nil { - return err - } - - // read the function index - functionIndexOffset := r.buf.offset - functionIndex, err := r.buf.readUint32LEB128() - if err != nil { - return InvalidStartSectionFunctionIndexError{ - Offset: int(functionIndexOffset), - ReadError: err, - } - } - - r.Module.StartFunctionIndex = &functionIndex - - return nil -} - -// readCodeSection reads the section that provides the function bodies for the functions -// declared by the function section (which only provides the function types) -func (r *WASMReader) readCodeSection() error { - - _, err := r.readSectionSize() - if err != nil { - return err - } - - // read the number of functions - countOffset := r.buf.offset - count, err := r.buf.readUint32LEB128() - if err != nil { - return InvalidCodeSectionFunctionCountError{ - Offset: int(countOffset), - ReadError: err, - } - } - - // read the code of each function - - functionBodies := make([]*Code, count) - - for i := uint32(0); i < count; i++ { - functionBody, err := r.readFunctionBody() - if err != nil { - return InvalidFunctionCodeError{ - Index: int(i), - ReadError: err, - } - } - functionBodies[i] = functionBody - } - - if !r.ensureModuleFunctions(len(functionBodies)) { - return FunctionCountMismatchError{ - Offset: int(r.buf.offset), - } - } - - for i, functionBody := range functionBodies { - r.Module.Functions[i].Code = functionBody - } - - return nil -} - -// readFunctionBody reads the body (locals and instruction) of one function in the code section -func (r *WASMReader) readFunctionBody() (*Code, error) { - - // read the size - sizeOffset := r.buf.offset - // TODO: use size - _, err := r.buf.readUint32LEB128() - if err != nil { - return nil, InvalidCodeSizeError{ - Offset: int(sizeOffset), - ReadError: err, - } - } - - // read the locals - locals, err := r.readLocals() - if err != nil { - return nil, err - } - - // read the instructions - instructions, err := r.readInstructions() - if err != nil { - return nil, err - } - - return &Code{ - Locals: locals, - Instructions: instructions, - }, nil -} - -// readLocals reads the locals for one function in the code sections -func (r *WASMReader) readLocals() ([]ValueType, error) { - // read the number of locals - localsCountOffset := r.buf.offset - localsCount, err := r.buf.readUint32LEB128() - if err != nil { - return nil, InvalidCodeSectionLocalsCountError{ - Offset: int(localsCountOffset), - ReadError: err, - } - } - - if localsCount == 0 { - return nil, nil - } - - locals := make([]ValueType, localsCount) - - // read each local - for i := uint32(0); i < localsCount; { - compressedLocalsCountOffset := r.buf.offset - compressedLocalsCount, err := r.buf.readUint32LEB128() - if err != nil { - return nil, InvalidCodeSectionCompressedLocalsCountError{ - Offset: int(compressedLocalsCountOffset), - ReadError: err, - } - } - - localTypeOffset := r.buf.offset - localType, err := r.readValType() - if err != nil { - return nil, InvalidCodeSectionLocalTypeError{ - Offset: int(localTypeOffset), - ReadError: err, - } - } - - locals[i] = localType - - i += compressedLocalsCount - - if i > localsCount { - return nil, CodeSectionLocalsCountMismatchError{ - Actual: i, - Expected: localsCount, - } - } - } - - return locals, nil -} - -// readInstructions reads the instructions for one function in the code sections -func (r *WASMReader) readInstructions() (instructions []Instruction, err error) { - for { - instruction, err := r.readInstruction() - if err != nil { - return nil, err - } - - if _, ok := instruction.(InstructionEnd); ok { - return instructions, nil - } - - instructions = append(instructions, instruction) - } -} - -// readName reads a name -func (r *WASMReader) readName() (string, error) { - - // read the length - lengthOffset := r.buf.offset - length, err := r.buf.readUint32LEB128() - if err != nil { - return "", InvalidNameLengthError{ - Offset: int(lengthOffset), - ReadError: err, - } - } - - // read the name - nameOffset := r.buf.offset - name := make([]byte, length) - n, err := r.buf.Read(name) - if err != nil { - return "", InvalidNameError{ - Offset: int(nameOffset), - ReadError: err, - } - } - - readCount := uint32(n) - - // ensure the full name was read - if readCount != length { - return "", IncompleteNameError{ - Offset: int(nameOffset), - Expected: length, - Actual: readCount, - } - } - - // ensure the name is valid UTF-8 - if !utf8.Valid(name) { - return "", InvalidNonUTF8NameError{ - Offset: int(nameOffset), - Name: string(name), - } - } - - return string(name), nil -} - -// readUint32LEB128InstructionArgument reads a uint32 instruction argument -// (in LEB128 format) -func (r *WASMReader) readUint32LEB128InstructionArgument() (uint32, error) { - offset := r.buf.offset - v, err := r.buf.readUint32LEB128() - if err != nil { - return 0, InvalidInstructionArgumentError{ - Offset: int(offset), - ReadError: err, - } - } - return v, nil -} - -// readInt32LEB128InstructionArgument reads an int32 instruction argument -// (in LEB128 format) -func (r *WASMReader) readInt32LEB128InstructionArgument() (int32, error) { - offset := r.buf.offset - v, err := r.buf.readInt32LEB128() - if err != nil { - return 0, InvalidInstructionArgumentError{ - Offset: int(offset), - ReadError: err, - } - } - return v, nil -} - -// readInt64LEB128InstructionArgument reads an int64 instruction argument -// (in LEB128 format) -func (r *WASMReader) readInt64LEB128InstructionArgument() (int64, error) { - offset := r.buf.offset - v, err := r.buf.readInt64LEB128() - if err != nil { - return 0, InvalidInstructionArgumentError{ - Offset: int(offset), - ReadError: err, - } - } - return v, nil -} - -// readBlockInstructionArgument reads a block instruction argument -func (r *WASMReader) readBlockInstructionArgument(allowElse bool) (Block, error) { - // read the block type. - blockType, err := r.readBlockType() - if err != nil { - // TODO: improve error - return Block{}, err - } - - // read the first sequence of instructions. - // `readInstructions` cannot be used, as it does not handle the `else` instruction / opcode. - - var instructions1 []Instruction - - sawElse := false - - for { - b, err := r.buf.PeekByte() - if err != nil { - // TODO: improve error - return Block{}, err - } - if b == byte(opcodeElse) { - if !allowElse { - return Block{}, InvalidBlockSecondInstructionsError{ - Offset: int(r.buf.offset), - } - } - r.buf.offset++ - sawElse = true - break - } - - instruction, err := r.readInstruction() - if err != nil { - // TODO: improve error - return Block{}, err - } - - if _, ok := instruction.(InstructionEnd); ok { - break - } - - instructions1 = append(instructions1, instruction) - } - - var instructions2 []Instruction - if sawElse { - // the first set of instructions contained an `else` instruction / opcode, - // so read the second set of instructions. the validity was already checked above. - - instructions2, err = r.readInstructions() - if err != nil { - // TODO: improve error - return Block{}, err - } - } - - return Block{ - BlockType: blockType, - Instructions1: instructions1, - Instructions2: instructions2, - }, nil -} - -func (r *WASMReader) readBlockType() (BlockType, error) { - // it may be the empty block type - b, err := r.buf.PeekByte() - if err != nil { - // TODO: improve error - return nil, err - } - if b == emptyBlockType { - r.buf.offset++ - return nil, nil - } - - // it may be a value type - blockType, err := r.readBlockTypeValueType() - if err != nil { - // TODO: improve error - return nil, err - } - - if blockType != nil { - return blockType, nil - } - - // the block type is not a value type, - // it must be a type index. - - typeIndexOffset := r.buf.offset - typeIndex, err := r.buf.readInt64LEB128() - if err != nil { - // TODO: improve error - return nil, err - } - - // the type index was read as a int64, - // but must fit a uint32 - if typeIndex < 0 || typeIndex > math.MaxUint32 { - return nil, InvalidBlockTypeTypeIndexError{ - Offset: int(typeIndexOffset), - TypeIndex: typeIndex, - } - } - - return TypeIndexBlockType{ - TypeIndex: uint32(typeIndex), - }, nil -} - -// readBlockTypeValueType reads a value type block type -// and returns nil if the next byte is not a valid value type -func (r *WASMReader) readBlockTypeValueType() (BlockType, error) { - b, err := r.buf.PeekByte() - if err != nil { - return nil, err - } - valueType := AsValueType(b) - if valueType == 0 { - return nil, nil - } - r.buf.offset++ - return valueType, nil -} - -// readCustomSection reads a custom section -func (r *WASMReader) readCustomSection() error { - - size, err := r.readSectionSize() - if err != nil { - return err - } - - // read the name of the custom section - - nameStartOffset := r.buf.offset - name, err := r.readName() - if err != nil { - return err - } - - size -= uint32(r.buf.offset - nameStartOffset) - - switch name { - case customSectionNameName: - return r.readNameSection(size) - } - - // skip unknown custom sections - r.buf.offset += offset(size) - - return nil -} - -// readDataSection reads the section that declares the data segments -func (r *WASMReader) readDataSection() error { - - _, err := r.readSectionSize() - if err != nil { - return err - } - - // read the number of data segments - countOffset := r.buf.offset - count, err := r.buf.readUint32LEB128() - if err != nil { - return InvalidDataSectionSegmentCountError{ - Offset: int(countOffset), - ReadError: err, - } - } - - segments := make([]*Data, count) - - // read each data segment - for i := uint32(0); i < count; i++ { - segment, err := r.readDataSegment() - if err != nil { - return InvalidDataSegmentError{ - Index: int(i), - ReadError: err, - } - } - segments[i] = segment - } - - r.Module.Data = segments - - return nil -} - -// readDataSegment reads a segment in the data section -func (r *WASMReader) readDataSegment() (*Data, error) { - - // read the memory index - memoryIndexOffset := r.buf.offset - memoryIndex, err := r.buf.readUint32LEB128() - if err != nil { - return nil, InvalidDataSectionMemoryIndexError{ - Offset: int(memoryIndexOffset), - ReadError: err, - } - } - - // read the offset instructions - instructions, err := r.readInstructions() - if err != nil { - return nil, err - } - - // read the number of init bytes - countOffset := r.buf.offset - count, err := r.buf.readUint32LEB128() - if err != nil { - return nil, InvalidDataSectionInitByteCountError{ - Offset: int(countOffset), - ReadError: err, - } - } - - init := make([]byte, count) - - // read each init byte - for i := uint32(0); i < count; i++ { - b, err := r.buf.ReadByte() - if err != nil { - return nil, err - } - init[i] = b - } - - return &Data{ - MemoryIndex: memoryIndex, - Offset: instructions, - Init: init, - }, nil -} - -// readNameSection reads the section that provides names -func (r *WASMReader) readNameSection(size uint32) error { - - // TODO: read the names and store them. for now, just skip the content - r.buf.offset += offset(size) - - return nil -} - -func (r *WASMReader) ReadModule() error { - if err := r.readMagicAndVersion(); err != nil { - return err - } - - for { - _, err := r.buf.PeekByte() - if err != nil { - if err == io.EOF { - return nil - } - - return err - } - - if err = r.readSection(); err != nil { - return err - } - } -} diff --git a/compiler/wasm/reader_test.go b/compiler/wasm/reader_test.go deleted file mode 100644 index 65485b59ee..0000000000 --- a/compiler/wasm/reader_test.go +++ /dev/null @@ -1,2243 +0,0 @@ -/* - * Cadence - The resource-oriented smart contract programming language - * - * Copyright Flow Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package wasm - -import ( - "io" - "math" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestWASMReader_readMagicAndVersion(t *testing.T) { - - t.Parallel() - - read := func(data []byte) error { - b := Buffer{data: data} - r := NewWASMReader(&b) - err := r.readMagicAndVersion() - if err != nil { - return err - } - require.Equal(t, offset(len(b.data)), b.offset) - return nil - } - - t.Run("invalid magic, too short", func(t *testing.T) { - - t.Parallel() - - err := read([]byte{0x0, 0x61}) - require.Error(t, err) - assert.Equal(t, - InvalidMagicError{ - Offset: 0, - ReadError: io.EOF, - }, - err, - ) - }) - - t.Run("invalid magic, incorrect", func(t *testing.T) { - - t.Parallel() - - err := read([]byte{0x0, 0x61, 0x73, 0xFF}) - require.Error(t, err) - assert.Equal(t, - InvalidMagicError{ - Offset: 0, - ReadError: nil, - }, - err, - ) - }) - - t.Run("invalid version, too short", func(t *testing.T) { - - t.Parallel() - - err := read([]byte{0x0, 0x61, 0x73, 0x6d, 0x1, 0x0}) - require.Error(t, err) - assert.Equal(t, - InvalidVersionError{ - Offset: 4, - ReadError: io.EOF, - }, - err, - ) - }) - - t.Run("invalid version, incorrect", func(t *testing.T) { - - t.Parallel() - - err := read([]byte{0x0, 0x61, 0x73, 0x6d, 0x2, 0x0, 0x0, 0x0}) - require.Error(t, err) - assert.Equal(t, - InvalidVersionError{ - Offset: 4, - ReadError: nil, - }, - err, - ) - }) - - t.Run("valid magic and version", func(t *testing.T) { - - t.Parallel() - - err := read([]byte{0x0, 0x61, 0x73, 0x6d, 0x1, 0x0, 0x0, 0x0}) - require.NoError(t, err) - }) -} - -func TestWASMReader_readValType(t *testing.T) { - - t.Parallel() - - read := func(data []byte) (ValueType, error) { - b := Buffer{data: data} - r := NewWASMReader(&b) - valueType, err := r.readValType() - if err != nil { - return 0, err - } - require.Equal(t, offset(len(b.data)), b.offset) - return valueType, nil - } - - t.Run("too short", func(t *testing.T) { - - t.Parallel() - - valType, err := read([]byte{}) - require.Error(t, err) - assert.Equal(t, - InvalidValTypeError{ - Offset: 0, - ValType: valType, - ReadError: io.EOF, - }, - err, - ) - assert.Equal(t, ValueType(0), valType) - }) - - t.Run("invalid", func(t *testing.T) { - - t.Parallel() - - valType, err := read([]byte{0xFF}) - require.Error(t, err) - assert.Equal(t, - InvalidValTypeError{ - Offset: 0, - ValType: 0xFF, - ReadError: nil, - }, - err, - ) - assert.Equal(t, ValueType(0), valType) - }) - - t.Run("i32", func(t *testing.T) { - - t.Parallel() - - valType, err := read([]byte{byte(ValueTypeI32)}) - require.NoError(t, err) - assert.Equal(t, ValueTypeI32, valType) - }) - - t.Run("i64", func(t *testing.T) { - - t.Parallel() - - valType, err := read([]byte{byte(ValueTypeI64)}) - require.NoError(t, err) - assert.Equal(t, ValueTypeI64, valType) - }) -} - -func TestWASMReader_readTypeSection(t *testing.T) { - - t.Parallel() - - read := func(data []byte) ([]*FunctionType, error) { - b := Buffer{data: data} - r := NewWASMReader(&b) - err := r.readTypeSection() - if err != nil { - return nil, err - } - require.Equal(t, offset(len(b.data)), b.offset) - return r.Module.Types, nil - } - - t.Run("valid", func(t *testing.T) { - - t.Parallel() - - funcTypes, err := read([]byte{ - // section size: 7 (LEB128) - 0x87, 0x80, 0x80, 0x80, 0x0, - // type count - 0x1, - // function type - 0x60, - // parameter count: 2 - 0x2, - // type of parameter 1: i32 - 0x7f, - // type of parameter 2: i32 - 0x7f, - // return value count: 1 - 0x1, - // type of return value 1: i32 - 0x7f, - }) - require.NoError(t, err) - assert.Equal(t, - []*FunctionType{ - { - Params: []ValueType{ValueTypeI32, ValueTypeI32}, - Results: []ValueType{ValueTypeI32}, - }, - }, - funcTypes, - ) - }) - - t.Run("invalid size", func(t *testing.T) { - - t.Parallel() - - funcTypes, err := read([]byte{ - 0x87, 0x80, 0x80, - }) - require.Error(t, err) - assert.Equal(t, - InvalidSectionSizeError{ - Offset: 0, - ReadError: io.EOF, - }, - err, - ) - assert.Nil(t, funcTypes) - }) - - t.Run("invalid count", func(t *testing.T) { - - t.Parallel() - - funcTypes, err := read([]byte{ - // section size: 0 (LEB128) - 0x80, 0x80, 0x80, 0x80, 0x0, - }) - require.Error(t, err) - assert.Equal(t, - InvalidTypeSectionTypeCountError{ - Offset: 5, - ReadError: io.EOF, - }, - err, - ) - assert.Nil(t, funcTypes) - }) - - t.Run("invalid indicator", func(t *testing.T) { - - t.Parallel() - - funcTypes, err := read([]byte{ - // section size: 2 (LEB128) - 0x82, 0x80, 0x80, 0x80, 0x0, - // type count - 0x1, - // function type - 0xFF, - }) - require.Error(t, err) - assert.Equal(t, - InvalidFuncTypeIndicatorError{ - Offset: 6, - FuncTypeIndicator: 0xff, - }, - err, - ) - assert.Nil(t, funcTypes) - }) - - t.Run("invalid parameter count", func(t *testing.T) { - - t.Parallel() - - funcTypes, err := read([]byte{ - // section size: 2 (LEB128) - 0x82, 0x80, 0x80, 0x80, 0x0, - // type count - 0x1, - // function type - 0x60, - }) - require.Error(t, err) - assert.Equal(t, - InvalidFuncTypeParameterCountError{ - Offset: 7, - ReadError: io.EOF, - }, - err, - ) - assert.Nil(t, funcTypes) - }) - - t.Run("invalid parameter type", func(t *testing.T) { - - t.Parallel() - - funcTypes, err := read([]byte{ - // section size: 4 (LEB128) - 0x84, 0x80, 0x80, 0x80, 0x0, - // type count - 0x1, - // function type - 0x60, - // parameter count: 1 - 0x1, - // type of parameter 1 - 0xff, - }) - require.Error(t, err) - assert.Equal(t, - InvalidFuncTypeParameterTypeError{ - Index: 0, - ReadError: InvalidValTypeError{ - Offset: 8, - ValType: 0xFF, - }, - }, - err, - ) - assert.Nil(t, funcTypes) - }) - - t.Run("invalid, parameter type missing", func(t *testing.T) { - - t.Parallel() - - funcTypes, err := read([]byte{ - // section size: 4 (LEB128) - 0x84, 0x80, 0x80, 0x80, 0x0, - // type count - 0x1, - // function type - 0x60, - // parameter count: 2 - 0x2, - // type of parameter 1: i32 - 0x7f, - }) - require.Error(t, err) - assert.Equal(t, - InvalidFuncTypeParameterTypeError{ - Index: 1, - ReadError: InvalidValTypeError{ - Offset: 9, - ValType: 0, - ReadError: io.EOF, - }, - }, - err, - ) - assert.Nil(t, funcTypes) - }) - - t.Run("invalid result count", func(t *testing.T) { - - t.Parallel() - - funcTypes, err := read([]byte{ - // section size: 3 (LEB128) - 0x83, 0x80, 0x80, 0x80, 0x0, - // type count - 0x1, - // function type - 0x60, - // parameter count - 0x0, - }) - require.Error(t, err) - assert.Equal(t, - InvalidFuncTypeResultCountError{ - Offset: 8, - ReadError: io.EOF, - }, - err, - ) - assert.Nil(t, funcTypes) - }) - - t.Run("invalid result type", func(t *testing.T) { - - t.Parallel() - - funcTypes, err := read([]byte{ - // section size: 5 (LEB128) - 0x85, 0x80, 0x80, 0x80, 0x0, - // type count - 0x1, - // function type - 0x60, - // parameter count: 0 - 0x0, - // result count: 1 - 0x1, - // type of result 1 - 0xff, - }) - require.Error(t, err) - assert.Equal(t, - InvalidFuncTypeResultTypeError{ - Index: 0, - ReadError: InvalidValTypeError{ - Offset: 9, - ValType: 0xFF, - }, - }, - err, - ) - assert.Nil(t, funcTypes) - }) - - t.Run("invalid, result type missing", func(t *testing.T) { - - t.Parallel() - - funcTypes, err := read([]byte{ - // section size: 5 (LEB128) - 0x85, 0x80, 0x80, 0x80, 0x0, - // type count - 0x1, - // function type - 0x60, - // parameter count: 0 - 0x0, - // result count: 2 - 0x2, - // type of result 1: i32 - 0x7f, - }) - require.Error(t, err) - assert.Equal(t, - InvalidFuncTypeResultTypeError{ - Index: 1, - ReadError: InvalidValTypeError{ - Offset: 10, - ValType: 0, - ReadError: io.EOF, - }, - }, - err, - ) - assert.Nil(t, funcTypes) - }) -} - -func TestWASMReader_readImportSection(t *testing.T) { - - t.Parallel() - - read := func(data []byte) ([]*Import, error) { - b := Buffer{data: data} - r := NewWASMReader(&b) - err := r.readImportSection() - if err != nil { - return nil, err - } - require.Equal(t, offset(len(b.data)), b.offset) - return r.Module.Imports, nil - } - - t.Run("valid", func(t *testing.T) { - - t.Parallel() - - typeIndices, err := read([]byte{ - // section size: 11 (LEB128) - 0x8b, 0x80, 0x80, 0x80, 0x0, - // import count: 1 - 0x1, - // module length - 0x3, - // module = "foo" - 0x66, 0x6f, 0x6f, - // name length - 0x3, - // name = "bar" - 0x62, 0x61, 0x72, - // indicator: function = 0 - 0x0, - // type index of function: 0 - 0x1, - }) - require.NoError(t, err) - assert.Equal(t, - []*Import{ - { - Module: "foo", - Name: "bar", - TypeIndex: 1, - }, - }, - typeIndices, - ) - }) - - t.Run("invalid size", func(t *testing.T) { - - t.Parallel() - - funcTypes, err := read([]byte{ - 0x87, 0x80, 0x80, - }) - require.Error(t, err) - assert.Equal(t, - InvalidSectionSizeError{ - Offset: 0, - ReadError: io.EOF, - }, - err, - ) - assert.Nil(t, funcTypes) - }) - - t.Run("invalid count", func(t *testing.T) { - - t.Parallel() - - funcTypes, err := read([]byte{ - // section size: 0 (LEB128) - 0x80, 0x80, 0x80, 0x80, 0x0, - }) - require.Error(t, err) - assert.Equal(t, - InvalidImportSectionImportCountError{ - Offset: 5, - ReadError: io.EOF, - }, - err, - ) - assert.Nil(t, funcTypes) - }) - - t.Run("invalid module", func(t *testing.T) { - - t.Parallel() - - funcTypes, err := read([]byte{ - // section size: 1 (LEB128) - 0x81, 0x80, 0x80, 0x80, 0x0, - // import count - 0x1, - }) - require.Error(t, err) - assert.Equal(t, - InvalidImportError{ - Index: 0, - ReadError: InvalidNameLengthError{ - Offset: 6, - ReadError: io.EOF, - }, - }, - err, - ) - assert.Nil(t, funcTypes) - }) - - t.Run("invalid name", func(t *testing.T) { - - t.Parallel() - - funcTypes, err := read([]byte{ - // section size: 5 (LEB128) - 0x85, 0x80, 0x80, 0x80, 0x0, - // import count - 0x1, - // module length - 0x3, - // module = "foo" - 0x66, 0x6f, 0x6f, - }) - require.Error(t, err) - assert.Equal(t, - InvalidImportError{ - Index: 0, - ReadError: InvalidNameLengthError{ - Offset: 10, - ReadError: io.EOF, - }, - }, - err, - ) - assert.Nil(t, funcTypes) - }) - - t.Run("missing indicator", func(t *testing.T) { - - t.Parallel() - - funcTypes, err := read([]byte{ - // section size: 9 (LEB128) - 0x89, 0x80, 0x80, 0x80, 0x0, - // import count - 0x1, - // module length - 0x3, - // module = "foo" - 0x66, 0x6f, 0x6f, - // name length - 0x3, - // name = "bar" - 0x62, 0x61, 0x72, - }) - require.Error(t, err) - assert.Equal(t, - InvalidImportError{ - Index: 0, - ReadError: InvalidImportIndicatorError{ - Offset: 14, - ReadError: io.EOF, - }, - }, - err, - ) - assert.Nil(t, funcTypes) - }) - - t.Run("invalid indicator", func(t *testing.T) { - - t.Parallel() - - funcTypes, err := read([]byte{ - // section size: 10 (LEB128) - 0x8a, 0x80, 0x80, 0x80, 0x0, - // import count - 0x1, - // module length - 0x3, - // module = "foo" - 0x66, 0x6f, 0x6f, - // name length - 0x3, - // name = "bar" - 0x62, 0x61, 0x72, - // indicator - 0x1, - }) - require.Error(t, err) - assert.Equal(t, - InvalidImportError{ - Index: 0, - ReadError: InvalidImportIndicatorError{ - Offset: 14, - ImportIndicator: 0x1, - ReadError: nil, - }, - }, - err, - ) - assert.Nil(t, funcTypes) - }) - - t.Run("invalid type index", func(t *testing.T) { - - t.Parallel() - - funcTypes, err := read([]byte{ - // section size: 10 (LEB128) - 0x8a, 0x80, 0x80, 0x80, 0x0, - // import count - 0x1, - // module length - 0x3, - // module = "foo" - 0x66, 0x6f, 0x6f, - // name length - 0x3, - // name = "bar" - 0x62, 0x61, 0x72, - // indicator: function = 0 - 0x0, - }) - require.Error(t, err) - assert.Equal(t, - InvalidImportError{ - Index: 0, - ReadError: InvalidImportSectionTypeIndexError{ - Offset: 15, - ReadError: io.EOF, - }, - }, - err, - ) - assert.Nil(t, funcTypes) - }) -} - -func TestWASMReader_readFunctionSection(t *testing.T) { - - t.Parallel() - - read := func(data []byte) ([]*Function, error) { - b := Buffer{data: data} - r := NewWASMReader(&b) - err := r.readFunctionSection() - if err != nil { - return nil, err - } - require.Equal(t, offset(len(b.data)), b.offset) - return r.Module.Functions, nil - } - - t.Run("valid", func(t *testing.T) { - - t.Parallel() - - typeIDs, err := read([]byte{ - // section size: 2 (LEB128) - 0x82, 0x80, 0x80, 0x80, 0x0, - // function count: 1 - 0x1, - // type index of function: 0x23 - 0x23, - }) - require.NoError(t, err) - assert.Equal(t, - []*Function{ - { - TypeIndex: 0x23, - }, - }, - typeIDs, - ) - }) - - t.Run("invalid size", func(t *testing.T) { - - t.Parallel() - - funcTypes, err := read([]byte{ - 0x87, 0x80, 0x80, - }) - require.Error(t, err) - assert.Equal(t, - InvalidSectionSizeError{ - Offset: 0, - ReadError: io.EOF, - }, - err, - ) - assert.Nil(t, funcTypes) - }) - - t.Run("invalid count", func(t *testing.T) { - - t.Parallel() - - funcTypes, err := read([]byte{ - // section size: 0 (LEB128) - 0x80, 0x80, 0x80, 0x80, 0x0, - }) - require.Error(t, err) - assert.Equal(t, - InvalidFunctionSectionFunctionCountError{ - Offset: 5, - ReadError: io.EOF, - }, - err, - ) - assert.Nil(t, funcTypes) - }) - - t.Run("invalid function type ID", func(t *testing.T) { - - t.Parallel() - - funcTypes, err := read([]byte{ - // section size: 1 (LEB128) - 0x81, 0x80, 0x80, 0x80, 0x0, - // function count - 0x1, - }) - require.Error(t, err) - assert.Equal(t, - InvalidFunctionSectionTypeIndexError{ - Offset: 6, - Index: 0, - ReadError: io.EOF, - }, - err, - ) - assert.Nil(t, funcTypes) - }) -} - -func TestWASMReader_readMemorySection(t *testing.T) { - - t.Parallel() - - read := func(data []byte) ([]*Memory, error) { - b := Buffer{data: data} - r := NewWASMReader(&b) - err := r.readMemorySection() - if err != nil { - return nil, err - } - require.Equal(t, offset(len(b.data)), b.offset) - return r.Module.Memories, nil - } - - t.Run("valid", func(t *testing.T) { - - t.Parallel() - - codes, err := read([]byte{ - // section size: 6 (LEB128) - 0x86, 0x80, 0x80, 0x80, 0x0, - // memory count: 2 - 0x2, - // memory type / limit: no max - 0x0, - // limit 1 min - 0x0, - // memory type / limit: max - 0x1, - // limit 2 min - 0x1, - // limit 2 max - 0x2, - }) - require.NoError(t, err) - assert.Equal(t, - []*Memory{ - { - Min: 0, - Max: nil, - }, - { - Min: 1, - Max: func() *uint32 { - var max uint32 = 2 - return &max - }(), - }, - }, - codes, - ) - }) - - t.Run("invalid size", func(t *testing.T) { - - t.Parallel() - - segments, err := read([]byte{ - 0x87, 0x80, 0x80, - }) - require.Error(t, err) - assert.Equal(t, - InvalidSectionSizeError{ - Offset: 0, - ReadError: io.EOF, - }, - err, - ) - assert.Nil(t, segments) - }) - - t.Run("invalid count", func(t *testing.T) { - - t.Parallel() - - segments, err := read([]byte{ - // section size: 7 (LEB128) - 0x80, 0x80, 0x80, 0x80, 0x0, - }) - require.Error(t, err) - assert.Equal(t, - InvalidMemorySectionMemoryCountError{ - Offset: 5, - ReadError: io.EOF, - }, - err, - ) - assert.Nil(t, segments) - }) - - t.Run("missing limit indicator", func(t *testing.T) { - - t.Parallel() - - segments, err := read([]byte{ - // section size: 10 (LEB128) - 0x8a, 0x80, 0x80, 0x80, 0x0, - // segment count - 0x1, - }) - require.Error(t, err) - assert.Equal(t, - InvalidMemoryError{ - Index: 0, - ReadError: InvalidLimitIndicatorError{ - Offset: 6, - ReadError: io.EOF, - }, - }, - err, - ) - assert.Nil(t, segments) - }) - - t.Run("invalid limit indicator", func(t *testing.T) { - - t.Parallel() - - segments, err := read([]byte{ - // section size: 10 (LEB128) - 0x8a, 0x80, 0x80, 0x80, 0x0, - // segment count - 0x1, - // limit indicator - 0xFF, - }) - require.Error(t, err) - assert.Equal(t, - InvalidMemoryError{ - Index: 0, - ReadError: InvalidLimitIndicatorError{ - Offset: 6, - LimitIndicator: 0xFF, - }, - }, - err, - ) - assert.Nil(t, segments) - }) - - t.Run("invalid min", func(t *testing.T) { - - t.Parallel() - - segments, err := read([]byte{ - // section size: 10 (LEB128) - 0x8a, 0x80, 0x80, 0x80, 0x0, - // segment count - 0x1, - // limit indicator: no max = 0x0 - 0x0, - }) - require.Error(t, err) - assert.Equal(t, - InvalidMemoryError{ - Index: 0, - ReadError: InvalidLimitMinError{ - Offset: 7, - ReadError: io.EOF, - }, - }, - err, - ) - assert.Nil(t, segments) - }) - - t.Run("invalid max", func(t *testing.T) { - - t.Parallel() - - segments, err := read([]byte{ - // section size: 10 (LEB128) - 0x8a, 0x80, 0x80, 0x80, 0x0, - // segment count - 0x1, - // limit indicator: max = 0x1 - 0x1, - // min - 0x1, - }) - require.Error(t, err) - assert.Equal(t, - InvalidMemoryError{ - Index: 0, - ReadError: InvalidLimitMaxError{ - Offset: 8, - ReadError: io.EOF, - }, - }, - err, - ) - assert.Nil(t, segments) - }) -} - -func TestWASMReader_readExportSection(t *testing.T) { - - t.Parallel() - - read := func(data []byte) ([]*Export, error) { - b := Buffer{data: data} - r := NewWASMReader(&b) - err := r.readExportSection() - if err != nil { - return nil, err - } - require.Equal(t, offset(len(b.data)), b.offset) - return r.Module.Exports, nil - } - - t.Run("valid", func(t *testing.T) { - - t.Parallel() - - typeIndices, err := read([]byte{ - // section size: 7 (LEB128) - 0x87, 0x80, 0x80, 0x80, 0x0, - // export count: 1 - 0x1, - // name length - 0x3, - // name = "foo" - 0x66, 0x6f, 0x6f, - // indicator: function = 0 - 0x0, - // index of function: 0 - 0x1, - }) - require.NoError(t, err) - assert.Equal(t, - []*Export{ - { - Name: "foo", - Descriptor: FunctionExport{ - FunctionIndex: 1, - }, - }, - }, - typeIndices, - ) - }) - - t.Run("invalid size", func(t *testing.T) { - - t.Parallel() - - funcTypes, err := read([]byte{ - 0x80, 0x80, 0x80, - }) - require.Error(t, err) - assert.Equal(t, - InvalidSectionSizeError{ - Offset: 0, - ReadError: io.EOF, - }, - err, - ) - assert.Nil(t, funcTypes) - }) - - t.Run("invalid count", func(t *testing.T) { - - t.Parallel() - - funcTypes, err := read([]byte{ - // section size: 0 (LEB128) - 0x80, 0x80, 0x80, 0x80, 0x0, - }) - require.Error(t, err) - assert.Equal(t, - InvalidExportSectionExportCountError{ - Offset: 5, - ReadError: io.EOF, - }, - err, - ) - assert.Nil(t, funcTypes) - }) - - t.Run("invalid name", func(t *testing.T) { - - t.Parallel() - - funcTypes, err := read([]byte{ - // section size: 1 (LEB128) - 0x81, 0x80, 0x80, 0x80, 0x0, - // export count - 0x1, - }) - require.Error(t, err) - assert.Equal(t, - InvalidExportError{ - Index: 0, - ReadError: InvalidNameLengthError{ - Offset: 6, - ReadError: io.EOF, - }, - }, - err, - ) - assert.Nil(t, funcTypes) - }) - - t.Run("missing indicator", func(t *testing.T) { - - t.Parallel() - - funcTypes, err := read([]byte{ - // section size: 5 (LEB128) - 0x85, 0x80, 0x80, 0x80, 0x0, - // export count - 0x1, - // name length - 0x3, - // name = "foo" - 0x66, 0x6f, 0x6f, - }) - require.Error(t, err) - assert.Equal(t, - InvalidExportError{ - Index: 0, - ReadError: InvalidExportIndicatorError{ - Offset: 10, - ReadError: io.EOF, - }, - }, - err, - ) - assert.Nil(t, funcTypes) - }) - - t.Run("invalid indicator", func(t *testing.T) { - - t.Parallel() - - funcTypes, err := read([]byte{ - // section size: 6 (LEB128) - 0x86, 0x80, 0x80, 0x80, 0x0, - // import count - 0x1, - // name length - 0x3, - // name = "foo" - 0x66, 0x6f, 0x6f, - // indicator: invalid - 0xFF, - // index - 0x1, - }) - require.Error(t, err) - assert.Equal(t, - InvalidExportError{ - Index: 0, - ReadError: InvalidExportIndicatorError{ - Offset: 10, - ExportIndicator: 0xFF, - ReadError: nil, - }, - }, - err, - ) - assert.Nil(t, funcTypes) - }) - - t.Run("invalid function index", func(t *testing.T) { - - t.Parallel() - - funcTypes, err := read([]byte{ - // section size: 6 (LEB128) - 0x86, 0x80, 0x80, 0x80, 0x0, - // import count - 0x1, - // name length - 0x3, - // name = "bar" - 0x62, 0x61, 0x72, - // indicator: function = 0 - 0x0, - }) - require.Error(t, err) - assert.Equal(t, - InvalidExportError{ - Index: 0, - ReadError: InvalidExportSectionIndexError{ - Offset: 11, - ReadError: io.EOF, - }, - }, - err, - ) - assert.Nil(t, funcTypes) - }) -} - -func TestWASMReader_readStartSection(t *testing.T) { - - t.Parallel() - - read := func(data []byte) (*uint32, error) { - b := Buffer{data: data} - r := NewWASMReader(&b) - err := r.readStartSection() - if err != nil { - return nil, err - } - require.Equal(t, offset(len(b.data)), b.offset) - return r.Module.StartFunctionIndex, nil - } - - t.Run("valid", func(t *testing.T) { - - t.Parallel() - - typeIDs, err := read([]byte{ - // section size: 1 (LEB128) - 0x81, 0x80, 0x80, 0x80, 0x0, - // function index: 1 - 0x1, - }) - require.NoError(t, err) - assert.Equal(t, - func() *uint32 { - var funcIndex uint32 = 1 - return &funcIndex - }(), - typeIDs, - ) - }) - - t.Run("invalid size", func(t *testing.T) { - - t.Parallel() - - funcTypes, err := read([]byte{ - 0x87, 0x80, 0x80, - }) - require.Error(t, err) - assert.Equal(t, - InvalidSectionSizeError{ - Offset: 0, - ReadError: io.EOF, - }, - err, - ) - assert.Nil(t, funcTypes) - }) - - t.Run("invalid function type ID", func(t *testing.T) { - - t.Parallel() - - funcTypes, err := read([]byte{ - // section size: 7 (LEB128) - 0x81, 0x80, 0x80, 0x80, 0x0, - }) - require.Error(t, err) - assert.Equal(t, - InvalidStartSectionFunctionIndexError{ - Offset: 5, - ReadError: io.EOF, - }, - err, - ) - assert.Nil(t, funcTypes) - }) -} - -func TestWASMReader_readCodeSection(t *testing.T) { - - t.Parallel() - - read := func(data []byte) ([]*Function, error) { - b := Buffer{data: data} - r := NewWASMReader(&b) - err := r.readCodeSection() - if err != nil { - return nil, err - } - require.Equal(t, offset(len(b.data)), b.offset) - return r.Module.Functions, nil - } - - t.Run("valid", func(t *testing.T) { - - t.Parallel() - - codes, err := read([]byte{ - // section size: 15 (LEB128) - 0x8f, 0x80, 0x80, 0x80, 0x0, - // function count: 1 - 0x1, - // code size: 9 (LEB128) - 0x89, 0x80, 0x80, 0x80, 0x0, - // number of locals: 1 - 0x1, - // number of locals with this type: 1 - 0x1, - // local type: i32 - 0x7f, - // opcode: local.get, 0 - 0x20, 0x0, - // opcode: local.get 1 - 0x20, 0x1, - // opcode: i32.add - 0x6a, - // opcode: end - 0xb, - }) - require.NoError(t, err) - assert.Equal(t, - []*Function{ - { - Code: &Code{ - Locals: []ValueType{ - ValueTypeI32, - }, - Instructions: []Instruction{ - InstructionLocalGet{LocalIndex: 0}, - InstructionLocalGet{LocalIndex: 1}, - InstructionI32Add{}, - }, - }, - }, - }, - codes, - ) - }) - - t.Run("invalid size", func(t *testing.T) { - - t.Parallel() - - funcTypes, err := read([]byte{ - 0x87, 0x80, 0x80, - }) - require.Error(t, err) - assert.Equal(t, - InvalidSectionSizeError{ - Offset: 0, - ReadError: io.EOF, - }, - err, - ) - assert.Nil(t, funcTypes) - }) - - t.Run("invalid count", func(t *testing.T) { - - t.Parallel() - - funcTypes, err := read([]byte{ - // section size: 0 (LEB128) - 0x80, 0x80, 0x80, 0x80, 0x0, - }) - require.Error(t, err) - assert.Equal(t, - InvalidCodeSectionFunctionCountError{ - Offset: 5, - ReadError: io.EOF, - }, - err, - ) - assert.Nil(t, funcTypes) - }) - - t.Run("invalid code size", func(t *testing.T) { - - t.Parallel() - - funcTypes, err := read([]byte{ - // section size: 1 (LEB128) - 0x81, 0x80, 0x80, 0x80, 0x0, - // function count: 1 - 0x1, - }) - require.Error(t, err) - assert.Equal(t, - InvalidFunctionCodeError{ - Index: 0, - ReadError: InvalidCodeSizeError{ - Offset: 6, - ReadError: io.EOF, - }, - }, - err, - ) - assert.Nil(t, funcTypes) - }) - - t.Run("invalid locals count", func(t *testing.T) { - - t.Parallel() - - funcTypes, err := read([]byte{ - // section size: 6 (LEB128) - 0x86, 0x80, 0x80, 0x80, 0x0, - // function count: 1 - 0x1, - // code size: 9 (LEB128) - 0x89, 0x80, 0x80, 0x80, 0x0, - }) - require.Error(t, err) - assert.Equal(t, - InvalidFunctionCodeError{ - Index: 0, - ReadError: InvalidCodeSectionLocalsCountError{ - Offset: 11, - ReadError: io.EOF, - }, - }, - err, - ) - assert.Nil(t, funcTypes) - }) - - t.Run("invalid compressed locals count", func(t *testing.T) { - - t.Parallel() - - funcTypes, err := read([]byte{ - // section size: 7 (LEB128) - 0x87, 0x80, 0x80, 0x80, 0x0, - // function count: 1 - 0x1, - // code size: 9 (LEB128) - 0x89, 0x80, 0x80, 0x80, 0x0, - // number of locals: 1 - 0x1, - }) - require.Error(t, err) - assert.Equal(t, - InvalidFunctionCodeError{ - Index: 0, - ReadError: InvalidCodeSectionCompressedLocalsCountError{ - Offset: 12, - ReadError: io.EOF, - }, - }, - err, - ) - assert.Nil(t, funcTypes) - }) - - t.Run("invalid local type", func(t *testing.T) { - - t.Parallel() - - funcTypes, err := read([]byte{ - // section size: 8 (LEB128) - 0x88, 0x80, 0x80, 0x80, 0x0, - // function count: 1 - 0x1, - // code size: 9 (LEB128) - 0x89, 0x80, 0x80, 0x80, 0x0, - // number of locals: 1 - 0x1, - // number of locals with this type: 1 - 0x1, - }) - require.Error(t, err) - assert.Equal(t, - InvalidFunctionCodeError{ - Index: 0, - ReadError: InvalidCodeSectionLocalTypeError{ - Offset: 13, - ReadError: InvalidValTypeError{ - Offset: 13, - ValType: 0, - ReadError: io.EOF, - }, - }, - }, - err, - ) - assert.Nil(t, funcTypes) - }) - - t.Run("invalid instruction", func(t *testing.T) { - - t.Parallel() - - funcTypes, err := read([]byte{ - // section size: 10 (LEB128) - 0x8a, 0x80, 0x80, 0x80, 0x0, - // function count: 1 - 0x1, - // code size: 9 (LEB128) - 0x89, 0x80, 0x80, 0x80, 0x0, - // number of locals: 1 - 0x1, - // number of locals with this type: 1 - 0x1, - // local type: i32 - 0x7f, - // invalid opcode - 0xff, - }) - require.Error(t, err) - assert.Equal(t, - InvalidFunctionCodeError{ - Index: 0, - ReadError: InvalidOpcodeError{ - Offset: 14, - Opcode: 0xff, - ReadError: nil, - }, - }, - err, - ) - assert.Nil(t, funcTypes) - }) - - t.Run("missing end", func(t *testing.T) { - - t.Parallel() - - funcTypes, err := read([]byte{ - // section size: 14 (LEB128) - 0x8e, 0x80, 0x80, 0x80, 0x0, - // function count: 1 - 0x1, - // code size: 9 (LEB128) - 0x89, 0x80, 0x80, 0x80, 0x0, - // number of locals: 1 - 0x1, - // number of locals with this type: 1 - 0x1, - // local type: i32 - 0x7f, - // opcode: local.get, 0 - 0x20, 0x0, - // opcode: local.get 1 - 0x20, 0x1, - // opcode: i32.add - 0x6a, - }) - require.Error(t, err) - assert.Equal(t, - InvalidFunctionCodeError{ - Index: 0, - ReadError: MissingEndInstructionError{ - Offset: 19, - }, - }, - err, - ) - assert.Nil(t, funcTypes) - }) -} - -func TestWASMReader_readDataSection(t *testing.T) { - - t.Parallel() - - read := func(data []byte) ([]*Data, error) { - b := Buffer{data: data} - r := NewWASMReader(&b) - err := r.readDataSection() - if err != nil { - return nil, err - } - require.Equal(t, offset(len(b.data)), b.offset) - return r.Module.Data, nil - } - - t.Run("valid", func(t *testing.T) { - - t.Parallel() - - codes, err := read([]byte{ - // section size: 9 (LEB128) - 0x89, 0x80, 0x80, 0x80, 0x0, - // segment count: 1 - 0x1, - // memory index - 0x1, - // i32.const 2 - 0x41, 0x2, - // end - 0xb, - // byte count - 0x3, - // init (bytes 0x3, 0x4, 0x5) - 0x3, 0x4, 0x5, - }) - require.NoError(t, err) - assert.Equal(t, - []*Data{ - { - MemoryIndex: 1, - Offset: []Instruction{ - InstructionI32Const{Value: 2}, - }, - Init: []byte{3, 4, 5}, - }, - }, - codes, - ) - }) - - t.Run("invalid size", func(t *testing.T) { - - t.Parallel() - - segments, err := read([]byte{ - 0x87, 0x80, 0x80, - }) - require.Error(t, err) - assert.Equal(t, - InvalidSectionSizeError{ - Offset: 0, - ReadError: io.EOF, - }, - err, - ) - assert.Nil(t, segments) - }) - - t.Run("invalid count", func(t *testing.T) { - - t.Parallel() - - segments, err := read([]byte{ - // section size: 7 (LEB128) - 0x80, 0x80, 0x80, 0x80, 0x0, - }) - require.Error(t, err) - assert.Equal(t, - InvalidDataSectionSegmentCountError{ - Offset: 5, - ReadError: io.EOF, - }, - err, - ) - assert.Nil(t, segments) - }) - - t.Run("invalid memory index", func(t *testing.T) { - - t.Parallel() - - segments, err := read([]byte{ - // section size: 10 (LEB128) - 0x8a, 0x80, 0x80, 0x80, 0x0, - // segment count - 0x1, - }) - require.Error(t, err) - assert.Equal(t, - InvalidDataSegmentError{ - Index: 0, - ReadError: InvalidDataSectionMemoryIndexError{ - Offset: 6, - ReadError: io.EOF, - }, - }, - err, - ) - assert.Nil(t, segments) - }) - - t.Run("invalid instruction", func(t *testing.T) { - - t.Parallel() - - segments, err := read([]byte{ - // section size: 10 (LEB128) - 0x8a, 0x80, 0x80, 0x80, 0x0, - // segment count - 0x1, - // memory index - 0x0, - // invalid opcode - 0xff, - }) - require.Error(t, err) - assert.Equal(t, - InvalidDataSegmentError{ - Index: 0, - ReadError: InvalidOpcodeError{ - Offset: 7, - Opcode: 0xff, - ReadError: nil, - }, - }, - err, - ) - assert.Nil(t, segments) - }) - - t.Run("missing end", func(t *testing.T) { - - t.Parallel() - - segments, err := read([]byte{ - // section size: 9 (LEB128) - 0x89, 0x80, 0x80, 0x80, 0x0, - // segment count: 1 - 0x1, - // memory index - 0x1, - // i32.const 2 - 0x41, 0x2, - }) - require.Error(t, err) - assert.Equal(t, - InvalidDataSegmentError{ - Index: 0, - ReadError: MissingEndInstructionError{ - Offset: 9, - }, - }, - err, - ) - assert.Nil(t, segments) - }) - - t.Run("invalid init byte count", func(t *testing.T) { - - t.Parallel() - - segments, err := read([]byte{ - // section size: 9 (LEB128) - 0x89, 0x80, 0x80, 0x80, 0x0, - // segment count: 1 - 0x1, - // memory index - 0x1, - // i32.const 2 - 0x41, 0x2, - // end - 0xb, - }) - require.Error(t, err) - assert.Equal(t, - InvalidDataSegmentError{ - Index: 0, - ReadError: InvalidDataSectionInitByteCountError{ - Offset: 10, - ReadError: io.EOF, - }, - }, - err, - ) - assert.Nil(t, segments) - }) - - t.Run("invalid init bytes", func(t *testing.T) { - - t.Parallel() - - segments, err := read([]byte{ - // section size: 9 (LEB128) - 0x89, 0x80, 0x80, 0x80, 0x0, - // segment count: 1 - 0x1, - // memory index - 0x1, - // i32.const 2 - 0x41, 0x2, - // end - 0xb, - // byte count - 0x2, - }) - require.Error(t, err) - assert.Equal(t, - InvalidDataSegmentError{ - Index: 0, - ReadError: io.EOF, - }, - err, - ) - assert.Nil(t, segments) - }) -} - -func TestWASMReader_readName(t *testing.T) { - - t.Parallel() - - read := func(data []byte) (string, error) { - b := Buffer{data: data} - r := NewWASMReader(&b) - name, err := r.readName() - if err != nil { - return "", err - } - require.Equal(t, offset(len(b.data)), b.offset) - return name, nil - } - - t.Run("valid", func(t *testing.T) { - - t.Parallel() - - name, err := read([]byte{ - // length - 0x5, - // "hello" - 0x68, 0x65, 0x6c, 0x6c, 0x6f, - }) - require.NoError(t, err) - - require.Equal(t, "hello", name) - }) - - t.Run("invalid length", func(t *testing.T) { - - t.Parallel() - - name, err := read(nil) - require.Error(t, err) - - assert.Equal(t, - InvalidNameLengthError{ - Offset: 0, - ReadError: io.EOF, - }, - err, - ) - assert.Empty(t, name) - }) - - t.Run("invalid name", func(t *testing.T) { - - t.Parallel() - - name, err := read([]byte{ - // length - 0x5, - // "he" - 0x68, 0x65, - }) - require.Error(t, err) - - assert.Equal(t, - IncompleteNameError{ - Offset: 1, - Expected: 5, - Actual: 2, - }, - err, - ) - assert.Empty(t, name) - }) - - t.Run("invalid non UTF-8", func(t *testing.T) { - - t.Parallel() - - name, err := read([]byte{ - // length - 0x3, - // name - 0xff, 0xfe, 0xfd, - }) - require.Error(t, err) - - assert.Equal(t, - InvalidNonUTF8NameError{ - Name: "\xff\xfe\xfd", - Offset: 1, - }, - err, - ) - assert.Empty(t, name) - }) -} - -func TestWASMReader_readInstruction(t *testing.T) { - - t.Parallel() - - t.Run("block, i32 result", func(t *testing.T) { - - t.Parallel() - - b := Buffer{ - data: []byte{ - // block - 0x02, - // i32 - 0x7f, - // i32.const - 0x41, - 0x01, - // end - 0x0b, - }, - offset: 0, - } - r := NewWASMReader(&b) - - expected := InstructionBlock{ - Block: Block{ - BlockType: ValueTypeI32, - Instructions1: []Instruction{ - InstructionI32Const{Value: 1}, - }, - Instructions2: nil, - }, - } - actual, err := r.readInstruction() - require.NoError(t, err) - - require.Equal(t, expected, actual) - require.Equal(t, offset(len(b.data)), b.offset) - }) - - t.Run("block, type index result", func(t *testing.T) { - - t.Parallel() - - b := Buffer{ - data: []byte{ - // block - 0x02, - // type index: 2 - 0x2, - // unreachable - 0x0, - // end - 0x0b, - }, - offset: 0, - } - r := NewWASMReader(&b) - - expected := InstructionBlock{ - Block: Block{ - BlockType: TypeIndexBlockType{TypeIndex: 2}, - Instructions1: []Instruction{ - InstructionUnreachable{}, - }, - Instructions2: nil, - }, - } - actual, err := r.readInstruction() - require.NoError(t, err) - - require.Equal(t, expected, actual) - require.Equal(t, offset(len(b.data)), b.offset) - }) - - t.Run("block, type index result, type index too large", func(t *testing.T) { - - t.Parallel() - - b := Buffer{ - data: []byte{ - // block - 0x02, - // type index: math.MaxUint32 + 1 - 0x80, 0x80, 0x80, 0x80, 0x10, - // unreachable - 0x0, - // end - 0x0b, - }, - offset: 0, - } - r := NewWASMReader(&b) - - _, err := r.readInstruction() - require.Equal(t, - InvalidBlockTypeTypeIndexError{ - TypeIndex: math.MaxUint32 + 1, - Offset: 1, - }, - err, - ) - }) - - t.Run("block, i32 result, second instructions", func(t *testing.T) { - - t.Parallel() - - b := Buffer{ - data: []byte{ - // block - 0x02, - // i32 - 0x7f, - // i32.const - 0x41, - 0x01, - // else - 0x05, - // i32.const - 0x41, - 0x02, - // end - 0x0b, - }, - offset: 0, - } - r := NewWASMReader(&b) - - _, err := r.readInstruction() - require.Equal(t, InvalidBlockSecondInstructionsError{ - Offset: 4, - }, err) - }) - - t.Run("loop, i32 result", func(t *testing.T) { - - t.Parallel() - - b := Buffer{ - data: []byte{ - // loop - 0x03, - // i32 - 0x7f, - // i32.const - 0x41, - 0x01, - // end - 0x0b, - }, - offset: 0, - } - r := NewWASMReader(&b) - - expected := InstructionLoop{ - Block: Block{ - BlockType: ValueTypeI32, - Instructions1: []Instruction{ - InstructionI32Const{Value: 1}, - }, - Instructions2: nil, - }, - } - actual, err := r.readInstruction() - require.NoError(t, err) - - require.Equal(t, expected, actual) - require.Equal(t, offset(len(b.data)), b.offset) - }) - - t.Run("loop, i32 result, second instructions", func(t *testing.T) { - - t.Parallel() - - b := Buffer{ - data: []byte{ - // loop - 0x03, - // i32 - 0x7f, - // i32.const - 0x41, - 0x01, - // else - 0x05, - // i32.const - 0x41, - 0x02, - // end - 0x0b, - }, - offset: 0, - } - r := NewWASMReader(&b) - - _, err := r.readInstruction() - require.Equal(t, InvalidBlockSecondInstructionsError{ - Offset: 4, - }, err) - }) - - t.Run("if, i32 result", func(t *testing.T) { - - t.Parallel() - - b := Buffer{ - data: []byte{ - // if - 0x04, - // i32 - 0x7f, - // i32.const - 0x41, - 0x01, - // end - 0x0b, - }, - offset: 0, - } - r := NewWASMReader(&b) - - expected := InstructionIf{ - Block: Block{ - BlockType: ValueTypeI32, - Instructions1: []Instruction{ - InstructionI32Const{Value: 1}, - }, - Instructions2: nil, - }, - } - actual, err := r.readInstruction() - require.NoError(t, err) - - require.Equal(t, expected, actual) - require.Equal(t, offset(len(b.data)), b.offset) - }) - - t.Run("if-else, i32 result", func(t *testing.T) { - - t.Parallel() - - b := Buffer{ - data: []byte{ - // loop - 0x04, - // i32 - 0x7f, - // i32.const - 0x41, - 0x01, - // else - 0x05, - // i32.const - 0x41, - 0x02, - // end - 0x0b, - }, - offset: 0, - } - r := NewWASMReader(&b) - - expected := InstructionIf{ - Block: Block{ - BlockType: ValueTypeI32, - Instructions1: []Instruction{ - InstructionI32Const{Value: 1}, - }, - Instructions2: []Instruction{ - InstructionI32Const{Value: 2}, - }, - }, - } - actual, err := r.readInstruction() - require.NoError(t, err) - - require.Equal(t, expected, actual) - require.Equal(t, offset(len(b.data)), b.offset) - }) - - t.Run("br_table", func(t *testing.T) { - - t.Parallel() - - b := Buffer{ - data: []byte{ - // br_table - 0x0e, - // number of branch depths - 0x04, - // 1. branch depth - 0x03, - // 2. branch depth - 0x02, - // 3. branch depth - 0x01, - // 4. branch depth - 0x00, - // default branch depth - 0x04, - }, - offset: 0, - } - r := NewWASMReader(&b) - - expected := InstructionBrTable{ - LabelIndices: []uint32{3, 2, 1, 0}, - DefaultLabelIndex: 4, - } - actual, err := r.readInstruction() - require.NoError(t, err) - - require.Equal(t, expected, actual) - require.Equal(t, offset(len(b.data)), b.offset) - }) -} - -func TestWASMReader_readNameSection(t *testing.T) { - - t.Parallel() - - b := Buffer{ - data: []byte{ - // section size: 37 (LEB128) - 0xa5, 0x80, 0x80, 0x80, 0x0, - // name length - 0x4, - // name = "name" - 0x6e, 0x61, 0x6d, 0x65, - // sub-section ID: module name = 0 - 0x0, - // sub-section size: 5 (LEB128) - 0x85, 0x80, 0x80, 0x80, 0x0, - // name length - 0x4, - // name = "test" - 0x74, 0x65, 0x73, 0x74, - // sub-section ID: function names = 1 - 0x1, - // sub-section size: 15 (LEB128) - 0x8f, 0x80, 0x80, 0x80, 0x0, - // name count - 0x2, - // function index = 0 - 0x0, - // name length - 0x7, - // name = "foo.bar" - 0x66, 0x6f, 0x6f, 0x2e, 0x62, 0x61, 0x72, - // function index = 1 - 0x1, - // name length - 0x3, - // name = "add" - 0x61, 0x64, 0x64, - }, - offset: 0, - } - - r := NewWASMReader(&b) - - err := r.readCustomSection() - require.NoError(t, err) - - require.Equal(t, offset(len(b.data)), b.offset) -} diff --git a/compiler/wasm/section.go b/compiler/wasm/section.go deleted file mode 100644 index e6609d33a1..0000000000 --- a/compiler/wasm/section.go +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Cadence - The resource-oriented smart contract programming language - * - * Copyright Flow Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package wasm - -// sectionID is the ID of a section in the WASM binary. -// -// See https://webassembly.github.io/spec/core/binary/modules.html#sections: -// -// The following section ids are used: -// -// 0 = custom section -// 1 = type section -// 2 = import section -// 3 = function section -// 4 = table section -// 5 = memory section -// 6 = global section -// 7 = export section -// 8 = start section -// 9 = element section -// 10 = code section -// 11 = data section -type sectionID byte - -const ( - sectionIDCustom sectionID = 0 - sectionIDType sectionID = 1 - sectionIDImport sectionID = 2 - sectionIDFunction sectionID = 3 - sectionIDMemory sectionID = 5 - sectionIDExport sectionID = 7 - sectionIDStart sectionID = 8 - sectionIDCode sectionID = 10 - sectionIDData sectionID = 11 -) diff --git a/compiler/wasm/valuetype.go b/compiler/wasm/valuetype.go deleted file mode 100644 index f80b21c788..0000000000 --- a/compiler/wasm/valuetype.go +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Cadence - The resource-oriented smart contract programming language - * - * Copyright Flow Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package wasm - -// ValueType is the type of a value -type ValueType byte - -const ( - // ValueTypeI32 is the `i32` type, - // the type of 32 bit integers. - // The value is the byte used in the WASM binary - ValueTypeI32 ValueType = 0x7F - - // ValueTypeI64 is the `i64` type, - // the type of 64 bit integers. - // The value is the byte used in the WASM binary - ValueTypeI64 ValueType = 0x7E - - // ValueTypeFuncRef is the `funcref` type, - // the type of first-class references to functions. - // The value is the byte used in the WASM binary - ValueTypeFuncRef ValueType = 0x70 - - // ValueTypeExternRef is the `funcref` type, - // the type of first-class references to objects owned by the embedder. - // The value is the byte used in the WASM binary - ValueTypeExternRef ValueType = 0x6F -) - -// AsValueType returns the value type for the given byte, -// or 0 if the byte is not a valid value type -func AsValueType(b byte) ValueType { - switch ValueType(b) { - case ValueTypeI32: - return ValueTypeI32 - - case ValueTypeI64: - return ValueTypeI64 - - case ValueTypeFuncRef: - return ValueTypeFuncRef - - case ValueTypeExternRef: - return ValueTypeExternRef - } - - return 0 -} - -func (ValueType) isBlockType() {} - -func (t ValueType) write(w *WASMWriter) error { - return w.buf.WriteByte(byte(t)) -} diff --git a/compiler/wasm/wasm.go b/compiler/wasm/wasm.go deleted file mode 100644 index 295f38da5b..0000000000 --- a/compiler/wasm/wasm.go +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Cadence - The resource-oriented smart contract programming language - * - * Copyright Flow Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// WebAssembly (https://webassembly.org/) is an open standard for portable executable programs. -// It is designed to be a portable compilation target for programming languages. -// -// The standard defines two formats for encoding WebAssembly programs ("modules"): -// -// - A machine-optimized binary format (WASM, https://webassembly.github.io/spec/core/binary/index.html), -// which is not designed to be used by humans -// -// - A human-readable text format (WAT, https://webassembly.github.io/spec/core/text/index.html) -// -// WebAssembly modules in either format can be converted into the other format. -// -// There exists also another textual format, WAST, which is a superset of WAT, -// but is not part of the official standard. -// -// Package wasm implements a representation of WebAssembly modules (Module) and related types, -// e.g. instructions (Instruction). -// -// Package wasm also implements a reader and writer for the binary format: -// -// - The reader (WASMReader) allows parsing a WebAssembly module in binary form ([]byte) -// into an representation of the module (Module). -// -// - The writer (WASMWriter) allows encoding the representation of the module (Module) -// to a WebAssembly program in binary form ([]byte). -// -// Package wasm does not currently provide a reader and writer for the textual format (WAT). -// -// Package wasm is not a compiler for Cadence programs, but rather a building block that allows -// reading and writing WebAssembly modules. -package wasm diff --git a/compiler/wasm/wasm2wat.go b/compiler/wasm/wasm2wat.go deleted file mode 100644 index 517cb217f4..0000000000 --- a/compiler/wasm/wasm2wat.go +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Cadence - The resource-oriented smart contract programming language - * - * Copyright Flow Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package wasm - -import ( - "fmt" - "os" - "os/exec" -) - -func WASM2WAT(binary []byte) string { - f, err := os.CreateTemp("", "wasm") - if err != nil { - panic(err) - } - - defer os.Remove(f.Name()) - - _, err = f.Write(binary) - if err != nil { - panic(err) - } - - err = f.Close() - if err != nil { - panic(err) - } - - cmd := exec.Command("wasm2wat", f.Name()) - out, err := cmd.Output() - if err != nil { - if ee, ok := err.(*exec.ExitError); ok { - panic(fmt.Errorf("wasm2wat failed: %w:\n%s", err, ee.Stderr)) - } else { - panic(fmt.Errorf("wasm2wat failed: %w", err)) - } - } - - return string(out) -} diff --git a/compiler/wasm/writer.go b/compiler/wasm/writer.go deleted file mode 100644 index 2ae17dd6ec..0000000000 --- a/compiler/wasm/writer.go +++ /dev/null @@ -1,714 +0,0 @@ -/* - * Cadence - The resource-oriented smart contract programming language - * - * Copyright Flow Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package wasm - -import ( - "fmt" - "unicode/utf8" -) - -// WASMWriter allows writing WASM binaries -type WASMWriter struct { - buf *Buffer - WriteNames bool -} - -func NewWASMWriter(buf *Buffer) *WASMWriter { - return &WASMWriter{ - buf: buf, - } -} - -// writeMagicAndVersion writes the magic byte sequence and version at the beginning of the WASM binary -func (w *WASMWriter) writeMagicAndVersion() error { - err := w.buf.WriteBytes(wasmMagic) - if err != nil { - return err - } - return w.buf.WriteBytes(wasmVersion) -} - -// writeSection writes a section in the WASM binary, with the given section ID and the given content. -// The content is a function that writes the contents of the section. -func (w *WASMWriter) writeSection(sectionID sectionID, content func() error) error { - // write the section ID - err := w.buf.WriteByte(byte(sectionID)) - if err != nil { - return err - } - - // write the size and the content - return w.writeContentWithSize(content) -} - -// writeContentWithSize writes the size of the content, -// and the content itself -func (w *WASMWriter) writeContentWithSize(content func() error) error { - - // write the temporary placeholder for the size - sizeOffset, err := w.buf.writeFixedUint32LEB128Space() - if err != nil { - return err - } - - // write the content - err = content() - if err != nil { - return err - } - - // write the actual size into the size placeholder - return w.buf.writeUint32LEB128SizeAt(sizeOffset) -} - -// writeCustomSection writes a custom section with the given name and content. -// The content is a function that writes the contents of the section. -func (w *WASMWriter) writeCustomSection(name string, content func() error) error { - return w.writeSection(sectionIDCustom, func() error { - err := w.writeName(name) - if err != nil { - return err - } - - return content() - }) -} - -// writeTypeSection writes the section that declares all function types -// so they can be referenced by index -func (w *WASMWriter) writeTypeSection(funcTypes []*FunctionType) error { - return w.writeSection(sectionIDType, func() error { - - // write the number of types - err := w.buf.writeUint32LEB128(uint32(len(funcTypes))) - if err != nil { - return err - } - - // write each type - for _, funcType := range funcTypes { - err = w.writeFuncType(funcType) - if err != nil { - return err - } - } - - return nil - }) -} - -// writeFuncType writes the function type -func (w *WASMWriter) writeFuncType(funcType *FunctionType) error { - // write the type - err := w.buf.WriteByte(functionTypeIndicator) - if err != nil { - return err - } - - // write the number of parameters - err = w.buf.writeUint32LEB128(uint32(len(funcType.Params))) - if err != nil { - return err - } - - // write the type of each parameter - for _, paramType := range funcType.Params { - err = w.buf.WriteByte(byte(paramType)) - if err != nil { - return err - } - } - - // write the number of results - err = w.buf.writeUint32LEB128(uint32(len(funcType.Results))) - if err != nil { - return err - } - - // write the type of each result - for _, resultType := range funcType.Results { - err = w.buf.WriteByte(byte(resultType)) - if err != nil { - return err - } - } - - return nil -} - -// writeImportSection writes the section that declares all imports -func (w *WASMWriter) writeImportSection(imports []*Import) error { - return w.writeSection(sectionIDImport, func() error { - - // write the number of imports - err := w.buf.writeUint32LEB128(uint32(len(imports))) - if err != nil { - return err - } - - // write each import - for _, im := range imports { - err = w.writeImport(im) - if err != nil { - return err - } - } - - return nil - }) -} - -// writeImport writes the import -func (w *WASMWriter) writeImport(im *Import) error { - // write the module - err := w.writeName(im.Module) - if err != nil { - return err - } - - // write the name - err = w.writeName(im.Name) - if err != nil { - return err - } - - // TODO: add support for tables, memories, and globals. adjust name section! - // write the type indicator - err = w.buf.WriteByte(byte(importIndicatorFunction)) - if err != nil { - return err - } - - // write the type index - return w.buf.writeUint32LEB128(im.TypeIndex) -} - -// writeFunctionSection writes the section that declares the types of functions. -// The bodies of these functions will later be provided in the code section -func (w *WASMWriter) writeFunctionSection(functions []*Function) error { - return w.writeSection(sectionIDFunction, func() error { - // write the number of functions - err := w.buf.writeUint32LEB128(uint32(len(functions))) - if err != nil { - return err - } - - // write the type index for each function - for _, function := range functions { - err = w.buf.writeUint32LEB128(function.TypeIndex) - if err != nil { - return err - } - } - - return nil - }) -} - -// writeMemorySection writes the section that declares all memories -func (w *WASMWriter) writeMemorySection(memories []*Memory) error { - return w.writeSection(sectionIDMemory, func() error { - - // write the number of memories - err := w.buf.writeUint32LEB128(uint32(len(memories))) - if err != nil { - return err - } - - // write each memory - for _, memory := range memories { - err = w.writeMemory(memory) - if err != nil { - return err - } - } - - return nil - }) -} - -// writeMemory writes the memory -func (w *WASMWriter) writeMemory(memory *Memory) error { - return w.writeLimit(memory.Max, memory.Min) -} - -func (w *WASMWriter) writeLimit(max *uint32, min uint32) error { - - // write the indicator - var indicator = limitIndicatorNoMax - if max != nil { - indicator = limitIndicatorMax - } - - err := w.buf.WriteByte(byte(indicator)) - if err != nil { - return err - } - - // write the minimum - err = w.buf.writeUint32LEB128(min) - if err != nil { - return err - } - - // write the maximum - if max != nil { - err := w.buf.writeUint32LEB128(*max) - if err != nil { - return err - } - } - - return nil -} - -// writeExportSection writes the section that declares all exports -func (w *WASMWriter) writeExportSection(exports []*Export) error { - return w.writeSection(sectionIDExport, func() error { - - // write the number of exports - err := w.buf.writeUint32LEB128(uint32(len(exports))) - if err != nil { - return err - } - - // write each export - for _, export := range exports { - err = w.writeExport(export) - if err != nil { - return err - } - } - - return nil - }) -} - -// writeExport writes the export -func (w *WASMWriter) writeExport(export *Export) error { - - // write the name - err := w.writeName(export.Name) - if err != nil { - return err - } - - // TODO: add support for tables and globals. adjust name section! - - var indicator exportIndicator - var index uint32 - - switch descriptor := export.Descriptor.(type) { - case FunctionExport: - indicator = exportIndicatorFunction - index = descriptor.FunctionIndex - case MemoryExport: - indicator = exportIndicatorMemory - index = descriptor.MemoryIndex - default: - return fmt.Errorf("unsupported export descripor: %#+v", descriptor) - } - - // write the indicator - err = w.buf.WriteByte(byte(indicator)) - if err != nil { - return err - } - - // write the index - return w.buf.writeUint32LEB128(index) -} - -// writeStartSection writes the section that declares the start function -func (w *WASMWriter) writeStartSection(funcIndex uint32) error { - return w.writeSection(sectionIDStart, func() error { - // write the index of the start function - return w.buf.writeUint32LEB128(funcIndex) - }) -} - -// writeCodeSection writes the section that provides the function bodies for the functions -// declared by the function section (which only provides the function types) -func (w *WASMWriter) writeCodeSection(functions []*Function) error { - return w.writeSection(sectionIDCode, func() error { - // write the number of code entries (one for each function) - err := w.buf.writeUint32LEB128(uint32(len(functions))) - if err != nil { - return err - } - - // write the code for each function - for _, function := range functions { - - err := w.writeFunctionBody(function.Code) - if err != nil { - return err - } - } - - return nil - }) -} - -// writeFunctionBody writes the body of the function -func (w *WASMWriter) writeFunctionBody(code *Code) error { - return w.writeContentWithSize(func() error { - - // write the number of locals - err := w.buf.writeUint32LEB128(uint32(len(code.Locals))) - if err != nil { - return err - } - - // TODO: run-length encode - // write each local - for _, localValType := range code.Locals { - err = w.buf.writeUint32LEB128(1) - if err != nil { - return err - } - - err = w.buf.WriteByte(byte(localValType)) - if err != nil { - return err - } - } - - err = w.writeInstructions(code.Instructions) - if err != nil { - return err - } - - return w.writeOpcode(opcodeEnd) - }) -} - -// writeInstructions writes an instruction sequence -func (w *WASMWriter) writeInstructions(instructions []Instruction) error { - for _, instruction := range instructions { - err := instruction.write(w) - if err != nil { - return err - } - } - return nil -} - -// writeOpcode writes the opcode of an instruction -func (w *WASMWriter) writeOpcode(opcodes ...opcode) error { - for _, b := range opcodes { - err := w.buf.WriteByte(byte(b)) - if err != nil { - return err - } - } - return nil -} - -// writeName writes a name, a UTF-8 byte sequence -func (w *WASMWriter) writeName(name string) error { - - // ensure the name is valid UTF-8 - if !utf8.ValidString(name) { - return InvalidNonUTF8NameError{ - Name: name, - Offset: int(w.buf.offset), - } - } - - // write the length - err := w.buf.writeUint32LEB128(uint32(len(name))) - if err != nil { - return err - } - - // write the name - return w.buf.WriteBytes([]byte(name)) -} - -// writeBlockInstructionArgument writes a block instruction argument -func (w *WASMWriter) writeBlockInstructionArgument(block Block, allowElse bool) error { - - // write the block type - if block.BlockType != nil { - err := block.BlockType.write(w) - if err != nil { - return err - } - } else { - err := w.buf.WriteByte(emptyBlockType) - if err != nil { - return err - } - } - - // write the first sequence of instructions - err := w.writeInstructions(block.Instructions1) - if err != nil { - return err - } - - // write the second sequence of instructions. - // in an if-instruction, this is the else branch. - // in other instructions, it is not allowed. - - if len(block.Instructions2) > 0 { - if !allowElse { - return InvalidBlockSecondInstructionsError{ - Offset: int(w.buf.offset), - } - } - - err := w.writeOpcode(opcodeElse) - if err != nil { - return err - } - - err = w.writeInstructions(block.Instructions2) - if err != nil { - return err - } - } - - // write the implicit end instruction / opcode - - return InstructionEnd{}.write(w) -} - -const customSectionNameName = "name" - -// writeNameSection writes the section which declares -// the names of the module, functions, and locals -func (w *WASMWriter) writeNameSection(moduleName string, imports []*Import, functions []*Function) error { - return w.writeCustomSection(customSectionNameName, func() error { - - // write the module name sub-section - err := w.writeNameSectionModuleNameSubSection(moduleName) - if err != nil { - return err - } - - // write the function names sub-section - return w.writeNameSectionFunctionNamesSubSection(imports, functions) - }) -} - -// nameSubSectionID is the ID of a sub-section in the name section of the WASM binary -type nameSubSectionID byte - -const ( - nameSubSectionIDModuleName nameSubSectionID = 0 - nameSubSectionIDFunctionNames nameSubSectionID = 1 - // TODO: - //nameSubSectionIDLocalNames nameSubSectionID = 2 -) - -// writeNameSubSection writes a sub-section in the name section of the WASM binary, -// with the given sub-section ID and the given content. -// The content is a function that writes the contents of the section. -func (w *WASMWriter) writeNameSubSection(nameSubSectionID nameSubSectionID, content func() error) error { - // write the name sub-section ID - err := w.buf.WriteByte(byte(nameSubSectionID)) - if err != nil { - return err - } - - // write the size and the content - return w.writeContentWithSize(content) -} - -// writeNameSectionModuleName writes the module name sub-section in the name section of the WASM binary -func (w *WASMWriter) writeNameSectionModuleNameSubSection(moduleName string) error { - return w.writeNameSubSection(nameSubSectionIDModuleName, func() error { - return w.writeName(moduleName) - }) -} - -// writeNameSectionFunctionNames writes the module name sub-section in the name section of the WASM binary -func (w *WASMWriter) writeNameSectionFunctionNamesSubSection(imports []*Import, functions []*Function) error { - return w.writeNameSubSection(nameSubSectionIDFunctionNames, func() error { - - // write the number of function names - count := len(imports) + len(functions) - - err := w.buf.writeUint32LEB128(uint32(count)) - if err != nil { - return err - } - - // write the name map entries for the imports - - var index uint32 - - for _, imp := range imports { - - // write the index - err := w.buf.writeUint32LEB128(index) - if err != nil { - return err - } - - index++ - - // write the name - - err = w.writeName(imp.FullName()) - if err != nil { - return err - } - } - - // write the name map entries for the functions - - for _, function := range functions { - - // write the index - err := w.buf.writeUint32LEB128(index) - if err != nil { - return err - } - - index++ - - // write the name - - err = w.writeName(function.Name) - if err != nil { - return err - } - } - - return nil - }) -} - -// writeDataSection writes the section that declares the data segments -func (w *WASMWriter) writeDataSection(segments []*Data) error { - return w.writeSection(sectionIDData, func() error { - // write the number of data segments - err := w.buf.writeUint32LEB128(uint32(len(segments))) - if err != nil { - return err - } - - // write each data segment - for _, segment := range segments { - err = w.writeDataSegment(segment) - if err != nil { - return err - } - } - - return nil - }) -} - -// writeDataSegment writes the data segment -func (w *WASMWriter) writeDataSegment(segment *Data) error { - - // write the memory index - err := w.buf.writeUint32LEB128(segment.MemoryIndex) - if err != nil { - return err - } - - // write the offset instructions - err = w.writeInstructions(segment.Offset) - if err != nil { - return err - } - - err = w.writeOpcode(opcodeEnd) - if err != nil { - return err - } - - // write the number of bytes - err = w.buf.writeUint32LEB128(uint32(len(segment.Init))) - if err != nil { - return err - } - - // write each byte - for _, b := range segment.Init { - err = w.buf.WriteByte(b) - if err != nil { - return err - } - } - - return nil -} - -func (w *WASMWriter) WriteModule(module *Module) error { - if err := w.writeMagicAndVersion(); err != nil { - return err - } - if len(module.Types) > 0 { - if err := w.writeTypeSection(module.Types); err != nil { - return err - } - } - if len(module.Imports) > 0 { - if err := w.writeImportSection(module.Imports); err != nil { - return err - } - } - if len(module.Functions) > 0 { - if err := w.writeFunctionSection(module.Functions); err != nil { - return err - } - } - if len(module.Memories) > 0 { - if err := w.writeMemorySection(module.Memories); err != nil { - return err - } - } - if len(module.Exports) > 0 { - if err := w.writeExportSection(module.Exports); err != nil { - return err - } - } - if module.StartFunctionIndex != nil { - if err := w.writeStartSection(*module.StartFunctionIndex); err != nil { - return err - } - } - if len(module.Functions) > 0 { - if err := w.writeCodeSection(module.Functions); err != nil { - return err - } - } - if len(module.Data) > 0 { - if err := w.writeDataSection(module.Data); err != nil { - return err - } - } - if w.WriteNames { - if err := w.writeNameSection( - module.Name, - module.Imports, - module.Functions, - ); err != nil { - return err - } - } - - return nil -} diff --git a/compiler/wasm/writer_test.go b/compiler/wasm/writer_test.go deleted file mode 100644 index 2b79997d25..0000000000 --- a/compiler/wasm/writer_test.go +++ /dev/null @@ -1,977 +0,0 @@ -/* - * Cadence - The resource-oriented smart contract programming language - * - * Copyright Flow Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package wasm - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestWASMWriter_writeMagicAndVersion(t *testing.T) { - - t.Parallel() - - var b Buffer - w := NewWASMWriter(&b) - - err := w.writeMagicAndVersion() - require.NoError(t, err) - - require.Equal(t, - []byte{ - // magic - 0x0, 0x61, 0x73, 0x6d, - // version - 0x1, 0x0, 0x0, 0x0, - }, - b.data, - ) -} - -func TestWASMWriter_writeTypeSection(t *testing.T) { - - t.Parallel() - - var b Buffer - w := NewWASMWriter(&b) - - err := w.writeTypeSection([]*FunctionType{ - { - Params: []ValueType{ValueTypeI32, ValueTypeI32}, - Results: []ValueType{ValueTypeI32}, - }, - }) - require.NoError(t, err) - - require.Equal(t, - []byte{ - // section ID: Type = 1 - 0x1, - // section size: 7 (LEB128) - 0x87, 0x80, 0x80, 0x80, 0x0, - // type count - 0x1, - // function type indicator - 0x60, - // parameter count: 2 - 0x2, - // type of parameter 1: i32 - 0x7f, - // type of parameter 2: i32 - 0x7f, - // return value count: 1 - 0x1, - // type of return value 1: i32 - 0x7f, - }, - b.data, - ) -} - -func TestWASMWriter_writeImportSection(t *testing.T) { - - t.Parallel() - - var b Buffer - w := NewWASMWriter(&b) - - imports := []*Import{ - { - Module: "foo", - Name: "bar", - TypeIndex: 1, - }, - } - - err := w.writeImportSection(imports) - require.NoError(t, err) - - require.Equal(t, - []byte{ - // section ID: Import = 2 - 0x2, - // section size: 11 (LEB128) - 0x8b, 0x80, 0x80, 0x80, 0x0, - // import count: 1 - 0x1, - // module length - 0x3, - // module = "foo" - 0x66, 0x6f, 0x6f, - // name length - 0x3, - // name = "bar" - 0x62, 0x61, 0x72, - // type indicator: function = 0 - 0x0, - // type index of function: 0 - 0x1, - }, - b.data, - ) -} - -func TestWASMWriter_writeFunctionSection(t *testing.T) { - - t.Parallel() - - var b Buffer - w := NewWASMWriter(&b) - - functions := []*Function{ - { - // not used, just for testing - Name: "add", - TypeIndex: 0, - // not used, just for testing - Code: &Code{ - Locals: []ValueType{ - ValueTypeI32, - }, - Instructions: []Instruction{ - InstructionLocalGet{LocalIndex: 0}, - InstructionLocalGet{LocalIndex: 1}, - InstructionI32Add{}, - }, - }, - }, - } - - err := w.writeFunctionSection(functions) - require.NoError(t, err) - - require.Equal(t, - []byte{ - // section ID: Function = 3 - 0x3, - // section size: 2 (LEB128) - 0x82, 0x80, 0x80, 0x80, 0x0, - // function count: 1 - 0x1, - // type index of function: 0 - 0x0, - }, - b.data, - ) -} - -func TestWASMWriter_writeMemorySection(t *testing.T) { - - t.Parallel() - - var b Buffer - w := NewWASMWriter(&b) - - memories := []*Memory{ - { - Min: 1024, - Max: nil, - }, - { - Min: 2048, - Max: func() *uint32 { - var max uint32 = 2 - return &max - }(), - }, - } - - err := w.writeMemorySection(memories) - require.NoError(t, err) - - require.Equal(t, - []byte{ - // section ID: Import = 5 - 0x5, - // section size: 8 (LEB128) - 0x88, 0x80, 0x80, 0x80, 0x0, - // memory count: 2 - 0x2, - // memory type / limit: no max - 0x0, - // limit 1 min: 1024 (LEB128) - 0x80, 0x8, - // memory type / limit: max - 0x1, - // limit 2 min: 2048 (LEB128) - 0x80, 0x10, - // limit 2 max - 0x2, - }, - b.data, - ) -} - -func TestWASMWriter_writeExportSection(t *testing.T) { - - t.Parallel() - - var b Buffer - w := NewWASMWriter(&b) - - exports := []*Export{ - { - Name: "foo", - Descriptor: FunctionExport{ - FunctionIndex: 1, - }, - }, - } - - err := w.writeExportSection(exports) - require.NoError(t, err) - - require.Equal(t, - []byte{ - // section ID: Export = 7 - 0x7, - // section size: 7 (LEB128) - 0x87, 0x80, 0x80, 0x80, 0x0, - // import count: 1 - 0x1, - // name length - 0x3, - // name = "foo" - 0x66, 0x6f, 0x6f, - // type indicator: function = 0 - 0x0, - // index of function: 1 - 0x1, - }, - b.data, - ) -} - -func TestWASMWriter_writeStartSection(t *testing.T) { - - t.Parallel() - - var b Buffer - w := NewWASMWriter(&b) - - err := w.writeStartSection(1) - require.NoError(t, err) - - require.Equal(t, - []byte{ - // Section ID: Code = 8 - 0x8, - // section size: 15 (LEB128) - 0x81, 0x80, 0x80, 0x80, 0x0, - // function index: 1 - 0x1, - }, - b.data, - ) -} - -func TestWASMWriter_writeCodeSection(t *testing.T) { - - t.Parallel() - - var b Buffer - w := NewWASMWriter(&b) - - functions := []*Function{ - { - // not used, just for testing - Name: "add", - // not used, just for testing - TypeIndex: 0, - Code: &Code{ - Locals: []ValueType{ - ValueTypeI32, - }, - Instructions: []Instruction{ - InstructionLocalGet{LocalIndex: 0}, - InstructionLocalGet{LocalIndex: 1}, - InstructionI32Add{}, - }, - }, - }, - } - - err := w.writeCodeSection(functions) - require.NoError(t, err) - - require.Equal(t, - []byte{ - // Section ID: Code = 10 - 0xa, - // section size: 15 (LEB128) - 0x8f, 0x80, 0x80, 0x80, 0x0, - // function count: 1 - 0x1, - // code size: 9 (LEB128) - 0x89, 0x80, 0x80, 0x80, 0x0, - // number of locals: 1 - 0x1, - // number of locals with this type: 1 - 0x1, - // local type: i32 - 0x7f, - // opcode: local.get, 0 - 0x20, 0x0, - // opcode: local.get 1 - 0x20, 0x1, - // opcode: i32.add - 0x6a, - // opcode: end - 0xb, - }, - b.data, - ) -} - -func TestWASMWriter_writeDataSection(t *testing.T) { - - t.Parallel() - - var b Buffer - w := NewWASMWriter(&b) - - dataSegments := []*Data{ - { - MemoryIndex: 1, - Offset: []Instruction{ - InstructionI32Const{Value: 2}, - }, - Init: []byte{3, 4, 5}, - }, - } - - err := w.writeDataSection(dataSegments) - require.NoError(t, err) - - require.Equal(t, - []byte{ - // section ID: Import = 11 - 0xB, - // section size: 9 (LEB128) - 0x89, 0x80, 0x80, 0x80, 0x0, - // segment count: 1 - 0x1, - // memory index - 0x1, - // i32.const 2 - 0x41, 0x2, - // end - 0xb, - // byte count - 0x3, - // init (bytes 0x3, 0x4, 0x5) - 0x3, 0x4, 0x5, - }, - b.data, - ) -} - -func TestWASMWriter_writeName(t *testing.T) { - - t.Parallel() - - t.Run("valid", func(t *testing.T) { - - t.Parallel() - - var b Buffer - w := NewWASMWriter(&b) - - err := w.writeName("hello") - require.NoError(t, err) - - require.Equal(t, - []byte{ - // length - 0x5, - // "hello" - 0x68, 0x65, 0x6c, 0x6c, 0x6f, - }, - b.data, - ) - }) - - t.Run("invalid", func(t *testing.T) { - - t.Parallel() - - var b Buffer - w := NewWASMWriter(&b) - - name := string([]byte{0xff, 0xfe, 0xfd}) - err := w.writeName(name) - require.Error(t, err) - - assert.Equal(t, - InvalidNonUTF8NameError{ - Name: name, - Offset: 0, - }, - err, - ) - - assert.Empty(t, b.data) - }) -} - -func TestWASMWriter_writeNameSection(t *testing.T) { - - t.Parallel() - - var b Buffer - w := NewWASMWriter(&b) - - imports := []*Import{ - { - Module: "foo", - Name: "bar", - }, - } - - functions := []*Function{ - { - Name: "add", - }, - } - - err := w.writeNameSection("test", imports, functions) - require.NoError(t, err) - - require.Equal(t, - []byte{ - // Section ID: Custom = 0 - 0x0, - // section size: 37 (LEB128) - 0xa5, 0x80, 0x80, 0x80, 0x0, - // name length - 0x4, - // name = "name" - 0x6e, 0x61, 0x6d, 0x65, - // sub-section ID: module name = 0 - 0x0, - // sub-section size: 5 (LEB128) - 0x85, 0x80, 0x80, 0x80, 0x0, - // name length - 0x4, - // name = "test" - 0x74, 0x65, 0x73, 0x74, - // sub-section ID: function names = 1 - 0x1, - // sub-section size: 15 (LEB128) - 0x8f, 0x80, 0x80, 0x80, 0x0, - // name count - 0x2, - // function index = 0 - 0x0, - // name length - 0x7, - // name = "foo.bar" - 0x66, 0x6f, 0x6f, 0x2e, 0x62, 0x61, 0x72, - // function index = 1 - 0x1, - // name length - 0x3, - // name = "add" - 0x61, 0x64, 0x64, - }, - b.data, - ) -} - -func TestWASMWriterReader(t *testing.T) { - - t.Skip("WIP") - - t.Parallel() - - var b Buffer - - w := NewWASMWriter(&b) - w.WriteNames = true - - module := &Module{ - Name: "test", - Types: []*FunctionType{ - { - Params: nil, - Results: nil, - }, - { - Params: []ValueType{ValueTypeI32, ValueTypeI32}, - Results: []ValueType{ValueTypeI32}, - }, - }, - Imports: []*Import{ - { - Module: "env", - Name: "add", - TypeIndex: 1, - }, - }, - Functions: []*Function{ - { - Name: "start", - TypeIndex: 0, - Code: &Code{ - Instructions: []Instruction{ - InstructionReturn{}, - }, - }, - }, - { - Name: "add", - TypeIndex: 1, - Code: &Code{ - // not used, just for testing - Locals: []ValueType{ - ValueTypeI32, - }, - Instructions: []Instruction{ - InstructionLocalGet{LocalIndex: 0}, - InstructionLocalGet{LocalIndex: 1}, - InstructionI32Add{}, - }, - }, - }, - }, - Memories: []*Memory{ - { - Min: 1024, - Max: func() *uint32 { - var max uint32 = 2048 - return &max - }(), - }, - }, - Exports: []*Export{ - { - Name: "add", - Descriptor: FunctionExport{ - FunctionIndex: 0, - }, - }, - { - Name: "mem", - Descriptor: MemoryExport{ - MemoryIndex: 0, - }, - }, - }, - StartFunctionIndex: func() *uint32 { - var funcIndex uint32 = 1 - return &funcIndex - }(), - Data: []*Data{ - { - MemoryIndex: 0, - Offset: []Instruction{ - InstructionI32Const{Value: 0}, - }, - Init: []byte{0x0, 0x1, 0x2, 0x3}, - }, - }, - } - - err := w.WriteModule(module) - require.NoError(t, err) - - expected := []byte{ - // magic - 0x0, 0x61, 0x73, 0x6d, - // version - 0x1, 0x0, 0x0, 0x0, - // type section - 0x1, - 0x8a, 0x80, 0x80, 0x80, 0x0, - 0x2, - 0x60, 0x0, 0x0, - 0x60, 0x2, 0x7f, 0x7f, 0x1, 0x7f, - // import section - 0x2, - 0x8b, 0x80, 0x80, 0x80, 0x0, - 0x1, - 0x3, 0x65, 0x6e, 0x76, 0x3, 0x61, 0x64, 0x64, 0x0, 0x1, - // function section - 0x3, - 0x83, 0x80, 0x80, 0x80, 0x0, - 0x2, - 0x0, - 0x1, - // memory section - 0x5, - 0x86, 0x80, 0x80, 0x80, 0x0, - 0x1, - 0x1, 0x80, 0x8, 0x80, 0x10, - // export section - 0x07, - 0x8d, 0x80, 0x80, 0x80, 0x00, - 0x02, - 0x03, 0x61, 0x64, 0x64, - 0x00, 0x00, - 0x03, 0x6d, 0x65, 0x6d, - 0x02, 0x00, - // start section - 0x8, - 0x81, 0x80, 0x80, 0x80, 0x0, - 0x1, - // code section - 0xa, - 0x97, 0x80, 0x80, 0x80, 0x0, - 0x2, - 0x83, 0x80, 0x80, 0x80, 0x0, 0x0, 0xf, 0xb, - 0x89, 0x80, 0x80, 0x80, 0x0, 0x1, 0x1, 0x7f, 0x20, 0x0, 0x20, 0x1, 0x6a, 0xb, - // data section - 0xb, - 0x8a, 0x80, 0x80, 0x80, 0x0, - 0x1, - 0x0, - 0x41, 0x0, 0xb, - 0x4, - 0x0, 0x1, 0x2, 0x3, - // name section - 0x0, - 0xac, 0x80, 0x80, 0x80, 0x0, - 0x4, 0x6e, 0x61, 0x6d, 0x65, 0x0, 0x85, 0x80, - 0x80, 0x80, 0x0, 0x4, 0x74, 0x65, 0x73, 0x74, - 0x1, 0x96, 0x80, 0x80, 0x80, 0x0, 0x3, 0x0, - 0x7, 0x65, 0x6e, 0x76, 0x2e, 0x61, 0x64, 0x64, - 0x1, 0x5, 0x73, 0x74, 0x61, 0x72, 0x74, - 0x2, 0x3, 0x61, 0x64, 0x64, - } - require.Equal(t, - expected, - b.data, - ) - - require.Equal(t, - `(module $test - (type (;0;) (func)) - (type (;1;) (func (param i32 i32) (result i32))) - (import "env" "add" (func $env.add (type 1))) - (func $start (type 0) - return) - (func $add (type 1) (param i32 i32) (result i32) - (local i32) - local.get 0 - local.get 1 - i32.add) - (memory (;0;) 1024 2048) - (export "add" (func $env.add)) - (export "mem" (memory 0)) - (start $start) - (data (;0;) (i32.const 0) "\00\01\02\03")) -`, - WASM2WAT(b.data), - ) - - b.offset = 0 - - r := NewWASMReader(&b) - err = r.ReadModule() - require.NoError(t, err) - - // prepare the expected module: - // remove all names, as the name section is not read yet - - module.Name = "" - for _, function := range module.Functions { - function.Name = "" - } - - require.Equal(t, - module, - &r.Module, - ) - - require.Equal(t, - offset(len(expected)), - b.offset, - ) -} - -func TestWASMWriter_writeInstruction(t *testing.T) { - - t.Parallel() - - t.Run("block, i32 result", func(t *testing.T) { - - t.Parallel() - - var b Buffer - w := NewWASMWriter(&b) - - instruction := InstructionBlock{ - Block: Block{ - BlockType: ValueTypeI32, - Instructions1: []Instruction{ - InstructionI32Const{Value: 1}, - }, - Instructions2: nil, - }, - } - err := instruction.write(w) - require.NoError(t, err) - - require.Equal(t, - []byte{ - // block - 0x02, - // i32 - 0x7f, - // i32.const - 0x41, - 0x01, - // end - 0x0b, - }, - b.data, - ) - }) - - t.Run("block, type index result", func(t *testing.T) { - - t.Parallel() - - var b Buffer - w := NewWASMWriter(&b) - - instruction := InstructionBlock{ - Block: Block{ - BlockType: TypeIndexBlockType{TypeIndex: 2}, - Instructions1: []Instruction{ - InstructionUnreachable{}, - }, - Instructions2: nil, - }, - } - err := instruction.write(w) - require.NoError(t, err) - - require.Equal(t, - []byte{ - // block - 0x02, - // type index: 2 - 0x2, - // unreachable - 0x0, - // end - 0x0b, - }, - b.data, - ) - }) - - t.Run("block, i32 result, second instructions", func(t *testing.T) { - - t.Parallel() - - var b Buffer - w := NewWASMWriter(&b) - - instruction := InstructionBlock{ - Block: Block{ - BlockType: ValueTypeI32, - Instructions1: []Instruction{ - InstructionI32Const{Value: 1}, - }, - Instructions2: []Instruction{ - InstructionI32Const{Value: 2}, - }, - }, - } - err := instruction.write(w) - require.Equal(t, InvalidBlockSecondInstructionsError{ - Offset: 4, - }, err) - }) - - t.Run("loop, i32 result", func(t *testing.T) { - - t.Parallel() - - var b Buffer - w := NewWASMWriter(&b) - - instruction := InstructionLoop{ - Block: Block{ - BlockType: ValueTypeI32, - Instructions1: []Instruction{ - InstructionI32Const{Value: 1}, - }, - Instructions2: nil, - }, - } - err := instruction.write(w) - require.NoError(t, err) - - require.Equal(t, - []byte{ - // loop - 0x03, - // i32 - 0x7f, - // i32.const - 0x41, - 0x01, - // end - 0x0b, - }, - b.data, - ) - }) - - t.Run("loop, i32 result, second instructions", func(t *testing.T) { - - t.Parallel() - - var b Buffer - w := NewWASMWriter(&b) - - instruction := InstructionLoop{ - Block: Block{ - BlockType: ValueTypeI32, - Instructions1: []Instruction{ - InstructionI32Const{Value: 1}, - }, - Instructions2: []Instruction{ - InstructionI32Const{Value: 2}, - }, - }, - } - err := instruction.write(w) - require.Equal(t, InvalidBlockSecondInstructionsError{ - Offset: 4, - }, err) - }) - - t.Run("if, i32 result", func(t *testing.T) { - - t.Parallel() - - var b Buffer - w := NewWASMWriter(&b) - - instruction := InstructionIf{ - Block: Block{ - BlockType: ValueTypeI32, - Instructions1: []Instruction{ - InstructionI32Const{Value: 1}, - }, - Instructions2: nil, - }, - } - err := instruction.write(w) - require.NoError(t, err) - - require.Equal(t, - []byte{ - // if - 0x04, - // i32 - 0x7f, - // i32.const - 0x41, - 0x01, - // end - 0x0b, - }, - b.data, - ) - }) - - t.Run("if-else, i32 result", func(t *testing.T) { - - t.Parallel() - - var b Buffer - w := NewWASMWriter(&b) - - instruction := InstructionIf{ - Block: Block{ - BlockType: ValueTypeI32, - Instructions1: []Instruction{ - InstructionI32Const{Value: 1}, - }, - Instructions2: []Instruction{ - InstructionI32Const{Value: 2}, - }, - }, - } - err := instruction.write(w) - require.NoError(t, err) - - require.Equal(t, - []byte{ - // ii - 0x04, - // i32 - 0x7f, - // i32.const - 0x41, - 0x01, - // else - 0x05, - // i32.const - 0x41, - 0x02, - // end - 0x0b, - }, - b.data, - ) - }) - - t.Run("br_table", func(t *testing.T) { - - t.Parallel() - - var b Buffer - w := NewWASMWriter(&b) - - instruction := InstructionBrTable{ - LabelIndices: []uint32{3, 2, 1, 0}, - DefaultLabelIndex: 4, - } - err := instruction.write(w) - require.NoError(t, err) - - require.Equal(t, - []byte{ - // br_table - 0x0e, - // number of branch depths - 0x04, - // 1. branch depth - 0x03, - // 2. branch depth - 0x02, - // 3. branch depth - 0x01, - // 4. branch depth - 0x00, - // default branch depth - 0x04, - }, - b.data, - ) - }) -} diff --git a/go.mod b/go.mod index 3412ef864a..0cfdc6c379 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,6 @@ go 1.22 require ( github.com/bits-and-blooms/bitset v1.5.0 - github.com/bytecodealliance/wasmtime-go/v7 v7.0.0 github.com/c-bata/go-prompt v0.2.6 github.com/dave/dst v0.27.2 github.com/fxamacker/cbor/v2 v2.4.1-0.20230228173756-c0c9f774e40c @@ -49,6 +48,7 @@ require ( github.com/mattn/go-runewidth v0.0.15 // indirect github.com/mattn/go-tty v0.0.3 // indirect github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect + github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect github.com/pkg/term v1.2.0-beta.2 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rogpeppe/go-internal v1.9.0 // indirect @@ -60,5 +60,6 @@ require ( golang.org/x/sys v0.26.0 // indirect golang.org/x/term v0.25.0 // indirect gonum.org/v1/gonum v0.6.1 // indirect + gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 4cbe6e714d..46e8c1a640 100644 --- a/go.sum +++ b/go.sum @@ -5,8 +5,6 @@ github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3 github.com/andybalholm/cascadia v1.0.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y= github.com/bits-and-blooms/bitset v1.5.0 h1:NpE8frKRLGHIcEzkR+gZhiioW1+WbYV6fKwD6ZIpQT8= github.com/bits-and-blooms/bitset v1.5.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= -github.com/bytecodealliance/wasmtime-go/v7 v7.0.0 h1:/rBNjgFju2HCZnkPb1eL+W4GBwP8DMbaQu7i+GR9DH4= -github.com/bytecodealliance/wasmtime-go/v7 v7.0.0/go.mod h1:bu6fic7trDt20w+LMooX7j3fsOwv4/ln6j8gAdP6vmA= github.com/c-bata/go-prompt v0.2.6 h1:POP+nrHE+DfLYx370bedwNhsqmpCUynWPxuHi0C5vZI= github.com/c-bata/go-prompt v0.2.6/go.mod h1:/LMAke8wD2FsNu9EXNdHxNLbd9MedkPnCdfpU9wwHfY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= diff --git a/vm/vm.go b/vm/vm.go deleted file mode 100644 index 74f2cf0542..0000000000 --- a/vm/vm.go +++ /dev/null @@ -1,164 +0,0 @@ -//go:build wasmtime -// +build wasmtime - -/* - * Cadence - The resource-oriented smart contract programming language - * - * Copyright Flow Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package vm - -import ( - "fmt" - "math/big" - - "C" - - "github.com/bytecodealliance/wasmtime-go/v7" - - "github.com/onflow/cadence/interpreter" -) - -type VM interface { - Invoke(name string, arguments ...interpreter.Value) (interpreter.Value, error) -} - -type vm struct { - instance *wasmtime.Instance - store *wasmtime.Store -} - -func (m *vm) Invoke(name string, arguments ...interpreter.Value) (interpreter.Value, error) { - f := m.instance.GetExport(m.store, name).Func() - - rawArguments := make([]any, len(arguments)) - for i, argument := range arguments { - rawArguments[i] = argument - } - - res, err := f.Call(m.store, rawArguments...) - if err != nil { - return nil, err - } - - if res == nil { - return nil, nil - } - - return res.(interpreter.Value), nil -} - -func NewVM(wasm []byte) (VM, error) { - - inter, err := interpreter.NewInterpreter(nil, nil, &interpreter.Config{}) - if err != nil { - return nil, err - } - - config := wasmtime.NewConfig() - config.SetWasmReferenceTypes(true) - - engine := wasmtime.NewEngineWithConfig(config) - - store := wasmtime.NewStore(engine) - - module, err := wasmtime.NewModule(store.Engine, wasm) - if err != nil { - return nil, err - } - - intFunc := wasmtime.WrapFunc( - store, - func(caller *wasmtime.Caller, offset int32, length int32) (any, *wasmtime.Trap) { - if offset < 0 { - return nil, wasmtime.NewTrap(fmt.Sprintf("Int: invalid offset: %d", offset)) - } - - if length < 2 { - return nil, wasmtime.NewTrap(fmt.Sprintf("Int: invalid length: %d", length)) - } - - mem := caller.GetExport("mem").Memory() - - bytes := C.GoBytes(mem.Data(store), C.int(length)) - - value := new(big.Int).SetBytes(bytes[1:]) - if bytes[0] == 0 { - value = value.Neg(value) - } - - return interpreter.NewUnmeteredIntValueFromBigInt(value), nil - }, - ) - - stringFunc := wasmtime.WrapFunc( - store, - func(caller *wasmtime.Caller, offset int32, length int32) (any, *wasmtime.Trap) { - if offset < 0 { - return nil, wasmtime.NewTrap(fmt.Sprintf("String: invalid offset: %d", offset)) - } - - if length < 0 { - return nil, wasmtime.NewTrap(fmt.Sprintf("String: invalid length: %d", length)) - } - - mem := caller.GetExport("mem").Memory() - - bytes := C.GoBytes(mem.Data(store), C.int(length)) - - return interpreter.NewUnmeteredStringValue(string(bytes)), nil - }, - ) - - addFunc := wasmtime.WrapFunc( - store, - func(left, right any) (any, *wasmtime.Trap) { - leftNumber, ok := left.(interpreter.NumberValue) - if !ok { - return nil, wasmtime.NewTrap(fmt.Sprintf("add: invalid left: %#+v", left)) - } - - rightNumber, ok := right.(interpreter.NumberValue) - if !ok { - return nil, wasmtime.NewTrap(fmt.Sprintf("add: invalid right: %#+v", right)) - } - - return leftNumber.Plus(inter, rightNumber, interpreter.EmptyLocationRange), nil - }, - ) - - // NOTE: wasmtime currently does not support specifying imports by name, - // unlike other WebAssembly APIs like wasmer, JavaScript, etc., - // i.e. imports are imported in the order they are given. - - instance, err := wasmtime.NewInstance( - store, - module, - []wasmtime.AsExtern{ - intFunc, - stringFunc, - addFunc, - }, - ) - if err != nil { - return nil, err - } - - return &vm{ - instance: instance, - store: store, - }, nil -}