JavahTask.java revision 2675:4be0e35f385a
1/* 2 * Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26package com.sun.tools.javah; 27 28import java.io.File; 29import java.io.FileNotFoundException; 30import java.io.IOException; 31import java.io.OutputStream; 32import java.io.PrintWriter; 33import java.io.Writer; 34import java.text.MessageFormat; 35import java.util.ArrayList; 36import java.util.Arrays; 37import java.util.Collections; 38import java.util.HashMap; 39import java.util.Iterator; 40import java.util.LinkedHashSet; 41import java.util.List; 42import java.util.Locale; 43import java.util.Map; 44import java.util.MissingResourceException; 45import java.util.ResourceBundle; 46import java.util.Set; 47 48import javax.annotation.processing.AbstractProcessor; 49import javax.annotation.processing.Messager; 50import javax.annotation.processing.ProcessingEnvironment; 51import javax.annotation.processing.RoundEnvironment; 52import javax.annotation.processing.SupportedAnnotationTypes; 53 54import javax.lang.model.SourceVersion; 55import javax.lang.model.element.ExecutableElement; 56import javax.lang.model.element.TypeElement; 57import javax.lang.model.element.VariableElement; 58import javax.lang.model.type.ArrayType; 59import javax.lang.model.type.DeclaredType; 60import javax.lang.model.type.TypeMirror; 61import javax.lang.model.type.TypeVisitor; 62import javax.lang.model.util.ElementFilter; 63import javax.lang.model.util.SimpleTypeVisitor9; 64import javax.lang.model.util.Types; 65 66import javax.tools.Diagnostic; 67import javax.tools.DiagnosticListener; 68import javax.tools.JavaCompiler; 69import javax.tools.JavaCompiler.CompilationTask; 70import javax.tools.JavaFileManager; 71import javax.tools.JavaFileObject; 72import javax.tools.StandardJavaFileManager; 73import javax.tools.StandardLocation; 74import javax.tools.ToolProvider; 75import static javax.tools.Diagnostic.Kind.*; 76 77import com.sun.tools.javac.code.Symbol.CompletionFailure; 78import com.sun.tools.javac.main.CommandLine; 79import com.sun.tools.javac.util.DefinedBy; 80import com.sun.tools.javac.util.DefinedBy.Api; 81 82/** 83 * Javah generates support files for native methods. 84 * Parse commandline options and invokes javadoc to execute those commands. 85 * 86 * <p><b>This is NOT part of any supported API. 87 * If you write code that depends on this, you do so at your own 88 * risk. This code and its internal interfaces are subject to change 89 * or deletion without notice.</b></p> 90 * 91 * @author Sucheta Dambalkar 92 * @author Jonathan Gibbons 93 */ 94public class JavahTask implements NativeHeaderTool.NativeHeaderTask { 95 public class BadArgs extends Exception { 96 private static final long serialVersionUID = 1479361270874789045L; 97 BadArgs(String key, Object... args) { 98 super(JavahTask.this.getMessage(key, args)); 99 this.key = key; 100 this.args = args; 101 } 102 103 BadArgs showUsage(boolean b) { 104 showUsage = b; 105 return this; 106 } 107 108 final String key; 109 final Object[] args; 110 boolean showUsage; 111 } 112 113 static abstract class Option { 114 Option(boolean hasArg, String... aliases) { 115 this.hasArg = hasArg; 116 this.aliases = aliases; 117 } 118 119 boolean isHidden() { 120 return false; 121 } 122 123 boolean matches(String opt) { 124 for (String a: aliases) { 125 if (a.equals(opt)) 126 return true; 127 } 128 return false; 129 } 130 131 boolean ignoreRest() { 132 return false; 133 } 134 135 abstract void process(JavahTask task, String opt, String arg) throws BadArgs; 136 137 final boolean hasArg; 138 final String[] aliases; 139 } 140 141 static abstract class HiddenOption extends Option { 142 HiddenOption(boolean hasArg, String... aliases) { 143 super(hasArg, aliases); 144 } 145 146 @Override 147 boolean isHidden() { 148 return true; 149 } 150 } 151 152 static final Option[] recognizedOptions = { 153 new Option(true, "-o") { 154 void process(JavahTask task, String opt, String arg) { 155 task.ofile = new File(arg); 156 } 157 }, 158 159 new Option(true, "-d") { 160 void process(JavahTask task, String opt, String arg) { 161 task.odir = new File(arg); 162 } 163 }, 164 165 new HiddenOption(true, "-td") { 166 void process(JavahTask task, String opt, String arg) { 167 // ignored; for backwards compatibility 168 } 169 }, 170 171 new HiddenOption(false, "-stubs") { 172 void process(JavahTask task, String opt, String arg) { 173 // ignored; for backwards compatibility 174 } 175 }, 176 177 new Option(false, "-v", "-verbose") { 178 void process(JavahTask task, String opt, String arg) { 179 task.verbose = true; 180 } 181 }, 182 183 new Option(false, "-h", "-help", "--help", "-?") { 184 void process(JavahTask task, String opt, String arg) { 185 task.help = true; 186 } 187 }, 188 189 new HiddenOption(false, "-trace") { 190 void process(JavahTask task, String opt, String arg) { 191 task.trace = true; 192 } 193 }, 194 195 new Option(false, "-version") { 196 void process(JavahTask task, String opt, String arg) { 197 task.version = true; 198 } 199 }, 200 201 new HiddenOption(false, "-fullversion") { 202 void process(JavahTask task, String opt, String arg) { 203 task.fullVersion = true; 204 } 205 }, 206 207 new Option(false, "-jni") { 208 void process(JavahTask task, String opt, String arg) { 209 task.jni = true; 210 } 211 }, 212 213 new Option(false, "-force") { 214 void process(JavahTask task, String opt, String arg) { 215 task.force = true; 216 } 217 }, 218 219 new HiddenOption(false, "-Xnew") { 220 void process(JavahTask task, String opt, String arg) { 221 // we're already using the new javah 222 } 223 }, 224 225 new HiddenOption(false, "-llni", "-Xllni") { 226 void process(JavahTask task, String opt, String arg) { 227 task.llni = true; 228 } 229 }, 230 231 new HiddenOption(false, "-llnidouble") { 232 void process(JavahTask task, String opt, String arg) { 233 task.llni = true; 234 task.doubleAlign = true; 235 } 236 }, 237 238 new HiddenOption(false) { 239 boolean matches(String opt) { 240 return opt.startsWith("-XD"); 241 } 242 void process(JavahTask task, String opt, String arg) { 243 task.javac_extras.add(opt); 244 } 245 }, 246 }; 247 248 JavahTask() { 249 } 250 251 JavahTask(Writer out, 252 JavaFileManager fileManager, 253 DiagnosticListener<? super JavaFileObject> diagnosticListener, 254 Iterable<String> options, 255 Iterable<String> classes) { 256 this(); 257 this.log = getPrintWriterForWriter(out); 258 this.fileManager = fileManager; 259 this.diagnosticListener = diagnosticListener; 260 261 try { 262 handleOptions(options, false); 263 } catch (BadArgs e) { 264 throw new IllegalArgumentException(e.getMessage()); 265 } 266 267 this.classes = new ArrayList<>(); 268 if (classes != null) { 269 for (String classname: classes) { 270 classname.getClass(); // null-check 271 this.classes.add(classname); 272 } 273 } 274 } 275 276 public void setLocale(Locale locale) { 277 if (locale == null) 278 locale = Locale.getDefault(); 279 task_locale = locale; 280 } 281 282 public void setLog(PrintWriter log) { 283 this.log = log; 284 } 285 286 public void setLog(OutputStream s) { 287 setLog(getPrintWriterForStream(s)); 288 } 289 290 static PrintWriter getPrintWriterForStream(OutputStream s) { 291 return new PrintWriter(s, true); 292 } 293 294 static PrintWriter getPrintWriterForWriter(Writer w) { 295 if (w == null) 296 return getPrintWriterForStream(null); 297 else if (w instanceof PrintWriter) 298 return (PrintWriter) w; 299 else 300 return new PrintWriter(w, true); 301 } 302 303 public void setDiagnosticListener(DiagnosticListener<? super JavaFileObject> dl) { 304 diagnosticListener = dl; 305 } 306 307 public void setDiagnosticListener(OutputStream s) { 308 setDiagnosticListener(getDiagnosticListenerForStream(s)); 309 } 310 311 private DiagnosticListener<JavaFileObject> getDiagnosticListenerForStream(OutputStream s) { 312 return getDiagnosticListenerForWriter(getPrintWriterForStream(s)); 313 } 314 315 private DiagnosticListener<JavaFileObject> getDiagnosticListenerForWriter(Writer w) { 316 final PrintWriter pw = getPrintWriterForWriter(w); 317 return new DiagnosticListener<JavaFileObject> () { 318 @DefinedBy(Api.COMPILER) 319 public void report(Diagnostic<? extends JavaFileObject> diagnostic) { 320 if (diagnostic.getKind() == Diagnostic.Kind.ERROR) { 321 pw.print(getMessage("err.prefix")); 322 pw.print(" "); 323 } 324 pw.println(diagnostic.getMessage(null)); 325 } 326 }; 327 } 328 329 int run(String[] args) { 330 try { 331 handleOptions(args); 332 boolean ok = run(); 333 return ok ? 0 : 1; 334 } catch (BadArgs e) { 335 diagnosticListener.report(createDiagnostic(e.key, e.args)); 336 return 1; 337 } catch (InternalError e) { 338 diagnosticListener.report(createDiagnostic("err.internal.error", e.getMessage())); 339 return 1; 340 } catch (Util.Exit e) { 341 return e.exitValue; 342 } finally { 343 log.flush(); 344 } 345 } 346 347 public void handleOptions(String[] args) throws BadArgs { 348 handleOptions(Arrays.asList(args), true); 349 } 350 351 private void handleOptions(Iterable<String> args, boolean allowClasses) throws BadArgs { 352 if (log == null) { 353 log = getPrintWriterForStream(System.out); 354 if (diagnosticListener == null) 355 diagnosticListener = getDiagnosticListenerForStream(System.err); 356 } else { 357 if (diagnosticListener == null) 358 diagnosticListener = getDiagnosticListenerForWriter(log); 359 } 360 361 if (fileManager == null) 362 fileManager = getDefaultFileManager(diagnosticListener, log); 363 364 Iterator<String> iter = expandAtArgs(args).iterator(); 365 noArgs = !iter.hasNext(); 366 367 while (iter.hasNext()) { 368 String arg = iter.next(); 369 if (arg.startsWith("-")) 370 handleOption(arg, iter); 371 else if (allowClasses) { 372 if (classes == null) 373 classes = new ArrayList<>(); 374 classes.add(arg); 375 while (iter.hasNext()) 376 classes.add(iter.next()); 377 } else 378 throw new BadArgs("err.unknown.option", arg).showUsage(true); 379 } 380 381 if ((classes == null || classes.size() == 0) && 382 !(noArgs || help || version || fullVersion)) { 383 throw new BadArgs("err.no.classes.specified"); 384 } 385 386 if (jni && llni) 387 throw new BadArgs("jni.llni.mixed"); 388 389 if (odir != null && ofile != null) 390 throw new BadArgs("dir.file.mixed"); 391 } 392 393 private void handleOption(String name, Iterator<String> rest) throws BadArgs { 394 for (Option o: recognizedOptions) { 395 if (o.matches(name)) { 396 if (o.hasArg) { 397 if (rest.hasNext()) 398 o.process(this, name, rest.next()); 399 else 400 throw new BadArgs("err.missing.arg", name).showUsage(true); 401 } else 402 o.process(this, name, null); 403 404 if (o.ignoreRest()) { 405 while (rest.hasNext()) 406 rest.next(); 407 } 408 return; 409 } 410 } 411 412 if (fileManager.handleOption(name, rest)) 413 return; 414 415 throw new BadArgs("err.unknown.option", name).showUsage(true); 416 } 417 418 private Iterable<String> expandAtArgs(Iterable<String> args) throws BadArgs { 419 try { 420 List<String> l = new ArrayList<>(); 421 for (String arg: args) l.add(arg); 422 return Arrays.asList(CommandLine.parse(l.toArray(new String[l.size()]))); 423 } catch (FileNotFoundException e) { 424 throw new BadArgs("at.args.file.not.found", e.getLocalizedMessage()); 425 } catch (IOException e) { 426 throw new BadArgs("at.args.io.exception", e.getLocalizedMessage()); 427 } 428 } 429 430 public Boolean call() { 431 return run(); 432 } 433 434 public boolean run() throws Util.Exit { 435 436 Util util = new Util(log, diagnosticListener); 437 438 if (noArgs || help) { 439 showHelp(); 440 return help; // treat noArgs as an error for purposes of exit code 441 } 442 443 if (version || fullVersion) { 444 showVersion(fullVersion); 445 return true; 446 } 447 448 util.verbose = verbose; 449 450 Gen g; 451 452 if (llni) 453 g = new LLNI(doubleAlign, util); 454 else { 455// if (stubs) 456// throw new BadArgs("jni.no.stubs"); 457 g = new JNI(util); 458 } 459 460 if (ofile != null) { 461 if (!(fileManager instanceof StandardJavaFileManager)) { 462 diagnosticListener.report(createDiagnostic("err.cant.use.option.for.fm", "-o")); 463 return false; 464 } 465 Iterable<? extends JavaFileObject> iter = 466 ((StandardJavaFileManager) fileManager).getJavaFileObjectsFromFiles(Collections.singleton(ofile)); 467 JavaFileObject fo = iter.iterator().next(); 468 g.setOutFile(fo); 469 } else { 470 if (odir != null) { 471 if (!(fileManager instanceof StandardJavaFileManager)) { 472 diagnosticListener.report(createDiagnostic("err.cant.use.option.for.fm", "-d")); 473 return false; 474 } 475 476 if (!odir.exists()) 477 if (!odir.mkdirs()) 478 util.error("cant.create.dir", odir.toString()); 479 try { 480 ((StandardJavaFileManager) fileManager).setLocation(StandardLocation.CLASS_OUTPUT, Collections.singleton(odir)); 481 } catch (IOException e) { 482 Object msg = e.getLocalizedMessage(); 483 if (msg == null) { 484 msg = e; 485 } 486 diagnosticListener.report(createDiagnostic("err.ioerror", odir, msg)); 487 return false; 488 } 489 } 490 g.setFileManager(fileManager); 491 } 492 493 /* 494 * Force set to false will turn off smarts about checking file 495 * content before writing. 496 */ 497 g.setForce(force); 498 499 if (fileManager instanceof JavahFileManager) 500 ((JavahFileManager) fileManager).setSymbolFileEnabled(false); 501 502 JavaCompiler c = ToolProvider.getSystemJavaCompiler(); 503 List<String> opts = new ArrayList<>(); 504 opts.add("-proc:only"); 505 opts.addAll(javac_extras); 506 CompilationTask t = c.getTask(log, fileManager, diagnosticListener, opts, classes, null); 507 JavahProcessor p = new JavahProcessor(g); 508 t.setProcessors(Collections.singleton(p)); 509 510 boolean ok = t.call(); 511 if (p.exit != null) 512 throw new Util.Exit(p.exit); 513 return ok; 514 } 515 516 private List<File> pathToFiles(String path) { 517 List<File> files = new ArrayList<>(); 518 for (String f: path.split(File.pathSeparator)) { 519 if (f.length() > 0) 520 files.add(new File(f)); 521 } 522 return files; 523 } 524 525 static StandardJavaFileManager getDefaultFileManager(final DiagnosticListener<? super JavaFileObject> dl, PrintWriter log) { 526 return JavahFileManager.create(dl, log); 527 } 528 529 private void showHelp() { 530 log.println(getMessage("main.usage", progname)); 531 for (Option o: recognizedOptions) { 532 if (o.isHidden()) 533 continue; 534 String name = o.aliases[0].substring(1); // there must always be at least one name 535 log.println(getMessage("main.opt." + name)); 536 } 537 String[] fmOptions = { "-classpath", "-cp", "-bootclasspath" }; 538 for (String o: fmOptions) { 539 if (fileManager.isSupportedOption(o) == -1) 540 continue; 541 String name = o.substring(1); 542 log.println(getMessage("main.opt." + name)); 543 } 544 log.println(getMessage("main.usage.foot")); 545 } 546 547 private void showVersion(boolean full) { 548 log.println(version(full)); 549 } 550 551 private static final String versionRBName = "com.sun.tools.javah.resources.version"; 552 private static ResourceBundle versionRB; 553 554 private String version(boolean full) { 555 String msgKey = (full ? "javah.fullVersion" : "javah.version"); 556 String versionKey = (full ? "full" : "release"); 557 // versionKey=product: mm.nn.oo[-milestone] 558 // versionKey=full: mm.mm.oo[-milestone]-build 559 if (versionRB == null) { 560 try { 561 versionRB = ResourceBundle.getBundle(versionRBName); 562 } catch (MissingResourceException e) { 563 return getMessage("version.resource.missing", System.getProperty("java.version")); 564 } 565 } 566 try { 567 return getMessage(msgKey, "javah", versionRB.getString(versionKey)); 568 } 569 catch (MissingResourceException e) { 570 return getMessage("version.unknown", System.getProperty("java.version")); 571 } 572 } 573 574 private Diagnostic<JavaFileObject> createDiagnostic(final String key, final Object... args) { 575 return new Diagnostic<JavaFileObject>() { 576 @DefinedBy(Api.COMPILER) 577 public Kind getKind() { 578 return Diagnostic.Kind.ERROR; 579 } 580 581 @DefinedBy(Api.COMPILER) 582 public JavaFileObject getSource() { 583 return null; 584 } 585 586 @DefinedBy(Api.COMPILER) 587 public long getPosition() { 588 return Diagnostic.NOPOS; 589 } 590 591 @DefinedBy(Api.COMPILER) 592 public long getStartPosition() { 593 return Diagnostic.NOPOS; 594 } 595 596 @DefinedBy(Api.COMPILER) 597 public long getEndPosition() { 598 return Diagnostic.NOPOS; 599 } 600 601 @DefinedBy(Api.COMPILER) 602 public long getLineNumber() { 603 return Diagnostic.NOPOS; 604 } 605 606 @DefinedBy(Api.COMPILER) 607 public long getColumnNumber() { 608 return Diagnostic.NOPOS; 609 } 610 611 @DefinedBy(Api.COMPILER) 612 public String getCode() { 613 return key; 614 } 615 616 @DefinedBy(Api.COMPILER) 617 public String getMessage(Locale locale) { 618 return JavahTask.this.getMessage(locale, key, args); 619 } 620 621 }; 622 } 623 624 private String getMessage(String key, Object... args) { 625 return getMessage(task_locale, key, args); 626 } 627 628 private String getMessage(Locale locale, String key, Object... args) { 629 if (bundles == null) { 630 // could make this a HashMap<Locale,SoftReference<ResourceBundle>> 631 // and for efficiency, keep a hard reference to the bundle for the task 632 // locale 633 bundles = new HashMap<>(); 634 } 635 636 if (locale == null) 637 locale = Locale.getDefault(); 638 639 ResourceBundle b = bundles.get(locale); 640 if (b == null) { 641 try { 642 b = ResourceBundle.getBundle("com.sun.tools.javah.resources.l10n", locale); 643 bundles.put(locale, b); 644 } catch (MissingResourceException e) { 645 throw new InternalError("Cannot find javah resource bundle for locale " + locale, e); 646 } 647 } 648 649 try { 650 return MessageFormat.format(b.getString(key), args); 651 } catch (MissingResourceException e) { 652 return key; 653 //throw new InternalError(e, key); 654 } 655 } 656 657 File ofile; 658 File odir; 659 String bootcp; 660 String usercp; 661 List<String> classes; 662 boolean verbose; 663 boolean noArgs; 664 boolean help; 665 boolean trace; 666 boolean version; 667 boolean fullVersion; 668 boolean jni; 669 boolean llni; 670 boolean doubleAlign; 671 boolean force; 672 Set<String> javac_extras = new LinkedHashSet<>(); 673 674 PrintWriter log; 675 JavaFileManager fileManager; 676 DiagnosticListener<? super JavaFileObject> diagnosticListener; 677 Locale task_locale; 678 Map<Locale, ResourceBundle> bundles; 679 680 private static final String progname = "javah"; 681 682 @SupportedAnnotationTypes("*") 683 class JavahProcessor extends AbstractProcessor { 684 private Messager messager; 685 686 JavahProcessor(Gen g) { 687 this.g = g; 688 } 689 690 @Override @DefinedBy(Api.ANNOTATION_PROCESSING) 691 public SourceVersion getSupportedSourceVersion() { 692 // since this is co-bundled with javac, we can assume it supports 693 // the latest source version 694 return SourceVersion.latest(); 695 } 696 697 @Override @DefinedBy(Api.ANNOTATION_PROCESSING) 698 public void init(ProcessingEnvironment pEnv) { 699 super.init(pEnv); 700 messager = processingEnv.getMessager(); 701 } 702 703 @DefinedBy(Api.ANNOTATION_PROCESSING) 704 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { 705 try { 706 Set<TypeElement> classes = getAllClasses(ElementFilter.typesIn(roundEnv.getRootElements())); 707 if (classes.size() > 0) { 708 checkMethodParameters(classes); 709 g.setProcessingEnvironment(processingEnv); 710 g.setClasses(classes); 711 g.run(); 712 } 713 } catch (CompletionFailure cf) { 714 messager.printMessage(ERROR, getMessage("class.not.found", cf.sym.getQualifiedName().toString())); 715 } catch (ClassNotFoundException cnfe) { 716 messager.printMessage(ERROR, getMessage("class.not.found", cnfe.getMessage())); 717 } catch (IOException ioe) { 718 messager.printMessage(ERROR, getMessage("io.exception", ioe.getMessage())); 719 } catch (Util.Exit e) { 720 exit = e; 721 } 722 723 return true; 724 } 725 726 private Set<TypeElement> getAllClasses(Set<? extends TypeElement> classes) { 727 Set<TypeElement> allClasses = new LinkedHashSet<>(); 728 getAllClasses0(classes, allClasses); 729 return allClasses; 730 } 731 732 private void getAllClasses0(Iterable<? extends TypeElement> classes, Set<TypeElement> allClasses) { 733 for (TypeElement c: classes) { 734 allClasses.add(c); 735 getAllClasses0(ElementFilter.typesIn(c.getEnclosedElements()), allClasses); 736 } 737 } 738 739 // 4942232: 740 // check that classes exist for all the parameters of native methods 741 private void checkMethodParameters(Set<TypeElement> classes) { 742 Types types = processingEnv.getTypeUtils(); 743 for (TypeElement te: classes) { 744 for (ExecutableElement ee: ElementFilter.methodsIn(te.getEnclosedElements())) { 745 for (VariableElement ve: ee.getParameters()) { 746 TypeMirror tm = ve.asType(); 747 checkMethodParametersVisitor.visit(tm, types); 748 } 749 } 750 } 751 } 752 753 private TypeVisitor<Void,Types> checkMethodParametersVisitor = 754 new SimpleTypeVisitor9<Void,Types>() { 755 @Override @DefinedBy(Api.LANGUAGE_MODEL) 756 public Void visitArray(ArrayType t, Types types) { 757 visit(t.getComponentType(), types); 758 return null; 759 } 760 @Override @DefinedBy(Api.LANGUAGE_MODEL) 761 public Void visitDeclared(DeclaredType t, Types types) { 762 t.asElement().getKind(); // ensure class exists 763 for (TypeMirror st: types.directSupertypes(t)) 764 visit(st, types); 765 return null; 766 } 767 }; 768 769 private Gen g; 770 private Util.Exit exit; 771 } 772} 773