Refactor handling of Assembly Documentation Providers (#3247)

This commit is contained in:
Mats Larsen
2022-01-31 14:05:21 +01:00
committed by GitHub
parent fe7eb18408
commit 9b890b1656
34 changed files with 446 additions and 725 deletions

View File

@@ -7,5 +7,5 @@ out
views
# Autogenerated files
lib/handlers/asm-docs-*.js
lib/asm-docs/generated/asm-docs-*.js
etc/scripts/vendor/jvms.html

6
.gitattributes vendored
View File

@@ -1,7 +1,7 @@
docs/* linguist-documentation
*.s linguist-generated
*.asm linguist-generated
lib/handlers/asm-docs-amd64.js linguist-generated
lib/handlers/asm-docs-arm32.js linguist-generated
lib/handlers/asm-docs-java.js linguist-generated
lib/asm-docs/generated/asm-docs-amd64.js linguist-generated
lib/asm-docs/generated/asm-docs-arm32.js linguist-generated
lib/asm-docs/generated/asm-docs-java.js linguist-generated
test/*-cases/* linguist-generated

View File

@@ -26,6 +26,7 @@
<option name="USE_DOUBLE_QUOTES" value="false" />
<option name="FORCE_QUOTE_STYlE" value="true" />
<option name="ENFORCE_TRAILING_COMMA" value="WhenMultiline" />
<option name="SPACES_WITHIN_IMPORTS" value="true" />
<option name="SPACES_WITHIN_OBJECT_TYPE_BRACES" value="false" />
</TypeScriptCodeStyleSettings>
<VueCodeStyleSettings>

View File

@@ -3,7 +3,7 @@ include:
- lib/**/*.js
- lib/**/*.ts
exclude:
- lib/handlers/asm-docs-*.js
- lib/asm-docs/generated/asm-docs-*.js
- lib/compilers/fake-for-test.js
report-dir: ./out/coverage
reporter:

View File

@@ -40,7 +40,7 @@ out
views
# Autogenerated files
lib/handlers/asm-docs-*.js
lib/asm-docs/generated/asm-docs-*.js
#########################

View File

@@ -12,26 +12,26 @@ To add a new assembly documentation handler, you need to perform the following s
## 1. Find a data source
First of all you need to find a data source to get our instruction info from. While it is possible to write down
information about every single instruction for an instruction set, it's far from maintainable and it is a lot of work.
First you need to find a data source to get our instruction info from. While it is possible to write down
information about every single instruction for an instruction set, it's far from maintainable, and it is a lot of work.
Existing assembly documentation handlers use some sort of established documentation. The arm32 handler uses the
developer.arm.com website and the JVM bytecode handler uses Oracle's documentation.
## 2. Create a tool for collecting the data
Since we want to go through the automated route, you should write a script or a piece of code to automatically gather the
data for us and store it in a nice format that CE expects. The output of the script should be a generated .js file
Since we want to go through the automated route, you should write a script or a piece of code to automatically gather
the data for us and store it in a nice format that CE expects. The output of the script should be a generated .js file
with a single exported function containing a gigantic switch for the instruction opcode. Examples of this generated file
can be found in `/lib/handlers/asm-docs-amd64.js`.
can be found in `/lib/asm-docs/generated/asm-docs-amd64.js`.
How you generate this file is completely up to you, just make sure it's easy for others to run the script if needed as
well. If you need inspiration on how to write this tool, you can look at the `docenizer-*` scripts found in
`/etc/scripts` in the source control tree.
CE expects the tool to output the file into the `/lib/handlers/` folder with a name following the existing convention.
Each case in the switch should return a piece of formatted HTML to insert into the popup, a tooltip text for the
on-hover tooltip and a URL to external documentation.
CE expects the tool to output the file into the `/lib/asm-docs/generated/` folder with a name following the existing
convention. Each case in the switch should return a piece of formatted HTML to insert into the popup, a tooltip text
for the on-hover tooltip and a URL to external documentation.
```js
case "CALL":
@@ -45,37 +45,32 @@ case "CALL":
## 3. Connect your tool output to CE
Once your tool has generated the JavaScript file, you want to connect it to CE. This is done by editing the files found
in `/lib/handlers/assembly-documentation`. You'll want to add a new file named after your instruction set which contains
a class extending `BaseAssemblyDocumentationHandler`. The class should implement the `getInstructionInformation` method.
in the `/lib/asm-docs` directory which is adjacent to your newly generated JavaScript file.
First you want to add a new file named after your instruction set which exports a class extending the
`BaseAssemblyDocumentationProvider` class. The class should implement the `getInstructionInformation` method which
in most cases, delegates to your generated JavaScript. Instruction sets like Arm32 have use code to tweak the output
if needed.
This method is expected to take the instruction opcode in full uppercase and either return the associated data or null
if not found.
```js
import { getAsmOpcode } from '../asm-docs-java';
import { BaseAssemblyDocumentationHandler } from '../base-assembly-documentation-handler';
```ts
import { getAsmOpcode } from './generated//asm-docs-java';
import { BaseAssemblyDocumentationProvider } from './base';
export class JavaDocumentationHandler extends BaseAssemblyDocumentationHandler {
getInstructionInformation(instruction) {
export class JavaDocumentationProvider extends BaseAssemblyDocumentationProvider {
// Return the instruction set name
public static get key() { return 'java'; }
public override getInstructionInformation(instruction: string): AssemblyInstructionInfo | null {
return getAsmOpcode(instruction) || null;
}
}
```
The last thing to do is to associate your instruction set in the CE API with this handler. This is done by modifying the
`/lib/handlers/assembly-documentation/router.js` file. Simply add your instruction set name and associate it with a
new instance of your class in the mapping object.
```js
const ASSEMBLY_DOCUMENTATION_HANDLERS = {
amd64: new Amd64DocumentationHandler(),
arm32: new Arm32DocumentationHandler(),
java: new JavaDocumentationHandler(),
};
```
Finally we want to tell CE that the new documentation provider exists. This is done by re-exporting the class inside
`/lib/asm-docs/_all.ts`. Please keep the exports here in alphabetic order.
## 4. Testing
You can ensure your API handler is working as expected by writing a test case for it in the
`/test/handlers/assembly-documentation` directory. Simply copy over one of the existing files and modify it to work
with the new slug you created in the step above.
Testing new assembly documentation providers is really easy. It is just a matter of modifying the `TEST_MATRIX` variable
found in the `/test/handlers/asm-docs-tests.js` file.

29
lib/asm-docs/_all.ts Normal file
View File

@@ -0,0 +1,29 @@
// Copyright (c) 2021, Compiler Explorer Authors
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
export { Amd64DocumentationProvider } from './amd64';
export { Arm32DocumentationProvider } from './arm32';
export { AvrDocumentationProvider } from './avr';
export { JavaDocumentationProvider } from './java';
export { Mos6502DocumentationProvider } from './mos6502';

View File

@@ -22,17 +22,17 @@
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
import { getAsmOpcode } from '../asm-docs-amd64';
import { BaseAssemblyDocumentationHandler } from '../base-assembly-documentation-handler';
import { AssemblyInstructionInfo, BaseAssemblyDocumentationProvider } from './base';
import { getAsmOpcode } from './generated/asm-docs-amd64';
const ATT_SUFFIX_REMOVER = /^([a-z]+)[blqw]$/i;
export class Amd64DocumentationHandler extends BaseAssemblyDocumentationHandler {
getInstructionInformation(instruction) {
// Try both default and with AT&T suffix removed
export class Amd64DocumentationProvider extends BaseAssemblyDocumentationProvider {
private static readonly ATT_SUFFIX_REMOVER = /^([a-z]+)[blqw]$/i;
public static get key() { return 'amd64'; }
public override getInstructionInformation(instruction: string): AssemblyInstructionInfo | null {
// Try both raw opcode and with AT&T suffix removed
let info = getAsmOpcode(instruction);
if (!info) {
const alternativeInstruction = ATT_SUFFIX_REMOVER.exec(instruction);
const alternativeInstruction = Amd64DocumentationProvider.ATT_SUFFIX_REMOVER.exec(instruction);
if (alternativeInstruction) {
info = getAsmOpcode(alternativeInstruction[1]);
}

68
lib/asm-docs/arm32.ts Normal file
View File

@@ -0,0 +1,68 @@
// Copyright (c) 2021, Compiler Explorer Authors
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
import { AssemblyInstructionInfo, BaseAssemblyDocumentationProvider } from './base';
import { getAsmOpcode } from './generated/asm-docs-arm32';
export class Arm32DocumentationProvider extends BaseAssemblyDocumentationProvider {
private static readonly CONDITIONAL_INSTRUCTION_REGEXP = /^([A-Za-z]+?)(EQ|NE|CS|CC|MI|PL|VS|VC|HI|LS|GE|LT|GT|LE|AL)$/;
public static get key() { return 'arm32'; }
public override getInstructionInformation(instruction: string): AssemblyInstructionInfo | null {
const info = getAsmOpcode(instruction) || Arm32DocumentationProvider.getConditionalOpcode(instruction);
return info || null;
}
private static readonly CONDITIONAL_OPCODE_TAGS: Record<string, string> = {
EQ: 'If equal, ',
NE: 'If not equal, ',
CS: 'If carry set, ',
CC: 'If carry clear, ',
MI: 'If negative, ',
PL: 'If positive or zero, ',
VS: 'If overflow, ',
VC: 'If no overflow, ',
HI: 'If unsigned higher, ',
LS: 'If unsigned lower or same, ',
GE: 'If signed greater than or equal, ',
LT: 'If signed less than, ',
GT: 'If signed greater than, ',
LE: 'If signed less than or equal, ',
};
/** Add additional notes for conditional instructions */
private static getConditionalOpcode(instruction: string): AssemblyInstructionInfo | null {
// If the instruction is a conditional instruction
const isConditionalOpcode = instruction.match(Arm32DocumentationProvider.CONDITIONAL_INSTRUCTION_REGEXP);
if (!isConditionalOpcode) {
return null;
}
const information = getAsmOpcode(isConditionalOpcode[1]);
const text = Arm32DocumentationProvider.CONDITIONAL_OPCODE_TAGS[isConditionalOpcode[2]] || '';
return {
...information,
tooltip: text + information.tooltip,
html: text + information.html,
};
}
}

33
lib/asm-docs/avr.ts Normal file
View File

@@ -0,0 +1,33 @@
// Copyright (c) 2021, Compiler Explorer Authors
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
import { AssemblyInstructionInfo, BaseAssemblyDocumentationProvider } from './base';
import { getAsmOpcode } from './generated/asm-docs-avr';
export class AvrDocumentationProvider extends BaseAssemblyDocumentationProvider {
public static get key() { return 'avr'; }
public override getInstructionInformation(instruction: string): AssemblyInstructionInfo | null {
return getAsmOpcode(instruction) || null;
}
}

View File

@@ -22,38 +22,22 @@
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
import { propsFor } from '../../properties';
import { Amd64DocumentationHandler } from './amd64';
import { Arm32DocumentationHandler } from './arm32';
import { AVRDocumentationHandler } from './avr';
import { JavaDocumentationHandler } from './java';
import { Mos6502DocumentationHandler } from './mos6502';
/** @type {Record.<string, BaseAssemblyDocumentationHandler>} */
const ASSEMBLY_DOCUMENTATION_HANDLERS = {
amd64: new Amd64DocumentationHandler(),
arm32: new Arm32DocumentationHandler(),
java: new JavaDocumentationHandler(),
6502: new Mos6502DocumentationHandler(),
avr: new AVRDocumentationHandler(),
};
const MAX_STATIC_AGE = propsFor('asm-docs')('staticMaxAgeSecs', 10);
export type AssemblyInstructionInfo = Record<'tooltip' | 'html' | 'url', string>;
/**
* Initialize all Assembly Docs routes
* Base class for all assembly documentation generators.
*
* @param {e.Router} router
* Implementations of this class are responsible for providing documentation
* about an instruction set's assembly instructions by instruction opcode.
*
* Each implementor should provide a static `get key()` method that returns
* the name of the instruction set.
*/
export const setup = (router) => router.get('/asm/:arch/:opcode', (req, res) => {
if (MAX_STATIC_AGE > 0) {
res.setHeader('Cache-Control', `public, max-age=${MAX_STATIC_AGE}`);
}
const architecture = req.params.arch;
const handler = ASSEMBLY_DOCUMENTATION_HANDLERS[architecture];
if (handler !== undefined) {
return handler.handle(req, res);
}
res.status(404).json({ error: `No documentation for '${architecture}'` }).send();
});
export abstract class BaseAssemblyDocumentationProvider {
/**
* Gather the assembly instruction information by the instruction name.
*
* Implementors should return null if the instruction is not supported.
*/
public abstract getInstructionInformation(instruction: string): AssemblyInstructionInfo | null;
}

View File

@@ -22,11 +22,11 @@
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
import { getAsmOpcode } from '../asm-docs-java';
import { BaseAssemblyDocumentationHandler } from '../base-assembly-documentation-handler';
import { makeKeyedTypeGetter } from '../keyed-type';
export class JavaDocumentationHandler extends BaseAssemblyDocumentationHandler {
getInstructionInformation(instruction) {
return getAsmOpcode(instruction) || null;
}
}
import * as all from './_all';
export { BaseAssemblyDocumentationProvider } from './base';
export * from './_all';
export const getDocumentationProviderTypeByKey = makeKeyedTypeGetter('documentation provider', all);

33
lib/asm-docs/java.ts Normal file
View File

@@ -0,0 +1,33 @@
// Copyright (c) 2021, Compiler Explorer Authors
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
import { AssemblyInstructionInfo, BaseAssemblyDocumentationProvider } from './base';
import { getAsmOpcode } from './generated/asm-docs-java';
export class JavaDocumentationProvider extends BaseAssemblyDocumentationProvider {
public static get key() { return 'java'; }
public override getInstructionInformation(instruction: string): AssemblyInstructionInfo | null {
return getAsmOpcode(instruction) || null;
}
}

33
lib/asm-docs/mos6502.ts Normal file
View File

@@ -0,0 +1,33 @@
// Copyright (c) 2021, Compiler Explorer Authors
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
import { AssemblyInstructionInfo, BaseAssemblyDocumentationProvider } from './base';
import { getAsmOpcode } from './generated/asm-docs-6502';
export class Mos6502DocumentationProvider extends BaseAssemblyDocumentationProvider {
public static get key() { return '6502'; }
public override getInstructionInformation(instruction: string): AssemblyInstructionInfo | null {
return getAsmOpcode(instruction) || null;
}
}

View File

@@ -31,7 +31,7 @@ import { logger } from '../logger';
import { getShortenerTypeByKey } from '../shortener';
import * as utils from '../utils';
import * as assemblyDocumentation from './assembly-documentation/router';
import { withAssemblyDocumentationProviders } from './assembly-documentation';
import { FormattingHandler } from './formatting';
export class ApiHandler {
@@ -61,7 +61,7 @@ export class ApiHandler {
this.handle.get('/libraries', this.handleAllLibraries.bind(this));
// Binding for assembly documentation
assemblyDocumentation.setup(this.handle);
withAssemblyDocumentationProviders(this.handle);
// Legacy binding for old clients.
this.handle.get('/asm/:opcode',
(req, res) => res.redirect(`amd64/${req.params.opcode}`),

View File

@@ -0,0 +1,69 @@
// Copyright (c) 2021, Compiler Explorer Authors
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
import express from 'express';
import { BaseAssemblyDocumentationProvider, getDocumentationProviderTypeByKey } from '../asm-docs';
import { propsFor } from '../properties';
const MAX_STATIC_AGE = propsFor('asm-docs')('staticMaxAgeSecs', 10);
const onDocumentationProviderRequest = (
provider: BaseAssemblyDocumentationProvider,
request: express.Request,
response: express.Response,
) => {
// If the request had no opcode parameter, we should fail. This assumes
// no assembly language has a __unknown_opcode instruction.
const instruction = (request.params.opcode || '__UNKNOWN_OPCODE').toUpperCase();
const information = provider.getInstructionInformation(instruction);
if (information === null) {
return response.status(404).send({ error: `Unknown opcode '${instruction}'` });
}
// Accept either JSON or Plaintext Content-Type
const requestedContentType = request.accepts(['text', 'json']);
switch (requestedContentType) {
case 'text': response.send(information.html); break;
case 'json': response.send(information); break;
default: response.status(406).send({ error: 'Not Acceptable' }); break;
}
};
/** Initialize API routes for assembly documentation */
export const withAssemblyDocumentationProviders = (
router: express.Router,
) => router.get('/asm/:arch/:opcode', (req, res) => {
if (MAX_STATIC_AGE > 0) {
res.setHeader('Cache-Control', `public, max-age=${MAX_STATIC_AGE}`);
}
const arch = req.params.arch;
// makeKeyedTypeGetter throws if the key is not found. We do not wish
// crash CE if this happens, so we catch the error and return a 404.
try {
const providerClass = getDocumentationProviderTypeByKey(arch);
onDocumentationProviderRequest(new providerClass(), req, res);
} catch {
res.status(404).json({ error: `No documentation for '${arch}'` }).send();
}
});

View File

@@ -1,71 +0,0 @@
// Copyright (c) 2021, Compiler Explorer Authors
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
import { getAsmOpcode } from '../asm-docs-arm32';
import { BaseAssemblyDocumentationHandler } from '../base-assembly-documentation-handler';
const CONDITIONAL_INSTRUCTION_REGEXP = /^([A-Za-z]+?)(EQ|NE|CS|CC|MI|PL|VS|VC|HI|LS|GE|LT|GT|LE|AL)$/;
export class Arm32DocumentationHandler extends BaseAssemblyDocumentationHandler {
// Notes for conditionals:
// https://developer.arm.com/documentation/dui0473/m/condition-codes/condition-code-suffixes-and-related-flags
getOpcodeConditional(opcode) {
if (!opcode) return;
const conditionals = {
EQ: 'If equal, ',
NE: 'If not equal, ',
CS: 'If carry set, ',
CC: 'If carry clear, ',
MI: 'If negative, ',
PL: 'If positive or zero, ',
VS: 'If overflow, ',
VC: 'If no overflow, ',
HI: 'If unsigned higher, ',
LS: 'If unsigned lower or same, ',
GE: 'If signed greater than or equal, ',
LT: 'If signed less than, ',
GT: 'If signed greater than, ',
LE: 'If signed less than or equal, ',
};
const matches = opcode.match(CONDITIONAL_INSTRUCTION_REGEXP);
if (matches) {
const opcodeDescription = getAsmOpcode(matches[1]);
if (!opcodeDescription) return;
const conditionalText = conditionals[matches[2]] || '';
opcodeDescription.tooltip = conditionalText + opcodeDescription.tooltip;
opcodeDescription.html = conditionalText + opcodeDescription.html;
return opcodeDescription;
}
}
getInstructionInformation(instruction) {
const info = getAsmOpcode(instruction) || this.getOpcodeConditional(instruction);
return info || null;
}
}

View File

@@ -1,8 +0,0 @@
import { getAsmOpcode } from '../asm-docs-avr';
import { BaseAssemblyDocumentationHandler } from '../base-assembly-documentation-handler';
export class AVRDocumentationHandler extends BaseAssemblyDocumentationHandler {
getInstructionInformation(instruction) {
return getAsmOpcode(instruction) || null;
}
}

View File

@@ -1,8 +0,0 @@
import { getAsmOpcode } from '../asm-docs-6502';
import { BaseAssemblyDocumentationHandler } from '../base-assembly-documentation-handler';
export class Mos6502DocumentationHandler extends BaseAssemblyDocumentationHandler {
getInstructionInformation(instruction) {
return getAsmOpcode(instruction) || null;
}
}

View File

@@ -1,61 +0,0 @@
// Copyright (c) 2021, Compiler Explorer Authors
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
import express from 'express';
import { AssemblyDocumentationResponse } from '../../types/features/assembly-documentation.interfaces';
export class BaseAssemblyDocumentationHandler {
/**
* Gather the assembly instruction information by the instruction name.
*
* Implementors should return null if the instruction is not supported.
*/
// eslint-disable-next-line no-unused-vars
getInstructionInformation(instruction: string): AssemblyDocumentationResponse | null {
return null;
}
/**
* Handle a request for assembly instruction documentation.
*
* Implementors should not have to override this.
*/
handle(request: express.Request, response: express.Response) {
// If the request had no opcode parameter, we should fail. This assumes
// no assembly language has a __unknown_opcode instruction.
const instruction = (request.params.opcode || '__UNKNOWN_OPCODE').toUpperCase();
const information = this.getInstructionInformation(instruction);
if (information === null) {
return response.status(404).send({ error: `Unknown opcode '${instruction}'` });
}
// Accept either JSON or Plaintext Content-Type
const requestedContentType = request.accepts(['text', 'json']);
switch (requestedContentType) {
case 'text': response.send(information.html); break;
case 'json': response.send(information); break;
default: response.status(406).send({ error: 'Not Acceptable' }); break;
}
}
}

22
package-lock.json generated
View File

@@ -13007,6 +13007,20 @@
}
}
},
<<<<<<< HEAD
=======
"node_modules/ts-node/node_modules/acorn": {
"version": "8.6.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.6.0.tgz",
"integrity": "sha512-U1riIR+lBSNi3IbxtaHOIKdH8sLFv3NYfNv8sg7ZsNhcfl4HF2++BfqqrNAxoCLQW1iiylOj76ecnaUxz+z9yw==",
"bin": {
"acorn": "bin/acorn"
},
"engines": {
"node": ">=0.4.0"
}
},
>>>>>>> 066865fc (Move Assembly Documentation providers into their own /asm-providers directory under /lib)
"node_modules/ts-node/node_modules/diff": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
@@ -24172,6 +24186,14 @@
"yn": "3.1.1"
},
"dependencies": {
<<<<<<< HEAD
=======
"acorn": {
"version": "8.6.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.6.0.tgz",
"integrity": "sha512-U1riIR+lBSNi3IbxtaHOIKdH8sLFv3NYfNv8sg7ZsNhcfl4HF2++BfqqrNAxoCLQW1iiylOj76ecnaUxz+z9yw=="
},
>>>>>>> 066865fc (Move Assembly Documentation providers into their own /asm-providers directory under /lib)
"diff": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",

View File

@@ -0,0 +1,92 @@
// Copyright (c) 2021, Compiler Explorer Authors
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
import { expect } from 'chai';
import express from 'express';
import { withAssemblyDocumentationProviders } from '../../lib/handlers/assembly-documentation';
import { chai } from '../utils';
/** Test matrix of architecture to [opcode, tooptip, html, url] */
export const TEST_MATRIX = {
6502: ['lda', 'Load Accumulator with Memory', 'data is transferred from memory to the accumulator', 'https://www.pagetable.com/c64ref/6502/'],
amd64: ['mov', 'Copies the second operand', 'Copies the second operand', 'www.felixcloutier.com'],
arm32: ['mov', 'writes an immediate value', 'writes an immediate value to the destination register', 'https://developer.arm.com/documentation/'],
avr: ['mov', 'Copy Register', 'makes a copy of one register into another', 'https://ww1.microchip.com/downloads/en/DeviceDoc/AVR-InstructionSet-Manual-DS40002198.pdf'],
java: ['iload_0', 'Load int from local variable', 'Load int from local variable', 'https://docs.oracle.com/javase/specs/jvms/se16/html/'],
};
describe('Assembly Documentation API', () => {
let app;
before(() => {
app = express();
const router = express.Router();
withAssemblyDocumentationProviders(router);
app.use('/api', router);
});
it('should return 404 for unknown architecture', async () => {
const res = await chai.request(app).get(`/api/asm/not_an_arch/mov`)
.set('Accept', 'application/json');
expect(res).to.have.status(404);
expect(res).to.be.json;
expect(res.body).to.deep.equal({ error: `No documentation for 'not_an_arch'` });
});
for (const [arch, [opcode, tooltip, html, url]] of Object.entries(TEST_MATRIX)) {
it(`should process ${arch} text requests`, async () => {
const res = await chai.request(app).get(`/api/asm/${arch}/${opcode}`)
.set('Accept', 'text/plain');
expect(res).to.have.status(200);
expect(res).to.be.html;
expect(res.text).to.contain(html);
});
it(`should process ${arch} json requests`, async () => {
const res = await chai.request(app).get(`/api/asm/${arch}/${opcode}`)
.set('Accept', 'application/json');
expect(res).to.have.status(200);
expect(res).to.be.json;
expect(res.body.html).to.contain(html);
expect(res.body.tooltip).to.contain(tooltip);
expect(res.body.url).to.contain(url);
});
it(`should return 404 for ${arch} unknown opcode requests`, async () => {
const res = await chai.request(app).get(`/api/asm/${arch}/not_an_opcode`)
.set('Accept', 'application/json');
expect(res).to.have.status(404);
expect(res).to.be.json;
expect(res.body).to.deep.equal({ error: 'Unknown opcode \'NOT_AN_OPCODE\'' });
});
it(`should return 406 for ${arch} bad accept type requests`, async () => {
const res = await chai.request(app).get(`/api/asm/${arch}/${opcode}`)
.set('Accept', 'application/pdf');
expect(res).to.have.status(406);
});
}
});

View File

@@ -1,75 +0,0 @@
// Copyright (c) 2021, Compiler Explorer Authors
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
import express from 'express';
import { Mos6502DocumentationHandler } from '../../../lib/handlers/assembly-documentation/mos6502';
import { chai } from '../../utils';
describe('6502 assembly documentation', () => {
let app;
before(() => {
app = express();
const handler = new Mos6502DocumentationHandler();
app.use('/asm/:opcode', handler.handle.bind(handler));
});
it('returns 404 for unknown opcodes', () => {
return chai.request(app).get('/asm/lda_oh_wait')
.then(res => {
res.should.have.status(404);
res.should.be.json;
res.body.should.deep.equal({ error: 'Unknown opcode \'LDA_OH_WAIT\'' });
}).catch(e => { throw e; });
});
it('responds to accept=text requests', () => {
return chai.request(app).get('/asm/lda')
.then(res => {
res.should.have.status(200);
res.should.be.html;
res.text.should.contain('data is transferred from memory to the accumulator');
}).catch(e => { throw e; });
});
it('responds to accept=json requests', () => {
return chai.request(app).get('/asm/lda')
.set('Accept', 'application/json')
.then(res => {
res.should.have.status(200);
res.should.be.json;
res.body.html.should.contain('data is transferred from memory to the accumulator');
res.body.tooltip.should.contain('Load Accumulator with Memory');
res.body.url.should.contain('https://www.pagetable.com/c64ref/6502/');
}).catch(e => { throw e; });
});
it('should return 406 on bad accept type', () => {
return chai.request(app).get('/asm/lda')
.set('Accept', 'application/pdf')
.then(res => {
res.should.have.status(406);
}).catch(e => { throw e; });
});
});

View File

@@ -1,85 +0,0 @@
// Copyright (c) 2021, Compiler Explorer Authors
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
import express from 'express';
import { Amd64DocumentationHandler } from '../../../lib/handlers/assembly-documentation/amd64';
import { chai } from '../../utils';
describe('amd64 assembly documentation', () => {
let app;
before(() => {
app = express();
const handler = new Amd64DocumentationHandler();
app.use('/asm/:opcode', handler.handle.bind(handler));
});
it('returns 404 for unknown opcodes', () => {
return chai.request(app).get('/asm/mov_oh_wait')
.then(res => {
res.should.have.status(404);
res.should.be.json;
res.body.should.deep.equal({ error: 'Unknown opcode \'MOV_OH_WAIT\'' });
}).catch(e => { throw e; });
});
it('responds to accept=text requests', () => {
return chai.request(app).get('/asm/mov')
.then(res => {
res.should.have.status(200);
res.should.be.html;
res.text.should.contain('Copies the second operand');
}).catch(e => { throw e; });
});
it('responds to accept=json requests', () => {
return chai.request(app).get('/asm/mov')
.set('Accept', 'application/json')
.then(res => {
res.should.have.status(200);
res.should.be.json;
res.body.html.should.contain('Copies the second operand');
res.body.tooltip.should.contain('Copies the second operand');
res.body.url.should.contain('www.felixcloutier.com');
}).catch(e => { throw e; });
});
it('should return 406 on bad accept type', () => {
return chai.request(app).get('/asm/mov')
.set('Accept', 'application/pdf')
.then(res => {
res.should.have.status(406);
}).catch(e => { throw e; });
});
it('should handle at&t syntax', () => {
return chai.request(app)
.get('/asm/addq')
.then(res => {
res.should.have.status(200);
res.should.be.html;
res.text.should.contain('Adds the destination operand');
}).catch(e => { throw e; });
});
});

View File

@@ -1,73 +0,0 @@
// Copyright (c) 2021, Compiler Explorer Authors
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
import express, { Router } from 'express';
import { setup } from '../../../lib/handlers/assembly-documentation/router';
import { chai } from '../../utils';
describe('Assembly Documentation API', () => {
let app;
before(() => {
app = express();
/** @type {e.Router} */
const router = Router();
setup(router);
app.use('/api', router);
});
it('should accept requests to the api', () => {
return chai.request(app)
.get('/api/asm/amd64/mov')
.set('Accept', 'application/json')
.then(res => {
res.should.have.status(200);
res.should.be.json;
})
.catch(err => {
throw err;
});
});
it('should return opcode not found', () => {
return chai.request(app).get('/api/asm/amd64/notexistingop')
.set('Accept', 'application/json')
.then((res) => {
res.should.have.status(404);
res.should.be.json;
res.body.should.deep.equals({ error: `Unknown opcode 'NOTEXISTINGOP'` });
}).catch(e => { throw e; });
});
it('should return architecture not found', () => {
return chai.request(app).get('/api/asm/notarch/mov')
.set('Accept', 'application/json')
.then((res) => {
res.should.have.status(404);
res.should.be.json;
res.body.should.deep.equals({ error: `No documentation for 'notarch'` });
}).catch(e => { throw e; });
});
});

View File

@@ -1,105 +0,0 @@
// Copyright (c) 2021, Compiler Explorer Authors
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
import express from 'express';
import { Arm32DocumentationHandler } from '../../../lib/handlers/assembly-documentation/arm32';
import { chai } from '../../utils';
describe('arm32 assembly documentation', () => {
let app;
before(() => {
app = express();
const handler = new Arm32DocumentationHandler();
app.use('/asm/:opcode', handler.handle.bind(handler));
});
it('returns 404 for unknown opcodes', () => {
return chai.request(app).get('/asm/mov_oh_wait')
.then(res => {
res.should.have.status(404);
res.should.be.json;
res.body.should.deep.equal({ error: 'Unknown opcode \'MOV_OH_WAIT\'' });
}).catch(e => { throw e; });
});
it('responds to accept=text requests', () => {
return chai.request(app).get('/asm/mov')
.then(res => {
res.should.have.status(200);
res.should.be.html;
res.text.should.contain('writes an immediate value to the destination register');
}).catch(e => { throw e; });
});
it('responds to accept=json requests', () => {
return chai.request(app).get('/asm/mov')
.set('Accept', 'application/json')
.then(res => {
res.should.have.status(200);
res.should.be.json;
res.body.html.should.contain('writes an immediate value');
res.body.tooltip.should.contain('writes an immediate value');
res.body.url.should.contain('https://developer.arm.com/documentation/');
}).catch(e => { throw e; });
});
it('should return 406 on bad accept type', () => {
return chai.request(app).get('/asm/mov')
.set('Accept', 'application/pdf')
.then(res => {
res.should.have.status(406);
}).catch(e => { throw e; });
});
it('should handle opcodes with a conditional type suffix that are not conditionals', () => {
return chai.request(app)
.get('/asm/adcs')
.then(res => {
res.should.have.status(200);
res.should.be.html;
res.text.should.contain('adds an immediate value and the Carry flag');
}).catch(e => { throw e; });
});
it('should handle conditional opcodes', () => {
return chai.request(app)
.get('/asm/beq')
.then(res => {
res.should.have.status(200);
res.should.be.html;
res.text.should.contain('If equal, <p>Branch causes a branch');
}).catch(e => { throw e; });
});
it('should respond with "unknown opcode" for unknown opcodes that can be parsed as conditional', () => {
return chai.request(app)
.get('/asm/jne')
.then(res => {
res.should.have.status(404);
res.should.be.json;
res.body.should.deep.equal({ error: 'Unknown opcode \'JNE\'' });
}).catch(e => { throw e; });
});
});

View File

@@ -1,75 +0,0 @@
// Copyright (c) 2021, Compiler Explorer Authors
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
import express from 'express';
import { AVRDocumentationHandler } from '../../../lib/handlers/assembly-documentation/avr';
import { chai } from '../../utils';
describe('AVR assembly documentation', () => {
let app;
before(() => {
app = express();
const handler = new AVRDocumentationHandler();
app.use('/asm/:opcode', handler.handle.bind(handler));
});
it('returns 404 for unknown opcodes', () => {
return chai.request(app).get('/asm/mov_oh_wait')
.then(res => {
res.should.have.status(404);
res.should.be.json;
res.body.should.deep.equal({ error: 'Unknown opcode \'MOV_OH_WAIT\'' });
}).catch(e => { throw e; });
});
it('responds to accept=text requests', () => {
return chai.request(app).get('/asm/mov')
.then(res => {
res.should.have.status(200);
res.should.be.html;
res.text.should.contain('makes a copy of one register into another');
}).catch(e => { throw e; });
});
it('responds to accept=json requests', () => {
return chai.request(app).get('/asm/mov')
.set('Accept', 'application/json')
.then(res => {
res.should.have.status(200);
res.should.be.json;
res.body.html.should.contain('makes a copy of one register into another');
res.body.tooltip.should.contain('Copy Register');
res.body.url.should.contain('https://ww1.microchip.com/downloads/en/DeviceDoc/AVR-InstructionSet-Manual-DS40002198.pdf');
}).catch(e => { throw e; });
});
it('should return 406 on bad accept type', () => {
return chai.request(app).get('/asm/mov')
.set('Accept', 'application/pdf')
.then(res => {
res.should.have.status(406);
}).catch(e => { throw e; });
});
});

View File

@@ -1,75 +0,0 @@
// Copyright (c) 2021, Compiler Explorer Authors
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
import express from 'express';
import { JavaDocumentationHandler } from '../../../lib/handlers/assembly-documentation/java';
import { chai } from '../../utils';
describe('jvm assembly documentation', () => {
let app;
before(() => {
app = express();
const handler = new JavaDocumentationHandler();
app.use('/asm/:opcode', handler.handle.bind(handler));
});
it('returns 404 for unknown opcodes', () => {
return chai.request(app).get('/asm/mov_oh_wait')
.then(res => {
res.should.have.status(404);
res.should.be.json;
res.body.should.deep.equal({ error: 'Unknown opcode \'MOV_OH_WAIT\'' });
}).catch(e => { throw e; });
});
it('responds to accept=text requests', () => {
return chai.request(app).get('/asm/iload_0')
.then(res => {
res.should.have.status(200);
res.should.be.html;
res.text.should.contain('Load int from local variable');
}).catch(e => { throw e; });
});
it('responds to accept=json requests', () => {
return chai.request(app).get('/asm/iload_0')
.set('Accept', 'application/json')
.then(res => {
res.should.have.status(200);
res.should.be.json;
res.body.html.should.contain('Load int from local variable');
res.body.tooltip.should.contain('Load int from local variable');
res.body.url.should.contain('https://docs.oracle.com/javase/specs/jvms/se16/html/');
}).catch(e => { throw e; });
});
it('should return 406 on bad accept type', () => {
return chai.request(app).get('/asm/iload_0')
.set('Accept', 'application/pdf')
.then(res => {
res.should.have.status(406);
}).catch(e => { throw e; });
});
});

View File

@@ -22,6 +22,8 @@
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
import { AssemblyInstructionInfo } from '../../lib/asm-docs/base';
export interface AssemblyDocumentationRequest {
/** Specifies which instruction set to look for */
instructionSet: 'amd64' | 'arm32' | 'java';
@@ -29,11 +31,7 @@ export interface AssemblyDocumentationRequest {
opcode: string;
}
export interface AssemblyDocumentationResponse {
tooltip: string;
html: string;
url: string;
}
export type AssemblyDocumentationResponse = AssemblyInstructionInfo;
export interface AssemblyDocumentationError {
/** Explanatory error string */