Skip to content

Commit c7c04bd

Browse files
committed
optimizer v3
1 parent 17bb4c2 commit c7c04bd

File tree

5 files changed

+382
-78
lines changed

5 files changed

+382
-78
lines changed

index.js

Lines changed: 29 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ const Serializer = require('./lib/serializer')
1111
const Validator = require('./lib/validator')
1212
const RefResolver = require('./lib/ref-resolver')
1313
const Location = require('./lib/location')
14+
const optimize = require('./lib/optimize')
1415

1516
let largeArraySize = 2e4
1617
let largeArrayMechanism = 'default'
@@ -27,6 +28,18 @@ const validLargeArrayMechanisms = [
2728
'json-stringify'
2829
]
2930

31+
const serializerFns = `
32+
const {
33+
asString,
34+
asInteger,
35+
asNumber,
36+
asBoolean,
37+
asDateTime,
38+
asDate,
39+
asTime,
40+
} = serializer
41+
`
42+
3043
const addComma = '!addComma && (addComma = true) || (json += \',\')'
3144

3245
function isValidSchema (schema, name) {
@@ -119,21 +132,8 @@ function build (schema, options) {
119132
const location = new Location(schema, context.rootSchemaId)
120133
const code = buildValue(context, location, 'input')
121134

122-
let contextFunctionCode
123-
124-
// If we have only the invocation of the 'anonymous0' function, we would
125-
// basically just wrap the 'anonymous0' function in the 'main' function and
126-
// and the overhead of the intermediate variable 'json'. We can avoid the
127-
// wrapping and the unnecessary memory allocation by aliasing 'anonymous0' to
128-
// 'main'
129-
if (code === 'json += anonymous0(input)') {
130-
contextFunctionCode = `
131-
${context.functions.join('\n')}
132-
const main = anonymous0
133-
return main
134-
`
135-
} else {
136-
contextFunctionCode = `
135+
let contextFunctionCode = `
136+
${serializerFns}
137137
function main (input) {
138138
let json = ''
139139
${code}
@@ -142,7 +142,8 @@ function build (schema, options) {
142142
${context.functions.join('\n')}
143143
return main
144144
`
145-
}
145+
146+
contextFunctionCode = optimize(contextFunctionCode)
146147

147148
const serializer = new Serializer(options)
148149
const validator = new Validator(options.ajv)
@@ -263,7 +264,7 @@ function buildExtraObjectPropertiesSerializer (context, location) {
263264
code += `
264265
if (/${propertyKey.replace(/\\*\//g, '\\/')}/.test(key)) {
265266
${addComma}
266-
json += serializer.asString(key) + ':'
267+
json += asString(key) + ':'
267268
${buildValue(context, propertyLocation, 'value')}
268269
continue
269270
}
@@ -278,13 +279,13 @@ function buildExtraObjectPropertiesSerializer (context, location) {
278279
if (additionalPropertiesSchema === true) {
279280
code += `
280281
${addComma}
281-
json += serializer.asString(key) + ':' + JSON.stringify(value)
282+
json += asString(key) + ':' + JSON.stringify(value)
282283
`
283284
} else {
284285
const propertyLocation = location.getPropertyLocation('additionalProperties')
285286
code += `
286287
${addComma}
287-
json += serializer.asString(key) + ':'
288+
json += asString(key) + ':'
288289
${buildValue(context, propertyLocation, 'value')}
289290
`
290291
}
@@ -504,8 +505,8 @@ function buildObject (context, location) {
504505
}
505506

506507
let functionCode = `
508+
// ${schemaRef}
507509
function ${functionName} (input) {
508-
// ${schemaRef}
509510
`
510511

511512
functionCode += `
@@ -549,8 +550,8 @@ function buildArray (context, location) {
549550
}
550551

551552
let functionCode = `
553+
// ${schemaRef}
552554
function ${functionName} (obj) {
553-
// ${schemaRef}
554555
`
555556

556557
functionCode += `
@@ -743,21 +744,21 @@ function buildSingleTypeSerializer (context, location, input) {
743744
return 'json += \'null\''
744745
case 'string': {
745746
if (schema.format === 'date-time') {
746-
return `json += serializer.asDateTime(${input})`
747+
return `json += asDateTime(${input})`
747748
} else if (schema.format === 'date') {
748-
return `json += serializer.asDate(${input})`
749+
return `json += asDate(${input})`
749750
} else if (schema.format === 'time') {
750-
return `json += serializer.asTime(${input})`
751+
return `json += asTime(${input})`
751752
} else {
752-
return `json += serializer.asString(${input})`
753+
return `json += asString(${input})`
753754
}
754755
}
755756
case 'integer':
756-
return `json += serializer.asInteger(${input})`
757+
return `json += asInteger(${input})`
757758
case 'number':
758-
return `json += serializer.asNumber(${input})`
759+
return `json += asNumber(${input})`
759760
case 'boolean':
760-
return `json += serializer.asBoolean(${input})`
761+
return `json += asBoolean(${input})`
761762
case 'object': {
762763
const funcName = buildObject(context, location)
763764
return `json += ${funcName}(${input})`

lib/optimize.js

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
'use strict'
2+
3+
const returnFnRE = /^\s+return ([.a-zA-Z0-9]+)\(\w+\)$/
4+
const fnRE = /^\s*function\s+/
5+
const fnNameRE = /^\s+function ([a-zA-Z0-9_]+) \(input\) {$/
6+
const jsonConcatRE = /^\s*json\s*\+=/
7+
const letJsonRE = /^\s*let json =/
8+
const returnJsonRE = /^\s*return json\s*$/
9+
const returnEmptyStringRE = /^\s*return '' \+/
10+
const closingCurlyBracketRE = /^\s*}\s*$/
11+
/**
12+
* @param {Array<string>} code
13+
* @returns {Array<string>}
14+
*/
15+
function optimize (raw) {
16+
let code = raw.split('\n')
17+
code = optimizeJsonConcat(code)
18+
code = optimizeLetJson(code)
19+
code = optimizeDirectReturn(code)
20+
code = optimizeReturnEmptyString(code)
21+
code = optimizeDirectAssignWrappedFns(code)
22+
return code.join('\n')
23+
}
24+
25+
function optimizeJsonConcat (code) {
26+
const optimizedJsonConcat = []
27+
28+
for (let i = 0; i < code.length; i++) {
29+
if (i > 0 && jsonConcatRE.test(code[i]) && jsonConcatRE.test(code[i - 1])) {
30+
const mergedEntry = code[i - 1] + ' +' + code[i].substring(code[i].indexOf('json +=') + 7)
31+
optimizedJsonConcat.pop() // Remove the previous entry
32+
optimizedJsonConcat.push(mergedEntry)
33+
} else {
34+
optimizedJsonConcat.push(code[i])
35+
}
36+
}
37+
38+
return optimizedJsonConcat
39+
}
40+
41+
function optimizeLetJson(code) {
42+
const optimizedLetJsonCode = []
43+
for (let i = 0; i < code.length; i++) {
44+
if (i > 0 && jsonConcatRE.test(code[i]) && letJsonRE.test(code[i - 1])) {
45+
const mergedEntry = code[i - 1] + ' +' + code[i].substring(code[i].indexOf('json +=') + 7)
46+
optimizedLetJsonCode.pop() // Remove the previous entry
47+
optimizedLetJsonCode.push(mergedEntry)
48+
} else {
49+
optimizedLetJsonCode.push(code[i])
50+
}
51+
}
52+
return optimizedLetJsonCode
53+
}
54+
55+
function optimizeDirectReturn(code) {
56+
const optimizedDirectReturnCode = []
57+
for (let i = 0; i < code.length; i++) {
58+
if (i > 0 && returnJsonRE.test(code[i]) && letJsonRE.test(code[i - 1])) {
59+
const mergedEntry = code[i].slice(0, code[i].indexOf('return') + 6) + code[i - 1].substring(code[i - 1].indexOf('let json =') + 10)
60+
optimizedDirectReturnCode.pop() // Remove the previous entry
61+
optimizedDirectReturnCode.push(mergedEntry)
62+
} else {
63+
optimizedDirectReturnCode.push(code[i])
64+
}
65+
}
66+
return optimizedDirectReturnCode
67+
}
68+
69+
function optimizeReturnEmptyString(code) {
70+
for (let i = 0; i < code.length; i++) {
71+
if (returnEmptyStringRE.test(code[i])) {
72+
code[i] = code[i].replace('return \'\' +', 'return')
73+
}
74+
}
75+
return code
76+
}
77+
78+
function optimizeDirectAssignWrappedFns (code) {
79+
const optimizedDirectAssignFns = []
80+
for (let i = 0; i < code.length; i++) {
81+
if (
82+
fnRE.test(code[i]) &&
83+
returnFnRE.test(code[i + 1]) &&
84+
closingCurlyBracketRE.test(code[i + 2])
85+
) {
86+
const serializerFnName = code[i + 1].match(returnFnRE)[1]
87+
const fnName = code[i].match(fnNameRE)[1]
88+
const whitespace = code[i].slice(0, code[i].indexOf('f'))
89+
optimizedDirectAssignFns[i] = `${whitespace}const ${fnName} = ${serializerFnName}`
90+
i += 2
91+
} else {
92+
optimizedDirectAssignFns.push(code[i])
93+
}
94+
}
95+
96+
return optimizedDirectAssignFns
97+
}
98+
module.exports = optimize

0 commit comments

Comments
 (0)