Home Reference Source

src/values/ErrorValue.js

'use strict';


const PrimitiveValue = require('./PrimitiveValue');
const ObjectValue = require('./ObjectValue');
const Value = require('../Value');
const EvaluatorInstruction = require('../EvaluatorInstruction');

class ErrorInstance extends ObjectValue {
	createNativeAnalog() {
		if ( !this.native ) {
			let stack;
			let NativeClass = this.proto.nativeClass || Error;
			this.native = new NativeClass();
			if ( !this.native.stack ) {
				try {
					throw native;
				} catch ( e ) {
					stack = e.stack;
				}
			} else {
				stack = this.native.stack;
			}

			let frames = stack ? stack.split(/\n/) : [];
			if ( stack.length > 1 ) {
				let header = frames.shift();
				while ( /at (ErrorInstance.createNativeAnalog|ErrorObject.make|Function.makeTypeError)/.test(frames[0]) ) {
					frames.shift();
				}
				this.native.stack = header + '\n' + frames.join('\n');
			}
			for ( var k in this.extra ) this.native[k] = this.extra[k];

		}
		return this.native;
	}
	toNative() {
		let out = this.createNativeAnalog();
		let msg = this.properties['message'].value;
		if ( msg ) out.message = msg.toNative();

		if ( this.properties['stack'] ) {
			msg.stack = this.properties['stack'].value.native;
		}

		return out;
	}

	*addExtra(extra) {
		if ( !this.realm.options.extraErrorInfo ) return;
		let evaluator = yield EvaluatorInstruction.getEvaluator;
		if ( evaluator ) {
			let scope = evaluator.topFrame.scope;
			let ast = extra.ast = evaluator.topFrame.ast;
			extra.scope = scope;
			//TODO: Sometiems scope is undefined, figure out why.
			if ( extra.ast.loc ) {
				extra.line = extra.ast.loc.start.line;
			}

			switch ( extra.code ) {
				case 'UndefinedVariable':
				case 'SmartAccessDenied':
					if ( scope ) extra.candidates = scope.getVariableNames();
					break;
				case 'CallNonFunction':
					let list;
					if ( extra.base && extra.base.getPropertyValueMap ) {
						list = extra.base.getPropertyValueMap();
					} else {
						list = scope.object.getPropertyValueMap();
					}

					extra.candidates = [];
					for ( let k in list ) {
						let v = list[k];
						if ( v && v.isCallable ) {
							extra.candidates.push(k);
						}
					}
					break;
				case 'IndexEmpty':
					break;
			}
		}
		if ( this.native ) {
			for ( var k in extra ) {
				if ( ['ast', 'scope', 'candidates', 'targetAst'].indexOf(k) !== -1 ) {
					Object.defineProperty(this.native, k, {
						value: extra[k],
						enumerable: false
					});
				} else {
					this.native[k] = extra[k];
				}
			}
		}
		this.extra = extra;
	}
}

module.exports = ErrorInstance;