/******************************************************************************* * Copyright (c) 2000, 2013 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation * Andy Clement - Contributions for * Bug 383624 - [1.8][compiler] Revive code generation support for type annotations (from Olivier's work) *******************************************************************************/ package org.eclipse.jdt.internal.eval; import org.eclipse.jdt.core.compiler.CategorizedProblem; import org.eclipse.jdt.internal.compiler.ClassFile; import org.eclipse.jdt.internal.compiler.CompilationResult; import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; import org.eclipse.jdt.internal.compiler.codegen.CodeStream; import org.eclipse.jdt.internal.compiler.codegen.ConstantPool; import org.eclipse.jdt.internal.compiler.codegen.StackMapFrameCodeStream; import org.eclipse.jdt.internal.compiler.codegen.TypeAnnotationCodeStream; import org.eclipse.jdt.internal.compiler.lookup.Binding; import org.eclipse.jdt.internal.compiler.lookup.FieldBinding; import org.eclipse.jdt.internal.compiler.lookup.MethodBinding; import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding; import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding; import org.eclipse.jdt.internal.compiler.lookup.TagBits; import org.eclipse.jdt.internal.compiler.lookup.TypeVariableBinding; import org.eclipse.jdt.internal.compiler.util.Util; public class CodeSnippetClassFile extends ClassFile { /** * CodeSnippetClassFile constructor comment. * @param aType org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding * @param enclosingClassFile org.eclipse.jdt.internal.compiler.ClassFile * @param creatingProblemType boolean */ public CodeSnippetClassFile( org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding aType, org.eclipse.jdt.internal.compiler.ClassFile enclosingClassFile, boolean creatingProblemType) { /** * INTERNAL USE-ONLY * This methods creates a new instance of the receiver. * * @param aType org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding * @param enclosingClassFile org.eclipse.jdt.internal.compiler.codegen.ClassFile * @param creatingProblemType <CODE>boolean</CODE> */ this.referenceBinding = aType; initByteArrays(); // generate the magic numbers inside the header this.header[this.headerOffset++] = (byte) (0xCAFEBABEL >> 24); this.header[this.headerOffset++] = (byte) (0xCAFEBABEL >> 16); this.header[this.headerOffset++] = (byte) (0xCAFEBABEL >> 8); this.header[this.headerOffset++] = (byte) (0xCAFEBABEL >> 0); long targetVersion = this.targetJDK = this.referenceBinding.scope.compilerOptions().targetJDK; //TODO: Might have to update even for CLDC_1_1 this.header[this.headerOffset++] = (byte) (targetVersion >> 8); // minor high this.header[this.headerOffset++] = (byte) (targetVersion >> 0); // minor low this.header[this.headerOffset++] = (byte) (targetVersion >> 24); // major high this.header[this.headerOffset++] = (byte) (targetVersion >> 16); // major low this.constantPoolOffset = this.headerOffset; this.headerOffset += 2; this.constantPool = new ConstantPool(this); int accessFlags = aType.getAccessFlags(); if (!aType.isInterface()) { // class or enum accessFlags |= ClassFileConstants.AccSuper; } if (aType.isNestedType()) { if (aType.isStatic()) { // clear Acc_Static accessFlags &= ~ClassFileConstants.AccStatic; } if (aType.isPrivate()) { // clear Acc_Private and Acc_Public accessFlags &= ~(ClassFileConstants.AccPrivate | ClassFileConstants.AccPublic); } if (aType.isProtected()) { // clear Acc_Protected and set Acc_Public accessFlags &= ~ClassFileConstants.AccProtected; accessFlags |= ClassFileConstants.AccPublic; } } // clear Acc_Strictfp accessFlags &= ~ClassFileConstants.AccStrictfp; this.enclosingClassFile = enclosingClassFile; // now we continue to generate the bytes inside the contents array this.contents[this.contentsOffset++] = (byte) (accessFlags >> 8); this.contents[this.contentsOffset++] = (byte) accessFlags; int classNameIndex = this.constantPool.literalIndexForType(aType); this.contents[this.contentsOffset++] = (byte) (classNameIndex >> 8); this.contents[this.contentsOffset++] = (byte) classNameIndex; int superclassNameIndex; if (aType.isInterface()) { superclassNameIndex = this.constantPool.literalIndexForType(ConstantPool.JavaLangObjectConstantPoolName); } else { superclassNameIndex = (aType.superclass == null ? 0 : this.constantPool.literalIndexForType(aType.superclass)); } this.contents[this.contentsOffset++] = (byte) (superclassNameIndex >> 8); this.contents[this.contentsOffset++] = (byte) superclassNameIndex; ReferenceBinding[] superInterfacesBinding = aType.superInterfaces(); int interfacesCount = superInterfacesBinding.length; this.contents[this.contentsOffset++] = (byte) (interfacesCount >> 8); this.contents[this.contentsOffset++] = (byte) interfacesCount; for (int i = 0; i < interfacesCount; i++) { int interfaceIndex = this.constantPool.literalIndexForType(superInterfacesBinding[i]); this.contents[this.contentsOffset++] = (byte) (interfaceIndex >> 8); this.contents[this.contentsOffset++] = (byte) interfaceIndex; } this.produceAttributes = this.referenceBinding.scope.compilerOptions().produceDebugAttributes; this.creatingProblemType = creatingProblemType; if (this.targetJDK >= ClassFileConstants.JDK1_6) { this.produceAttributes |= ClassFileConstants.ATTR_STACK_MAP_TABLE; if (this.targetJDK >= ClassFileConstants.JDK1_8) { this.produceAttributes |= ClassFileConstants.ATTR_TYPE_ANNOTATION; this.codeStream = new TypeAnnotationCodeStream(this); } else { this.codeStream = new StackMapFrameCodeStream(this); } } else if (this.targetJDK == ClassFileConstants.CLDC_1_1) { this.targetJDK = ClassFileConstants.JDK1_1; // put back 45.3 this.produceAttributes |= ClassFileConstants.ATTR_STACK_MAP; this.codeStream = new StackMapFrameCodeStream(this); } else { this.codeStream = new CodeStream(this); } // retrieve the enclosing one guaranteed to be the one matching the propagated flow info // 1FF9ZBU: LFCOM:ALL - Local variable attributes busted (Sanity check) this.codeStream.maxFieldCount = aType.scope.outerMostClassScope().referenceType().maxFieldCount; } /** * INTERNAL USE-ONLY * Request the creation of a ClassFile compatible representation of a problematic type * * @param typeDeclaration org.eclipse.jdt.internal.compiler.ast.TypeDeclaration * @param unitResult org.eclipse.jdt.internal.compiler.CompilationUnitResult */ public static void createProblemType(TypeDeclaration typeDeclaration, CompilationResult unitResult) { SourceTypeBinding typeBinding = typeDeclaration.binding; ClassFile classFile = new CodeSnippetClassFile(typeBinding, null, true); // inner attributes if (typeBinding.hasMemberTypes()) { // see bug 180109 ReferenceBinding[] members = typeBinding.memberTypes; for (int i = 0, l = members.length; i < l; i++) classFile.recordInnerClasses(members[i]); } // TODO (olivier) handle cases where a field cannot be generated (name too long) // TODO (olivier) handle too many methods // inner attributes if (typeBinding.isNestedType()) { classFile.recordInnerClasses(typeBinding); } TypeVariableBinding[] typeVariables = typeBinding.typeVariables(); for (int i = 0, max = typeVariables.length; i < max; i++) { TypeVariableBinding typeVariableBinding = typeVariables[i]; if ((typeVariableBinding.tagBits & TagBits.ContainsNestedTypeReferences) != 0) { Util.recordNestedType(classFile, typeVariableBinding); } } // add its fields FieldBinding[] fields = typeBinding.fields(); if ((fields != null) && (fields != Binding.NO_FIELDS)) { classFile.addFieldInfos(); } else { // we have to set the number of fields to be equals to 0 classFile.contents[classFile.contentsOffset++] = 0; classFile.contents[classFile.contentsOffset++] = 0; } // leave some space for the methodCount classFile.setForMethodInfos(); // add its user defined methods int problemsLength; CategorizedProblem[] problems = unitResult.getErrors(); if (problems == null) { problems = new CategorizedProblem[0]; } CategorizedProblem[] problemsCopy = new CategorizedProblem[problemsLength = problems.length]; System.arraycopy(problems, 0, problemsCopy, 0, problemsLength); AbstractMethodDeclaration[] methodDecls = typeDeclaration.methods; boolean abstractMethodsOnly = false; if (methodDecls != null) { if (typeBinding.isInterface()) { if (typeBinding.scope.compilerOptions().sourceLevel < ClassFileConstants.JDK1_8) abstractMethodsOnly = true; // We generate a clinit which contains all the problems, since we may not be able to generate problem methods (< 1.8) and problem constructors (all levels). classFile.addProblemClinit(problemsCopy); } for (int i = 0, length = methodDecls.length; i < length; i++) { AbstractMethodDeclaration methodDecl = methodDecls[i]; MethodBinding method = methodDecl.binding; if (method == null) continue; if (abstractMethodsOnly) { method.modifiers = ClassFileConstants.AccPublic | ClassFileConstants.AccAbstract; } if (method.isConstructor()) { if (typeBinding.isInterface()) continue; classFile.addProblemConstructor(methodDecl, method, problemsCopy); } else if (method.isAbstract()) { classFile.addAbstractMethod(methodDecl, method); } else { classFile.addProblemMethod(methodDecl, method, problemsCopy); } } // add abstract methods classFile.addDefaultAbstractMethods(); } // propagate generation of (problem) member types if (typeDeclaration.memberTypes != null) { for (int i = 0, max = typeDeclaration.memberTypes.length; i < max; i++) { TypeDeclaration memberType = typeDeclaration.memberTypes[i]; if (memberType.binding != null) { ClassFile.createProblemType(memberType, unitResult); } } } classFile.addAttributes(); unitResult.record(typeBinding.constantPoolName(), classFile); } }