Skip to content

Commit

Permalink
The War of The Types (#4490)
Browse files Browse the repository at this point in the history
  • Loading branch information
jeremy-rifkin committed Jan 14, 2023
1 parent 9496e5c commit 07f37ab
Show file tree
Hide file tree
Showing 44 changed files with 416 additions and 303 deletions.
3 changes: 2 additions & 1 deletion docs/API.md
Expand Up @@ -122,7 +122,8 @@ probably not useful for most REST users.

To force a cache bypass, set `bypassCache` in the root of the request to `true`.

Filters include `binary`, `binaryObject`, `labels`, `intel`, `directives` and `demangle`, which correspond to the UI buttons on the HTML version.
Filters include `binary`, `binaryObject`, `labels`, `intel`, `directives` and `demangle`, which correspond to the UI
buttons on the HTML version.

With the tools array you can ask CE to execute certain tools available for the current compiler, and also supply
arguments for this tool.
Expand Down
2 changes: 1 addition & 1 deletion docs/AddingACompiler.md
Expand Up @@ -85,7 +85,7 @@ once the site runs on the Amazon environment, the `&clang` group **will not** ha
### Configuration keys

| Key Name | Type | Description |
|----------------------|------------|------------------------------------------------------------------------------------------------------------------|
| -------------------- | ---------- | ---------------------------------------------------------------------------------------------------------------- |
| name | String | Human readable name of the compiler |
| exe | Path | Path to the executable |
| alias | Identifier | Another identifier for this compiler (mostly deprecated, used for backwards compatibility with very old CE URLs) |
Expand Down
7 changes: 5 additions & 2 deletions lib/base-compiler.ts
Expand Up @@ -137,8 +137,11 @@ export class BaseCompiler implements ICompiler {
if (!this.compiler.supportsOptOutput) this.compiler.supportsOptOutput = false;

if (!this.compiler.disabledFilters) this.compiler.disabledFilters = [];
else if (typeof this.compiler.disabledFilters === 'string')
this.compiler.disabledFilters = this.compiler.disabledFilters.split(',');
else if (typeof (this.compiler.disabledFilters as any) === 'string') {
// When first loaded from props it may be a string so we split it here
// I'd like a better way to do this that doesn't involve type hacks
this.compiler.disabledFilters = (this.compiler.disabledFilters as any).split(',');
}

this.asm = new AsmParser(this.compilerProps);
this.llvmIr = new LlvmIrParser(this.compilerProps);
Expand Down
34 changes: 34 additions & 0 deletions lib/common-utils.ts
@@ -0,0 +1,34 @@
// Copyright (c) 2022, 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 function isString(x: any): x is string {
return typeof x === 'string' || x instanceof String;
}

// Object.keys is typed as returning :string[] for some reason
// This util is for cases where the key is a union of a few possible keys and we
// want the resulting array properly typed.
export function keys<K extends string | number | symbol>(o: Record<K, any>): K[] {
return Object.keys(o) as K[];
}
2 changes: 2 additions & 0 deletions static/.eslintrc.js
Expand Up @@ -64,6 +64,8 @@ module.exports = {
'@typescript-eslint/no-explicit-any': 'off', // Too much js code still exists
'@typescript-eslint/ban-ts-comment': 'off', // We need some @ts-ignore at some points
'@typescript-eslint/no-unnecessary-condition': 'error',
'@typescript-eslint/no-unnecessary-type-assertion': 'error',
'@typescript-eslint/prefer-includes': 'error',
},
},
],
Expand Down
22 changes: 12 additions & 10 deletions static/ansi-to-html.ts
Expand Up @@ -29,6 +29,8 @@

import _ from 'underscore';
import {AnsiToHtmlOptions, ColorCodes} from './ansi-to-html.interfaces';
import {assert, unwrap} from './assert';
import {isString} from '../lib/common-utils';

const defaults: AnsiToHtmlOptions = {
fg: '#FFF',
Expand Down Expand Up @@ -120,16 +122,16 @@ function toColorHexString(ref: number[]): string {

function generateOutput(stack: string[], token: string, data: string | number, options: AnsiToHtmlOptions): string {
if (token === 'text') {
// Note: Param 'data' must be a string at this point
return pushText(data as string, options);
assert(isString(data), "Param 'data' must be a string at this point");
return pushText(data, options);
} else if (token === 'display') {
return handleDisplay(stack, data, options);
} else if (token === 'xterm256') {
// Note: Param 'data' must be a string at this point
return handleXterm256(stack, data as string, options);
assert(isString(data), "Param 'data' must be a string at this point");
return handleXterm256(stack, data, options);
} else if (token === 'rgb') {
// Note: Param 'data' must be a string at this point
return handleRgb(stack, data as string, options);
assert(isString(data), "Param 'data' must be a string at this point");
return handleRgb(stack, data, options);
}
return '';
}
Expand Down Expand Up @@ -162,7 +164,7 @@ function handleXterm256(stack: string[], data: string, options: AnsiToHtmlOption
}

function handleDisplay(stack: string[], _code: string | number, options: AnsiToHtmlOptions): string {
const code: number = parseInt(_code as string, 10);
const code: number = isString(_code) ? parseInt(_code, 10) : _code;
const codeMap: Record<number, () => string> = {
'-1': () => '<br />',
// @ts-ignore
Expand All @@ -176,8 +178,8 @@ function handleDisplay(stack: string[], _code: string | number, options: AnsiToH
22: () => closeTag(stack, 'b'),
23: () => closeTag(stack, 'i'),
24: () => closeTag(stack, 'u'),
39: () => pushForegroundColor(stack, options.fg as string),
49: () => pushBackgroundColor(stack, options.bg as string),
39: () => pushForegroundColor(stack, unwrap(options.fg)),
49: () => pushBackgroundColor(stack, unwrap(options.bg)),
};

if (code in codeMap) {
Expand Down Expand Up @@ -249,7 +251,7 @@ function notCategory(category: string): (e: StickyStackElement) => boolean {
* @returns the ansi token type
*/
function categoryForCode(_code: string | number): string {
const code: number = parseInt(_code as string, 10);
const code: number = isString(_code) ? parseInt(_code, 10) : _code;

if (code === 0) {
return 'all';
Expand Down
11 changes: 11 additions & 0 deletions static/assert.ts
Expand Up @@ -22,6 +22,7 @@
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

import {isString} from '../lib/common-utils';
import stacktrace from '../lib/stacktrace';

// This file defines three assert utilities:
Expand Down Expand Up @@ -78,3 +79,13 @@ export function unwrap<T>(x: T | undefined | null, message?: string, ...extra_in
}
return x;
}

// This mainly a utility for JQuery.val(): string | number | string[] | undefined, in our code we typically want a
// single string.
// T is syntax sugar for unwrapping to a string union
export function unwrapString<T extends string>(x: any, message?: string, ...extra_info: any[]): T {
if (!isString(x)) {
fail('String unwrap failed', message, extra_info);
}
return x as T;
}
8 changes: 4 additions & 4 deletions static/compiler-service.ts
Expand Up @@ -79,7 +79,7 @@ export class CompilerService {

const foundCompiler = this.findCompiler(langId, compilerId);
if (!foundCompiler) {
const compilers = Object.values(this.getCompilersForLang(langId));
const compilers = Object.values(this.getCompilersForLang(langId) ?? {});
if (compilers.length > 0) {
return {
compiler: compilers[0],
Expand Down Expand Up @@ -144,8 +144,8 @@ export class CompilerService {
.value();
}

getCompilersForLang(langId: string): Record<string, CompilerInfo> {
return langId in this.compilersByLang ? this.compilersByLang[langId] : {};
getCompilersForLang(langId: string): Record<string, CompilerInfo> | undefined {
return langId in this.compilersByLang ? this.compilersByLang[langId] : undefined;
}

private findCompilerInList(compilers: Record<string, CompilerInfo>, compilerId: string) {
Expand All @@ -162,7 +162,7 @@ export class CompilerService {

findCompiler(langId: string, compilerId: string): CompilerInfo | null {
if (!compilerId) return null;
const compilers = this.getCompilersForLang(langId);
const compilers = this.getCompilersForLang(langId) ?? {};
return this.findCompilerInList(compilers, compilerId);
}

Expand Down
9 changes: 5 additions & 4 deletions static/components.interfaces.ts
Expand Up @@ -61,14 +61,15 @@ export interface ComponentConfig<S> {
}

type StateWithLanguage = {lang: string};
type StateWithEditor = {source: number};
// TODO(#4490 The War of The Types) We should normalize state types
type StateWithEditor = {source: string | number};
type StateWithTree = {tree: number};
type StateWithId = {id: number};
type EmptyState = Record<never, never>;

export type EmptyCompilerState = StateWithLanguage & StateWithEditor;
export type PopulatedCompilerState = StateWithEditor & {
filters: CompilerOutputOptions;
filters: CompilerOutputOptions | undefined;
options: unknown;
compiler: string;
libs?: unknown;
Expand All @@ -95,12 +96,12 @@ export type PopulatedEditorState = StateWithId & {
export type EmptyTreeState = Partial<StateWithId>;

export type OutputState = StateWithTree & {
compiler: string;
compiler: number; // CompilerID
editor: number; // EditorId
};

export type ToolViewState = StateWithTree & {
compiler: string;
compiler: number; // CompilerId
editor: number; // EditorId
toolId: string;
args: unknown;
Expand Down
12 changes: 6 additions & 6 deletions static/components.ts
Expand Up @@ -127,7 +127,7 @@ export function getCompiler(editorId: number, lang: string): ComponentConfig<Emp
*/
export function getCompilerWith(
editorId: number,
filters: ParseFiltersAndOutputOptions,
filters: ParseFiltersAndOutputOptions | undefined,
options: unknown,
compilerId: string,
langId?: string,
Expand Down Expand Up @@ -255,7 +255,7 @@ export function getTree(id?: number): ComponentConfig<EmptyTreeState> {
}

/** Get an output component with the given configuration. */
export function getOutput(compiler: string, editor: number, tree: number): ComponentConfig<OutputState> {
export function getOutput(compiler: number, editor: number, tree: number): ComponentConfig<OutputState> {
return {
type: 'component',
componentName: OUTPUT_COMPONENT_NAME,
Expand All @@ -269,7 +269,7 @@ export function getOutput(compiler: string, editor: number, tree: number): Compo

/** Get a tool view component with the given configuration. */
export function getToolViewWith(
compiler: string,
compiler: number,
editor: number,
toolId: string,
args: string,
Expand Down Expand Up @@ -353,7 +353,7 @@ export function getOptView(): ComponentConfig<EmptyOptViewState> {
/** Get an opt view with the given configuration. */
export function getOptViewWith(
id: number,
source: number,
source: string,
optOutput: unknown,
compilerName: string,
editorid: number,
Expand Down Expand Up @@ -411,7 +411,7 @@ export function getPpView(): ComponentConfig<EmptyPpViewState> {
/** Get a preprocessor view with the given configuration. */
export function getPpViewWith(
id: number,
source: number,
source: string,
ppOutput: unknown,
compilerName: string,
editorid: number,
Expand Down Expand Up @@ -443,7 +443,7 @@ export function getAstView(): ComponentConfig<EmptyAstViewState> {
/** Get an ast view with the given configuration. */
export function getAstViewWith(
id: number,
source: number,
source: string,
astOutput: unknown,
compilerName: string,
editorid: number,
Expand Down
2 changes: 1 addition & 1 deletion static/event-map.ts
Expand Up @@ -86,7 +86,7 @@ export type EventMap = {
) => void;
executorClose: (executorId: number) => void;
executorOpen: (executorId: number, editorId: boolean | number) => void;
filtersChange: (compilerId: number, filters: CompilerOutputOptions) => void;
filtersChange: (compilerId: number, filters: Partial<CompilerOutputOptions>) => void;
findCompilers: () => void;
findEditors: () => void;
findExecutors: () => void;
Expand Down
3 changes: 2 additions & 1 deletion static/formatter-registry.ts
Expand Up @@ -28,6 +28,7 @@ import {Alert} from './widgets/alert';
import {Settings} from './settings';
import {FormattingRequest} from './api/formatting.interfaces';
import {getFormattedCode} from './api/api';
import {unwrap} from './assert';

// Proxy function to emit the error to the alert system
const onFormatError = (cause: string, source: string) => {
Expand All @@ -44,7 +45,7 @@ const doFormatRequest = async (options: FormattingRequest) => {
const body = await res.json();
if (res.status === 200 && body.exit === 0) {
// API sent 200 and we have a valid response
return body.answer as string;
return unwrap(body.answer);
}
// We had an error (either HTTP request error, or API error)
// Figure out which it is, show it to the user, and reject the promise
Expand Down
8 changes: 6 additions & 2 deletions static/graph-layout-core.ts
Expand Up @@ -655,12 +655,16 @@ export class GraphLayoutCore {
// Throw everything away and do it all again, but smarter
for (const edgeColumn of this.edgeColumns) {
for (const intervalTree of edgeColumn.intervals) {
intervalTree.root = null as unknown as Node<EdgeSegment>;
// Note: Incorrect types in the interval tree library
// After https://github.com/alexbol99/flatten-interval-tree/pull/42 this can be intervalTree.clear()
(intervalTree.root as Node<EdgeSegment> | null) = null;
}
}
for (const edgeRow of this.edgeRows) {
for (const intervalTree of edgeRow.intervals) {
intervalTree.root = null as unknown as Node<EdgeSegment>;
// Note: Incorrect types in the interval tree library
// After https://github.com/alexbol99/flatten-interval-tree/pull/42 this can be intervalTree.clear()
(intervalTree.root as Node<EdgeSegment> | null) = null;
}
}
// Edge kind is the primary heuristic for subrow/column assignment
Expand Down
6 changes: 3 additions & 3 deletions static/hub.ts
Expand Up @@ -91,7 +91,7 @@ export class Hub {
public trees: Tree[] = [];
public editors: any[] = []; // typeof Editor

public readonly compilerService: any; // typeof CompilerService
public readonly compilerService: CompilerService;

public deferred = true;
public deferredEmissions: unknown[][] = [];
Expand Down Expand Up @@ -340,8 +340,8 @@ export class Hub {
}

public addAtRoot(elem: GoldenLayout.ContentItem): void {
const rootFirstItem = this.layout.root.contentItems[0] as GoldenLayout.ContentItem | undefined;
if (rootFirstItem) {
if (this.layout.root.contentItems.length > 0) {
const rootFirstItem = this.layout.root.contentItems[0];
if (rootFirstItem.isRow || rootFirstItem.isColumn) {
rootFirstItem.addChild(elem);
} else {
Expand Down
5 changes: 2 additions & 3 deletions static/modes/llvm-ir-mode.ts
Expand Up @@ -22,13 +22,12 @@
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

'use strict';
const monaco = require('monaco-editor');
import * as monaco from 'monaco-editor';

// This definition is based on the official LLVM vim syntax:
// http://llvm.org/svn/llvm-project/llvm/trunk/utils/vim/syntax/llvm.vim
// For VIM regex syntax, see: http://vimdoc.sourceforge.net/htmldoc/pattern.html
export function definition() {
export function definition(): monaco.languages.IMonarchLanguage {
return {
// llvmType
types: [
Expand Down
2 changes: 1 addition & 1 deletion static/modes/mlir-mode.ts
Expand Up @@ -29,6 +29,6 @@ import {definition} from './llvm-ir-mode';
// TODO: write an actual MLIR monaco mode

monaco.languages.register({id: 'mlir'});
monaco.languages.setMonarchTokensProvider('mlir', definition() as any);
monaco.languages.setMonarchTokensProvider('mlir', definition());

export {};
4 changes: 2 additions & 2 deletions static/multifile-service.ts
Expand Up @@ -26,6 +26,7 @@ import _ from 'underscore';
import path from 'path-browserify';
import JSZip from 'jszip';
import {Hub} from './hub';
import {unwrap} from './assert';
const languages = require('./options').options.languages;

export interface MultifileFile {
Expand Down Expand Up @@ -148,8 +149,7 @@ export class MultifileService {
return;
}

// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
let content = await zip.file(zipEntry.name)!.async('string');
let content = await unwrap(zip.file(zipEntry.name)).async('string');
if (content.length > this.maxFilesize) {
return;
}
Expand Down
3 changes: 3 additions & 0 deletions static/options.interfaces.ts
Expand Up @@ -62,4 +62,7 @@ export type Options = {
sentryEnvironment?: string;
compileOptions: Record<LanguageKey, string>;
tools: Record<LanguageKey, Record<string, Tool>>;
supportsExecute: boolean;
supportsLibraryCodeFilter: boolean;
cvCompilerCountMax: number;
};

0 comments on commit 07f37ab

Please sign in to comment.