sptc 0.0.0 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,64 @@
1
+ const Compiler=require('./compiler')
2
+ const Interpreter=require('./interpreter')
3
+ const CompilerMacro=require('./compiler-macro')
4
+ const InterpreterMacro=require('./interpreter-macro')
5
+
6
+ const {md5}=require('../utils')
7
+
8
+ function executeSptcFile(filename, payload, option={}) {
9
+ const {__DEV__, macroOption, mockFileContent}=option
10
+
11
+ // using macro preprocessor or provides a mock content will critically degrade the performance
12
+ // if you really want to use them, specified the option.__DEV__ as true
13
+ const _option=__DEV__!==true? {}: {
14
+ mockFileContent,
15
+ contentWrapper: macroOption?
16
+ content=>executeSptcMacroFile(filename, macroOption, content):
17
+ undefined,
18
+ cacheKeyWrapper: (!mockFileContent && !macroOption)?
19
+ undefined:
20
+ filename=>md5(filename+'\n'+[mockFileContent || '', ...(macroOption.defs || [])].join('\n')),
21
+ }
22
+
23
+ const [ctx0, vm]=Compiler.compileSptcFile(filename, _option)
24
+ const [ctx, priv]=Interpreter.buildContext({...payload, ...ctx0}, option)
25
+ Interpreter.executeVm(vm, ctx, priv)
26
+ }
27
+
28
+ function executeSptcFileEx(...x) {
29
+ const e=new Promise((resolve, reject)=>{
30
+ const {end, onError, readAsBinary}=(x[2]=x[2] || {})
31
+ const buf=[]
32
+ x[2].write=(...x)=>{
33
+ buf.push(...x)
34
+ }
35
+ x[2].end=_=>{
36
+ end && end()
37
+ resolve(readAsBinary? Buffer.concat(buf): buf.join(''))
38
+ }
39
+ x[2].onError=e=>{
40
+ onError && onError(e)
41
+ reject(e)
42
+ }
43
+ executeSptcFile(...x)
44
+ })
45
+ e.catch(_=>null)
46
+ return e
47
+ }
48
+
49
+ function executeSptcMacroFile(filename, config, mockContent) {
50
+ const ast=CompilerMacro.compileSptcMacroFile(filename, mockContent)
51
+ const ctx=InterpreterMacro.buildMacroContext({filename, ...config})
52
+ return InterpreterMacro.executeMacroContext(ctx, ast)
53
+ }
54
+
55
+ module.exports={
56
+ ...Compiler,
57
+ ...Interpreter,
58
+ ...CompilerMacro,
59
+ ...InterpreterMacro,
60
+ executeSptcFile,
61
+ executeSptcFileEx,
62
+ executeSptcMacroFile,
63
+ version: require('../package.json').version
64
+ }
@@ -0,0 +1,77 @@
1
+ const {compileSptcMacroFile, MACRO_TYPES}=require('./compiler-macro')
2
+
3
+ const {
4
+ O_IFDEF,
5
+ O_STR,
6
+ O_DEFINE_CONST,
7
+ O_DEFINE_CALL,
8
+ O_CALL_CONST,
9
+ O_CALL_DEFINE,
10
+ O_DEF,
11
+ O_INCLUDE,
12
+ O_UNDEF,
13
+ }=MACRO_TYPES
14
+
15
+ function buildMacroContext({filename, ...inherits}) {
16
+ const ctx=Object.assign({
17
+ defs: new Set,
18
+ defines: {},
19
+ filename: filename || '',
20
+ }, inherits || {})
21
+ if(filename) {
22
+ ctx.filename=filename
23
+ }
24
+ if(Array.isArray(ctx.defs)) {
25
+ ctx.defs=new Set(ctx.defs)
26
+ }
27
+ return ctx
28
+ }
29
+
30
+ function execute(ctx, ast) {
31
+ let ret=''
32
+ for(let i=0; i<ast.length; i++) {
33
+ const p=ast[i]
34
+ if(p.type===O_IFDEF) {
35
+ const {match, consequent, alternate}=p
36
+ const sub_tree=ctx.defs.has(match)? consequent: alternate
37
+ ret+=execute(ctx, sub_tree)
38
+ }else if(p.type===O_STR) {
39
+ ret+=p.str
40
+ }else if(p.type===O_DEFINE_CONST) {
41
+ ctx.defines[p.cname]=p.cvalue
42
+ }else if(p.type===O_DEFINE_CALL) {
43
+ ctx.defines[p.fname]=p.fcall
44
+ }else if([O_CALL_CONST, O_CALL_DEFINE].includes(p.type)) {
45
+ if(!(p.call in ctx.defines)) {
46
+ ret+=p.source
47
+ }else{
48
+ if(p.type===O_CALL_CONST) {
49
+ ret+=ctx.defines[p.call]
50
+ }else{
51
+ ret+=ctx.defines[p.call](...p.argv)
52
+ }
53
+ }
54
+ }else if(p.type===O_DEF) {
55
+ ctx.defs.add(p.def)
56
+ }else if(p.type===O_UNDEF) {
57
+ ctx.defs.delete(p.def)
58
+ }else if(p.type===O_INCLUDE) {
59
+ const filename=path.resolve(ctx.filename+'/..', p.include)
60
+ const nctx=Object.assign({}, ctx, {filename})
61
+ ret+=executeContext(nctx)
62
+ }else{
63
+ throw new Error('unsupported node: '+JSON.stringify(p))
64
+ }
65
+ }
66
+ return ret
67
+ }
68
+
69
+ function executeMacroContext(ctx, ast) {
70
+ const {filename}=ctx
71
+ return execute(ctx, ast || compileSptcMacroFile(filename))
72
+ }
73
+
74
+ module.exports={
75
+ buildMacroContext,
76
+ executeMacroContext,
77
+ }
@@ -0,0 +1,291 @@
1
+ const _module=require('module')
2
+ const vm=require('vm')
3
+ const path=require('path')
4
+ const Compiler=require('./compiler')
5
+ const {generate_uuid}=require('../utils')
6
+
7
+ /*
8
+ const tt={x: 2, b: [2, 5], c: _=>2, d: /xx/ig}
9
+ tt.y=tt
10
+ tt.qq=[{r: tt}]
11
+ const r1={zzz: 44}
12
+ r1.rrr=r1
13
+ tt.H=r1
14
+ console.log(var_dump(tt))
15
+ */
16
+ function var_dump(...x) {
17
+ return x.map(_var_dump).join('\n')
18
+ }
19
+ function _var_dump(x) {
20
+ let i=new WeakSet, b=0, K=generate_uuid('VAR_DUMP__'), ls=[]
21
+ JSON.stringify(x, (k, v)=>{
22
+ if(v && typeof v==='object') {
23
+ if(!i.has(v)) {
24
+ v[K]=b++
25
+ i.add(v)
26
+ ls.push(v)
27
+ }else return 0
28
+ }
29
+ return v
30
+ })
31
+
32
+ i=new WeakSet
33
+ b=0
34
+ ls=[]
35
+
36
+ const e=JSON.stringify(x, (k, v)=>{
37
+ if(v && typeof v==='object') {
38
+ if(!i.has(v)) {
39
+ v[K]=b++
40
+ i.add(v)
41
+ ls.push(v)
42
+ }else{
43
+ return `[Circular] #`+v[K]
44
+ }
45
+ }
46
+ if(k===K) {
47
+ return '[*Ref] #'+v
48
+ }
49
+ if(typeof v==='function') {
50
+ return `[Function] ${v.toString()}`
51
+ }
52
+ if(v && v.constructor===RegExp) {
53
+ return `[RegExp] ${v.toString()}`
54
+ }
55
+ return v
56
+ }, 2)
57
+
58
+ for(let o of ls) {
59
+ delete o[K]
60
+ }
61
+
62
+ return e && e.replace(new RegExp(K, 'g'), '__ref_key__')
63
+ }
64
+
65
+ function buildGlobal() {
66
+ const ctx={
67
+ // nodejs
68
+ setTimeout,
69
+ setInterval,
70
+ clearTimeout,
71
+ clearInterval,
72
+ queueMicrotask,
73
+ clearImmediate,
74
+ setImmediate,
75
+
76
+ console,
77
+ Buffer,
78
+ process,
79
+
80
+ TextDecoder,
81
+ TextEncoder,
82
+ URL,
83
+ URLSearchParams,
84
+
85
+ Promise,
86
+ // require
87
+
88
+ // v8
89
+ JSON,
90
+ RegExp,
91
+ Object,
92
+ Array,
93
+ String,
94
+ Number,
95
+ Boolean,
96
+ Date,
97
+ Math,
98
+ Set, WeakSet,
99
+ Map, WeakMap,
100
+ Proxy,
101
+ Symbol,
102
+ Error,
103
+ Function,
104
+ // eval,
105
+
106
+ escape, unescape,
107
+ encodeURI, decodeURI,
108
+ encodeURIComponent, decodeURIComponent,
109
+ isNaN, isFinite,
110
+ parseInt, parseFloat,
111
+ }
112
+ ctx.global=ctx
113
+ ctx.globalThis=ctx
114
+ return ctx
115
+ }
116
+
117
+ let _patched=false
118
+ function securityPatch() {
119
+ if(_patched) return;
120
+ _patched=true
121
+ const sfunc=(new vm.Script(`(_=>null).constructor`)).runInNewContext({})
122
+ parseInt.constructor.prototype.constructor=sfunc
123
+ global.Function=sfunc
124
+ global.global=buildGlobal()
125
+ }
126
+
127
+ const NOOP=_=>null
128
+ function buildContext(ctx0, option) {
129
+
130
+ const {
131
+ write=NOOP,
132
+ end=NOOP,
133
+ onError=NOOP,
134
+ masterPriv=null,
135
+ readAsBinary=false,
136
+ }=option
137
+
138
+ const ctx=buildGlobal()
139
+
140
+ const priv=masterPriv || {
141
+ isEnd: false,
142
+ echos: [],
143
+ syncs: [],
144
+ _defers: [],
145
+ __autoload_func: null,
146
+ __autoload_vars: {},
147
+ }
148
+
149
+ Object.assign(ctx, {
150
+ end,
151
+ onError,
152
+ isMaster: !masterPriv,
153
+ })
154
+
155
+ ctx.echo=(...x)=>{
156
+ if(priv.isEnd) return;
157
+ priv.echos.push(...x)
158
+ }
159
+
160
+ ctx.flush=_=>{
161
+ if(priv.isEnd) return;
162
+ const {echos}=priv
163
+ echos.splice(0).map(x=>write(toWritable(x, readAsBinary)))
164
+ }
165
+
166
+ ctx.var_dump=(...x)=>{
167
+ for(let o of x) {
168
+ ctx.echo(var_dump(o))
169
+ }
170
+ }
171
+
172
+ ctx.Sync={
173
+ Push: (...x)=>{
174
+ if(priv.isEnd) return;
175
+ priv.syncs.push(...x)
176
+ },
177
+ }
178
+
179
+ ctx.defer=fn=>{
180
+ if(priv.isEnd) return;
181
+ priv._defers.push(fn)
182
+ }
183
+
184
+ try{
185
+ ctx.require=_module.createRequire(ctx0.__filefullname)
186
+ }catch(e) {
187
+ ctx.require=require
188
+ }
189
+
190
+ ctx._exports={}
191
+ ctx.exports=o=>{
192
+ Object.assign(ctx._exports, o)
193
+ }
194
+
195
+ ctx.eval=code=>(new vm.Script(code)).runInNewContext(ctx)
196
+
197
+ ctx.include=(inc_filename, payload={})=>{
198
+ const inc=path.resolve(ctx0.__dirname, inc_filename)
199
+ const [_ctx0, _vm]=Compiler.compileSptcFile(inc)
200
+ const [_ctx, _]=buildContext({...payload, ..._ctx0}, {
201
+ write: ctx.write,
202
+ // end: ctx.end,
203
+ onError: ctx.onError,
204
+ masterPriv: priv,
205
+ readAsBinary,
206
+ })
207
+ const ret=executeVm(_vm, _ctx, priv)
208
+ return [_ctx._exports, x=>ret.constructor('return '+x)()]
209
+ }
210
+
211
+ ctx.__autoload=fn=>{
212
+ priv.__autoload_func=fn
213
+ }
214
+
215
+ let pctx=Object.assign({}, ctx0, ctx)
216
+ pctx=new Proxy(pctx, {
217
+ get: (target, prop, receiver)=>{
218
+ if(false===prop in target) {
219
+ if(prop in priv.__autoload_vars) {
220
+ return priv.__autoload_vars[prop]
221
+ }
222
+ if(priv.__autoload_func) {
223
+ let inc_fn=priv.__autoload_func(prop)
224
+ if(inc_fn) {
225
+ const [{default: d}, q]=ctx.include(inc_fn)
226
+ return priv.__autoload_vars[prop]=d || q(prop)
227
+ }
228
+ }
229
+ }
230
+ return target[prop]
231
+ },
232
+ })
233
+
234
+ return [pctx, priv]
235
+ }
236
+
237
+ function toWritable(x, asBinary=false) {
238
+ if(x && [Uint8Array, Buffer].includes(x.constructor)) {
239
+ return asBinary? x: Buffer.from(x).toString('utf8')
240
+ }
241
+ const str=(_=>{
242
+ if(typeof x==='string') return x
243
+ if(typeof x==='undefined') return 'undefined'
244
+ if(typeof x==='number') {
245
+ if(isNaN(x))return 'NaN'
246
+ if(x===Infinity) return 'Infinity'
247
+ if(x===-Infinity) return '-Infinity'
248
+ }
249
+ return JSON.stringify(x)+''
250
+ })()
251
+ return asBinary? Buffer.from(str): str
252
+ }
253
+
254
+ function executeVm(vm, ctx, priv) {
255
+ const {onError, end, flush, isMaster}=ctx
256
+ let ret
257
+ try{
258
+ ret=vm.runInNewContext(ctx)
259
+ }catch(e) {
260
+ onError(e)
261
+ }
262
+ ; (async _=>{
263
+
264
+ try{
265
+ const syncs=priv.syncs.length? Promise.all(priv.syncs): null
266
+ await Promise.resolve(syncs)
267
+ flush()
268
+ end()
269
+ if(isMaster) priv.isEnd=true
270
+ }catch(e) {
271
+ onError(e)
272
+ }
273
+
274
+ for(let fn of priv._defers.splice(0)) {
275
+ try{
276
+ fn()
277
+ }catch(e) {
278
+ onError(e)
279
+ }
280
+ }
281
+
282
+ })()
283
+ return ret
284
+ }
285
+
286
+
287
+ module.exports={
288
+ buildContext,
289
+ executeVm,
290
+ securityPatch,
291
+ }
package/package.json CHANGED
@@ -1,9 +1,16 @@
1
1
  {
2
2
  "name": "sptc",
3
- "version": "0.0.0",
3
+ "version": "0.0.2",
4
4
  "description": "Simple Pretreat Toolkit CLI",
5
5
  "main": "index.js",
6
6
  "engines": {
7
7
  "node": ">=12"
8
+ },
9
+ "bin": {
10
+ "sptc": "./bin/sptc.js",
11
+ "sptcd": "./bin/sptcd.js"
12
+ },
13
+ "dependencies": {
14
+ "commander": "^10.0.1"
8
15
  }
9
16
  }
package/tests/bin.sh ADDED
@@ -0,0 +1,9 @@
1
+
2
+ echo ============================
3
+ echo ============================
4
+ echo ============================
5
+ echo ============================
6
+ echo ============================
7
+ echo Testing engine:
8
+
9
+ node $0/../engine/test
@@ -0,0 +1,7 @@
1
+ <?js
2
+
3
+ class TestModel{
4
+ static test() {
5
+ return 'YYYY'+require(__dirname+'/m1').A()
6
+ }
7
+ }
@@ -0,0 +1,58 @@
1
+ <?js
2
+ /*
3
+ function test(key, fn) {
4
+ console.log('--> '+key+' :')
5
+ try{
6
+ const p=[GLOBAL, THIS, TOP].indexOf(o)
7
+ console.log(p<0? '-- pass --': (' -->'+['GLOBAL', 'this', 'globalThis'][p]))
8
+ }catch(e) {
9
+ console.log('-- pass --')
10
+ }
11
+ }
12
+
13
+
14
+ test('nodeGlobal', _=>parseInt.constructor('return global')())
15
+ test('nodeGlobalThis', _=>parseInt.constructor('return this')())
16
+ test('realGlobal', _=>Function('return global')())
17
+ test('realGlobalThis', _=>Function('return this')())
18
+ test('controllableGlobal', _=>global)
19
+ test('currentContext', _=>globalThis)
20
+ test('currentThis', _=>this)
21
+
22
+ */
23
+
24
+
25
+ #ifdef W1
26
+ console.log('W1 defined!')
27
+ process.exit()
28
+ #endif
29
+
30
+
31
+ console.log(_x)
32
+ function sleep(t) {
33
+ return new Promise(r=>setTimeout(r, t))
34
+ }
35
+
36
+ __autoload(varname=>{
37
+ if(varname==='TestModel') return './TestModel.sjs'
38
+ })
39
+
40
+ const [v, _]=include('./ee.sjs', {P: 55})
41
+ echo('['+v.ee+']')
42
+
43
+ Sync.Push((async _=>{
44
+ echo(aa, 'LLL')
45
+ flush()
46
+ echo(1919)
47
+ var_dump({x: 3})
48
+ console.log(">>", eval+'')
49
+ defer(_=>{
50
+ console.log('defer')
51
+ })
52
+ flush()
53
+ await sleep(2e3)
54
+ echo('SLEEP=1e3')
55
+ setTimeout(_=>{
56
+ echo(2929)
57
+ }, 2e3)
58
+ })())
@@ -0,0 +1,17 @@
1
+ <?js
2
+ exports({
3
+ ee: 444,
4
+ })
5
+ echo('---EE--', P*1e8)
6
+ defer(_=>{
7
+ console.log("::EE<<")
8
+ })
9
+
10
+ echo('----------->', TestModel.test())
11
+
12
+ Sync.Push(new Promise(r=>{
13
+ setTimeout(_=>{
14
+ echo('xxxxxxxxx')
15
+ r()
16
+ }, 2e3)
17
+ }))
@@ -0,0 +1,20 @@
1
+ exports.A=_=>'m1 -> A'
2
+
3
+ console.log('global.XXX - global.YYY', global.XXX, global.YYY)
4
+
5
+ function test(key, fn) {
6
+ console.log('--> '+key+' :')
7
+ try{
8
+ console.log(fn())
9
+ }catch(e) {
10
+ console.log(' -- pass --')
11
+ }
12
+ }
13
+
14
+ test('nodeGlobal', _=>parseInt.constructor('return global')())
15
+ test('nodeGlobalThis', _=>parseInt.constructor('return this')())
16
+ test('realGlobal', _=>Function('return global')())
17
+ test('realGlobalThis', _=>Function('return this')())
18
+ test('controllableGlobal', _=>global)
19
+ test('currentContext', _=>globalThis)
20
+ test('currentThis', _=>this)
@@ -0,0 +1,6 @@
1
+ <?js
2
+ #ifdef XX
3
+ echo('XX defined')
4
+ #endif
5
+
6
+ echo(333)
@@ -0,0 +1,16 @@
1
+ const {
2
+ executeSptcMacroFile,
3
+ }=require('../../engine')
4
+
5
+ console.log(executeSptcMacroFile('aa.js', {defs: ['XX']}, `
6
+ #ifdef XX
7
+ mkmk
8
+ #def yy
9
+ #endif
10
+
11
+ #ifdef yy
12
+ yy is defined
13
+ #else
14
+ yy is undefined
15
+ #endif
16
+ `))
@@ -0,0 +1,33 @@
1
+ const {
2
+ executeSptcFile,
3
+ executeSptcFileEx,
4
+
5
+ securityPatch,
6
+ }=require('../../engine')
7
+
8
+ global.XXX=22
9
+
10
+ securityPatch()
11
+
12
+ global.YYY=22
13
+
14
+ /*
15
+ executeSptcFile(__dirname+'/a.sjs', {aa: 'AAA', GLOBAL: global, THIS: this, TOP: globalThis}, {
16
+ write: x=>process.stdout.write(x),
17
+ end: _=>console.log('>>end'),
18
+ onError: e=>console.log(e),
19
+ })
20
+ */
21
+
22
+ const option={
23
+ __DEV__: true,
24
+ macroOption: {
25
+ //defs: ['W1'],
26
+ },
27
+ }
28
+ executeSptcFileEx(__dirname+'/a.sjs', {aa: 'AAA', GLOBAL: global, THIS: this, TOP: globalThis}, option).then(out=>{
29
+ console.log(out, '<<END')
30
+ }, e=>{
31
+ console.log(e)
32
+ })
33
+
@@ -0,0 +1,17 @@
1
+ <?js
2
+ echo($_REQUEST_FILE)
3
+ echo('aaaa<pre>')
4
+ var_dump($_GLOBAL, this)
5
+
6
+ function sleep(t) {
7
+ return new Promise(r=>setTimeout(r, t))
8
+ }
9
+
10
+ Sync.Push((async _=>{
11
+ for(let i=0; i<10; i++) {
12
+ echo(i*1e8+'<br/>')
13
+ flush()
14
+ await sleep(1e3)
15
+ }
16
+ echo('--done--')
17
+ })())
package/utils/base.js ADDED
@@ -0,0 +1,65 @@
1
+
2
+ const fs=require('fs')
3
+ const crypto=require('crypto')
4
+ const path=require('path')
5
+
6
+ function readTextFile(filename) {
7
+ return fs.readFileSync(filename, 'utf8')
8
+ }
9
+
10
+ function mtime(filename) {
11
+ return fs.statSync(filename).mtime.getTime()
12
+ }
13
+
14
+ let _g=0
15
+ function generate_uuid(prefix='') {
16
+ return [
17
+ prefix,
18
+ (_g=(_g+1)%1e8).toString(36),
19
+ (Date.now()+Math.random()).toString(36),
20
+ ].join('_')
21
+ }
22
+
23
+ function md5(str) {
24
+ return crypto.createHash('md5').update(str).digest('hex')
25
+ }
26
+
27
+ function safe_path(ref, dir) {
28
+ return path.normalize((ref? ref+'/': '')+path.normalize(dir))
29
+ }
30
+
31
+ function fileExists(fn) {
32
+ try{
33
+ return fs.statSync(fn).isFile()
34
+ }catch(e) {}
35
+ }
36
+
37
+ function getExtension(filename) {
38
+ return path.parse(filename).ext
39
+ }
40
+
41
+ function getLocalIpv4Addresses() {
42
+ const os=require('os')
43
+ let rr={'127.0.0.1': 1}
44
+ try{
45
+ const ii=os.networkInterfaces()
46
+ for(let a in ii) {
47
+ ii[a].map(x=>{
48
+ if(!x.family.match(/IPV4/i)) return;
49
+ rr[x.address]=1
50
+ })
51
+ }
52
+ }catch(e) {}
53
+ return Object.keys(rr)
54
+ }
55
+
56
+ module.exports={
57
+ readTextFile,
58
+ mtime,
59
+ md5,
60
+ safe_path,
61
+ fileExists,
62
+ generate_uuid,
63
+ getExtension,
64
+ getLocalIpv4Addresses,
65
+ }