comparison src/luan/impl/Compiled.java @ 1434:56fb5cd8228d

cache compiled code in temp files
author Franklin Schmidt <fschmidt@gmail.com>
date Sun, 29 Dec 2019 15:25:07 -0700
parents src/luan/impl/LuanJavaCompiler.java@1a68fc55a80c
children 65d4afc9ad07
comparison
equal deleted inserted replaced
1433:5f038be65271 1434:56fb5cd8228d
1 package luan.impl;
2
3 import java.io.OutputStream;
4 import java.io.ByteArrayOutputStream;
5 import java.io.StringWriter;
6 import java.io.File;
7 import java.io.FileInputStream;
8 import java.io.FileOutputStream;
9 import java.io.DataInputStream;
10 import java.io.DataOutputStream;
11 import java.io.IOException;
12 import java.net.URI;
13 import java.util.Collections;
14 import java.util.Map;
15 import java.util.HashMap;
16 import java.util.Set;
17 import java.util.HashSet;
18 import javax.tools.FileObject;
19 import javax.tools.JavaFileObject;
20 import javax.tools.SimpleJavaFileObject;
21 import javax.tools.JavaCompiler;
22 import javax.tools.ToolProvider;
23 import javax.tools.JavaFileManager;
24 import javax.tools.StandardJavaFileManager;
25 import javax.tools.ForwardingJavaFileManager;
26 import goodjava.logging.Logger;
27 import goodjava.logging.LoggerFactory;
28
29
30 final class Compiled {
31 private static final Logger logger = LoggerFactory.getLogger(Compiled.class);
32
33 private static class MyJavaFileObject extends SimpleJavaFileObject {
34 final ByteArrayOutputStream baos = new ByteArrayOutputStream();
35
36 MyJavaFileObject() {
37 super(URI.create("whatever"),JavaFileObject.Kind.CLASS);
38 }
39
40 @Override public OutputStream openOutputStream() {
41 return baos;
42 }
43
44 byte[] byteCode(String sourceName) {
45 byte[] byteCode = baos.toByteArray();
46 final int len = sourceName.length();
47 int max = byteCode.length-len-3;
48 outer:
49 for( int i=0; true; i++ ) {
50 if( i > max )
51 throw new RuntimeException("len="+len);
52 if( byteCode[i]==1 && (byteCode[i+1] << 8 | 0xFF & byteCode[i+2]) == len ) {
53 for( int j=i+3; j<i+3+len; j++ ) {
54 if( byteCode[j] != '$' )
55 continue outer;
56 }
57 System.arraycopy(sourceName.getBytes(),0,byteCode,i+3,len);
58 break;
59 }
60 }
61 return byteCode;
62 }
63 }
64
65 static Compiled compile(final String className,final String sourceName,final String code) {
66 final int len = sourceName.length();
67 StringBuilder sb = new StringBuilder(sourceName);
68 for( int i=0; i<len; i++ )
69 sb.setCharAt(i,'$');
70 JavaFileObject sourceFile = new SimpleJavaFileObject(URI.create(sb.toString()),JavaFileObject.Kind.SOURCE) {
71 @Override public CharSequence getCharContent(boolean ignoreEncodingErrors) {
72 return code;
73 }
74 @Override public String getName() {
75 return sourceName;
76 }
77 @Override public boolean isNameCompatible(String simpleName,JavaFileObject.Kind kind) {
78 return true;
79 }
80 };
81 final Map<String,MyJavaFileObject> map = new HashMap<String,MyJavaFileObject>();
82 JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
83 StandardJavaFileManager sjfm = compiler.getStandardFileManager(null,null,null);
84 ForwardingJavaFileManager fjfm = new ForwardingJavaFileManager(sjfm) {
85 @Override public JavaFileObject getJavaFileForOutput(JavaFileManager.Location location, String className, JavaFileObject.Kind kind, FileObject sibling) throws IOException {
86 if( map.containsKey(className) )
87 throw new RuntimeException(className);
88 MyJavaFileObject classFile = new MyJavaFileObject();
89 map.put(className,classFile);
90 return classFile;
91 }
92 };
93 StringWriter out = new StringWriter();
94 boolean b = compiler.getTask(out, fjfm, null, null, null, Collections.singletonList(sourceFile)).call();
95 if( !b )
96 throw new RuntimeException("\n"+out+"\ncode:\n"+code+"\n");
97 Map<String,byte[]> map2 = new HashMap<String,byte[]>();
98 for( Map.Entry<String,MyJavaFileObject> entry : map.entrySet() ) {
99 map2.put( entry.getKey(), entry.getValue().byteCode(sourceName) );
100 }
101 return new Compiled(className,map2);
102 }
103
104
105 private final String className;
106 private final Map<String,byte[]> map;
107 private final Set<String> set = new HashSet<String>();
108
109 private Compiled(String className,Map<String,byte[]> map) {
110 this.className = className;
111 this.map = map;
112 }
113
114 Class loadClass() {
115 try {
116 ClassLoader cl = new ClassLoader() {
117 @Override protected Class<?> findClass(String name) throws ClassNotFoundException {
118 if( !set.add(name) )
119 logger.error("dup "+name);
120 byte[] byteCode = map.get(name);
121 if( byteCode != null ) {
122 return defineClass(name, byteCode, 0, byteCode.length);
123 }
124 return super.findClass(name);
125 }
126 };
127 return cl.loadClass(className);
128 } catch(ClassNotFoundException e) {
129 throw new RuntimeException(e);
130 }
131 }
132
133
134 private static final int VERSION = 1;
135 private static final File tmpDir;
136 static {
137 File f = new File(System.getProperty("java.io.tmpdir"));
138 tmpDir = new File(f,"luan");
139 tmpDir.mkdir();
140 if( !tmpDir.exists() )
141 throw new RuntimeException();
142 }
143
144 static Compiled load(String fileName,String key) {
145 try {
146 File f = new File(tmpDir,fileName);
147 if( !f.exists() )
148 return null;
149 DataInputStream in = new DataInputStream(new FileInputStream(f));
150 if( in.readInt() != VERSION )
151 return null;
152 if( !in.readUTF().equals(key) )
153 return null;
154 String className = in.readUTF();
155 int n = in.readInt();
156 Map<String,byte[]> map = new HashMap<String,byte[]>();
157 for( int i=0; i<n; i++ ) {
158 String s = in.readUTF();
159 int len = in.readInt();
160 byte[] a = new byte[len];
161 in.readFully(a);
162 map.put(s,a);
163 }
164 in.close();
165 return new Compiled(className,map);
166 } catch(IOException e) {
167 logger.error("load failed",e);
168 return null;
169 }
170 }
171
172 void save(String fileName,String key) {
173 try {
174 File f = new File(tmpDir,fileName);
175 DataOutputStream out = new DataOutputStream(new FileOutputStream(f));
176 out.writeInt(VERSION);
177 out.writeUTF(key);
178 out.writeUTF(className);
179 out.writeInt(map.size());
180 for( Map.Entry<String,byte[]> entry : map.entrySet() ) {
181 out.writeUTF( entry.getKey() );
182 byte[] a = entry.getValue();
183 out.writeInt(a.length);
184 out.write(a,0,a.length);
185 }
186 out.close();
187 } catch(IOException e) {
188 logger.error("save failed",e);
189 }
190 }
191 }