243 lines
6.4 KiB
JavaScript
243 lines
6.4 KiB
JavaScript
|
/**
|
||
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||
|
*
|
||
|
* This source code is licensed under the MIT license found in the
|
||
|
* LICENSE file in the root directory of this source tree.
|
||
|
*
|
||
|
*
|
||
|
* @format
|
||
|
*/
|
||
|
'use strict';
|
||
|
|
||
|
Object.defineProperty(exports, "__esModule", {
|
||
|
value: true
|
||
|
});
|
||
|
exports.default = void 0;
|
||
|
|
||
|
var _HermesParserDecodeUTF8String = _interopRequireDefault(require("./HermesParserDecodeUTF8String"));
|
||
|
|
||
|
var _HermesParserNodeDeserializers = _interopRequireDefault(require("./HermesParserNodeDeserializers"));
|
||
|
|
||
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||
|
|
||
|
class HermesParserDeserializer {
|
||
|
// Matches StoredComment::Kind enum in JSLexer.h
|
||
|
// Matches TokenType enum in HermesParserJSSerializer.h
|
||
|
constructor(programBuffer, positionBuffer, positionBufferSize, wasmParser, options) {
|
||
|
this.programBufferIdx = void 0;
|
||
|
this.positionBufferIdx = void 0;
|
||
|
this.positionBufferSize = void 0;
|
||
|
this.locMap = void 0;
|
||
|
this.HEAPU8 = void 0;
|
||
|
this.HEAPU32 = void 0;
|
||
|
this.HEAPF64 = void 0;
|
||
|
this.options = void 0;
|
||
|
this.commentTypes = ['CommentLine', 'CommentBlock', 'InterpreterDirective'];
|
||
|
this.tokenTypes = ['Boolean', 'Identifier', 'Keyword', 'Null', 'Numeric', 'BigInt', 'Punctuator', 'String', 'RegularExpression', 'Template', 'JSXText'];
|
||
|
// Program and position buffer are memory addresses, so we must convert
|
||
|
// into indices into HEAPU32 (an array of 4-byte integers).
|
||
|
this.programBufferIdx = programBuffer / 4;
|
||
|
this.positionBufferIdx = positionBuffer / 4;
|
||
|
this.positionBufferSize = positionBufferSize;
|
||
|
this.locMap = {};
|
||
|
this.HEAPU8 = wasmParser.HEAPU8;
|
||
|
this.HEAPU32 = wasmParser.HEAPU32;
|
||
|
this.HEAPF64 = wasmParser.HEAPF64;
|
||
|
this.options = options;
|
||
|
}
|
||
|
/**
|
||
|
* Consume and return the next 4 bytes in the program buffer.
|
||
|
*/
|
||
|
|
||
|
|
||
|
next() {
|
||
|
const num = this.HEAPU32[this.programBufferIdx++];
|
||
|
return num;
|
||
|
}
|
||
|
|
||
|
deserialize() {
|
||
|
const program = {
|
||
|
type: 'Program',
|
||
|
loc: this.addEmptyLoc(),
|
||
|
body: this.deserializeNodeList(),
|
||
|
comments: this.deserializeComments()
|
||
|
};
|
||
|
|
||
|
if (this.options.tokens === true) {
|
||
|
program.tokens = this.deserializeTokens();
|
||
|
}
|
||
|
|
||
|
this.fillLocs();
|
||
|
return program;
|
||
|
}
|
||
|
/**
|
||
|
* Booleans are serialized as a single 4-byte integer.
|
||
|
*/
|
||
|
|
||
|
|
||
|
deserializeBoolean() {
|
||
|
return Boolean(this.next());
|
||
|
}
|
||
|
/**
|
||
|
* Numbers are serialized directly into program buffer, taking up 8 bytes
|
||
|
* preceded by 4 bytes of alignment padding if necessary.
|
||
|
*/
|
||
|
|
||
|
|
||
|
deserializeNumber() {
|
||
|
let floatIdx; // Numbers are aligned on 8-byte boundaries, so skip padding if we are at
|
||
|
// an odd index into the 4-byte aligned program buffer.
|
||
|
|
||
|
if (this.programBufferIdx % 2 === 0) {
|
||
|
floatIdx = this.programBufferIdx / 2;
|
||
|
this.programBufferIdx += 2;
|
||
|
} else {
|
||
|
floatIdx = (this.programBufferIdx + 1) / 2;
|
||
|
this.programBufferIdx += 3;
|
||
|
}
|
||
|
|
||
|
return this.HEAPF64[floatIdx];
|
||
|
}
|
||
|
/**
|
||
|
* Strings are serialized as a 4-byte pointer into the heap, followed
|
||
|
* by their size as a 4-byte integer. The size is only present if the
|
||
|
* pointer is non-null.
|
||
|
*/
|
||
|
|
||
|
|
||
|
deserializeString() {
|
||
|
const ptr = this.next();
|
||
|
|
||
|
if (ptr === 0) {
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
const size = this.next();
|
||
|
return (0, _HermesParserDecodeUTF8String.default)(ptr, size, this.HEAPU8);
|
||
|
}
|
||
|
/**
|
||
|
* Nodes are serialized as a 4-byte integer denoting their node kind,
|
||
|
* followed by a 4-byte loc ID, followed by serialized node properties.
|
||
|
*
|
||
|
* If the node kind is 0 the node is null, otherwise the node kind - 1 is an
|
||
|
* index into the array of node deserialization functions.
|
||
|
*/
|
||
|
|
||
|
|
||
|
deserializeNode() {
|
||
|
const nodeType = this.next();
|
||
|
|
||
|
if (nodeType === 0) {
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
const nodeDeserializer = _HermesParserNodeDeserializers.default[nodeType - 1].bind(this);
|
||
|
|
||
|
return nodeDeserializer();
|
||
|
}
|
||
|
/**
|
||
|
* Node lists are serialized as a 4-byte integer denoting the number of
|
||
|
* elements in the list, followed by the serialized elements.
|
||
|
*/
|
||
|
|
||
|
|
||
|
deserializeNodeList() {
|
||
|
const size = this.next();
|
||
|
const nodeList = [];
|
||
|
|
||
|
for (let i = 0; i < size; i++) {
|
||
|
nodeList.push(this.deserializeNode());
|
||
|
}
|
||
|
|
||
|
return nodeList;
|
||
|
}
|
||
|
/**
|
||
|
* Comments are serialized as a node list, where each comment is serialized
|
||
|
* as a 4-byte integer denoting comment type, followed by a 4-byte value
|
||
|
* denoting the loc ID, followed by a serialized string for the comment value.
|
||
|
*/
|
||
|
|
||
|
|
||
|
deserializeComments() {
|
||
|
const size = this.next();
|
||
|
const comments = [];
|
||
|
|
||
|
for (let i = 0; i < size; i++) {
|
||
|
const commentType = this.commentTypes[this.next()];
|
||
|
const loc = this.addEmptyLoc();
|
||
|
const value = this.deserializeString();
|
||
|
comments.push({
|
||
|
type: commentType,
|
||
|
loc,
|
||
|
value
|
||
|
});
|
||
|
}
|
||
|
|
||
|
return comments;
|
||
|
}
|
||
|
|
||
|
deserializeTokens() {
|
||
|
const size = this.next();
|
||
|
const tokens = [];
|
||
|
|
||
|
for (let i = 0; i < size; i++) {
|
||
|
const tokenType = this.tokenTypes[this.next()];
|
||
|
const loc = this.addEmptyLoc();
|
||
|
const value = this.deserializeString();
|
||
|
tokens.push({
|
||
|
type: tokenType,
|
||
|
loc,
|
||
|
value
|
||
|
});
|
||
|
}
|
||
|
|
||
|
return tokens;
|
||
|
}
|
||
|
/**
|
||
|
* While deserializing the AST locations are represented by
|
||
|
* a 4-byte loc ID. This is used to create a map of loc IDs to empty loc
|
||
|
* objects that are filled after the AST has been deserialized.
|
||
|
*/
|
||
|
|
||
|
|
||
|
addEmptyLoc() {
|
||
|
// $FlowExpectedError
|
||
|
const loc = {};
|
||
|
this.locMap[this.next()] = loc;
|
||
|
return loc;
|
||
|
}
|
||
|
/**
|
||
|
* Positions are serialized as a loc ID which denotes which loc it is associated with,
|
||
|
* followed by kind which denotes whether it is a start or end position,
|
||
|
* followed by line, column, and offset (4-bytes each).
|
||
|
*/
|
||
|
|
||
|
|
||
|
fillLocs() {
|
||
|
for (let i = 0; i < this.positionBufferSize; i++) {
|
||
|
const locId = this.HEAPU32[this.positionBufferIdx++];
|
||
|
const kind = this.HEAPU32[this.positionBufferIdx++];
|
||
|
const line = this.HEAPU32[this.positionBufferIdx++];
|
||
|
const column = this.HEAPU32[this.positionBufferIdx++];
|
||
|
const offset = this.HEAPU32[this.positionBufferIdx++];
|
||
|
const loc = this.locMap[locId];
|
||
|
|
||
|
if (kind === 0) {
|
||
|
loc.start = {
|
||
|
line,
|
||
|
column
|
||
|
};
|
||
|
loc.rangeStart = offset;
|
||
|
} else {
|
||
|
loc.end = {
|
||
|
line,
|
||
|
column
|
||
|
};
|
||
|
loc.rangeEnd = offset;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
exports.default = HermesParserDeserializer;
|