Skip to content

Commit

Permalink
Merge branch 'main' into go-maps
Browse files Browse the repository at this point in the history
  • Loading branch information
mergify[bot] authored Feb 17, 2021
2 parents 7681a4d + e4a4d3c commit c1b8e4f
Show file tree
Hide file tree
Showing 2 changed files with 122 additions and 24 deletions.
1 change: 1 addition & 0 deletions packages/@jsii/go-runtime/.gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/jsii-calc/
*.generated.go
*.generated_test.go

*.js
*.d.ts
145 changes: 121 additions & 24 deletions packages/@jsii/go-runtime/build-tools/gen.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#!/usr/bin/env npx ts-node

import { CodeMaker } from 'codemaker';
import { createHash } from 'crypto';
import { readdirSync, readFileSync, statSync } from 'fs';
import { resolve } from 'path';

Expand All @@ -15,6 +16,7 @@ const EMBEDDED_RUNTIME_ROOT = resolve(
const OUTPUT_DIR = resolve(__dirname, '..', 'jsii-runtime-go');

const RUNTIME_FILE = 'embeddedruntime.generated.go';
const RUNTIME_TEST_FILE = 'embeddedruntime.generated_test.go';
const VERSION_FILE = 'version.generated.go';

const code = new CodeMaker({ indentationLevel: 1, indentCharacter: '\t' });
Expand All @@ -23,8 +25,10 @@ code.openFile(RUNTIME_FILE);
code.line('package jsii');
code.line();
code.open('var embeddedruntime = map[string][]byte{');
const bytesPerLine = 16;
const fileSize: Record<string, number> = {};
const fileInfo: Record<
string,
{ readonly size: number; readonly hash: readonly string[] }
> = {};

(function emitFiles(directory: string, prefix?: string) {
for (const file of readdirSync(directory)) {
Expand All @@ -42,35 +46,83 @@ const fileSize: Record<string, number> = {};

const key = prefix ? `${prefix}/${file}` : file;

const byteSlice = getByteSlice(fullPath);
fileSize[key] = byteSlice.length;
const { byteSlice, hash } = getByteSlice(fullPath);
fileInfo[key] = {
size: byteSlice.length,
hash,
};
code.open(`${JSON.stringify(key)}: []byte{`);
for (let i = 0; i < byteSlice.length; i += bytesPerLine) {
const line = byteSlice.slice(i, i + bytesPerLine);
code.line(`${line.join(', ')},`);
}
formatBytes(code, byteSlice);
code.close('},');
}
})(EMBEDDED_RUNTIME_ROOT);

code.close('}');
code.line();
const mainKey = JSON.stringify(
Object.keys(fileSize).find((f) => f.endsWith('jsii-runtime.js')),
Object.keys(fileInfo).find((f) => f.endsWith('jsii-runtime.js')),
)!;
code.line(`const embeddedruntimeMain = ${mainKey}`);
code.closeFile(RUNTIME_FILE);

// This allows us to sanity-check we've generated correct data
code.openFile(RUNTIME_TEST_FILE);
code.line('package jsii');
code.line();
// This performs sanity tests upon initialization
code.open('func init() {');
for (const [file, size] of Object.entries(fileSize)) {
code.open(`if len(embeddedruntime[${JSON.stringify(file)}]) != ${size} {`);
code.line(
`panic("Embedded runtime file ${file} does not have expected size of ${size} bytes!")`,
);
code.close('}');
code.open('import (');
code.line('"crypto/sha512"');
code.line('"testing"');
code.close(')');
code.line();
code.openBlock('func TestEmbeddedruntime(t *testing.T)');

code.open(
't.Run("embeddedruntime[embeddedruntimeMain] exists", func(t *testing.T) {',
);
code.openBlock('if _, exists := embeddedruntime[embeddedruntimeMain]; !exists');
code.line(
't.Errorf("embeddedruntimeMain refers to non-existent file %s", embeddedruntimeMain)',
);
code.closeBlock();
code.close('})');

for (const [file, { size, hash }] of Object.entries(fileInfo)) {
code.line();
code.open(`t.Run("embeddedruntime[\\"${file}\\"]", func(t *testing.T) {`);

code.open('checkEmbeddedFile(');
code.line('t,');
code.line(`"${file}",`);
code.line(`${readableNumber(size)},`);
code.open('[sha512.Size]byte{');
formatBytes(code, hash);
code.close('},');
code.close(')');

code.close('})');
}
code.close('}');
code.closeFile(RUNTIME_FILE);
code.closeBlock();
code.line();
code.openBlock(
'func checkEmbeddedFile(t *testing.T, name string, expectedSize int, expectedHash [sha512.Size]byte)',
);
code.line('data := embeddedruntime[name]');
code.line();
code.line('size := len(data)');
code.openBlock('if size != expectedSize');
code.line(
't.Errorf("Size mismatch: expected %d bytes, got %d", expectedSize, size)',
);
code.closeBlock();
code.line();
code.line('hash := sha512.Sum512(data)');
code.openBlock('if hash != expectedHash');
code.line(
't.Errorf("SHA512 do not match:\\nExpected: %x\\nActual: %x", expectedHash, hash)',
);
code.closeBlock();
code.closeBlock();
code.closeFile(RUNTIME_TEST_FILE);

code.openFile(VERSION_FILE);
code.line('package jsii');
Expand All @@ -83,12 +135,57 @@ code.closeFile(VERSION_FILE);

code.save(OUTPUT_DIR).catch(console.error);

function getByteSlice(path: string) {
const fileData = readFileSync(path).toString('hex');
function getByteSlice(path: string): { byteSlice: string[]; hash: string[] } {
const rawData = readFileSync(path);
return {
byteSlice: toHexBytes(rawData),
hash: toHexBytes(createHash('SHA512').update(rawData).digest()),
};
}

function toHexBytes(rawData: Buffer): string[] {
const hexString = rawData.toString('hex');
const result = [];
for (let i = 0; i < fileData.length; i += 2) {
result.push(`0x${fileData[i]}${fileData[i + 1]}`);
for (let i = 0; i < hexString.length; i += 2) {
result.push(`0x${hexString[i]}${hexString[i + 1]}`);
}

return result;
}

function formatBytes(
code: CodeMaker,
byteSlice: readonly string[],
bytesPerLine = 16,
) {
for (let i = 0; i < byteSlice.length; i += bytesPerLine) {
const line = byteSlice.slice(i, i + bytesPerLine);
code.line(`${line.join(', ')},`);
}
}

/**
* Turns a integer into a "human-readable" format, adding an `_` thousand
* separator.
*
* @param val an integer to be formatted.
*
* @returns the formatted number with thousand separators.
*/
function readableNumber(val: number): string {
return val.toFixed(0).replace(
// This regex can be a little jarring, so it is annotated below with the
// corresponding explanation. It can also be explained in plain english:
// matches the position before any sequence of N consecutive digits (0-9)
// where N is a multiple of 3.
/**/ /\B(?=(\d{3})+(?!\d))/g,
// \B -- not a word boundary (i.e: not start of input)
// (?= ) -- positive lookahead (does not consume input)
// ( )+ -- repeated one or more times
// \d -- any digit (0-9)
// {3} -- repeated exactly 3 times
// (?! ) -- negative lookahead (does not consume input)
// \d -- any digit (0-9), negated by surrounding group
//
'_',
);
}

0 comments on commit c1b8e4f

Please sign in to comment.