import Scope , { ScopeData , createScope } from '../Model/Scope' ; import { Parser } from "acorn" ; import { Messages , MessageItem , InterruptThrowError , InterruptThrowReferenceError , InterruptThrowSyntaxError , } from "../Model/Message" ; import { Node , ESTree } from "../Model/Node" ; import { ClosureHandler , BaseClosure , CaseItem , ReturnStringClosure , SwitchCaseClosure } from '../Model/Closure' ; import { Break , Continue , DefaultCase , EmptyStatementReturn , GlobalScopeName , RootScopeName , SuperScopeName , WithScopeName , createSymbolFunc , isSymbol , storeKey } from '../Model/Symbols' ; import { BreakLabel , ContinueLabel , Return } from '../Model/TokenClass' ; import { isFunction } from '../util' ; import { createArrayClass } from '../Model/Array' ; import { MacroTaskList } from '../Model/Task' ; import { createPromiseClass } from '../Model/Promise' ; export let MyParser = Parser . extend ( require ( 'acorn-class-fields' ) , require ( 'acorn-static-class-features' ) , require ( 'acorn-jsx' ) ( ) ) ; const version = "%VERSION%" ; type Getter = ( ) => any ; type ECMA_VERSION = 3 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 2015 | 2016 | 2017 | 2018 | 2019 | 2020 ; interface Options { ecmaVersion ? : ECMA_VERSION ; /**
     * 沙箱执行超时时间
     * 默认为0，不限制大小
     */ timeout ? : number ; /**
     * 根作用域，沙箱环境中顶级作用域中访问的this变量，
     * 例如，浏览器环境中的rootContext就是window
     */ rootContext ? : Context | null ; /**
     * 方法中的根作用域，如果一个没有绑定this变量的方法被调用，
     * 那么这个方法中的this访问是什么呢？通过这个配置指定
     * ```javascript
     * function func(){
     * 		return this;
     * }
     * func()
     * ```
     */ globalContextInFunction ? : any ; /**
     * 在一个Interpreter实例化之后立即执行
     * 可以用来自定义这个Interpreter实例的任何操作，高阶用法
     * 最常用的，你可以console一下这个实例的globalScope。会加深对这个沙箱环境的理解
     */ _initEnv ? : ( this : Interpreter ) => void ; } interface CollectDeclarations { [ key ] : undefined | BaseClosure ; } /**
 * 词法环境
 */ type LexContext = { [ key ] : { kind : 'const' | 'let' ; init : boolean ; } ; } ; type Context = { [ prop ] : any ; [ prop ] : any ; } ; interface GeneratorReflection { getOptions ( ) : Readonly < Options > ; getCurrentScope ( ) : Scope ; getGlobalScope ( ) : Scope ; getCurrentContext ( ) : Context ; getExecStartTime ( ) : number ; } export function internalFunction ( reflection , ... params ) { if ( ! ( reflection instanceof InternalInterpreterReflection ) ) { throw new Error ( "Illegal call" ) ; } const instance = reflection . generator ( ) ; const code = params . pop ( ) ; const interpreter = new Interpreter ( instance . getGlobalScope ( ) , instance . getOptions ( ) ) ; const wrapCode = `
		    (function anonymous(${ params . join ( "," ) }){
		        ${ code }
		    });
		    ` ; return interpreter . evaluate ( wrapCode ) ; } Object . defineProperty ( internalFunction , "__IS_FUNCTION_FUNC" , { value : true , writable : false , enumerable : false , configurable : false , } ) ; export class InternalInterpreterReflection { protected interpreter : Interpreter ; constructor ( interpreter ) { this . interpreter = interpreter ; } generator ( ) { const interpreter = this . interpreter ; function getCurrentScope ( this ) { return this . getCurrentScope ( ) ; } function getGlobalScope ( this ) { return this . getGlobalScope ( ) ; } function getCurrentContext ( this ) { return this . getCurrentContext ( ) ; } return { getOptions : interpreter . getOptions . bind ( interpreter ) , getCurrentScope : getCurrentScope . bind ( interpreter ) , getGlobalScope : getGlobalScope . bind ( interpreter ) , getCurrentContext : getCurrentContext . bind ( interpreter ) , getExecStartTime : interpreter . getExecStartTime . bind ( interpreter ) , } ; } } function internalEval ( reflection , code ? , useGlobalScope = true ) { if ( ! ( reflection instanceof InternalInterpreterReflection ) ) { throw new Error ( "Illegal call" ) ; } if ( typeof code !== "string" ) return code ; if ( ! code ) return void 0 ; const instance = reflection . generator ( ) ; const opts = instance . getOptions ( ) ; const options = { timeout : opts . timeout , _initEnv : function ( this ) { if ( ! useGlobalScope ) { this . setCurrentContext ( instance . getCurrentContext ( ) ) ; } this . execStartTime = instance . getExecStartTime ( ) ; this . execEndTime = this . execStartTime ; } , } ; const currentScope = useGlobalScope ? instance . getGlobalScope ( ) : instance . getCurrentScope ( ) ; const interpreter = new Interpreter ( currentScope , options ) ; return interpreter . evaluate ( code ) ; } Object . defineProperty ( internalEval , "__IS_EVAL_FUNC" , { value : true , writable : false , enumerable : false , configurable : false , } ) ; /**
 * scope chain
 *
 * superScope
 *     ↓
 * rootScope
 *     ↓
 * globalScope
 *     ↓
 * functionScope
 *
 */ function createRootContext ( data ) { return Object . create ( data ) ; } const BuildInObjects = { NaN , Infinity , undefined , Object , String , Boolean , Number , Date , RegExp , Error , URIError , TypeError , RangeError , SyntaxError , ReferenceError , Math , parseInt , parseFloat , isNaN , isFinite , decodeURI , decodeURIComponent , encodeURI , encodeURIComponent , escape , unescape , eval : internalEval , Function : internalFunction , } ; if ( typeof JSON !== "undefined" ) { BuildInObjects . JSON = JSON ; } if ( typeof Set !== "undefined" ) { BuildInObjects . Set = Set ; } if ( typeof Map !== "undefined" ) { BuildInObjects . Map = Map ; } if ( typeof Proxy !== "undefined" ) { BuildInObjects . Proxy = Proxy ; } if ( typeof WeakMap !== "undefined" ) { BuildInObjects . WeakMap = WeakMap ; } if ( typeof WeakSet !== "undefined" ) { BuildInObjects . WeakSet = WeakSet ; } if ( typeof Reflect !== "undefined" ) { BuildInObjects . Reflect = Reflect ; } export class Interpreter extends ClosureHandler { static readonly version : string = version ; static readonly eval = internalEval ; static readonly Function = internalFunction ; static ecmaVersion : ECMA_VERSION = 2018 ; static globalContextInFunction : any = void 0 ; static global : Context = Object . create ( null ) ; protected value : any ; protected context : Context | Scope ; protected globalContext : Context ; protected source : string ; protected sourceList : string [ ] = [ ] ; protected currentScope : Scope ; protected globalScope : Scope ; protected currentContext : Context ; protected options : Options ; protected callStack : string [ ] ; protected taskManager : MacroTaskList ; protected collectDeclVars : CollectDeclarations = Object . create ( null ) ; protected collectDeclFuncs : CollectDeclarations = Object . create ( null ) ; protected collectDeclLex : LexContext [ ] = [ ] ; /**
     * 用于临时存放函数声明的scope，函数声明执行时会用它更新函数体
     */ protected _functionVarScope : Scope ; protected isVarDeclMode : boolean = false ; protected lastExecNode : Node | null = null ; protected isRunning : boolean = false ; protected execStartTime : number ; protected execEndTime : number ; constructor ( context = Interpreter . global , options = { } ) { super ( ) ; this . options = { ecmaVersion : options . ecmaVersion || Interpreter . ecmaVersion , timeout : options . timeout || 0 , rootContext : options . rootContext , globalContextInFunction : options . globalContextInFunction === undefined ? Interpreter . globalContextInFunction : options . globalContextInFunction , _initEnv : options . _initEnv , } ; this . taskManager = new MacroTaskList ( ) ; this . context = context || Object . create ( null ) ; this . callStack = [ ] ; this . initEnvironment ( this . context ) ; } param ctx protected initEnvironment ( ctx ) { let scope ; if ( ctx instanceof Scope ) { scope = ctx ; } else { let rootScope = null ; const superScope = this . createSuperScope ( ctx ) ; if ( this . options . rootContext ) { rootScope = new Scope ( createRootContext ( this . options . rootContext ) , superScope , RootScopeName ) ; } scope = new Scope ( ctx , rootScope || superScope , GlobalScopeName ) ; } this . globalScope = scope ; let smbl ; this . globalScope . data [ 'Symbol' ] = smbl = createSymbolFunc ( ) ; this . globalScope . data [ 'Array' ] = createArrayClass ( smbl ) ; this . globalScope . data [ 'setTimeout' ] = ( fn , delay , ... args ) => { this . taskManager . queue ( ( ) => { fn . apply ( null , args ) ; } , delay ) ; } ; this . globalScope . data [ 'queueMicrotask' ] = ( fn ) => { this . taskManager . microQueue ( fn ) ; } ; this . globalScope . data [ 'Promise' ] = createPromiseClass ( this . taskManager ) ; this . currentScope = this . globalScope ; this . globalContext = scope . data ; this . currentContext = scope . data ; this . collectDeclVars = Object . create ( null ) ; this . collectDeclFuncs = Object . create ( null ) ; this . collectDeclLex = [ ] ; this . execStartTime = Date . now ( ) ; this . execEndTime = this . execStartTime ; const _initEnv = this . options . _initEnv ; if ( _initEnv ) { _initEnv . call ( this ) ; } } /**
     * 包含块级作用域的语句声明之前执行。创建一个新的hash，记录块作用域内声明的let/const变量
     */ protected blockDeclareStart ( ) { this . collectDeclLex . push ( Object . create ( null ) ) ; } /**
     * 包含块级作用域的语句声明之后执行，退栈，获取该块作用域声明的let/const变量
     */ protected blockDeclareEnd ( ) { let lexDeclInThisBlock = this . collectDeclLex . pop ( ) ; let lexDeclared ; let lexNames = Object . getOwnPropertyNames ( lexDeclInThisBlock ) ; if ( lexNames . length ) { lexDeclared = Object . create ( null ) ; lexNames . forEach ( key => { lexDeclared ! [ key ] = { init : false , kind : lexDeclInThisBlock ! [ key ] . kind } ; } ) ; } else { lexDeclared = null ; } return lexDeclared ; } getExecStartTime ( ) { return this . execStartTime ; } getExecutionTime ( ) { return this . execEndTime - this . execStartTime ; } setExecTimeout ( timeout = 0 ) { this . options . timeout = timeout ; } getOptions ( ) { return this . options ; } protected getGlobalScope ( ) { return this . globalScope ; } protected getCurrentScope ( ) { return this . currentScope ; } protected getCurrentContext ( ) { return this . currentContext ; } protected isInterruptThrow < T > ( err ) { return ( err instanceof InterruptThrowError || err instanceof InterruptThrowReferenceError || err instanceof InterruptThrowSyntaxError ) ; } protected createSuperScope ( ctx ) { let data = { ... BuildInObjects , } ; const buildInObjectKeys = Object . keys ( data ) ; buildInObjectKeys . forEach ( key => { if ( key in ctx ) { delete data [ key ] ; } } ) ; return new Scope ( data , null , SuperScopeName ) ; } protected setCurrentContext ( ctx ) { this . currentContext = ctx ; } protected setCurrentScope ( scope ) { this . currentScope = scope ; } evaluate ( code = "" ) { let node ; if ( ! code ) return ; node = MyParser . parse ( code , { ranges : true , locations : true , ecmaVersion : this . options . ecmaVersion || Interpreter . ecmaVersion , } ) ; return this . evaluateNode ( node as ESTree . Program , code ) ; } appendCode ( code ) { return this . evaluate ( code ) ; } protected evaluateNode ( node , source = "" ) { this . value = void 0 ; this . source = source ; this . sourceList . push ( source ) ; this . isRunning = true ; this . execStartTime = Date . now ( ) ; this . execEndTime = this . execStartTime ; this . collectDeclVars = Object . create ( null ) ; this . collectDeclFuncs = Object . create ( null ) ; this . collectDeclLex = [ ] ; const currentScope = this . getCurrentScope ( ) ; const currentContext = this . getCurrentContext ( ) ; const labelStack = currentScope . labelStack . concat ( [ ] ) ; const callStack = this . callStack . concat ( [ ] ) ; const reset = ( ) => { this . setCurrentScope ( currentScope ) ; this . setCurrentContext ( currentContext ) ; currentScope . labelStack = labelStack ; this . callStack = callStack ; } ; try { const bodyClosure = this . createClosure ( node ) ; this . addDeclarationsToScope ( this . collectDeclVars , this . collectDeclFuncs , this . getCurrentScope ( ) ) ; let currScope = this . getCurrentScope ( ) ; this . _functionVarScope = currScope ; bodyClosure ( ) ; } catch ( e ) { throw e ; } finally { reset ( ) ; this . execEndTime = Date . now ( ) ; } this . isRunning = false ; return this . getValue ( ) ; } protected createErrorMessage ( msg , value , node ? ) { let message = msg [ 1 ] . replace ( "%0" , String ( value ) ) ; if ( node !== null ) { message += this . getNodePosition ( node || this . lastExecNode ) ; } return message ; } protected createError < T > ( message , error ) { return new error ( message ) ; } protected createThrowError < T > ( message , error ) { return this . createError ( message , error ) ; } protected createInternalThrowError < T extends MessageItem > ( msg , value , node ? ) { return this . createError ( this . createErrorMessage ( msg , value , node ) , msg [ 2 ] ) ; } protected checkTimeout ( ) { if ( ! this . isRunning ) return false ; const timeout = this . options . timeout || 0 ; const now = Date . now ( ) ; if ( now - this . execStartTime > timeout ) { return true ; } return false ; } protected getNodePosition ( node ) { if ( node ) { const errorCode = "" ; return node . loc ? ` [${ node . loc . start . line }:${ node . loc . start . column }]${ errorCode }` : "" ; } return "" ; } protected createClosure ( node ) { let closure = this . getClosure ( node ) ; if ( ! closure ) { console . info ( '====> ' , node ) ; throw this . createInternalThrowError ( Messages . NodeTypeSyntaxError , node . type , node ) ; } return ( ... args ) => { const timeout = this . options . timeout ; if ( timeout && timeout > 0 && this . checkTimeout ( ) ) { throw this . createInternalThrowError ( Messages . ExecutionTimeOutError , timeout , null ) ; } this . lastExecNode = node ; return closure ! ( ... args ) ; } ; } protected safeObjectGet ( obj , key , node ) { if ( isSymbol ( key ) ) { return obj [ storeKey ( key ) ] ; } return obj [ key ] ; } param node protected createCallFunctionGetter ( node ) { switch ( node . type ) { case "MemberExpression" : const objectGetter = this . createClosure ( node . object ) ; const keyGetter = this . createMemberKeyGetter ( node ) ; const source = this . source ; return ( ) => { const obj = objectGetter ( ) ; const key = keyGetter ( ) ; const func = this . safeObjectGet ( obj , key , node ) ; if ( ! func || ! isFunction ( func ) ) { const name = source . slice ( node . start , node . end ) ; throw this . createInternalThrowError ( Messages . FunctionUndefinedReferenceError , name , node ) ; } if ( func . __IS_EVAL_FUNC ) { return ( code ? ) => { return ( func as typeof internalEval ) ( new InternalInterpreterReflection ( this ) , code , true ) ; } ; } if ( func . __IS_FUNCTION_FUNC ) { return ( ... args ) => { return ( func as typeof internalFunction ) ( new InternalInterpreterReflection ( this ) , ... args ) ; } ; } return func . bind ( obj ) ; } ; default : const closure = this . createClosure ( node ) ; return ( ) => { let name = "" ; if ( node . type === "Identifier" ) { name = node . name ; } const func = closure ( ) ; if ( ! func || ! isFunction ( func ) ) { throw this . createInternalThrowError ( Messages . FunctionUndefinedReferenceError , name , node ) ; } if ( node . type === "Identifier" && func . __IS_EVAL_FUNC && name === "eval" ) { return ( code ? ) => { const scope = this . getScopeFromName ( name , this . getCurrentScope ( ) ) ; const useGlobalScope = scope . name === SuperScopeName || scope . name === GlobalScopeName || scope . name === RootScopeName ; return ( func as typeof internalEval ) ( new InternalInterpreterReflection ( this ) , code , ! useGlobalScope ) ; } ; } if ( func . __IS_EVAL_FUNC ) { return ( code ? ) => { return ( func as typeof internalEval ) ( new InternalInterpreterReflection ( this ) , code , true ) ; } ; } if ( func . __IS_FUNCTION_FUNC ) { return ( ... args ) => { return ( func as typeof internalFunction ) ( new InternalInterpreterReflection ( this ) , ... args ) ; } ; } let ctx = this . options . globalContextInFunction ; if ( node . type === "Identifier" ) { const scope = this . getIdentifierScope ( node ) ; if ( scope . name === WithScopeName ) { ctx = scope . data ; } } return func . bind ( ctx ) ; } ; } } protected getIdentifierScope ( node ) { const currentScope = this . getCurrentScope ( ) ; const scope = this . getScopeFromName ( node . name , currentScope ) ; return scope ; } protected getVariableName ( node ) { if ( node . type === "Identifier" ) { return node . name ; } else { throw this . createInternalThrowError ( Messages . VariableTypeSyntaxError , node . type , node ) ; } } param data param name param node protected assertVariable ( data , name , node ) { if ( data === this . globalScope . data && ! ( name in data ) ) { throw this . createInternalThrowError ( Messages . VariableUndefinedReferenceError , name , node ) ; } } protected createParamNameGetter ( node ) { if ( node . type === "Identifier" ) { return ( ) => node . name ; } else if ( node . type === 'RestElement' ) { return this . createParamNameGetter ( node . argument ) ; } else { throw this . createInternalThrowError ( Messages . ParamTypeSyntaxError , node . type , node ) ; } } protected createObjectKeyGetter ( node ) { let getter ; if ( node . type === "Identifier" ) { getter = ( ) => node . name ; } else { getter = this . createClosure ( node ) ; } return function ( ) { return getter ( ) ; } ; } protected createMemberKeyGetter ( node ) { return node . computed ? this . createClosure ( node . property ) : this . createObjectKeyGetter ( node . property ) ; } param node protected createLeftObjectGetter ( node ) { switch ( node . type ) { case 'Identifier' : return ( ) => { let name = node . name ; let scope = this . getScopeFromName ( name , this . getCurrentScope ( ) , true ) ; if ( scope . lexDeclared && scope . lexDeclared [ name ] && scope . lexDeclared [ name ] . kind == 'const' ) { if ( scope . lexDeclared [ name ] . init === false ) { scope . lexDeclared [ name ] . init = true ; } else { throw this . createInternalThrowError ( Messages . ConstChangeError , name , node ) ; } } return scope . data ; } ; case "MemberExpression" : return this . createClosure ( node . object ) ; default : throw this . createInternalThrowError ( Messages . AssignmentTypeSyntaxError , node . type , node ) ; } } protected createObjectGetter ( node ) { switch ( node . type ) { case "Identifier" : return ( ) => this . getScopeDataFromName ( node . name , this . getCurrentScope ( ) ) ; case "MemberExpression" : return this . createClosure ( node . object ) ; default : throw this . createInternalThrowError ( Messages . AssignmentTypeSyntaxError , node . type , node ) ; } } protected createNameGetter ( node ) { switch ( node . type ) { case "Identifier" : return ( ) => node . name ; case "MemberExpression" : return this . createMemberKeyGetter ( node ) ; default : throw this . createInternalThrowError ( Messages . AssignmentTypeSyntaxError , node . type , node ) ; } } protected varDeclaration ( name ) { const context = this . collectDeclVars ; context [ name ] = undefined ; } protected funcDeclaration ( name , func ) { const context = this . collectDeclFuncs ; context [ name ] = func ; } param name param declVars param declFuncs param scope protected addDeclarationsToScope ( declVars , declFuncs , scope ) { const scopeData = scope . data ; for ( let key in declFuncs ) { const value = declFuncs [ key ] ; scopeData [ key ] = value ? value ( ) : value ; } for ( let key in declVars ) { if ( ! ( key in scopeData ) ) { scopeData [ key ] = void 0 ; } } } protected getScopeValue ( name , startScope ) { const scope = this . getScopeFromName ( name , startScope ) ; return scope . data [ name ] ; } protected getScopeDataFromName ( name , startScope ) { return this . getScopeFromName ( name , startScope ) . data ; } param name param startScope protected getScopeFromName ( name , startScope , constInit ? ) { let scope = startScope ; do { if ( scope . type == 'block' ) { if ( ! scope . lexDeclared [ name ] ) { if ( name in scope . data ) { return scope ; } } else { if ( scope . lexDeclared [ name ] . init === false ) { if ( constInit && scope . lexDeclared [ name ] . kind == 'const' ) { return scope ; } throw this . createInternalThrowError ( Messages . LetVariableUseBeforeInitReferenceError , name ) ; } else if ( scope . lexDeclared [ name ] . init === true ) { return scope ; } } } else { if ( scope . lexDeclared && scope . lexDeclared [ name ] && scope . lexDeclared [ name ] . init === false ) { throw this . createInternalThrowError ( Messages . LetVariableUseBeforeInitReferenceError , name ) ; } if ( name in scope . data ) { return scope ; } } } while ( ( scope = scope . parent ) ) ; return this . globalScope ; } protected entryBlockScope ( newScope ) { const prevScope = this . getCurrentScope ( ) ; this . setCurrentScope ( newScope ) ; return prevScope ; } protected setValue ( value ) { const isFunctionCall = this . callStack . length ; if ( this . isVarDeclMode || isFunctionCall || value === EmptyStatementReturn || value === Break || value === Continue || value instanceof BreakLabel || value instanceof ContinueLabel ) { return value ; } this . value = value instanceof Return ? value . value : value ; return value ; } getValue ( ) { return this . value ; } }