Custom plugin with custom decompiler

Hello I'm currently working on a plugin for a web framework that uses a custom programming language. I was able to write a plugin that has basic functionality built for working with the full source code so far. The problem I've run into is that most users who may use this framework, will only have access to the compiled code base. I've written a script in java that can decompile the compiled source to a simplified version of the full source, but I don't know how to integrate it.

I'm not sure how to get Intellij to decompile the files when it encounters them, and I don't know how to index those files if it does. I've been digging through source code from some repositiories and through the forum, but I still can't figure it out. I've tried adding  <psi.classFileDecompiler /> extension as well as the <filetype.decompiler />, but they do not pick up on the compiled code.

The other problem I have is that the compiled file is comprised of many sub-files that have been flattened into a single file. I know how to split them, but I don't know how to have them show up in Intellij as the main language files. Intellij cannot handle the decompiled source code as a single file. It runs out of memory, so I'd need to split it into the files the user would expect.

Also, when trying to use classFileDecompiler, I have to provide a custom editor, but I just want to use the basic one that would show up when opening the source code, only it would be read-only. I've been looking into it, but I can't figure out how to just open it in a standard editor. 

If you could help me, I'd really appreciate it.

0
2 comments

Could you please provide more details? It is not clear what a project using the framework looks like, what type of file the custom language is compiled to, etc.

0

I don't have an actual, short code example to show, as the compiled code is about 15,000 lines long, and the decompiled code is 9,000, but here is a sample of each. These parts are not directly mapped together though, just an example of each

Compiled

@STATIC;1.0;p;21;_CPAutocompleteMenu.jt;21227;@STATIC;1.0;I;21;Foundation/CPObject.ji;13;CPTextField.ji;13;CPTableView.ji;15;_CPMenuWindow.jt;21125;objj_executeFile("Foundation/CPObject.j", NO);objj_executeFile("CPTextField.j", YES);objj_executeFile("CPTableView.j", YES);objj_executeFile("_CPMenuWindow.j", YES);var _CPAutocompleteMenuMaximumHeight = 307;
{var the_class = objj_allocateClassPair(CPObject, "_CPAutocompleteMenu"),
meta_class = the_class.isa;class_addIvars(the_class, [new objj_ivar("textField", "CPTextField"), new objj_ivar("contentArray", "CPArray"), new objj_ivar("widestItemWidth", "float"), new objj_ivar("_menuWindow", "CPWindow"), new objj_ivar("scrollView", "CPScrollView"), new objj_ivar("tableView", "CPTableView"), new objj_ivar("_showCompletionsTimer", "CPTimer")]);objj_registerClassPair(the_class);
class_addMethods(the_class, [new objj_method(sel_getUid("textField"), function(self, _cmd)
{
return self.textField;
}
,["CPTextField"]), new objj_method(sel_getUid("setTextField:"), function(self, _cmd, newValue)
{
self.textField = newValue;
}
,["void","CPTextField"]), new objj_method(sel_getUid("contentArray"), function(self, _cmd)
{
return self.contentArray;
}
,["CPArray"]), new objj_method(sel_getUid("setContentArray:"), function(self, _cmd, newValue)
{
self.contentArray = newValue;
}
,["void","CPArray"]), new objj_method(sel_getUid("initWithTextField:"), function(self, _cmd, aTextField)
{
if (self = (objj_getClass("_CPAutocompleteMenu").super_class.method_dtable["init"] || _objj_forward)(self, "init"))
{
self.textField = aTextField;
self._menuWindow = ((___r1 = (_CPAutocompleteWindow == null ? null : (_CPAutocompleteWindow.isa.method_msgSend["alloc"] || _objj_forward)(_CPAutocompleteWindow, "alloc"))), ___r1 == null ? null : (___r1.isa.method_msgSend["initWithContentRect:styleMask:"] || _objj_forward)(___r1, "initWithContentRect:styleMask:", CGRectMake(0, 0, 100, 100), CPBorderlessWindowMask));
((___r1 = self._menuWindow), ___r1 == null ? null : (___r1.isa.method_msgSend["setLevel:"] || _objj_forward)(___r1, "setLevel:", CPPopUpMenuWindowLevel));
((___r1 = self._menuWindow), ___r1 == null ? null : (___r1.isa.method_msgSend["setHasShadow:"] || _objj_forward)(___r1, "setHasShadow:", YES));
((___r1 = self._menuWindow), ___r1 == null ? null : (___r1.isa.method_msgSend["setShadowStyle:"] || _objj_forward)(___r1, "setShadowStyle:", CPMenuWindowShadowStyle));
((___r1 = self._menuWindow), ___r1 == null ? null : (___r1.isa.method_msgSend["setAcceptsMouseMovedEvents:"] || _objj_forward)(___r1, "setAcceptsMouseMovedEvents:", YES));
((___r1 = self._menuWindow), ___r1 == null ? null : (___r1.isa.method_msgSend["setBackgroundColor:"] || _objj_forward)(___r1, "setBackgroundColor:", (_CPMenuWindow.isa.method_msgSend["backgroundColorForBackgroundStyle:"] || _objj_forward)(_CPMenuWindow, "backgroundColorForBackgroundStyle:", _CPMenuWindowPopUpBackgroundStyle)));
var contentView = ((___r1 = self._menuWindow), ___r1 == null ? null : (___r1.isa.method_msgSend["contentView"] || _objj_forward)(___r1, "contentView"));
self.scrollView = ((___r1 = (CPScrollView == null ? null : (CPScrollView.isa.method_msgSend["alloc"] || _objj_forward)(CPScrollView, "alloc"))), ___r1 == null ? null : (___r1.isa.method_msgSend["initWithFrame:"] || _objj_forward)(___r1, "initWithFrame:", CGRectMakeZero()));
((___r1 = self.scrollView), ___r1 == null ? null : (___r1.isa.method_msgSend["setAutohidesScrollers:"] || _objj_forward)(___r1, "setAutohidesScrollers:", YES));
((___r1 = self.scrollView), ___r1 == null ? null : (___r1.isa.method_msgSend["setHasHorizontalScroller:"] || _objj_forward)(___r1, "setHasHorizontalScroller:", NO));
(contentView == null ? null : (contentView.isa.method_msgSend["addSubview:"] || _objj_forward)(contentView, "addSubview:", self.scrollView));
self.tableView = ((___r1 = (_CPNonFirstResponderTableView == null ? null : (_CPNonFirstResponderTableView.isa.method_msgSend["alloc"] || _objj_forward)(_CPNonFirstResponderTableView, "alloc"))), ___r1 == null ? null : (___r1.isa.method_msgSend["initWithFrame:"] || _objj_forward)(___r1, "initWithFrame:", CGRectMakeZero()));
var tableColumn = (CPTableColumn.isa.method_msgSend["new"] || _objj_forward)(CPTableColumn, "new");
(tableColumn == null ? null : (tableColumn.isa.method_msgSend["setResizingMask:"] || _objj_forward)(tableColumn, "setResizingMask:", CPTableColumnAutoresizingMask));
((___r1 = self.tableView), ___r1 == null ? null : (___r1.isa.method_msgSend["addTableColumn:"] || _objj_forward)(___r1, "addTableColumn:", tableColumn));
((___r1 = self.tableView), ___r1 == null ? null : (___r1.isa.method_msgSend["setDataSource:"] || _objj_forward)(___r1, "setDataSource:", self));
((___r1 = self.tableView), ___r1 == null ? null : (___r1.isa.method_msgSend["setDelegate:"] || _objj_forward)(___r1, "setDelegate:", self));
((___r1 = self.tableView), ___r1 == null ? null : (___r1.isa.method_msgSend["setTarget:"] || _objj_forward)(___r1, "setTarget:", self));
((___r1 = self.tableView), ___r1 == null ? null : (___r1.isa.method_msgSend["setAction:"] || _objj_forward)(___r1, "setAction:", sel_getUid("complete:")));
((___r1 = self.tableView), ___r1 == null ? null : (___r1.isa.method_msgSend["setAllowsMultipleSelection:"] || _objj_forward)(___r1, "setAllowsMultipleSelection:", NO));
((___r1 = self.tableView), ___r1 == null ? null : (___r1.isa.method_msgSend["setHeaderView:"] || _objj_forward)(___r1, "setHeaderView:", nil));
((___r1 = self.tableView), ___r1 == null ? null : (___r1.isa.method_msgSend["setCornerView:"] || _objj_forward)(___r1, "setCornerView:", nil));
((___r1 = self.tableView), ___r1 == null ? null : (___r1.isa.method_msgSend["setRowHeight:"] || _objj_forward)(___r1, "setRowHeight:", 24.0));
((___r1 = self.tableView), ___r1 == null ? null : (___r1.isa.method_msgSend["setGridStyleMask:"] || _objj_forward)(___r1, "setGridStyleMask:", CPTableViewGridNone));
((___r1 = self.tableView), ___r1 == null ? null : (___r1.isa.method_msgSend["setBackgroundColor:"] || _objj_forward)(___r1, "setBackgroundColor:", (CPColor.isa.method_msgSend["clearColor"] || _objj_forward)(CPColor, "clearColor")));
((___r1 = self.scrollView), ___r1 == null ? null : (___r1.isa.method_msgSend["setDocumentView:"] || _objj_forward)(___r1, "setDocumentView:", self.tableView));
}
return self;
var ___r1;
}
,["id","CPTextField"])]);
}

Decompiled:

/*Start File: _CPImageAndTextView.j*/
@import <Foundation/CPString.j>
@import "CPColor.j"
@import "CPFont.j"
@import "CPImage.j"
@import "CPView.j"
@import "CPControl.j"
@import "CPPlatform.j"

var _CPimageAndTextViewFrameSizeChangedFlag = __COMPILED_CODE__,
_CPImageAndTextViewImageChangedFlag = __COMPILED_CODE__,
_CPImageAndTextViewTextChangedFlag = __COMPILED_CODE__,

@implementation _CPImageAndTextView : CPView
{
CGSize _textShadowOffset;
CPTextAlignment _alignment;
CPFont _font;
}

- (CPString)themeClass
{
/* compiled code */
}

- (void)setThemeClass:(CPString)var1
{
/* compiled code */
}

@end

The problem I have is, the compiled code is a flat file, but inside it there is a bunch of code from many other files concatenated together. The decompiler separates them into the individual files as some of the markup shows the start and stop of the files.

I have written a plugin for the actual source code, and I can do a lot with the actual source, but most people won't have access to the source code for the main SDK. That's why I needed the decompiler. Like I said, I have written a decompiler, I just don't know how to make the decompiler decompile the file in a way that can be indexed and used by my plugin.

I saw some mention to Virtual Files, but it seemed like they don't stick around. I thought too about saving the decompiled code out, but I wasn't sure where I would save it that it wouldn't show up in the users file system, and if the SDK changed, the decompiled files would need to be reconstructed. I really don't know how to force Intellij to run the decompiler in the first place, and I don't know how to let the decompiled files get indexed. I have all the code set up to index, search, and resolve the source code, which the decompiled code will stand in for. 

My decompiler still needs work, as it takes a long time, so I would need to let it run in the background. I figured out how to run it in the background when the file is opened, as I wrote a FileEditor that when opened, starts the decompiler in the background, and when it is done, loads in the decompiled text. This text though is not indexed, and it has to be re-done every time the file is opened. It also takes a long time at the moment, so if you try to open the file, you have to wait 40 seconds until the editor window shows the text instead of just the placeholder start text.

I just can't find much information on adding in a decompiler for a plugin. I'm hoping for something like the Java plugin, where when you add a jar, it automatically adds all the intellisense information that references it. I would like to do that with these compiled files, so it's automatic and mapped to my decompiled code. I would want the users of the plugin to be able to navigate to the decompiled files, but not edit them.

0

Please sign in to leave a comment.