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