ClassGenerator.java revision 379:80c66d3fd872
1145519Sdarrenr/*
2145510Sdarrenr * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
3145510Sdarrenr * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4255332Scy *
5145510Sdarrenr * This code is free software; you can redistribute it and/or modify it
6145510Sdarrenr * under the terms of the GNU General Public License version 2 only, as
7145510Sdarrenr * published by the Free Software Foundation.  Oracle designates this
8255332Scy * particular file as subject to the "Classpath" exception as provided
9145510Sdarrenr * by Oracle in the LICENSE file that accompanied this code.
10145510Sdarrenr *
11145510Sdarrenr * This code is distributed in the hope that it will be useful, but WITHOUT
12255332Scy * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13145510Sdarrenr * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14145510Sdarrenr * version 2 for more details (a copy is included in the LICENSE file that
15145510Sdarrenr * accompanied this code).
16145510Sdarrenr *
17145510Sdarrenr * You should have received a copy of the GNU General Public License version
18145510Sdarrenr * 2 along with this work; if not, write to the Free Software Foundation,
19145510Sdarrenr * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20145510Sdarrenr *
21145510Sdarrenr * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22145510Sdarrenr * or visit www.oracle.com if you need additional information or have any
23145510Sdarrenr * questions.
24145510Sdarrenr */
25255332Scy
26145510Sdarrenrpackage jdk.nashorn.internal.tools.nasgen;
27145510Sdarrenr
28145547Sdarrenrimport static jdk.internal.org.objectweb.asm.Opcodes.ACC_PRIVATE;
29145547Sdarrenrimport static jdk.internal.org.objectweb.asm.Opcodes.ACC_PUBLIC;
30145510Sdarrenrimport static jdk.internal.org.objectweb.asm.Opcodes.ACC_STATIC;
31145510Sdarrenrimport static jdk.internal.org.objectweb.asm.Opcodes.H_INVOKESTATIC;
32145510Sdarrenrimport static jdk.internal.org.objectweb.asm.Opcodes.H_INVOKEVIRTUAL;
33145510Sdarrenrimport static jdk.nashorn.internal.tools.nasgen.StringConstants.CLINIT;
34145510Sdarrenrimport static jdk.nashorn.internal.tools.nasgen.StringConstants.DEFAULT_INIT_DESC;
35145510Sdarrenrimport static jdk.nashorn.internal.tools.nasgen.StringConstants.GETTER_PREFIX;
36145510Sdarrenrimport static jdk.nashorn.internal.tools.nasgen.StringConstants.GET_CLASS_NAME;
37145510Sdarrenrimport static jdk.nashorn.internal.tools.nasgen.StringConstants.GET_CLASS_NAME_DESC;
38145510Sdarrenrimport static jdk.nashorn.internal.tools.nasgen.StringConstants.INIT;
39255332Scyimport static jdk.nashorn.internal.tools.nasgen.StringConstants.LOOKUP_NEWPROPERTY;
40255332Scyimport static jdk.nashorn.internal.tools.nasgen.StringConstants.LOOKUP_NEWPROPERTY_DESC;
41255332Scyimport static jdk.nashorn.internal.tools.nasgen.StringConstants.LOOKUP_TYPE;
42145510Sdarrenrimport static jdk.nashorn.internal.tools.nasgen.StringConstants.MAP_DESC;
43145510Sdarrenrimport static jdk.nashorn.internal.tools.nasgen.StringConstants.MAP_FIELD_NAME;
44145510Sdarrenrimport static jdk.nashorn.internal.tools.nasgen.StringConstants.MAP_NEWMAP;
45145510Sdarrenrimport static jdk.nashorn.internal.tools.nasgen.StringConstants.MAP_NEWMAP_DESC;
46145510Sdarrenrimport static jdk.nashorn.internal.tools.nasgen.StringConstants.MAP_TYPE;
47145510Sdarrenrimport static jdk.nashorn.internal.tools.nasgen.StringConstants.OBJECT_DESC;
48255332Scyimport static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTIONIMPL_MAKEFUNCTION;
49255332Scyimport static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTIONIMPL_MAKEFUNCTION_DESC;
50145510Sdarrenrimport static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTIONIMPL_MAKEFUNCTION_SPECS_DESC;
51255332Scyimport static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTIONIMPL_TYPE;
52145510Sdarrenrimport static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_SETARITY;
53145510Sdarrenrimport static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_SETARITY_DESC;
54145510Sdarrenrimport static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_TYPE;
55145510Sdarrenrimport static jdk.nashorn.internal.tools.nasgen.StringConstants.SETTER_PREFIX;
56145510Sdarrenrimport static jdk.nashorn.internal.tools.nasgen.StringConstants.TYPE_OBJECT;
57145510Sdarrenr
58145510Sdarrenrimport java.io.BufferedInputStream;
59255332Scyimport java.io.FileInputStream;
60145510Sdarrenrimport java.io.IOException;
61145510Sdarrenrimport java.util.List;
62145510Sdarrenrimport jdk.internal.org.objectweb.asm.ClassReader;
63145510Sdarrenrimport jdk.internal.org.objectweb.asm.ClassVisitor;
64255332Scyimport jdk.internal.org.objectweb.asm.ClassWriter;
65145510Sdarrenrimport jdk.internal.org.objectweb.asm.FieldVisitor;
66145510Sdarrenrimport jdk.internal.org.objectweb.asm.Handle;
67145510Sdarrenrimport jdk.internal.org.objectweb.asm.MethodVisitor;
68145510Sdarrenrimport jdk.internal.org.objectweb.asm.Type;
69145510Sdarrenrimport jdk.nashorn.internal.tools.nasgen.MemberInfo.Kind;
70145510Sdarrenr
71145510Sdarrenr/**
72145510Sdarrenr * Base class for class generator classes.
73255332Scy *
74145510Sdarrenr */
75161357Sguidopublic class ClassGenerator {
76145510Sdarrenr    /** ASM class writer used to output bytecode for this class */
77145510Sdarrenr    protected final ClassWriter cw;
78145510Sdarrenr
79161357Sguido    /**
80145510Sdarrenr     * Constructor
81161357Sguido     */
82161357Sguido    protected ClassGenerator() {
83161357Sguido        this.cw = makeClassWriter();
84145510Sdarrenr    }
85145510Sdarrenr
86145510Sdarrenr    MethodGenerator makeStaticInitializer() {
87145510Sdarrenr        return makeStaticInitializer(cw);
88255332Scy    }
89145510Sdarrenr
90145510Sdarrenr    MethodGenerator makeConstructor() {
91145510Sdarrenr        return makeConstructor(cw);
92145510Sdarrenr    }
93145510Sdarrenr
94145510Sdarrenr    MethodGenerator makeMethod(final int access, final String name, final String desc) {
95145510Sdarrenr        return makeMethod(cw, access, name, desc);
96145510Sdarrenr    }
97145510Sdarrenr
98145510Sdarrenr    void addMapField() {
99145510Sdarrenr        addMapField(cw);
100145510Sdarrenr    }
101145510Sdarrenr
102145510Sdarrenr    void addField(final String name, final String desc) {
103145510Sdarrenr        addField(cw, name, desc);
104145510Sdarrenr    }
105145510Sdarrenr
106145510Sdarrenr    void addFunctionField(final String name) {
107145510Sdarrenr        addFunctionField(cw, name);
108145510Sdarrenr    }
109145510Sdarrenr
110145510Sdarrenr    void addGetter(final String owner, final MemberInfo memInfo) {
111145510Sdarrenr        addGetter(cw, owner, memInfo);
112145510Sdarrenr    }
113145510Sdarrenr
114145510Sdarrenr    void addSetter(final String owner, final MemberInfo memInfo) {
115145510Sdarrenr        addSetter(cw, owner, memInfo);
116255332Scy    }
117255332Scy
118255332Scy    void emitGetClassName(final String name) {
119255332Scy        final MethodGenerator mi = makeMethod(ACC_PUBLIC, GET_CLASS_NAME, GET_CLASS_NAME_DESC);
120145510Sdarrenr        mi.loadLiteral(name);
121145510Sdarrenr        mi.returnValue();
122145510Sdarrenr        mi.computeMaxs();
123170268Sdarrenr        mi.visitEnd();
124255332Scy    }
125255332Scy
126145510Sdarrenr    static ClassWriter makeClassWriter() {
127255332Scy        return new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS) {
128255332Scy            @Override
129255332Scy            protected String getCommonSuperClass(final String type1, final String type2) {
130145510Sdarrenr                try {
131145510Sdarrenr                    return super.getCommonSuperClass(type1, type2);
132145510Sdarrenr                } catch (final RuntimeException | LinkageError e) {
133145510Sdarrenr                    return StringConstants.OBJECT_TYPE;
134145510Sdarrenr                }
135145510Sdarrenr            }
136145510Sdarrenr        };
137145510Sdarrenr    }
138145510Sdarrenr
139145510Sdarrenr    static MethodGenerator makeStaticInitializer(final ClassVisitor cv) {
140153881Sguido        return makeStaticInitializer(cv, CLINIT);
141145510Sdarrenr    }
142145510Sdarrenr
143145510Sdarrenr    static MethodGenerator makeStaticInitializer(final ClassVisitor cv, final String name) {
144170268Sdarrenr        final int access = ACC_PUBLIC | ACC_STATIC;
145170268Sdarrenr        final String desc = DEFAULT_INIT_DESC;
146255332Scy        final MethodVisitor mv = cv.visitMethod(access, name, desc, null, null);
147255332Scy        return new MethodGenerator(mv, access, name, desc);
148255332Scy    }
149255332Scy
150255332Scy    static MethodGenerator makeConstructor(final ClassVisitor cv) {
151255332Scy        final int access = ACC_PUBLIC;
152255332Scy        final String name = INIT;
153255332Scy        final String desc = DEFAULT_INIT_DESC;
154255332Scy        final MethodVisitor mv = cv.visitMethod(access, name, desc, null, null);
155255332Scy        return new MethodGenerator(mv, access, name, desc);
156255332Scy    }
157170268Sdarrenr
158145510Sdarrenr    static MethodGenerator makeMethod(final ClassVisitor cv, final int access, final String name, final String desc) {
159153881Sguido        final MethodVisitor mv = cv.visitMethod(access, name, desc, null, null);
160153881Sguido        return new MethodGenerator(mv, access, name, desc);
161145510Sdarrenr    }
162145510Sdarrenr
163145510Sdarrenr    static void emitStaticInitPrefix(final MethodGenerator mi, final String className) {
164145510Sdarrenr        mi.visitCode();
165255332Scy        mi.pushNull();
166255332Scy        mi.putStatic(className, MAP_FIELD_NAME, MAP_DESC);
167255332Scy        mi.loadClass(className);
168255332Scy        mi.invokeStatic(MAP_TYPE, MAP_NEWMAP, MAP_NEWMAP_DESC);
169145510Sdarrenr        // stack: PropertyMap
170145510Sdarrenr    }
171145510Sdarrenr
172145510Sdarrenr    static void emitStaticInitSuffix(final MethodGenerator mi, final String className) {
173145510Sdarrenr        // stack: PropertyMap
174145510Sdarrenr        mi.putStatic(className, MAP_FIELD_NAME, MAP_DESC);
175145510Sdarrenr        mi.returnVoid();
176145510Sdarrenr        mi.computeMaxs();
177145510Sdarrenr        mi.visitEnd();
178145510Sdarrenr    }
179145510Sdarrenr
180145510Sdarrenr    @SuppressWarnings("fallthrough")
181145510Sdarrenr    private static Type memInfoType(final MemberInfo memInfo) {
182145510Sdarrenr        switch (memInfo.getJavaDesc().charAt(0)) {
183255332Scy            case 'I': return Type.INT_TYPE;
184145510Sdarrenr            case 'J': return Type.LONG_TYPE;
185145510Sdarrenr            case 'D': return Type.DOUBLE_TYPE;
186145510Sdarrenr            default:  assert false : memInfo.getJavaDesc();
187145510Sdarrenr            case 'L': return TYPE_OBJECT;
188145510Sdarrenr        }
189145510Sdarrenr    }
190145510Sdarrenr
191145510Sdarrenr    private static String getterDesc(final MemberInfo memInfo) {
192145510Sdarrenr        return Type.getMethodDescriptor(memInfoType(memInfo));
193145510Sdarrenr    }
194145510Sdarrenr
195145510Sdarrenr    private static String setterDesc(final MemberInfo memInfo) {
196255332Scy        return Type.getMethodDescriptor(Type.VOID_TYPE, memInfoType(memInfo));
197255332Scy    }
198255332Scy
199255332Scy    static void addGetter(final ClassVisitor cv, final String owner, final MemberInfo memInfo) {
200255332Scy        final int access = ACC_PUBLIC;
201255332Scy        final String name = GETTER_PREFIX + memInfo.getJavaName();
202255332Scy        final String desc = getterDesc(memInfo);
203145510Sdarrenr        final MethodVisitor mv = cv.visitMethod(access, name, desc, null, null);
204145510Sdarrenr        final MethodGenerator mi = new MethodGenerator(mv, access, name, desc);
205145510Sdarrenr        mi.visitCode();
206145510Sdarrenr        if (memInfo.isStatic() && memInfo.getKind() == Kind.PROPERTY) {
207145510Sdarrenr            mi.getStatic(owner, memInfo.getJavaName(), memInfo.getJavaDesc());
208145510Sdarrenr        } else {
209145510Sdarrenr            mi.loadLocal(0);
210145510Sdarrenr            mi.getField(owner, memInfo.getJavaName(), memInfo.getJavaDesc());
211145510Sdarrenr        }
212145510Sdarrenr        mi.returnValue();
213145510Sdarrenr        mi.computeMaxs();
214145510Sdarrenr        mi.visitEnd();
215145510Sdarrenr    }
216145510Sdarrenr
217145510Sdarrenr    static void addSetter(final ClassVisitor cv, final String owner, final MemberInfo memInfo) {
218145510Sdarrenr        final int access = ACC_PUBLIC;
219145510Sdarrenr        final String name = SETTER_PREFIX + memInfo.getJavaName();
220145510Sdarrenr        final String desc = setterDesc(memInfo);
221145510Sdarrenr        final MethodVisitor mv = cv.visitMethod(access, name, desc, null, null);
222145510Sdarrenr        final MethodGenerator mi = new MethodGenerator(mv, access, name, desc);
223145510Sdarrenr        mi.visitCode();
224145510Sdarrenr        if (memInfo.isStatic() && memInfo.getKind() == Kind.PROPERTY) {
225145510Sdarrenr            mi.loadLocal(1);
226145510Sdarrenr            mi.putStatic(owner, memInfo.getJavaName(), memInfo.getJavaDesc());
227145510Sdarrenr        } else {
228145510Sdarrenr            mi.loadLocal(0);
229145510Sdarrenr            mi.loadLocal(1);
230145510Sdarrenr            mi.putField(owner, memInfo.getJavaName(), memInfo.getJavaDesc());
231145510Sdarrenr        }
232145510Sdarrenr        mi.returnVoid();
233145510Sdarrenr        mi.computeMaxs();
234145510Sdarrenr        mi.visitEnd();
235145510Sdarrenr    }
236145510Sdarrenr
237145510Sdarrenr    static void addMapField(final ClassVisitor cv) {
238145510Sdarrenr        // add a MAP static field
239145510Sdarrenr        final FieldVisitor fv = cv.visitField(ACC_PRIVATE | ACC_STATIC,
240145510Sdarrenr            MAP_FIELD_NAME, MAP_DESC, null, null);
241145510Sdarrenr        if (fv != null) {
242145510Sdarrenr            fv.visitEnd();
243145510Sdarrenr        }
244145510Sdarrenr    }
245145510Sdarrenr
246145510Sdarrenr    static void addField(final ClassVisitor cv, final String name, final String desc) {
247145510Sdarrenr        final FieldVisitor fv = cv.visitField(ACC_PRIVATE, name, desc, null, null);
248145510Sdarrenr        if (fv != null) {
249145510Sdarrenr            fv.visitEnd();
250145510Sdarrenr        }
251145510Sdarrenr    }
252145510Sdarrenr
253145510Sdarrenr    static void addFunctionField(final ClassVisitor cv, final String name) {
254145510Sdarrenr        addField(cv, name, OBJECT_DESC);
255145510Sdarrenr    }
256145510Sdarrenr
257145510Sdarrenr    static void newFunction(final MethodGenerator mi, final String className, final MemberInfo memInfo, final List<MemberInfo> specs) {
258145510Sdarrenr        final boolean arityFound = (memInfo.getArity() != MemberInfo.DEFAULT_ARITY);
259145510Sdarrenr
260145510Sdarrenr        mi.loadLiteral(memInfo.getName());
261145510Sdarrenr        mi.visitLdcInsn(new Handle(H_INVOKESTATIC, className, memInfo.getJavaName(), memInfo.getJavaDesc()));
262145510Sdarrenr
263145510Sdarrenr        assert specs != null;
264145510Sdarrenr        if (!specs.isEmpty()) {
265145510Sdarrenr            mi.memberInfoArray(className, specs);
266145510Sdarrenr            mi.invokeStatic(SCRIPTFUNCTIONIMPL_TYPE, SCRIPTFUNCTIONIMPL_MAKEFUNCTION, SCRIPTFUNCTIONIMPL_MAKEFUNCTION_SPECS_DESC);
267145510Sdarrenr        } else {
268145510Sdarrenr            mi.invokeStatic(SCRIPTFUNCTIONIMPL_TYPE, SCRIPTFUNCTIONIMPL_MAKEFUNCTION, SCRIPTFUNCTIONIMPL_MAKEFUNCTION_DESC);
269145510Sdarrenr        }
270145510Sdarrenr
271145510Sdarrenr        if (arityFound) {
272145510Sdarrenr            mi.dup();
273145510Sdarrenr            mi.push(memInfo.getArity());
274145510Sdarrenr            mi.invokeVirtual(SCRIPTFUNCTION_TYPE, SCRIPTFUNCTION_SETARITY, SCRIPTFUNCTION_SETARITY_DESC);
275145510Sdarrenr        }
276172776Sdarrenr
277172776Sdarrenr    }
278172776Sdarrenr
279145510Sdarrenr    static void linkerAddGetterSetter(final MethodGenerator mi, final String className, final MemberInfo memInfo) {
280172776Sdarrenr        final String propertyName = memInfo.getName();
281172776Sdarrenr        // stack: PropertyMap
282172776Sdarrenr        mi.loadLiteral(propertyName);
283172776Sdarrenr        // setup flags
284172776Sdarrenr        mi.push(memInfo.getAttributes());
285172776Sdarrenr        // setup getter method handle
286172776Sdarrenr        String javaName = GETTER_PREFIX + memInfo.getJavaName();
287172776Sdarrenr        mi.visitLdcInsn(new Handle(H_INVOKEVIRTUAL, className, javaName, getterDesc(memInfo)));
288145510Sdarrenr        // setup setter method handle
289145510Sdarrenr        if (memInfo.isFinal()) {
290172776Sdarrenr            mi.pushNull();
291172776Sdarrenr        } else {
292172776Sdarrenr            javaName = SETTER_PREFIX + memInfo.getJavaName();
293172776Sdarrenr            mi.visitLdcInsn(new Handle(H_INVOKEVIRTUAL, className, javaName, setterDesc(memInfo)));
294172776Sdarrenr        }
295172776Sdarrenr        mi.invokeStatic(LOOKUP_TYPE, LOOKUP_NEWPROPERTY, LOOKUP_NEWPROPERTY_DESC);
296172776Sdarrenr        // stack: PropertyMap
297172776Sdarrenr    }
298172776Sdarrenr
299172776Sdarrenr    static void linkerAddGetterSetter(final MethodGenerator mi, final String className, final MemberInfo getter, final MemberInfo setter) {
300145510Sdarrenr        final String propertyName = getter.getName();
301255332Scy        // stack: PropertyMap
302145510Sdarrenr        mi.loadLiteral(propertyName);
303153881Sguido        // setup flags
304153881Sguido        mi.push(getter.getAttributes());
305153881Sguido        // setup getter method handle
306153881Sguido        mi.visitLdcInsn(new Handle(H_INVOKESTATIC, className,
307255332Scy                getter.getJavaName(), getter.getJavaDesc()));
308153881Sguido        // setup setter method handle
309255332Scy        if (setter == null) {
310255332Scy            mi.pushNull();
311255332Scy        } else {
312255332Scy            mi.visitLdcInsn(new Handle(H_INVOKESTATIC, className,
313255332Scy                    setter.getJavaName(), setter.getJavaDesc()));
314255332Scy        }
315255332Scy        mi.invokeStatic(LOOKUP_TYPE, LOOKUP_NEWPROPERTY, LOOKUP_NEWPROPERTY_DESC);
316153881Sguido        // stack: PropertyMap
317145510Sdarrenr    }
318145510Sdarrenr
319145510Sdarrenr    static ScriptClassInfo getScriptClassInfo(final String fileName) throws IOException {
320145510Sdarrenr        try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(fileName))) {
321145510Sdarrenr            return getScriptClassInfo(new ClassReader(bis));
322145510Sdarrenr        }
323145510Sdarrenr    }
324145510Sdarrenr
325145510Sdarrenr    static ScriptClassInfo getScriptClassInfo(final byte[] classBuf) {
326145510Sdarrenr        return getScriptClassInfo(new ClassReader(classBuf));
327255332Scy    }
328145510Sdarrenr
329145510Sdarrenr    private static ScriptClassInfo getScriptClassInfo(final ClassReader reader) {
330145510Sdarrenr        final ScriptClassInfoCollector scic = new ScriptClassInfoCollector();
331145510Sdarrenr        reader.accept(scic, 0);
332145510Sdarrenr        return scic.getScriptClassInfo();
333145510Sdarrenr    }
334145510Sdarrenr}
335145510Sdarrenr