• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2017 The Wuffs Authors.
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//    https://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 check
16
17// TODO: should bounds checking even be responsible for selecting between
18// optimized implementations of e.g. read_u8? Instead, the Wuffs code could
19// explicitly call either read_u8 or read_u8_fast, with the latter having
20// stronger preconditions.
21//
22// Doing so might need some syntactical distinction (not just a question mark)
23// between "foo?" and "bar?" if one of those methods can still return an error
24// code but never actually suspend.
25
26import (
27	"math/big"
28
29	a "github.com/google/wuffs/lang/ast"
30	t "github.com/google/wuffs/lang/token"
31)
32
33// splitReceiverMethodArgs returns the "receiver", "method" and "args" in the
34// expression "receiver.method(args)".
35func splitReceiverMethodArgs(n *a.Expr) (receiver *a.Expr, method t.ID, args []*a.Node) {
36	if n.Operator() != t.IDOpenParen {
37		return nil, 0, nil
38	}
39	args = n.Args()
40	n = n.LHS().AsExpr()
41	if n.Operator() != t.IDDot {
42		return nil, 0, nil
43	}
44	return n.LHS().AsExpr(), n.Ident(), args
45}
46
47func (q *checker) optimizeIOMethodAdvance(receiver *a.Expr, advance *big.Int, update bool) (retOK bool, retErr error) {
48	// TODO: do two passes? The first one to non-destructively check retOK. The
49	// second one to drop facts if indeed retOK? Otherwise, an advance
50	// precondition failure will lose some of the facts in its error message.
51
52	retErr = q.facts.update(func(x *a.Expr) (*a.Expr, error) {
53		// TODO: update (discard?) any facts that merely mention
54		// receiver.available(), even if they aren't an exact match.
55
56		op := x.Operator()
57		if op != t.IDXBinaryGreaterEq && op != t.IDXBinaryGreaterThan {
58			return x, nil
59		}
60
61		rcv := x.RHS().AsExpr().ConstValue()
62		if rcv == nil {
63			return x, nil
64		}
65
66		// Check that lhs is "receiver.available()".
67		lhs := x.LHS().AsExpr()
68		if lhs.Operator() != t.IDOpenParen || len(lhs.Args()) != 0 {
69			return x, nil
70		}
71		lhs = lhs.LHS().AsExpr()
72		if lhs.Operator() != t.IDDot || lhs.Ident() != t.IDAvailable {
73			return x, nil
74		}
75		lhs = lhs.LHS().AsExpr()
76		if !lhs.Eq(receiver) {
77			return x, nil
78		}
79
80		// Check if the bytes available is >= the bytes needed. If so, update
81		// rcv to be the bytes remaining. If not, discard the fact x.
82		if op == t.IDXBinaryGreaterThan {
83			op = t.IDXBinaryGreaterEq
84			rcv = big.NewInt(0).Add(rcv, one)
85		}
86		if rcv.Cmp(advance) < 0 {
87			if !update {
88				return x, nil
89			}
90			return nil, nil
91		}
92
93		retOK = true
94
95		if !update {
96			return x, nil
97		}
98
99		if rcv.Cmp(advance) == 0 {
100			// TODO: delete the (adjusted) fact, as newRCV will be zero, and
101			// "foo.advance() >= 0" is redundant.
102		}
103
104		// Create a new a.Expr to hold the adjusted RHS constant value, newRCV.
105		newRCV := big.NewInt(0).Sub(rcv, advance)
106		o, err := makeConstValueExpr(q.tm, newRCV)
107		if err != nil {
108			return nil, err
109		}
110
111		return a.NewExpr(x.AsNode().AsRaw().Flags(),
112			t.IDXBinaryGreaterEq, 0, 0, x.LHS(), nil, o.AsNode(), nil), nil
113	})
114	return retOK, retErr
115}
116