Not logged in.  Login/Logout/Register | List snippets | | Create snippet | Upload image | Upload data

333
LINES

< > BotCompany Repo | #1030286 // Trying to augment Jython's class loader for use with BCEL

JavaX source code (desktop) [tags: use-pretranspiled] - run with: x30.jar

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]