1 require({ version
: '1.8' });
2 require({ after_gcc_pass
: 'cfg' });
4 include('treehydra.js');
7 include('gcc_util.js');
8 include('gcc_print.js');
9 include('unstable/adts.js');
10 include('unstable/analysis.js');
11 include('unstable/esp.js');
12 let Zero_NonZero
= {};
13 include('unstable/zero_nonzero.js', Zero_NonZero
);
15 include('mayreturn.js');
17 function safe_location_of(t
) {
19 return UNKNOWN_LOCATION
;
21 return location_of(t
);
24 MapFactory
.use_injective
= true;
26 // Print a trace for each function analyzed
27 let TRACE_FUNCTIONS
= 0;
28 // Trace operation of the ESP analysis, use 2 or 3 for more detail
30 // Trace determination of function call parameter semantics, 2 for detail
31 let TRACE_CALL_SEM
= 0;
32 // Print time-taken stats
34 // Log analysis results in a special format
35 let LOG_RESULTS
= false;
37 let WARN_ON_SET_NULL
= false;
39 // Filter functions to process per CLI
41 if (this.arg
== undefined || this.arg
== '') {
42 func_filter = function(fd
) true;
44 func_filter = function(fd
) function_decl_name(fd
) == this.arg
;
47 function process_tree(func_decl
) {
48 if (!func_filter(func_decl
)) return;
50 // Determine outparams and return if function not relevant
51 if (is_constructor(func_decl
)) return;
52 let psem
= OutparamCheck
.prototype.func_param_semantics(func_decl
);
53 if (!psem
.some(function(x
) x
.check
)) return;
54 let decl
= rectify_function_decl(func_decl
);
55 if (decl
.resultType
!= 'nsresult' && decl
.resultType
!= 'PRBool' &&
56 decl
.resultType
!= 'void') {
57 warning("Cannot analyze outparam usage for function with return type '" +
58 decl
.resultType
+ "'", location_of(func_decl
));
62 let params
= [ v
for (v
in flatten_chain(DECL_ARGUMENTS(func_decl
))) ];
63 let outparam_list
= [];
65 for (let i
= 0; i
< psem
.length
; ++i
) {
67 outparam_list
.push(params
[i
]);
68 psem_list
.push(psem
[i
]);
71 if (outparam_list
.length
== 0) return;
73 // At this point we have a function we want to analyze
74 let fstring
= rfunc_string(decl
);
75 if (TRACE_FUNCTIONS
) {
76 print('* function ' + fstring
);
77 print(' ' + loc_string(location_of(func_decl
)));
79 if (TRACE_PERF
) timer_start(fstring
);
80 for (let i
= 0; i
< outparam_list
.length
; ++i
) {
81 let p
= outparam_list
[i
];
82 if (TRACE_FUNCTIONS
) {
83 print(" outparam " + expr_display(p
) + " " + DECL_UID(p
) + ' ' +
88 let cfg
= function_decl_cfg(func_decl
);
90 let [retvar
, retvars
] = function() {
92 let a
= new MayReturnAnalysis(cfg
, trace
);
94 return [a
.retvar
, a
.vbls
];
96 if (retvar
== undefined && decl
.resultType
!= 'void') throw new Error("assert");
99 let trace
= TRACE_ESP
;
100 for (let i
= 0; i
< outparam_list
.length
; ++i
) {
101 let psem
= [ psem_list
[i
] ];
102 let outparam
= [ outparam_list
[i
] ];
103 let a
= new OutparamCheck(cfg
, psem
, outparam
, retvar
, retvars
, trace
);
104 // This is annoying, but this field is only used for logging anyway.
105 a
.fndecl
= func_decl
;
107 a
.check(decl
.resultType
== 'void', func_decl
);
111 if (TRACE_PERF
) timer_stop(fstring
);
114 function is_constructor(function_decl
)
116 return function_decl
.decl_common
.lang_specific
.decl_flags
.constructor_attr
;
119 // Outparam check analysis
120 function OutparamCheck(cfg
, psem_list
, outparam_list
, retvar
, retvar_set
,
122 // We need to save the retvars so we can detect assignments through
123 // their addresses passed as arguments.
124 this.retvar_set
= retvar_set
;
125 this.retvar
= retvar
;
127 // We need both an ordered set and a lookup structure
128 this.outparam_list
= outparam_list
129 this.outparams
= create_decl_set(outparam_list
);
130 this.psem_list
= psem_list
;
132 // Set up property state vars for ESP
134 for each (let v
in outparam_list
) {
135 psvar_list
.push(new ESP
.PropVarSpec(v
, true, av
.NOT_WRITTEN
));
137 for (let v
in retvar_set
.items()) {
138 psvar_list
.push(new ESP
.PropVarSpec(v
, v
== this.retvar
, ESP
.TOP
));
142 for each (let v
in this.psvar_list
) {
143 print(" " + expr_display(v
.vbl
));
146 this.zeroNonzero
= new Zero_NonZero
.Zero_NonZero();
147 ESP
.Analysis
.call(this, cfg
, psvar_list
, av
.meet
, trace
);
150 // Abstract values for outparam check
151 function AbstractValue(name
, ch
) {
156 AbstractValue
.prototype.equals = function(v
) {
160 AbstractValue
.prototype.toString = function() {
161 return this.name
+ ' (' + this.ch
+ ')';
164 AbstractValue
.prototype.toShortString = function() {
169 // Abstract values for outparam contents write status
170 [ 'NULL', 'x' ], // is a null pointer
171 [ 'NOT_WRITTEN', '-' ], // not written
172 [ 'WROTE_NULL', '/' ], // had NULL written to
173 [ 'WRITTEN', '+' ], // had anything written to
174 // MAYBE_WRITTEN is special. "Officially", it means the same thing as
175 // NOT_WRITTEN. What it really means is that an outparam was passed
176 // to another function as a possible outparam (outparam type, but not
177 // in last position), so if there is an error with it not being written,
178 // we can give a hint about the possible outparam in the warning.
179 [ 'MAYBE_WRITTEN', '?' ], // written if possible outparam is one
183 for each (let [name
, ch
] in avspec
) {
184 av
[name
] = new AbstractValue(name
, ch
);
187 av
.ZERO
= Zero_NonZero
.Lattice
.ZERO
;
188 av
.NONZERO
= Zero_NonZero
.Lattice
.NONZERO
;
191 av.ZERO.negation = av.NONZERO;
192 av.NONZERO.negation = av.ZERO;
194 // Abstract values for int constants. We use these to figure out feasible
195 // paths in the presence of GCC finally_tmp-controlled switches.
196 function makeIntAV(v) {
197 let key = 'int_' + v;
198 if (cachedAVs.hasOwnProperty(key)) return cachedAVs[key];
201 let ans = cachedAVs[key] = new AbstractValue(s, s);
209 // Abstract values for pointers that contain a copy of an outparam
210 // pointer. We use these to figure out writes to a casted copy of
211 // an outparam passed to another method.
212 function makeOutparamAV(v
) {
213 let key
= 'outparam_' + DECL_UID(v
);
214 if (key
in cachedAVs
) return cachedAVs
[key
];
216 let ans
= cachedAVs
[key
] =
217 new AbstractValue('OUTPARAM:' + expr_display(v
), 'P');
222 /** Return the integer value if this is an integer av, otherwise undefined. */
223 av
.intVal = function(v
) {
224 if (v
.hasOwnProperty('int_val'))
229 /** Meet function for our abstract values. */
230 av
.meet = function(v1
, v2
) {
231 // At this point we know v1 != v2.
233 if (values
.indexOf(av
.LOCKED
) != -1
234 || values
.indexOf(av
.UNLOCKED
) != -1)
235 return ESP
.NOT_REACHED
;
237 return Zero_NonZero
.meet(v1
, v2
)
240 // Outparam check analysis
241 OutparamCheck
.prototype = new ESP
.Analysis
;
243 OutparamCheck
.prototype.split = function(vbl
, v
) {
244 // Can't happen for current version of ESP, but could change
245 if (v
!= ESP
.TOP
) throw new Error("not implemented");
246 return [ av
.ZERO
, av
.NONZERO
];
249 OutparamCheck
.prototype.updateEdgeState = function(e
) {
250 e
.state
.keepOnly(e
.dest
.keepVars
);
253 OutparamCheck
.prototype.flowState = function(isn
, state
) {
254 switch (TREE_CODE(isn
)) {
255 case GIMPLE_MODIFY_STMT
:
256 this.processAssign(isn
, state
);
259 this.processCall(undefined, isn
, isn
, state
);
263 // This gets handled by flowStateCond instead, has no exec effect
266 this.zeroNonzero
.flowState(isn
, state
);
270 OutparamCheck
.prototype.flowStateCond = function(isn
, truth
, state
) {
271 this.zeroNonzero
.flowStateCond(isn
, truth
, state
);
274 // For any outparams-specific semantics, we handle it here and then
275 // return. Otherwise we delegate to the zero-nonzero analysis.
276 OutparamCheck
.prototype.processAssign = function(isn
, state
) {
277 let lhs
= isn
.operands()[0];
278 let rhs
= isn
.operands()[1];
281 // Unwrap NOP_EXPR, which is semantically a copy.
282 if (TREE_CODE(rhs
) == NOP_EXPR
) {
283 rhs
= rhs
.operands()[0];
286 if (DECL_P(rhs
) && this.outparams
.has(rhs
)) {
287 // Copying an outparam pointer. We have to remember this so that
288 // if it is assigned thru later, we pick up the write.
289 state
.assignValue(lhs
, makeOutparamAV(rhs
), isn
);
293 // Cases of this switch that handle something should return from
294 // the function. Anything that does not return is picked up afteward.
295 switch (TREE_CODE(rhs
)) {
297 if (this.outparams
.has(lhs
)) {
298 warning("assigning to outparam pointer");
303 // We only care about testing outparams for NULL (and then not writing)
304 let [op1
, op2
] = rhs
.operands();
305 if (DECL_P(op1
) && this.outparams
.has(op1
) && expr_literal_int(op2
) == 0) {
306 state
.update(function(ss
) {
307 let [s1
, s2
] = [ss
, ss
.copy()]; // s1 true, s2 false
308 s1
.assignValue(lhs
, av
.NONZERO
, isn
);
309 s1
.assignValue(op1
, av
.NULL
, isn
);
310 s2
.assignValue(lhs
, av
.ZERO
, isn
);
318 let fname
= call_function_name(rhs
);
319 if (fname
== 'NS_FAILED') {
320 this.processTest(lhs
, rhs
, av
.NONZERO
, isn
, state
);
321 } else if (fname
== 'NS_SUCCEEDED') {
322 this.processTest(lhs
, rhs
, av
.ZERO
, isn
, state
);
323 } else if (fname
== '__builtin_expect') {
324 // Same as an assign from arg 0 to lhs
325 state
.assign(lhs
, call_args(rhs
)[0], isn
);
327 this.processCall(lhs
, rhs
, isn
, state
);
332 // If rhs is *outparam and pointer-typed, lhs is NULL iff rhs is
333 // WROTE_NULL. Required for testcase onull.cpp.
334 let v
= rhs
.operands()[0];
335 if (DECL_P(v
) && this.outparams
.has(v
) &&
336 TREE_CODE(TREE_TYPE(v
)) == POINTER_TYPE
) {
337 state
.update(function(ss
) {
338 let val
= ss
.get(v
) == av
.WROTE_NULL
? av
.ZERO
: av
.NONZERO
;
339 ss
.assignValue(lhs
, val
, isn
);
346 // Nothing special -- delegate
347 this.zeroNonzero
.processAssign(isn
, state
);
351 switch (TREE_CODE(lhs
)) {
353 // Writing to an outparam. We want to try to figure out if we're
355 let e
= TREE_OPERAND(lhs
, 0);
356 if (this.outparams
.has(e
)) {
357 if (expr_literal_int(rhs
) == 0) {
358 state
.assignValue(e
, av
.WROTE_NULL
, isn
);
359 } else if (DECL_P(rhs
)) {
360 state
.update(function(ss
) {
361 let [s1
, s2
] = [ss
.copy(), ss
]; // s1 NULL, s2 non-NULL
362 s1
.assignValue(e
, av
.WROTE_NULL
, isn
);
363 s1
.assignValue(rhs
, av
.ZERO
, isn
);
364 s2
.assignValue(e
, av
.WRITTEN
, isn
);
365 s2
.assignValue(rhs
, av
.NONZERO
, isn
);
369 state
.assignValue(e
, av
.WRITTEN
, isn
);
372 // unsound -- could be writing to anything through this ptr
375 case COMPONENT_REF
: // unsound
376 case ARRAY_REF
: // unsound
381 print(TREE_CODE(lhs
));
382 throw new Error("ni");
386 // Handle an assignment x := test(foo) where test is a simple predicate
387 OutparamCheck
.prototype.processTest = function(lhs
, call
, val
, blame
, state
) {
388 let arg
= call_arg(call
, 0);
390 this.zeroNonzero
.predicate(state
, lhs
, val
, arg
, blame
);
392 state
.assignValue(lhs
, ESP
.TOP
, blame
);
396 // The big one: outparam semantics of function calls.
397 OutparamCheck
.prototype.processCall = function(dest
, expr
, blame
, state
) {
398 let args
= call_args(expr
);
399 let callable
= callable_arg_function_decl(CALL_EXPR_FN(expr
));
400 let psem
= this.func_param_semantics(callable
);
402 if (TRACE_CALL_SEM
) {
403 print("param semantics:" + psem
);
406 if (args
.length
!= psem
.length
) {
407 let ct
= TREE_TYPE(callable
);
408 if (TREE_CODE(ct
) == POINTER_TYPE
) ct
= TREE_TYPE(ct
);
409 if (args
.length
< psem
.length
|| !stdarg_p(ct
)) {
410 let name
= function_decl_name(callable
);
411 // TODO Can __builtin_memcpy write to an outparam? Probably not.
412 if (name
!= 'operator new' && name
!= 'operator delete' &&
413 name
!= 'operator new []' && name
!= 'operator delete []' &&
414 name
.substr(0, 5) != '__cxa' &&
415 name
.substr(0, 9) != '__builtin') {
416 throw Error("bad len for '" + name
+ "': " + args
.length
+ ' args, ' +
417 psem
.length
+ ' params');
422 // Collect variables that are possibly written to on callee success
424 for (let i
= 0; i
< psem
.length
; ++i
) {
426 // The arg could be the address of a return-value variable.
427 // This means it's really the nsresult code for the call,
428 // so we treat it the same as the target of an rv assignment.
429 if (TREE_CODE(arg
) == ADDR_EXPR
) {
430 let v
= arg
.operands()[0];
431 if (DECL_P(v
) && this.retvar_set
.has(v
)) {
435 // The arg could be a copy of an outparam. We'll unwrap to the
436 // outparam if it is. The following is cheating a bit because
437 // we munge states together, but it should be OK in practice.
438 arg
= unwrap_outparam(arg
, state
);
440 if (sem
== ps
.CONST
) continue;
441 // At this point, we know the call can write thru this param.
442 // Invalidate any vars whose addresses are passed here. This
443 // is distinct from the rv handling above.
444 if (TREE_CODE(arg
) == ADDR_EXPR
) {
445 let v
= arg
.operands()[0];
450 if (!DECL_P(arg
) || !this.outparams
.has(arg
)) continue;
451 // At this point, we may be writing to an outparam
452 updates
.push([arg
, sem
]);
455 if (updates
.length
) {
456 if (dest
!= undefined && DECL_P(dest
)) {
457 // Update & stored rv. Do updates predicated on success.
458 let [ succ_ret
, fail_ret
] = ret_coding(callable
);
460 state
.update(function(ss
) {
461 let [s1
, s2
] = [ss
.copy(), ss
]; // s1 success, s2 fail
462 for each (let [vbl
, sem
] in updates
) {
463 s1
.assignValue(vbl
, sem
.val
, blame
);
464 s1
.assignValue(dest
, succ_ret
, blame
);
466 s2
.assignValue(dest
, fail_ret
, blame
);
470 // Discarded rv. Per spec in the bug, we assume that either success
471 // or failure is possible (if not, callee should return void).
472 // Exceptions: Methods that return void and string mutators are
473 // considered no-fail.
474 state
.update(function(ss
) {
475 for each (let [vbl
, sem
] in updates
) {
476 if (sem
== ps
.OUTNOFAIL
|| sem
== ps
.OUTNOFAILNOCHECK
) {
477 ss
.assignValue(vbl
, av
.WRITTEN
, blame
);
480 let [s1
, s2
] = [ss
.copy(), ss
]; // s1 success, s2 fail
481 for each (let [vbl
, sem
] in updates
) {
482 s1
.assignValue(vbl
, sem
.val
, blame
);
490 // no updates, just kill any destination for the rv
491 if (dest
!= undefined && DECL_P(dest
)) {
492 state
.remove(dest
, blame
);
497 /** Return the return value coding of the given function. This is a pair
498 * [ succ, fail ] giving the abstract values of the return value under
499 * success and failure conditions. */
500 function ret_coding(callable
) {
501 let type
= TREE_TYPE(callable
);
502 if (TREE_CODE(type
) == POINTER_TYPE
) type
= TREE_TYPE(type
);
504 let rtname
= TYPE_NAME(TREE_TYPE(type
));
505 if (rtname
&& IDENTIFIER_POINTER(DECL_NAME(rtname
)) == 'PRBool') {
506 return [ av
.NONZERO
, av
.ZERO
];
508 return [ av
.ZERO
, av
.NONZERO
];
512 function unwrap_outparam(arg
, state
) {
513 if (!DECL_P(arg
) || state
.factory
.outparams
.has(arg
)) return arg
;
516 for (let ss
in state
.substates
.getValues()) {
517 let val
= ss
.get(arg
);
518 if (val
!= undefined && val
.hasOwnProperty('outparam')) {
519 outparam
= val
.outparam
;
522 if (outparam
) return outparam
;
526 // Check for errors. Must .run() analysis before calling this.
527 OutparamCheck
.prototype.check = function(isvoid
, fndecl
) {
528 let state
= this.cfg
.x_exit_block_ptr
.stateOut
;
529 for (let substate
in state
.substates
.getValues()) {
530 this.checkSubstate(isvoid
, fndecl
, substate
);
534 OutparamCheck
.prototype.checkSubstate = function(isvoid
, fndecl
, ss
) {
536 this.checkSubstateSuccess(ss
);
538 let [succ
, fail
] = ret_coding(fndecl
);
539 let rv
= ss
.get(this.retvar
);
540 // We want to check if the abstract value of the rv is entirely
541 // contained in the success or failure condition.
542 if (av
.meet(rv
, succ
) == rv
) {
543 this.checkSubstateSuccess(ss
);
544 } else if (av
.meet(rv
, fail
) == rv
) {
545 this.checkSubstateFailure(ss
);
547 // This condition indicates a bug in outparams.js. We'll just
548 // warn so we don't break static analysis builds.
549 warning("Outparams checker cannot determine rv success/failure",
550 location_of(fndecl
));
551 this.checkSubstateSuccess(ss
);
552 this.checkSubstateFailure(ss
);
557 /* @return The return statement in the function
558 * that writes the return value in the given substate.
559 * If the function returns void, then the substate doesn't
560 * matter and we just look for the return. */
561 OutparamCheck
.prototype.findReturnStmt = function(ss
) {
562 if (this.retvar
!= undefined)
563 return ss
.getBlame(this.retvar
);
565 if (this.cfg
._cached_return
)
566 return this.cfg
._cached_return
;
568 for (let bb
in cfg_bb_iterator(this.cfg
)) {
569 for (let isn
in bb_isn_iterator(bb
)) {
570 if (TREE_CODE(isn
) == RETURN_EXPR
) {
571 return this.cfg
._cached_return
= isn
;
579 OutparamCheck
.prototype.checkSubstateSuccess = function(ss
) {
580 for (let i
= 0; i
< this.psem_list
.length
; ++i
) {
581 let [v
, psem
] = [ this.outparam_list
[i
], this.psem_list
[i
] ];
582 if (psem
== ps
.INOUT
) continue;
584 if (val
== av
.NOT_WRITTEN
) {
585 this.logResult('succ', 'not_written', 'error');
586 this.warn([this.findReturnStmt(ss
), "outparam '" + expr_display(v
) + "' not written on NS_SUCCEEDED(return value)"],
587 [v
, "outparam declared here"]);
588 } else if (val
== av
.MAYBE_WRITTEN
) {
589 this.logResult('succ', 'maybe_written', 'error');
591 let blameStmt
= ss
.getBlame(v
);
595 let callExpr
= blameStmt
.tree_check(GIMPLE_MODIFY_STMT
).
596 operands()[1].tree_check(CALL_EXPR
);
597 let callDecl
= callable_arg_function_decl(CALL_EXPR_FN(callExpr
));
599 callMsg
= [callDecl
, "declared here"];
600 callName
= " '" + decl_name(callDecl
) + "'";
602 catch (e
if e
.TreeCheckError
) { }
604 this.warn([this.findReturnStmt(ss
), "outparam '" + expr_display(v
) + "' not written on NS_SUCCEEDED(return value)"],
605 [v
, "outparam declared here"],
606 [blameStmt
, "possibly written by unannotated function call" + callName
],
609 this.logResult('succ', '', 'ok');
614 OutparamCheck
.prototype.checkSubstateFailure = function(ss
) {
615 for (let i
= 0; i
< this.psem_list
.length
; ++i
) {
616 let [v
, ps
] = [ this.outparam_list
[i
], this.psem_list
[i
] ];
618 if (val
== av
.WRITTEN
) {
619 this.logResult('fail', 'written', 'error');
620 this.warn([this.findReturnStmt(ss
), "outparam '" + expr_display(v
) + "' written on NS_FAILED(return value)"],
621 [v
, "outparam declared here"],
622 [ss
.getBlame(v
), "written here"]);
623 } else if (val
== av
.WROTE_NULL
) {
624 this.logResult('fail', 'wrote_null', 'warning');
625 if (WARN_ON_SET_NULL
) {
626 this.warn([this.findReturnStmt(ss
), "NULL written to outparam '" + expr_display(v
) + "' on NS_FAILED(return value)"],
627 [v
, "outparam declared here"],
628 [ss
.getBlame(v
), "written here"]);
631 this.logResult('fail', '', 'ok');
637 * Generate a warning from one or more tuples [treeforloc, message]
639 OutparamCheck
.prototype.warn = function(arg0
) {
640 let loc
= safe_location_of(arg0
[0]);
643 for (let i
= 1; i
< arguments
.length
; ++i
) {
644 if (arguments
[i
] === undefined) continue;
645 let [atree
, amsg
] = arguments
[i
];
646 msg
+= "\n" + loc_string(safe_location_of(atree
)) + ": " + amsg
;
651 OutparamCheck
.prototype.logResult = function(rv
, msg
, kind
) {
653 let s
= [ '"' + x
+ '"' for each (x
in [ loc_string(location_of(this.fndecl
)), function_decl_name(this.fndecl
), rv
, msg
, kind
]) ].join(', ');
654 print(":LR: (" + s
+ ")");
658 // Parameter Semantics values -- indicates whether a parameter is
660 // label Used for debugging output
661 // val Abstract value (state) that holds on an argument after
663 // check True if parameters with this semantics should be
664 // checked by this analysis
666 OUTNOFAIL
: { label
: 'out-no-fail', val
: av
.WRITTEN
, check
: true },
667 // Special value for receiver of strings methods. Callers should
668 // consider this to be an outparam (i.e., it modifies the string),
669 // but we don't want to check the method itself.
670 OUTNOFAILNOCHECK
: { label
: 'out-no-fail-no-check' },
671 OUT
: { label
: 'out', val
: av
.WRITTEN
, check
: true },
672 INOUT
: { label
: 'inout', val
: av
.WRITTEN
, check
: true },
673 MAYBE
: { label
: 'maybe', val
: av
.MAYBE_WRITTEN
}, // maybe out
674 CONST
: { label
: 'const' } // i.e. not out
677 // Return the param semantics of a FUNCTION_DECL or VAR_DECL representing
678 // a function pointer. The result is a pair [ ann, sems ].
679 OutparamCheck
.prototype.func_param_semantics = function(callable
) {
680 let ftype
= TREE_TYPE(callable
);
681 if (TREE_CODE(ftype
) == POINTER_TYPE
) ftype
= TREE_TYPE(ftype
);
682 // What failure semantics to use for outparams
683 let rtype
= TREE_TYPE(ftype
);
684 let nofail
= TREE_CODE(rtype
) == VOID_TYPE
;
685 // Whether to guess outparams by type
686 let guess
= type_string(rtype
) == 'nsresult';
688 // Set up param lists for analysis
689 let params
; // param decls, if available
690 let types
; // param types
691 let string_mutator
= false;
692 if (TREE_CODE(callable
) == FUNCTION_DECL
) {
693 params
= [ p
for (p
in function_decl_params(callable
)) ];
694 types
= [ TREE_TYPE(p
) for each (p
in params
) ];
695 string_mutator
= is_string_mutator(callable
);
697 types
= [ p
for (p
in function_type_args(ftype
))
698 if (TREE_CODE(p
) != VOID_TYPE
) ];
703 for (let i
= 0; i
< types
.length
; ++i
) {
705 if (i
== 0 && string_mutator
) {
706 // Special case: string mutator receiver is an no-fail outparams
708 sem
= ps
.OUTNOFAILNOCHECK
;
710 if (params
) sem
= decode_attr(DECL_ATTRIBUTES(params
[i
]));
711 if (TRACE_CALL_SEM
>= 2) print("param " + i
+ ": annotated " + sem
);
712 if (sem
== undefined) {
713 sem
= decode_attr(TYPE_ATTRIBUTES(types
[i
]));
714 if (TRACE_CALL_SEM
>= 2) print("type " + i
+ ": annotated " + sem
);
715 if (sem
== undefined) {
716 if (guess
&& type_is_outparam(types
[i
])) {
717 // Params other than last are guessed as MAYBE
718 sem
= i
< types
.length
- 1 ? ps
.MAYBE
: ps
.OUT
;
724 if (sem
== ps
.OUT
&& nofail
) sem
= ps
.OUTNOFAIL
;
726 if (sem
== undefined) throw new Error("assert");
732 /* Decode parameter semantics GCC attributes.
733 * @param attrs GCC attributes of a parameter. E.g., TYPE_ATTRIBUTES
734 * or DECL_ATTRIBUTES of an item
735 * @return The parameter semantics value defined by the attributes,
736 * or undefined if no such attributes were present. */
737 function decode_attr(attrs
) {
738 // Note: we're not checking for conflicts, we just take the first
740 for each (let attr
in rectify_attributes(attrs
)) {
741 if (attr
.name
== 'user') {
742 for each (let arg
in attr
.args
) {
743 if (arg
== 'NS_outparam') {
745 } else if (arg
== 'NS_inoutparam') {
747 } else if (arg
== 'NS_inparam') {
756 /* @return true if the given type appears to be an outparam
757 * type based on the type alone (i.e., not considering
759 function type_is_outparam(type
) {
760 switch (TREE_CODE(type
)) {
762 return pointer_type_is_outparam(TREE_TYPE(type
));
764 let rt
= TREE_TYPE(type
);
765 return !TYPE_READONLY(rt
) && is_string_type(rt
);
767 // Note: This is unsound for UNION_TYPE, because the union could
768 // contain a pointer.
773 /* Helper for type_is_outparam.
774 * @return true if 'pt *' looks like an outparam type. */
775 function pointer_type_is_outparam(pt
) {
776 if (TYPE_READONLY(pt
)) return false;
778 switch (TREE_CODE(pt
)) {
781 // Look for void **, nsIFoo **, char **, PRUnichar **
782 let ppt
= TREE_TYPE(pt
);
783 let tname
= TYPE_NAME(ppt
);
784 if (tname
== undefined) return false;
785 let name
= decl_name_string(tname
);
786 return name
== 'void' || name
== 'char' || name
== 'PRUnichar' ||
787 name
.substr(0, 3) == 'nsI';
790 // char * and PRUnichar * are probably strings, otherwise guess
791 // it is an integer outparam.
792 let name
= decl_name_string(TYPE_NAME(pt
));
793 return name
!= 'char' && name
!= 'PRUnichar';
800 // TODO: should we consider field writes?
806 throw new Error("can't guess if a pointer to this type is an outparam: " +
807 TREE_CODE(pt
) + ': ' + type_string(pt
));
811 // Map type name to boolean as to whether it is a string.
812 let cached_string_types
= MapFactory
.create_map(
813 function (x
, y
) x
== y
,
818 // Base string types. Others will be found by searching the inheritance
821 cached_string_types
.put('nsAString', true);
822 cached_string_types
.put('nsACString', true);
823 cached_string_types
.put('nsAString_internal', true);
824 cached_string_types
.put('nsACString_internal', true);
826 // Return true if the given type represents a Mozilla string type.
827 // The binfo arg is the binfo to use for further iteration. This is
828 // for internal use only, users of this function should pass only
830 function is_string_type(type
, binfo
) {
831 if (TREE_CODE(type
) != RECORD_TYPE
) return false;
832 //print(">>>IST " + type_string(type));
833 let name
= decl_name_string(TYPE_NAME(type
));
834 let ans
= cached_string_types
.get(name
);
835 if (ans
!= undefined) return ans
;
838 binfo
= binfo
!= undefined ? binfo
: TYPE_BINFO(type
);
839 if (binfo
!= undefined) {
840 for each (let base
in VEC_iterate(BINFO_BASE_BINFOS(binfo
))) {
841 let parent_ans
= is_string_type(BINFO_TYPE(base
), base
);
848 cached_string_types
.put(name
, ans
);
849 //print("<<<IST " + type_string(type) + ' ' + ans);
853 function is_string_ptr_type(type
) {
854 return TREE_CODE(type
) == POINTER_TYPE
&& is_string_type(TREE_TYPE(type
));
857 // Return true if the given function is a mutator method of a Mozilla
859 function is_string_mutator(fndecl
) {
860 let first_param = function() {
861 for (let p
in function_decl_params(fndecl
)) {
867 return first_param
!= undefined &&
868 decl_name_string(first_param
) == 'this' &&
869 is_string_ptr_type(TREE_TYPE(first_param
)) &&
870 !TYPE_READONLY(TREE_TYPE(TREE_TYPE(first_param
)));