blob: 0c5bc40cf57e1f416217f370cecc4864f6c74698 [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:
Cole Faustf1f44d32021-11-16 14:52:12 -0800227 ctx.write(`("true" if (`)
Cole Faustf8320212021-11-10 15:05:07 -0800228 s.expr.emit(ctx)
Cole Faustf1f44d32021-11-16 14:52:12 -0800229 ctx.write(`) else "")`)
Cole Faustf8320212021-11-10 15:05:07 -0800230 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) {
Cole Faustf1f44d32021-11-16 14:52:12 -0800288 var stringOperand string
289 var otherOperand starlarkExpr
290 if s, ok := maybeString(eq.left); ok {
291 stringOperand = s
292 otherOperand = eq.right
293 } else if s, ok := maybeString(eq.right); ok {
294 stringOperand = s
295 otherOperand = eq.left
Sasha Smundakb051c4e2020-11-05 20:45:07 -0800296 }
297
Cole Faustf1f44d32021-11-16 14:52:12 -0800298 // If we've identified one of the operands as being a string literal, check
299 // for some special cases we can do to simplify the resulting expression.
300 if otherOperand != nil {
301 if stringOperand == "" {
302 if eq.isEq {
303 gctx.write("not ")
304 }
305 otherOperand.emit(gctx)
306 return
307 }
308 if stringOperand == "true" && otherOperand.typ() == starlarkTypeBool {
309 if !eq.isEq {
310 gctx.write("not ")
311 }
312 otherOperand.emit(gctx)
313 return
314 }
Sasha Smundak0554d762021-07-08 18:26:12 -0700315 }
Cole Faustf8320212021-11-10 15:05:07 -0800316
317 if eq.left.typ() != eq.right.typ() {
318 eq.left = &toStringExpr{expr: eq.left}
319 eq.right = &toStringExpr{expr: eq.right}
320 }
321
Sasha Smundakb051c4e2020-11-05 20:45:07 -0800322 // General case
323 eq.left.emit(gctx)
324 if eq.isEq {
325 gctx.write(" == ")
326 } else {
327 gctx.write(" != ")
328 }
329 eq.right.emit(gctx)
330}
331
332func (_ *eqExpr) typ() starlarkType {
333 return starlarkTypeBool
334}
335
336func (eq *eqExpr) emitListVarCopy(gctx *generationContext) {
337 eq.emit(gctx)
338}
339
340// variableDefinedExpr corresponds to Make's ifdef VAR
341type variableDefinedExpr struct {
342 v variable
343}
344
345func (v *variableDefinedExpr) eval(_ map[string]starlarkExpr) (res starlarkExpr, same bool) {
346 res = v
347 same = true
348 return
349
350}
351
352func (v *variableDefinedExpr) emit(gctx *generationContext) {
353 if v.v != nil {
354 v.v.emitDefined(gctx)
355 return
356 }
357 gctx.writef("%s(%q)", cfnWarning, "TODO(VAR)")
358}
359
360func (_ *variableDefinedExpr) typ() starlarkType {
361 return starlarkTypeBool
362}
363
364func (v *variableDefinedExpr) emitListVarCopy(gctx *generationContext) {
365 v.emit(gctx)
366}
367
368type listExpr struct {
369 items []starlarkExpr
370}
371
372func (l *listExpr) eval(valueMap map[string]starlarkExpr) (res starlarkExpr, same bool) {
373 newItems := make([]starlarkExpr, len(l.items))
374 same = true
375 for i, item := range l.items {
376 var sameItem bool
377 newItems[i], sameItem = item.eval(valueMap)
378 same = same && sameItem
379 }
380 if same {
381 res = l
382 } else {
383 res = &listExpr{newItems}
384 }
385 return
386}
387
388func (l *listExpr) emit(gctx *generationContext) {
389 if !gctx.inAssignment || len(l.items) < 2 {
390 gctx.write("[")
391 sep := ""
392 for _, item := range l.items {
393 gctx.write(sep)
394 item.emit(gctx)
395 sep = ", "
396 }
397 gctx.write("]")
398 return
399 }
400
401 gctx.write("[")
402 gctx.indentLevel += 2
403
404 for _, item := range l.items {
405 gctx.newLine()
406 item.emit(gctx)
407 gctx.write(",")
408 }
409 gctx.indentLevel -= 2
410 gctx.newLine()
411 gctx.write("]")
412}
413
414func (_ *listExpr) typ() starlarkType {
415 return starlarkTypeList
416}
417
418func (l *listExpr) emitListVarCopy(gctx *generationContext) {
419 l.emit(gctx)
420}
421
422func newStringListExpr(items []string) *listExpr {
423 v := listExpr{}
424 for _, item := range items {
425 v.items = append(v.items, &stringLiteralExpr{item})
426 }
427 return &v
428}
429
430// concatExpr generates epxr1 + expr2 + ... + exprN in Starlark.
431type concatExpr struct {
432 items []starlarkExpr
433}
434
435func (c *concatExpr) emit(gctx *generationContext) {
436 if len(c.items) == 1 {
437 c.items[0].emit(gctx)
438 return
439 }
440
441 if !gctx.inAssignment {
442 c.items[0].emit(gctx)
443 for _, item := range c.items[1:] {
444 gctx.write(" + ")
445 item.emit(gctx)
446 }
447 return
448 }
449 gctx.write("(")
450 c.items[0].emit(gctx)
451 gctx.indentLevel += 2
452 for _, item := range c.items[1:] {
453 gctx.write(" +")
454 gctx.newLine()
455 item.emit(gctx)
456 }
457 gctx.write(")")
458 gctx.indentLevel -= 2
459}
460
461func (c *concatExpr) eval(valueMap map[string]starlarkExpr) (res starlarkExpr, same bool) {
462 same = true
463 xConcat := &concatExpr{items: make([]starlarkExpr, len(c.items))}
464 for i, item := range c.items {
465 var sameItem bool
466 xConcat.items[i], sameItem = item.eval(valueMap)
467 same = same && sameItem
468 }
469 if same {
470 res = c
471 } else {
472 res = xConcat
473 }
474 return
475}
476
477func (_ *concatExpr) typ() starlarkType {
478 return starlarkTypeList
479}
480
481func (c *concatExpr) emitListVarCopy(gctx *generationContext) {
482 c.emit(gctx)
483}
484
485// inExpr generates <expr> [not] in <list>
486type inExpr struct {
487 expr starlarkExpr
488 list starlarkExpr
489 isNot bool
490}
491
492func (i *inExpr) eval(valueMap map[string]starlarkExpr) (res starlarkExpr, same bool) {
493 x := &inExpr{isNot: i.isNot}
494 var sameExpr, sameList bool
495 x.expr, sameExpr = i.expr.eval(valueMap)
496 x.list, sameList = i.list.eval(valueMap)
497 if same = sameExpr && sameList; same {
498 res = i
499 } else {
500 res = x
501 }
502 return
503}
504
505func (i *inExpr) emit(gctx *generationContext) {
506 i.expr.emit(gctx)
507 if i.isNot {
508 gctx.write(" not in ")
509 } else {
510 gctx.write(" in ")
511 }
512 i.list.emit(gctx)
513}
514
515func (_ *inExpr) typ() starlarkType {
516 return starlarkTypeBool
517}
518
519func (i *inExpr) emitListVarCopy(gctx *generationContext) {
520 i.emit(gctx)
521}
522
523type indexExpr struct {
524 array starlarkExpr
525 index starlarkExpr
526}
527
528func (ix indexExpr) emit(gctx *generationContext) {
529 ix.array.emit(gctx)
530 gctx.write("[")
531 ix.index.emit(gctx)
532 gctx.write("]")
533}
534
535func (ix indexExpr) typ() starlarkType {
536 return starlarkTypeString
537}
538
539func (ix indexExpr) eval(valueMap map[string]starlarkExpr) (res starlarkExpr, same bool) {
540 newArray, isSameArray := ix.array.eval(valueMap)
541 newIndex, isSameIndex := ix.index.eval(valueMap)
542 if same = isSameArray && isSameIndex; same {
543 res = ix
544 } else {
545 res = &indexExpr{newArray, newIndex}
546 }
547 return
548}
549
550func (ix indexExpr) emitListVarCopy(gctx *generationContext) {
551 ix.emit(gctx)
552}
553
554type callExpr struct {
555 object starlarkExpr // nil if static call
556 name string
557 args []starlarkExpr
558 returnType starlarkType
559}
560
561func (cx *callExpr) eval(valueMap map[string]starlarkExpr) (res starlarkExpr, same bool) {
562 newCallExpr := &callExpr{name: cx.name, args: make([]starlarkExpr, len(cx.args)),
563 returnType: cx.returnType}
564 if cx.object != nil {
565 newCallExpr.object, same = cx.object.eval(valueMap)
566 } else {
567 same = true
568 }
569 for i, args := range cx.args {
570 var s bool
571 newCallExpr.args[i], s = args.eval(valueMap)
572 same = same && s
573 }
574 if same {
575 res = cx
576 } else {
577 res = newCallExpr
578 }
579 return
580}
581
582func (cx *callExpr) emit(gctx *generationContext) {
Sasha Smundak3deb9682021-07-26 18:42:25 -0700583 sep := ""
Sasha Smundakb051c4e2020-11-05 20:45:07 -0800584 if cx.object != nil {
585 gctx.write("(")
586 cx.object.emit(gctx)
587 gctx.write(")")
588 gctx.write(".", cx.name, "(")
589 } else {
590 kf, found := knownFunctions[cx.name]
591 if !found {
592 panic(fmt.Errorf("callExpr with unknown function %q", cx.name))
593 }
594 if kf.runtimeName[0] == '!' {
595 panic(fmt.Errorf("callExpr for %q should not be there", cx.name))
596 }
597 gctx.write(kf.runtimeName, "(")
Sasha Smundak3deb9682021-07-26 18:42:25 -0700598 if kf.hiddenArg == hiddenArgGlobal {
599 gctx.write("g")
600 sep = ", "
601 } else if kf.hiddenArg == hiddenArgConfig {
602 gctx.write("cfg")
603 sep = ", "
604 }
Sasha Smundakb051c4e2020-11-05 20:45:07 -0800605 }
Sasha Smundakb051c4e2020-11-05 20:45:07 -0800606 for _, arg := range cx.args {
607 gctx.write(sep)
608 arg.emit(gctx)
609 sep = ", "
610 }
611 gctx.write(")")
612}
613
614func (cx *callExpr) typ() starlarkType {
615 return cx.returnType
616}
617
618func (cx *callExpr) emitListVarCopy(gctx *generationContext) {
619 cx.emit(gctx)
620}
621
622type badExpr struct {
623 node mkparser.Node
624 message string
625}
626
627func (b *badExpr) eval(_ map[string]starlarkExpr) (res starlarkExpr, same bool) {
628 res = b
629 same = true
630 return
631}
632
633func (b *badExpr) emit(_ *generationContext) {
634 panic("implement me")
635}
636
637func (_ *badExpr) typ() starlarkType {
638 return starlarkTypeUnknown
639}
640
641func (b *badExpr) emitListVarCopy(gctx *generationContext) {
642 panic("implement me")
643}
644
645func maybeConvertToStringList(expr starlarkExpr) starlarkExpr {
646 if xString, ok := expr.(*stringLiteralExpr); ok {
647 return newStringListExpr(strings.Fields(xString.literal))
648 }
649 return expr
650}
Sasha Smundak0554d762021-07-08 18:26:12 -0700651
652func isEmptyString(expr starlarkExpr) bool {
653 x, ok := expr.(*stringLiteralExpr)
654 return ok && x.literal == ""
655}