This repository has been archived by the owner on Mar 14, 2022. It is now read-only.
forked from istio/istio
-
Notifications
You must be signed in to change notification settings - Fork 0
/
program.go
154 lines (137 loc) · 5.78 KB
/
program.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
// Copyright 2017 Istio Authors
//
// 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 il implements the intermediate-language for the config-language interpreter of Mixer.
// The IL is designed as a 32-bit instruction set. The opcode/argument size of the IL is 32-bits
// and both are modeled as uint32s. Different instructions can have different number of arguments,
// but a given instruction will always have a well-defined number of arguments. Each argument
// can be 1 or 2 uint32s, depending on type.
//
// The IL intrinsically supports the following basic primitive types:
//
// - String: represents a string value and maps to the native string Go type. Internally, the
// strings are interned in a StringTable, thus allowing extremely fast equality comparisons. The
// id of the string in the StringTable is encoded in the ByteCode, when stored, or during execution.
// - Bool: represents a boolean value and maps to the native bool Go type. The boolean values of
// false and true are represented as uint32(0) and uint32(1), respectively. The VM will consider
// any non-zero value as true, if it encounters during execution.
// - Integer: represents a signed integer value and maps to the native int64 Go type. The integer
// is stored in 2 uint32s internally.
// - Double: represents a signed float value and maps to the native float64 Go type. The double
// is stored in 2 uint32s internally.
// - Record represents a mapping of string-> string and maps to the map[string]string Go Type.
// There is no constant representation of Record in il form.
// - Void represents no-value. This is typically used in the return type of a function, to indicate
// that the function does not return a result.
//
// In addition to these types, the VM inherently supports errors, based on Go errors. It is possible
// to raise these errors from within the IL. However, it is not possible to trap or return these as
// values. Once an error is raised, whether as a program instruction, or due to an invalid evaluation
// step, it bubbles all the way out of the evaluation context.
//
// The execution model is based on an operand stack and registers. Though most operations work on an
// operand stack to perform calculations, it is possible to transfer data from registers to stack
// and vice-versa.
//
// The IL supports a stack-based calling-convention. All the parameters to a function need to be
// pushed onto the stack before the CALL instruction is invoked. Similarly, the return values need
// to be pushed onto the stack when RET is called.
//
// Typically, an IL based program is created by a compiler by initializing a Program type and
// by adding Functions to it, whose bodies are built using the Builder type. Once built, programs
// can be serialized/deserialized into a textual form for ease of use, using Write* and Read* method
// in this package.
//
package il
import (
"fmt"
)
const (
// programCodeSize is the default size of a program.
defaultProgramCodeSize int = 256
)
// Program is a self-contained IL based set of functions that can be executed by an interpreter.
type Program struct {
// Strings is the collection of strings that is used by the program.
strings *StringTable
// Functions is the collection of functions that are contained in the program.
Functions *FunctionTable
// Code is the actual byte-code based body of all the functions in the program.
code []uint32
}
// NewProgram creates and returns a new, empty program.
func NewProgram() *Program {
strings := newStringTable()
p := &Program{
strings: strings,
Functions: newFunctionTable(strings),
code: make([]uint32, 0, defaultProgramCodeSize),
}
p.code = append(p.code, uint32(Halt))
return p
}
// AddExternDef adds a new external/native function to the program. The actual bodies of functions
// need to be supplied to the interpreter separately.
func (p *Program) AddExternDef(name string, parameters []Type, returnType Type) {
f := Function{
ID: p.strings.Add(name),
Length: 0,
Address: 0,
Parameters: parameters,
ReturnType: returnType,
}
p.Functions.add(&f)
}
// AddFunction adds a new IL based function to the program.
func (p *Program) AddFunction(name string, parameters []Type, returnType Type, body []uint32) error {
p.code = append(p.code, uint32(Halt)) // Leave a single instruction gap between function bodies.
start := uint32(len(p.code))
l := uint32(len(body))
// Rewrite jump addresses from local to global when transcribing method body.
var i uint32
for i = 0; i < l; {
op := Opcode(body[i])
info := opCodeInfos[op]
if i+op.Size() > l {
return fmt.Errorf("opcode requires more arguments than are present in the body: op: %v, loc: %d", op, i)
}
p.code = append(p.code, body[i])
i++
for _, a := range info.args {
for j := 0; j < int(a.Size()); j++ {
p.code = append(p.code, body[i])
if a == OpcodeArgAddress {
p.code[len(p.code)-1] += start
}
i++
}
}
}
f := &Function{
ID: p.strings.Add(name),
Length: l,
Address: start,
Parameters: parameters,
ReturnType: returnType,
}
p.Functions.add(f)
return nil
}
// Strings returns the strings table of this program.
func (p *Program) Strings() *StringTable {
return p.strings
}
// ByteCode returns a copy of the byte-code of this program.
func (p *Program) ByteCode() []uint32 {
return p.code
}