Skip to content

Commit

Permalink
Support arguments objects (incomplete)
Browse files Browse the repository at this point in the history
Can't finish this yet, as requires serializing functions.
  • Loading branch information
overlookmotel committed Oct 1, 2023
1 parent 6ca5652 commit 8b07c7e
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 46 deletions.
129 changes: 86 additions & 43 deletions lib/serialize/arguments.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,65 +6,108 @@
'use strict';

// Modules
const t = require('@babel/types');
const t = require('@babel/types'),
{isNumber} = require('is-it-type');

// Imports
const {createDependency} = require('./records.js'),
{recordIsCircular, isIntegerKey} = require('./utils.js');
const {ARGUMENTS_TYPE, registerSerializer} = require('./types.js');

// Exports

module.exports = function serializeArguments(args, record) {
const varName = record.varNode.name,
undefinedRecord = this.serializeValue(undefined),
argumentNodes = [],
defaultProps = [];
for (const key of Object.getOwnPropertyNames(args)) {
if (!isIntegerKey(key)) break;
module.exports = traceArguments;

// Fill gaps (deleted elements) with undefined
const index = key * 1;
let nextIndex = argumentNodes.length;
/**
* Trace arguments object.
* @this {Object} Serializer
* @param {Object} args - Arguments object
* @param {Object} record - Record
* @returns {number} - Type ID
*/
function traceArguments(args, record) {
this.traceProperties(args, record, argumentsShouldSkipKey);
return ARGUMENTS_TYPE;
}

/**
* Serialize arguments object.
* Use runtime function `createArguments()` to generate the arguments object.
* @this {Object} Serializer
* @param {Object} record - Record
* @returns {Object} - AST node
*/
function serializeArguments(record) {
const argumentNodes = [],
existingProps = [];
const pushUndefinedArg = (index) => {
argumentNodes.push(this.serializeValue(this.undefinedRecord));
existingProps.push({
key: index,
valRecord: this.undefinedRecord,
getRecord: undefined,
setRecord: undefined,
writable: true,
enumerable: true,
configurable: true
});
};

// Get all arguments which can be passed to `createArguments()`
let nextIndex = 0;
for (const prop of record.props) {
// Skip non-integer keys
const index = prop.key;
if (!isNumber(index)) break;

// Fill gaps (deleted properties) with `undefined`
while (index !== nextIndex) {
argumentNodes[nextIndex] = undefinedRecord.varNode;
createDependency(record, undefinedRecord, argumentNodes, nextIndex);
defaultProps[nextIndex] = {
name: `${nextIndex}`, value: undefined, writable: true, enumerable: true, configurable: true
};
pushUndefinedArg(nextIndex);
nextIndex++;
}

// Add value
const descriptor = Object.getOwnPropertyDescriptor(args, key);
let val = descriptor.value,
valRecord = this.serializeValue(val, `${varName}_${key}`, `[${key}]`);
if (recordIsCircular(valRecord)) {
// Circular reference - leave it to `wrapWithProperties()` to assign value
val = undefined;
valRecord = undefinedRecord;
const {valRecord} = prop;
if (valRecord && !valRecord.isCircular) {
// Value is present (not a getter/setter) and is not circular - add to arguments
argumentNodes.push(this.serializeValue(valRecord));
existingProps.push({
key: index,
valRecord,
getRecord: undefined,
setRecord: undefined,
writable: true,
enumerable: true,
configurable: true
});
} else {
// Add `undefined` to arguments
pushUndefinedArg(index);
}

argumentNodes[index] = valRecord.varNode;
createDependency(record, valRecord, argumentNodes, index);
defaultProps[index] = {
name: key, value: val, writable: true, enumerable: true, configurable: true
};
nextIndex++;
}

// Create call to `createArguments` function
const createArgumentsRecord = this.serializeRuntime('createArguments');
const node = t.callExpression(createArgumentsRecord.varNode, argumentNodes);
createDependency(record, createArgumentsRecord, node, 'callee');

// Wrap in properties
defaultProps.push({
name: 'length', value: argumentNodes.length, writable: true, enumerable: false, configurable: true
// Add `length` to existing props
existingProps.push({
key: 'length',
valRecord: this.traceAndSerializeGlobal(nextIndex),
getRecord: undefined,
setRecord: undefined,
writable: true,
enumerable: false,
configurable: true
});

return this.wrapWithProperties(
args, record, node, Object.prototype, defaultProps, argumentsShouldSkipKey
);
};
// Use runtime function `createArguments` to create arguments object.
// `createArguments(x, y, z)`
// TODO: Write `traceAndSerializeRuntime()`.
// Will require an extra pass to figure out what output the runtime function goes in,
// and add it to that output file (or create a new output file), after globals.
const createArgumentsNode = this.traceAndSerializeRuntime('createArguments', record); // TODO
const node = t.callExpression(createArgumentsNode, argumentNodes);

// Wrap will additional properties + delete deleted properties
return this.wrapWithProperties(node, record, this.objectPrototypeRecord, existingProps);
}
registerSerializer(ARGUMENTS_TYPE, serializeArguments);

function argumentsShouldSkipKey(key) {
// TODO: Would be better to include `Symbol.iterator` in default props
Expand Down
4 changes: 2 additions & 2 deletions lib/serialize/serializer.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const serializeMethods = require('./serialize.js'),
otherMethods = require('./other.js'),
blockMethods = require('./blocks.js'),
splitMethods = require('./split.js').methods,
serializeArguments = require('./arguments.js'),
traceArguments = require('./arguments.js'),
parseFunction = require('./parseFunction.js'),
serializeRuntime = require('./runtime.js'),
{createMangledVarNameTransform, createUnmangledVarNameTransform} = require('./varNames.js'),
Expand Down Expand Up @@ -75,7 +75,7 @@ Object.assign(
otherMethods,
blockMethods,
splitMethods,
{serializeArguments, parseFunction, serializeRuntime}
{traceArguments, parseFunction, serializeRuntime}
);

module.exports = Serializer;
2 changes: 1 addition & 1 deletion lib/serialize/trace.js
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ module.exports = {
if (objType === 'Number') return this.traceBoxedNumber(val, record);
if (objType === 'BigInt') return this.traceBoxedBigInt(val, record);
if (objType === 'Symbol') return this.traceBoxedSymbol(val, record);
// if (objType === 'Arguments') return this.traceArguments(val, record);
if (objType === 'Arguments') return this.traceArguments(val, record);
throw new Error(`Cannot serialize ${objType}s`);
},

Expand Down
2 changes: 2 additions & 0 deletions lib/serialize/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ const NO_TYPE = 0,
BOXED_NUMBER_TYPE = OBJECT_TYPE | 12,
BOXED_BIGINT_TYPE = OBJECT_TYPE | 13,
BOXED_SYMBOL_TYPE = OBJECT_TYPE | 14,
ARGUMENTS_TYPE = OBJECT_TYPE | 15,
FUNCTION_TYPE = 64,
METHOD_TYPE = FUNCTION_TYPE | 1,
GLOBAL_TYPE = 128,
Expand Down Expand Up @@ -80,6 +81,7 @@ module.exports = {
BOXED_NUMBER_TYPE,
BOXED_BIGINT_TYPE,
BOXED_SYMBOL_TYPE,
ARGUMENTS_TYPE,
FUNCTION_TYPE,
METHOD_TYPE,
GLOBAL_TYPE,
Expand Down

0 comments on commit 8b07c7e

Please sign in to comment.