Commit 2dc8d11a authored by aniket-engg's avatar aniket-engg Committed by Aniket

imports and some type updated

parent 3f4965e0
'use strict' 'use strict'
const EventManager = require('../eventManager') import { EventManager } from '../eventManager'
const traceHelper = require('../trace/traceHelper') import { isContractCreation } from '../trace/traceHelper'
const SourceMappingDecoder = require('../source/sourceMappingDecoder') import { findNodeAtInstructionIndex } from '../source/sourceMappingDecoder'
import { CodeResolver } from './codeResolver' import { CodeResolver } from './codeResolver'
/* /*
...@@ -70,7 +70,7 @@ export class CodeManager { ...@@ -70,7 +70,7 @@ export class CodeManager {
* @param {Function} cb - callback function, return the bytecode * @param {Function} cb - callback function, return the bytecode
*/ */
async getCode (address) { async getCode (address) {
if (!traceHelper.isContractCreation(address)) { if (!isContractCreation(address)) {
const code = await this.codeResolver.resolveCode(address) const code = await this.codeResolver.resolveCode(address)
return code return code
} }
...@@ -131,7 +131,7 @@ export class CodeManager { ...@@ -131,7 +131,7 @@ export class CodeManager {
*/ */
getFunctionFromPC (address, pc, sourceMap, ast) { getFunctionFromPC (address, pc, sourceMap, ast) {
const instIndex = this.codeResolver.getInstructionIndex(address, pc) const instIndex = this.codeResolver.getInstructionIndex(address, pc)
return SourceMappingDecoder.findNodeAtInstructionIndex('FunctionDefinition', instIndex, sourceMap, ast) return findNodeAtInstructionIndex('FunctionDefinition', instIndex, sourceMap, ast)
} }
private retrieveCodeAndTrigger (codeMananger, address, stepIndex, tx) { private retrieveCodeAndTrigger (codeMananger, address, stepIndex, tx) {
......
const EventManager = require('../eventManager') import { EventManager } from '../eventManager'
const StorageResolver = require('../storage/storageResolver') import { StorageResolver } from '../storage/storageResolver'
const StorageViewer = require('../storage/storageViewer') import { StorageViewer } from '../storage/storageViewer'
import { helpers } from '@remix-project/remix-lib' import { helpers } from '@remix-project/remix-lib'
import { DebuggerSolidityState } from './solidityState' import { DebuggerSolidityState } from './solidityState'
import { DebuggerSolidityLocals } from './solidityLocals' import { DebuggerSolidityLocals } from './solidityLocals'
const ui = helpers.ui const { ui } = helpers
export class VmDebuggerLogic { export class VmDebuggerLogic {
......
'use strict' 'use strict'
const Ethdebugger = require('../Ethdebugger') import { Ethdebugger } from '../Ethdebugger'
const EventManager = require('../eventManager') import { EventManager } from '../eventManager'
const traceHelper = require('../trace/traceHelper') import { contractCreationToken } from '../trace/traceHelper'
import { BreakpointManager } from '../code/breakpointManager' import { BreakpointManager } from '../code/breakpointManager'
import { DebuggerStepManager } from './stepManager' import { DebuggerStepManager } from './stepManager'
import { VmDebuggerLogic } from './VmDebugger' import { VmDebuggerLogic } from './VmDebugger'
...@@ -18,7 +18,6 @@ export class Debugger { ...@@ -18,7 +18,6 @@ export class Debugger {
constructor (options) { constructor (options) {
this.event = new EventManager() this.event = new EventManager()
this.offsetToLineColumnConverter = options.offsetToLineColumnConverter this.offsetToLineColumnConverter = options.offsetToLineColumnConverter
/* /*
Returns a compilation result for a given address or the last one available if none are found Returns a compilation result for a given address or the last one available if none are found
...@@ -96,7 +95,7 @@ export class Debugger { ...@@ -96,7 +95,7 @@ export class Debugger {
this.debugger.web3 = web3 this.debugger.web3 = web3
} }
debug (blockNumber, txNumber, tx, loadingCb) { debug (blockNumber, txNumber, tx, loadingCb): Promise<void> {
const web3 = this.debugger.web3 const web3 = this.debugger.web3
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
...@@ -106,7 +105,7 @@ export class Debugger { ...@@ -106,7 +105,7 @@ export class Debugger {
if (tx) { if (tx) {
if (!tx.to) { if (!tx.to) {
tx.to = traceHelper.contractCreationToken('0') tx.to = contractCreationToken('0')
} }
this.debugTx(tx, loadingCb) this.debugTx(tx, loadingCb)
return resolve() return resolve()
......
const EventManager = require('../eventManager') import { EventManager } from '../eventManager'
const localDecoder = require('../solidity-decoder/localDecoder') import { solidityLocals } from '../solidity-decoder/localDecoder'
const StorageViewer = require('../storage/storageViewer') import { StorageViewer } from '../storage/storageViewer'
export class DebuggerSolidityLocals { export class DebuggerSolidityLocals {
...@@ -73,16 +73,16 @@ export class DebuggerSolidityLocals { ...@@ -73,16 +73,16 @@ export class DebuggerSolidityLocals {
var memory = result[1].value var memory = result[1].value
try { try {
var storageViewer = new StorageViewer({ stepIndex: this.stepManager.currentStepIndex, tx: this.tx, address: result[2].value }, this.storageResolver, this.traceManager) var storageViewer = new StorageViewer({ stepIndex: this.stepManager.currentStepIndex, tx: this.tx, address: result[2].value }, this.storageResolver, this.traceManager)
localDecoder.solidityLocals(this.stepManager.currentStepIndex, this.internalTreeCall, stack, memory, storageViewer, sourceLocation, cursor).then((locals) => { solidityLocals(this.stepManager.currentStepIndex, this.internalTreeCall, stack, memory, storageViewer, sourceLocation, cursor).then((locals) => {
if (!cursor) { if (!cursor) {
if (!locals.error) { if (!locals['error']) {
this.event.trigger('solidityLocals', [locals]) this.event.trigger('solidityLocals', [locals])
} }
if (!Object.keys(locals).length) { if (!Object.keys(locals).length) {
this.event.trigger('solidityLocalsMessage', ['no locals']) this.event.trigger('solidityLocalsMessage', ['no locals'])
} }
} else { } else {
if (!locals.error) { if (!locals['error']) {
this.event.trigger('solidityLocalsLoadMoreCompleted', [locals]) this.event.trigger('solidityLocalsLoadMoreCompleted', [locals])
} }
} }
......
const EventManager = require('../eventManager') import { EventManager } from '../eventManager'
const stateDecoder = require('../solidity-decoder/stateDecoder') import { decodeState } from '../solidity-decoder/stateDecoder'
const StorageViewer = require('../storage/storageViewer') import { StorageViewer } from '../storage/storageViewer'
export class DebuggerSolidityState { export class DebuggerSolidityState {
...@@ -76,10 +76,10 @@ export class DebuggerSolidityState { ...@@ -76,10 +76,10 @@ export class DebuggerSolidityState {
extractStateVariables (stateVars, address) { extractStateVariables (stateVars, address) {
const storageViewer = new StorageViewer({ stepIndex: this.stepManager.currentStepIndex, tx: this.tx, address: address }, this.storageResolver, this.traceManager) const storageViewer = new StorageViewer({ stepIndex: this.stepManager.currentStepIndex, tx: this.tx, address: address }, this.storageResolver, this.traceManager)
stateDecoder.decodeState(stateVars, storageViewer).then((result) => { decodeState(stateVars, storageViewer).then((result) => {
this.event.trigger('solidityStateMessage', ['']) this.event.trigger('solidityStateMessage', [''])
if (result.error) { if (result['error']) {
return this.event.trigger('solidityStateMessage', [result.error]) return this.event.trigger('solidityStateMessage', [result['error']])
} }
this.event.trigger('solidityState', [result]) this.event.trigger('solidityState', [result])
}) })
......
import { util } from '@remix-project/remix-lib' import { util } from '@remix-project/remix-lib'
const EventManager = require('../eventManager') import { EventManager } from '../eventManager'
export class DebuggerStepManager { export class DebuggerStepManager {
......
'use strict' 'use strict'
const util = require('./util') import { extractHexByteSlice } from './util'
const ValueType = require('./ValueType') import { ValueType } from './ValueType'
export class Address extends ValueType { export class Address extends ValueType {
constructor () { constructor () {
...@@ -11,6 +11,6 @@ export class Address extends ValueType { ...@@ -11,6 +11,6 @@ export class Address extends ValueType {
if (!value) { if (!value) {
return '0x0000000000000000000000000000000000000000' return '0x0000000000000000000000000000000000000000'
} }
return '0x' + util.extractHexByteSlice(value, this.storageBytes, 0).toUpperCase() return '0x' + extractHexByteSlice(value, this.storageBytes, 0).toUpperCase()
} }
} }
'use strict' 'use strict'
const util = require('./util') import { add, toBN, extractHexValue } from './util'
const remixLib = require('@remix-project/remix-lib') import { util } from '@remix-project/remix-lib'
const sha3256 = remixLib.util.sha3_256 import { BN } from 'ethereumjs-util'
const BN = require('ethereumjs-util').BN import { RefType } from './RefType'
const RefType = require('./RefType') const sha3256 = util.sha3_256
export class ArrayType extends RefType { export class ArrayType extends RefType {
underlyingType
arraySize
constructor (underlyingType, arraySize, location) { constructor (underlyingType, arraySize, location) {
let storageSlots = null let storageSlots = null
if (arraySize === 'dynamic') { if (arraySize === 'dynamic') {
...@@ -30,7 +33,7 @@ export class ArrayType extends RefType { ...@@ -30,7 +33,7 @@ export class ArrayType extends RefType {
let size = null let size = null
let slotValue let slotValue
try { try {
slotValue = await util.extractHexValue(location, storageResolver, this.storageBytes) slotValue = await extractHexValue(location, storageResolver, this.storageBytes)
} catch (e) { } catch (e) {
console.log(e) console.log(e)
return { return {
...@@ -43,12 +46,12 @@ export class ArrayType extends RefType { ...@@ -43,12 +46,12 @@ export class ArrayType extends RefType {
slot: location.slot slot: location.slot
} }
if (this.arraySize === 'dynamic') { if (this.arraySize === 'dynamic') {
size = util.toBN('0x' + slotValue) size = toBN('0x' + slotValue)
currentLocation.slot = sha3256(location.slot) currentLocation.slot = sha3256(location.slot)
} else { } else {
size = new BN(this.arraySize) size = new BN(this.arraySize)
} }
var k = util.toBN(0) var k = toBN(0)
for (; k.lt(size) && k.ltn(300); k.iaddn(1)) { for (; k.lt(size) && k.ltn(300); k.iaddn(1)) {
try { try {
ret.push(await this.underlyingType.decodeFromStorage(currentLocation, storageResolver)) ret.push(await this.underlyingType.decodeFromStorage(currentLocation, storageResolver))
...@@ -62,10 +65,10 @@ export class ArrayType extends RefType { ...@@ -62,10 +65,10 @@ export class ArrayType extends RefType {
currentLocation.offset += this.underlyingType.storageBytes currentLocation.offset += this.underlyingType.storageBytes
if (currentLocation.offset + this.underlyingType.storageBytes > 32) { if (currentLocation.offset + this.underlyingType.storageBytes > 32) {
currentLocation.offset = 0 currentLocation.offset = 0
currentLocation.slot = '0x' + util.add(currentLocation.slot, 1).toString(16) currentLocation.slot = '0x' + add(currentLocation.slot, 1).toString(16)
} }
} else { } else {
currentLocation.slot = '0x' + util.add(currentLocation.slot, this.underlyingType.storageSlots).toString(16) currentLocation.slot = '0x' + add(currentLocation.slot, this.underlyingType.storageSlots).toString(16)
currentLocation.offset = 0 currentLocation.offset = 0
} }
} }
......
'use strict' 'use strict'
const ValueType = require('./ValueType') import { ValueType } from './ValueType'
const util = require('./util') import { extractHexByteSlice } from './util'
export class Bool extends ValueType { export class Bool extends ValueType {
constructor () { constructor () {
...@@ -11,7 +11,7 @@ export class Bool extends ValueType { ...@@ -11,7 +11,7 @@ export class Bool extends ValueType {
if (!value) { if (!value) {
return false return false
} }
value = util.extractHexByteSlice(value, this.storageBytes, 0) value = extractHexByteSlice(value, this.storageBytes, 0)
return value !== '00' return value !== '00'
} }
} }
'use strict' 'use strict'
const util = require('./util') import { extractHexValue, readFromStorage } from './util'
const remixLib = require('@remix-project/remix-lib') import { util } from '@remix-project/remix-lib'
const sha3256 = remixLib.util.sha3_256 import { BN } from 'ethereumjs-util'
const BN = require('ethereumjs-util').BN import { RefType } from './RefType'
const RefType = require('./RefType') const sha3256 = util.sha3_256
export class DynamicByteArray extends RefType { export class DynamicByteArray extends RefType {
constructor (location) { constructor (location) {
...@@ -13,29 +13,29 @@ export class DynamicByteArray extends RefType { ...@@ -13,29 +13,29 @@ export class DynamicByteArray extends RefType {
async decodeFromStorage (location, storageResolver) { async decodeFromStorage (location, storageResolver) {
let value = '0x0' let value = '0x0'
try { try {
value = await util.extractHexValue(location, storageResolver, this.storageBytes) value = await extractHexValue(location, storageResolver, this.storageBytes)
} catch (e) { } catch (e) {
console.log(e) console.log(e)
return {value: '<decoding failed - ' + e.message + '>', type: this.typeName} return {value: '<decoding failed - ' + e.message + '>', type: this.typeName}
} }
const bn = new BN(value, 16) const bn = new BN(value, 16)
if (bn.testn(0)) { if (bn.testn(0)) {
const length = bn.div(new BN(2)) const length: BN = bn.div(new BN(2))
let dataPos = new BN(sha3256(location.slot).replace('0x', ''), 16) let dataPos = new BN(sha3256(location.slot).replace('0x', ''), 16)
let ret = '' let ret = ''
let currentSlot = '0x' let currentSlot = '0x'
try { try {
currentSlot = await util.readFromStorage(dataPos, storageResolver) currentSlot = await readFromStorage(dataPos, storageResolver)
} catch (e) { } catch (e) {
console.log(e) console.log(e)
return {value: '<decoding failed - ' + e.message + '>', type: this.typeName} return {value: '<decoding failed - ' + e.message + '>', type: this.typeName}
} }
while (length.gt(ret.length) && ret.length < 32000) { while (length.gt(new BN(ret.length)) && ret.length < 32000) {
currentSlot = currentSlot.replace('0x', '') currentSlot = currentSlot.replace('0x', '')
ret += currentSlot ret += currentSlot
dataPos = dataPos.add(new BN(1)) dataPos = dataPos.add(new BN(1))
try { try {
currentSlot = await util.readFromStorage(dataPos, storageResolver) currentSlot = await readFromStorage(dataPos, storageResolver)
} catch (e) { } catch (e) {
console.log(e) console.log(e)
return {value: '<decoding failed - ' + e.message + '>', type: this.typeName} return {value: '<decoding failed - ' + e.message + '>', type: this.typeName}
......
'use strict' 'use strict'
const ValueType = require('./ValueType') import { ValueType } from './ValueType'
export class Enum extends ValueType { export class Enum extends ValueType {
enumDef
constructor (enumDef) { constructor (enumDef) {
let storageBytes = 0 let storageBytes = 0
let length = enumDef.members.length let length = enumDef.members.length
......
'use strict' 'use strict'
const ValueType = require('./ValueType') import { ValueType } from './ValueType'
export class FixedByteArray extends ValueType { export class FixedByteArray extends ValueType {
constructor (storageBytes) { constructor (storageBytes) {
......
'use strict' 'use strict'
const util = require('./util') import { extractHexByteSlice, decodeIntFromHex } from './util'
const ValueType = require('./ValueType') import { ValueType } from './ValueType'
export class Int extends ValueType { export class Int extends ValueType {
constructor (storageBytes) { constructor (storageBytes) {
...@@ -8,7 +8,7 @@ export class Int extends ValueType { ...@@ -8,7 +8,7 @@ export class Int extends ValueType {
} }
decodeValue (value) { decodeValue (value) {
value = util.extractHexByteSlice(value, this.storageBytes, 0) value = extractHexByteSlice(value, this.storageBytes, 0)
return util.decodeIntFromHex(value, this.storageBytes, true) return decodeIntFromHex(value, this.storageBytes, true)
} }
} }
'use strict' 'use strict'
const RefType = require('./RefType') import { RefType } from './RefType'
const util = require('./util') import { normalizeHex } from './util'
const ethutil = require('ethereumjs-util') import { toBuffer, setLengthLeft, keccak, BN, bufferToHex} from 'ethereumjs-util'
import { intToBuffer } from 'ethjs-util'
export class Mapping extends RefType { export class Mapping extends RefType {
keyType
valueType
initialDecodedState
constructor (underlyingTypes, location, fullType) { constructor (underlyingTypes, location, fullType) {
super(1, 32, fullType, 'storage') super(1, 32, fullType, 'storage')
this.keyType = underlyingTypes.keyType this.keyType = underlyingTypes.keyType
...@@ -38,7 +44,7 @@ export class Mapping extends RefType { ...@@ -38,7 +44,7 @@ export class Mapping extends RefType {
} }
async decodeMappingsLocation (preimages, location, storageResolver) { async decodeMappingsLocation (preimages, location, storageResolver) {
const mapSlot = util.normalizeHex(ethutil.bufferToHex(location.slot)) const mapSlot = normalizeHex(bufferToHex(location.slot))
if (!preimages[mapSlot]) { if (!preimages[mapSlot]) {
return {} return {}
} }
...@@ -60,14 +66,14 @@ function getMappingLocation (key, position) { ...@@ -60,14 +66,14 @@ function getMappingLocation (key, position) {
// > the value corresponding to a mapping key k is located at keccak256(k . p) where . is concatenation. // > the value corresponding to a mapping key k is located at keccak256(k . p) where . is concatenation.
// key should be a hex string, and position an int // key should be a hex string, and position an int
const mappingK = ethutil.toBuffer('0x' + key) const mappingK = toBuffer('0x' + key)
let mappingP = ethutil.intToBuffer(position) let mappingP = intToBuffer(position)
mappingP = ethutil.setLengthLeft(mappingP, 32) mappingP = setLengthLeft(mappingP, 32)
const mappingKeyBuf = concatTypedArrays(mappingK, mappingP) const mappingKeyBuf = concatTypedArrays(mappingK, mappingP)
const mappingKeyPreimage = '0x' + mappingKeyBuf.toString('hex') const mappingKeyPreimage: string = '0x' + mappingKeyBuf.toString('hex')
let mappingStorageLocation = ethutil.keccak(mappingKeyPreimage) let mappingStorageLocation: Buffer = keccak(mappingKeyPreimage)
mappingStorageLocation = new ethutil.BN(mappingStorageLocation, 16) const mappingStorageLocationinBn: BN = new BN(mappingStorageLocation, 16)
return mappingStorageLocation return mappingStorageLocationinBn
} }
function concatTypedArrays (a, b) { // a, b TypedArray of same type function concatTypedArrays (a, b) { // a, b TypedArray of same type
......
'use strict' 'use strict'
const util = require('./util') import { toBN } from './util'
export class RefType { export class RefType {
......
'use strict' 'use strict'
const DynamicBytes = require('./DynamicByteArray') import { DynamicByteArray } from './DynamicByteArray'
export class StringType extends DynamicByteArray {
typeName
export class StringType extends DynamicBytes {
constructor (location) { constructor (location) {
super(location) super(location)
this.typeName = 'string' this.typeName = 'string'
} }
async decodeFromStorage (location, storageResolver) { async decodeFromStorage (location, storageResolver) {
let decoded = '0x' let decoded: any = '0x'
try { try {
decoded = await super.decodeFromStorage(location, storageResolver) decoded = await super.decodeFromStorage(location, storageResolver)
} catch (e) { } catch (e) {
...@@ -20,7 +23,7 @@ export class StringType extends DynamicBytes { ...@@ -20,7 +23,7 @@ export class StringType extends DynamicBytes {
async decodeFromStack (stackDepth, stack, memory) { async decodeFromStack (stackDepth, stack, memory) {
try { try {
return await super.decodeFromStack(stackDepth, stack, memory) return await super.decodeFromStack(stackDepth, stack, memory, null, null)
} catch (e) { } catch (e) {
console.log(e) console.log(e)
return '<decoding failed - ' + e.message + '>' return '<decoding failed - ' + e.message + '>'
......
'use strict' 'use strict'
const util = require('./util') import { add } from './util'
const RefType = require('./RefType') import { RefType } from './RefType'
export class Struct extends RefType { export class Struct extends RefType {
members
constructor (memberDetails, location, fullType) { constructor (memberDetails, location, fullType) {
super(memberDetails.storageSlots, 32, 'struct ' + fullType, location) super(memberDetails.storageSlots, 32, 'struct ' + fullType, location)
this.members = memberDetails.members this.members = memberDetails.members
...@@ -13,7 +16,7 @@ export class Struct extends RefType { ...@@ -13,7 +16,7 @@ export class Struct extends RefType {
for (var item of this.members) { for (var item of this.members) {
const globalLocation = { const globalLocation = {
offset: location.offset + item.storagelocation.offset, offset: location.offset + item.storagelocation.offset,
slot: util.add(location.slot, item.storagelocation.slot) slot: add(location.slot, item.storagelocation.slot)
} }
try { try {
ret[item.name] = await item.type.decodeFromStorage(globalLocation, storageResolver) ret[item.name] = await item.type.decodeFromStorage(globalLocation, storageResolver)
......
'use strict' 'use strict'
const util = require('./util') import { extractHexByteSlice, decodeIntFromHex } from './util'
const ValueType = require('./ValueType') import { ValueType } from './ValueType'
export class Uint extends ValueType { export class Uint extends ValueType {
constructor (storageBytes) { constructor (storageBytes) {
...@@ -8,7 +8,7 @@ export class Uint extends ValueType { ...@@ -8,7 +8,7 @@ export class Uint extends ValueType {
} }
decodeValue (value) { decodeValue (value) {
value = util.extractHexByteSlice(value, this.storageBytes, 0) value = extractHexByteSlice(value, this.storageBytes, 0)
return util.decodeIntFromHex(value, this.storageBytes, false) return decodeIntFromHex(value, this.storageBytes, false)
} }
} }
'use strict' 'use strict'
var util = require('./util') import { extractHexValue } from './util'
export class ValueType { export class ValueType {
...@@ -24,7 +24,7 @@ export class ValueType { ...@@ -24,7 +24,7 @@ export class ValueType {
*/ */
async decodeFromStorage (location, storageResolver) { async decodeFromStorage (location, storageResolver) {
try { try {
var value = await util.extractHexValue(location, storageResolver, this.storageBytes) var value = await extractHexValue(location, storageResolver, this.storageBytes)
return {value: this.decodeValue(value), type: this.typeName} return {value: this.decodeValue(value), type: this.typeName}
} catch (e) { } catch (e) {
console.log(e) console.log(e)
...@@ -62,5 +62,3 @@ export class ValueType { ...@@ -62,5 +62,3 @@ export class ValueType {
return {value: this.decodeValue(value), type: this.typeName} return {value: this.decodeValue(value), type: this.typeName}
} }
} }
module.exports = ValueType
...@@ -9,7 +9,7 @@ export function decodeIntFromHex (value, byteLength, signed) { ...@@ -9,7 +9,7 @@ export function decodeIntFromHex (value, byteLength, signed) {
return bigNumber.toString(10) return bigNumber.toString(10)
} }
export function readFromStorage(slot, storageResolver) { export function readFromStorage(slot, storageResolver): Promise<string> {
const hexSlot = '0x' + normalizeHex(bufferToHex(slot)) const hexSlot = '0x' + normalizeHex(bufferToHex(slot))
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
storageResolver.storageSlot(hexSlot, (error, slot) => { storageResolver.storageSlot(hexSlot, (error, slot) => {
...@@ -94,7 +94,7 @@ export function extractLocationFromAstVariable (node) { ...@@ -94,7 +94,7 @@ export function extractLocationFromAstVariable (node) {
return 'default' // local variables => storage, function parameters & return values => memory, state => storage return 'default' // local variables => storage, function parameters & return values => memory, state => storage
} }
export function normalizeHex (hex) { export function normalizeHex (hex): string {
hex = hex.replace('0x', '') hex = hex.replace('0x', '')
if (hex.length < 64) { if (hex.length < 64) {
return (new Array(64 - hex.length + 1).join('0')) + hex return (new Array(64 - hex.length + 1).join('0')) + hex
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment