1 /* -*- indent-tabs-mode: nil; tab-width: 4 -*- */
2 /* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
3 /* ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
16 * The Original Code is [Open Source Virtual Machine.].
18 * The Initial Developer of the Original Code is
19 * Adobe System Incorporated.
20 * Portions created by the Initial Developer are Copyright (C) 2010
21 * the Initial Developer. All Rights Reserved.
26 * Alternatively, the contents of this file may be used under the terms of
27 * either the GNU General Public License Version 2 or later (the "GPL"), or
28 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
38 * ***** END LICENSE BLOCK ***** */
40 // exactgc.as generates exact tracing code from annotated classes. It
41 // is usually run by builtin-tracers.py, shell_toplevel-tracers.py,
42 // and similar scripts, and occasionally cooperates with nativegen.py.
44 // The <a href="doc/mmgc/exactgc-cookbook.html">cookbook</a> provides a gentle
45 // introduction to annotations; most people need look no further.
47 // The <a href="doc/mmgc/exactgc-manual.html">manual</a> provides a complete
48 // reference manual for exact GC and describes every facility provided
49 // by exactgc.as and more.
54 // exactgc.as extracts native attributes from ".as" and ".h" files and
55 // generates C++ code. Little is assumed about the structure of the
56 // files; indeed the script looks only for annotations, never for C++
57 // or AS3 phrases, and you can blindly feed a lot of files to it and
58 // expect the right thing to happen.
62 // avm exactgc.abc -- -b avmplus-as3-gc.h -n avmplus-cpp-gc.h -i avmplus-gc-interlock.h *.h *.as
66 // filename A file to process
67 // @filename A file from which to read more file names
68 // -x filename A file to process (useful if the filename starts with '@' or '-')
69 // -n filename Emit tracers for GC_CPP_EXACT ("natives") to this file
70 // -b filename Emit tracers for GC_AS3_EXACT ("builtins") to this file
71 // -i filename Emit interlock definitons to this file
72 // -ns namespace The C++ namespace to wrap around the output, default global
77 // The script has been tuned quite a bit, and there's a built-in profiling option
78 // to help us tune more (search for 'var profiling' below). Even so, about
79 // 90% of the time is still spent in reading input and extracting annotations, so
80 // obvious things to do if further performance improvements are needed would be:
82 // - cache intermediate data (eg, save extracted lines + file's path and mtime)
83 // - don't split the file into individual lines (splitting takes some time).
85 // The design is currently line-based because I would like to expand it later to
86 // include various kinds of error checking that goes hand-in-hand with SafeGC, eg,
87 // if GCMember appears on a line then there should be a GC annotation too. So
88 // it would be good to try caching first.
92 // For [native] annotations on AS3 class definitions that have "gc",
93 // "clsgc", or "instancegc" annotations.
96 function AS3Class
(ns
, cls
) {
97 // This is for dealing with the "::avmshell::" prefix in some native annotations,
98 // though there is probably a better way.
99 var s
= "::" + ns
+ "::";
100 if (cls
.indexOf
(s
) == 0)
101 cls
= cls
.substring
(s
.length
);
105 public function toString
() { return printProps
(this, ["cls"]) }
111 // For GC_AS3_EXACT and GC_CPP_EXACT annotations on C++ class
112 // definitions, also for the _WITH_HOOK variants. Note
113 // _WITH_HOOK_{IF,IFDEF,IFNDEF} means we can have both hooks
114 // and conditional compilation.
118 function GCClass
(tag
, attr
) {
119 cls
= getAttr
(attr
, 0);
120 base
= getAttr
(attr
, 1);
121 hook
= tag
.match
("_WITH_HOOK") != null;
122 ifdef
= tag
.match
("_IFDEF") != null ? getAttr
(attr
,2) : false;
123 ifndef
= tag
.match
("_IFNDEF") != null ? getAttr
(attr
,2) : false;
124 if_
= !ifdef
&& !ifndef
&& tag
.match
("_IF") != null ? getAttr
(attr
,2) : false;
127 public function toString
() { return printProps
(this, ["cls","base","hook","ifdef"]) }
129 public function fullName
() { return fullClassPrefix
+ cls
; }
138 var fullClassPrefix
=""; // for nested classes
139 var out
= new Printer
(1);
141 var fieldList
= []; // sorted by property name
143 var variable_length_field
= null; // does not appear in the 'fields' list
144 var probablyLarge
= false;
148 class GCCppExact
extends GCClass
{ function GCCppExact
(tag
,attr
) { super(tag
,attr
) } }
149 class GCAS3Exact
extends GCClass
{ function GCAS3Exact
(tag
,attr
) { super(tag
,attr
) } }
152 // For GC_DATA_BEGIN and GC_DATA_END annotations in C++ classes.
155 function GCDataSection
(tag
, attr
) {
156 cls
= getAttr
(attr
, 0);
159 public function toString
() { return printProps
(this, ["cls"]) }
164 class GCDataBegin
extends GCDataSection
{ function GCDataBegin
(tag
, attr
) { super(tag
,attr
) } }
165 class GCDataEnd
extends GCDataSection
{ function GCDataEnd
(tag
, attr
) { super(tag
,attr
) } }
166 class GCNoData
extends GCDataSection
{ function GCNoData
(tag
, attr
) { super(tag
,attr
) } }
168 // For the various field annotations in C++ classes.
170 // The cls attribute is implicitly present in the C++ source code and
171 // is inserted at the beginning of the attribute array by the code
172 // that constructs GCField instances.
174 // Any condition on the field is considered part of the field's name,
175 // to support typical cases like these:
178 // Bletch* GC_POINTER_IFDEF(p, BLAH)
180 // Blotch* GC_POINTER_IFNDEF(p, BLAH)
185 function GCField
(tag
, attr
) {
186 cls
= getAttr
(attr
, 0);
187 name
= getAttr
(attr
, 1);
188 ifdef
= tag
.match
("_IFDEF") != null ? getAttr
(attr
,2) : false;
189 ifndef
= tag
.match
("_IFNDEF") != null ? getAttr
(attr
,2) : false;
190 if_
= !ifdef
&& !ifndef
&& tag
.match
("_IF") != null ? getAttr
(attr
,2) : false;
193 public function toString
() { return printProps
(this, ["cls","name","ifdef", "if_"]) }
202 class GCPointer
extends GCField
{ function GCPointer
(tag
, attr
) { super(tag
, attr
) } }
203 class GCAtom
extends GCField
{ function GCAtom
(tag
, attr
) { super(tag
, attr
) } }
204 class GCStructure
extends GCField
{ function GCStructure
(tag
, attr
) { super(tag
, attr
) } }
205 class GCConservative
extends GCField
{ function GCConservative
(tag
, attr
) { super(tag
, attr
) } }
207 class GCPointers
extends GCField
209 function GCPointers
(tag
, attr
) {
210 splitFieldAndSize
(attr
, 1);
212 declCount
= getAttr
(attr
, 2);
213 count
= getAttr
(attr
, 3);
214 hint
= tag
.match
("_SMALL") != null ? "small" : null;
217 override public function toString
() { return printProps
(this, ["cls","name","ifdef","hint"]) }
224 class GCAtoms
extends GCField
226 function GCAtoms
(tag
, attr
) {
227 splitFieldAndSize
(attr
, 1);
229 declCount
= getAttr
(attr
, 2);
230 count
= getAttr
(attr
, 3);
231 hint
= tag
.match
("_SMALL") != null ? "small" : null;
234 override public function toString
() { return printProps
(this, ["cls","name","ifdef","hint"]) }
241 class GCStructures
extends GCField
243 function GCStructures
(tag
, attr
) {
244 splitFieldAndSize
(attr
, 1);
246 declCount
= getAttr
(attr
, 2);
247 count
= getAttr
(attr
, 3);
248 hint
= tag
.match
("_SMALL") != null ? "small" : null;
251 override public function toString
() { return printProps
(this, ["cls","name","ifdef","hint"]) }
260 function Printer
(_indent
=0) {
261 for ( var i
=0 ; i
< _indent
; i
++ )
267 if (indent
> indentString
.length
)
280 if (s
.charAt
(0) != "#")
281 output
+= indentString
.substring
(0,indent
);
282 while (s
.length
> 0 && s
.charAt
(s
.length
-1) == "\n")
283 s
= s
.substring
(0,s
.length
-1);
303 var indentString
= "";
308 { "GC_CPP_EXACT": GCCppExact
,
309 "GC_CPP_EXACT_IFDEF": GCCppExact
,
310 "GC_CPP_EXACT_WITH_HOOK": GCCppExact
,
311 "GC_CPP_EXACT_WITH_HOOK_IFDEF": GCCppExact
,
312 "GC_CPP_EXACT_WITH_HOOK_IFNDEF": GCCppExact
,
313 "GC_CPP_EXACT_WITH_HOOK_IF": GCCppExact
,
314 "GC_AS3_EXACT": GCAS3Exact
,
315 "GC_AS3_EXACT_IFDEF": GCAS3Exact
,
316 "GC_AS3_EXACT_WITH_HOOK": GCAS3Exact
,
317 "GC_AS3_EXACT_WITH_HOOK_IFDEF": GCAS3Exact
,
318 "GC_AS3_EXACT_WITH_HOOK_IFNDEF": GCAS3Exact
,
319 "GC_AS3_EXACT_WITH_HOOK_IF": GCAS3Exact
,
320 "GC_DATA_BEGIN": GCDataBegin
,
321 "GC_DATA_END": GCDataEnd
,
322 "GC_NO_DATA": GCNoData
,
324 "GC_ATOM_IF": GCAtom
,
325 "GC_ATOM_IFDEF": GCAtom
,
326 "GC_ATOM_IFNDEF": GCAtom
,
327 "GC_POINTER": GCPointer
,
328 "GC_POINTER_IF": GCPointer
,
329 "GC_POINTER_IFDEF": GCPointer
,
330 "GC_POINTER_IFNDEF": GCPointer
,
331 "GC_CONSERVATIVE": GCConservative
,
332 "GC_CONSERVATIVE_IF": GCConservative
,
333 "GC_CONSERVATIVE_IFDEF": GCConservative
,
334 "GC_CONSERVATIVE_IFNDEF": GCConservative
,
335 "GC_STRUCTURE": GCStructure
,
336 "GC_STRUCTURE_IF": GCStructure
,
337 "GC_STRUCTURE_IFDEF": GCStructure
,
338 "GC_STRUCTURE_IFNDEF": GCStructure
,
340 "GC_ATOMS_SMALL": GCAtoms
,
341 "GC_POINTERS": GCPointers
,
342 "GC_POINTERS_SMALL": GCPointers
,
343 "GC_STRUCTURES": GCStructures
,
344 "GC_STRUCTURES_SMALL": GCStructures
348 var debug
= false; // print useful debugging info
349 var profiling
= false; // profile at a function level for selected functions
350 var errorContext
= "top level"; // for error messages, updated as we go
351 const largeObjectCutoff
= 2000; // more arbitrary than not, "close" to large object limit in MMgc
354 var builtinOutputFile
= null; // null == don't output, otherwise file name
355 var nativeOutputFile
= null; // null == don't output, otherwise file name
356 var interlockOutputFile
= null; // null == don't output (and don't do interlock checking), otherwise file name
357 var cppNamespace
= ""; // wrap this namespace around the emitted code if not empty string
359 // Used during parsing and some initial processing
360 var specs
= []; // intermediate list of all metadata during parsing and field/class collection
362 // Created during processing
363 var cppClassList
= []; // all C++ classes ordered by class name
364 var as3ClassList
= []; // all AS3 classes ordered by class name
365 var cppClassMap
= {}; // maps C++ class name to GCClass
366 var as3ClassMap
= {}; // maps AS3 class name to GCClass
368 // Occasionally useful during debugging to print the attribute maps
369 Object.prototype
.toString
=
372 for ( var i
in this )
373 if (this.hasOwnProperty
(i
))
374 s
.push
(i
+ ": " + this[i
]);
375 return "{" + s
.join
(", ") + "}";
378 Array.prototype
.toString
=
380 return "[" + this.join
(",") + "]";
383 function printProps
(obj
, attrs
) {
385 for ( var i
=0 ; i
< attrs
.length
; i
++ )
386 s
.push
(attrs
[i
] + ": " + obj
[attrs
[i
]]);
387 return "{" + s
.join
(", ") + "}";
391 ("/* ***** BEGIN LICENSE BLOCK *****\n" +
392 " * Version: MPL 1.1/GPL 2.0/LGPL 2.1\n" +
394 " * The contents of this file are subject to the Mozilla Public License Version\n" +
395 " * 1.1 (the \"License\"); you may not use this file except in compliance with\n" +
396 " * the License. You may obtain a copy of the License at\n" +
397 " * http://www.mozilla.org/MPL/\n" +
399 " * Software distributed under the License is distributed on an \"AS IS\" basis,\n" +
400 " * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License\n" +
401 " * for the specific language governing rights and limitations under the\n" +
404 " * The Original Code is [Open Source Virtual Machine.].\n" +
406 " * The Initial Developer of the Original Code is\n" +
407 " * Adobe System Incorporated.\n" +
408 " * Portions created by the Initial Developer are Copyright (C) 2010\n" +
409 " * the Initial Developer. All Rights Reserved.\n" +
411 " * Contributor(s):\n" +
412 " * Adobe AS3 Team\n" +
414 " * Alternatively, the contents of this file may be used under the terms of\n" +
415 " * either the GNU General Public License Version 2 or later (the \"GPL\"), or\n" +
416 " * the GNU Lesser General Public License Version 2.1 or later (the \"LGPL\"),\n" +
417 " * in which case the provisions of the GPL or the LGPL are applicable instead\n" +
418 " * of those above. If you wish to allow use of your version of this file only\n" +
419 " * under the terms of either the GPL or the LGPL, and not to allow others to\n" +
420 " * use your version of this file under the terms of the MPL, indicate your\n" +
421 " * decision by deleting the provisions above and replace them with the notice\n" +
422 " * and other provisions required by the GPL or the LGPL. If you do not delete\n" +
423 " * the provisions above, a recipient may use your version of this file under\n" +
424 " * the terms of any one of the MPL, the GPL or the LGPL.\n" +
426 " * ***** END LICENSE BLOCK ***** */\n");
428 // Read an attribute with range checking from the attribute array
429 function getAttr
(attr
, n
)
431 if (n
>= attr
.length
)
432 fail
("Out-of-range attribute reference: " + attr
+ " " + n
);
436 // attr[n] has text of the form "<something1>[<something2>]" with no embedded commas.
437 // Break them apart, and leave <something1> in position n and insert <something2> in
440 function splitFieldAndSize
(attr
, n
)
442 if (n
>= attr
.length
)
443 fail
("Out-of-range attribute reference: " + attr
+ " " + n
);
445 var result
= (/^\s
*([^\]]*)\s
*\[([^\]]*)\]\s
*$/).exec
(v
);
446 if (result
== null || result
.length
!= 3)
447 fail
("Incorrect format for array field, expected name[size]: " + n
);
448 for ( var k
=attr
.length
; k
> n
+1 ; k
-- )
451 attr
[n
+1] = result
[2];
454 // Fail with an error message, never return
457 throw new Error(errorContext
+ ": " + s
);
460 // Compare two strings and return -1, 0, or 1
463 if (a
< b
) return -1;
468 // The following function is about 20x faster than the obvious one-liner
469 // return File.read(filename).split(/\r\n|\r|\n/);
471 // Using single regular expressions in place of the disjunction gave us
472 // a factor of five; switching to strings for the splitting another
473 // factor of four. (Reorganizing the code further, to use knowledge
474 // of the absence of \r to avoid scanning for \r\n, say, has yielded
477 function readLines
(filename
) {
478 var text
= File.read
(filename
);
479 if (text
.indexOf
("\r\n") != -1)
480 return text
.split
("\r\n");
482 if (text
.indexOf
("\r") != -1)
483 return text
.split
("\r");
485 return text
.split
("\n");
488 // Look for and record any options, return array of file names. Skip
489 // files named 'GC.h' because MMgc/GC.h includes the macros for the
490 // annotations and cannot be processed.
491 function processOptionsAndFindFiles
(args
)
495 errorContext
= "Processing options";
499 function processArgs
(args
) {
501 var limit
=args
.length
;
504 var result
= (/^\s
*(.*)\s
*$/).exec
(s
);
505 if (result
== null) // shouldn't happen
510 function getArg
(opt
) {
512 fail
("Missing argument for " + opt
);
519 builtinOutputFile
= getArg
(s
);
521 nativeOutputFile
= getArg
(s
);
523 interlockOutputFile
= getArg
(s
);
525 cppNamespace
= getArg
(s
);
527 files
.push
(getArg
(s
));
528 else if (s
.match
(/GC
\.h
$/))
530 else if (s
.charAt
(0) == "@") // indirection file
531 files
.push
.apply
(files
, readLines
(s
.substring
(1)).map
(strip
));
538 // Read files, accumulate all metadata in order in 'specs'.
540 // Syntactic side conditions:
542 // GC_DATA_BEGIN must follow a GC class spec with the same name.
543 // GC_DATA_END pops both stacks, so there can only be one data
544 // section per class.
546 // At the end of a file, both stacks must be empty.
548 function readFiles
(files
)
552 var processingTime
= 0;
553 var splittingTime
= 0;
555 var cppClassStack
= []; // tracks GC_CPP_EXACT, etc
556 var cppDataStack
= []; // tracks GC_DATA_BEGIN / GC_DATA_END
558 // TODO: factor regular expressions to avoid duplication?
560 const attrStringRegex
:RegExp = /^\s
*\"([^\"]*)\"\s
*$/;
561 const attrMiscRegex
:RegExp = /^\s
*((?:<\s
+|\s
+>|[a
-zA
-Z0
-9_
:<>])+)\s
*$/;
562 const attrNumberRegex
:RegExp = /^\s
*([0-9]+(?:\.[0-9]+)?)\s
*$/;
563 const attrArraydefRegex
:RegExp = /^\s
*([a
-zA
-Z0
-9_
]+\[[^\]]*\])\s
*$/;
565 function parseAttrValue
(s
) {
567 if ((result
= attrStringRegex
.exec
(s
)) != null ||
568 (result
= attrMiscRegex
.exec
(s
)) != null ||
569 (result
= attrNumberRegex
.exec
(s
)) != null ||
570 (result
= attrArraydefRegex
.exec
(s
)) != null) {
574 fail
("Bogus name-value pair: " + s
);
577 // We can't simply split by "," here because the value can
578 // contain a comma, for example, in "count" attributes the
579 // expression frequently uses offsetof(a,b).
581 const spacesStartRegex
:RegExp = /^(\s
+)/;
582 const spacesEndRegex
:RegExp = /(\s
+)$/;
583 const commaSpacesStartRegex
:RegExp = /^(,\s
*)/;
584 const nameValuePairRegex
:RegExp = /^([a
-zA
-Z0
-9_
]+)\s
*=\s
*(\"[^\"]*\"|true|false)/;
585 const valueRegex
:RegExp = /^\"[^\"]*\"|[a
-zA
-Z0
-9_
]+\[[^\]]*\]|(?:[0-9]+(?:\.[0-9]+)?)|(?:<\s
+|\s
+>|[a
-zA
-Z0
-9_
:<>])+/;
587 function splitAttrs
(s
, paren
=null) {
588 var then
= new Date();
592 // strip leading and trailing spaces
593 if ((result
= spacesStartRegex
.exec
(s
)) != null)
594 s
= s
.substring
(result
[1].length
);
595 if ((result
= spacesEndRegex
.exec
(s
)) != null)
596 s
= s
.substring
(0,s
.length
-result
[1].length
);
600 fail
("Missing closing parenthesis: '" + paren
+ "'");
601 if (s
.charAt
(0) == paren
)
607 // strip leading comma and any spaces following it
609 if ((result
= commaSpacesStartRegex
.exec
(s
)) == null)
610 fail
("Incorrect attribute string: missing comma in " + s
);
611 s
= s
.substring
(result
[1].length
);
615 // simple-identifier=(string|boolean), string, number, qualified-identifier
616 if ((result
= nameValuePairRegex
.exec
(s
)) != null)
617 xs
.push
([result
[1], parseAttrValue
(result
[2])]);
618 else if ((result
= valueRegex
.exec
(s
)) != null)
619 xs
.push
(parseAttrValue
(result
[0]));
621 fail
("Incorrect attribute string: bad value or name/value pair: " + s
);
622 s
= s
.substring
(result
[0].length
)
626 splittingTime
+= (new Date() - then
);
630 function positionalAttrs
(tag
, s
, paren
, cls
) {
631 var attr
= splitAttrs
(s
, paren
);
632 for ( var i
=0 ; i
< attr
.length
; i
++ ) {
633 if (attr
[i
] is Array)
634 fail
("Named attributes not allowed here: " + attr
[i
]);
638 return new constructors
[tag
](tag
,attr
);
641 function parseNamedAttrs
(s
) {
642 var attr
= splitAttrs
(s
);
643 for ( var i
=0 ; i
< attr
.length
; i
++ ) {
644 if (!(attr
[i
] is Array || i
==0 && attr
[i
] is String && attr
.length
== 1))
645 fail
("Named attributes required here: " + attr
[i
]);
650 function reportMatch
(line
) {
655 function currentClassName
() {
656 if (cppDataStack
.length
== 0)
657 fail
("No active GC_DATA_BEGIN");
658 return cppDataStack
[cppDataStack
.length
-1];
661 // Does not match the trailing right paren. $1 is the tag, $2 the
662 // rest of the text starting just right of the left paren for the
667 ["GC_CPP_EXACT_WITH_HOOK_IFDEF",
668 "GC_CPP_EXACT_WITH_HOOK_IFNDEF",
669 "GC_CPP_EXACT_WITH_HOOK_IF",
670 "GC_CPP_EXACT_WITH_HOOK",
671 "GC_CPP_EXACT_WITH_HOOK",
672 "GC_CPP_EXACT_WITH_HOOK",
673 "GC_CPP_EXACT_IFDEF",
674 "GC_CPP_EXACT_IFNDEF",
677 "GC_AS3_EXACT_WITH_HOOK_IFDEF",
678 "GC_AS3_EXACT_WITH_HOOK_IFNDEF",
679 "GC_AS3_EXACT_WITH_HOOK_IF",
680 "GC_AS3_EXACT_WITH_HOOK",
681 "GC_AS3_EXACT_WITH_HOOK",
682 "GC_AS3_EXACT_WITH_HOOK",
683 "GC_AS3_EXACT_IFDEF",
684 "GC_AS3_EXACT_IFNDEF",
689 "GC_DATA_END"].join
("|") +
692 function matchCppMetaTag
(line
, where
) {
693 return cppMetaTag
.exec
(line
.substring
(where
));
696 // Does not match the trailing right paren. $1 is the tag, $2 the
697 // rest of the text starting just right of the left paren for the
705 "GC_STRUCTURES_SMALL",
709 "GC_STRUCTURE_IFDEF",
710 "GC_STRUCTURE_IFNDEF",
721 "GC_CONSERVATIVE_IFDEF",
722 "GC_CONSERVATIVE_IFNDEF",
723 "GC_CONSERVATIVE_IF"].join
("|") +
726 function matchCppFieldTag
(line
, where
) {
727 return cppFieldTag
.exec
(line
.substring
(where
));
730 function stackToCppPrefix
() {
732 for ( var i
=0; i
< cppClassStack
.length
; i
++)
733 pfx
+= cppClassStack
[i
] + "::";
737 const nativeAnnotationRegex
:RegExp = /^\[native\s
*\((.*)\)\s
*\]/;
739 // FIXME: Additional error checking we could add here:
740 // - only one data section per class, globally
742 function processFile
(filename
) {
743 cppDataStack
.length
= 0;
744 cppClassStack
.length
= 0;
746 const beforeReadLines
= new Date();
747 const text
= readLines
(filename
);
748 const afterReadLines
= new Date();
750 const beforeProcessing
= new Date();
751 const cppfile
= Boolean(filename
.match
(/\.(h
|cpp
)$/));
752 const as3file
= Boolean(filename
.match
(/\.as$/));
755 for ( var i
=0 ; i
< text
.length
; i
++ ) {
761 // Quick precomputation to filter out lines that we don't
762 // need to examine any further with regular expressions. The
763 // regex search will start at the known good location.
766 var nativeIndex
= -1;
768 gcIndex
= line
.indexOf
("GC_");
770 nativeIndex
= line
.indexOf
("[native");
771 if (gcIndex
== -1 && nativeIndex
== -1)
774 errorContext
= "On " + filename
+ " line " + lineno
;
776 // For line matching we match only at the start of the
777 // line after taking the substring starting at the
778 // known-good index. This yields the same performance as
779 // using a global regex and setting lastIndex to indicate
780 // where we want to start matching.
785 if ((result
= matchCppMetaTag
(line
, gcIndex
)) != null) {
787 var v
= positionalAttrs
(result
[1], result
[2], ")", null);
788 if (v
is GCDataBegin
|| v
is GCNoData
) {
789 if (cppClassStack
.length
== 0 || cppClassStack
[cppClassStack
.length
-1] != v
.cls
)
790 fail
("Mismatched " + result
[1] + " here: " + v
.cls
);
791 cppDataStack
.push
(v
.cls
);
793 if (v
is GCDataEnd
|| v
is GCNoData
) {
794 var top
= currentClassName
();
796 fail
(result
[1] + " for " + v
.cls
+ " but " + top
+ " is on the stack top");
800 if (!(v
is GCDataSection
)) {
802 v
.fullClassPrefix
= stackToCppPrefix
();
803 cppClassStack
.push
(v
.cls
);
808 else if ((result
= matchCppFieldTag
(line
, gcIndex
)) != null) {
810 var v
= positionalAttrs
(result
[1], result
[2], ")", currentClassName
());
815 if (nativeIndex
>= 0) {
818 // For AS3 annotations we collect C++ class names if the [native] spec says
819 // that the C++ class should be exactly traced.
821 if ((result
= nativeAnnotationRegex
.exec
(line
.substring
(nativeIndex
))) != null) {
823 var attr
= parseNamedAttrs
(result
[1]);
825 for ( var j
=0 ; j
< attr
.length
; j
++ ) {
826 if (attr
[j
] is Array)
827 flags
[attr
[j
][0]] = attr
[j
][1];
829 if ("cls" in flags
&& ("classgc" in flags
|| "gc" in flags
))
830 specs
.push
(new AS3Class
(cppNamespace
, flags
["cls"]));
831 if ("instance" in flags
&& ("instancegc" in flags
|| "gc" in flags
))
832 specs
.push
(new AS3Class
(cppNamespace
, flags
["instance"]));
837 if (cppDataStack
.length
!= 0)
838 fail
("Missing GC_DATA_END for " + currentClassName
());
840 if (cppClassStack
.length
!= 0)
841 fail
("Missing GC_DATA_BEGIN/GC_DATA_END for these: " + cppClassStack
);
843 const afterProcessing
= new Date();
845 readingTime
+= (afterReadLines
- beforeReadLines
);
846 processingTime
+= (afterProcessing
- beforeProcessing
);
849 for ( var i
=0 ; i
< files
.length
; i
++ )
850 processFile
(files
[i
]);
853 print
(" reading time = " + readingTime
/1000 + "s");
854 print
(" processing time = " + processingTime
/1000 + "s");
855 print
(" splitting time = " + splittingTime
/1000 + "s");
859 function isVariableLength
(t
) {
860 return t
is GCPointers
|| t
is GCAtoms
|| t
is GCStructures
;
863 // Populate as3ClassList, as3ClassMap, cppClassList, and cppClassMap.
866 function collectClasses
()
868 errorContext
= "Collecting classes";
870 for ( var i
=0, limit
=specs
.length
; i
< limit
; i
++ ) {
872 if (s
is GCAS3Exact
|| s
is GCCppExact
) {
874 if (cppClassMap
.hasOwnProperty
(clsname
))
875 fail
("Duplicate " + (s
is GCAS3Exact
? "GC_AS3_EXACT" : "GC_CPP_EXACT") + " spec: " + clsname
);
876 cppClassList
.push
(s
);
877 cppClassMap
[clsname
] = s
;
879 else if (s
is AS3Class
) {
881 if (as3ClassMap
.hasOwnProperty
(clsname
)) {
882 // Completely gross hack in the vector code - the instance for VectorClass and ObjectVectorClass
883 // are both ObjectVectorObject. Just work around it for now with this gross hack.
884 if (clsname
== "ObjectVectorObject")
886 fail
("Duplicate AS3 native spec: " + clsname
);
888 as3ClassList
.push
(s
);
889 as3ClassMap
[clsname
] = s
;
893 as3ClassList
.sort
(function (a
,b
) { return strcmp
(a
.cls
,b
.cls
) });
894 cppClassList
.sort
(function (a
,b
) { return strcmp
(a
.cls
,b
.cls
) });
897 print
("C++ class map: " + cppClassMap
);
898 print
("AS3 class map: " + as3ClassMap
);
902 // Check that the sets of classes correspond: every AS3 class must be
903 // a C++ GC_AS3_EXACT class, and vice versa. GC_CPP_EXACT classes must
904 // not have AS3 counterparts.
906 function checkClasses
()
908 errorContext
= "Checking class correspondence";
910 for ( var i
=0 ; i
< cppClassList
.length
; i
++ ) {
911 var c
= cppClassList
[i
];
913 var probe
= as3ClassMap
.hasOwnProperty
(n
);
914 if (c
is GCCppExact
) {
916 fail
("AS3 side may not be defined for GC_CPP_EXACT " + n
);
920 fail
("AS3 side is missing annotation for " + n
);
923 for ( var i
=0 ; i
< as3ClassList
.length
; i
++ ) {
924 var c
= as3ClassList
[i
];
926 if (!cppClassMap
.hasOwnProperty
(n
))
927 fail
("C++ side is missing annotation for " + n
);
928 if (!(cppClassMap
[n
] is GCAS3Exact
))
929 fail
("C++ side is not a GC_AS3_EXACT class for " + n
);
933 // Add fields to classes, check for errors in class names, duplicates, etc.
934 // Sort the fields by name.
936 function collectFields
()
938 errorContext
= "Collecting fields";
940 for ( var i
=0 ; i
< specs
.length
; i
++ ) {
942 if (s
is GCClass
|| s
is AS3Class
)
944 if (!cppClassMap
.hasOwnProperty
(s
.cls
))
945 fail
("Bad field annotation - unknown class: " + s
.cls
+ " in " + s
);
946 var c
= cppClassMap
[s
.cls
];
947 var fieldname
= s
.name
;
948 if (s
.if_
) fieldname
+= "!if!" + s
.if_
;
949 if (s
.ifdef
) fieldname
+= "!ifdef!" + s
.ifdef
;
950 if (s
.ifndef
) fieldname
+= "!ifndef!" + s
.ifndef
;
951 if (isVariableLength
(s
)) {
952 // FIXME: It would be good to loosen the following restriction up; it
953 // would be sufficient to decree that only one of the arrays can be
954 // large. It is rarely the case that pointers or atom arrays are anything
955 // but trailing (and then there's only one), but eg MethodInfo has
956 // an inline fixed-size pointer array for the lookup cache, and if it
957 // were to have a trailing array as well then we'd run into this restriction.
958 if (c
.variable_length_field
!= null)
959 fail
("Arbitrary restriction: More than one variable length field on " + c
);
960 if (c
.fieldMap
.hasOwnProperty
(fieldname
))
961 fail
("Duplicate field name: " + s
.name
+ "; canonically " + fieldname
);
962 c
.variable_length_field
= s
;
965 if (c
.fieldMap
.hasOwnProperty
(fieldname
) || (c
.variable_length_field
!= null && c
.variable_length_field
.name
== fieldname
))
966 fail
("Duplicate field name: " + s
.name
+ "; canonically " + fieldname
);
967 c
.fieldMap
[fieldname
] = s
;
972 for ( var i
=0 ; i
< cppClassList
.length
; i
++ )
973 cppClassList
[i
].fieldList
.sort
(function (a
,b
) { return strcmp
(a
.name
,b
.name
) });
976 // For each class compute whether it's likely to be large or small,
977 // this will influence how we generate code.
978 // A manifestly large number of fixed fields overrides any "small" hint.
979 // A pointer array will make us assume the object is large unless
980 // the hint is that it's small.
982 function computeLargeOrSmall
()
984 for ( var i
=0 ; i
< cppClassList
.length
; i
++ ) {
985 var c
= cppClassList
[i
];
986 if (c
.fieldList
.length
>= largeObjectCutoff
/4) // "4" is a proxy for word size, not correct on 64-bit systems but OK for this purpose
987 c
.probablyLarge
= true;
988 else if (c
is GCCppExact
&& c
.variable_length_field
!= null) {
989 c
.hint
= c
.variable_length_field
.hint
;
990 c
.probablyLarge
= (c
.hint
!= "small")
995 // If a class is large it could be because it has a pointer array or
996 // because it has many fixed fields or both. It is pretty much never
997 // going to be the case that it has many fixed fields so we always
998 // trace the fixed fields for cursor==0, technically that's suboptimal
999 // for incrementality but not a problem for correctness.
1001 function constructTracerBodies
()
1003 function traceField
(out
, field
) {
1005 out
.PR
("#ifdef " + field
.ifdef
);
1008 out
.PR
("#ifndef " + field
.ifndef
);
1011 out
.PR
("#if " + field
.if_
);
1016 catch (f
: GCPointer
) { out
.PR
("gc->TraceLocation(&" + field
.name
+ ");") }
1017 catch (f
: GCConservative
) { out
.PR
("gc->TraceConservativeLocation(&" + field
.name
+ ");") }
1018 catch (f
: GCStructure
) { out
.PR
(field
.name
+ ".gcTrace(gc);") }
1019 catch (f
: GCAtom
) { out
.PR
("gc->TraceAtom(&" + field
.name
+ ");") }
1021 fail
("Unknown type to trace: " + field
);
1024 if (field
.ifdef
|| field
.ifndef
|| field
.if_
)
1028 // Here we can do better: we can collect the fields that have the
1029 // same pointer attribute and the same ifdef attribute, and then
1030 // emit each group in a chunk. That will allow us to use the
1031 // multi-argument tracing functions when they're available.
1033 function emitFixedFields
(c
) {
1034 for ( var j
=0 ; j
< c
.fieldList
.length
; j
++ ) {
1035 var f
= c
.fieldList
[j
];
1036 if (isVariableLength
(f
))
1037 fail
("Invariant failure: there should be no variable length field among the fixed fields: " + f
);
1038 traceField
(c
.out
, f
);
1042 function emitChunk
(out
, f
, start
, len
) {
1046 catch (f
:GCPointers
) { c
.out
.PR
("gc->TraceLocations((" + f
.name
+ "+" + start
+ "), " + len
+ ");"); }
1047 catch (f
:GCAtoms
) { c
.out
.PR
("gc->TraceAtoms((" + f
.name
+ "+" + start
+ "), " + len
+ ");"); }
1048 catch (f
:GCStructures
) {
1050 PR
("for ( size_t _xact_iter=0 ; _xact_iter < " + len
+ "; _xact_iter++ )").
1052 PR
(f
.name
+ "[+_xact_iter+" + start
+ "].gcTrace(gc);").
1056 fail
("Unknown variable length field type: " + f
);
1060 // Important that the names introduced here do not shadow the ones
1061 // in the class so we prefix locals with _xact_.
1063 function emitArrayChunked
(c
) {
1065 PR
("const size_t _xact_work_increment = " + largeObjectCutoff
+ "/sizeof(void*);").
1066 PR
("const size_t _xact_work_count = " + c
.variable_length_field
.count
+ ";").
1067 PR
("if (_xact_cursor * _xact_work_increment >= _xact_work_count)").
1069 PR
("return false;").
1071 PR
("size_t _xact_work = _xact_work_increment;").
1072 PR
("bool _xact_more = true;").
1073 PR
("if ((_xact_cursor + 1) * _xact_work_increment >= _xact_work_count)").
1076 PR
("_xact_work = _xact_work_count - (_xact_cursor * _xact_work_increment);").
1077 PR
("_xact_more = false;").
1080 emitChunk
(c
.out
, c
.variable_length_field
, "(_xact_cursor * _xact_work_increment)", "_xact_work");
1081 c
.out
.PR
("return _xact_more;");
1084 function emitArrayUnchunked
(c
) {
1085 emitChunk
(c
.out
, c
.variable_length_field
, "0", c
.variable_length_field
.count
);
1088 // FIXME: Not currently using this, but we could use it - probably elsewhere - to flag
1089 // probably-incorrect code.
1091 function noCredibleTracer
(n
) {
1093 case "MMgc::GCObject":
1101 function noUsefulTracer
(n
) {
1103 case "MMgc::GCFinalizedObject":
1104 case "GCFinalizedObject":
1105 case "MMgc::GCTraceableObject":
1106 case "GCTraceableObject":
1107 case "MMgc::RCObject":
1115 function cleanupNs
(name
) {
1116 // If the name has a namespace, keep it
1117 // Otherwise prepend the current output namespace
1118 if (name
.match
("::"))
1119 name
= name
.replace
(/::/g
, "_");
1121 name
= cppNamespace
+ "_" + name
;
1122 // Brockets appear in template expansions: Fnord<A>
1123 // Spaces appear in nested template expressions: Fnord< A<B> >
1124 return name
.replace
(/ /g
, "").replace
(/<|>/g
, "X");
1127 errorContext
= "Accumulating bodies";
1129 for ( var i
=0 ; i
< cppClassList
.length
; i
++ ) {
1130 var c
= cppClassList
[i
];
1132 // The interlock is just a #define with that name emitted at
1133 // the beginning of the output, we'll get a compilation error
1134 // if it's not defined. This way we ensure that every exactly
1135 // traced class has an exactly traced base class.
1137 if (c
.base
!= null && !noUsefulTracer
(c
.base
)) {
1138 c
.out
.PR
(c
.base
+ "::gcTrace(gc, 0);");
1139 if (interlockOutputFile
!= null)
1140 c
.out
.PR
("(void)(" + cleanupNs
(c
.base
) + "_isExactInterlock != 0);");
1144 c
.out
.PR
("gcTraceHook_" + c
.cls
+ "(gc);");
1146 if (c
.probablyLarge
) {
1147 if (c
.fieldList
.length
> 0) {
1149 PR
("if (_xact_cursor == 0) {").
1156 if (c
is GCClass
&& c
.variable_length_field
!= null)
1157 emitArrayChunked
(c
);
1161 if (c
is GCClass
&& c
.variable_length_field
!= null)
1162 emitArrayUnchunked
(c
);
1167 function constructAndPrintTracers
()
1169 var interlocks
= interlockOutputFile
? new Printer
() : null;
1170 var builtins
= builtinOutputFile
? new Printer
() : null;
1171 var natives
= nativeOutputFile
? new Printer
() : null;
1173 function emitInterlock
(c
) {
1175 interlocks
.PR
("#define " + cppNamespace
+ "_" + c
.cls
+ "_isExactInterlock 1");
1178 function emitTracers
()
1180 for ( var i
=0 ; i
< cppClassList
.length
; i
++ ) {
1181 var c
= cppClassList
[i
];
1183 if (c
is GCCppExact
) {
1197 PR
("#ifdef " + c
.ifdef
).
1201 PR
("#ifndef " + c
.ifndef
).
1207 if (c
.probablyLarge
) {
1209 PR
("bool " + c
.fullName
() + "::gcTrace(MMgc::GC* gc, size_t _xact_cursor)").
1211 DO
(function (output
) {
1212 if (output
=== builtins
) {
1215 PR
("#ifndef GC_TRIVIAL_TRACER_" + c
.cls
).
1216 PR
("if (_xact_cursor == 0)").
1219 PR
("m_slots_" + c
.cls
+ ".gcTracePrivateProperties(gc);").
1232 PR
("bool " + c
.fullName
() + "::gcTrace(MMgc::GC* gc, size_t _xact_cursor)").
1236 PR
("(void)_xact_cursor;").
1237 DO
(function (output
) {
1238 if (output
=== builtins
) {
1240 PR
("#ifndef GC_TRIVIAL_TRACER_" + c
.cls
).
1241 PR
("m_slots_" + c
.cls
+ ".gcTracePrivateProperties(gc);").
1248 PR
("return false;").
1253 if (c
.ifdef
|| c
.ifndef
|| c
.if_
)
1255 PR
("#endif // " + (c
.ifdef
|| c
.ifndef
|| c
.if_
)).
1260 function emitDelegates
()
1263 for ( var i
=0 ; i
< cppClassList
.length
; i
++ ) {
1264 var c
= cppClassList
[i
];
1265 if (c
.base
!= null && c
.base
!= "")
1266 output
+= ("#define GCDELEGATE_" + c
.cls
+ " " + c
.base
+ "\n");
1271 function printToFile
(fn
, txt
) {
1272 File.write
(fn
, txt
);
1277 if(cppNamespace
== "")
1280 return "namespace " + cppNamespace
+"\n{\n";
1285 if(cppNamespace
== "")
1291 errorContext
= "Emitting code";
1295 const doNotEdit
= "\n/* machine generated file via utils/exactgc.as -- do not edit */\n\n";
1297 if (builtinOutputFile
)
1298 printToFile
(builtinOutputFile
,
1303 (nativeOutputFile
== builtinOutputFile
? natives
.get() : "") +
1306 if (nativeOutputFile
&& nativeOutputFile
!= builtinOutputFile
)
1307 printToFile
(nativeOutputFile
,
1314 if (interlockOutputFile
)
1315 printToFile
(interlockOutputFile
,
1318 interlocks
.get() + "\n"));
1321 function profile
(what
, thunk
)
1323 var then
= new Date();
1324 var result
= thunk
();
1325 var now
= new Date();
1327 print
(what
+ ": " + (now
- then
)/1000 + "s");
1333 profile
("readFiles", function() { readFiles
(processOptionsAndFindFiles
(System.argv
)) });
1334 profile
("collectClasses", collectClasses
);
1335 profile
("checkClasses", checkClasses
);
1336 profile
("collectFields", collectFields
);
1337 profile
("computeLargeOrSmall", computeLargeOrSmall
);
1338 profile
("constructTracerBodies",constructTracerBodies
);
1339 profile
("constructAndPrintTracers",constructAndPrintTracers
);
1342 profile
("main", main
);