worker.js 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892
  1. import * as base from '../source/base.js';
  2. import * as fs from 'fs/promises';
  3. import * as node from '../source/node.js';
  4. import * as path from 'path';
  5. import * as process from 'process';
  6. import * as python from '../source/python.js';
  7. import * as tar from '../source/tar.js';
  8. import * as url from 'url';
  9. import * as view from '../source/view.js';
  10. import * as worker_threads from 'worker_threads';
  11. import * as zip from '../source/zip.js';
  12. const access = async (path) => {
  13. try {
  14. await fs.access(path);
  15. return true;
  16. } catch {
  17. return false;
  18. }
  19. };
  20. const dirname = (...args) => {
  21. const file = url.fileURLToPath(import.meta.url);
  22. const dir = path.dirname(file);
  23. return path.join(dir, ...args);
  24. };
  25. const decompress = (buffer) => {
  26. let archive = zip.Archive.open(buffer, 'gzip');
  27. if (archive && archive.entries.size === 1) {
  28. const stream = archive.entries.values().next().value;
  29. buffer = stream.peek();
  30. }
  31. const formats = [zip, tar];
  32. for (const module of formats) {
  33. archive = module.Archive.open(buffer);
  34. if (archive) {
  35. break;
  36. }
  37. }
  38. return archive;
  39. };
  40. const host = {};
  41. host.TestHost = class {
  42. constructor(window, environment) {
  43. this._window = window;
  44. this._environment = environment;
  45. this._errors = [];
  46. host.TestHost.source = host.TestHost.source || dirname('..', 'source');
  47. }
  48. async view(/* view */) {
  49. }
  50. async start() {
  51. }
  52. get window() {
  53. return this._window;
  54. }
  55. get document() {
  56. return this._window.document;
  57. }
  58. get errors() {
  59. return this._errors;
  60. }
  61. get type() {
  62. return 'Test';
  63. }
  64. environment(name) {
  65. return this._environment[name];
  66. }
  67. update() {
  68. }
  69. screen(/* name */) {
  70. }
  71. async require(id) {
  72. const file = path.join(host.TestHost.source, `${id}.js`);
  73. return await import(`file://${file}`);
  74. }
  75. worker(id) {
  76. const file = path.join(host.TestHost.source, `${id}.js`);
  77. const worker = new worker_threads.Worker(file);
  78. worker.addEventListener = (type, listener) => {
  79. worker.on(type, (message) => listener({ data: message }));
  80. };
  81. return worker;
  82. }
  83. async request(file, encoding, basename) {
  84. const pathname = path.join(basename || host.TestHost.source, file);
  85. const exists = await access(pathname);
  86. if (!exists) {
  87. throw new Error(`The file '${file}' does not exist.`);
  88. }
  89. const stats = await fs.stat(pathname);
  90. if (stats.isDirectory()) {
  91. throw new Error(`The path '${file}' is a directory.`);
  92. }
  93. if (encoding) {
  94. return await fs.readFile(pathname, encoding);
  95. }
  96. return new node.FileStream(pathname, 0, stats.size, stats.mtimeMs);
  97. }
  98. event(/* name, params */) {
  99. }
  100. exception(error /*, fatal */) {
  101. this._errors.push(error);
  102. }
  103. message() {
  104. }
  105. };
  106. host.TestHost.Context = class {
  107. constructor(host, folder, identifier, stream, entries) {
  108. this._host = host;
  109. this._folder = folder;
  110. this._identifier = identifier;
  111. this._stream = stream;
  112. this._entries = entries;
  113. }
  114. get identifier() {
  115. return this._identifier;
  116. }
  117. get stream() {
  118. return this._stream;
  119. }
  120. get entries() {
  121. return this._entries;
  122. }
  123. async request(file, encoding, base) {
  124. return this._host.request(file, encoding, base === undefined ? this._folder : base);
  125. }
  126. async require(id) {
  127. return this._host.require(id);
  128. }
  129. error(error, fatal) {
  130. this._host.exception(error, fatal);
  131. }
  132. };
  133. class CSSStyleDeclaration {
  134. constructor() {
  135. this._properties = new Map();
  136. }
  137. setProperty(name, value) {
  138. this._properties.set(name, value);
  139. }
  140. removeProperty(name) {
  141. this._properties.delete(name);
  142. }
  143. }
  144. class DOMTokenList {
  145. constructor(element) {
  146. this._element = element;
  147. }
  148. add(...tokens) {
  149. const value = this._element.getAttribute('class') || '';
  150. const set = new Set(value.split(' ').concat(...tokens));
  151. this._element.setAttribute('class', Array.from(set).filter((s) => s).join(' '));
  152. }
  153. contains(token) {
  154. const value = this._element.getAttribute('class');
  155. if (value === null || value.indexOf(token) === -1) {
  156. return false;
  157. }
  158. return value.split(' ').some((s) => s === token);
  159. }
  160. }
  161. class HTMLElement {
  162. constructor() {
  163. this._childNodes = [];
  164. this._attributes = new Map();
  165. this._style = new CSSStyleDeclaration();
  166. }
  167. get style() {
  168. return this._style;
  169. }
  170. get childNodes() {
  171. return this._childNodes;
  172. }
  173. get firstChild() {
  174. return this._childNodes.length > 0 ? this._childNodes[0] : null;
  175. }
  176. get lastChild() {
  177. const index = this._childNodes.length - 1;
  178. if (index >= 0) {
  179. return this._childNodes[index];
  180. }
  181. return null;
  182. }
  183. appendChild(node) {
  184. this._childNodes.push(node);
  185. }
  186. insertBefore(newNode, referenceNode) {
  187. const index = this._childNodes.indexOf(referenceNode);
  188. if (index !== -1) {
  189. this._childNodes.splice(index, 0, newNode);
  190. }
  191. }
  192. removeChild(node) {
  193. const index = this._childNodes.lastIndexOf(node);
  194. if (index !== -1) {
  195. this._childNodes.splice(index, 1);
  196. }
  197. }
  198. setAttribute(name, value) {
  199. this._attributes.set(name, value);
  200. }
  201. hasAttribute(name) {
  202. return this._attributes.has(name);
  203. }
  204. getAttribute(name) {
  205. return this._attributes.has(name) ? this._attributes.get(name) : null;
  206. }
  207. getElementsByClassName(name) {
  208. const elements = [];
  209. for (const node of this._childNodes) {
  210. if (node instanceof HTMLElement) {
  211. elements.push(...node.getElementsByClassName(name));
  212. if (node.classList.contains(name)) {
  213. elements.push(node);
  214. }
  215. }
  216. }
  217. return elements;
  218. }
  219. addEventListener(/* event, callback */) {
  220. }
  221. removeEventListener(/* event, callback */) {
  222. }
  223. get classList() {
  224. this._classList = this._classList || new DOMTokenList(this);
  225. return this._classList;
  226. }
  227. getBBox() {
  228. return { x: 0, y: 0, width: 10, height: 10 };
  229. }
  230. getBoundingClientRect() {
  231. return { left: 0, top: 0, width: 0, height: 0 };
  232. }
  233. scrollTo() {
  234. }
  235. focus() {
  236. }
  237. }
  238. class Document {
  239. constructor() {
  240. this._elements = {};
  241. this._documentElement = new HTMLElement();
  242. this._body = new HTMLElement();
  243. }
  244. get documentElement() {
  245. return this._documentElement;
  246. }
  247. get body() {
  248. return this._body;
  249. }
  250. createElement(/* name */) {
  251. return new HTMLElement();
  252. }
  253. createElementNS(/* namespace, name */) {
  254. return new HTMLElement();
  255. }
  256. createTextNode(/* text */) {
  257. return new HTMLElement();
  258. }
  259. getElementById(id) {
  260. let element = this._elements[id];
  261. if (!element) {
  262. element = new HTMLElement();
  263. this._elements[id] = element;
  264. }
  265. return element;
  266. }
  267. addEventListener(/* event, callback */) {
  268. }
  269. removeEventListener(/* event, callback */) {
  270. }
  271. }
  272. class Window {
  273. constructor() {
  274. this._document = new Document();
  275. }
  276. get document() {
  277. return this._document;
  278. }
  279. addEventListener(/* event, callback */) {
  280. }
  281. removeEventListener(/* event, callback */) {
  282. }
  283. requestAnimationFrame(callback) {
  284. callback();
  285. }
  286. }
  287. export class Target {
  288. constructor(item) {
  289. Object.assign(this, item);
  290. this.events = {};
  291. this.tags = new Set(this.tags);
  292. this.folder = item.type ? path.normalize(dirname('..', 'third_party' , 'test', item.type)) : process.cwd();
  293. this.assert = !this.assert || Array.isArray(this.assert) ? this.assert : [this.assert];
  294. }
  295. on(event, callback) {
  296. this.events[event] = this.events[event] || [];
  297. this.events[event].push(callback);
  298. }
  299. emit(event, data) {
  300. if (this.events && this.events[event]) {
  301. for (const callback of this.events[event]) {
  302. callback(this, data);
  303. }
  304. }
  305. }
  306. status(message) {
  307. this.emit('status', message);
  308. }
  309. async execute() {
  310. if (this.measures) {
  311. this.measures.set('name', this.name);
  312. }
  313. await zip.Archive.import();
  314. this.window = this.window || new Window();
  315. const environment = {
  316. zoom: 'none',
  317. measure: this.measures ? true : false
  318. };
  319. this.host = await new host.TestHost(this.window, environment);
  320. this.view = new view.View(this.host);
  321. this.view.options.attributes = true;
  322. this.view.options.initializers = true;
  323. const time = async (method) => {
  324. const start = process.hrtime.bigint();
  325. let err = null;
  326. try {
  327. await method.call(this);
  328. } catch (error) {
  329. err = error;
  330. }
  331. const duration = Number(process.hrtime.bigint() - start) / 1e9;
  332. if (this.measures) {
  333. this.measures.set(method.name, duration);
  334. }
  335. if (err) {
  336. throw err;
  337. }
  338. };
  339. this.status({ name: 'name', target: this.name });
  340. const errors = [];
  341. try {
  342. await time(this.download);
  343. await time(this.load);
  344. await time(this.validate);
  345. if (!this.tags.has('skip-render')) {
  346. await time(this.render);
  347. }
  348. } catch (error) {
  349. errors.push(error);
  350. }
  351. errors.push(...this.host.errors);
  352. if (errors.length === 0 && this.error) {
  353. throw new Error('Expected error.');
  354. }
  355. if (errors.length > 0 && (!this.error || errors.map((error) => error.message).join('\n') !== this.error)) {
  356. throw errors[0];
  357. }
  358. }
  359. async request(url, init) {
  360. const response = await fetch(url, init);
  361. if (!response.ok) {
  362. throw new Error(response.status.toString());
  363. }
  364. if (response.body) {
  365. const reader = response.body.getReader();
  366. const length = response.headers.has('Content-Length') ? parseInt(response.headers.get('Content-Length'), 10) : -1;
  367. let position = 0;
  368. /* eslint-disable consistent-this */
  369. const target = this;
  370. /* eslint-enable consistent-this */
  371. const stream = new ReadableStream({
  372. async start(controller) {
  373. const read = async () => {
  374. try {
  375. const result = await reader.read();
  376. if (result.done) {
  377. target.status({ name: 'download' });
  378. controller.close();
  379. } else {
  380. position += result.value.length;
  381. if (length >= 0) {
  382. const percent = position / length;
  383. target.status({ name: 'download', target: url, percent });
  384. } else {
  385. target.status({ name: 'download', target: url, position });
  386. }
  387. controller.enqueue(result.value);
  388. return await read();
  389. }
  390. } catch (error) {
  391. controller.error(error);
  392. throw error;
  393. }
  394. return null;
  395. };
  396. return read();
  397. }
  398. });
  399. return new Response(stream, {
  400. status: response.status,
  401. statusText: response.statusText,
  402. headers: response.headers
  403. });
  404. }
  405. return response;
  406. }
  407. async download(targets, sources) {
  408. targets = targets || Array.from(this.targets);
  409. sources = sources || this.source;
  410. const files = targets.map((file) => path.resolve(this.folder, file));
  411. const exists = await Promise.all(files.map((file) => access(file)));
  412. if (exists.every((value) => value)) {
  413. return;
  414. }
  415. if (!sources) {
  416. throw new Error('Download source not specified.');
  417. }
  418. let source = '';
  419. let sourceFiles = [];
  420. const match = sources.match(/^(.*?)\[(.*?)\](.*)$/);
  421. if (match) {
  422. [, source, sourceFiles, sources] = match;
  423. sourceFiles = sourceFiles.split(',').map((file) => file.trim());
  424. sources = sources && sources.startsWith(',') ? sources.substring(1).trim() : '';
  425. } else {
  426. const comma = sources.indexOf(',');
  427. if (comma === -1) {
  428. source = sources;
  429. sources = '';
  430. } else {
  431. source = sources.substring(0, comma);
  432. sources = sources.substring(comma + 1);
  433. }
  434. }
  435. await Promise.all(targets.map((target) => {
  436. const dir = path.dirname(`${this.folder}/${target}`);
  437. return fs.mkdir(dir, { recursive: true });
  438. }));
  439. const response = await this.request(source);
  440. const buffer = await response.arrayBuffer();
  441. const data = new Uint8Array(buffer);
  442. if (sourceFiles.length > 0) {
  443. this.status({ name: 'decompress' });
  444. const archive = decompress(data);
  445. for (const name of sourceFiles) {
  446. this.status({ name: 'write', target: name });
  447. if (name === '.') {
  448. const target = targets.shift();
  449. const dir = path.join(this.folder, target);
  450. /* eslint-disable no-await-in-loop */
  451. await fs.mkdir(dir, { recursive: true });
  452. /* eslint-enable no-await-in-loop */
  453. } else {
  454. const stream = archive.entries.get(name);
  455. if (!stream) {
  456. throw new Error(`Entry not found '${name}. Archive contains entries: ${JSON.stringify(Array.from(archive.entries.keys()))} .`);
  457. }
  458. const target = targets.shift();
  459. const buffer = stream.peek();
  460. const file = path.join(this.folder, target);
  461. /* eslint-disable no-await-in-loop */
  462. await fs.writeFile(file, buffer, null);
  463. /* eslint-enable no-await-in-loop */
  464. }
  465. }
  466. } else {
  467. const target = targets.shift();
  468. this.status({ name: 'write', target });
  469. await fs.writeFile(`${this.folder}/${target}`, data, null);
  470. }
  471. if (targets.length > 0 && sources.length > 0) {
  472. await this.download(targets, sources);
  473. }
  474. }
  475. async load() {
  476. const target = path.resolve(this.folder, this.targets[0]);
  477. const identifier = path.basename(target);
  478. const stat = await fs.stat(target);
  479. let context = null;
  480. if (stat.isFile()) {
  481. const stream = new node.FileStream(target, 0, stat.size, stat.mtimeMs);
  482. const dirname = path.dirname(target);
  483. context = new host.TestHost.Context(this.host, dirname, identifier, stream, new Map());
  484. } else if (stat.isDirectory()) {
  485. const entries = new Map();
  486. const file = async (pathname) => {
  487. const stat = await fs.stat(pathname);
  488. const stream = new node.FileStream(pathname, 0, stat.size, stat.mtimeMs);
  489. const name = pathname.split(path.sep).join(path.posix.sep);
  490. entries.set(name, stream);
  491. };
  492. const walk = async (dir) => {
  493. const stats = await fs.readdir(dir, { withFileTypes: true });
  494. const promises = [];
  495. for (const stat of stats) {
  496. const pathname = path.join(dir, stat.name);
  497. if (stat.isDirectory()) {
  498. promises.push(walk(pathname));
  499. } else if (stat.isFile()) {
  500. promises.push(file(pathname));
  501. }
  502. }
  503. await Promise.all(promises);
  504. };
  505. await walk(target);
  506. context = new host.TestHost.Context(this.host, target, identifier, null, entries);
  507. }
  508. const modelFactoryService = new view.ModelFactoryService(this.host);
  509. this.model = await modelFactoryService.open(context);
  510. this.view.model = this.model;
  511. }
  512. async validate() {
  513. const model = this.model;
  514. if (!model.format || (this.format && this.format !== model.format)) {
  515. throw new Error(`Invalid model format '${model.format}'.`);
  516. }
  517. if (this.producer && model.producer !== this.producer) {
  518. throw new Error(`Invalid producer '${model.producer}'.`);
  519. }
  520. if (this.runtime && model.runtime !== this.runtime) {
  521. throw new Error(`Invalid runtime '${model.runtime}'.`);
  522. }
  523. if (model.metadata && (!Array.isArray(model.metadata) || !model.metadata.every((argument) => argument.name && (argument.value || argument.value === null || argument.value === '' || argument.value === false || argument.value === 0)))) {
  524. throw new Error("Invalid model metadata.");
  525. }
  526. if (this.assert) {
  527. for (const assert of this.assert) {
  528. const parts = assert.split('==').map((item) => item.trim());
  529. const properties = parts[0].split('.');
  530. const value = JSON.parse(parts[1].replace(/\s*'|'\s*/g, '"'));
  531. let context = { model };
  532. while (properties.length) {
  533. const property = properties.shift();
  534. if (context[property] !== undefined) {
  535. context = context[property];
  536. continue;
  537. }
  538. const match = /(.*)\[(.*)\]/.exec(property);
  539. if (match && match.length === 3 && context[match[1]] !== undefined) {
  540. const array = context[match[1]];
  541. const index = parseInt(match[2], 10);
  542. if (array[index] !== undefined) {
  543. context = array[index];
  544. continue;
  545. }
  546. }
  547. throw new Error(`Invalid property path '${parts[0]}'.`);
  548. }
  549. if (context !== value) {
  550. throw new Error(`Invalid '${context}' != '${assert}'.`);
  551. }
  552. }
  553. }
  554. if (model.version || model.description || model.author || model.license) {
  555. // continue
  556. }
  557. const validateGraph = async (graph) => {
  558. /* eslint-disable no-unused-expressions */
  559. const values = new Map();
  560. const validateValue = async (value) => {
  561. if (value === null) {
  562. return;
  563. }
  564. value.name.toString();
  565. value.name.length;
  566. value.description;
  567. if (value.quantization) {
  568. if (!this.tags.has('quantization')) {
  569. throw new Error("Invalid 'quantization' tag.");
  570. }
  571. const quantization = new view.Quantization(value.quantization);
  572. quantization.toString();
  573. }
  574. if (value.type) {
  575. value.type.toString();
  576. }
  577. if (value.initializer) {
  578. value.initializer.type.toString();
  579. if (value.initializer && value.initializer.peek && !value.initializer.peek()) {
  580. await value.initializer.read();
  581. }
  582. const tensor = new base.Tensor(value.initializer);
  583. if (!this.tags.has('skip-tensor-value')) {
  584. if (tensor.encoding !== '<' && tensor.encoding !== '>' && tensor.encoding !== '|') {
  585. throw new Error(`Tensor encoding '${tensor.encoding}' is not implemented.`);
  586. }
  587. if (tensor.layout && (tensor.layout !== 'sparse' && tensor.layout !== 'sparse.coo')) {
  588. throw new Error(`Tensor layout '${tensor.layout}' is not implemented.`);
  589. }
  590. if (!tensor.empty) {
  591. if (tensor.type && tensor.type.dataType === '?') {
  592. throw new Error('Tensor data type is not defined.');
  593. } else if (tensor.type && !tensor.type.shape) {
  594. throw new Error('Tensor shape is not defined.');
  595. } else {
  596. tensor.toString();
  597. if (this.tags.has('validation')) {
  598. const size = tensor.type.shape.dimensions.reduce((a, b) => a * b, 1);
  599. if (tensor.type && tensor.type.dataType !== '?' && size < 8192) {
  600. let data_type = '?';
  601. switch (tensor.type.dataType) {
  602. case 'boolean': data_type = 'bool'; break;
  603. case 'bfloat16': data_type = 'float32'; break;
  604. case 'float8e5m2': data_type = 'float16'; break;
  605. case 'float8e5m2fnuz': data_type = 'float16'; break;
  606. case 'float8e4m3fn': data_type = 'float16'; break;
  607. case 'float8e4m3fnuz': data_type = 'float16'; break;
  608. case 'int4': data_type = 'int8'; break;
  609. default: data_type = tensor.type.dataType; break;
  610. }
  611. Target.execution = Target.execution || new python.Execution();
  612. const execution = Target.execution;
  613. const bytes = execution.invoke('io.BytesIO', []);
  614. const dtype = execution.invoke('numpy.dtype', [data_type]);
  615. const array = execution.invoke('numpy.asarray', [tensor.value, dtype]);
  616. execution.invoke('numpy.save', [bytes, array]);
  617. }
  618. }
  619. }
  620. }
  621. }
  622. } else if (value.name.length === 0) {
  623. throw new Error('Empty value name.');
  624. }
  625. if (value.name.length > 0 && value.initializer === null) {
  626. if (!values.has(value.name)) {
  627. values.set(value.name, value);
  628. } else if (value !== values.get(value.name)) {
  629. throw new Error(`Duplicate value '${value.name}'.`);
  630. }
  631. }
  632. };
  633. const signatures = Array.isArray(graph.signatures) ? graph.signatures : [graph];
  634. for (const signature of signatures) {
  635. for (const input of signature.inputs) {
  636. input.name.toString();
  637. input.name.length;
  638. for (const value of input.value) {
  639. /* eslint-disable no-await-in-loop */
  640. await validateValue(value);
  641. /* eslint-enable no-await-in-loop */
  642. }
  643. }
  644. for (const output of signature.outputs) {
  645. output.name.toString();
  646. output.name.length;
  647. if (Array.isArray(output.value)) {
  648. for (const value of output.value) {
  649. /* eslint-disable no-await-in-loop */
  650. await validateValue(value);
  651. /* eslint-enable no-await-in-loop */
  652. }
  653. }
  654. }
  655. }
  656. if (graph.metadata && (!Array.isArray(graph.metadata) || !graph.metadata.every((argument) => argument.name && argument.value !== undefined))) {
  657. throw new Error("Invalid graph metadata.");
  658. }
  659. for (const node of graph.nodes) {
  660. const type = node.type;
  661. if (!type || typeof type.name !== 'string') {
  662. throw new Error(`Invalid node type '${JSON.stringify(node.type)}'.`);
  663. }
  664. if (Array.isArray(type.nodes)) {
  665. /* eslint-disable no-await-in-loop */
  666. await validateGraph(type);
  667. /* eslint-enable no-await-in-loop */
  668. }
  669. view.Documentation.open(type);
  670. node.name.toString();
  671. node.description;
  672. if (node.metadata && (!Array.isArray(node.metadata) || !node.metadata.every((argument) => argument.name && argument.value !== undefined))) {
  673. throw new Error("Invalid node metadata.");
  674. }
  675. const attributes = node.attributes;
  676. if (attributes) {
  677. for (const attribute of attributes) {
  678. attribute.name.toString();
  679. attribute.name.length;
  680. const type = attribute.type;
  681. const value = attribute.value;
  682. if ((type === 'graph' || type === 'function') && value && Array.isArray(value.nodes)) {
  683. /* eslint-disable no-await-in-loop */
  684. await validateGraph(value);
  685. /* eslint-enable no-await-in-loop */
  686. } else {
  687. let text = new view.Formatter(attribute.value, attribute.type).toString();
  688. if (text && text.length > 1000) {
  689. text = `${text.substring(0, 1000)}...`;
  690. }
  691. /* value = */ text.split('<');
  692. }
  693. }
  694. }
  695. const inputs = node.inputs;
  696. if (Array.isArray(inputs)) {
  697. for (const input of inputs) {
  698. input.name.toString();
  699. input.name.length;
  700. if (!input.type || input.type.endsWith('*')) {
  701. for (const value of input.value) {
  702. /* eslint-disable no-await-in-loop */
  703. await validateValue(value);
  704. /* eslint-enable no-await-in-loop */
  705. }
  706. if (this.tags.has('validation')) {
  707. if (input.value.length === 1 && input.value[0].initializer) {
  708. const sidebar = new view.TensorSidebar(this.view, input);
  709. sidebar.render();
  710. }
  711. }
  712. }
  713. }
  714. }
  715. const outputs = node.outputs;
  716. if (Array.isArray(outputs)) {
  717. for (const output of node.outputs) {
  718. output.name.toString();
  719. output.name.length;
  720. if (!output.type || output.type.endsWith('*')) {
  721. for (const value of output.value) {
  722. /* eslint-disable no-await-in-loop */
  723. await validateValue(value);
  724. /* eslint-enable no-await-in-loop */
  725. }
  726. }
  727. }
  728. }
  729. if (node.chain) {
  730. for (const chain of node.chain) {
  731. chain.name.toString();
  732. chain.name.length;
  733. }
  734. }
  735. const sidebar = new view.NodeSidebar(this.view, node);
  736. sidebar.render();
  737. }
  738. const sidebar = new view.ModelSidebar(this.view, this.model, graph);
  739. sidebar.render();
  740. /* eslint-enable no-unused-expressions */
  741. };
  742. const validateTarget = async (target) => {
  743. switch (target.type) {
  744. default: {
  745. await validateGraph(target);
  746. }
  747. }
  748. };
  749. for (const module of model.modules) {
  750. /* eslint-disable no-await-in-loop */
  751. await validateTarget(module);
  752. /* eslint-enable no-await-in-loop */
  753. }
  754. const functions = model.functions || [];
  755. for (const func of functions) {
  756. /* eslint-disable no-await-in-loop */
  757. await validateTarget(func);
  758. /* eslint-enable no-await-in-loop */
  759. }
  760. }
  761. async render() {
  762. for (const graph of this.model.modules) {
  763. const signatures = Array.isArray(graph.signatures) && graph.signatures.length > 0 ? graph.signatures : [graph];
  764. for (const signature of signatures) {
  765. /* eslint-disable no-await-in-loop */
  766. await this.view.render(graph, signature);
  767. /* eslint-enable no-await-in-loop */
  768. }
  769. }
  770. }
  771. }
  772. if (!worker_threads.isMainThread) {
  773. worker_threads.parentPort.addEventListener('message', async (e) => {
  774. const message = e.data;
  775. const response = {};
  776. try {
  777. const target = new Target(message);
  778. response.type = 'complete';
  779. response.target = target.name;
  780. target.on('status', (sender, message) => {
  781. message = { type: 'status', ...message };
  782. worker_threads.parentPort.postMessage(message);
  783. });
  784. if (message.measures) {
  785. target.measures = new Map();
  786. }
  787. await target.execute();
  788. response.measures = target.measures;
  789. } catch (error) {
  790. response.type = 'error';
  791. response.error = {
  792. name: error.name,
  793. message: error.message
  794. };
  795. const cause = error.cause;
  796. if (cause) {
  797. response.error.cause = {
  798. name: cause.name,
  799. message: cause.message
  800. };
  801. }
  802. }
  803. worker_threads.parentPort.postMessage(response);
  804. });
  805. }