## vim: ft=makojava <% api = java_api nat = c_api.get_name %> // ========== // Define classes to handle project loading // ========== /** * Exception to represent errors during project manipulation */ public static final class ProjectManagerException extends RuntimeException { ProjectManagerException( final String message ) { super(message); } } /** * Enum to represent the source file mode for a GPR project */ public static enum SourceFileMode { // ----- Enum values ----- DEFAULT(0), ROOT_PROJECT(1), WHOLE_PROJECT(2), WHOLE_PROJECT_WITH_RUNTIME(3), ; // ----- Class attributes ----- /** Singleton that represents the none source file mode */ public static final SourceFileMode NONE = DEFAULT; /** The map from int to enum values */ private static final Map map = new HashMap<>(); // ----- Instance attributes ----- /** The value of the instance */ private final int value; // ----- Constructors ----- /** * The private constructor */ private SourceFileMode( final int value ) { this.value = value; } static { for(SourceFileMode elem : SourceFileMode.values()) { map.put(elem.value, elem); } } // ----- Enum methods ----- /** * Get a source file mode from a native integer value. * * @param cValue The native value of the enum. * @return The Java source file mode. * @throws EnumException If the given native value doesn't correspond * to an actual enum value. */ public static SourceFileMode fromC( final int cValue ) throws EnumException { if(!map.containsKey(cValue)) throw new EnumException( "Cannot get SourceFileMode from " + cValue ); return (SourceFileMode) map.get(cValue); } /** * Get the native integer value of the enum instance. * * @return The native C value. */ public int toC() { return this.value; } } /** * Enum to represent a given project loading option. */ public static enum ProjectOption { // ----- Enum values ----- AP(0), AUTOCONF(1), CONFIG(2), DB(3), DB_MINUS(4), IMPLICIT_WITH(5), RESOLVE_LINKS(6), NO_PROJECT(7), P(8), PRINT_GPR_REGISTRY(9), RELOCATE_BUILD_TREE(10), ROOT_DIR(11), RTS(12), SRC_SUBDIRS(13), SUBDIRS(14), TARGET(15), X(16), ; // ----- Class attributes ----- /** The map from int to enum values */ private static final Map map = new HashMap<>(); // ----- Instance attributes ----- /** The value of the instance */ private final int value; // ----- Constructors ----- /** * The private constructor */ private ProjectOption( final int value ) { this.value = value; } static { for(ProjectOption elem : ProjectOption.values()) { map.put(elem.value, elem); } } // ----- Enum methods ----- /** * Get a project option from a native integer value. * * @param cValue The native value of the enum. * @return The corresponding Java enum value. * @throws EnumException If the given native value doesn't correspond * to an actual enum value. */ public static ProjectOption fromC( final int cValue ) throws EnumException { if(!map.containsKey(cValue)) throw new EnumException( "Cannot get ProjectOption from " + cValue ); return (ProjectOption) map.get(cValue); } /** * Get the native integer value of the enum instance. * * @return The native C value. */ public int toC() { return this.value; } } ${java_doc("libadalang.gpr_options", 4)} public static final class ProjectOptions implements AutoCloseable { // ----- Instance attributes ----- /** * Reference to the native value. * * It is package visible so that binding code in the ProjectManager can * convert ProjectOptions arguments down to the C value. */ final PointerWrapper reference; // ----- Constructors ----- /** * Create a new project loading options holder. * * @param reference The reference to the native options holder. */ private ProjectOptions( final PointerWrapper reference ) { this.reference = reference; } /** * Create a new project loading options holder. */ public ProjectOptions() { if(ImageInfo.inImageCode()) { ProjectOptionsNative res = NI_LIB.${nat("gpr_options_create")}(); this.reference = new PointerWrapper(res); } else { this.reference = JNI_LIB.${nat("gpr_options_create")}(); } } /** * Add a switch to the given options. * * See the Ada API for `GPR2.Options.Add_Switch`. */ public void addSwitch( ProjectOption switch_, String param, String index, boolean overwrite ) { if(ImageInfo.inImageCode()) { try ( CCharPointerHolder paramNative = param == null ? null : CTypeConversion.toCString(param); CCharPointerHolder indexNative = index == null ? null : CTypeConversion.toCString(index); ) { NI_LIB.${nat("gpr_options_add_switch")}( this.reference.ni(), switch_.toC(), (param == null ? WordFactory.nullPointer() : paramNative.get()), (index == null ? WordFactory.nullPointer() : indexNative.get()), overwrite ? 1 : 0 ); } } else { JNI_LIB.${nat("gpr_options_add_switch")}( this, switch_.toC(), param, index, overwrite ); } try { checkException(); } catch (LangkitException e) { throw new ProjectManagerException(e.getMessage()); } } /** * Add a switch to the given options. * * See the Ada API for `GPR2.Options.Add_Switch`. */ public void addSwitch( ProjectOption switch_, String param, String index ) { this.addSwitch(switch_, param, index, false); } /** * Add a switch to the given options. * * See the Ada API for `GPR2.Options.Add_Switch`. */ public void addSwitch( ProjectOption switch_, String param ) { this.addSwitch(switch_, param, null, false); } /** * Add a switch to the given options. * * See the Ada API for `GPR2.Options.Add_Switch`. */ public void addSwitch( ProjectOption switch_ ) { this.addSwitch(switch_, null, null, false); } /** @see java.lang.AutoCloseable#close() */ @Override public void close() { if(ImageInfo.inImageCode()) { NI_LIB.${nat("gpr_options_free")}(this.reference.ni()); } else { JNI_LIB.${nat("gpr_options_free")}(this); } } } /** * This class is used for the GPR project loading. */ public static final class ProjectManager implements AutoCloseable { // ----- Class attributes ----- /** Singleton that represents the none project manager. */ public static final ProjectManager NONE = new ProjectManager(PointerWrapper.nullPointer()); // ----- Instance attributes ----- /** Reference to the native value. */ private final PointerWrapper reference; /** * List of diagnostics emitted by the native API during the project * opening. */ private final List diagnostics; // ----- Constructors ----- /** * Create a new project manager from its native reference. * * @param reference The reference to the native project manager. */ ProjectManager( final PointerWrapper reference ) { this.reference = reference; this.diagnostics = new ArrayList<>(); } /** * Create a new project manager. * * @param options The GPR loading options. * @param adaOnly Restrict project loading to the Ada language. */ public ProjectManager( final ProjectOptions options, final boolean adaOnly ) { if (options == null) { throw new ProjectManagerException( "non-null `options` argument required" ); } if(ImageInfo.inImageCode()) { // Declare native values final ProjectOptionsNative optionsPointer; final Pointer errorsPointer; // Prepare the result pointer final Pointer projectPointer = StackValue.get(SizeOf.get(VoidPointer.class)); projectPointer.writeWord(0, WordFactory.nullPointer()); optionsPointer = options.reference.ni(); errorsPointer = StackValue.get(SizeOf.get(WordPointer.class)); errorsPointer.writeWord(0, WordFactory.nullPointer()); NI_LIB.${nat("gpr_project_load")}( optionsPointer, adaOnly ? 1 : 0, projectPointer, errorsPointer ); // Check the langkit exception and cast it into a project manager error try { checkException(); } catch (LangkitException e) { throw new ProjectManagerException(e.getMessage()); } // Get the result of modified values final ProjectManagerNative projectManagerNative = (ProjectManagerNative) projectPointer.readWord(0); final StringArrayNative errorArrayNative = (StringArrayNative) errorsPointer.readWord(0); // `errorsPointer` is not allocated if an exception was raised during project file loading String[] errors = new String[0]; if (errorArrayNative.isNonNull()) { // Translate the error native array into a Java array errors = toJStringArray(errorArrayNative); // Free the error array NI_LIB.${nat("free_string_array")}(errorArrayNative); } // Check the langkit exception and cast it into a project manager error try { checkException(); } catch (LangkitException e) { throw new ProjectManagerException(e.getMessage()); } // Create the result project manager and add diagnostics if any this.reference = new PointerWrapper(projectManagerNative); this.diagnostics = List.of(errors); } else { // Call the native function from the stubs final List diagnostics = new ArrayList<>(); final PointerWrapper reference = JNI_LIB.${nat("gpr_project_load")}( options, adaOnly, diagnostics ); // Check the langkit exceptions try { checkException(); } catch (LangkitException e) { throw new ProjectManagerException(e.getMessage()); } // Return the project manager this.reference = reference; this.diagnostics = diagnostics; } } // ----- Graal C API methods ----- /** * Wrap a native project manager in the Java class. * * @param pointer The pointer to the native project manager. * @return The newly wrapped project manager. */ static ProjectManager wrap( final Pointer pointer ) { return wrap((ProjectManagerNative) pointer.readWord(0)); } /** * Wrap a native project manager in the Java class. * * @param projectManagerNative The native project manager to wrap. * @return The newly wrapped project manager. */ static ProjectManager wrap( final ProjectManagerNative projectManagerNative ) { return new ProjectManager(new PointerWrapper(projectManagerNative)); } /** * Unwrap the project manager inside the given pointer. * * @param pointer The pointer to write in. */ public void unwrap( final Pointer pointer ) { pointer.writeWord(0, this.unwrap()); } /** * Get the native value of the project manager. * * @return The native project manager. */ public ProjectManagerNative unwrap() { return (ProjectManagerNative) this.reference.ni(); } // ----- Class methods ----- /** * Translate a native string array structure into a Java string * array. * * @param stringArrayNative The native string array structure. * @return The Java string array. */ private static String[] toJStringArray( final StringArrayNative stringArrayNative ) { final String[] res = new String[stringArrayNative.get_length()]; final CCharPointerPointer nativeFilesPointer = stringArrayNative.get_c_ptr(); for(int i = 0 ; i < res.length ; i++) { final CCharPointer nativeFile = nativeFilesPointer.read(i); res[i] = toJString(nativeFile); } return res; } // ----- Instance methods ----- public List getDiagnostics() { return this.diagnostics; } /** * Create a unit provider for the given subproject. * * @param subproject The subproject for which to create a unit provider. * @return The unit provider for the project manager. */ public UnitProvider getProvider(final String subproject) { final UnitProvider result; if(ImageInfo.inImageCode()) { try ( CCharPointerHolder subprojectNative = subproject == null ? null : CTypeConversion.toCString(subproject); ) { UnitProviderNative unitProviderNative = NI_LIB.${nat('gpr_project_create_unit_provider')}( this.reference.ni(), (subproject == null ? WordFactory.nullPointer() : subprojectNative.get()) ); result = UnitProvider.wrap(unitProviderNative); } } else { result = JNI_LIB.${nat("gpr_project_create_unit_provider")}( this, subproject ); } return result; } /** * Create a unit provider for root project. */ public UnitProvider getProvider() { return this.getProvider(null); } ${java_doc("libadalang.gpr_project_create_context", 8)} public AnalysisContext createContext( String subproject, EventHandler eventHandler, boolean withTrivia, int tabStop ) { if(ImageInfo.inImageCode()) { try ( CCharPointerHolder subproject_c = subproject == null ? null : CTypeConversion.toCString(subproject); ) { // Manually allocate a C-level analysis context so that we can // initialize it ourselves. final PointerWrapper context = new PointerWrapper( NI_LIB.${nat("allocate_analysis_context")}() ); // Create the Java wrapper, so that we have one ready for // event handler callbacks triggered during context // initialization. AnalysisContext result = AnalysisContext.fromReference(context, eventHandler, true); // "result" has its own ownership share: release ours NI_LIB.${nat("context_decref")}(context.ni()); // TODO: attach "this" to "result" so that the former lives at // least as long as the former. // Finally, initialize the analysis context. Note that this // step may raise an exception: in that case, the analysis // context is considered not initialized: release it. NI_LIB.${nat("gpr_project_initialize_context")}( this.reference.ni(), context.ni(), (subproject == null ? WordFactory.nullPointer() : subproject_c.get()), (eventHandler == null ? WordFactory.nullPointer() : eventHandler.reference.ni()), withTrivia ? 1 : 0, tabStop ); final LangkitExceptionNative exc_c = NI_LIB.${nat("get_last_exception")}(); if (exc_c.isNonNull()) { LangkitException exc = wrapException(exc_c); NI_LIB.${nat("context_decref")}(context.ni()); throw exc; } return result; } } else { return JNI_LIB.${nat("gpr_project_create_context")}( this, subproject, eventHandler, withTrivia, tabStop ); } } /** * Get the files for the given subprojects in a string array. * * @param mode The file getting mode. * @param subprojects The subprojects to consider. * @return The array that contains the project files. */ public String[] getFiles( final SourceFileMode mode, final String[] subprojects ) { // Verify if the project is null if(this.reference.isNull()) return new String[0]; String[] result = null; LangkitException exc; if(ImageInfo.inImageCode()) { try ( CCharPointerPointerHolder subprojectsNative = subprojects == null ? null : CTypeConversion.toCStrings(subprojects); ) { // Call the C API. Keep track of a potential native // ²exception. StringArrayNative sourceFileArray = NI_LIB.${nat('gpr_project_source_files')}( this.reference.ni(), mode.toC(), (subprojects == null ? WordFactory.nullPointer() : subprojectsNative.get()), (subprojects == null ? 0 : subprojects.length) ); exc = getLastException(); // If the call was successful, create the Java array result // and initialize it, and free the C array result. if(exc == null) { result = toJStringArray(sourceFileArray); NI_LIB.${nat("free_string_array")}(sourceFileArray); } } } else { result = JNI_LIB.${nat("gpr_project_source_files")}( this, mode.toC(), subprojects ); exc = getLastException(); } // If we got an exception, turn it into a ProjectManagerException // one. if(exc != null) { throw new ProjectManagerException(exc.getMessage()); } return result; } /** * Get the files of the root project in a string array. * * @param mode The file getting mode. * @return The array that contains the project files. */ public String[] getFiles(final SourceFileMode mode) { return this.getFiles(mode, null); } /** @see java.lang.AutoCloseable#close() */ @Override public void close() { if(ImageInfo.inImageCode()) { NI_LIB.${nat("gpr_project_free")}(this.reference.ni()); } else { JNI_LIB.${nat("gpr_project_free")}(this); } } } ${java_doc("libadalang.create_auto_provider", 4)} public static UnitProvider createAutoProvider( final String[] sourceFiles, final String charset ) { if (ImageInfo.inImageCode()) { try ( CCharPointerPointerHolder sourceFilesNative = sourceFiles == null ? null : CTypeConversion.toCStrings(sourceFiles); CCharPointerHolder charsetNative = charset == null ? null : CTypeConversion.toCString(charset); ) { final UnitProviderNative unitProviderNative = NI_LIB.${nat('create_auto_provider')}( (sourceFiles == null ? WordFactory.nullPointer() : sourceFilesNative.get()), (charset == null ? WordFactory.nullPointer() : charsetNative.get()) ); return UnitProvider.wrap(unitProviderNative); } } else { return JNI_LIB.${nat("create_auto_provider")}( sourceFiles, charset ); } }