blob: fc3a16131364c325f6b6c7ec103e6a67fc66751d [file] [log] [blame]
Jiri Simsad7616c92015-03-24 23:44:30 -07001// Copyright 2015 The Vanadium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
Todd Wang232d6492015-02-25 18:04:54 -08005package compile
6
7import (
Todd Wang232d6492015-02-25 18:04:54 -08008 "v.io/v23/vdl"
Jiri Simsa24a71552015-02-27 11:31:36 -08009 "v.io/x/lib/toposort"
Jiri Simsaffceefa2015-02-28 11:03:34 -080010 "v.io/x/ref/lib/vdl/parse"
Todd Wang232d6492015-02-25 18:04:54 -080011)
12
13// compileInterfaces is the "entry point" to the rest of this file. It takes
14// the interfaces defined in pfiles and compiles them into Interfaces in pkg.
15func compileInterfaces(pkg *Package, pfiles []*parse.File, env *Env) {
16 id := ifaceDefiner{pkg, pfiles, env, make(map[string]*ifaceBuilder)}
17 if id.Declare(); !env.Errors.IsEmpty() {
18 return
19 }
20 id.SortAndDefine()
21}
22
23// ifaceDefiner defines interfaces in a package. This is split into two phases:
24// 1) Declare ensures local interface references can be resolved.
25// 2) SortAndDefine sorts in dependency order, and evaluates and defines each
26// const.
27//
28// It holds a builders map from interface name to ifaceBuilder, where the
29// ifaceBuilder is responsible for compiling and defining a single interface.
30type ifaceDefiner struct {
31 pkg *Package
32 pfiles []*parse.File
33 env *Env
34 builders map[string]*ifaceBuilder
35}
36
37type ifaceBuilder struct {
38 def *Interface
39 pdef *parse.Interface
40}
41
42func printIfaceBuilderName(ibuilder interface{}) string {
43 return ibuilder.(*ifaceBuilder).def.Name
44}
45
46// Declare creates builders for each interface defined in the package.
47func (id ifaceDefiner) Declare() {
48 for ix := range id.pkg.Files {
49 file, pfile := id.pkg.Files[ix], id.pfiles[ix]
50 for _, pdef := range pfile.Interfaces {
Todd Wang53a4e2e2015-03-18 10:54:54 -070051 export, err := validIdent(pdef.Name, reservedNormal)
Todd Wang232d6492015-02-25 18:04:54 -080052 if err != nil {
53 id.env.prefixErrorf(file, pdef.Pos, err, "interface %s invalid name", pdef.Name)
54 continue // keep going to catch more errors
55 }
56 detail := identDetail("interface", file, pdef.Pos)
57 if err := file.DeclareIdent(pdef.Name, detail); err != nil {
58 id.env.prefixErrorf(file, pdef.Pos, err, "interface %s name conflict", pdef.Name)
59 continue
60 }
61 def := &Interface{NamePos: NamePos(pdef.NamePos), Exported: export, File: file}
62 id.builders[pdef.Name] = &ifaceBuilder{def, pdef}
63 }
64 }
65}
66
67// Sort and define interfaces. We sort by dependencies on other interfaces in
68// this package. The sorting is to ensure there are no cycles.
69func (id ifaceDefiner) SortAndDefine() {
70 // Populate sorter with dependency information. The sorting ensures that the
71 // list of interfaces within each file is topologically sorted, and also
72 // deterministic; in the absence of interface embeddings, interfaces are
73 // listed in the same order they were defined in the parsed files.
74 var sorter toposort.Sorter
75 for _, pfile := range id.pfiles {
76 for _, pdef := range pfile.Interfaces {
77 b := id.builders[pdef.Name]
78 sorter.AddNode(b)
79 for _, dep := range id.getLocalDeps(b) {
80 sorter.AddEdge(b, dep)
81 }
82 }
83 }
84 // Sort and check for cycles.
85 sorted, cycles := sorter.Sort()
86 if len(cycles) > 0 {
87 cycleStr := toposort.DumpCycles(cycles, printIfaceBuilderName)
88 first := cycles[0][0].(*ifaceBuilder)
89 id.env.Errorf(first.def.File, first.def.Pos, "package %v has cyclic interfaces: %v", id.pkg.Name, cycleStr)
90 return
91 }
92 // Define all interfaces. Since we add the interfaces as we go and evaluate
93 // in topological order, dependencies are guaranteed to be resolvable when we
94 // get around to defining the interfaces that embed on them.
95 for _, ibuilder := range sorted {
96 b := ibuilder.(*ifaceBuilder)
97 id.define(b)
98 addIfaceDef(b.def)
99 }
100}
101
102// addIfaceDef updates our various structures to add a new interface.
103func addIfaceDef(def *Interface) {
104 def.File.Interfaces = append(def.File.Interfaces, def)
105 def.File.Package.ifaceDefs[def.Name] = def
106}
107
108// getLocalDeps returns the list of interface dependencies for b that are in
109// this package.
110func (id ifaceDefiner) getLocalDeps(b *ifaceBuilder) (deps []*ifaceBuilder) {
111 for _, pe := range b.pdef.Embeds {
112 // Embeddings of other interfaces in this package are all we care about.
113 if dep := id.builders[pe.Name]; dep != nil {
114 deps = append(deps, dep)
115 }
116 }
117 return
118}
119
120func (id ifaceDefiner) define(b *ifaceBuilder) {
121 id.defineEmbeds(b)
122 id.defineMethods(b)
123}
124
125func (id ifaceDefiner) defineEmbeds(b *ifaceBuilder) {
126 // TODO(toddw): Check for duplicate methods.
127 def, file := b.def, b.def.File
128 seen := make(map[string]*parse.NamePos)
129 for _, pe := range b.pdef.Embeds {
130 if dup := seen[pe.Name]; dup != nil {
131 id.env.Errorf(file, pe.Pos, "interface %s duplicate embedding (previous at %s)", pe.Name, dup.Pos)
132 continue // keep going to catch more errors
133 }
134 seen[pe.Name] = pe
135 // Resolve the embedded interface.
136 embed, matched := id.env.ResolveInterface(pe.Name, file)
137 if embed == nil {
138 id.env.Errorf(file, pe.Pos, "interface %s undefined", pe.Name)
139 continue // keep going to catch more errors
140 }
141 if len(matched) < len(pe.Name) {
142 id.env.Errorf(file, pe.Pos, "interface %s invalid (%s unmatched)", pe.Name, pe.Name[len(matched):])
143 continue // keep going to catch more errors
144 }
145 def.Embeds = append(def.Embeds, embed)
146 }
147}
148
149func (id ifaceDefiner) defineMethods(b *ifaceBuilder) {
150 def, file := b.def, b.def.File
151 seen := make(map[string]*parse.Method)
152 for _, pm := range b.pdef.Methods {
153 if dup := seen[pm.Name]; dup != nil {
154 id.env.Errorf(file, pm.Pos, "method %s redefined (previous at %s)", pm.Name, dup.Pos)
155 continue // keep going to catch more errors
156 }
157 seen[pm.Name] = pm
Todd Wang53a4e2e2015-03-18 10:54:54 -0700158 if err := validExportedIdent(pm.Name, reservedFirstRuneLower); err != nil {
Todd Wang232d6492015-02-25 18:04:54 -0800159 id.env.Errorf(file, pm.Pos, "method %s name (%s)", pm.Name, err)
160 continue // keep going to catch more errors
161 }
162 m := &Method{NamePos: NamePos(pm.NamePos)}
163 m.InArgs = id.defineArgs(in, m.NamePos, pm.InArgs, file)
164 m.OutArgs = id.defineArgs(out, m.NamePos, pm.OutArgs, file)
165 m.InStream = id.defineStreamType(pm.InStream, file)
166 m.OutStream = id.defineStreamType(pm.OutStream, file)
167 m.Tags = id.defineTags(pm.Tags, file)
168 def.Methods = append(def.Methods, m)
169 }
170}
171
172type inout string
173
174const (
175 in inout = "in"
176 out inout = "out"
177)
178
179func (id ifaceDefiner) defineArgs(io inout, method NamePos, pargs []*parse.Field, file *File) (args []*Field) {
180 seen := make(map[string]*parse.Field)
181 for _, parg := range pargs {
182 if dup := seen[parg.Name]; dup != nil && parg.Name != "" {
183 id.env.Errorf(file, parg.Pos, "method %s arg %s duplicate name (previous at %s)", method.Name, parg.Name, dup.Pos)
184 continue // keep going to catch more errors
185 }
186 seen[parg.Name] = parg
Todd Wangd87d7462015-04-26 01:59:54 -0700187 switch {
188 case io == in && parg.Name == "":
189 id.env.Errorf(file, parg.Pos, "method %s in-arg unnamed (must name all in-args)", method.Name)
190 continue // keep going to catch more errors
191 case io == out && len(pargs) > 1 && parg.Name == "":
192 id.env.Errorf(file, parg.Pos, "method %s out-arg unnamed (must name all out-args if there are more than 1)", method.Name)
Todd Wang232d6492015-02-25 18:04:54 -0800193 continue // keep going to catch more errors
194 }
195 if parg.Name != "" {
Todd Wang53a4e2e2015-03-18 10:54:54 -0700196 if _, err := validIdent(parg.Name, reservedFirstRuneLower); err != nil {
Todd Wang232d6492015-02-25 18:04:54 -0800197 id.env.prefixErrorf(file, parg.Pos, err, "method %s invalid arg %s", method.Name, parg.Name)
198 continue // keep going to catch more errors
199 }
200 }
201 arg := &Field{NamePos(parg.NamePos), compileType(parg.Type, file, id.env)}
202 args = append(args, arg)
203 }
204 return
205}
206
207func (id ifaceDefiner) defineStreamType(ptype parse.Type, file *File) *vdl.Type {
208 if ptype == nil {
209 return nil
210 }
211 if tn, ok := ptype.(*parse.TypeNamed); ok && tn.Name == "_" {
212 // Special-case the _ placeholder, which means there's no stream type.
213 return nil
214 }
215 return compileType(ptype, file, id.env)
216}
217
218func (id ifaceDefiner) defineTags(ptags []parse.ConstExpr, file *File) (tags []*vdl.Value) {
219 for _, ptag := range ptags {
220 if tag := compileConst("tag", nil, ptag, file, id.env); tag != nil {
221 tags = append(tags, tag)
222 }
223 }
224 return
225}