blob: 8279d2e17e7ad0ece47b7c7f97761464a64f6064 [file] [log] [blame]
Sasha Smundakb051c4e2020-11-05 20:45:07 -08001// Copyright 2021 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package mk2rbc
16
17import (
18 "fmt"
19 "strconv"
20 "strings"
21
22 mkparser "android/soong/androidmk/parser"
23)
24
25// Represents an expression in the Starlark code. An expression has
26// a type, and it can be evaluated.
27type starlarkExpr interface {
28 starlarkNode
29 typ() starlarkType
30 // Try to substitute variable values. Return substitution result
31 // and whether it is the same as the original expression.
32 eval(valueMap map[string]starlarkExpr) (res starlarkExpr, same bool)
33 // Emit the code to copy the expression, otherwise we will end up
34 // with source and target pointing to the same list.
35 emitListVarCopy(gctx *generationContext)
36}
37
38func maybeString(expr starlarkExpr) (string, bool) {
39 if x, ok := expr.(*stringLiteralExpr); ok {
40 return x.literal, true
41 }
42 return "", false
43}
44
45type stringLiteralExpr struct {
46 literal string
47}
48
49func (s *stringLiteralExpr) eval(_ map[string]starlarkExpr) (res starlarkExpr, same bool) {
50 res = s
51 same = true
52 return
53}
54
55func (s *stringLiteralExpr) emit(gctx *generationContext) {
56 gctx.writef("%q", s.literal)
57}
58
59func (_ *stringLiteralExpr) typ() starlarkType {
60 return starlarkTypeString
61}
62
63func (s *stringLiteralExpr) emitListVarCopy(gctx *generationContext) {
64 s.emit(gctx)
65}
66
67// Integer literal
68type intLiteralExpr struct {
69 literal int
70}
71
72func (s *intLiteralExpr) eval(_ map[string]starlarkExpr) (res starlarkExpr, same bool) {
73 res = s
74 same = true
75 return
76}
77
78func (s *intLiteralExpr) emit(gctx *generationContext) {
79 gctx.writef("%d", s.literal)
80}
81
82func (_ *intLiteralExpr) typ() starlarkType {
83 return starlarkTypeInt
84}
85
86func (s *intLiteralExpr) emitListVarCopy(gctx *generationContext) {
87 s.emit(gctx)
88}
89
90// interpolateExpr represents Starlark's interpolation operator <string> % list
91// we break <string> into a list of chunks, i.e., "first%second%third" % (X, Y)
92// will have chunks = ["first", "second", "third"] and args = [X, Y]
93type interpolateExpr struct {
94 chunks []string // string chunks, separated by '%'
95 args []starlarkExpr
96}
97
98func (xi *interpolateExpr) emit(gctx *generationContext) {
99 if len(xi.chunks) != len(xi.args)+1 {
100 panic(fmt.Errorf("malformed interpolateExpr: #chunks(%d) != #args(%d)+1",
101 len(xi.chunks), len(xi.args)))
102 }
103 // Generate format as join of chunks, but first escape '%' in them
104 format := strings.ReplaceAll(xi.chunks[0], "%", "%%")
105 for _, chunk := range xi.chunks[1:] {
106 format += "%s" + strings.ReplaceAll(chunk, "%", "%%")
107 }
108 gctx.writef("%q %% ", format)
109 emitarg := func(arg starlarkExpr) {
110 if arg.typ() == starlarkTypeList {
111 gctx.write(`" ".join(`)
112 arg.emit(gctx)
113 gctx.write(`)`)
114 } else {
115 arg.emit(gctx)
116 }
117 }
118 if len(xi.args) == 1 {
119 emitarg(xi.args[0])
120 } else {
121 sep := "("
122 for _, arg := range xi.args {
123 gctx.write(sep)
124 emitarg(arg)
125 sep = ", "
126 }
127 gctx.write(")")
128 }
129}
130
131func (xi *interpolateExpr) eval(valueMap map[string]starlarkExpr) (res starlarkExpr, same bool) {
132 same = true
133 newChunks := []string{xi.chunks[0]}
134 var newArgs []starlarkExpr
135 for i, arg := range xi.args {
136 newArg, sameArg := arg.eval(valueMap)
137 same = same && sameArg
138 switch x := newArg.(type) {
139 case *stringLiteralExpr:
140 newChunks[len(newChunks)-1] += x.literal + xi.chunks[i+1]
141 same = false
142 continue
143 case *intLiteralExpr:
144 newChunks[len(newChunks)-1] += strconv.Itoa(x.literal) + xi.chunks[i+1]
145 same = false
146 continue
147 default:
148 newChunks = append(newChunks, xi.chunks[i+1])
149 newArgs = append(newArgs, newArg)
150 }
151 }
152 if same {
153 res = xi
154 } else if len(newChunks) == 1 {
155 res = &stringLiteralExpr{newChunks[0]}
156 } else {
157 res = &interpolateExpr{chunks: newChunks, args: newArgs}
158 }
159 return
160}
161
162func (_ *interpolateExpr) typ() starlarkType {
163 return starlarkTypeString
164}
165
166func (xi *interpolateExpr) emitListVarCopy(gctx *generationContext) {
167 xi.emit(gctx)
168}
169
170type variableRefExpr struct {
171 ref variable
172 isDefined bool
173}
174
175func (v *variableRefExpr) eval(map[string]starlarkExpr) (res starlarkExpr, same bool) {
176 predefined, ok := v.ref.(*predefinedVariable)
177 if same = !ok; same {
178 res = v
179 } else {
180 res = predefined.value
181 }
182 return
183}
184
185func (v *variableRefExpr) emit(gctx *generationContext) {
186 v.ref.emitGet(gctx, v.isDefined)
187}
188
189func (v *variableRefExpr) typ() starlarkType {
190 return v.ref.valueType()
191}
192
193func (v *variableRefExpr) emitListVarCopy(gctx *generationContext) {
194 v.emit(gctx)
195 if v.typ() == starlarkTypeList {
196 gctx.write("[:]") // this will copy the list
197 }
198}
199
Cole Faustf8320212021-11-10 15:05:07 -0800200type toStringExpr struct {
201 expr starlarkExpr
202}
203
204func (s *toStringExpr) eval(valueMap map[string]starlarkExpr) (res starlarkExpr, same bool) {
205 if x, same := s.expr.eval(valueMap); same {
206 res = s
207 } else {
208 res = &toStringExpr{expr: x}
209 }
210 return
211}
212
213func (s *toStringExpr) emit(ctx *generationContext) {
214 switch s.expr.typ() {
215 case starlarkTypeString, starlarkTypeUnknown:
216 // Assume unknown types are strings already.
217 s.expr.emit(ctx)
218 case starlarkTypeList:
219 ctx.write(`" ".join(`)
220 s.expr.emit(ctx)
221 ctx.write(")")
222 case starlarkTypeInt:
223 ctx.write(`("%d" % (`)
224 s.expr.emit(ctx)
225 ctx.write("))")
226 case starlarkTypeBool:
227 ctx.write("((")
228 s.expr.emit(ctx)
229 ctx.write(`) ? "true" : "")`)
230 case starlarkTypeVoid:
231 ctx.write(`""`)
232 default:
233 panic("Unknown starlark type!")
234 }
235}
236
237func (s *toStringExpr) typ() starlarkType {
238 return starlarkTypeString
239}
240
241func (s *toStringExpr) emitListVarCopy(gctx *generationContext) {
242 s.emit(gctx)
243}
244
Sasha Smundakb051c4e2020-11-05 20:45:07 -0800245type notExpr struct {
246 expr starlarkExpr
247}
248
249func (n *notExpr) eval(valueMap map[string]starlarkExpr) (res starlarkExpr, same bool) {
250 if x, same := n.expr.eval(valueMap); same {
251 res = n
252 } else {
253 res = &notExpr{expr: x}
254 }
255 return
256}
257
258func (n *notExpr) emit(ctx *generationContext) {
259 ctx.write("not ")
260 n.expr.emit(ctx)
261}
262
263func (_ *notExpr) typ() starlarkType {
264 return starlarkTypeBool
265}
266
267func (n *notExpr) emitListVarCopy(gctx *generationContext) {
268 n.emit(gctx)
269}
270
271type eqExpr struct {
272 left, right starlarkExpr
273 isEq bool // if false, it's !=
274}
275
276func (eq *eqExpr) eval(valueMap map[string]starlarkExpr) (res starlarkExpr, same bool) {
277 xLeft, sameLeft := eq.left.eval(valueMap)
278 xRight, sameRight := eq.right.eval(valueMap)
279 if same = sameLeft && sameRight; same {
280 res = eq
281 } else {
282 res = &eqExpr{left: xLeft, right: xRight, isEq: eq.isEq}
283 }
284 return
285}
286
287func (eq *eqExpr) emit(gctx *generationContext) {
Sasha Smundak0554d762021-07-08 18:26:12 -0700288 emitSimple := func(expr starlarkExpr) {
Sasha Smundakb051c4e2020-11-05 20:45:07 -0800289 if eq.isEq {
290 gctx.write("not ")
291 }
Sasha Smundak0554d762021-07-08 18:26:12 -0700292 expr.emit(gctx)
Sasha Smundakb051c4e2020-11-05 20:45:07 -0800293 }
Sasha Smundak0554d762021-07-08 18:26:12 -0700294 // Are we checking that a variable is empty?
295 if isEmptyString(eq.left) {
296 emitSimple(eq.right)
297 return
298 } else if isEmptyString(eq.right) {
299 emitSimple(eq.left)
300 return
Sasha Smundakb051c4e2020-11-05 20:45:07 -0800301
Sasha Smundak0554d762021-07-08 18:26:12 -0700302 }
Cole Faustf8320212021-11-10 15:05:07 -0800303
304 if eq.left.typ() != eq.right.typ() {
305 eq.left = &toStringExpr{expr: eq.left}
306 eq.right = &toStringExpr{expr: eq.right}
307 }
308
Sasha Smundakb051c4e2020-11-05 20:45:07 -0800309 // General case
310 eq.left.emit(gctx)
311 if eq.isEq {
312 gctx.write(" == ")
313 } else {
314 gctx.write(" != ")
315 }
316 eq.right.emit(gctx)
317}
318
319func (_ *eqExpr) typ() starlarkType {
320 return starlarkTypeBool
321}
322
323func (eq *eqExpr) emitListVarCopy(gctx *generationContext) {
324 eq.emit(gctx)
325}
326
327// variableDefinedExpr corresponds to Make's ifdef VAR
328type variableDefinedExpr struct {
329 v variable
330}
331
332func (v *variableDefinedExpr) eval(_ map[string]starlarkExpr) (res starlarkExpr, same bool) {
333 res = v
334 same = true
335 return
336
337}
338
339func (v *variableDefinedExpr) emit(gctx *generationContext) {
340 if v.v != nil {
341 v.v.emitDefined(gctx)
342 return
343 }
344 gctx.writef("%s(%q)", cfnWarning, "TODO(VAR)")
345}
346
347func (_ *variableDefinedExpr) typ() starlarkType {
348 return starlarkTypeBool
349}
350
351func (v *variableDefinedExpr) emitListVarCopy(gctx *generationContext) {
352 v.emit(gctx)
353}
354
355type listExpr struct {
356 items []starlarkExpr
357}
358
359func (l *listExpr) eval(valueMap map[string]starlarkExpr) (res starlarkExpr, same bool) {
360 newItems := make([]starlarkExpr, len(l.items))
361 same = true
362 for i, item := range l.items {
363 var sameItem bool
364 newItems[i], sameItem = item.eval(valueMap)
365 same = same && sameItem
366 }
367 if same {
368 res = l
369 } else {
370 res = &listExpr{newItems}
371 }
372 return
373}
374
375func (l *listExpr) emit(gctx *generationContext) {
376 if !gctx.inAssignment || len(l.items) < 2 {
377 gctx.write("[")
378 sep := ""
379 for _, item := range l.items {
380 gctx.write(sep)
381 item.emit(gctx)
382 sep = ", "
383 }
384 gctx.write("]")
385 return
386 }
387
388 gctx.write("[")
389 gctx.indentLevel += 2
390
391 for _, item := range l.items {
392 gctx.newLine()
393 item.emit(gctx)
394 gctx.write(",")
395 }
396 gctx.indentLevel -= 2
397 gctx.newLine()
398 gctx.write("]")
399}
400
401func (_ *listExpr) typ() starlarkType {
402 return starlarkTypeList
403}
404
405func (l *listExpr) emitListVarCopy(gctx *generationContext) {
406 l.emit(gctx)
407}
408
409func newStringListExpr(items []string) *listExpr {
410 v := listExpr{}
411 for _, item := range items {
412 v.items = append(v.items, &stringLiteralExpr{item})
413 }
414 return &v
415}
416
417// concatExpr generates epxr1 + expr2 + ... + exprN in Starlark.
418type concatExpr struct {
419 items []starlarkExpr
420}
421
422func (c *concatExpr) emit(gctx *generationContext) {
423 if len(c.items) == 1 {
424 c.items[0].emit(gctx)
425 return
426 }
427
428 if !gctx.inAssignment {
429 c.items[0].emit(gctx)
430 for _, item := range c.items[1:] {
431 gctx.write(" + ")
432 item.emit(gctx)
433 }
434 return
435 }
436 gctx.write("(")
437 c.items[0].emit(gctx)
438 gctx.indentLevel += 2
439 for _, item := range c.items[1:] {
440 gctx.write(" +")
441 gctx.newLine()
442 item.emit(gctx)
443 }
444 gctx.write(")")
445 gctx.indentLevel -= 2
446}
447
448func (c *concatExpr) eval(valueMap map[string]starlarkExpr) (res starlarkExpr, same bool) {
449 same = true
450 xConcat := &concatExpr{items: make([]starlarkExpr, len(c.items))}
451 for i, item := range c.items {
452 var sameItem bool
453 xConcat.items[i], sameItem = item.eval(valueMap)
454 same = same && sameItem
455 }
456 if same {
457 res = c
458 } else {
459 res = xConcat
460 }
461 return
462}
463
464func (_ *concatExpr) typ() starlarkType {
465 return starlarkTypeList
466}
467
468func (c *concatExpr) emitListVarCopy(gctx *generationContext) {
469 c.emit(gctx)
470}
471
472// inExpr generates <expr> [not] in <list>
473type inExpr struct {
474 expr starlarkExpr
475 list starlarkExpr
476 isNot bool
477}
478
479func (i *inExpr) eval(valueMap map[string]starlarkExpr) (res starlarkExpr, same bool) {
480 x := &inExpr{isNot: i.isNot}
481 var sameExpr, sameList bool
482 x.expr, sameExpr = i.expr.eval(valueMap)
483 x.list, sameList = i.list.eval(valueMap)
484 if same = sameExpr && sameList; same {
485 res = i
486 } else {
487 res = x
488 }
489 return
490}
491
492func (i *inExpr) emit(gctx *generationContext) {
493 i.expr.emit(gctx)
494 if i.isNot {
495 gctx.write(" not in ")
496 } else {
497 gctx.write(" in ")
498 }
499 i.list.emit(gctx)
500}
501
502func (_ *inExpr) typ() starlarkType {
503 return starlarkTypeBool
504}
505
506func (i *inExpr) emitListVarCopy(gctx *generationContext) {
507 i.emit(gctx)
508}
509
510type indexExpr struct {
511 array starlarkExpr
512 index starlarkExpr
513}
514
515func (ix indexExpr) emit(gctx *generationContext) {
516 ix.array.emit(gctx)
517 gctx.write("[")
518 ix.index.emit(gctx)
519 gctx.write("]")
520}
521
522func (ix indexExpr) typ() starlarkType {
523 return starlarkTypeString
524}
525
526func (ix indexExpr) eval(valueMap map[string]starlarkExpr) (res starlarkExpr, same bool) {
527 newArray, isSameArray := ix.array.eval(valueMap)
528 newIndex, isSameIndex := ix.index.eval(valueMap)
529 if same = isSameArray && isSameIndex; same {
530 res = ix
531 } else {
532 res = &indexExpr{newArray, newIndex}
533 }
534 return
535}
536
537func (ix indexExpr) emitListVarCopy(gctx *generationContext) {
538 ix.emit(gctx)
539}
540
541type callExpr struct {
542 object starlarkExpr // nil if static call
543 name string
544 args []starlarkExpr
545 returnType starlarkType
546}
547
548func (cx *callExpr) eval(valueMap map[string]starlarkExpr) (res starlarkExpr, same bool) {
549 newCallExpr := &callExpr{name: cx.name, args: make([]starlarkExpr, len(cx.args)),
550 returnType: cx.returnType}
551 if cx.object != nil {
552 newCallExpr.object, same = cx.object.eval(valueMap)
553 } else {
554 same = true
555 }
556 for i, args := range cx.args {
557 var s bool
558 newCallExpr.args[i], s = args.eval(valueMap)
559 same = same && s
560 }
561 if same {
562 res = cx
563 } else {
564 res = newCallExpr
565 }
566 return
567}
568
569func (cx *callExpr) emit(gctx *generationContext) {
Sasha Smundak3deb9682021-07-26 18:42:25 -0700570 sep := ""
Sasha Smundakb051c4e2020-11-05 20:45:07 -0800571 if cx.object != nil {
572 gctx.write("(")
573 cx.object.emit(gctx)
574 gctx.write(")")
575 gctx.write(".", cx.name, "(")
576 } else {
577 kf, found := knownFunctions[cx.name]
578 if !found {
579 panic(fmt.Errorf("callExpr with unknown function %q", cx.name))
580 }
581 if kf.runtimeName[0] == '!' {
582 panic(fmt.Errorf("callExpr for %q should not be there", cx.name))
583 }
584 gctx.write(kf.runtimeName, "(")
Sasha Smundak3deb9682021-07-26 18:42:25 -0700585 if kf.hiddenArg == hiddenArgGlobal {
586 gctx.write("g")
587 sep = ", "
588 } else if kf.hiddenArg == hiddenArgConfig {
589 gctx.write("cfg")
590 sep = ", "
591 }
Sasha Smundakb051c4e2020-11-05 20:45:07 -0800592 }
Sasha Smundakb051c4e2020-11-05 20:45:07 -0800593 for _, arg := range cx.args {
594 gctx.write(sep)
595 arg.emit(gctx)
596 sep = ", "
597 }
598 gctx.write(")")
599}
600
601func (cx *callExpr) typ() starlarkType {
602 return cx.returnType
603}
604
605func (cx *callExpr) emitListVarCopy(gctx *generationContext) {
606 cx.emit(gctx)
607}
608
609type badExpr struct {
610 node mkparser.Node
611 message string
612}
613
614func (b *badExpr) eval(_ map[string]starlarkExpr) (res starlarkExpr, same bool) {
615 res = b
616 same = true
617 return
618}
619
620func (b *badExpr) emit(_ *generationContext) {
621 panic("implement me")
622}
623
624func (_ *badExpr) typ() starlarkType {
625 return starlarkTypeUnknown
626}
627
628func (b *badExpr) emitListVarCopy(gctx *generationContext) {
629 panic("implement me")
630}
631
632func maybeConvertToStringList(expr starlarkExpr) starlarkExpr {
633 if xString, ok := expr.(*stringLiteralExpr); ok {
634 return newStringListExpr(strings.Fields(xString.literal))
635 }
636 return expr
637}
Sasha Smundak0554d762021-07-08 18:26:12 -0700638
639func isEmptyString(expr starlarkExpr) bool {
640 x, ok := expr.(*stringLiteralExpr)
641 return ok && x.literal == ""
642}