Download Jar. Uses 42250K of libraries. Click here for Pure Java version (406L/5K).
1 | lib 1400440 // jython 2.7.2 |
2 | lib 1400441 // objectweb asm |
3 | |
4 | set flag LeanMode. |
5 | |
6 | // Copyright (c) Corporation for National Research Initiatives |
7 | // edited by Stefan Reich |
8 | mainPackage org.python.core |
9 | mainClassName BytecodeLoader |
10 | set flag mainClassPublic. |
11 | |
12 | import org.objectweb.asm.ClassReader; |
13 | |
14 | import java.io.ByteArrayInputStream; |
15 | import java.io.IOException; |
16 | import java.io.ObjectInputStream; |
17 | import java.lang.reflect.Constructor; |
18 | import java.lang.reflect.Field; |
19 | import java.net.URL; |
20 | import java.net.URLClassLoader; |
21 | import java.util.LinkedList; |
22 | import java.util.List; |
23 | |
24 | /** |
25 | * Utility class for loading compiled Python modules and Java classes defined in Python modules. |
26 | */ |
27 | //public class BytecodeLoader { |
28 | |
29 | public sbool recordRawData; // set to true to record raw byte arrays in class loader |
30 | |
31 | /** |
32 | * Turn the Java class file data into a Java class. |
33 | * |
34 | * @param name fully-qualified binary name of the class |
35 | * @param data a class file as a byte array |
36 | * @param referents super-classes and interfaces that the new class will reference. |
37 | */ |
38 | @SuppressWarnings("unchecked") |
39 | public static Class<?> makeClass(String name, byte[] data, Class<?>... referents) { |
40 | @SuppressWarnings("resource") |
41 | Loader loader = new Loader(); |
42 | for (Class<?> referent : referents) { |
43 | try { |
44 | loader.addParent(referent.getClassLoader()); |
45 | } catch (SecurityException e) {} |
46 | } |
47 | Class<?> c = loader.loadClassFromBytes(name, data); |
48 | if (ContainsPyBytecode.class.isAssignableFrom(c)) { |
49 | try { |
50 | fixPyBytecode((Class<? extends ContainsPyBytecode>) c); |
51 | } catch (IllegalAccessException | NoSuchFieldException | ClassNotFoundException |
52 | | IOException e) { |
53 | throw new RuntimeException(e); |
54 | } |
55 | } |
56 | BytecodeNotification.notify(name, data, c); |
57 | return c; |
58 | } |
59 | |
60 | /** |
61 | * Turn the Java class file data into a Java class. |
62 | * |
63 | * @param name the name of the class |
64 | * @param referents super-classes and interfaces that the new class will reference. |
65 | * @param data a class file as a byte array |
66 | */ |
67 | public static Class<?> makeClass(String name, List<Class<?>> referents, byte[] data) { |
68 | if (referents != null) { |
69 | return makeClass(name, data, referents.toArray(new Class[referents.size()])); |
70 | } |
71 | return makeClass(name, data); |
72 | } |
73 | |
74 | private static PyCode parseSerializedCode(String code_str) |
75 | throws IOException, ClassNotFoundException { |
76 | // From Java 8 use: byte[] b = Base64.getDecoder().decode(code_str); |
77 | byte[] b = base64decode(code_str); |
78 | ByteArrayInputStream bi = new ByteArrayInputStream(b); |
79 | ObjectInputStream si = new ObjectInputStream(bi); |
80 | PyBytecode meth_code = (PyBytecode) si.readObject(); |
81 | si.close(); |
82 | bi.close(); |
83 | return meth_code; |
84 | } |
85 | |
86 | /** |
87 | * Implement a restricted form of base64 decoding compatible with the encoding in Module. This |
88 | * decoder treats characters outside the set of 64 necessary to encode data as errors, including |
89 | * the pad "=". As a result, the length of the argument exactly determines the size of array |
90 | * returned. |
91 | * |
92 | * @param src to decode |
93 | * @return a new byte array |
94 | * @throws IllegalArgumentException if src has an invalid character or impossible length. |
95 | */ |
96 | private static byte[] base64decode(String src) throws IllegalArgumentException { |
97 | |
98 | // Length L is a multiple of 4 plus 0, 2 or 3 tail characters (bearing 0, 8, or 16 bits) |
99 | final int L = src.length(); |
100 | final int tail = L % 4; // 0 to 3 where 1 (an extra 6 bits) is invalid. |
101 | if (tail == 1) { |
102 | throw new IllegalArgumentException("Input length invalid (4n+1)"); |
103 | } |
104 | |
105 | // src encodes exactly this many bytes: |
106 | final int N = (L / 4) * 3 + (tail > 0 ? tail - 1 : 0); |
107 | byte[] data = new byte[N]; |
108 | |
109 | // Work through src in blocks of 4 |
110 | int s = 0, b = 0, quantum; |
111 | while (s <= L - 4) { |
112 | // Process src[s:s+4] |
113 | quantum = (base64CharToBits(src.charAt(s++)) << 18) |
114 | + (base64CharToBits(src.charAt(s++)) << 12) |
115 | + (base64CharToBits(src.charAt(s++)) << 6) + base64CharToBits(src.charAt(s++)); |
116 | data[b++] = (byte) (quantum >> 16); |
117 | data[b++] = (byte) (quantum >> 8); |
118 | data[b++] = (byte) quantum; |
119 | } |
120 | |
121 | // Now deal with 2 or 3 tail characters, generating one or two bytes. |
122 | if (tail >= 2) { |
123 | // Repeat the loop body, but everything is 8 bits to the right. |
124 | quantum = (base64CharToBits(src.charAt(s++)) << 10) |
125 | + (base64CharToBits(src.charAt(s++)) << 4); |
126 | data[b++] = (byte) (quantum >> 8); |
127 | if (tail == 3) { |
128 | quantum += (base64CharToBits(src.charAt(s++)) >> 2); |
129 | data[b++] = (byte) quantum; |
130 | } |
131 | } |
132 | |
133 | return data; |
134 | } |
135 | |
136 | /** |
137 | * Helper for {@link #base64decode(String)}, converting one character. |
138 | * |
139 | * @param c to convert |
140 | * @return value 0..63 |
141 | * @throws IllegalArgumentException if not a base64 character |
142 | */ |
143 | private static int base64CharToBits(char c) throws IllegalArgumentException { |
144 | if (c >= 'a') { |
145 | if (c <= 'z') { |
146 | return c - 71; // c - 'a' + 26 |
147 | } |
148 | } else if (c >= 'A') { |
149 | if (c <= 'Z') { |
150 | return c - 'A'; |
151 | } |
152 | } else if (c >= '0') { |
153 | if (c <= '9') { |
154 | return c + 4; // c - '0' + 52 |
155 | } |
156 | } else if (c == '+') { |
157 | return 62; |
158 | } else if (c == '/') { |
159 | return 63; |
160 | } |
161 | throw new IllegalArgumentException("Invalid character " + c); |
162 | } |
163 | |
164 | /** |
165 | * This method looks for Python-Bytecode stored in String literals. |
166 | * While Java supports rather long strings, constrained only by |
167 | * int-addressing of arrays, it supports only up to 65535 characters |
168 | * in literals (not sure how escape-sequences are counted). |
169 | * To circumvent this limitation, the code is automatically splitted |
170 | * into several literals with the following naming-scheme. |
171 | * |
172 | * - The marker-interface 'ContainsPyBytecode' indicates that a class |
173 | * contains (static final) literals of the following scheme: |
174 | * - a prefix of '___' indicates a bytecode-containing string literal |
175 | * - a number indicating the number of parts follows |
176 | * - '0_' indicates that no splitting occurred |
177 | * - otherwise another number follows, naming the index of the literal |
178 | * - indexing starts at 0 |
179 | * |
180 | * Examples: |
181 | * ___0_method1 contains bytecode for method1 |
182 | * ___2_0_method2 contains first part of method2's bytecode |
183 | * ___2_1_method2 contains second part of method2's bytecode |
184 | * |
185 | * Note that this approach is provisional. In future, Jython might contain |
186 | * the bytecode directly as bytecode-objects. The current approach was |
187 | * feasible with much less complicated JVM bytecode-manipulation, but needs |
188 | * special treatment after class-loading. |
189 | */ |
190 | public static void fixPyBytecode(Class<? extends ContainsPyBytecode> c) |
191 | throws IllegalAccessException, NoSuchFieldException, java.io.IOException, |
192 | ClassNotFoundException { |
193 | Field[] fields = c.getDeclaredFields(); |
194 | for (Field fld: fields) { |
195 | String fldName = fld.getName(); |
196 | if (fldName.startsWith("___")) { |
197 | fldName = fldName.substring(3); |
198 | |
199 | String[] splt = fldName.split("_"); |
200 | if (splt[0].equals("0")) { |
201 | fldName = fldName.substring(2); |
202 | Field codeField = c.getDeclaredField(fldName); |
203 | if (codeField.get(null) == null) { |
204 | codeField.set(null, parseSerializedCode((String) fld.get(null))); |
205 | } |
206 | } else { |
207 | if (splt[1].equals("0")) { |
208 | fldName = fldName.substring(splt[0].length()+splt[1].length()+2); |
209 | Field codeField = c.getDeclaredField(fldName); |
210 | if (codeField.get(null) == null) { |
211 | // assemble original code-string: |
212 | int len = Integer.parseInt(splt[0]); |
213 | StringBuilder blt = new StringBuilder((String) fld.get(null)); |
214 | int pos = 1, pos0; |
215 | String partName; |
216 | while (pos < len) { |
217 | pos0 = pos; |
218 | for (Field fldPart: fields) { |
219 | partName = fldPart.getName(); |
220 | if (partName.length() != fldName.length() && |
221 | partName.startsWith("___") && |
222 | partName.endsWith(fldName)) { |
223 | String[] splt2 = partName.substring(3).split("_"); |
224 | if (Integer.parseInt(splt2[1]) == pos) { |
225 | blt.append((String) fldPart.get(null)); |
226 | pos += 1; |
227 | if (pos == len) { |
228 | break; |
229 | } |
230 | } |
231 | } |
232 | } |
233 | if (pos0 == pos) { |
234 | throw new RuntimeException( |
235 | "Invalid PyBytecode splitting in " + c.getName() |
236 | + ":\nSplit-index " + pos + " wasn't found."); |
237 | } |
238 | } |
239 | codeField.set(null, parseSerializedCode(blt.toString())); |
240 | } |
241 | } |
242 | } |
243 | } |
244 | } |
245 | } |
246 | |
247 | /** |
248 | * Turn the Java class file data for a compiled Python module into a {@code PyCode} object, by |
249 | * constructing an instance of the named class and calling the instance's |
250 | * {@link PyRunnable#getMain()}. |
251 | * |
252 | * @param name fully-qualified binary name of the class |
253 | * @param data a class file as a byte array |
254 | * @param filename to provide to the constructor of the named class |
255 | * @return the {@code PyCode} object produced by the named class' {@code getMain} |
256 | */ |
257 | public static PyCode makeCode(String name, byte[] data, String filename) { |
258 | try { |
259 | Class<?> c = makeClass(name, data); |
260 | // A compiled module has a constructor taking a String filename argument. |
261 | Constructor<?> cons = c.getConstructor(new Class<?>[] {String.class}); |
262 | Object instance = cons.newInstance(new Object[] {filename}); |
263 | PyCode result = ((PyRunnable) instance).getMain(); |
264 | return result; |
265 | } catch (Exception e) { |
266 | throw Py.JavaError(e); |
267 | } |
268 | } |
269 | |
270 | public static class Loader extends URLClassLoader { |
271 | Map<S, byte[]> rawData = recordRawData ? synchroHashMap() : null; |
272 | |
273 | private LinkedList<ClassLoader> parents = new LinkedList<>(); |
274 | |
275 | public Loader() { |
276 | super(new URL[0]); |
277 | parents.add(imp.getSyspathJavaLoader()); |
278 | } |
279 | |
280 | /** Add given loader at the front of the list of the parent list (if not {@code null}). */ |
281 | public void addParent(ClassLoader referent) { |
282 | if (referent != null && !parents.contains(referent)) { |
283 | parents.addFirst(referent); |
284 | } |
285 | } |
286 | |
287 | @Override |
288 | protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { |
289 | Class<?> c = findLoadedClass(name); |
290 | if (c != null) { |
291 | return c; |
292 | } |
293 | for (ClassLoader loader : parents) { |
294 | try { |
295 | return loader.loadClass(name); |
296 | } catch (ClassNotFoundException cnfe) {} |
297 | } |
298 | // couldn't find the .class file on sys.path |
299 | throw new ClassNotFoundException(name); |
300 | } |
301 | |
302 | /** |
303 | * Define the named class using the class file data provided, and resolve it. (See JVM |
304 | * specification.) For class names ending "$py", this method may adjust that name to that |
305 | * found in the class file itself. |
306 | * |
307 | * @param name fully-qualified binary name of the class |
308 | * @param data a class file as a byte array |
309 | * @return the defined and resolved class |
310 | */ |
311 | public Class<?> loadClassFromBytes(String name, byte[] data) { |
312 | if (name.endsWith("$py")) { |
313 | try { |
314 | // Get the real class name: we might request a 'bar' |
315 | // Jython module that was compiled as 'foo.bar', or |
316 | // even 'baz.__init__' which is compiled as just 'baz' |
317 | ClassReader cr = new ClassReader(data); |
318 | name = cr.getClassName().replace('/', '.'); |
319 | } catch (RuntimeException re) { |
320 | // Probably an invalid .class, fallback to the |
321 | // specified name |
322 | } |
323 | } |
324 | |
325 | if (rawData != null) |
326 | rawData.put(name, data); |
327 | |
328 | Class<?> c = defineClass(name, data, 0, data.length, getClass().getProtectionDomain()); |
329 | resolveClass(c); |
330 | return c; |
331 | } |
332 | } |
333 | //} |
download show line numbers debug dex old transpilations
Travelled to 5 computer(s): bhatertpkbcr, mqqgnosmbjvj, onxytkatvevr, pyentgdyhuwx, vouqrxazstgt
No comments. add comment
Snippet ID: | #1030286 |
Snippet name: | Trying to augment Jython's class loader for use with BCEL |
Eternal ID of this version: | #1030286/12 |
Text MD5: | f9d54f334b1c362c0efb70abf541c8bd |
Transpilation MD5: | 3369a355a9fbbcd10a6e1f50738fe65c |
Author: | stefan |
Category: | javax / jython |
Type: | JavaX source code (desktop) |
Public (visible to everyone): | Yes |
Archived (hidden from active list): | No |
Created/modified: | 2020-11-27 15:56:20 |
Source code size: | 14174 bytes / 333 lines |
Pitched / IR pitched: | No / No |
Views / Downloads: | 148 / 620 |
Version history: | 11 change(s) |
Referenced in: | [show references] |