2020-02-04 01:06:47 -05:00
"use strict" ;
var _ _extends = ( this && this . _ _extends ) || ( function ( ) {
var extendStatics = function ( d , b ) {
extendStatics = Object . setPrototypeOf ||
( { _ _proto _ _ : [ ] } instanceof Array && function ( d , b ) { d . _ _proto _ _ = b ; } ) ||
2021-03-07 18:24:04 -05:00
function ( d , b ) { for ( var p in b ) if ( Object . prototype . hasOwnProperty . call ( b , p ) ) d [ p ] = b [ p ] ; } ;
2020-02-04 01:06:47 -05:00
return extendStatics ( d , b ) ;
} ;
return function ( d , b ) {
2021-03-07 18:24:04 -05:00
if ( typeof b !== "function" && b !== null )
throw new TypeError ( "Class extends value " + String ( b ) + " is not a constructor or null" ) ;
2020-02-04 01:06:47 -05:00
extendStatics ( d , b ) ;
function _ _ ( ) { this . constructor = d ; }
d . prototype = b === null ? Object . create ( b ) : ( _ _ . prototype = b . prototype , new _ _ ( ) ) ;
} ;
} ) ( ) ;
var _ _awaiter = ( this && this . _ _awaiter ) || function ( thisArg , _arguments , P , generator ) {
function adopt ( value ) { return value instanceof P ? value : new P ( function ( resolve ) { resolve ( value ) ; } ) ; }
return new ( P || ( P = Promise ) ) ( function ( resolve , reject ) {
function fulfilled ( value ) { try { step ( generator . next ( value ) ) ; } catch ( e ) { reject ( e ) ; } }
function rejected ( value ) { try { step ( generator [ "throw" ] ( value ) ) ; } catch ( e ) { reject ( e ) ; } }
function step ( result ) { result . done ? resolve ( result . value ) : adopt ( result . value ) . then ( fulfilled , rejected ) ; }
step ( ( generator = generator . apply ( thisArg , _arguments || [ ] ) ) . next ( ) ) ;
} ) ;
} ;
var _ _generator = ( this && this . _ _generator ) || function ( thisArg , body ) {
var _ = { label : 0 , sent : function ( ) { if ( t [ 0 ] & 1 ) throw t [ 1 ] ; return t [ 1 ] ; } , trys : [ ] , ops : [ ] } , f , y , t , g ;
return g = { next : verb ( 0 ) , "throw" : verb ( 1 ) , "return" : verb ( 2 ) } , typeof Symbol === "function" && ( g [ Symbol . iterator ] = function ( ) { return this ; } ) , g ;
function verb ( n ) { return function ( v ) { return step ( [ n , v ] ) ; } ; }
function step ( op ) {
if ( f ) throw new TypeError ( "Generator is already executing." ) ;
while ( _ ) try {
if ( f = 1 , y && ( t = op [ 0 ] & 2 ? y [ "return" ] : op [ 0 ] ? y [ "throw" ] || ( ( t = y [ "return" ] ) && t . call ( y ) , 0 ) : y . next ) && ! ( t = t . call ( y , op [ 1 ] ) ) . done ) return t ;
if ( y = 0 , t ) op = [ op [ 0 ] & 2 , t . value ] ;
switch ( op [ 0 ] ) {
case 0 : case 1 : t = op ; break ;
case 4 : _ . label ++ ; return { value : op [ 1 ] , done : false } ;
case 5 : _ . label ++ ; y = op [ 1 ] ; op = [ 0 ] ; continue ;
case 7 : op = _ . ops . pop ( ) ; _ . trys . pop ( ) ; continue ;
default :
if ( ! ( t = _ . trys , t = t . length > 0 && t [ t . length - 1 ] ) && ( op [ 0 ] === 6 || op [ 0 ] === 2 ) ) { _ = 0 ; continue ; }
if ( op [ 0 ] === 3 && ( ! t || ( op [ 1 ] > t [ 0 ] && op [ 1 ] < t [ 3 ] ) ) ) { _ . label = op [ 1 ] ; break ; }
if ( op [ 0 ] === 6 && _ . label < t [ 1 ] ) { _ . label = t [ 1 ] ; t = op ; break ; }
if ( t && _ . label < t [ 2 ] ) { _ . label = t [ 2 ] ; _ . ops . push ( op ) ; break ; }
if ( t [ 2 ] ) _ . ops . pop ( ) ;
_ . trys . pop ( ) ; continue ;
}
op = body . call ( thisArg , _ ) ;
} catch ( e ) { op = [ 6 , e ] ; y = 0 ; } finally { f = t = 0 ; }
if ( op [ 0 ] & 5 ) throw op [ 1 ] ; return { value : op [ 0 ] ? op [ 1 ] : void 0 , done : true } ;
}
} ;
var _ _importDefault = ( this && this . _ _importDefault ) || function ( mod ) {
return ( mod && mod . _ _esModule ) ? mod : { "default" : mod } ;
} ;
Object . defineProperty ( exports , "__esModule" , { value : true } ) ;
2021-03-07 18:24:04 -05:00
exports . assemble = exports . parse = exports . SemanticErrorSeverity = exports . formatBytecode = exports . disassemble = exports . ScopeNode = exports . ExecutionNode = exports . EvaluationNode = exports . DataNode = exports . PaddingNode = exports . LabelNode = exports . LabelledNode = exports . OpcodeNode = exports . LinkNode = exports . PopNode = exports . LiteralNode = exports . ValueNode = exports . Node = void 0 ;
2020-02-04 01:06:47 -05:00
// @TODO:
// - warn return/revert non-empty, comment ; !assert(+1 @extra)
// - In JS add config (positionIndependent)
// - When checking name collisions, verify no collision in javascript
var path _1 = require ( "path" ) ;
var vm _1 = _ _importDefault ( require ( "vm" ) ) ;
var ethers _1 = require ( "ethers" ) ;
var opcodes _1 = require ( "./opcodes" ) ;
var _parser _1 = require ( "./_parser" ) ;
var _version _1 = require ( "./_version" ) ;
var logger = new ethers _1 . ethers . utils . Logger ( _version _1 . version ) ;
var Guard = { } ;
function hexConcat ( values ) {
return ethers _1 . ethers . utils . hexlify ( ethers _1 . ethers . utils . concat ( values . map ( function ( v ) {
if ( v instanceof opcodes _1 . Opcode ) {
return [ v . value ] ;
}
2020-03-12 19:14:50 +01:00
if ( typeof ( v ) === "number" ) {
if ( v >= 0 && v <= 255 && ! ( v % 1 ) ) {
return ethers _1 . ethers . utils . hexlify ( v ) ;
}
else {
throw new Error ( "invalid number: " + v ) ;
}
}
2020-02-04 01:06:47 -05:00
return v ;
} ) ) ) ;
}
function repeat ( char , length ) {
var result = char ;
while ( result . length < length ) {
result += result ;
}
return result . substring ( 0 , length ) ;
}
var Script = /** @class */ ( function ( ) {
function Script ( filename , callback ) {
ethers _1 . ethers . utils . defineReadOnly ( this , "filename" , filename ) ;
ethers _1 . ethers . utils . defineReadOnly ( this , "contextObject" , this . _baseContext ( callback ) ) ;
ethers _1 . ethers . utils . defineReadOnly ( this , "context" , vm _1 . default . createContext ( this . contextObject ) ) ;
}
Script . prototype . _baseContext = function ( callback ) {
var _this = this ;
return new Proxy ( {
_ _filename : this . filename ,
_ _dirname : path _1 . dirname ( this . filename ) ,
console : console ,
Uint8Array : Uint8Array ,
ethers : ethers _1 . ethers ,
utils : ethers _1 . ethers . utils ,
BigNumber : ethers _1 . ethers . BigNumber ,
arrayify : ethers _1 . ethers . utils . arrayify ,
concat : hexConcat ,
hexlify : ethers _1 . ethers . utils . hexlify ,
zeroPad : function ( value , length ) {
return ethers _1 . ethers . utils . hexlify ( ethers _1 . ethers . utils . zeroPad ( value , length ) ) ;
} ,
id : ethers _1 . ethers . utils . id ,
keccak256 : ethers _1 . ethers . utils . keccak256 ,
namehash : ethers _1 . ethers . utils . namehash ,
sha256 : ethers _1 . ethers . utils . sha256 ,
parseEther : ethers _1 . ethers . utils . parseEther ,
formatEther : ethers _1 . ethers . utils . formatEther ,
parseUnits : ethers _1 . ethers . utils . parseUnits ,
formatUnits : ethers _1 . ethers . utils . formatUnits ,
randomBytes : function ( length ) {
return ethers _1 . ethers . utils . hexlify ( ethers _1 . ethers . utils . randomBytes ( length ) ) ;
} ,
toUtf8Bytes : ethers _1 . ethers . utils . toUtf8Bytes ,
toUtf8String : ethers _1 . ethers . utils . toUtf8String ,
formatBytes32String : ethers _1 . ethers . utils . formatBytes32String ,
parseBytes32String : ethers _1 . ethers . utils . parseBytes32String ,
Opcode : opcodes _1 . Opcode ,
sighash : function ( signature ) {
return ethers _1 . ethers . utils . id ( ethers _1 . ethers . utils . FunctionFragment . from ( signature ) . format ( ) ) . substring ( 0 , 10 ) ;
} ,
topichash : function ( signature ) {
return ethers _1 . ethers . utils . id ( ethers _1 . ethers . utils . EventFragment . from ( signature ) . format ( ) ) ;
} ,
assemble : assemble ,
2020-03-12 19:14:50 +01:00
disassemble : disassemble ,
Error : Error
2020-02-04 01:06:47 -05:00
} , {
get : function ( obj , key ) {
if ( obj [ key ] ) {
return obj [ key ] ;
}
if ( ! callback ) {
return undefined ;
}
return callback ( key , _this . _context . context ) ;
}
} ) ;
} ;
Script . prototype . evaluate = function ( code , context ) {
return _ _awaiter ( this , void 0 , void 0 , function ( ) {
var script , result ;
return _ _generator ( this , function ( _a ) {
switch ( _a . label ) {
case 0 :
if ( this . _context ) {
throw new Error ( "evaluation collision" ) ;
}
this . _context = { context : context } ;
script = new vm _1 . default . Script ( code , { filename : this . filename } ) ;
result = script . runInContext ( this . context ) ;
if ( ! ( result instanceof Promise ) ) return [ 3 /*break*/ , 2 ] ;
return [ 4 /*yield*/ , result ] ;
case 1 :
result = _a . sent ( ) ;
_a . label = 2 ;
case 2 :
this . _context = null ;
return [ 2 /*return*/ , result ] ;
}
} ) ;
} ) ;
} ;
return Script ;
} ( ) ) ;
var nextTag = 1 ;
2020-03-12 19:14:50 +01:00
function throwError ( message , location ) {
return logger . throwError ( message , "ASSEMBLER" , {
location : location
} ) ;
}
2020-02-04 01:06:47 -05:00
var Node = /** @class */ ( function ( ) {
function Node ( guard , location , options ) {
var _newTarget = this . constructor ;
if ( guard !== Guard ) {
2020-03-12 19:14:50 +01:00
throwError ( "cannot instantiate class" , location ) ;
2020-02-04 01:06:47 -05:00
}
logger . checkAbstract ( _newTarget , Node ) ;
2020-02-06 18:21:34 -05:00
ethers _1 . ethers . utils . defineReadOnly ( this , "location" , Object . freeze ( location ) ) ;
2020-02-04 01:06:47 -05:00
ethers _1 . ethers . utils . defineReadOnly ( this , "tag" , "node-" + nextTag ++ + "-" + this . constructor . name ) ;
for ( var key in options ) {
ethers _1 . ethers . utils . defineReadOnly ( this , key , options [ key ] ) ;
}
}
// Note: EVERY node must call assemble with `this`, even if only with
// the bytes "0x" to trigger the offset and bytecode checks
Node . prototype . assemble = function ( assembler , visit ) {
return _ _awaiter ( this , void 0 , void 0 , function ( ) {
return _ _generator ( this , function ( _a ) {
assembler . start ( this ) ;
visit ( this , "0x" ) ;
assembler . end ( this ) ;
return [ 2 /*return*/ ] ;
} ) ;
} ) ;
} ;
Node . prototype . children = function ( ) {
return [ ] ;
} ;
Node . prototype . visit = function ( visit ) {
visit ( this ) ;
2020-02-06 18:21:34 -05:00
this . children ( ) . forEach ( function ( child ) {
child . visit ( visit ) ;
} ) ;
2020-02-04 01:06:47 -05:00
} ;
Node . from = function ( options ) {
var Factories = {
data : DataNode ,
decimal : LiteralNode ,
eval : EvaluationNode ,
exec : ExecutionNode ,
hex : LiteralNode ,
label : LabelNode ,
length : LinkNode ,
offset : LinkNode ,
opcode : OpcodeNode ,
2020-02-06 18:21:34 -05:00
pop : PopNode ,
2020-02-04 01:06:47 -05:00
scope : ScopeNode ,
} ;
var factory = Factories [ options . type ] ;
if ( ! factory ) {
2020-04-23 23:35:39 -04:00
throwError ( "unknown type: " + options . type , options . loc ) ;
2020-02-04 01:06:47 -05:00
}
return factory . from ( options ) ;
} ;
return Node ;
} ( ) ) ;
exports . Node = Node ;
var ValueNode = /** @class */ ( function ( _super ) {
_ _extends ( ValueNode , _super ) ;
function ValueNode ( guard , location , options ) {
var _newTarget = this . constructor ;
var _this = this ;
logger . checkAbstract ( _newTarget , ValueNode ) ;
_this = _super . call ( this , guard , location , options ) || this ;
return _this ;
}
2020-03-12 19:14:50 +01:00
ValueNode . prototype . getPushLiteral = function ( value ) {
// Convert value into a hexstring
var hex = ethers _1 . ethers . utils . hexlify ( value ) ;
if ( hex === "0x" ) {
throwError ( "invalid literal: 0x" , this . location ) ;
}
// Make sure it will fit into a push
var length = ethers _1 . ethers . utils . hexDataLength ( hex ) ;
if ( length === 0 || length > 32 ) {
throwError ( "literal out of range: " + hex , this . location ) ;
}
return hexConcat ( [ opcodes _1 . Opcode . from ( "PUSH" + String ( length ) ) , hex ] ) ;
} ;
2020-02-04 01:06:47 -05:00
return ValueNode ;
} ( Node ) ) ;
exports . ValueNode = ValueNode ;
var LiteralNode = /** @class */ ( function ( _super ) {
_ _extends ( LiteralNode , _super ) ;
function LiteralNode ( guard , location , value , verbatim ) {
return _super . call ( this , guard , location , { value : value , verbatim : verbatim } ) || this ;
}
LiteralNode . prototype . assemble = function ( assembler , visit ) {
return _ _awaiter ( this , void 0 , void 0 , function ( ) {
return _ _generator ( this , function ( _a ) {
assembler . start ( this ) ;
if ( this . verbatim ) {
if ( this . value . substring ( 0 , 2 ) === "0x" ) {
visit ( this , this . value ) ;
}
else {
visit ( this , ethers _1 . ethers . BigNumber . from ( this . value ) . toHexString ( ) ) ;
}
}
else {
2020-03-12 19:14:50 +01:00
visit ( this , this . getPushLiteral ( ethers _1 . ethers . BigNumber . from ( this . value ) ) ) ;
2020-02-04 01:06:47 -05:00
}
assembler . end ( this ) ;
return [ 2 /*return*/ ] ;
} ) ;
} ) ;
} ;
LiteralNode . from = function ( options ) {
if ( options . type !== "hex" && options . type !== "decimal" ) {
2020-03-12 19:14:50 +01:00
throwError ( "expected hex or decimal type" , options . loc ) ;
2020-02-04 01:06:47 -05:00
}
return new LiteralNode ( Guard , options . loc , options . value , ! ! options . verbatim ) ;
} ;
return LiteralNode ;
} ( ValueNode ) ) ;
exports . LiteralNode = LiteralNode ;
2020-02-06 18:21:34 -05:00
var PopNode = /** @class */ ( function ( _super ) {
_ _extends ( PopNode , _super ) ;
function PopNode ( guard , location , index ) {
return _super . call ( this , guard , location , { index : index } ) || this ;
}
Object . defineProperty ( PopNode . prototype , "placeholder" , {
get : function ( ) {
if ( this . index === 0 ) {
return "$$" ;
}
return "$" + String ( this . index ) ;
} ,
2021-03-07 18:24:04 -05:00
enumerable : false ,
2020-02-06 18:21:34 -05:00
configurable : true
} ) ;
PopNode . from = function ( options ) {
return new PopNode ( Guard , options . loc , options . index ) ;
} ;
return PopNode ;
} ( ValueNode ) ) ;
exports . PopNode = PopNode ;
2020-02-04 01:06:47 -05:00
var LinkNode = /** @class */ ( function ( _super ) {
_ _extends ( LinkNode , _super ) ;
function LinkNode ( guard , location , type , label ) {
return _super . call ( this , guard , location , { type : type , label : label } ) || this ;
}
LinkNode . prototype . assemble = function ( assembler , visit ) {
return _ _awaiter ( this , void 0 , void 0 , function ( ) {
2020-02-06 18:21:34 -05:00
var value , isOffset , target , result , here , opcodes , literal , w ;
2020-02-04 01:06:47 -05:00
return _ _generator ( this , function ( _a ) {
assembler . start ( this ) ;
value = null ;
2020-02-06 18:21:34 -05:00
isOffset = false ;
2020-02-04 01:06:47 -05:00
target = assembler . getTarget ( this . label ) ;
if ( target instanceof LabelNode ) {
if ( this . type === "offset" ) {
value = ( assembler . getLinkValue ( target , this ) ) ;
2020-02-06 18:21:34 -05:00
isOffset = true ;
2020-02-04 01:06:47 -05:00
}
}
else {
result = ( assembler . getLinkValue ( target , this ) ) ;
if ( this . type === "offset" ) {
value = result . offset ;
2020-02-06 18:21:34 -05:00
isOffset = true ;
2020-02-04 01:06:47 -05:00
}
else if ( this . type === "length" ) {
value = result . length ;
}
}
if ( value == null ) {
2020-03-12 19:14:50 +01:00
throwError ( "labels can only be targetted as offsets" , this . location ) ;
2020-02-04 01:06:47 -05:00
}
2020-02-06 18:21:34 -05:00
if ( isOffset && assembler . positionIndependentCode ) {
here = assembler . getOffset ( this , this ) ;
opcodes = [ ] ;
if ( here > value ) {
literal = "0x" ;
for ( w = 1 ; w <= 5 ; w ++ ) {
if ( w > 4 ) {
2020-03-12 19:14:50 +01:00
throwError ( "jump too large!" , this . location ) ;
2020-02-06 18:21:34 -05:00
}
2020-03-12 19:14:50 +01:00
literal = this . getPushLiteral ( here - value + w ) ;
2020-02-06 18:21:34 -05:00
if ( ethers _1 . ethers . utils . hexDataLength ( literal ) <= w ) {
literal = ethers _1 . ethers . utils . hexZeroPad ( literal , w ) ;
break ;
}
}
opcodes . push ( literal ) ;
opcodes . push ( opcodes _1 . Opcode . from ( "PC" ) ) ;
opcodes . push ( opcodes _1 . Opcode . from ( "SUB" ) ) ;
// This also works, in case the above literal thing doesn't work out...
//opcodes.push(Opcode.from("PC"));
//opcodes.push(pushLiteral(-delta));
//opcodes.push(Opcode.from("SWAP1"));
//opcodes.push(Opcode.from("SUB"));
}
else {
// Jump forwards; this is easy to calculate since we can
// do PC firat.
opcodes . push ( opcodes _1 . Opcode . from ( "PC" ) ) ;
2020-03-12 19:14:50 +01:00
opcodes . push ( this . getPushLiteral ( value - here ) ) ;
2020-02-06 18:21:34 -05:00
opcodes . push ( opcodes _1 . Opcode . from ( "ADD" ) ) ;
}
visit ( this , hexConcat ( opcodes ) ) ;
}
else {
2020-03-12 19:14:50 +01:00
visit ( this , this . getPushLiteral ( value ) ) ;
2020-02-06 18:21:34 -05:00
}
2020-02-04 01:06:47 -05:00
assembler . end ( this ) ;
return [ 2 /*return*/ ] ;
} ) ;
} ) ;
} ;
LinkNode . from = function ( options ) {
// @TODO: Verify type is offset or link...
return new LinkNode ( Guard , options . loc , options . type , options . label ) ;
} ;
return LinkNode ;
} ( ValueNode ) ) ;
exports . LinkNode = LinkNode ;
var OpcodeNode = /** @class */ ( function ( _super ) {
_ _extends ( OpcodeNode , _super ) ;
2020-02-06 18:21:34 -05:00
function OpcodeNode ( guard , location , opcode , operands , instructional ) {
return _super . call ( this , guard , location , { instructional : instructional , opcode : opcode , operands : operands } ) || this ;
2020-02-04 01:06:47 -05:00
}
OpcodeNode . prototype . assemble = function ( assembler , visit ) {
return _ _awaiter ( this , void 0 , void 0 , function ( ) {
var i ;
return _ _generator ( this , function ( _a ) {
switch ( _a . label ) {
case 0 :
assembler . start ( this ) ;
i = this . operands . length - 1 ;
_a . label = 1 ;
case 1 :
if ( ! ( i >= 0 ) ) return [ 3 /*break*/ , 4 ] ;
return [ 4 /*yield*/ , this . operands [ i ] . assemble ( assembler , visit ) ] ;
case 2 :
_a . sent ( ) ;
_a . label = 3 ;
case 3 :
i -- ;
return [ 3 /*break*/ , 1 ] ;
case 4 :
// Append this opcode
visit ( this , ethers _1 . ethers . utils . hexlify ( this . opcode . value ) ) ;
assembler . end ( this ) ;
return [ 2 /*return*/ ] ;
}
} ) ;
} ) ;
} ;
OpcodeNode . prototype . children = function ( ) {
return this . operands ;
} ;
OpcodeNode . prototype . visit = function ( visit ) {
for ( var i = this . operands . length - 1 ; i >= 0 ; i -- ) {
this . operands [ i ] . visit ( visit ) ;
}
visit ( this ) ;
} ;
OpcodeNode . from = function ( options ) {
if ( options . type !== "opcode" ) {
2020-03-12 19:14:50 +01:00
throwError ( "expected opcode type" , options . loc ) ;
2020-02-04 01:06:47 -05:00
}
var opcode = opcodes _1 . Opcode . from ( options . mnemonic ) ;
if ( ! opcode ) {
2020-03-12 19:14:50 +01:00
throwError ( "unknown opcode: " + options . mnemonic , options . loc ) ;
2020-02-04 01:06:47 -05:00
}
var operands = Object . freeze ( options . operands . map ( function ( o ) {
var operand = Node . from ( o ) ;
if ( ! ( operand instanceof ValueNode ) ) {
2020-03-12 19:14:50 +01:00
throwError ( "bad grammar?!" , options . loc ) ;
2020-02-04 01:06:47 -05:00
}
return operand ;
} ) ) ;
2020-02-06 18:21:34 -05:00
return new OpcodeNode ( Guard , options . loc , opcode , operands , ! ! options . bare ) ;
2020-02-04 01:06:47 -05:00
} ;
return OpcodeNode ;
} ( ValueNode ) ) ;
exports . OpcodeNode = OpcodeNode ;
var LabelledNode = /** @class */ ( function ( _super ) {
_ _extends ( LabelledNode , _super ) ;
function LabelledNode ( guard , location , name , values ) {
var _newTarget = this . constructor ;
var _this = this ;
logger . checkAbstract ( _newTarget , LabelledNode ) ;
values = ethers _1 . ethers . utils . shallowCopy ( values || { } ) ;
values . name = name ;
_this = _super . call ( this , guard , location , values ) || this ;
return _this ;
}
return LabelledNode ;
} ( Node ) ) ;
exports . LabelledNode = LabelledNode ;
var LabelNode = /** @class */ ( function ( _super ) {
_ _extends ( LabelNode , _super ) ;
function LabelNode ( ) {
return _super !== null && _super . apply ( this , arguments ) || this ;
}
LabelNode . prototype . assemble = function ( assembler , visit ) {
return _ _awaiter ( this , void 0 , void 0 , function ( ) {
return _ _generator ( this , function ( _a ) {
assembler . start ( this ) ;
visit ( this , ethers _1 . ethers . utils . hexlify ( opcodes _1 . Opcode . from ( "JUMPDEST" ) . value ) ) ;
assembler . end ( this ) ;
return [ 2 /*return*/ ] ;
} ) ;
} ) ;
} ;
LabelNode . from = function ( options ) {
if ( options . type !== "label" ) {
2020-03-12 19:14:50 +01:00
throwError ( "expected label type" , options . loc ) ;
2020-02-04 01:06:47 -05:00
}
return new LabelNode ( Guard , options . loc , options . name ) ;
} ;
return LabelNode ;
} ( LabelledNode ) ) ;
exports . LabelNode = LabelNode ;
2020-03-12 19:14:50 +01:00
var PaddingNode = /** @class */ ( function ( _super ) {
_ _extends ( PaddingNode , _super ) ;
function PaddingNode ( guard , location ) {
var _this = _super . call ( this , guard , location , { } ) || this ;
_this . _length = 0 ;
return _this ;
}
PaddingNode . prototype . setLength = function ( length ) {
this . _length = length ;
} ;
PaddingNode . prototype . assemble = function ( assembler , visit ) {
return _ _awaiter ( this , void 0 , void 0 , function ( ) {
var padding ;
return _ _generator ( this , function ( _a ) {
assembler . start ( this ) ;
padding = new Uint8Array ( this . _length ) ;
padding . fill ( 0 ) ;
visit ( this , ethers _1 . ethers . utils . hexlify ( padding ) ) ;
assembler . end ( this ) ;
return [ 2 /*return*/ ] ;
} ) ;
} ) ;
} ;
return PaddingNode ;
} ( ValueNode ) ) ;
exports . PaddingNode = PaddingNode ;
2020-02-04 01:06:47 -05:00
var DataNode = /** @class */ ( function ( _super ) {
_ _extends ( DataNode , _super ) ;
function DataNode ( guard , location , name , data ) {
2020-03-12 19:14:50 +01:00
var _this = _super . call ( this , guard , location , name , { data : data } ) || this ;
ethers _1 . ethers . utils . defineReadOnly ( _this , "padding" , new PaddingNode ( Guard , _this . location ) ) ;
return _this ;
2020-02-04 01:06:47 -05:00
}
DataNode . prototype . assemble = function ( assembler , visit ) {
return _ _awaiter ( this , void 0 , void 0 , function ( ) {
2020-03-12 19:14:50 +01:00
var i _1 , bytecode , i , opcode ;
2020-02-04 01:06:47 -05:00
return _ _generator ( this , function ( _a ) {
switch ( _a . label ) {
case 0 :
assembler . start ( this ) ;
2020-03-12 19:14:50 +01:00
// @TODO: This is a problem... We need to visit before visiting children
// so offsets are correct, but then we cannot pad...
visit ( this , "0x" ) ;
2020-02-04 01:06:47 -05:00
i _1 = 0 ;
_a . label = 1 ;
case 1 :
if ( ! ( i _1 < this . data . length ) ) return [ 3 /*break*/ , 4 ] ;
return [ 4 /*yield*/ , this . data [ i _1 ] . assemble ( assembler , visit ) ] ;
case 2 :
_a . sent ( ) ;
_a . label = 3 ;
case 3 :
i _1 ++ ;
return [ 3 /*break*/ , 1 ] ;
case 4 :
2020-03-12 19:14:50 +01:00
bytecode = ethers _1 . ethers . utils . concat ( this . data . map ( function ( d ) { return assembler . getBytecode ( d ) ; } ) ) ;
2020-02-04 01:06:47 -05:00
i = 0 ;
while ( i < bytecode . length ) {
opcode = opcodes _1 . Opcode . from ( bytecode [ i ++ ] ) ;
if ( opcode ) {
i += opcode . isPush ( ) ;
}
}
2020-03-12 19:14:50 +01:00
// The amount we overshot the data by is how much padding we need
this . padding . setLength ( i - bytecode . length ) ;
return [ 4 /*yield*/ , this . padding . assemble ( assembler , visit ) ] ;
case 5 :
_a . sent ( ) ;
2020-02-04 01:06:47 -05:00
assembler . end ( this ) ;
return [ 2 /*return*/ ] ;
}
} ) ;
} ) ;
} ;
DataNode . prototype . children = function ( ) {
2020-03-12 19:14:50 +01:00
var children = this . data . slice ( ) ;
children . push ( this . padding ) ;
return children ;
2020-02-04 01:06:47 -05:00
} ;
DataNode . from = function ( options ) {
if ( options . type !== "data" ) {
2020-03-12 19:14:50 +01:00
throwError ( "expected data type" , options . loc ) ;
2020-02-04 01:06:47 -05:00
}
return new DataNode ( Guard , options . loc , options . name , Object . freeze ( options . data . map ( function ( d ) { return Node . from ( d ) ; } ) ) ) ;
} ;
return DataNode ;
} ( LabelledNode ) ) ;
exports . DataNode = DataNode ;
var EvaluationNode = /** @class */ ( function ( _super ) {
_ _extends ( EvaluationNode , _super ) ;
function EvaluationNode ( guard , location , script , verbatim ) {
return _super . call ( this , guard , location , { script : script , verbatim : verbatim } ) || this ;
}
EvaluationNode . prototype . assemble = function ( assembler , visit ) {
return _ _awaiter ( this , void 0 , void 0 , function ( ) {
var result ;
return _ _generator ( this , function ( _a ) {
switch ( _a . label ) {
case 0 :
assembler . start ( this ) ;
return [ 4 /*yield*/ , assembler . evaluate ( this . script , this ) ] ;
case 1 :
result = _a . sent ( ) ;
if ( this . verbatim ) {
if ( typeof ( result ) === "number" ) {
visit ( this , ethers _1 . ethers . BigNumber . from ( result ) . toHexString ( ) ) ;
}
else {
visit ( this , ethers _1 . ethers . utils . hexlify ( result ) ) ;
}
}
else {
2020-03-12 19:14:50 +01:00
visit ( this , this . getPushLiteral ( result ) ) ;
2020-02-04 01:06:47 -05:00
}
assembler . end ( this ) ;
return [ 2 /*return*/ ] ;
}
} ) ;
} ) ;
} ;
EvaluationNode . from = function ( options ) {
if ( options . type !== "eval" ) {
2020-03-12 19:14:50 +01:00
throwError ( "expected eval type" , options . loc ) ;
2020-02-04 01:06:47 -05:00
}
return new EvaluationNode ( Guard , options . loc , options . script , ! ! options . verbatim ) ;
} ;
return EvaluationNode ;
} ( ValueNode ) ) ;
exports . EvaluationNode = EvaluationNode ;
var ExecutionNode = /** @class */ ( function ( _super ) {
_ _extends ( ExecutionNode , _super ) ;
function ExecutionNode ( guard , location , script ) {
return _super . call ( this , guard , location , { script : script } ) || this ;
}
ExecutionNode . prototype . assemble = function ( assembler , visit ) {
return _ _awaiter ( this , void 0 , void 0 , function ( ) {
return _ _generator ( this , function ( _a ) {
switch ( _a . label ) {
case 0 :
assembler . start ( this ) ;
return [ 4 /*yield*/ , assembler . evaluate ( this . script , this ) ] ;
case 1 :
_a . sent ( ) ;
assembler . end ( this ) ;
return [ 2 /*return*/ ] ;
}
} ) ;
} ) ;
} ;
ExecutionNode . from = function ( options ) {
if ( options . type !== "exec" ) {
2020-03-12 19:14:50 +01:00
throwError ( "expected exec type" , options . loc ) ;
2020-02-04 01:06:47 -05:00
}
return new ExecutionNode ( Guard , options . loc , options . script ) ;
} ;
return ExecutionNode ;
} ( Node ) ) ;
exports . ExecutionNode = ExecutionNode ;
var ScopeNode = /** @class */ ( function ( _super ) {
_ _extends ( ScopeNode , _super ) ;
function ScopeNode ( guard , location , name , statements ) {
return _super . call ( this , guard , location , name , { statements : statements } ) || this ;
}
ScopeNode . prototype . assemble = function ( assembler , visit ) {
return _ _awaiter ( this , void 0 , void 0 , function ( ) {
var i ;
return _ _generator ( this , function ( _a ) {
switch ( _a . label ) {
case 0 :
assembler . start ( this ) ;
visit ( this , "0x" ) ;
i = 0 ;
_a . label = 1 ;
case 1 :
if ( ! ( i < this . statements . length ) ) return [ 3 /*break*/ , 4 ] ;
return [ 4 /*yield*/ , this . statements [ i ] . assemble ( assembler , visit ) ] ;
case 2 :
_a . sent ( ) ;
_a . label = 3 ;
case 3 :
i ++ ;
return [ 3 /*break*/ , 1 ] ;
case 4 :
assembler . end ( this ) ;
return [ 2 /*return*/ ] ;
}
} ) ;
} ) ;
} ;
ScopeNode . prototype . children = function ( ) {
return this . statements ;
} ;
ScopeNode . from = function ( options ) {
if ( options . type !== "scope" ) {
2020-03-12 19:14:50 +01:00
throwError ( "expected scope type" , options . loc ) ;
2020-02-04 01:06:47 -05:00
}
return new ScopeNode ( Guard , options . loc , options . name , Object . freeze ( options . statements . map ( function ( s ) { return Node . from ( s ) ; } ) ) ) ;
} ;
return ScopeNode ;
} ( LabelledNode ) ) ;
exports . ScopeNode = ScopeNode ;
function disassemble ( bytecode ) {
var ops = [ ] ;
var offsets = { } ;
var bytes = ethers _1 . ethers . utils . arrayify ( bytecode , { allowMissingPrefix : true } ) ;
var i = 0 ;
var oob = false ;
while ( i < bytes . length ) {
var opcode = opcodes _1 . Opcode . from ( bytes [ i ] ) ;
if ( ! opcode ) {
opcode = new opcodes _1 . Opcode ( "unknown (" + ethers _1 . ethers . utils . hexlify ( bytes [ i ] ) + ")" , bytes [ i ] , 0 , 0 ) ;
}
else if ( oob && opcode . mnemonic === "JUMPDEST" ) {
opcode = new opcodes _1 . Opcode ( "JUMPDEST (invalid; OOB!!)" , bytes [ i ] , 0 , 0 ) ;
}
var op = {
opcode : opcode ,
2020-09-04 01:37:14 -04:00
offset : i ,
length : 1
2020-02-04 01:06:47 -05:00
} ;
offsets [ i ] = op ;
ops . push ( op ) ;
i ++ ;
var push = opcode . isPush ( ) ;
if ( push ) {
var data = ethers _1 . ethers . utils . hexlify ( bytes . slice ( i , i + push ) ) ;
if ( ethers _1 . ethers . utils . hexDataLength ( data ) === push ) {
op . pushValue = data ;
2020-09-04 01:37:14 -04:00
op . length += push ;
2020-02-04 01:06:47 -05:00
i += push ;
}
else {
oob = true ;
}
}
}
ops . getOperation = function ( offset ) {
2020-09-04 01:37:14 -04:00
if ( offset >= bytes . length ) {
return {
opcode : opcodes _1 . Opcode . from ( "STOP" ) ,
offset : offset ,
length : 1
} ;
}
2020-02-04 01:06:47 -05:00
return ( offsets [ offset ] || null ) ;
} ;
2020-09-04 01:37:14 -04:00
ops . getByte = function ( offset ) {
if ( offset >= bytes . length ) {
return 0x00 ;
}
return bytes [ offset ] ;
} ;
ops . getBytes = function ( offset , length ) {
var result = new Uint8Array ( length ) ;
result . fill ( 0 ) ;
if ( offset < bytes . length ) {
result . set ( bytes . slice ( offset ) ) ;
}
return ethers _1 . ethers . utils . arrayify ( result ) ;
} ;
ops . byteLength = bytes . length ;
2020-02-04 01:06:47 -05:00
return ops ;
}
exports . disassemble = disassemble ;
function formatBytecode ( bytecode ) {
var lines = [ ] ;
bytecode . forEach ( function ( op ) {
var opcode = op . opcode ;
var offset = ethers _1 . ethers . utils . hexZeroPad ( ethers _1 . ethers . utils . hexlify ( op . offset ) , 2 ) ;
if ( opcode . isValidJumpDest ( ) ) {
offset += "*" ;
}
else {
offset += " " ;
}
var operation = opcode . mnemonic ;
var push = opcode . isPush ( ) ;
if ( push ) {
if ( op . pushValue ) {
operation = op . pushValue + ( repeat ( " " , 67 - op . pushValue . length ) + "; #" + push + " " ) ;
}
else {
operation += repeat ( " " , 67 - operation . length ) + "; OOB!! " ;
}
}
lines . push ( offset . substring ( 2 ) + ": " + operation ) ;
} ) ;
return lines . join ( "\n" ) ;
}
exports . formatBytecode = formatBytecode ;
var Assembler = /** @class */ ( function ( ) {
2020-02-06 18:21:34 -05:00
function Assembler ( root , positionIndependentCode ) {
2020-02-04 01:06:47 -05:00
ethers _1 . ethers . utils . defineReadOnly ( this , "root" , root ) ;
2020-02-06 18:21:34 -05:00
ethers _1 . ethers . utils . defineReadOnly ( this , "positionIndependentCode" , ! ! positionIndependentCode ) ;
2020-02-04 01:06:47 -05:00
var nodes = { } ;
var labels = { } ;
var parents = { } ;
// Link labels to their target node
root . visit ( function ( node ) {
nodes [ node . tag ] = {
node : node ,
offset : 0x0 ,
2020-02-06 18:21:34 -05:00
bytecode : "0x"
2020-02-04 01:06:47 -05:00
} ;
if ( node instanceof LabelledNode ) {
// Check for duplicate labels
if ( labels [ node . name ] ) {
logger . throwError ( ( "duplicate label: " + node . name ) , ethers _1 . ethers . utils . Logger . errors . UNSUPPORTED _OPERATION , { } ) ;
}
labels [ node . name ] = node ;
}
} ) ;
root . visit ( function ( node ) {
// Check all labels exist
if ( node instanceof LinkNode ) {
var target = labels [ node . label ] ;
if ( ! target ) {
logger . throwError ( ( "missing label: " + node . label ) , ethers _1 . ethers . utils . Logger . errors . UNSUPPORTED _OPERATION , { } ) ;
}
}
// Build the parent structure
node . children ( ) . forEach ( function ( child ) {
parents [ child . tag ] = node ;
} ) ;
} ) ;
ethers _1 . ethers . utils . defineReadOnly ( this , "labels" , Object . freeze ( labels ) ) ;
ethers _1 . ethers . utils . defineReadOnly ( this , "nodes" , Object . freeze ( nodes ) ) ;
ethers _1 . ethers . utils . defineReadOnly ( this , "_parents" , Object . freeze ( parents ) ) ;
}
// Link operations
2020-02-06 18:21:34 -05:00
Assembler . prototype . getTarget = function ( label ) {
return this . labels [ label ] ;
2020-02-04 01:06:47 -05:00
} ;
2020-02-06 18:21:34 -05:00
// Evaluate script in the context of a {{! }} or {{= }}
2020-02-04 01:06:47 -05:00
Assembler . prototype . evaluate = function ( script , source ) {
2020-02-06 18:21:34 -05:00
return Promise . resolve ( new Uint8Array ( 0 ) ) ;
2020-02-04 01:06:47 -05:00
} ;
Assembler . prototype . getAncestor = function ( node , cls ) {
node = this . _parents [ node . tag ] ;
while ( node ) {
if ( node instanceof cls ) {
return node ;
}
node = this . _parents [ node . tag ] ;
}
return null ;
} ;
2020-02-06 18:21:34 -05:00
Assembler . prototype . getOffset = function ( node , source ) {
var offset = this . nodes [ node . tag ] . offset ;
if ( source == null ) {
return offset ;
}
var sourceScope = ( ( source instanceof ScopeNode ) ? source : this . getAncestor ( source , ScopeNode ) ) ;
return offset - this . nodes [ sourceScope . tag ] . offset ;
} ;
Assembler . prototype . setOffset = function ( node , offset ) {
this . nodes [ node . tag ] . offset = offset ;
} ;
Assembler . prototype . getBytecode = function ( node ) {
return this . nodes [ node . tag ] . bytecode ;
} ;
Assembler . prototype . setBytecode = function ( node , bytecode ) {
this . nodes [ node . tag ] . bytecode = bytecode ;
} ;
2020-02-04 01:06:47 -05:00
Assembler . prototype . getLinkValue = function ( target , source ) {
var sourceScope = ( ( source instanceof ScopeNode ) ? source : this . getAncestor ( source , ScopeNode ) ) ;
var targetScope = ( ( target instanceof ScopeNode ) ? target : this . getAncestor ( target , ScopeNode ) ) ;
if ( target instanceof LabelNode ) {
// Label offset (e.g. "@foo:"); accessible only within its direct scope
//const scope = this.getAncestor(source, Scope);
if ( targetScope !== sourceScope ) {
2020-03-12 19:14:50 +01:00
throwError ( "cannot access " + target . name + " from " + source . tag , source . location ) ;
2020-02-04 01:06:47 -05:00
}
// Return the offset relative to its scope
2020-02-06 18:21:34 -05:00
return this . nodes [ target . tag ] . offset - this . nodes [ targetScope . tag ] . offset ;
2020-02-04 01:06:47 -05:00
}
var info = this . nodes [ target . tag ] ;
// Return the offset is relative to its scope
var bytes = Array . prototype . slice . call ( ethers _1 . ethers . utils . arrayify ( info . bytecode ) ) ;
2020-03-12 19:14:50 +01:00
ethers _1 . ethers . utils . defineReadOnly ( bytes , "ast" , target ) ;
ethers _1 . ethers . utils . defineReadOnly ( bytes , "source" , target . location . source ) ;
2020-02-04 01:06:47 -05:00
if ( ! ( ( target instanceof DataNode ) || ( target instanceof ScopeNode ) ) ) {
2020-03-12 19:14:50 +01:00
throwError ( "invalid link value lookup" , source . location ) ;
2020-02-04 01:06:47 -05:00
}
// Check that target is any descendant (or self) of the source scope
var safeOffset = ( sourceScope == targetScope ) ;
if ( ! safeOffset ) {
sourceScope . visit ( function ( node ) {
if ( node === targetScope ) {
safeOffset = true ;
}
} ) ;
}
// Not safe to access the offset; this will fault if anything tries.
if ( ! safeOffset ) {
Object . defineProperty ( bytes , "offset" , {
2020-03-12 19:14:50 +01:00
get : function ( ) { throwError ( "cannot access " + target . name + ".offset from " + source . tag , this . location ) ; }
2020-02-04 01:06:47 -05:00
} ) ;
2020-03-12 19:14:50 +01:00
ethers _1 . ethers . utils . defineReadOnly ( bytes , "_freeze" , function ( ) { } ) ;
2020-02-04 01:06:47 -05:00
}
// Add the offset relative to the scope; unless the offset has
// been marked as invalid, in which case accessing it will fail
if ( safeOffset ) {
bytes . offset = info . offset - this . nodes [ sourceScope . tag ] . offset ;
2020-03-12 19:14:50 +01:00
var frozen _1 = false ;
ethers _1 . ethers . utils . defineReadOnly ( bytes , "_freeze" , function ( ) {
if ( frozen _1 ) {
return ;
}
frozen _1 = true ;
ethers _1 . ethers . utils . defineReadOnly ( bytes , "offset" , bytes . offset ) ;
} ) ;
2020-02-06 18:21:34 -05:00
}
return bytes ;
} ;
Assembler . prototype . start = function ( node ) { } ;
Assembler . prototype . end = function ( node ) { } ;
return Assembler ;
} ( ) ) ;
var SemanticErrorSeverity ;
( function ( SemanticErrorSeverity ) {
SemanticErrorSeverity [ "error" ] = "error" ;
SemanticErrorSeverity [ "warning" ] = "warning" ;
} ) ( SemanticErrorSeverity = exports . SemanticErrorSeverity || ( exports . SemanticErrorSeverity = { } ) ) ;
;
// This Assembler is designed to only check for errors and warnings
// Warnings
// - Bare PUSH opcodes
// - Instructional opcode that has parameters
// Errors
// - Using a $$ outside of RPN
// - Using a $$ when it is not adjacent to the stack
// - The operand count does not match the opcode
// - An opcode is used as an operand but does not return a value
var SemanticChecker = /** @class */ ( function ( _super ) {
_ _extends ( SemanticChecker , _super ) ;
function SemanticChecker ( ) {
return _super !== null && _super . apply ( this , arguments ) || this ;
}
SemanticChecker . prototype . check = function ( ) {
var _this = this ;
var errors = [ ] ;
this . root . visit ( function ( node ) {
if ( node instanceof OpcodeNode ) {
var opcode = node . opcode ;
if ( node . instructional ) {
if ( opcode . delta ) {
errors . push ( {
message : opcode . mnemonic + " used as instructional" ,
severity : SemanticErrorSeverity . warning ,
node : node
} ) ;
}
}
else {
if ( opcode . mnemonic === "POP" ) {
if ( node . operands . length !== 0 ) {
errors . push ( {
message : "POP expects 0 operands" ,
severity : SemanticErrorSeverity . error ,
node : node
} ) ;
}
}
else if ( node . operands . length !== opcode . delta ) {
errors . push ( {
message : opcode . mnemonic + " expects " + opcode . delta + " operands" ,
severity : SemanticErrorSeverity . error ,
node : node
} ) ;
}
}
if ( opcode . isPush ( ) ) {
// A stray PUSH operation will gobble up the following code
// bytes which is bad. But this may be a disassembled program
// and that PUSH may actually be just some data (which is safe)
errors . push ( {
message : "PUSH opcode modifies program flow - use literals instead" ,
severity : SemanticErrorSeverity . warning ,
node : node
} ) ;
}
else if ( ! node . location . statement && opcode . alpha !== 1 ) {
// If an opcode does not push anything on the stack, it
// cannot be used as an operand
errors . push ( {
message : node . opcode . mnemonic + " cannot be an operand" ,
severity : SemanticErrorSeverity . error ,
node : node
} ) ;
}
}
if ( node . location . statement ) {
if ( node instanceof PopNode ) {
// $$ by istelf is useless and is intended to be an operand
errors . push ( {
message : "$$ must be an operand" ,
severity : SemanticErrorSeverity . error ,
node : node
} ) ;
}
else {
var scope _1 = _this . getAncestor ( node , ScopeNode ) ;
// Make sure any $$ is stack adjacent (within this scope)
var ordered _1 = [ ] ;
node . visit ( function ( node ) {
if ( scope _1 !== _this . getAncestor ( node , ScopeNode ) ) {
return ;
}
ordered _1 . push ( node ) ;
} ) ;
// Allow any number of stack adjacent $$
var foundZero = null ;
var lastIndex = 0 ;
while ( ordered _1 . length && ordered _1 [ 0 ] instanceof PopNode ) {
var popNode = ( ordered _1 . shift ( ) ) ;
var index = popNode . index ;
if ( index === 0 ) {
foundZero = popNode ;
}
else if ( index !== lastIndex + 1 ) {
errors . push ( {
message : "out-of-order stack placeholder " + popNode . placeholder + "; expected $$" + ( lastIndex + 1 ) ,
severity : SemanticErrorSeverity . error ,
node : popNode
} ) ;
while ( ordered _1 . length && ordered _1 [ 0 ] instanceof PopNode ) {
ordered _1 . shift ( ) ;
}
break ;
}
else {
lastIndex = index ;
}
}
if ( foundZero && lastIndex > 0 ) {
errors . push ( {
message : "cannot mix $$ and $1 stack placeholder" ,
severity : SemanticErrorSeverity . error ,
node : foundZero
} ) ;
}
// If there are still any buried, we have a problem
var pops = ordered _1 . filter ( function ( n ) { return ( n instanceof PopNode ) ; } ) ;
if ( pops . length ) {
errors . push ( {
message : "stack placeholder " + ( pops [ 0 ] ) . placeholder + " must be stack adjacent" ,
severity : SemanticErrorSeverity . error ,
node : pops [ 0 ]
} ) ;
}
}
}
} ) ;
return errors ;
} ;
return SemanticChecker ;
} ( Assembler ) ) ;
var CodeGenerationAssembler = /** @class */ ( function ( _super ) {
_ _extends ( CodeGenerationAssembler , _super ) ;
function CodeGenerationAssembler ( root , options ) {
var _this = _super . call ( this , root , ! ! options . positionIndependentCode ) || this ;
ethers _1 . ethers . utils . defineReadOnly ( _this , "retry" , ( ( options . retry != null ) ? options . retry : 512 ) ) ;
ethers _1 . ethers . utils . defineReadOnly ( _this , "filename" , path _1 . resolve ( options . filename || "./contract.asm" ) ) ;
ethers _1 . ethers . utils . defineReadOnly ( _this , "defines" , Object . freeze ( options . defines || { } ) ) ;
ethers _1 . ethers . utils . defineReadOnly ( _this , "_stack" , [ ] ) ;
_this . reset ( ) ;
return _this ;
}
CodeGenerationAssembler . prototype . _didChange = function ( ) {
this . _changed = true ;
} ;
Object . defineProperty ( CodeGenerationAssembler . prototype , "changed" , {
get : function ( ) {
return this . _changed ;
} ,
2021-03-07 18:24:04 -05:00
enumerable : false ,
2020-02-06 18:21:34 -05:00
configurable : true
} ) ;
// Reset the assmebler for another run with updated values
CodeGenerationAssembler . prototype . reset = function ( ) {
var _this = this ;
this . _changed = false ;
this . _objectCache = { } ;
2020-03-12 19:14:50 +01:00
this . _nextBytecode = { } ;
2020-02-06 18:21:34 -05:00
this . _script = new Script ( this . filename , function ( name , context ) {
return _this . get ( name , context ) ;
} ) ;
2020-03-12 19:14:50 +01:00
this . _checks = [ ] ;
2020-02-06 18:21:34 -05:00
} ;
CodeGenerationAssembler . prototype . evaluate = function ( script , source ) {
return this . _script . evaluate ( script , source ) ;
} ;
2020-03-12 19:14:50 +01:00
CodeGenerationAssembler . prototype . _runChecks = function ( ) {
var _this = this ;
this . _checks . forEach ( function ( func ) {
if ( ! func ( ) ) {
_this . _didChange ( ) ;
}
} ) ;
} ;
2020-02-06 18:21:34 -05:00
CodeGenerationAssembler . prototype . getLinkValue = function ( target , source ) {
2020-03-12 19:14:50 +01:00
var _this = this ;
2020-02-06 18:21:34 -05:00
// Since we are iteratively generating code, offsets and lengths
// may not be stable at any given point in time, so if an offset
// is negative the code is obviously wrong, however we set it to
// 0 so we can proceed with generation to fill in as many blanks
// as possible; then we will try assembling again
var result = _super . prototype . getLinkValue . call ( this , target , source ) ;
if ( typeof ( result ) === "number" ) {
if ( result < 0 ) {
2020-03-12 19:14:50 +01:00
this . _checks . push ( function ( ) { return false ; } ) ;
2020-02-06 18:21:34 -05:00
return 0 ;
2020-02-04 01:06:47 -05:00
}
2020-03-12 19:14:50 +01:00
this . _checks . push ( function ( ) {
return ( _super . prototype . getLinkValue . call ( _this , target , source ) === result ) ;
} ) ;
2020-02-06 18:21:34 -05:00
return result ;
}
2020-03-12 19:14:50 +01:00
// The offset cannot be used so is independent
try {
if ( result . offset < 0 ) {
this . _checks . push ( function ( ) { return false ; } ) ;
result . offset = 0 ;
//this._didChange();
}
else {
this . _checks . push ( function ( ) {
var check = _super . prototype . getLinkValue . call ( _this , target , source ) ;
if ( check . offset === result . offset && ethers _1 . ethers . utils . hexlify ( check ) === ethers _1 . ethers . utils . hexlify ( result ) ) {
return true ;
}
return false ;
} ) ;
}
}
catch ( error ) {
this . _checks . push ( function ( ) {
var check = _super . prototype . getLinkValue . call ( _this , target , source ) ;
return ( ethers _1 . ethers . utils . hexlify ( check ) === ethers _1 . ethers . utils . hexlify ( result ) ) ;
} ) ;
2020-02-04 01:06:47 -05:00
}
2020-02-06 18:21:34 -05:00
return result ;
2020-02-04 01:06:47 -05:00
} ;
2020-02-06 18:21:34 -05:00
CodeGenerationAssembler . prototype . start = function ( node ) {
this . _stack . push ( node ) ;
2020-03-12 19:14:50 +01:00
//this._oldBytecode[node.tag] = this.getBytecode(node);
//this.setBytecode(node, "0x");
this . _nextBytecode [ node . tag ] = "0x" ;
2020-02-06 18:21:34 -05:00
} ;
CodeGenerationAssembler . prototype . end = function ( node ) {
2020-03-12 19:14:50 +01:00
var _this = this ;
2020-02-06 18:21:34 -05:00
if ( this . _stack . pop ( ) !== node ) {
2020-03-12 19:14:50 +01:00
throwError ( "missing push/pop pair" , node . location ) ;
2020-02-06 18:21:34 -05:00
}
2020-03-12 19:14:50 +01:00
var oldBytecode = this . getBytecode ( node ) ;
this . setBytecode ( node , this . _nextBytecode [ node . tag ] ) ;
if ( ! ( node instanceof PaddingNode ) ) {
this . _checks . push ( function ( ) {
return ( oldBytecode === _this . getBytecode ( node ) ) ;
} ) ;
2020-02-06 18:21:34 -05:00
}
} ;
// This is used by evaluate to access properties in JavaScript
// - "defines" allow meta-programming values to be used
// - jump destinations are available as numbers
// - bytecode and data are available as an immuatble DataSource
CodeGenerationAssembler . prototype . get = function ( name , source ) {
2020-02-04 01:06:47 -05:00
if ( name === "defines" ) {
return this . defines ;
}
2020-03-12 19:14:50 +01:00
else if ( name === "_ok" ) {
this . _runChecks ( ) ;
return ! this . _didChange ;
}
2020-02-04 01:06:47 -05:00
var node = this . labels [ name ] ;
if ( ! node ) {
return undefined ;
}
2020-02-06 18:21:34 -05:00
// We cache objects when they are generated so all nodes
// receive consistent data; if there is a change we will
// run the entire assembly process again with the updated
// values
if ( this . _objectCache [ node . tag ] == null ) {
2020-03-12 19:14:50 +01:00
var result = this . getLinkValue ( node , source ) ;
if ( typeof ( result ) !== "number" ) {
result . _freeze ( ) ;
}
this . _objectCache [ node . tag ] = result ;
2020-02-04 01:06:47 -05:00
}
2020-02-06 18:21:34 -05:00
return this . _objectCache [ node . tag ] ;
2020-02-04 01:06:47 -05:00
} ;
2020-02-06 18:21:34 -05:00
CodeGenerationAssembler . prototype . _assemble = function ( ) {
2020-02-04 01:06:47 -05:00
return _ _awaiter ( this , void 0 , void 0 , function ( ) {
2020-02-06 18:21:34 -05:00
var offset ;
2020-02-04 01:06:47 -05:00
var _this = this ;
return _ _generator ( this , function ( _a ) {
switch ( _a . label ) {
case 0 :
offset = 0 ;
return [ 4 /*yield*/ , this . root . assemble ( this , function ( node , bytecode ) {
// Things have moved; we will need to try again
2020-02-06 18:21:34 -05:00
if ( _this . getOffset ( node ) !== offset ) {
_this . setOffset ( node , offset ) ;
2020-03-12 19:14:50 +01:00
//this._didChange();
_this . _checks . push ( function ( ) { return false ; } ) ;
2020-02-04 01:06:47 -05:00
}
2020-02-06 18:21:34 -05:00
_this . _stack . forEach ( function ( node ) {
2020-03-12 19:14:50 +01:00
_this . _nextBytecode [ node . tag ] = hexConcat ( [
_this . _nextBytecode [ node . tag ] ,
2020-02-06 18:21:34 -05:00
bytecode
2020-03-12 19:14:50 +01:00
] ) ;
2020-02-06 18:21:34 -05:00
} ) ;
2020-02-04 01:06:47 -05:00
offset += ethers _1 . ethers . utils . hexDataLength ( bytecode ) ;
} ) ] ;
case 1 :
_a . sent ( ) ;
2020-03-12 19:14:50 +01:00
this . _runChecks ( ) ;
2020-02-06 18:21:34 -05:00
return [ 2 /*return*/ ] ;
2020-02-04 01:06:47 -05:00
}
} ) ;
} ) ;
} ;
2020-02-06 18:21:34 -05:00
CodeGenerationAssembler . prototype . assemble = function ( label ) {
2020-02-04 01:06:47 -05:00
return _ _awaiter ( this , void 0 , void 0 , function ( ) {
2020-02-06 18:21:34 -05:00
var target , i ;
2020-02-04 01:06:47 -05:00
return _ _generator ( this , function ( _a ) {
switch ( _a . label ) {
2020-02-06 18:21:34 -05:00
case 0 :
if ( label == null ) {
label = "_" ;
}
target = this . getTarget ( label ) ;
if ( ! target ) {
logger . throwArgumentError ( "unknown labelled target: " + label , "label" , label ) ;
}
else if ( ! ( target instanceof ScopeNode || target instanceof DataNode ) ) {
logger . throwArgumentError ( "cannot assemble a bodyless label: " + label , "label" , label ) ;
}
// Continue re-evaluating the bytecode until a stable set of
// offsets, length and values are reached.
return [ 4 /*yield*/ , this . _assemble ( ) ] ;
2020-02-04 01:06:47 -05:00
case 1 :
2020-02-06 18:21:34 -05:00
// Continue re-evaluating the bytecode until a stable set of
// offsets, length and values are reached.
_a . sent ( ) ;
2020-02-04 01:06:47 -05:00
i = 0 ;
_a . label = 2 ;
case 2 :
if ( ! ( i < this . retry ) ) return [ 3 /*break*/ , 5 ] ;
// Regenerate the code with the updated assembler values
this . reset ( ) ;
return [ 4 /*yield*/ , this . _assemble ( ) ] ;
case 3 :
2020-02-06 18:21:34 -05:00
_a . sent ( ) ;
2020-02-04 01:06:47 -05:00
// Generated bytecode is stable!! :)
if ( ! this . changed ) {
2020-02-06 18:21:34 -05:00
// This should not happen; something is wrong with the grammar
// or missing enter/exit call in assemble
if ( this . _stack . length !== 0 ) {
2020-03-12 19:14:50 +01:00
throwError ( "Bad AST! Bad grammar?!" , null ) ;
2020-02-06 18:21:34 -05:00
}
//console.log(`Assembled in ${ i } attempts`);
return [ 2 /*return*/ , this . getBytecode ( target ) ] ;
2020-02-04 01:06:47 -05:00
}
_a . label = 4 ;
case 4 :
i ++ ;
return [ 3 /*break*/ , 2 ] ;
2020-02-06 18:21:34 -05:00
case 5 : return [ 2 /*return*/ , logger . throwError ( "unable to assemble; " + this . retry + " attempts failed to generate stable bytecode" , ethers _1 . ethers . utils . Logger . errors . UNKNOWN _ERROR , { } ) ] ;
2020-02-04 01:06:47 -05:00
}
} ) ;
} ) ;
} ;
2020-02-06 18:21:34 -05:00
return CodeGenerationAssembler ;
} ( Assembler ) ) ;
function parse ( code , options ) {
if ( options == null ) {
options = { } ;
}
// Since jison allows \n, \r or \r\n line endings, we need some
// twekaing to get the correct position
var lines = [ ] ;
var offset = 0 ;
code . split ( /(\r\n?|\n)/g ) . forEach ( function ( clump , index ) {
if ( index % 2 ) {
lines [ lines . length - 1 ] . line += clump ;
}
else {
lines . push ( { line : clump , offset : offset } ) ;
}
offset += clump . length ;
} ) ;
// Add a mock-EOF to the end of the file so we don't out-of-bounds
// on the last character
if ( lines . length ) {
lines [ lines . length - 1 ] . line += "\n" ;
}
// Givens a line (1 offset) and column (0 offset) return the byte offset
var getOffset = function ( line , column ) {
var info = lines [ line - 1 ] ;
if ( ! info || column >= info . line . length ) {
throw new Error ( "out of range" ) ;
}
return info . offset + column ;
} ;
// We use this in the _parser to convert locations to source
_parser _1 . parser . yy . _ethersLocation = function ( loc ) {
// The _ scope should call with null to get the full source
if ( loc == null ) {
return {
offset : 0 ,
line : 0 ,
length : code . length ,
source : code ,
statement : true
} ;
}
var offset = getOffset ( loc . first _line , loc . first _column ) ;
var end = getOffset ( loc . last _line , loc . last _column ) ;
return {
offset : offset ,
line : loc . first _line - 1 ,
length : ( end - offset ) ,
source : code . substring ( offset , end ) ,
statement : ( ! ! loc . statement )
} ;
} ;
var result = Node . from ( _parser _1 . parse ( code ) ) ;
// Nuke the source code lookup callback
_parser _1 . parser . yy . _ethersLocation = null ;
// Semantic Checks
var checker = new SemanticChecker ( result ) ;
var errors = checker . check ( ) ;
if ( errors . filter ( function ( e ) { return ( e . severity === SemanticErrorSeverity . error ) ; } ) . length || ( errors . length && ! options . ignoreWarnings ) ) {
var error = new Error ( "semantic errors during parsing" ) ;
error . errors = errors ;
throw error ;
}
return result ;
}
exports . parse = parse ;
2020-02-04 01:06:47 -05:00
function assemble ( ast , options ) {
return _ _awaiter ( this , void 0 , void 0 , function ( ) {
var assembler ;
return _ _generator ( this , function ( _a ) {
2020-02-06 18:21:34 -05:00
assembler = new CodeGenerationAssembler ( ast , options || { } ) ;
return [ 2 /*return*/ , assembler . assemble ( options . target || "_" ) ] ;
2020-02-04 01:06:47 -05:00
} ) ;
} ) ;
}
exports . assemble = assemble ;
2020-07-13 08:03:56 -04:00
//# sourceMappingURL=assembler.js.map