/**
* Copyright (c) 2012-2016 André Bargull
* Alle Rechte vorbehalten / All Rights Reserved. Use is subject to license terms.
*
* <https://github.com/anba/es6draft>
*/
package com.github.anba.es6draft.runtime.objects;
import static com.github.anba.es6draft.runtime.types.Undefined.UNDEFINED;
import java.util.EnumSet;
import com.github.anba.es6draft.Script;
import com.github.anba.es6draft.compiler.CompilationException;
import com.github.anba.es6draft.parser.Parser;
import com.github.anba.es6draft.parser.ParserException;
import com.github.anba.es6draft.runtime.ExecutionContext;
import com.github.anba.es6draft.runtime.Realm;
import com.github.anba.es6draft.runtime.internal.Source;
import com.github.anba.es6draft.runtime.types.Callable;
import com.github.anba.es6draft.runtime.types.Type;
/**
* <h1>18 The Global Object</h1><br>
* <h2>18.2 Function Properties of the Global Object</h2>
* <ul>
* <li>18.2.1 eval (x)
* </ul>
*/
public final class Eval {
private Eval() {
}
public enum EvalFlags {
/**
* Flag for direct eval calls
*/
Direct(0x0001),
/**
* Flag for strict-mode eval calls
*/
Strict(0x0002),
/**
* Flag for global code eval calls
*/
GlobalCode(0x0004),
/**
* Flag for global scope eval calls
*/
GlobalScope(0x0008),
/**
* Flag for global this eval calls
*/
GlobalThis(0x0010),
/**
* Flag for eval calls enclosed by with-statement
*/
EnclosedByWithStatement(0x0020),
/**
* Flag for eval calls enclosed by lexical declaration
*/
EnclosedByLexicalDeclaration(0x0040);
private final int value;
private EvalFlags(int value) {
this.value = value;
}
public int getValue() {
return value;
}
boolean isSet(int bitmask) {
return (value & bitmask) != 0;
}
static EnumSet<Parser.Option> toOptions(int flags) {
EnumSet<Parser.Option> options = EnumSet.of(Parser.Option.EvalScript);
if (EvalFlags.Direct.isSet(flags)) {
options.add(Parser.Option.DirectEval);
}
if (EvalFlags.Strict.isSet(flags)) {
options.add(Parser.Option.Strict);
}
if (!EvalFlags.GlobalCode.isSet(flags)) {
options.add(Parser.Option.FunctionCode);
}
if (!EvalFlags.GlobalScope.isSet(flags)) {
options.add(Parser.Option.LocalScope);
}
if (!EvalFlags.GlobalThis.isSet(flags)) {
options.add(Parser.Option.FunctionThis);
}
if (EvalFlags.EnclosedByWithStatement.isSet(flags)) {
options.add(Parser.Option.EnclosedByWithStatement);
}
if (EvalFlags.EnclosedByLexicalDeclaration.isSet(flags)) {
options.add(Parser.Option.EnclosedByLexicalDeclaration);
}
return options;
}
public static int toFlags(EnumSet<Parser.Option> options) {
int flags = 0;
if (options.contains(Parser.Option.DirectEval)) {
flags |= EvalFlags.Direct.getValue();
}
if (options.contains(Parser.Option.Strict)) {
flags |= EvalFlags.Strict.getValue();
}
if (!options.contains(Parser.Option.FunctionCode)) {
flags |= EvalFlags.GlobalCode.getValue();
}
if (!options.contains(Parser.Option.LocalScope)) {
flags |= EvalFlags.GlobalScope.getValue();
}
if (!options.contains(Parser.Option.FunctionThis)) {
flags |= EvalFlags.GlobalThis.getValue();
}
if (options.contains(Parser.Option.EnclosedByWithStatement)) {
flags |= EvalFlags.EnclosedByWithStatement.getValue();
}
if (options.contains(Parser.Option.EnclosedByLexicalDeclaration)) {
flags |= EvalFlags.EnclosedByLexicalDeclaration.getValue();
}
return flags;
}
}
/**
* 18.2.1 eval (x)
*
* @param cx
* the execution context
* @param caller
* the caller context
* @param source
* the source string
* @return the evaluation result
*/
public static Object globalEval(ExecutionContext cx, ExecutionContext caller, Object source) {
return PerformEval(cx, caller, source, EvalFlags.GlobalCode.getValue()
| EvalFlags.GlobalScope.getValue() | EvalFlags.GlobalThis.getValue());
}
/**
* 18.2.1 eval (x)
*
* @param cx
* the execution context
* @param caller
* the caller context
* @param arguments
* the arguments
* @return the evaluation result
*/
public static Object indirectEval(ExecutionContext cx, ExecutionContext caller,
Object... arguments) {
Object source;
Callable indirectEval = cx.getRealm().getIndirectEval();
if (indirectEval != null) {
source = indirectEval.call(cx, cx.getRealm().getRealmObject(), arguments);
} else {
source = arguments.length > 0 ? arguments[0] : UNDEFINED;
}
return globalEval(cx, caller, source);
}
/**
* 18.2.1 eval (x)
* <p>
* [Called from generated code]
*
* @param arguments
* the arguments
* @param cx
* the execution context
* @param flags
* the eval flags
* @return the evaluation result
*/
public static Object directEval(Object[] arguments, ExecutionContext cx, int flags) {
assert EvalFlags.Direct.isSet(flags);
Object source;
Callable translate = cx.getRealm().getDirectEvalTranslate();
if (translate != null) {
source = translate.call(cx, cx.getRealm().getRealmObject(), arguments);
} else {
source = arguments.length > 0 ? arguments[0] : UNDEFINED;
}
return PerformEval(cx, cx, source, flags);
}
/**
* 18.2.1 eval (x)
* <p>
* [Called from generated code]
*
* @param source
* the eval source code
* @param cx
* the execution context
* @param flags
* the eval flags
* @return the evaluation result
*/
public static Object directEval(Object source, ExecutionContext cx, int flags) {
assert EvalFlags.Direct.isSet(flags);
return PerformEval(cx, cx, source, flags);
}
/**
* 18.2.1.1 Runtime Semantics: PerformEval( x, evalRealm, strictCaller, direct)
*
* @param cx
* the execution context
* @param caller
* the caller execution context
* @param source
* the source string
* @param flags
* the eval flags
* @return the evaluation result
*/
private static Object PerformEval(ExecutionContext cx, ExecutionContext caller, Object source,
int flags) {
boolean strictCaller = EvalFlags.Strict.isSet(flags);
boolean direct = EvalFlags.Direct.isSet(flags);
assert direct || cx == cx.getRealm().defaultContext() : "indirect eval with non-default context";
/* step 1 */
assert direct || !strictCaller;
/* step 2 */
if (!Type.isString(source)) {
return source;
}
/* step 3 */
Script script = script(cx, caller, Type.stringValue(source).toString(), flags);
/* step 4 */
if (script == null) {
return UNDEFINED;
}
/* steps 5-23 */
return script.evaluate(cx);
}
private static Script script(ExecutionContext cx, ExecutionContext caller, String sourceCode,
int flags) {
try {
Realm realm = cx.getRealm();
Source source = evalSource(realm, caller);
EnumSet<Parser.Option> options = EvalFlags.toOptions(flags);
return realm.getScriptLoader().evalScript(source, sourceCode, options);
} catch (ParserException | CompilationException e) {
throw e.toScriptException(cx);
}
}
private static Source evalSource(Realm realm, ExecutionContext caller) {
Source baseSource = realm.sourceInfo(caller);
String sourceName;
if (baseSource != null) {
sourceName = "<eval> (" + baseSource.getName() + ")";
} else {
sourceName = "<eval>";
}
return new Source(baseSource, sourceName, 1);
}
}