ClassGenerator.java revision 414:edca88d3a03e
1/*
2 * Copyright (c) 2010, 2013, 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 jdk.nashorn.internal.tools.nasgen;
27
28import static jdk.internal.org.objectweb.asm.Opcodes.ACC_FINAL;
29import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PRIVATE;
30import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PUBLIC;
31import static jdk.internal.org.objectweb.asm.Opcodes.ACC_STATIC;
32import static jdk.internal.org.objectweb.asm.Opcodes.H_INVOKESTATIC;
33import static jdk.internal.org.objectweb.asm.Opcodes.H_INVOKEVIRTUAL;
34import static jdk.nashorn.internal.tools.nasgen.StringConstants.CLINIT;
35import static jdk.nashorn.internal.tools.nasgen.StringConstants.DEFAULT_INIT_DESC;
36import static jdk.nashorn.internal.tools.nasgen.StringConstants.GETTER_PREFIX;
37import static jdk.nashorn.internal.tools.nasgen.StringConstants.GET_CLASS_NAME;
38import static jdk.nashorn.internal.tools.nasgen.StringConstants.GET_CLASS_NAME_DESC;
39import static jdk.nashorn.internal.tools.nasgen.StringConstants.INIT;
40import static jdk.nashorn.internal.tools.nasgen.StringConstants.LOOKUP_NEWPROPERTY;
41import static jdk.nashorn.internal.tools.nasgen.StringConstants.LOOKUP_NEWPROPERTY_DESC;
42import static jdk.nashorn.internal.tools.nasgen.StringConstants.LOOKUP_TYPE;
43import static jdk.nashorn.internal.tools.nasgen.StringConstants.MAP_DESC;
44import static jdk.nashorn.internal.tools.nasgen.StringConstants.MAP_FIELD_NAME;
45import static jdk.nashorn.internal.tools.nasgen.StringConstants.MAP_NEWMAP;
46import static jdk.nashorn.internal.tools.nasgen.StringConstants.MAP_NEWMAP_DESC;
47import static jdk.nashorn.internal.tools.nasgen.StringConstants.MAP_TYPE;
48import static jdk.nashorn.internal.tools.nasgen.StringConstants.OBJECT_DESC;
49import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTIONIMPL_MAKEFUNCTION;
50import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTIONIMPL_MAKEFUNCTION_DESC;
51import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTIONIMPL_MAKEFUNCTION_SPECS_DESC;
52import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTIONIMPL_TYPE;
53import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_SETARITY;
54import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_SETARITY_DESC;
55import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_TYPE;
56import static jdk.nashorn.internal.tools.nasgen.StringConstants.SETTER_PREFIX;
57import static jdk.nashorn.internal.tools.nasgen.StringConstants.TYPE_OBJECT;
58
59import java.io.BufferedInputStream;
60import java.io.FileInputStream;
61import java.io.IOException;
62import java.util.List;
63import jdk.internal.org.objectweb.asm.ClassReader;
64import jdk.internal.org.objectweb.asm.ClassVisitor;
65import jdk.internal.org.objectweb.asm.ClassWriter;
66import jdk.internal.org.objectweb.asm.FieldVisitor;
67import jdk.internal.org.objectweb.asm.Handle;
68import jdk.internal.org.objectweb.asm.MethodVisitor;
69import jdk.internal.org.objectweb.asm.Type;
70import jdk.nashorn.internal.tools.nasgen.MemberInfo.Kind;
71
72/**
73 * Base class for class generator classes.
74 *
75 */
76public class ClassGenerator {
77    /** ASM class writer used to output bytecode for this class */
78    protected final ClassWriter cw;
79
80    /**
81     * Constructor
82     */
83    protected ClassGenerator() {
84        this.cw = makeClassWriter();
85    }
86
87    MethodGenerator makeStaticInitializer() {
88        return makeStaticInitializer(cw);
89    }
90
91    MethodGenerator makeConstructor() {
92        return makeConstructor(cw);
93    }
94
95    MethodGenerator makeMethod(final int access, final String name, final String desc) {
96        return makeMethod(cw, access, name, desc);
97    }
98
99    void addMapField() {
100        addMapField(cw);
101    }
102
103    void addField(final String name, final String desc) {
104        addField(cw, name, desc);
105    }
106
107    void addFunctionField(final String name) {
108        addFunctionField(cw, name);
109    }
110
111    void addGetter(final String owner, final MemberInfo memInfo) {
112        addGetter(cw, owner, memInfo);
113    }
114
115    void addSetter(final String owner, final MemberInfo memInfo) {
116        addSetter(cw, owner, memInfo);
117    }
118
119    void emitGetClassName(final String name) {
120        final MethodGenerator mi = makeMethod(ACC_PUBLIC, GET_CLASS_NAME, GET_CLASS_NAME_DESC);
121        mi.loadLiteral(name);
122        mi.returnValue();
123        mi.computeMaxs();
124        mi.visitEnd();
125    }
126
127    static ClassWriter makeClassWriter() {
128        return new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS) {
129            @Override
130            protected String getCommonSuperClass(final String type1, final String type2) {
131                try {
132                    return super.getCommonSuperClass(type1, type2);
133                } catch (final RuntimeException | LinkageError e) {
134                    return StringConstants.OBJECT_TYPE;
135                }
136            }
137        };
138    }
139
140    static MethodGenerator makeStaticInitializer(final ClassVisitor cv) {
141        return makeStaticInitializer(cv, CLINIT);
142    }
143
144    static MethodGenerator makeStaticInitializer(final ClassVisitor cv, final String name) {
145        final int access = ACC_PUBLIC | ACC_STATIC;
146        final String desc = DEFAULT_INIT_DESC;
147        final MethodVisitor mv = cv.visitMethod(access, name, desc, null, null);
148        return new MethodGenerator(mv, access, name, desc);
149    }
150
151    static MethodGenerator makeConstructor(final ClassVisitor cv) {
152        final int access = ACC_PUBLIC;
153        final String name = INIT;
154        final String desc = DEFAULT_INIT_DESC;
155        final MethodVisitor mv = cv.visitMethod(access, name, desc, null, null);
156        return new MethodGenerator(mv, access, name, desc);
157    }
158
159    static MethodGenerator makeMethod(final ClassVisitor cv, final int access, final String name, final String desc) {
160        final MethodVisitor mv = cv.visitMethod(access, name, desc, null, null);
161        return new MethodGenerator(mv, access, name, desc);
162    }
163
164    static void emitStaticInitPrefix(final MethodGenerator mi, final String className) {
165        mi.visitCode();
166        mi.pushNull();
167        mi.putStatic(className, MAP_FIELD_NAME, MAP_DESC);
168        mi.invokeStatic(MAP_TYPE, MAP_NEWMAP, MAP_NEWMAP_DESC);
169        // stack: PropertyMap
170    }
171
172    static void emitStaticInitSuffix(final MethodGenerator mi, final String className) {
173        // stack: PropertyMap
174        mi.putStatic(className, MAP_FIELD_NAME, MAP_DESC);
175        mi.returnVoid();
176        mi.computeMaxs();
177        mi.visitEnd();
178    }
179
180    @SuppressWarnings("fallthrough")
181    private static Type memInfoType(final MemberInfo memInfo) {
182        switch (memInfo.getJavaDesc().charAt(0)) {
183            case 'I': return Type.INT_TYPE;
184            case 'J': return Type.LONG_TYPE;
185            case 'D': return Type.DOUBLE_TYPE;
186            default:  assert false : memInfo.getJavaDesc();
187            case 'L': return TYPE_OBJECT;
188        }
189    }
190
191    private static String getterDesc(final MemberInfo memInfo) {
192        return Type.getMethodDescriptor(memInfoType(memInfo));
193    }
194
195    private static String setterDesc(final MemberInfo memInfo) {
196        return Type.getMethodDescriptor(Type.VOID_TYPE, memInfoType(memInfo));
197    }
198
199    static void addGetter(final ClassVisitor cv, final String owner, final MemberInfo memInfo) {
200        final int access = ACC_PUBLIC;
201        final String name = GETTER_PREFIX + memInfo.getJavaName();
202        final String desc = getterDesc(memInfo);
203        final MethodVisitor mv = cv.visitMethod(access, name, desc, null, null);
204        final MethodGenerator mi = new MethodGenerator(mv, access, name, desc);
205        mi.visitCode();
206        if (memInfo.isStatic() && memInfo.getKind() == Kind.PROPERTY) {
207            mi.getStatic(owner, memInfo.getJavaName(), memInfo.getJavaDesc());
208        } else {
209            mi.loadLocal(0);
210            mi.getField(owner, memInfo.getJavaName(), memInfo.getJavaDesc());
211        }
212        mi.returnValue();
213        mi.computeMaxs();
214        mi.visitEnd();
215    }
216
217    static void addSetter(final ClassVisitor cv, final String owner, final MemberInfo memInfo) {
218        final int access = ACC_PUBLIC;
219        final String name = SETTER_PREFIX + memInfo.getJavaName();
220        final String desc = setterDesc(memInfo);
221        final MethodVisitor mv = cv.visitMethod(access, name, desc, null, null);
222        final MethodGenerator mi = new MethodGenerator(mv, access, name, desc);
223        mi.visitCode();
224        if (memInfo.isStatic() && memInfo.getKind() == Kind.PROPERTY) {
225            mi.loadLocal(1);
226            mi.putStatic(owner, memInfo.getJavaName(), memInfo.getJavaDesc());
227        } else {
228            mi.loadLocal(0);
229            mi.loadLocal(1);
230            mi.putField(owner, memInfo.getJavaName(), memInfo.getJavaDesc());
231        }
232        mi.returnVoid();
233        mi.computeMaxs();
234        mi.visitEnd();
235    }
236
237    static void addMapField(final ClassVisitor cv) {
238        // add a MAP static field
239        final FieldVisitor fv = cv.visitField(ACC_PRIVATE | ACC_STATIC | ACC_FINAL,
240            MAP_FIELD_NAME, MAP_DESC, null, null);
241        if (fv != null) {
242            fv.visitEnd();
243        }
244    }
245
246    static void addField(final ClassVisitor cv, final String name, final String desc) {
247        final FieldVisitor fv = cv.visitField(ACC_PRIVATE, name, desc, null, null);
248        if (fv != null) {
249            fv.visitEnd();
250        }
251    }
252
253    static void addFunctionField(final ClassVisitor cv, final String name) {
254        addField(cv, name, OBJECT_DESC);
255    }
256
257    static void newFunction(final MethodGenerator mi, final String className, final MemberInfo memInfo, final List<MemberInfo> specs) {
258        final boolean arityFound = (memInfo.getArity() != MemberInfo.DEFAULT_ARITY);
259
260        mi.loadLiteral(memInfo.getName());
261        mi.visitLdcInsn(new Handle(H_INVOKESTATIC, className, memInfo.getJavaName(), memInfo.getJavaDesc()));
262
263        assert specs != null;
264        if (!specs.isEmpty()) {
265            mi.memberInfoArray(className, specs);
266            mi.invokeStatic(SCRIPTFUNCTIONIMPL_TYPE, SCRIPTFUNCTIONIMPL_MAKEFUNCTION, SCRIPTFUNCTIONIMPL_MAKEFUNCTION_SPECS_DESC);
267        } else {
268            mi.invokeStatic(SCRIPTFUNCTIONIMPL_TYPE, SCRIPTFUNCTIONIMPL_MAKEFUNCTION, SCRIPTFUNCTIONIMPL_MAKEFUNCTION_DESC);
269        }
270
271        if (arityFound) {
272            mi.dup();
273            mi.push(memInfo.getArity());
274            mi.invokeVirtual(SCRIPTFUNCTION_TYPE, SCRIPTFUNCTION_SETARITY, SCRIPTFUNCTION_SETARITY_DESC);
275        }
276
277    }
278
279    static void linkerAddGetterSetter(final MethodGenerator mi, final String className, final MemberInfo memInfo) {
280        final String propertyName = memInfo.getName();
281        // stack: PropertyMap
282        mi.loadLiteral(propertyName);
283        // setup flags
284        mi.push(memInfo.getAttributes());
285        // setup getter method handle
286        String javaName = GETTER_PREFIX + memInfo.getJavaName();
287        mi.visitLdcInsn(new Handle(H_INVOKEVIRTUAL, className, javaName, getterDesc(memInfo)));
288        // setup setter method handle
289        if (memInfo.isFinal()) {
290            mi.pushNull();
291        } else {
292            javaName = SETTER_PREFIX + memInfo.getJavaName();
293            mi.visitLdcInsn(new Handle(H_INVOKEVIRTUAL, className, javaName, setterDesc(memInfo)));
294        }
295        mi.invokeStatic(LOOKUP_TYPE, LOOKUP_NEWPROPERTY, LOOKUP_NEWPROPERTY_DESC);
296        // stack: PropertyMap
297    }
298
299    static void linkerAddGetterSetter(final MethodGenerator mi, final String className, final MemberInfo getter, final MemberInfo setter) {
300        final String propertyName = getter.getName();
301        // stack: PropertyMap
302        mi.loadLiteral(propertyName);
303        // setup flags
304        mi.push(getter.getAttributes());
305        // setup getter method handle
306        mi.visitLdcInsn(new Handle(H_INVOKESTATIC, className,
307                getter.getJavaName(), getter.getJavaDesc()));
308        // setup setter method handle
309        if (setter == null) {
310            mi.pushNull();
311        } else {
312            mi.visitLdcInsn(new Handle(H_INVOKESTATIC, className,
313                    setter.getJavaName(), setter.getJavaDesc()));
314        }
315        mi.invokeStatic(LOOKUP_TYPE, LOOKUP_NEWPROPERTY, LOOKUP_NEWPROPERTY_DESC);
316        // stack: PropertyMap
317    }
318
319    static ScriptClassInfo getScriptClassInfo(final String fileName) throws IOException {
320        try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(fileName))) {
321            return getScriptClassInfo(new ClassReader(bis));
322        }
323    }
324
325    static ScriptClassInfo getScriptClassInfo(final byte[] classBuf) {
326        return getScriptClassInfo(new ClassReader(classBuf));
327    }
328
329    private static ScriptClassInfo getScriptClassInfo(final ClassReader reader) {
330        final ScriptClassInfoCollector scic = new ScriptClassInfoCollector();
331        reader.accept(scic, 0);
332        return scic.getScriptClassInfo();
333    }
334}
335