require.java 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485
  1. // Copyright (C) 2005, 2006 Per M.A. Bothner.
  2. // This is free software; for terms and warranty disclaimer see ../../COPYING.
  3. package kawa.standard;
  4. import kawa.lang.*;
  5. import gnu.mapping.*;
  6. import gnu.lists.*;
  7. import gnu.bytecode.*;
  8. import gnu.expr.*;
  9. import gnu.kawa.io.InPort;
  10. import gnu.kawa.io.Path;
  11. import gnu.kawa.reflect.*;
  12. import gnu.text.*;
  13. import java.util.*;
  14. import kawa.lang.Translator.FormStack;
  15. public class require extends Syntax
  16. {
  17. public static final require require = new require();
  18. static { require.setName("require"); }
  19. /* NOTE on handling mutually recursive modules:
  20. How can Kawa compile two or more modules that mutually require
  21. each other? Kawa separates the "scan" stage (top-level
  22. scanning of a module, looking for definitions), and "rewrite"
  23. (expand macros and resolve names) makes this possible.
  24. If module A sees a (require <B>), it needs to suspend scanning A,
  25. and import the definitions exported by B. If B has not been
  26. compiled yet, it must parse and scan B. If while scanning B, it
  27. sees a (require <A>), it must wait to import the definitions of A
  28. until we've done scanning B, returned to A, and finished scanning
  29. A. At that point we can add to B the definitions exported from
  30. A. Thus the (require <A>) in B.has to *lazily* imports A's
  31. definitions, using some kind of placeholder.
  32. One complication is knowing whether a (require <B>) refers to a
  33. source file to be compiled. It is not enough to check if a class
  34. B exists, since if we're compiling B we want to use the current
  35. source B.scm, not an older B.class. This is complicated by the
  36. (module-name B) declaration: We don't know whether source file
  37. B.scm provides the B class until we've parsed B.scm. A solution
  38. to this problem is that we first parse all the source files (as
  39. listed on the command line),
  40. yielding their S-expression form. We then check for module-name
  41. forms. However, the possibility of macros does complicate this:
  42. There could be a macro that re-defines module-name, and there
  43. could be a macro that expands to module-name. Also, we could
  44. have commands that change the reader or read-table. Arguably worrying
  45. about these possibilities may be overkill. However, it can be
  46. handled thus: Parse each source file to S-expressions. Scan each
  47. source file's S-expression until the first require (if any).
  48. Then go back to each source file, process the require, and scan
  49. the rest of the file. If we see a require for one of the source
  50. files later in the compilation list, skip it until the end. At
  51. the end process any deferred require's. Finally, do the
  52. "rewrite" step and the rest of compilation.
  53. */
  54. static java.util.Hashtable featureMap = new java.util.Hashtable();
  55. static void map(String featureName, String className) {
  56. featureMap.put(featureName, Mangling.mangleQualifiedName(className));
  57. }
  58. private static final String SLIB_PREFIX = "gnu.kawa.slib.";
  59. private static final String LIB_SRFI_PREFIX = "kawa.lib.srfi.";
  60. static {
  61. map("generic-write", SLIB_PREFIX + "genwrite");
  62. map("pretty-print", SLIB_PREFIX + "pp");
  63. map("pprint-file", SLIB_PREFIX + "ppfile");
  64. map("printf", SLIB_PREFIX + "printf");
  65. map("xml", SLIB_PREFIX + "XML");
  66. map("readtable", SLIB_PREFIX + "readtable");
  67. map("srfi-10", SLIB_PREFIX + "readtable");
  68. map("http", "gnu.kawa.servlet.HTTP");
  69. map("servlets", "gnu.kawa.servlet.servlets");
  70. map("srfi-1", SLIB_PREFIX + "srfi1");
  71. map("list-lib", SLIB_PREFIX + "srfi1");
  72. map("srfi-2", SLIB_PREFIX + "srfi2");
  73. map("and-let*", SLIB_PREFIX + "srfi2");
  74. map("srfi-8", LIB_SRFI_PREFIX + "8");
  75. map("receive", LIB_SRFI_PREFIX + "8");
  76. map("srfi-13", SLIB_PREFIX + "srfi13");
  77. map("srfi-14", SLIB_PREFIX + "srfi14");
  78. map("string-lib", SLIB_PREFIX + "srfi13");
  79. map("srfi-26", LIB_SRFI_PREFIX + "26");
  80. map("srfi-34", SLIB_PREFIX + "srfi34");
  81. map("srfi-35", SLIB_PREFIX + "conditions");
  82. map("condition", SLIB_PREFIX + "conditions");
  83. map("conditions", SLIB_PREFIX + "conditions");
  84. map("srfi-37", SLIB_PREFIX + "srfi37");
  85. map("args-fold", SLIB_PREFIX + "srfi37");
  86. map("srfi-41", SLIB_PREFIX + "Streams");
  87. map("srfi-41-streams", SLIB_PREFIX + "Streams");
  88. map("srfi-41-streams-type", SLIB_PREFIX + "StreamsType");
  89. map("srfi-41-streams-primitive", SLIB_PREFIX + "StreamsPrimitive");
  90. map("srfi-41-streams-derived", SLIB_PREFIX + "StreamsDerived");
  91. map("srfi-60", SLIB_PREFIX + "srfi60");
  92. map("srfi-64", SLIB_PREFIX + "testing");
  93. map("testing", SLIB_PREFIX + "testing");
  94. map("srfi-69", SLIB_PREFIX + "srfi69");
  95. map("hash-table", SLIB_PREFIX + "srfi69");
  96. map("basic-hash-tables", SLIB_PREFIX + "srfi69");
  97. map("srfi-95", LIB_SRFI_PREFIX + "95");
  98. map("sorting-and-merging", LIB_SRFI_PREFIX + "95");
  99. map("srfi-101", SLIB_PREFIX + "ralists");
  100. map("random-access-lists", SLIB_PREFIX + "ralists");
  101. map("ra-lists", SLIB_PREFIX + "ralists");
  102. map("regex", "kawa.lib.kawa.regex");
  103. map("pregexp", SLIB_PREFIX + "pregexp");
  104. map("gui", SLIB_PREFIX + "gui");
  105. map("swing-gui", SLIB_PREFIX + "swing");
  106. map("android-defs", "gnu.kawa.android.defs");
  107. map("javafx-defs", "gnu.kawa.javafx.defs");
  108. map("syntax-utils", SLIB_PREFIX + "syntaxutils");
  109. map("quaternions", "kawa.lib.kawa.quaternions");
  110. }
  111. public static String mapFeature(String featureName) {
  112. return (String) featureMap.get(featureName);
  113. }
  114. public static Object find(String typeName) {
  115. return ModuleManager.getInstance()
  116. .findWithClassName(typeName).getInstance();
  117. }
  118. @Override
  119. public boolean scanForDefinitions (Pair st, ScopeExp defs, Translator tr) {
  120. if (tr.getState() == Compilation.PROLOG_PARSING) {
  121. tr.setState(Compilation.PROLOG_PARSED);
  122. tr.pendingForm = st;
  123. // FIXME - we want to call 'run' here anyway, rather than have
  124. // it be emitted at the end of the 'body'.
  125. return true;
  126. }
  127. Pair args = (Pair) st.getCdr();
  128. Object name = args.getCar();
  129. Type type = null;
  130. Pair p;
  131. if (name instanceof Pair
  132. && tr.matches((p = (Pair) name).getCar(), Scheme.quote_str)) {
  133. Object fname = p.getCdr();
  134. if (! (fname instanceof Pair)
  135. || (p = (Pair) fname).getCdr() != LList.Empty
  136. || ! (p.getCar() instanceof Symbol)) {
  137. tr.error('e', "invalid quoted symbol for 'require'");
  138. return false;
  139. }
  140. fname = mapFeature(p.getCar().toString());
  141. if (fname == null) {
  142. tr.error('e', "unknown feature name '"+p.getCar()+"' for 'require'");
  143. return false;
  144. }
  145. type = ClassType.make((String) fname);
  146. }
  147. else if (name instanceof CharSequence) {
  148. String sourceName = name.toString();
  149. ModuleInfo info = lookupModuleFromSourcePath(sourceName, defs);
  150. if (info == null) {
  151. tr.error('e', "malformed URL: "+sourceName);
  152. return false;
  153. }
  154. return importDefinitions(null, info, null, tr.formStack, defs, tr);
  155. } else if (name instanceof Symbol && ! tr.selfEvaluatingSymbol(name)) {
  156. String requestedClass = name.toString();
  157. int nlen = requestedClass.length();
  158. if (nlen > 2 && requestedClass.charAt(0) == '<'
  159. && requestedClass.charAt(nlen-1) == '>')
  160. requestedClass = requestedClass.substring(1, nlen-1);
  161. String implicitSource = requestedClass.replace('.', '/');
  162. requestedClass = Mangling.mangleQualifiedName(requestedClass);
  163. String explicitSource = null;
  164. if (args.getCdr() instanceof Pair) {
  165. Object sname = ((Pair) args.getCdr()).getCar();
  166. if (sname instanceof CharSequence) {
  167. explicitSource = sname.toString();
  168. } // else ERROR
  169. }
  170. ImportFromLibrary.handleImport(implicitSource, explicitSource, requestedClass, defs, tr, null);
  171. return true;
  172. }
  173. if (! (type instanceof ClassType)) {
  174. if (type != null)
  175. tr.error('e', "specifier for 'require' is not a classname");
  176. else if (name instanceof SimpleSymbol)
  177. tr.error('e', "class '"+name+"' for 'require' not found");
  178. else
  179. tr.error('e', "invalid specifier for 'require'");
  180. return false;
  181. }
  182. ModuleInfo minfo;
  183. try {
  184. minfo = ModuleInfo.find((ClassType) type);
  185. } catch (Exception ex) {
  186. tr.error('e', "unknown class "+type.getName());
  187. return false;
  188. }
  189. importDefinitions(null, minfo, null,
  190. tr.formStack, defs, tr);
  191. return true;
  192. }
  193. public static ModuleInfo lookupModuleFromSourcePath (String sourceName, ScopeExp defs) {
  194. ModuleManager manager = ModuleManager.getInstance();
  195. String baseName = defs.getFileName();
  196. if (baseName != null
  197. && baseName != InPort.systemInFilename
  198. && baseName != InPort.stringPathname
  199. && baseName != InPort.evalPathname)
  200. sourceName = Path.valueOf(baseName).resolve(sourceName).toString();
  201. return manager.findWithSourcePath(sourceName);
  202. }
  203. /** Import a module with a known source path.
  204. * @param className Optional fully-qualified name of module's class,
  205. * or null if unknown.
  206. */
  207. public static boolean
  208. importDefinitions(String className, ModuleInfo info,
  209. DeclSetMapper mapper, FormStack forms,
  210. ScopeExp defs, Compilation tr) {
  211. ModuleManager manager = ModuleManager.getInstance();
  212. if ((info.getState() & 1) == 0
  213. && (info.getCompilation() == null
  214. || info.getState() == Compilation.ERROR_SEEN)
  215. && ! info.checkCurrent(manager, System.currentTimeMillis())) {
  216. SourceMessages messages = tr.getMessages();
  217. Language language = Language.getDefaultLanguage();
  218. Compilation comp;
  219. try {
  220. InPort fstream = InPort.openFile(info.getSourceAbsPath());
  221. info.clearClass();
  222. int options = Language.PARSE_PROLOG;
  223. if (tr.immediate)
  224. options |= Language.PARSE_IMMEDIATE;
  225. comp = language.parse(fstream, messages, options, info);
  226. if (tr.getModule().getFlag(ModuleExp.INTERACTIVE))
  227. comp.getModule().setFlag(ModuleExp.INTERACTIVE);
  228. } catch (java.io.FileNotFoundException ex) {
  229. tr.error('e', "not found: "+ex.getMessage());
  230. return false;
  231. } catch (java.io.IOException ex) {
  232. tr.error('e', "caught "+ex);
  233. return false;
  234. } catch (SyntaxException ex) {
  235. if (ex.getMessages() != messages)
  236. throw new RuntimeException ("confussing syntax error: "+ex);
  237. // otherwise ignore it - it's already been recorded in messages.
  238. return false;
  239. }
  240. String compiledClassName = comp.getModule().classFor(comp).getName();
  241. if (className != null) {
  242. Map<String,ModuleInfo> subModuleMap = comp.subModuleMap;
  243. ModuleInfo modinfo;
  244. if (subModuleMap != null) {
  245. modinfo = subModuleMap.get(className);
  246. } else
  247. modinfo = null;
  248. if (modinfo == null) {
  249. String[] classPrefixPath
  250. = ImportFromLibrary.classPrefixPath;
  251. int classPrefixPathLength = classPrefixPath.length;
  252. for (int i = 0; i < classPrefixPathLength; i++) {
  253. String tname = classPrefixPath[i] + className;
  254. if (tname.equals(compiledClassName)) {
  255. modinfo = info;
  256. break;
  257. }
  258. }
  259. }
  260. if (modinfo == null)
  261. tr.error('e', ("file '"+info.getSourceAbsPath()
  262. +"' does not declare library '"
  263. +className+"'"));
  264. else
  265. info = modinfo;
  266. }
  267. }
  268. ModuleInfo curinfo = tr.getMinfo();
  269. if (curinfo != null && tr.getState() < Compilation.BODY_PARSED) {
  270. curinfo.addDependency(info);
  271. if (! info.loadEager(Compilation.COMPILED)
  272. && info.getState() < Compilation.RESOLVED) {
  273. // Oops. We found a cycle.
  274. tr.pushPendingImport(info, defs, forms, mapper);
  275. return true;
  276. }
  277. }
  278. ClassType type = info.getClassType();
  279. String tname = type.getName();
  280. boolean sharedModule = tr.sharedModuleDefs();
  281. boolean isRunnable = (info.getState() < Compilation.RESOLVED
  282. ? info.getCompilation().makeRunnable()
  283. : type.isSubtype(Compilation.typeRunnable));
  284. Declaration decl = null;
  285. ClassType thisType = ClassType.make("kawa.standard.require");
  286. Expression[] args = { new QuoteExp(tname) };
  287. Expression dofind = Invoke.makeInvokeStatic(thisType, "find", args);
  288. Field instanceField = null;
  289. Language language = tr.getLanguage();
  290. dofind.setLine(tr);
  291. ModuleExp mod = info.setupModuleExp();
  292. Map<Symbol,Expression> dmap
  293. = new LinkedHashMap<Symbol,Expression>();
  294. Map<String,Declaration> moduleReferences = null;
  295. for (Declaration fdecl = mod.firstDecl();
  296. fdecl != null; fdecl = fdecl.nextDecl()) {
  297. if (fdecl.isPrivate())
  298. continue;
  299. if (fdecl.getField() != null) {
  300. String fname = fdecl.getField().getName();
  301. if (fname.equals("$instance"))
  302. {
  303. instanceField = fdecl.getField();
  304. continue;
  305. }
  306. }
  307. if (fdecl.getField() != null
  308. && fdecl.getField().getName().endsWith("$instance")) {
  309. if (moduleReferences == null)
  310. moduleReferences = new HashMap<String,Declaration>();
  311. moduleReferences.put(fdecl.getField().getName(), fdecl);
  312. } else
  313. dmap.put((Symbol) fdecl.getSymbol(),
  314. new ReferenceExp(fdecl));
  315. }
  316. if (mapper != null)
  317. dmap = mapper.map(dmap, tr);
  318. for (Map.Entry<Symbol,Expression> entry : dmap.entrySet()) {
  319. Symbol aname = entry.getKey();
  320. ReferenceExp fref = (ReferenceExp) entry.getValue();
  321. Declaration fdecl = fref.getBinding();
  322. // We create an alias in the current context that points
  323. // a dummy declaration in the exported module. Normally,
  324. // followAliases will skip the alias, so we use the latter.
  325. // But if the binding is re-exported (or EXTERNAL_ACCESS
  326. // gets set), then we need a separate declaration.
  327. // (If EXTERNAL_ACCESS, the field gets PRIVATE_PREFIX.)
  328. Declaration adecl;
  329. Declaration old = defs.lookup(aname, language, language.getNamespaceOf(fdecl));
  330. if (old != null
  331. && ! old.getFlag(Declaration.NOT_DEFINING)
  332. && (Declaration.followAliases(old)
  333. == Declaration.followAliases(fdecl)))
  334. continue;
  335. if (decl == null && ! fdecl.getFlag(Declaration.STATIC_SPECIFIED)) {
  336. String iname = tname.replace('.', '$') + "$instance";
  337. decl = new Declaration(SimpleSymbol.valueOf(iname), type);
  338. decl.setPrivate(true);
  339. decl.setFlag(Declaration.IS_CONSTANT
  340. |Declaration.MODULE_REFERENCE);
  341. defs.addDeclaration(decl);
  342. decl.noteValue(dofind);
  343. SetExp sexp = new SetExp(decl, dofind);
  344. sexp.setLine(tr);
  345. sexp.setDefining(true);
  346. forms.push(sexp);
  347. decl.setFlag(Declaration.EARLY_INIT);
  348. // If Runnable, we need to set decl value in initializer,
  349. // and later 'run' it, so it needs to be stored in a field.
  350. if (isRunnable)
  351. decl.setSimple(false);
  352. decl.setFlag(Declaration.TYPE_SPECIFIED);
  353. }
  354. if (old != null
  355. && (old.getFlag(Declaration.NOT_DEFINING | Declaration.IS_UNKNOWN))) {
  356. old.setFlag(false, Declaration.NOT_DEFINING|Declaration.IS_UNKNOWN);
  357. adecl = old;
  358. } else {
  359. adecl = defs.addDeclaration(aname);
  360. if (old != null)
  361. ScopeExp.duplicateDeclarationError(old, adecl, tr);
  362. }
  363. adecl.setAlias(true);
  364. adecl.setIndirectBinding(true);
  365. fref.setContextDecl(decl);
  366. fref.setDontDereference(true);
  367. if (! sharedModule)
  368. adecl.setPrivate(true);
  369. linkDecls(adecl, fdecl, fref, forms, tr);
  370. Expression fval = fdecl.getValue();
  371. if (fdecl.isIndirectBinding() && fval instanceof ReferenceExp) {
  372. ReferenceExp aref = (ReferenceExp) adecl.getValue();
  373. Declaration xdecl = ((ReferenceExp) fval).getBinding();
  374. aref.setBinding(xdecl);
  375. // xdecl can be null on an error.
  376. if (xdecl != null && xdecl.needsContext()) {
  377. String iname
  378. = (xdecl.getField().getDeclaringClass().getName().replace('.', '$')
  379. + "$instance");
  380. Declaration cdecl = moduleReferences == null ? null
  381. : moduleReferences.get(iname);
  382. if (cdecl != null) {
  383. if (cdecl.context != defs) {
  384. Declaration acdecl = defs.addDeclaration(SimpleSymbol.valueOf(iname));
  385. moduleReferences.put(iname, acdecl);
  386. acdecl.setFlag(Declaration.IS_CONSTANT
  387. |Declaration.TYPE_SPECIFIED
  388. |Declaration.MODULE_REFERENCE);
  389. acdecl.setType(cdecl.getType());
  390. ReferenceExp cref = new ReferenceExp(cdecl);
  391. cref.setContextDecl(decl);
  392. linkDecls(acdecl, cdecl, cref, forms, tr);
  393. cdecl = acdecl;
  394. }
  395. cdecl.setFlag(Declaration.EXPORT_SPECIFIED);
  396. aref.setContextDecl(cdecl);
  397. }
  398. }
  399. }
  400. }
  401. if (isRunnable) {
  402. Method run = Compilation.typeRunnable.getDeclaredMethod("run", 0);
  403. if (decl != null) // Need to make sure 'run' is invoked.
  404. dofind = new ReferenceExp(decl);
  405. else {
  406. if (instanceField != null)
  407. { //Optimization
  408. args = new Expression[]
  409. { new QuoteExp(type), new QuoteExp("$instance") };
  410. dofind = new ApplyExp(SlotGet.staticField, args);
  411. }
  412. }
  413. dofind = new ApplyExp(run, new Expression[] { dofind });
  414. dofind.setLine(tr);
  415. forms.push(dofind);
  416. }
  417. return true;
  418. }
  419. static void linkDecls(Declaration adecl, Declaration fdecl,
  420. ReferenceExp fref, FormStack forms, Compilation tr) {
  421. adecl.setLocation(tr);
  422. // Imported variables should be read-only.
  423. adecl.setFlag(Declaration.IS_CONSTANT);
  424. if (fdecl.getFlag(Declaration.IS_SYNTAX))
  425. adecl.setFlag(Declaration.IS_SYNTAX);
  426. if (fdecl.isProcedureDecl())
  427. adecl.setProcedureDecl(true);
  428. if (fdecl.getFlag(Declaration.STATIC_SPECIFIED))
  429. adecl.setFlag(Declaration.STATIC_SPECIFIED);
  430. SetExp sexp = new SetExp(adecl, fref);
  431. adecl.setFlag(Declaration.EARLY_INIT);
  432. sexp.setDefining(true);
  433. forms.push(sexp);
  434. adecl.noteValue(fref);
  435. adecl.setFlag(Declaration.IS_IMPORTED);
  436. tr.push(adecl); // Add to translation env.
  437. }
  438. public Expression rewriteForm(Pair form, Translator tr) {
  439. return null;
  440. }
  441. public static interface DeclSetMapper {
  442. public Map<Symbol, Expression> map(Map<Symbol, Expression> decls, Compilation comp);
  443. }
  444. }