diff --git a/.vscodeignore b/.vscodeignore
index e1590c9..5053536 100644
--- a/.vscodeignore
+++ b/.vscodeignore
@@ -1,5 +1,6 @@
.vscode/**
.vscode-test/**
+releases/**
out/test/**
out/**/*.map
src/**
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c9ec648..e6be1a8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,7 +7,17 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
## [Unreleased]
-- Configurarable setting to change format of the disassembled code
+- HTML views for sections
+
+- Navigation between view via links
+
+- Erlang assembly formatter
+
+## 1.0.0
+
+- Dedicated view in the explorer panel for every .beam file in the workspace
+
+- Each section of a beam file has a subitem in the tree view
## 0.0.2 - 2018-04-03
diff --git a/README.md b/README.md
index 11bf8b7..daaff4b 100644
--- a/README.md
+++ b/README.md
@@ -11,12 +11,13 @@ BEAM files disassembler extension for Visual Studio Code.
## Description
Erlang\Elixir bytecode viewer.
-This extension allows to "peek" inside compiled bytecode for BEAM (Bogdan/Björn Erlang Abstract machine) binary files.
+This extension allows to "peek" inside compiled bytecode for BEAM (Bogdan/Björn Erlang Abstract machine).
## Features
- Dedicated View in the Explorer for all .beam files in the workspace
- Subitems for main sections:
+ - Bytecode instructions (**Code** section)
- Atoms ('**Atom**' and '**AtU8**' sections)
- Exported functions ('**ExpT**' section)
- Imported functions ('**ImpT**' section)
@@ -24,9 +25,9 @@ This extension allows to "peek" inside compiled bytecode for BEAM (Bogdan/Björn
- Strings '**StrT**' section)
- Attributes ('**Attr**' section)
- Literals ('**LitT**' section)
-- Opcodes, labels, registers highlighting
-- Hover description for opcodes taken from erlang source code (when available)
-- Gutter images for start of the functions
+- Opcodes, labels and registers color highlighting
+- Hover description for opcodes taken from erlang source code (when available)
+- Gutter images to indicate start of each function
To activate the extension select "Disassemble BEAM" from the context menu for a .beam file in the Explorer View:
@@ -48,7 +49,7 @@ None
## Release Notes
-Initial previe release
+First non-preview release
## Contributing
diff --git a/media/main.gif b/media/main.gif
index 0a8ee99..7f18024 100644
Binary files a/media/main.gif and b/media/main.gif differ
diff --git a/package.json b/package.json
index c258b42..ddfe173 100644
--- a/package.json
+++ b/package.json
@@ -3,7 +3,7 @@
"displayName": "BEAMdasm",
"description": "BEAM file disassembler",
"license": "SEE LICENSE IN LICENSE.md",
- "version": "0.0.2",
+ "version": "1.0.0",
"publisher": "Valentin",
"engines": {
"vscode": "^1.21.0"
@@ -26,7 +26,6 @@
"color": "#1E1E1E",
"theme": "dark"
},
- "preview": true,
"activationEvents": [
"onView:beamdasm.beamFilesTree",
"onCommand:beamdasm.disassemble"
diff --git a/resources/dark/attr.svg b/resources/dark/attr.svg
new file mode 100644
index 0000000..e5260e2
--- /dev/null
+++ b/resources/dark/attr.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/resources/dark/code.svg b/resources/dark/code.svg
index 44bc97a..865b4ab 100644
--- a/resources/dark/code.svg
+++ b/resources/dark/code.svg
@@ -1,29 +1 @@
-
-
-
+
\ No newline at end of file
diff --git a/resources/dark/litt.svg b/resources/dark/litt.svg
index b98ce38..6fdba1a 100644
--- a/resources/dark/litt.svg
+++ b/resources/dark/litt.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/resources/dark/strt.svg b/resources/dark/strt.svg
new file mode 100644
index 0000000..79b9c48
--- /dev/null
+++ b/resources/dark/strt.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/resources/light/attr.svg b/resources/light/attr.svg
new file mode 100644
index 0000000..9c130b4
--- /dev/null
+++ b/resources/light/attr.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/resources/light/code.svg b/resources/light/code.svg
index 55da68f..ffc45aa 100644
--- a/resources/light/code.svg
+++ b/resources/light/code.svg
@@ -1,29 +1 @@
-
-
-
+
\ No newline at end of file
diff --git a/resources/light/litt.svg b/resources/light/litt.svg
index 6eeb323..5bda23c 100644
--- a/resources/light/litt.svg
+++ b/resources/light/litt.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/resources/light/strt.svg b/resources/light/strt.svg
new file mode 100644
index 0000000..694938a
--- /dev/null
+++ b/resources/light/strt.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/beam/beamFile.ts b/src/beam/beamFile.ts
index 4de176b..316ba95 100644
--- a/src/beam/beamFile.ts
+++ b/src/beam/beamFile.ts
@@ -1,11 +1,11 @@
// Copyright 2018 Valentin Ivanov (valen.ivanov@gmail.com)
-
+//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
-
+//
// http://www.apache.org/licenses/LICENSE-2.0
-
+//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -18,13 +18,14 @@ import * as fs from 'fs';
import * as zlib from 'zlib';
import List from './terms/list';
import Tuple from './terms/tuple';
+import Map from './terms/map';
import { opcodes } from './opcodes';
-import * as tags from './tags';
+import * as Tags from './tags';
///
-export default class BeamFile implements beamdasm.IBeamFile {
+export default class BeamFile implements beamdasm.Beam {
readonly code: any[] = [];
_codeNumberOfFunctions: number = 0;
@@ -33,9 +34,12 @@ export default class BeamFile implements beamdasm.IBeamFile {
_codeInstructionSet: number = 0;
_codeExtraFields: number = 0;
- sections:any = {};
+ sections: any = {};
atoms: string[] = ["nil"];
+ numberOfAtoms: number = 0;
+ maxAtomNameLength: number = 0;
+
imports: any[] = [];
exports: any[] = [];
LocT: any[] = [];
@@ -61,6 +65,7 @@ export default class BeamFile implements beamdasm.IBeamFile {
}
readBeamFile(filePath: string) {
+
let buffer: Buffer = fs.readFileSync(filePath);
let for1 = buffer.toString('utf8', 0, 4).toLowerCase();
@@ -79,25 +84,24 @@ export default class BeamFile implements beamdasm.IBeamFile {
let offset = 12;
- this.sections ={};
+ this.sections = {};
//Quick scan to get chunks offsets and sizes
//We want to read them in particular order, not the order
//chunks are present in the file
- while ( offset < length )
- {
- let name = buffer.toString('utf8', offset, offset+4);
- let size = buffer.readUInt32BE(offset+4);
+ while (offset < length) {
+ let name = buffer.toString('utf8', offset, offset + 4);
+ let size = buffer.readUInt32BE(offset + 4);
- this.sections[name.toLowerCase()] = {start: offset + 8, length: size};
+ this.sections[name.toLowerCase()] = { start: offset + 8, length: size };
- offset = offset + 8 + (((size + 3)>>2)<<2);
+ offset = offset + 8 + (((size + 3) >> 2) << 2);
}
this.atoms = ['nil'];
-
+
this.readAtu8Section(buffer);
- this.readAtomSection(buffer);
+ this.readAtomSection(buffer);
this.readImptSection(buffer);
this.readExptSection(buffer);
this.readFuntSection(buffer);
@@ -107,13 +111,13 @@ export default class BeamFile implements beamdasm.IBeamFile {
this.readAttrSection(buffer);
this.readLittSection(buffer);
this.readLineSection(buffer);
-
- if( 'catt' in this.sections ){
+
+ if ('catt' in this.sections) {
console.log('Found CatT chunk');
}
- if( 'abst' in this.sections ){
+ if ('abst' in this.sections) {
console.log('Found Abst chunk');
}
@@ -244,7 +248,7 @@ export default class BeamFile implements beamdasm.IBeamFile {
let term = this.readTerm(buffer, offset);
offset = term.offset;
- if (term.tag === tags.TAG_ATOM) {
+ if (term.tag === Tags.TAG_ATOM) {
num_line_refs++;
fname_index = term.data;
}
@@ -290,7 +294,7 @@ export default class BeamFile implements beamdasm.IBeamFile {
}
readAttrSection(buffer: Buffer) {
- if( 'attr' in this.sections ){
+ if ('attr' in this.sections) {
let section = this.sections['attr'];
section.name = 'Attributes (Attr)';
let offset = section.start;
@@ -301,7 +305,7 @@ export default class BeamFile implements beamdasm.IBeamFile {
}
readCInfSection(buffer: Buffer) {
- if('cinf' in this.sections){
+ if ('cinf' in this.sections) {
let section = this.sections['cinf'];
section.name = 'Compilation Info (CInf)';
let offset = section.start;
@@ -328,8 +332,7 @@ export default class BeamFile implements beamdasm.IBeamFile {
readImptSection(buffer: Buffer) {
- if( 'impt' in this.sections )
- {
+ if ('impt' in this.sections) {
let section = this.sections['impt'];
section.name = 'Imports (ImpT)';
this.imports = [];
@@ -337,7 +340,7 @@ export default class BeamFile implements beamdasm.IBeamFile {
let offset = section.start;
let nImports = buffer.readUInt32BE(offset);
- if( nImports === 0){
+ if (nImports === 0) {
section.empty = true;
return;
}
@@ -364,12 +367,12 @@ export default class BeamFile implements beamdasm.IBeamFile {
section.name = 'Exports (ExpT)';
let offset = section.start;
let nExport = buffer.readUInt32BE(offset);
-
- if( nExport === 0){
+
+ if (nExport === 0) {
section.empty = true;
return;
}
-
+
offset += 4;
while (nExport-- > 0) {
@@ -429,38 +432,42 @@ export default class BeamFile implements beamdasm.IBeamFile {
}
readAtu8Section(buffer: Buffer) {
- if( 'atu8' in this.sections )
- {
+ if ('atu8' in this.sections) {
let section = this.sections['atu8'];
section.name = 'Atoms (AtU8)';
let offset = section.start;
let nAtoms = buffer.readUInt32BE(offset);
- if( nAtoms === 0){
+ this.numberOfAtoms = nAtoms;
+ if (nAtoms === 0) {
section.empty = 0;
return;
}
offset += 4;
+ let maxLength = 0;
+
while (nAtoms-- > 0) {
let atomLength = buffer.readUInt8(offset);
let atom = buffer.toString('utf8', offset + 1, offset + 1 + atomLength);
+ maxLength = ( atom.length > maxLength) ? atom.length : maxLength;
offset = offset + 1 + atomLength;
this.atoms.push(atom);
}
+
+ this.maxAtomNameLength = maxLength;
}
}
readAtomSection(buffer: Buffer) {
- if( 'atom' in this.sections )
- {
+ if ('atom' in this.sections) {
let section = this.sections['atom'];
section.name = 'Atoms (Atom)';
let offset = section.start;
let nAtoms = buffer.readUInt32BE(offset);
- if( nAtoms === 0){
+ if (nAtoms === 0) {
section.empty = 0;
return;
}
@@ -474,14 +481,14 @@ export default class BeamFile implements beamdasm.IBeamFile {
this.atoms.push(atom);
}
}
- }
+ }
//TODO: implement the rest of the terms
readObject(tag: number, buffer: Buffer, offset: number): any {
switch (tag) {
case 70: {
- return { data: "float8bytes", offset: offset + 8};
+ return { data: "float8bytes", offset: offset + 8 };
}
case 97:
return { data: buffer.readUInt8(offset), offset: offset + 1 };
@@ -544,23 +551,23 @@ export default class BeamFile implements beamdasm.IBeamFile {
readMap(buffer: Buffer, offset: number): any {
let arity = buffer.readUInt32BE(offset); offset += 4;
+
+ let map = new Map();
+
while (arity-- > 0) {
let keyTag = buffer.readUInt8(offset++);
let keyObj = this.readObject(keyTag, buffer, offset);
offset = keyObj.offset;
- //console.log(`${keyObj.data}`);
-
let valTag = buffer.readUInt8(offset++);
let valObj = this.readObject(valTag, buffer, offset);
offset = valObj.offset;
- //console.log(`${valObj.data}`);
- //Add object to the map
+ map.add(keyObj.data, valObj.data);
}
- return { data: "Map", offset: offset };
+ return { data: map, offset: offset };
}
readAtom(buffer: Buffer, offset: number): any {
@@ -584,7 +591,7 @@ export default class BeamFile implements beamdasm.IBeamFile {
return { data: buffer.toString('utf8', offset, offset + length), offset: offset + length };
}
- readonly hex: (d:number) => string = (d: number) => ("0" + d.toString(16)).slice(-2).toUpperCase();
+ readonly hex: (d: number) => string = (d: number) => ("0" + d.toString(16)).slice(-2).toUpperCase();
readSmallBigNum(buffer: Buffer, offset: number): any {
let length = buffer.readUInt8(offset++);
@@ -609,7 +616,7 @@ export default class BeamFile implements beamdasm.IBeamFile {
}
if (index > 11) {
- index = tags.TAG_UNKNOWN;
+ index = Tags.TAG_UNKNOWN;
}
return index;
@@ -619,7 +626,7 @@ export default class BeamFile implements beamdasm.IBeamFile {
let firstByte = buffer.readUInt8(offset++);
let tag = this.readTag(firstByte);
- if (tag === tags.TAG_EXT_LITERAL) {
+ if (tag === Tags.TAG_EXT_LITERAL) {
//Read index in literals table
let val = this.readTerm(buffer, offset);
offset = val.offset;
@@ -630,10 +637,10 @@ export default class BeamFile implements beamdasm.IBeamFile {
};
}
- if (tag === tags.TAG_EXT_LIST) {
+ if (tag === Tags.TAG_EXT_LIST) {
let size = this.readTerm(buffer, offset);
offset = size.offset;
- let list:any[] = [];
+ let list: any[] = [];
for (let i = 0; i < size.data; i++) {
let obj = this.readTerm(buffer, offset);
offset = obj.offset;
@@ -646,7 +653,7 @@ export default class BeamFile implements beamdasm.IBeamFile {
};
}
- if (tag === tags.TAG_EXT_FLOAT_REGISTER) {
+ if (tag === Tags.TAG_EXT_FLOAT_REGISTER) {
//Read register number
let val = this.readTerm(buffer, offset);
//Would not hurt to assert the bellow assumption
@@ -657,11 +664,11 @@ export default class BeamFile implements beamdasm.IBeamFile {
//Float registers contain TAG_LITERAL for the register number
//safe to pass it directly instead of nesting, but keep the tag
//for reporting purposes
- data: val.data,
+ data: val.data,
offset: offset
};
}
- if (tag === tags.TAG_EXT_ALLOC_LIST) {
+ if (tag === Tags.TAG_EXT_ALLOC_LIST) {
let size = this.readTerm(buffer, offset);
offset = size.offset;
let list = [];
@@ -681,7 +688,7 @@ export default class BeamFile implements beamdasm.IBeamFile {
};
}
- if (tag > tags.TAG_CHARACTER) {
+ if (tag > Tags.TAG_CHARACTER) {
console.log(`READING TAG: ${tag} is not implemented`);
}
@@ -713,10 +720,10 @@ export default class BeamFile implements beamdasm.IBeamFile {
offset: offset + 2
};
}
- if( size === 3 ) {
+ if (size === 3) {
return {
tag: tag,
- data: (buffer.readUInt8(offset) << 16) + (buffer.readUInt8(offset+1) << 8) + buffer.readUInt8(offset+2),
+ data: (buffer.readUInt8(offset) << 16) + (buffer.readUInt8(offset + 1) << 8) + buffer.readUInt8(offset + 2),
offset: offset + 3
};
}
@@ -756,7 +763,7 @@ export default class BeamFile implements beamdasm.IBeamFile {
if (byteCode > 163) {
console.log(`Illegal opcode ${byteCode}`);
}
-
+
// if( byteCode === 125 ){
// console.log('fdiv');
@@ -769,16 +776,16 @@ export default class BeamFile implements beamdasm.IBeamFile {
list.push(obj);
}
- if( byteCode === 1){
+ if (byteCode === 1) {
label = list;
continue;
}
- if( byteCode === 153 ){
+ if (byteCode === 153) {
line = list;
continue;
}
-
+
this.code.push({ op: byteCode, params: list, label: label, line: line });
line = null;
label = null;
diff --git a/src/beam/beamFileCache.ts b/src/beam/beamFileCache.ts
new file mode 100644
index 0000000..6fd69c7
--- /dev/null
+++ b/src/beam/beamFileCache.ts
@@ -0,0 +1,47 @@
+// Copyright 2018 Valentin Ivanov (valen.ivanov@gmail.com)
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+'use strict';
+
+///
+
+import BeamFile from './beamFile';
+
+class BeamCacheSingleton
+{
+ private static _instance: BeamCacheSingleton = new BeamCacheSingleton();
+
+ private constructor()
+ {}
+
+ public static get Instance()
+ {
+ return this._instance;
+ }
+
+ beamFilePath: string = '';
+ beamFile: beamdasm.Beam | undefined = undefined;
+
+ public getBeamFile(path: string): beamdasm.Beam {
+ if( this.beamFilePath === path ){
+ return this.beamFile as beamdasm.Beam;
+ }
+
+ this.beamFilePath = path;
+ this.beamFile = BeamFile.fromFile(this.beamFilePath);
+ return this.beamFile;
+ }
+}
+
+export let BeamCache = BeamCacheSingleton.Instance;
\ No newline at end of file
diff --git a/src/beam/beamdasmFormatter.ts b/src/beam/beamdasmFormatter.ts
new file mode 100644
index 0000000..643816e
--- /dev/null
+++ b/src/beam/beamdasmFormatter.ts
@@ -0,0 +1,242 @@
+// Copyright 2018 Valentin Ivanov (valen.ivanov@gmail.com)
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+'use strict';
+
+///
+
+import { opcodes } from './opcodes';
+import * as Tags from './tags';
+
+let lbl: (val: number) => string;
+
+function instructionToString(beamFile: beamdasm.Beam, obj: any, func: number): string {
+ let name = opcodes[obj.op].nm;
+ let str = ` ${name}` + ' '.repeat(20 - name.length);
+
+ for (let i = 0; i < opcodes[obj.op].ar; i++) {
+ if (i === func) {
+ let func_info = beamFile.imports[obj.params[i].data];
+ str += ` ${beamFile.atoms[func_info.module]}:${beamFile.atoms[func_info.function]}/${func_info.arity}`;
+ }
+ else {
+ str += ` ${termToString(beamFile, obj.params[i])}`;
+ }
+ }
+ return str;
+}
+
+function termToString(beamFile: beamdasm.Beam, obj: any): string {
+ if (obj.tag === Tags.TAG_LABEL) {
+ return lbl(obj.data);
+ }
+ if (obj.tag === Tags.TAG_X_REGISTER) {
+ return `X[${obj.data}]`;
+ }
+ if (obj.tag === Tags.TAG_Y_REGISTER) {
+ return `Y[${obj.data}]`;
+ }
+ if (obj.tag === Tags.TAG_ATOM) {
+ let value = beamFile.atoms[obj.data];
+ return value === undefined ? `.` : `${value}`;
+ }
+ if (obj.tag === Tags.TAG_EXT_FLOAT_REGISTER) {
+ return `FR[${obj.data}]`;
+ }
+ if (obj.tag === Tags.TAG_EXT_LITERAL) {
+ return `${beamFile.literals[obj.data]}`;
+ }
+ if (obj.tag === Tags.TAG_EXT_LIST) {
+ let str = '[';
+ for (let i = 0; i < obj.data.length; i++) {
+ str += (i !== 0) ? ', ' : '';
+ str += `${termToString(beamFile, obj.data[i])}`;
+ }
+ str += ']';
+
+ return str;
+ }
+
+ if (obj.tag === Tags.TAG_LITERAL || obj.tag === Tags.TAG_INTEGER) {
+ return `${obj.data}`;
+ }
+
+ return `.`;
+}
+
+export class BeamdasmFormatter implements beamdasm.BeamFormatter {
+
+ formatModuleInfo(beamFile: beamdasm.Beam): string {
+ let str = `Module: ${beamFile.atoms[1]}\n`;
+ str += '\n';
+ str += `Attributes: ${beamFile.attributes}\n`;
+ str += '\n';
+ str += `Compilation Info: ${beamFile.compilationInfo}\n`;
+ str += '\n';
+ return str;
+ }
+
+ formatcode(beamFile: beamdasm.Beam): string {
+
+ let str = '';
+
+ let lblLength = beamFile.codeNumberOfLabels.toString().length;
+ lblLength = lblLength < 2 ? 2 : lblLength;
+ if (!lbl) {
+ lbl = (val: number) => `label${("0".repeat(lblLength) + val.toString()).slice(-lblLength)}`;
+ }
+
+ str += this.formatModuleInfo(beamFile);
+
+ for (let i = 0; i < beamFile.code.length; i++) {
+ let obj = beamFile.code[i];
+
+ if (obj.op === 2) {
+ str += `\n//Function ${beamFile.atoms[obj.params[0].data]}:${beamFile.atoms[obj.params[1].data]}/${obj.params[2].data}\n`;
+ }
+
+ if (obj.label) {
+ str += `${lbl(obj.label[0].data)}:`;
+ } else {
+ //6 === 'label:'.length
+ str += ' '.repeat(lblLength + 6);
+ }
+
+ if (obj.op === 7 || obj.op === 8) {
+ str += instructionToString(beamFile, obj, 1);
+ }
+ else if (obj.op === 9) {
+ str += instructionToString(beamFile, obj, 0);
+ }
+ else if (obj.op === 78) {
+ str += instructionToString(beamFile, obj, 1);
+ }
+ else if (obj.op === 124 || obj.op === 125) {
+ str += instructionToString(beamFile, obj, 2);
+ }
+ else {
+ str += instructionToString(beamFile, obj, -1);
+ }
+
+ if (obj.line) {
+ //skip zero lines
+ if (obj.line[0].data !== 0) {
+ let line_ref = beamFile.lineRefs[obj.line[0].data];
+ str += ` //line ${beamFile.lineFNames[line_ref[0]]}, ${line_ref[1]}`;
+ }
+ }
+
+ str += '\n';
+ }
+ return str;
+ }
+
+ formatlitt(beamFile: beamdasm.Beam): string {
+ let str = 'Literals: \n';
+
+ for( let i = 0; i < beamFile.literals.length; i++ ){
+ str += `\t${beamFile.literals[i]}\n`;
+ }
+
+ str += '\n';
+ return str;
+ }
+
+ formatatu8(beamFile: beamdasm.Beam): string {
+ let str = 'Atoms: ';
+ let offset = str.length;
+
+ for (let i = 0; i < beamFile.atoms.length; i++) {
+ str += (i !== 0) ? ' '.repeat(offset) : '';
+ str += `${i}\t${beamFile.atoms[i]}\n`;
+ }
+ str += '\n';
+
+ return str;
+ }
+
+ formatimpt(beamFile: beamdasm.Beam): string {
+ let str = 'Imports: ';
+ let offset = str.length;
+
+ for (let i = 0; i < beamFile.imports.length; i++) {
+ let func_info = beamFile.imports[i];
+ str += (i !== 0) ? ' '.repeat(offset) : '';
+ str += `${func_info.module}/${func_info.function}/${func_info.arity} ${beamFile.atoms[func_info.module]}:${beamFile.atoms[func_info.function]}/${func_info.arity}\n`;
+ }
+
+ str += '\n';
+
+ return str;
+ }
+
+ formatexpt(beamFile: beamdasm.Beam): string {
+
+ if (!lbl) {
+ let lblLength = beamFile.codeNumberOfLabels.toString().length;
+ lblLength = lblLength < 2 ? 2 : lblLength;
+
+ lbl = (val: number) => `label${("0".repeat(lblLength) + val.toString()).slice(-lblLength)}`;
+ }
+
+ let str = 'Exports: ';
+ let offset = str.length;
+ for (let i = 0; i < beamFile.exports.length; i++) {
+ let func_info = beamFile.exports[i];
+ str += (i !== 0) ? ' '.repeat(offset) : '';
+ str += `${func_info.function}/${func_info.arity}/${func_info.label} ${beamFile.atoms[func_info.function]}/${func_info.arity} ${lbl(func_info.label)}\n`;
+ }
+ str += '\n';
+ return str;
+ }
+
+ formatloct(beamFile: beamdasm.Beam): string {
+
+ if (!lbl) {
+ let lblLength = beamFile.codeNumberOfLabels.toString().length;
+ lblLength = lblLength < 2 ? 2 : lblLength;
+
+ lbl = (val: number) => `label${("0".repeat(lblLength) + val.toString()).slice(-lblLength)}`;
+ }
+
+ let str = 'Private: ';
+ let offset = str.length;
+
+ for (let i = 0; i < beamFile.LocT.length; i++) {
+ let func_info = beamFile.LocT[i];
+ str += (i !== 0) ? ' '.repeat(offset) : '';
+ str += `${func_info.function}/${func_info.arity}/${func_info.label} ${beamFile.atoms[func_info.function]}/${func_info.arity} ${lbl(func_info.label)}\n`;
+ }
+ str += '\n';
+ return str;
+ }
+
+ formatstrt(beamFile: beamdasm.Beam): string {
+ let str = 'Strings: ';
+
+ str += `\"${beamFile.StrT}\"\n`;
+
+ str += '\n';
+ return str;
+ }
+
+ formatattr(beamFile: beamdasm.Beam): string {
+ let str = 'Attributes:\n';
+
+ str += `${beamFile.attributes}`;
+ return str;
+ }
+
+ [func: string]: (beamFile: beamdasm.Beam) => string;
+}
\ No newline at end of file
diff --git a/src/beam/erlangFormatter.ts b/src/beam/erlangFormatter.ts
new file mode 100644
index 0000000..d9874b2
--- /dev/null
+++ b/src/beam/erlangFormatter.ts
@@ -0,0 +1,64 @@
+// Copyright 2018 Valentin Ivanov (valen.ivanov@gmail.com)
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+'use strict';
+
+///
+
+export class ErlangFormatter implements beamdasm.BeamFormatter {
+
+ formatModuleInfo(beamFile: beamdasm.Beam): string {
+ let str = `Module: ${beamFile.atoms[1]}\n`;
+ str += '\n';
+ str += `Attributes: ${beamFile.attributes}\n`;
+ str += '\n';
+ str += `Compilation Info: ${beamFile.compilationInfo}\n`;
+ str += '\n';
+ return str;
+ }
+
+ formatcode(beamFile: beamdasm.Beam): string {
+ return '';
+ }
+
+ formatlitt(beamFile: beamdasm.Beam): string {
+ return '';
+ }
+
+ formatatu8(beamFile: beamdasm.Beam): string {
+ return '';
+ }
+
+ formatimpt(beamFile: beamdasm.Beam): string {
+ return '';
+ }
+
+ formatexpt(beamFile: beamdasm.Beam): string {
+ return '';
+ }
+
+ formatloct(beamFile: beamdasm.Beam): string {
+ return '';
+ }
+
+ formatstrt(beamFile: beamdasm.Beam): string {
+ return '';
+ }
+
+ formatattr(beamFile: beamdasm.Beam): string {
+ return '';
+ }
+
+ [func: string]: (beamFile: beamdasm.Beam) => string;
+}
\ No newline at end of file
diff --git a/src/beam/interface.ts b/src/beam/interface.ts
index ab9937d..0ebfdb1 100644
--- a/src/beam/interface.ts
+++ b/src/beam/interface.ts
@@ -1,11 +1,11 @@
// Copyright 2018 Valentin Ivanov (valen.ivanov@gmail.com)
-
+//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
-
+//
// http://www.apache.org/licenses/LICENSE-2.0
-
+//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -15,11 +15,14 @@
'use strict';
namespace beamdasm {
- export interface IBeamFile{
+ export interface Beam {
- sections:any;
+ sections: any;
atoms: string[];
+ numberOfAtoms: number;
+ maxAtomNameLength: number;
+
code: any[];
lineRefs: any[];
lineFNames: any[];
@@ -29,25 +32,25 @@ namespace beamdasm {
imports: any[];
exports: any[];
- literals: any[];
-
+ literals: any[];
+
attributes: any;
compilationInfo: any;
-
+
codeNumberOfLabels: number;
}
- export interface BeamBytecodeFormatter {
-
- formatModuleInfo(beamFile: IBeamFile): string;
- formatcode(beamFile: IBeamFile): string;
- formatlitt(beamFile: IBeamFile): string;
- formatatu8(beamFile: IBeamFile) :string;
- formatimpt(beamFile: IBeamFile): string;
- formatexpt(beamFile: IBeamFile): string;
- formatloct(beamFile: IBeamFile): string;
- formatstrt(beamFile: IBeamFile): string;
-
- [func: string]: (beamFile: IBeamFile) => string;
+ export interface BeamFormatter {
+
+ formatcode(beamFile: Beam): string;
+ formatlitt(beamFile: Beam): string;
+ formatatu8(beamFile: Beam): string;
+ formatimpt(beamFile: Beam): string;
+ formatexpt(beamFile: Beam): string;
+ formatloct(beamFile: Beam): string;
+ formatstrt(beamFile: Beam): string;
+ formatattr(beamFile: Beam): string;
+
+ [func: string]: (beamFile: Beam) => string;
}
}
diff --git a/src/beam/opcodes.ts b/src/beam/opcodes.ts
index 774e586..4219e2a 100644
--- a/src/beam/opcodes.ts
+++ b/src/beam/opcodes.ts
@@ -1,11 +1,11 @@
// Copyright 2018 Valentin Ivanov (valen.ivanov@gmail.com)
-
+//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
-
+//
// http://www.apache.org/licenses/LICENSE-2.0
-
+//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -15,445 +15,445 @@
'use strict';
export const opcodes: any = {
- 1: {
- ar: 1,
+ 1: {
+ ar: 1,
nm: 'label',
- doc: '**label** *Lbl*\nSpecify a module local label. Label gives this code address a name *Lbl* and marks the start of a basic block.'
+ doc: '**label** *Lbl*\nSpecify a module local label. Label gives this code address a name *Lbl* and marks the start of a basic block.'
},
- 2: {
- ar: 3,
+ 2: {
+ ar: 3,
nm: 'func_info',
- doc: '**func_info** *M* *F* *A*\nDefine a function M:F/A'
+ doc: '**func_info** *Module* *Function* *Arity*\nDefine a function *Module*:*Function*/*Arity*'
},
- 3: {
+ 3: {
ar: 0,
nm: 'int_code_end',
doc: 'int_code_end\n',
},
- 4: {
+ 4: {
ar: 2,
nm: 'call',
doc: '**call** *Arity* *Label*\nCall the function at *Label*. Save the next instruction as the return address in the CP register.'
},
- 5: {
+ 5: {
ar: 3,
nm: 'call_last',
doc: '**call_last** *Arity* *Label* *Deallocate*\nDeallocate and do a tail recursive call to the function at *Label*. Do not update the CP register. Before the call deallocate *Deallocate* words of stack.'
},
- 6: {
+ 6: {
ar: 2,
nm: 'call_only',
doc: '**call_only** *Arity* *Label*\nDo a tail recursive call to the function at *Label*. Do not update the CP register.'
},
- 7: {
+ 7: {
ar: 2,
nm: 'call_ext',
doc: '**call_ext** *Arity* *Destination*\nCall the function of arity *Arity* pointed by the *Destination*. Save the next instruction as the return address in the CP register.'
},
- 8: {
+ 8: {
ar: 3,
nm: 'call_ext_last',
doc: '**call_ext_last** *Arity* *Destination* *Deallocate*\nDeallocate and do a tail recursive call to function of arity *Arity* pointed by *Destination*. Dealllocate *Deallocte* words from the stack before the call.'
},
- 9: {
+ 9: {
ar: 2,
nm: 'bif0',
doc: '**bif0** *Bif* *Reg*\nCall the built-in function *Bif* and store the result in *Reg*.'
},
- 10: {
+ 10: {
ar: 4,
nm: 'bif1',
doc: '**bif1** *Lbl* *Bif* *Arg* *Reg*\nCall the buil-in function *Bif* with the argument *Arg*, and store the result in *Reg*. On failure jump to *Lbl*.'
},
- 11: {
+ 11: {
ar: 5,
nm: 'bif2',
doc: '**bif2** *Lbl* *Bif* *Arg1* *Arg2* *Reg*\nCall the built-in function *Bif* with the arguments *Arg1* and *Arg2*, and store the result in *Reg*. On failure jump to *Lbl*.'
},
- 12: {
+ 12: {
ar: 2,
nm: 'allocate',
doc: '**allocate** *StackNeed* *Live*\nAllocate space for *StackNeed* words on the stack. If GC is needed during allocation there are *Live* number of live X registers. Also save the continuation pointer (CP) on the stack.'
},
- 13: {
+ 13: {
ar: 3,
nm: 'allocate_heap',
doc: '**allocate_heap** *StackNeed* *HeapNeed* *Live*\nAllocate space for *StackNeed* words on the stack and ensure there is space for *HeepNeed* words on the heap. If GC is needed save *Live* number of X registers. Also save the continuation pointer (CP) on the stack.'
},
- 14: {
+ 14: {
ar: 2,
nm: 'allocate_zero',
doc: '**allocate_zero** *StackNeed* *Live*\nAllocate space for *StackNeed* words on the stack. If GC is needed during allocation there are *Live* number of live X registers. Clear the new stack words (by writing NIL). Also save the continuation pointer (CP) on the stack.'
},
- 15: {
+ 15: {
ar: 3,
nm: 'allocate_heap_zero',
doc: '**allocate_heap_zero** *StackNeed* *HeapNeed* *Live*\nAllocate space for *StackNeed* words on the stack and *HeapNeed* wordson the heap. If GC is needed during allocation there are *Live* number of live X registers. Clear the new stack words (by writing NIL). Also save the continuation pointer (CP) on the stack.'
},
- 16: {
+ 16: {
ar: 2,
nm: 'test_heap',
doc: '**test_heap** *HeepNeed* *Live*\nEnsure there is space for *HeepNeed* words on the heap. If GC is needed save *Live* number of X registers.'
},
- 17: {
+ 17: {
ar: 1,
nm: 'init',
doc: '**init** *N*\nClear the *Nth* stack word (by writing NIL).'
},
- 18: {
+ 18: {
ar: 1,
nm: 'deallocate',
doc: '**deallocate** *N*\nRestore the continuation pointer (CP) from the stack and deallocate *N+1*words from the stack (the +1 is for the CP).'
},
- 19: {
+ 19: {
ar: 0,
nm: 'return',
doc: '**return**\nReturn to the address in the continuation pointer (CP).'
},
- 20: {
+ 20: {
ar: 0,
nm: 'send',
doc: '**send**\n Send argument in x(1) as a message to the destination process in x(0). The message in x(1) ends up as the result of the send in x(0).'
},
- 21: {
+ 21: {
ar: 0,
nm: 'remove_message',
doc: '**remove_message**\nUnlink the current message from the message queue and store a pointer to the message in x(0). Remove any timeout.'
},
- 22: {
+ 22: {
ar: 0,
nm: 'timeout',
doc: '**timeout**\nReset the save point of the mailbox and clear the timeout flag.'
},
- 23: {
+ 23: {
ar: 2,
nm: 'loop_rec',
doc: '**loop_rec** *Label* *Source*\nLoop over the message queue, if it is empty jump to *Label*.'
},
- 24: {
+ 24: {
ar: 1,
nm: 'loop_rec_end',
doc: '**loop_rec_end** *Label*\nAdvance the save pointer to the next message and jump back to *Label*'
},
- 25: {
+ 25: {
ar: 1,
nm: 'wait',
doc: '**wait** *Label*\nSuspend the process and set the entry point to the beginning of the receive loop at *Label*.'
},
- 26: {
+ 26: {
ar: 2,
nm: 'wait_timeout',
doc: '**wait_timeout** *Label* *Time*\nSets up a timeout of *Time* milliseconds and saves the address of the following instruction as the entry point if the timeout triggers.'
},
- 27: { ar: 4, nm: 'm_plus' },
- 28: { ar: 4, nm: 'm_minus' },
- 29: { ar: 4, nm: 'm_times' },
- 30: { ar: 4, nm: 'm_div' },
- 31: { ar: 4, nm: 'int_div' },
- 32: { ar: 4, nm: 'int_rem' },
- 33: { ar: 4, nm: 'int_band' },
- 34: { ar: 4, nm: 'int_bor' },
- 35: { ar: 4, nm: 'int_bxor' },
- 36: { ar: 4, nm: 'int_bsl' },
- 37: { ar: 4, nm: 'int_bsr' },
- 38: { ar: 3, nm: 'int_bnot' },
- 39: {
+ 27: { ar: 4, nm: 'm_plus' },
+ 28: { ar: 4, nm: 'm_minus' },
+ 29: { ar: 4, nm: 'm_times' },
+ 30: { ar: 4, nm: 'm_div' },
+ 31: { ar: 4, nm: 'int_div' },
+ 32: { ar: 4, nm: 'int_rem' },
+ 33: { ar: 4, nm: 'int_band' },
+ 34: { ar: 4, nm: 'int_bor' },
+ 35: { ar: 4, nm: 'int_bxor' },
+ 36: { ar: 4, nm: 'int_bsl' },
+ 37: { ar: 4, nm: 'int_bsr' },
+ 38: { ar: 3, nm: 'int_bnot' },
+ 39: {
ar: 3,
nm: 'is_lt',
doc: '**is_lt** *Label* *Arg1* *Arg2*\nCompare two terms and jump to *Label* if *Arg1* is not less than *Arg2*.'
},
- 40: {
+ 40: {
ar: 3,
nm: 'is_ge',
doc: '**is_ge** *Label* *Arg1* *Arg2*\nCompare two terms and jump to *Label* if *Arg1* is less than *Arg2*.'
},
- 41: {
+ 41: {
ar: 3,
nm: 'is_eq',
doc: '**is_eq** *Label* *Arg1* *Arg2*\nCompare two terms and jump to *Label* is *Arg1* is not (numerically) equal to *Arg2*.'
},
- 42: {
+ 42: {
ar: 3,
nm: 'is_ne',
doc: '**is_ne** *Label* *Arg1* *Arg2*\nCompare two terms and jump to *Label* if *Arg1* is (numerically) equal to *Arg2*.'
},
- 43: {
+ 43: {
ar: 3,
nm: 'is_eq_exact',
doc: '**is_eq_exact** *Label* *Arg1* *Arg2*\nCompare two terms and jump to *Label* if *Arg1* is not exactly equal to *Arg2*.'
},
- 44: {
+ 44: {
ar: 3,
nm: 'is_ne_exact',
doc: '**is _ne_exact** *Label* *Arg1* *Arg2*\nCompare two terms and jump to *Label* if *Arg1* is exactly equal to *Arg2*.'
},
- 45: {
+ 45: {
ar: 2,
nm: 'is_integer',
doc: '**is_integer** *Label* *Arg*\nTest the type of *Arg* and jump to *Label* if it is not an integer.'
},
- 46: {
+ 46: {
ar: 2,
nm: 'is_float',
doc: '**is_float** *Label* *Arg*\nTest the type of *Arg* and jump to *Label* if it is not a float.'
},
- 47: {
+ 47: {
ar: 2,
nm: 'is_number',
doc: '**is_number** *Label* *Arg*\nTest the type of *Arg* and jump to *Label* if it is not a number.'
},
- 48: {
+ 48: {
ar: 2,
nm: 'is_atom',
doc: '**is_atom** *Label* *Arg*\nTest the type of *Arg* and jump to *Label* if it is not an atom.'
},
- 49: {
+ 49: {
ar: 2,
nm: 'is_pid',
doc: '**is_pid** *Label* *Arg*\nTest the type of *Arg* and jump to *Label* if it is not a pid.'
},
- 50: {
+ 50: {
ar: 2,
nm: 'is_reference',
doc: '**is_reference** *Label* *Arg*\nTest the type of *Arg* and jump to *Label* if it is not a reference.'
},
- 51: {
+ 51: {
ar: 2,
nm: 'is_port',
doc: '**is_port** *Label* *Arg*\nTest the type of *Arg* and jump to *Label* if it is not a port.'
},
- 52: {
+ 52: {
ar: 2,
nm: 'is_nil',
doc: '**is_nil** *Label* *Arg*\nTest the type of *Arg* and jump to *Label* if it is not nil.'
},
- 53: {
+ 53: {
ar: 2,
nm: 'is_binary',
doc: '**is_binary** *Label* *Arg*\nTest the type of *Arg* and jump to *Label* if it is not a binary.'
},
- 54: {
+ 54: {
ar: 2,
nm: 'is_constant',
doc: '**is_constant** *Label* *Arg*\nTest the type of *Arg* and jump to *Label* if it is not a constant.'
},
- 55: {
+ 55: {
ar: 2,
nm: 'is_list',
doc: '**is_list** *Label* *Arg*\nTest the type of *Arg* and jump to *Label* if it is not a cons or nil.'
},
- 56: {
+ 56: {
ar: 2,
nm: 'is_nonempty_list',
doc: '**is_nonempty_list** *Label* *Arg*\nTest the type of *Arg* and jump to *Label* if it is not a cons.'
},
- 57: {
+ 57: {
ar: 2,
nm: 'is_tuple',
- doc: '**is_tuple** *Label* *Arg*\nTest the type of *Arg* and jump to *Label* if it is not a tuple.'
+ doc: '**is_tuple** *Label* *Arg*\nTest the type of *Arg* and jump to *Label* if it is not a tuple.'
},
- 58: {
+ 58: {
ar: 3,
nm: 'test_arity',
doc: '**test_arity** *Label* *Arg* *Arity*\nTest the arity of the tuple in *Arg* and jump to *Label* if it is not equal to *Arity*.'
},
- 59: {
+ 59: {
ar: 3,
nm: 'select_val',
doc: '**select_val** *Arg* *FailLabel* *Destinations*\nJump to the destination label corresponding to *Arg* in the *Destinations* list, if no arity matches, jump to *FailLabel*.'
},
- 60: {
+ 60: {
ar: 3,
nm: 'select_tuple_arity',
doc: '**select_tuple_arity** *Tuple* *FailLabel* *Destiantions*\nCheck the arity of the tuple *Tuple* and jump to the corresponding destination label, if no arity matches, jump to *FailLabel*.'
},
- 61: {
+ 61: {
ar: 1,
nm: 'jump',
doc: '**jump** *Label*\nJump to *Label*.'
},
- 62: { ar: 2, nm: 'catch' },
- 63: { ar: 1, nm: 'catch_end' },
- 64: {
- ar: 2,
+ 62: { ar: 2, nm: 'catch' },
+ 63: { ar: 1, nm: 'catch_end' },
+ 64: {
+ ar: 2,
nm: 'move',
- doc: '**move** *Source* *Destination*\nMove the source *Source* (a literal or a register) to the destination register *Destination*.'
+ doc: '**move** *Source* *Destination*\nMove the source *Source* (a literal or a register) to the destination register *Destination*.'
},
- 65: {
+ 65: {
ar: 3,
nm: 'get_list',
doc: '**get_list** *Source* *Head* *Tail*\nGet the head and tail (or car and cdr) parts of a list (a cons cell) from *Source* and put them in the registers *Head* and *Tail*.'
},
- 66: {
+ 66: {
ar: 3,
nm: 'get_tuple_element',
doc: '**get_tuple_element** *Source* *Element* *Destination*\nGet element number *Element* from the tuple in *Source* and put it in the destination register *Destination*.'
},
- 67: {
+ 67: {
ar: 3,
nm: 'set_tuple_element',
doc: '**set_tuple_element** *NewElement* *Tuple* *Position*\nUpdate the element at position *Position* of the tuple *Tuple* with the new element *NewElement*.'
},
- 68: { ar: 3, nm: 'put_string' },
- 69: { ar: 3, nm: 'put_list' },
- 70: { ar: 2, nm: 'put_tuple' },
- 71: { ar: 1, nm: 'put' },
- 72: { ar: 1, nm: 'badmatch' },
- 73: { ar: 0, nm: 'if_end' },
- 74: { ar: 1, nm: 'case_end' },
- 75: {
+ 68: { ar: 3, nm: 'put_string' },
+ 69: { ar: 3, nm: 'put_list' },
+ 70: { ar: 2, nm: 'put_tuple' },
+ 71: { ar: 1, nm: 'put' },
+ 72: { ar: 1, nm: 'badmatch' },
+ 73: { ar: 0, nm: 'if_end' },
+ 74: { ar: 1, nm: 'case_end' },
+ 75: {
ar: 1,
nm: 'call_fun',
doc: '**call_fun** *Arity*\nCall a fun of arity *Arity*. Assume arguments in registers x(0) to x(Arity-1) and that the fun is in x(Arity). Save the next instruction as the return address in the CP register.'
},
- 76: { ar: 3, nm: 'make_fun' },
- 77: {
+ 76: { ar: 3, nm: 'make_fun' },
+ 77: {
ar: 2,
nm: 'is_function',
doc: '**is_function** *Label* *Arg*\nTest the type of *Arg* and jump to *Label* if it is not a function (i.e. fun or closure).'
},
- 78: {
+ 78: {
ar: 2,
nm: 'call_ext_only',
doc: '**call_ext_only** *Arity* *Desitnation*\nDo a tail recursive call to the function of arity *Arity* pointed by the *Destination*. Do not update the CP register.',
},
- 79: { ar: 2, nm: 'bs_start_match' },
- 80: { ar: 5, nm: 'bs_get_integer' },
- 81: { ar: 5, nm: 'bs_get_float' },
- 82: { ar: 5, nm: 'bs_get_binary' },
- 83: { ar: 4, nm: 'bs_skip_bits' },
- 84: { ar: 2, nm: 'bs_test_tail' },
- 85: { ar: 1, nm: 'bs_save' },
- 86: { ar: 1, nm: 'bs_restore' },
- 87: { ar: 2, nm: 'bs_init' },
- 88: { ar: 2, nm: 'bs_final' },
- 89: { ar: 5, nm: 'bs_put_integer' },
- 90: { ar: 5, nm: 'bs_put_binary' },
- 91: { ar: 5, nm: 'bs_put_float' },
- 92: { ar: 2, nm: 'bs_put_string' },
- 93: { ar: 1, nm: 'bs_need_buf' },
- 94: { ar: 0, nm: 'fclearerror' },
- 95: { ar: 1, nm: 'fcheckerror' },
- 96: { ar: 2, nm: 'fmove' },
- 97: { ar: 2, nm: 'fconv' },
- 98: { ar: 4, nm: 'fadd' },
- 99: { ar: 4, nm: 'fsub' },
- 100: { ar: 4, nm: 'fmul' },
- 101: { ar: 4, nm: 'fdiv' },
- 102: { ar: 3, nm: 'fnegate' },
- 103: { ar: 1, nm: 'make_fun2' },
- 104: { ar: 2, nm: 'try' },
- 105: { ar: 1, nm: 'try_end' },
- 106: { ar: 1, nm: 'try_case' },
- 107: { ar: 1, nm: 'try_case_end' },
- 108: { ar: 2, nm: 'raise' },
- 109: { ar: 6, nm: 'bs_init2' },
- 110: { ar: 3, nm: 'bs_bits_to_bytes' },
- 111: { ar: 5, nm: 'bs_add' },
- 112: { ar: 1, nm: 'apply' },
- 113: { ar: 2, nm: 'apply_last' },
- 114: {
+ 79: { ar: 2, nm: 'bs_start_match' },
+ 80: { ar: 5, nm: 'bs_get_integer' },
+ 81: { ar: 5, nm: 'bs_get_float' },
+ 82: { ar: 5, nm: 'bs_get_binary' },
+ 83: { ar: 4, nm: 'bs_skip_bits' },
+ 84: { ar: 2, nm: 'bs_test_tail' },
+ 85: { ar: 1, nm: 'bs_save' },
+ 86: { ar: 1, nm: 'bs_restore' },
+ 87: { ar: 2, nm: 'bs_init' },
+ 88: { ar: 2, nm: 'bs_final' },
+ 89: { ar: 5, nm: 'bs_put_integer' },
+ 90: { ar: 5, nm: 'bs_put_binary' },
+ 91: { ar: 5, nm: 'bs_put_float' },
+ 92: { ar: 2, nm: 'bs_put_string' },
+ 93: { ar: 1, nm: 'bs_need_buf' },
+ 94: { ar: 0, nm: 'fclearerror' },
+ 95: { ar: 1, nm: 'fcheckerror' },
+ 96: { ar: 2, nm: 'fmove' },
+ 97: { ar: 2, nm: 'fconv' },
+ 98: { ar: 4, nm: 'fadd' },
+ 99: { ar: 4, nm: 'fsub' },
+ 100: { ar: 4, nm: 'fmul' },
+ 101: { ar: 4, nm: 'fdiv' },
+ 102: { ar: 3, nm: 'fnegate' },
+ 103: { ar: 1, nm: 'make_fun2' },
+ 104: { ar: 2, nm: 'try' },
+ 105: { ar: 1, nm: 'try_end' },
+ 106: { ar: 1, nm: 'try_case' },
+ 107: { ar: 1, nm: 'try_case_end' },
+ 108: { ar: 2, nm: 'raise' },
+ 109: { ar: 6, nm: 'bs_init2' },
+ 110: { ar: 3, nm: 'bs_bits_to_bytes' },
+ 111: { ar: 5, nm: 'bs_add' },
+ 112: { ar: 1, nm: 'apply' },
+ 113: { ar: 2, nm: 'apply_last' },
+ 114: {
ar: 2,
nm: 'is_boolean',
doc: '**is_boolean** *Label* *Arg*\nTest the type of *Arg* and jump to *Label* if it is not a Boolean.'
},
- 115: {
+ 115: {
ar: 3,
nm: 'is_function2',
doc: '**is_function2** *Label* *Arg* *Arity*\nTest the type of *Arg* and jump to *Label* if it is not a function of arity *Arity*.'
},
- 116: { ar: 5, nm: 'bs_start_match2' },
- 117: { ar: 7, nm: 'bs_get_integer2' },
- 118: { ar: 7, nm: 'bs_get_float2' },
- 119: { ar: 7, nm: 'bs_get_binary2' },
- 120: { ar: 5, nm: 'bs_skip_bits2' },
- 121: { ar: 3, nm: 'bs_test_tail2' },
- 122: { ar: 2, nm: 'bs_save2' },
- 123: { ar: 2, nm: 'bs_restore2' },
- 124: {
+ 116: { ar: 5, nm: 'bs_start_match2' },
+ 117: { ar: 7, nm: 'bs_get_integer2' },
+ 118: { ar: 7, nm: 'bs_get_float2' },
+ 119: { ar: 7, nm: 'bs_get_binary2' },
+ 120: { ar: 5, nm: 'bs_skip_bits2' },
+ 121: { ar: 3, nm: 'bs_test_tail2' },
+ 122: { ar: 2, nm: 'bs_save2' },
+ 123: { ar: 2, nm: 'bs_restore2' },
+ 124: {
ar: 5,
nm: 'gc_bif1',
doc: '**gc_bif1** *Label* *Live* *Bif* *Arg* *Reg*\nCall the built-in function *Bif* with the argument *Arg*, and store the result in *Reg*. On failure jump to *Label*. Do a garbage collection if necessary to allocate space on the heap for the result (saving *Live* number of X registers).'
},
- 125: {
+ 125: {
ar: 6,
nm: 'gc_bif2',
doc: '**gc_bif2** *Label* *Live*, *Bif* *Arg1* *Arg2* *Reg*\nCall the built-in function *Bif* with the arguments *Arg1* and *Arg2*, and store the result in *Reg*. On failure jump to *Label*. Do a garbage collection if necessary to allocate space to the heap for the result (saving *Live* number of X registers).'
},
- 126: { ar: 2, nm: 'bs_final2' },
- 127: { ar: 2, nm: 'bs_bits_to_bytes2' },
- 128: { ar: 2, nm: 'put_literal' },
- 129: {
+ 126: { ar: 2, nm: 'bs_final2' },
+ 127: { ar: 2, nm: 'bs_bits_to_bytes2' },
+ 128: { ar: 2, nm: 'put_literal' },
+ 129: {
ar: 2,
nm: 'is_bitstr',
doc: '**is_bitstr** *Label* *Arg*\nTest the type of *Arg* and jump to *Label* if it is not a bit string.'
},
- 130: { ar: 1, nm: 'bs_context_to_binary' },
- 131: { ar: 3, nm: 'bs_test_unit' },
- 132: { ar: 4, nm: 'bs_match_string' },
- 133: { ar: 0, nm: 'bs_init_writable' },
- 134: { ar: 8, nm: 'bs_append' },
- 135: { ar: 6, nm: 'bs_private_append' },
- 136: {
+ 130: { ar: 1, nm: 'bs_context_to_binary' },
+ 131: { ar: 3, nm: 'bs_test_unit' },
+ 132: { ar: 4, nm: 'bs_match_string' },
+ 133: { ar: 0, nm: 'bs_init_writable' },
+ 134: { ar: 8, nm: 'bs_append' },
+ 135: { ar: 6, nm: 'bs_private_append' },
+ 136: {
ar: 2,
nm: 'trim',
doc: '**trim** *N* *Remaining*\nReduce the stack usage by *N* words, keeping the CP on the top of the stack.'
},
- 137: { ar: 6, nm: 'bs_init_bits' },
- 138: { ar: 5, nm: 'bs_get_utf8' },
- 139: { ar: 4, nm: 'bs_skip_utf8' },
- 140: { ar: 5, nm: 'bs_get_utf16' },
- 141: { ar: 4, nm: 'bs_skip_utf16' },
- 142: { ar: 5, nm: 'bs_get_utf32' },
- 143: { ar: 4, nm: 'bs_skip_utf32' },
- 144: { ar: 3, nm: 'bs_utf8_size' },
- 145: { ar: 3, nm: 'bs_put_utf8' },
- 146: { ar: 3, nm: 'bs_utf16_size' },
- 147: { ar: 3, nm: 'bs_put_utf16' },
- 148: { ar: 3, nm: 'bs_put_utf32' },
- 149: { ar: 0, nm: 'on_load' },
- 150: {
+ 137: { ar: 6, nm: 'bs_init_bits' },
+ 138: { ar: 5, nm: 'bs_get_utf8' },
+ 139: { ar: 4, nm: 'bs_skip_utf8' },
+ 140: { ar: 5, nm: 'bs_get_utf16' },
+ 141: { ar: 4, nm: 'bs_skip_utf16' },
+ 142: { ar: 5, nm: 'bs_get_utf32' },
+ 143: { ar: 4, nm: 'bs_skip_utf32' },
+ 144: { ar: 3, nm: 'bs_utf8_size' },
+ 145: { ar: 3, nm: 'bs_put_utf8' },
+ 146: { ar: 3, nm: 'bs_utf16_size' },
+ 147: { ar: 3, nm: 'bs_put_utf16' },
+ 148: { ar: 3, nm: 'bs_put_utf32' },
+ 149: { ar: 0, nm: 'on_load' },
+ 150: {
ar: 1,
nm: 'recv_mark',
doc: '**recv_mark** *Label*\nSave the end of the message queue and the address of the label *Label* so that a recv_set instruction can start scanning the inbox from this position.'
},
- 151: {
+ 151: {
ar: 1,
nm: 'recv_set',
doc: '**recv_set** *Label*\nCheck that the saved mark points to *Label* and set the save pointer in the message queue to the last position of the message queue saved by the recv_mark instruction.'
},
- 152: {
+ 152: {
ar: 7,
nm: 'gc_bif3',
doc: '**gc_bif3** *Label* *Live* *Bif* *Arg1* *Arg2* *Arg3* *Reg*\nCall the built-in function *Bif* with the arguments *Arg1*, *Arg2* and *Arg3*, and store the result in *Reg*. On failure jump to *Label*. Do a garbage collection if necessary to allocate space on the heap for the result (saving *Live* number of X registers).'
},
- 153: { ar: 1, nm: 'line' },
- 154: { ar: 5, nm: 'put_map_assoc' },
- 155: { ar: 5, nm: 'put_map_exact' },
- 156: { ar: 2, nm: 'is_map' },
- 157: { ar: 3, nm: 'has_map_fields' },
- 158: { ar: 3, nm: 'get_map_elements' },
- 159: {
+ 153: { ar: 1, nm: 'line' },
+ 154: { ar: 5, nm: 'put_map_assoc' },
+ 155: { ar: 5, nm: 'put_map_exact' },
+ 156: { ar: 2, nm: 'is_map' },
+ 157: { ar: 3, nm: 'has_map_fields' },
+ 158: { ar: 3, nm: 'get_map_elements' },
+ 159: {
ar: 4,
nm: 'is_tagged_tuple',
doc: '**is_tagged_tuple** *Label* *Reg* *N* *Atom*\nTest the type of *Reg* and jumps to *Label* if it is not a tuple. Test the arity of *Reg* and jumps to *Label* if it is not *N*. Test the first element of the tuple and jumps to *Label* if it is not *Atom*.'
},
- 160: {
+ 160: {
ar: 0,
nm: 'build_stacktrace',
doc: '**built_stacktrace**\nGiven the raw stacktrace in x(0), build a cooked stacktrace suitable for human consumption. Store it in x(0). Destroys all other registers. Do a garbage collection if necessary to allocate space on the heap for the result.'
},
- 161: {
+ 161: {
ar: 0,
nm: 'raw_raise',
doc: '**raw_raise**\nThis instruction works like the erlang:raise/3 BIF, except that the stacktrace in x(2) must be a raw stacktrace. x(0) is the class of the exception (error, exit, or throw), x(1) is the exception term, and x(2) is the raw stackframe. If x(0) is not a valid class, the instruction will not throw an exception, but store the atom \'badarg\' in x(0) and execute the next instruction.'
},
- 162: {
+ 162: {
ar: 2,
nm: 'get_hd',
doc: '**get_hd** *Source* *Head*\nGet the head (or car) part of a list (a cons cell) from *Source* and put it into the register *Head*.'
},
- 163: {
+ 163: {
ar: 2,
nm: 'get_tl',
doc: '**get_tl** *Source* *Tail*\nGet the tail (or cdr) part of a list (a cons cell) from *Source* and put it into the register *Tail*.'
@@ -461,11 +461,9 @@ export const opcodes: any = {
};
export function get_doc(name: string): string {
- for(let opcode = 1; opcode<164; opcode++)
- {
- if( opcodes[opcode].nm === name )
- {
- return opcodes[opcode].doc;
+ for (let opcode = 1; opcode < 164; opcode++) {
+ if (opcodes[opcode].nm === name) {
+ return opcodes[opcode].doc;
}
}
return '';
diff --git a/src/beam/tags.ts b/src/beam/tags.ts
index fd6c4c1..c9608b5 100644
--- a/src/beam/tags.ts
+++ b/src/beam/tags.ts
@@ -1,11 +1,11 @@
// Copyright 2018 Valentin Ivanov (valen.ivanov@gmail.com)
-
+//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
-
+//
// http://www.apache.org/licenses/LICENSE-2.0
-
+//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -14,6 +14,7 @@
'use strict';
+
export const TAG_LITERAL = 0;
export const TAG_INTEGER = 1;
export const TAG_ATOM = 2;
@@ -26,4 +27,4 @@ export const TAG_EXT_LIST = 8;
export const TAG_EXT_FLOAT_REGISTER = 9;
export const TAG_EXT_ALLOC_LIST = 10;
export const TAG_EXT_LITERAL = 11;
-export const TAG_UNKNOWN = 12;
\ No newline at end of file
+export const TAG_UNKNOWN = 12;
diff --git a/src/beam/terms/list.ts b/src/beam/terms/list.ts
index 0dbdec3..46c0f56 100644
--- a/src/beam/terms/list.ts
+++ b/src/beam/terms/list.ts
@@ -1,11 +1,11 @@
// Copyright 2018 Valentin Ivanov (valen.ivanov@gmail.com)
-
+//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
-
+//
// http://www.apache.org/licenses/LICENSE-2.0
-
+//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -16,25 +16,25 @@
export default class List {
- _elements: any[] = [];
+ items: any[] = [];
add(obj: any) {
- this._elements.push(obj);
+ this.items.push(obj);
}
toString(): string {
let str = '[';
- for (let i = 0; i < this._elements.length; i++) {
- str += i!==0? ', ' : '';
- str += `${this._elements[i]}`;
+ for (let i = 0; i < this.items.length; i++) {
+ str += i !== 0 ? ', ' : '';
+ str += `${this.items[i]}`;
}
str += ']';
return str;
}
- get(index :number) : any | undefined {
- if( index >=0 && index < this._elements.length ){
- return this._elements[index];
+ get(index: number): any | undefined {
+ if (index >= 0 && index < this.items.length) {
+ return this.items[index];
}
return;
}
diff --git a/src/beam/terms/map.ts b/src/beam/terms/map.ts
new file mode 100644
index 0000000..ef75d33
--- /dev/null
+++ b/src/beam/terms/map.ts
@@ -0,0 +1,42 @@
+// Copyright 2018 Valentin Ivanov (valen.ivanov@gmail.com)
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+'use strict';
+
+import Tuple from './tuple';
+
+export default class Map {
+ items: Tuple[] =[];
+
+ add( key: any, value: any) {
+ let item = new Tuple();
+ item.add(key);
+ item.add(value);
+ this.items.push(item);
+ }
+
+ toString():string {
+ let str = '%{';
+
+ for( let i = 0; i< this.items.length; i++)
+ {
+ str += i!==0 ? ', ' : '';
+ str += `${this.items[i].items[0]} => ${this.items[0].items[1]}`;
+ }
+
+ str += '}';
+
+ return str;
+ }
+}
\ No newline at end of file
diff --git a/src/beam/terms/tuple.ts b/src/beam/terms/tuple.ts
index 66f66c0..11de227 100644
--- a/src/beam/terms/tuple.ts
+++ b/src/beam/terms/tuple.ts
@@ -1,11 +1,11 @@
// Copyright 2018 Valentin Ivanov (valen.ivanov@gmail.com)
-
+//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
-
+//
// http://www.apache.org/licenses/LICENSE-2.0
-
+//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -15,19 +15,18 @@
'use strict';
export default class Tuple {
- _elements : any[] = [];
+ items: any[] = [];
- add( obj: any) {
- this._elements.push(obj);
+ add(obj: any) {
+ this.items.push(obj);
}
- toString() : string {
+ toString(): string {
let str = '{';
- for( let i = 0; i {
- if( token.isCancellationRequested ){
+ if (token.isCancellationRequested) {
return;
}
diff --git a/src/beamTextDocumentContentProvider.ts b/src/beamTextDocumentContentProvider.ts
new file mode 100644
index 0000000..a2e94e4
--- /dev/null
+++ b/src/beamTextDocumentContentProvider.ts
@@ -0,0 +1,85 @@
+// Copyright 2018 Valentin Ivanov (valen.ivanov@gmail.com)
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+'use strict';
+
+import * as fs from 'fs';
+import * as vscode from 'vscode';
+
+import { BeamdasmFormatter } from './beam/beamdasmFormatter';
+import { ErlangFormatter } from './beam/erlangFormatter';
+import { BeamCache } from './beam/beamFileCache';
+
+///
+///
+///
+
+
+
+export default class BeamTextDocumentContentProvider implements vscode.TextDocumentContentProvider { //, vscode.DocumentLinkProvider {
+
+ provideDocumentLinks(document: vscode.TextDocument, token: vscode.CancellationToken): vscode.ProviderResult {
+ throw new Error("Method not implemented.");
+ }
+
+
+ formatters: { [s: string]: beamdasm.BeamFormatter; } = {};
+
+ constructor() {
+ this.formatters['erlang'] = new ErlangFormatter();
+ this.formatters['beamdasm'] = new BeamdasmFormatter();
+ }
+
+ public provideTextDocumentContent(uri: vscode.Uri, token: vscode.CancellationToken): vscode.ProviderResult {
+
+ if (token.isCancellationRequested) {
+ return;
+ }
+
+ if (!uri || !(uri instanceof vscode.Uri)) {
+ return;
+ }
+
+ let beamFilePath: string = uri.fsPath.substr(0, uri.fsPath.length - 5);
+
+ if (!fs.existsSync(beamFilePath)) {
+ return;
+ }
+
+ let beamFile = BeamCache.getBeamFile(beamFilePath);
+
+ let str = '';
+
+ function formatSection(formatter: beamdasm.BeamFormatter, section: string): string {
+ let str = '';
+
+ if (section in beamFile.sections) {
+ let funcName = `format${section}`;
+ str = 'BEAM section is missing formatting function';
+ if (funcName in formatter) {
+ str = formatter[funcName](beamFile);
+ }
+ }
+ return str;
+ }
+
+ //let configuration = vscode.workspace.getConfiguration('beamdasm');
+ let formatterToUse = 'beamdasm'; //configuration['formatter'];
+
+ let section = uri.scheme.substr(4, 4);
+ str = formatSection(this.formatters[formatterToUse], section);
+
+ return str;
+ }
+}
\ No newline at end of file
diff --git a/src/beamFilesProvider.ts b/src/beamTreeDataProvider.ts
similarity index 67%
rename from src/beamFilesProvider.ts
rename to src/beamTreeDataProvider.ts
index df0c2b5..d36f607 100644
--- a/src/beamFilesProvider.ts
+++ b/src/beamTreeDataProvider.ts
@@ -1,11 +1,11 @@
// Copyright 2018 Valentin Ivanov (valen.ivanov@gmail.com)
-
+//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
-
+//
// http://www.apache.org/licenses/LICENSE-2.0
-
+//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -17,18 +17,13 @@
import * as fs from 'fs';
import * as path from 'path';
import * as vscode from 'vscode';
-import BeamFile from './beam/beamFile';
+import { BeamCache } from './beam/beamFileCache';
-export default class BeamFilesProvider implements vscode.TreeDataProvider {
+export default class BeamTreeDataProvider implements vscode.TreeDataProvider {
private _didChangeTreeData: vscode.EventEmitter = new vscode.EventEmitter();
readonly onDidChangeTreeData?: vscode.Event = this._didChangeTreeData.event;
- dev: BeamVirtualFolder = new BeamVirtualFolder('dev');
- prod: BeamVirtualFolder = new BeamVirtualFolder('prod');
- deps: BeamVirtualFolder = new BeamVirtualFolder('deps');
-
-
constructor(context: vscode.ExtensionContext, private workspaceRoot: string | undefined, private supportedSections: string[]) {
let watcher = vscode.workspace.createFileSystemWatcher('**/*.{beam}');
@@ -38,7 +33,7 @@ export default class BeamFilesProvider implements vscode.TreeDataProvider triggerUpdateTree(), undefined, context.subscriptions);
watcher.onDidDelete(_ => triggerUpdateTree(), undefined, context.subscriptions);
- //For some reason onDidDelete is not firing when file is deleted by not a user
+ //For some reason onDidDelete is not firing when file is deleted outside of VS Code
if (workspaceRoot && fs.existsSync(workspaceRoot)) {
//Watcher is not 100% consistent on all platforms
let fsWatcher = fs.watch(workspaceRoot, { recursive: true }, (event: string, filename: string | Buffer) => {
@@ -85,42 +80,42 @@ export default class BeamFilesProvider implements vscode.TreeDataProvider {
- let final: vscode.TreeItem[] = [];
+ let toBeResolved: vscode.TreeItem[] = [];
if (!element) {
- this.dev.items = [];
- this.prod.items = [];
- this.deps.items = [];
+ let dev: BeamVirtualFolder = new BeamVirtualFolder('dev');
+ let prod: BeamVirtualFolder = new BeamVirtualFolder('prod');
+ let deps: BeamVirtualFolder = new BeamVirtualFolder('deps');
+
+ let otherBeamFiles: any[] = [];
- let beamFiles: any[] = [];
const isDirectory = (source: string) => fs.lstatSync(source).isDirectory();
- const getBeamFiles = (source: string, acc: any[]) => {
+
+ const getBeamFiles = (source: string) => {
let items = fs.readdirSync(source);
items.forEach((value: string) => {
try {
let fullPath = path.join(source, value);
if (isDirectory(fullPath)) {
- getBeamFiles(fullPath, acc);
+ getBeamFiles(fullPath);
} else {
if (path.extname(fullPath) === ".beam") {
if (fullPath.search('_build\\\\prod') !== -1) {
- this.prod.items.push(new BeamFileItem(value, fullPath));
+ prod.items.push(new BeamFileItem(value, fullPath));
}
else if (fullPath.search('_build\\\\dev') !== -1) {
- this.dev.items.push(new BeamFileItem(value, fullPath));
+ dev.items.push(new BeamFileItem(value, fullPath));
}
else if (fullPath.search('deps') !== -1) {
- this.deps.items.push(new BeamFileItem(value, fullPath));
+ deps.items.push(new BeamFileItem(value, fullPath));
}
else {
- acc.push({ name: value, location: fullPath });
+ otherBeamFiles.push({ name: value, location: fullPath });
}
}
}
@@ -130,12 +125,12 @@ export default class BeamFilesProvider implements vscode.TreeDataProvider(
+ let top: vscode.TreeItem[] = [dev, prod, deps];
+ toBeResolved = top.concat(
+ otherBeamFiles.map(
(item: any, index: number, array: any[]) => {
return new BeamFileItem(item.name,
item.location
@@ -143,41 +138,24 @@ export default class BeamFilesProvider implements vscode.TreeDataProvider
-
-let lbl: (val: number) => string;
-
-function instructionToString(bm: beamdasm.IBeamFile, obj: any, func: number): string {
- let name = opcodes[obj.op].nm;
- let str = ` ${name}` + ' '.repeat(20 - name.length);
-
- for (let i = 0; i < opcodes[obj.op].ar; i++) {
- if (i === func) {
- let func_info = bm.imports[obj.params[i].data];
- str += ` ${bm.atoms[func_info.module]}:${bm.atoms[func_info.function]}/${func_info.arity}`;
- }
- else {
- str += ` ${termToString(bm, obj.params[i])}`;
- }
- }
- return str;
-}
-
-function termToString(bm: beamdasm.IBeamFile, obj: any): string {
- if (obj.tag === tags.TAG_LABEL) {
- return lbl(obj.data);
- }
- if (obj.tag === tags.TAG_X_REGISTER) {
- return `X[${obj.data}]`;
- }
- if (obj.tag === tags.TAG_Y_REGISTER) {
- return `Y[${obj.data}]`;
- }
- if (obj.tag === tags.TAG_ATOM) {
- let value = bm.atoms[obj.data];
- return value === undefined ? `.` : `${value}`;
- }
- if (obj.tag === tags.TAG_EXT_FLOAT_REGISTER) {
- return `FR[${obj.data}]`;
- }
- if (obj.tag === tags.TAG_EXT_LITERAL) {
- return `${bm.literals[obj.data]}`;
- }
- if (obj.tag === tags.TAG_EXT_LIST) {
- let str = '[';
- for (let i = 0; i < obj.data.length; i++) {
- str += (i !== 0) ? ', ' : '';
- str += `${termToString(bm, obj.data[i])}`;
- }
- str += ']';
-
- return str;
- }
-
- if (obj.tag === tags.TAG_LITERAL || obj.tag === tags.TAG_INTEGER) {
- return `${obj.data}`;
- }
-
- return `.`;
-}
-
-export class BeamdasmFormatter implements beamdasm.BeamBytecodeFormatter {
-
- formatModuleInfo(bm: beamdasm.IBeamFile): string {
- let str = `Module: ${bm.atoms[1]}\n`;
- str += '\n';
- str += `Attributes: ${bm.attributes}\n`;
- str += '\n';
- str += `Compilation Info: ${bm.compilationInfo}\n`;
- str += '\n';
- return str;
- }
-
- formatcode(bm: beamdasm.IBeamFile): string {
-
- let str = '';
-
- let lblLength = bm.codeNumberOfLabels.toString().length;
- lblLength = lblLength < 2 ? 2 : lblLength;
- if (!lbl) {
- lbl = (val: number) => `label${("0".repeat(lblLength) + val.toString()).slice(-lblLength)}`;
- }
-
- str += this.formatModuleInfo(bm);
-
- for (let i = 0; i < bm.code.length; i++) {
- let obj = bm.code[i];
-
- if (obj.op === 2) {
- str += `\n//Function ${bm.atoms[obj.params[0].data]}:${bm.atoms[obj.params[1].data]}/${obj.params[2].data}\n`;
- }
-
- if (obj.label) {
- str += `${lbl(obj.label[0].data)}:`;
- } else {
- //6 === 'label:'.length
- str += ' '.repeat(lblLength + 6);
- }
-
- if (obj.op === 7 || obj.op === 8) {
- str += instructionToString(bm, obj, 1);
- }
- else if (obj.op === 9) {
- str += instructionToString(bm, obj, 0);
- }
- else if (obj.op === 78) {
- str += instructionToString(bm, obj, 1);
- }
- else if (obj.op === 124 || obj.op === 125) {
- str += instructionToString(bm, obj, 2);
- }
- else {
- str += instructionToString(bm, obj, -1);
- }
-
- if (obj.line) {
- //skip zero lines
- if (obj.line[0].data !== 0) {
- let line_ref = bm.lineRefs[obj.line[0].data];
- str += ` //line ${bm.lineFNames[line_ref[0]]}, ${line_ref[1]}`;
- }
- }
-
- str += '\n';
- }
- return str;
- }
-
- formatlitt(bm: beamdasm.IBeamFile): string {
- let str = 'Literals: ';
- str += `${bm.literals}\n`;
-
- str += '\n';
- return str;
- }
-
- formatatu8(bm: beamdasm.IBeamFile): string {
- let str = 'Atoms: ';
- let offset = str.length;
-
- for (let i = 0; i < bm.atoms.length; i++) {
- str += (i !== 0) ? ' '.repeat(offset) : '';
- str += `${i}\t${bm.atoms[i]}\n`;
- }
- str += '\n';
-
- return str;
- }
-
- formatimpt(bm: beamdasm.IBeamFile): string {
- let str = 'Imports: ';
- let offset = str.length;
-
- for (let i = 0; i < bm.imports.length; i++) {
- let func_info = bm.imports[i];
- str += (i !== 0) ? ' '.repeat(offset) : '';
- str += `${func_info.module}/${func_info.function}/${func_info.arity} ${bm.atoms[func_info.module]}:${bm.atoms[func_info.function]}/${func_info.arity}\n`;
- }
-
- str += '\n';
-
- return str;
- }
-
- formatexpt(bm: beamdasm.IBeamFile): string {
-
- if (!lbl) {
- let lblLength = bm.codeNumberOfLabels.toString().length;
- lblLength = lblLength < 2 ? 2 : lblLength;
-
- lbl = (val: number) => `label${("0".repeat(lblLength) + val.toString()).slice(-lblLength)}`;
- }
-
- let str = 'Exports: ';
- let offset = str.length;
- for (let i = 0; i < bm.exports.length; i++) {
- let func_info = bm.exports[i];
- str += (i !== 0) ? ' '.repeat(offset) : '';
- str += `${func_info.function}/${func_info.arity}/${func_info.label} ${bm.atoms[func_info.function]}/${func_info.arity} ${lbl(func_info.label)}\n`;
- }
- str += '\n';
- return str;
- }
-
- formatloct(bm: beamdasm.IBeamFile): string {
-
- if (!lbl) {
- let lblLength = bm.codeNumberOfLabels.toString().length;
- lblLength = lblLength < 2 ? 2 : lblLength;
-
- lbl = (val: number) => `label${("0".repeat(lblLength) + val.toString()).slice(-lblLength)}`;
- }
-
- let str = 'Private: ';
- let offset = str.length;
-
- for (let i = 0; i < bm.LocT.length; i++) {
- let func_info = bm.LocT[i];
- str += (i !== 0) ? ' '.repeat(offset) : '';
- str += `${func_info.function}/${func_info.arity}/${func_info.label} ${bm.atoms[func_info.function]}/${func_info.arity} ${lbl(func_info.label)}\n`;
- }
- str += '\n';
- return str;
- }
-
- formatstrt(bm: beamdasm.IBeamFile): string {
- let str = 'Strings: ';
-
- str += `\"${bm.StrT}\"\n`;
-
- str += '\n';
- return str;
- }
-
- [func: string]: (beamFile: beamdasm.IBeamFile) => string;
-}
\ No newline at end of file
diff --git a/src/contentProvider.ts b/src/contentProvider.ts
deleted file mode 100644
index d02e54f..0000000
--- a/src/contentProvider.ts
+++ /dev/null
@@ -1,72 +0,0 @@
-// Copyright 2018 Valentin Ivanov (valen.ivanov@gmail.com)
-
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-
-// http://www.apache.org/licenses/LICENSE-2.0
-
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-'use strict';
-
-import * as fs from 'fs';
-import * as vscode from 'vscode';
-import BeamFile from './beam/beamFile';
-import { BeamdasmFormatter } from './beamdasmFormatter';
-
-///
-///
-
-
-
-export default class BeamDasmContentProvider implements vscode.TextDocumentContentProvider {
-
- formatter: beamdasm.BeamBytecodeFormatter;
- constructor(){
- this.formatter = new BeamdasmFormatter();
- }
-
- public provideTextDocumentContent(uri: vscode.Uri, token: vscode.CancellationToken): vscode.ProviderResult {
-
- if(token.isCancellationRequested){
- return;
- }
-
- if( !uri || !(uri instanceof vscode.Uri)){
- return;
- }
-
- let beamFilePath : string = uri.fsPath.substr(0,uri.fsPath.length-5);
-
- if( !fs.existsSync(beamFilePath) ){
- return;
- }
-
- let beamFile = BeamFile.fromFile(beamFilePath);
-
- let str = '';
-
- function formatSection(formatter: beamdasm.BeamBytecodeFormatter, section: string): string {
- let str = '';
-
- if( section in beamFile.sections ){
- let funcName = `format${section}`;
- str = 'BEAM section is missing formatting function';
- if( funcName in formatter ){
- str = formatter[funcName](beamFile);
- }
- }
- return str;
- }
-
- let section = uri.scheme.substr(4,4);
- str = formatSection(this.formatter, section);
-
- return str;
- }
-}
\ No newline at end of file
diff --git a/src/extension.ts b/src/extension.ts
index 9128929..1098a0b 100644
--- a/src/extension.ts
+++ b/src/extension.ts
@@ -1,11 +1,11 @@
// Copyright 2018 Valentin Ivanov (valen.ivanov@gmail.com)
-
+//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
-
+//
// http://www.apache.org/licenses/LICENSE-2.0
-
+//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -16,9 +16,9 @@
import * as vscode from 'vscode';
-import BeamDasmContentProvider from './contentProvider';
-import BeamDasmHoverProvider from './hoverProvider';
-import BeamFilesProvider from './beamFilesProvider';
+import BeamTextDocumentContentProvider from './beamTextDocumentContentProvider';
+import BeamHoverProvider from './beamHoverProvider';
+import BeamTreeDataProvider from './beamTreeDataProvider';
import * as fs from 'fs';
import * as path from 'path';
@@ -27,11 +27,7 @@ export function activate(context: vscode.ExtensionContext) {
const rootPath = vscode.workspace.rootPath;
- context.subscriptions.push(
- vscode.languages.registerHoverProvider("beam", new BeamDasmHoverProvider())
- );
-
- let contentProvider = new BeamDasmContentProvider();
+ context.subscriptions.push(vscode.languages.registerHoverProvider("beam", new BeamHoverProvider()));
let supportedSections = [
"code",
@@ -43,38 +39,34 @@ export function activate(context: vscode.ExtensionContext) {
"loct",
"attr",
"strt"
- ]
-
- supportedSections.forEach( (section:string) => {
- context.subscriptions.push(vscode.workspace.registerTextDocumentContentProvider(`beam${section}`, contentProvider));
- });
-
- let beamFilesProvider = new BeamFilesProvider(context, rootPath, supportedSections);
- let command = vscode.commands.registerCommand('beamdasm.refreshBeamTree', () => beamFilesProvider.refresh());
- context.subscriptions.push(command);
+ ];
- context.subscriptions.push(vscode.window.registerTreeDataProvider("beamdasm.beamFilesTree", beamFilesProvider));
+ let contentProvider = new BeamTextDocumentContentProvider();
+ supportedSections.forEach((section: string) => {
+ context.subscriptions.push(vscode.workspace.registerTextDocumentContentProvider(`beam${section}`, contentProvider));
+ });
+ let beamTreeDataProvider = new BeamTreeDataProvider(context, rootPath, supportedSections);
+ context.subscriptions.push(vscode.window.registerTreeDataProvider("beamdasm.beamFilesTree", beamTreeDataProvider));
- context.subscriptions.push(
- vscode.commands.registerCommand('beamdasm.disassemble', (fileUri, beamFile?: any) => {
- if (!fileUri || !(fileUri instanceof vscode.Uri)) {
- let editor = vscode.window.activeTextEditor;
+ context.subscriptions.push(vscode.commands.registerCommand('beamdasm.refreshBeamTree', () => beamTreeDataProvider.refresh()));
- if (!editor) {
- return;
- }
+ context.subscriptions.push(vscode.commands.registerCommand('beamdasm.disassemble', (fileUri, beamFile?: any) => {
+ if (!fileUri || !(fileUri instanceof vscode.Uri)) {
+ let editor = vscode.window.activeTextEditor;
- fileUri = editor.document.uri;
+ if (!editor) {
+ return;
}
- if (fs.existsSync(fileUri.fsPath)) {
- let sectionDocument = vscode.Uri.file(fileUri.fsPath.replace('.beam','.beam_code'));
- vscode.commands.executeCommand('vscode.open', sectionDocument.with({ scheme: 'beamcode' }));
- }
+ fileUri = editor.document.uri;
}
- )
- );
+
+ if (fs.existsSync(fileUri.fsPath)) {
+ let sectionDocument = vscode.Uri.file(fileUri.fsPath.replace('.beam', '.beam_code'));
+ vscode.commands.executeCommand('vscode.open', sectionDocument.with({ scheme: 'beamcode' }));
+ }
+ }));
setupDecorators(context);
}
@@ -101,14 +93,14 @@ function setupDecorators(context: vscode.ExtensionContext) {
}
let activeEditor = vscode.window.activeTextEditor;
-
+
if (activeEditor) {
triggerUpdateDecorations();
}
- vscode.window.onDidChangeActiveTextEditor( editor => {
+ vscode.window.onDidChangeActiveTextEditor(editor => {
activeEditor = editor;
- if( editor ){
+ if (editor) {
triggerUpdateDecorations();
}
});
@@ -118,7 +110,7 @@ function setupDecorators(context: vscode.ExtensionContext) {
return;
}
- if( activeEditor.document.uri.scheme !== "beamcode" ){
+ if (activeEditor.document.uri.scheme !== "beamcode") {
return;
}