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.ACCESSORPROPERTY_CREATE;
35import static jdk.nashorn.internal.tools.nasgen.StringConstants.ACCESSORPROPERTY_CREATE_DESC;
36import static jdk.nashorn.internal.tools.nasgen.StringConstants.ACCESSORPROPERTY_TYPE;
37import static jdk.nashorn.internal.tools.nasgen.StringConstants.ARRAYLIST_INIT_DESC;
38import static jdk.nashorn.internal.tools.nasgen.StringConstants.ARRAYLIST_TYPE;
39import static jdk.nashorn.internal.tools.nasgen.StringConstants.CLINIT;
40import static jdk.nashorn.internal.tools.nasgen.StringConstants.COLLECTIONS_EMPTY_LIST;
41import static jdk.nashorn.internal.tools.nasgen.StringConstants.COLLECTIONS_TYPE;
42import static jdk.nashorn.internal.tools.nasgen.StringConstants.COLLECTION_ADD;
43import static jdk.nashorn.internal.tools.nasgen.StringConstants.COLLECTION_ADD_DESC;
44import static jdk.nashorn.internal.tools.nasgen.StringConstants.COLLECTION_TYPE;
45import static jdk.nashorn.internal.tools.nasgen.StringConstants.DEFAULT_INIT_DESC;
46import static jdk.nashorn.internal.tools.nasgen.StringConstants.GETTER_PREFIX;
47import static jdk.nashorn.internal.tools.nasgen.StringConstants.GET_CLASS_NAME;
48import static jdk.nashorn.internal.tools.nasgen.StringConstants.GET_CLASS_NAME_DESC;
49import static jdk.nashorn.internal.tools.nasgen.StringConstants.INIT;
50import static jdk.nashorn.internal.tools.nasgen.StringConstants.LIST_DESC;
51import static jdk.nashorn.internal.tools.nasgen.StringConstants.NATIVESYMBOL_TYPE;
52import static jdk.nashorn.internal.tools.nasgen.StringConstants.OBJECT_DESC;
53import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROPERTYMAP_DESC;
54import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROPERTYMAP_FIELD_NAME;
55import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROPERTYMAP_NEWMAP;
56import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROPERTYMAP_NEWMAP_DESC;
57import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROPERTYMAP_TYPE;
58import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_CREATEBUILTIN;
59import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_CREATEBUILTIN_DESC;
60import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_CREATEBUILTIN_SPECS_DESC;
61import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_SETARITY;
62import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_SETARITY_DESC;
63import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_SETDOCUMENTATIONKEY;
64import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_SETDOCUMENTATIONKEY_DESC;
65import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_TYPE;
66import static jdk.nashorn.internal.tools.nasgen.StringConstants.SETTER_PREFIX;
67import static jdk.nashorn.internal.tools.nasgen.StringConstants.SYMBOL_DESC;
68import static jdk.nashorn.internal.tools.nasgen.StringConstants.SYMBOL_PREFIX;
69import static jdk.nashorn.internal.tools.nasgen.StringConstants.TYPE_OBJECT;
70
71import java.io.BufferedInputStream;
72import java.io.FileInputStream;
73import java.io.IOException;
74import java.util.List;
75import jdk.internal.org.objectweb.asm.ClassReader;
76import jdk.internal.org.objectweb.asm.ClassVisitor;
77import jdk.internal.org.objectweb.asm.ClassWriter;
78import jdk.internal.org.objectweb.asm.FieldVisitor;
79import jdk.internal.org.objectweb.asm.Handle;
80import jdk.internal.org.objectweb.asm.MethodVisitor;
81import jdk.internal.org.objectweb.asm.Type;
82import jdk.nashorn.internal.tools.nasgen.MemberInfo.Kind;
83
84/**
85 * Base class for class generator classes.
86 *
87 */
88public class ClassGenerator {
89    /** ASM class writer used to output bytecode for this class */
90    protected final ClassWriter cw;
91
92    /**
93     * Constructor
94     */
95    protected ClassGenerator() {
96        this.cw = makeClassWriter();
97    }
98
99    MethodGenerator makeStaticInitializer() {
100        return makeStaticInitializer(cw);
101    }
102
103    MethodGenerator makeConstructor() {
104        return makeConstructor(cw);
105    }
106
107    MethodGenerator makeMethod(final int access, final String name, final String desc) {
108        return makeMethod(cw, access, name, desc);
109    }
110
111    void addMapField() {
112        addMapField(cw);
113    }
114
115    void addField(final String name, final String desc) {
116        addField(cw, name, desc);
117    }
118
119    void addFunctionField(final String name) {
120        addFunctionField(cw, name);
121    }
122
123    void addGetter(final String owner, final MemberInfo memInfo) {
124        addGetter(cw, owner, memInfo);
125    }
126
127    void addSetter(final String owner, final MemberInfo memInfo) {
128        addSetter(cw, owner, memInfo);
129    }
130
131    void emitGetClassName(final String name) {
132        final MethodGenerator mi = makeMethod(ACC_PUBLIC, GET_CLASS_NAME, GET_CLASS_NAME_DESC);
133        mi.loadLiteral(name);
134        mi.returnValue();
135        mi.computeMaxs();
136        mi.visitEnd();
137    }
138
139    static ClassWriter makeClassWriter() {
140        return new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS) {
141            @Override
142            protected String getCommonSuperClass(final String type1, final String type2) {
143                try {
144                    return super.getCommonSuperClass(type1, type2);
145                } catch (final RuntimeException | LinkageError e) {
146                    if (MemberInfo.isScriptObject(type1) && MemberInfo.isScriptObject(type2)) {
147                        return StringConstants.SCRIPTOBJECT_TYPE;
148                    }
149                    return StringConstants.OBJECT_TYPE;
150                }
151            }
152        };
153    }
154
155    static MethodGenerator makeStaticInitializer(final ClassVisitor cv) {
156        return makeStaticInitializer(cv, CLINIT);
157    }
158
159    static MethodGenerator makeStaticInitializer(final ClassVisitor cv, final String name) {
160        final int access =  ACC_PUBLIC | ACC_STATIC;
161        final String desc = DEFAULT_INIT_DESC;
162        final MethodVisitor mv = cv.visitMethod(access, name, desc, null, null);
163        return new MethodGenerator(mv, access, name, desc);
164    }
165
166    static MethodGenerator makeConstructor(final ClassVisitor cv) {
167        final int access = 0;
168        final String name = INIT;
169        final String desc = DEFAULT_INIT_DESC;
170        final MethodVisitor mv = cv.visitMethod(access, name, desc, null, null);
171        return new MethodGenerator(mv, access, name, desc);
172    }
173
174    static MethodGenerator makeMethod(final ClassVisitor cv, final int access, final String name, final String desc) {
175        final MethodVisitor mv = cv.visitMethod(access, name, desc, null, null);
176        return new MethodGenerator(mv, access, name, desc);
177    }
178
179    static void emitStaticInitPrefix(final MethodGenerator mi, final String className, final int memberCount) {
180        mi.visitCode();
181        if (memberCount > 0) {
182            // new ArrayList(int)
183            mi.newObject(ARRAYLIST_TYPE);
184            mi.dup();
185            mi.push(memberCount);
186            mi.invokeSpecial(ARRAYLIST_TYPE, INIT, ARRAYLIST_INIT_DESC);
187            // stack: ArrayList
188        } else {
189            // java.util.Collections.EMPTY_LIST
190            mi.getStatic(COLLECTIONS_TYPE, COLLECTIONS_EMPTY_LIST, LIST_DESC);
191            // stack List
192        }
193    }
194
195    static void emitStaticInitSuffix(final MethodGenerator mi, final String className) {
196        // stack: Collection
197        // pmap = PropertyMap.newMap(Collection<Property>);
198        mi.invokeStatic(PROPERTYMAP_TYPE, PROPERTYMAP_NEWMAP, PROPERTYMAP_NEWMAP_DESC);
199        // $nasgenmap$ = pmap;
200        mi.putStatic(className, PROPERTYMAP_FIELD_NAME, PROPERTYMAP_DESC);
201        mi.returnVoid();
202        mi.computeMaxs();
203        mi.visitEnd();
204    }
205
206    @SuppressWarnings("fallthrough")
207    private static Type memInfoType(final MemberInfo memInfo) {
208        switch (memInfo.getJavaDesc().charAt(0)) {
209            case 'I': return Type.INT_TYPE;
210            case 'J': return Type.LONG_TYPE;
211            case 'D': return Type.DOUBLE_TYPE;
212            default:  assert false : memInfo.getJavaDesc();
213            case 'L': return TYPE_OBJECT;
214        }
215    }
216
217    private static String getterDesc(final MemberInfo memInfo) {
218        return Type.getMethodDescriptor(memInfoType(memInfo));
219    }
220
221    private static String setterDesc(final MemberInfo memInfo) {
222        return Type.getMethodDescriptor(Type.VOID_TYPE, memInfoType(memInfo));
223    }
224
225    static void addGetter(final ClassVisitor cv, final String owner, final MemberInfo memInfo) {
226        final int access = ACC_PUBLIC;
227        final String name = GETTER_PREFIX + memInfo.getJavaName();
228        final String desc = getterDesc(memInfo);
229        final MethodVisitor mv = cv.visitMethod(access, name, desc, null, null);
230        final MethodGenerator mi = new MethodGenerator(mv, access, name, desc);
231        mi.visitCode();
232        if (memInfo.isStatic() && memInfo.getKind() == Kind.PROPERTY) {
233            mi.getStatic(owner, memInfo.getJavaName(), memInfo.getJavaDesc());
234        } else {
235            mi.loadLocal(0);
236            mi.getField(owner, memInfo.getJavaName(), memInfo.getJavaDesc());
237        }
238        mi.returnValue();
239        mi.computeMaxs();
240        mi.visitEnd();
241    }
242
243    static void addSetter(final ClassVisitor cv, final String owner, final MemberInfo memInfo) {
244        final int access = ACC_PUBLIC;
245        final String name = SETTER_PREFIX + memInfo.getJavaName();
246        final String desc = setterDesc(memInfo);
247        final MethodVisitor mv = cv.visitMethod(access, name, desc, null, null);
248        final MethodGenerator mi = new MethodGenerator(mv, access, name, desc);
249        mi.visitCode();
250        if (memInfo.isStatic() && memInfo.getKind() == Kind.PROPERTY) {
251            mi.loadLocal(1);
252            mi.putStatic(owner, memInfo.getJavaName(), memInfo.getJavaDesc());
253        } else {
254            mi.loadLocal(0);
255            mi.loadLocal(1);
256            mi.putField(owner, memInfo.getJavaName(), memInfo.getJavaDesc());
257        }
258        mi.returnVoid();
259        mi.computeMaxs();
260        mi.visitEnd();
261    }
262
263    static void addMapField(final ClassVisitor cv) {
264        // add a PropertyMap static field
265        final FieldVisitor fv = cv.visitField(ACC_PRIVATE | ACC_STATIC | ACC_FINAL,
266            PROPERTYMAP_FIELD_NAME, PROPERTYMAP_DESC, null, null);
267        if (fv != null) {
268            fv.visitEnd();
269        }
270    }
271
272    static void addField(final ClassVisitor cv, final String name, final String desc) {
273        final FieldVisitor fv = cv.visitField(ACC_PRIVATE, name, desc, null, null);
274        if (fv != null) {
275            fv.visitEnd();
276        }
277    }
278
279    static void addFunctionField(final ClassVisitor cv, final String name) {
280        addField(cv, name, OBJECT_DESC);
281    }
282
283    static void newFunction(final MethodGenerator mi, final String objName, final String className, final MemberInfo memInfo, final List<MemberInfo> specs) {
284        final boolean arityFound = (memInfo.getArity() != MemberInfo.DEFAULT_ARITY);
285
286        loadFunctionName(mi, memInfo.getName());
287        mi.visitLdcInsn(new Handle(H_INVOKESTATIC, className, memInfo.getJavaName(), memInfo.getJavaDesc(), false));
288
289        assert specs != null;
290        if (!specs.isEmpty()) {
291            mi.memberInfoArray(className, specs);
292            mi.invokeStatic(SCRIPTFUNCTION_TYPE, SCRIPTFUNCTION_CREATEBUILTIN, SCRIPTFUNCTION_CREATEBUILTIN_SPECS_DESC);
293        } else {
294            mi.invokeStatic(SCRIPTFUNCTION_TYPE, SCRIPTFUNCTION_CREATEBUILTIN, SCRIPTFUNCTION_CREATEBUILTIN_DESC);
295        }
296
297        if (arityFound) {
298            mi.dup();
299            mi.push(memInfo.getArity());
300            mi.invokeVirtual(SCRIPTFUNCTION_TYPE, SCRIPTFUNCTION_SETARITY, SCRIPTFUNCTION_SETARITY_DESC);
301        }
302
303        mi.dup();
304        mi.loadLiteral(memInfo.getDocumentationKey(objName));
305        mi.invokeVirtual(SCRIPTFUNCTION_TYPE, SCRIPTFUNCTION_SETDOCUMENTATIONKEY, SCRIPTFUNCTION_SETDOCUMENTATIONKEY_DESC);
306    }
307
308    static void linkerAddGetterSetter(final MethodGenerator mi, final String className, final MemberInfo memInfo) {
309        final String propertyName = memInfo.getName();
310        // stack: Collection
311        // dup of Collection instance
312        mi.dup();
313
314        // Load property name, converting to Symbol if it begins with "@@"
315        loadPropertyKey(mi, propertyName);
316        // setup flags
317        mi.push(memInfo.getAttributes());
318        // setup getter method handle
319        String javaName = GETTER_PREFIX + memInfo.getJavaName();
320        mi.visitLdcInsn(new Handle(H_INVOKEVIRTUAL, className, javaName, getterDesc(memInfo), false));
321        // setup setter method handle
322        if (memInfo.isFinal()) {
323            mi.pushNull();
324        } else {
325            javaName = SETTER_PREFIX + memInfo.getJavaName();
326            mi.visitLdcInsn(new Handle(H_INVOKEVIRTUAL, className, javaName, setterDesc(memInfo), false));
327        }
328        // property = AccessorProperty.create(key, flags, getter, setter);
329        mi.invokeStatic(ACCESSORPROPERTY_TYPE, ACCESSORPROPERTY_CREATE, ACCESSORPROPERTY_CREATE_DESC);
330        // boolean Collection.add(property)
331        mi.invokeInterface(COLLECTION_TYPE, COLLECTION_ADD, COLLECTION_ADD_DESC);
332        // pop return value of Collection.add
333        mi.pop();
334        // stack: Collection
335    }
336
337    static void linkerAddGetterSetter(final MethodGenerator mi, final String className, final MemberInfo getter, final MemberInfo setter) {
338        final String propertyName = getter.getName();
339        // stack: Collection
340        // dup of Collection instance
341        mi.dup();
342
343        // Load property name, converting to Symbol if it begins with "@@"
344        loadPropertyKey(mi, propertyName);
345        // setup flags
346        mi.push(getter.getAttributes());
347        // setup getter method handle
348        mi.visitLdcInsn(new Handle(H_INVOKESTATIC, className,
349                getter.getJavaName(), getter.getJavaDesc(), false));
350        // setup setter method handle
351        if (setter == null) {
352            mi.pushNull();
353        } else {
354            mi.visitLdcInsn(new Handle(H_INVOKESTATIC, className,
355                    setter.getJavaName(), setter.getJavaDesc(), false));
356        }
357        // property = AccessorProperty.create(key, flags, getter, setter);
358        mi.invokeStatic(ACCESSORPROPERTY_TYPE, ACCESSORPROPERTY_CREATE, ACCESSORPROPERTY_CREATE_DESC);
359        // boolean Collection.add(property)
360        mi.invokeInterface(COLLECTION_TYPE, COLLECTION_ADD, COLLECTION_ADD_DESC);
361        // pop return value of Collection.add
362        mi.pop();
363        // stack: Collection
364    }
365
366    static ScriptClassInfo getScriptClassInfo(final String fileName) throws IOException {
367        try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(fileName))) {
368            return getScriptClassInfo(new ClassReader(bis));
369        }
370    }
371
372    static ScriptClassInfo getScriptClassInfo(final byte[] classBuf) {
373        return getScriptClassInfo(new ClassReader(classBuf));
374    }
375
376    private static void loadFunctionName(final MethodGenerator mi, final String propertyName) {
377        if (propertyName.startsWith(SYMBOL_PREFIX)) {
378            mi.loadLiteral("Symbol[" + propertyName.substring(2) + "]");
379        } else {
380            mi.loadLiteral(propertyName);
381        }
382    }
383
384    private static void loadPropertyKey(final MethodGenerator mi, final String propertyName) {
385        if (propertyName.startsWith(SYMBOL_PREFIX)) {
386            mi.getStatic(NATIVESYMBOL_TYPE, propertyName.substring(2), SYMBOL_DESC);
387        } else {
388            mi.loadLiteral(propertyName);
389        }
390    }
391
392    private static ScriptClassInfo getScriptClassInfo(final ClassReader reader) {
393        final ScriptClassInfoCollector scic = new ScriptClassInfoCollector();
394        reader.accept(scic, 0);
395        return scic.getScriptClassInfo();
396    }
397}
398