use of com.github.anba.es6draft.compiler.assembler.Jump in project es6draft by anba.
the class EvalDeclarationInstantiationGenerator method generateGlobal.
private void generateGlobal(Script evalScript, InstructionVisitor mv) {
assert evalScript.isGlobalCode() && !evalScript.isStrict() && !evalScript.isScripting();
Variable<ExecutionContext> context = mv.getParameter(EXECUTION_CONTEXT, ExecutionContext.class);
Variable<LexicalEnvironment<GlobalEnvironmentRecord>> varEnv = mv.newVariable("varEnv", LexicalEnvironment.class).uncheckedCast();
Variable<LexicalEnvironment<DeclarativeEnvironmentRecord>> lexEnv = mv.newVariable("lexEnv", LexicalEnvironment.class).uncheckedCast();
Variable<FunctionObject> fo = null;
getVariableEnvironment(context, varEnv, mv);
getLexicalEnvironment(context, lexEnv, mv);
/* step 1 */
Set<Name> varNames = VarDeclaredNames(evalScript);
/* step 2 */
List<StatementListItem> varDeclarations = VarScopedDeclarations(evalScript);
/* step 3 */
Variable<DeclarativeEnvironmentRecord> lexEnvRec = mv.newVariable("lexEnvRec", DeclarativeEnvironmentRecord.class);
/* step 4 */
Variable<GlobalEnvironmentRecord> varEnvRec = mv.newVariable("varEnvRec", GlobalEnvironmentRecord.class);
getEnvironmentRecord(varEnv, varEnvRec, mv);
/* step 5 */
if (!varNames.isEmpty()) {
/* step 5.a */
// Iterate over declarations to be able to emit line-info entries.
HashSet<Name> checkedVarNames = new HashSet<>();
for (StatementListItem item : VarScopedDeclarations(evalScript)) {
if (item instanceof VariableStatement) {
for (VariableDeclaration vd : ((VariableStatement) item).getElements()) {
for (Name name : BoundNames(vd)) {
if (checkedVarNames.add(name)) {
canDeclareVarScopedOrThrow(context, varEnvRec, vd, name, mv);
}
}
}
} else {
HoistableDeclaration d = (HoistableDeclaration) item;
Name name = BoundName(d);
if (checkedVarNames.add(name)) {
canDeclareVarScopedOrThrow(context, varEnvRec, d, name, mv);
}
}
}
/* steps 5.b-d */
if (isEnclosedByLexicalOrHasVarForOf(evalScript)) {
checkLexicalRedeclaration(evalScript, context, varEnv, lexEnv, varNames, mv);
}
}
/* step 6 */
ArrayDeque<HoistableDeclaration> functionsToInitialize = new ArrayDeque<>();
/* step 7 */
HashSet<Name> declaredFunctionNames = new HashSet<>();
/* step 8 */
for (StatementListItem item : reverse(varDeclarations)) {
if (item instanceof HoistableDeclaration) {
HoistableDeclaration d = (HoistableDeclaration) item;
Name fn = BoundName(d);
if (declaredFunctionNames.add(fn)) {
canDeclareGlobalFunctionOrThrow(context, varEnvRec, d, fn, mv);
functionsToInitialize.addFirst(d);
}
}
}
if (!functionsToInitialize.isEmpty()) {
fo = mv.newVariable("fo", FunctionObject.class);
}
/* step 9 */
LinkedHashMap<Name, VariableDeclaration> declaredVarNames = new LinkedHashMap<>();
/* step 10 */
for (StatementListItem d : varDeclarations) {
if (d instanceof VariableStatement) {
for (VariableDeclaration vd : ((VariableStatement) d).getElements()) {
for (Name vn : BoundNames(vd)) {
if (!declaredFunctionNames.contains(vn)) {
canDeclareGlobalVarOrThrow(context, varEnvRec, vd, vn, mv);
declaredVarNames.put(vn, vd);
}
}
}
}
}
// ES2016: Block-scoped global function declarations
if (hasBlockFunctions(evalScript)) {
final boolean catchVar = codegen.isEnabled(CompatibilityOption.CatchVarStatement);
int idCounter = 0;
List<FunctionDeclaration> blockFunctions = evalScript.getScope().blockFunctions();
HashSet<Name> declaredFunctionOrVarNames = new HashSet<>();
declaredFunctionOrVarNames.addAll(declaredFunctionNames);
declaredFunctionOrVarNames.addAll(declaredVarNames.keySet());
for (FunctionDeclaration f : blockFunctions) {
Name fn = f.getName();
Jump next = new Jump();
// Runtime check always required for global block-level function declarations.
f.setLegacyBlockScopeId(++idCounter);
if (isEnclosedByLexical(evalScript)) {
canDeclareVarBinding(varEnv, lexEnv, fn, catchVar, next, mv);
}
// FIXME: spec issue - avoid (observable!) duplicate checks for same name?
// FIXME: spec issue - property creation order important?
canDeclareGlobalFunction(varEnvRec, f, fn, next, mv);
setLegacyBlockFunction(context, f, mv);
if (declaredFunctionOrVarNames.add(fn)) {
createGlobalFunctionBinding(varEnvRec, f, fn, true, mv);
}
mv.mark(next);
}
}
/* step 12 */
List<Declaration> lexDeclarations = LexicallyScopedDeclarations(evalScript);
/* step 13 */
if (!lexDeclarations.isEmpty()) {
getEnvironmentRecord(lexEnv, lexEnvRec, mv);
createLexicalDeclarations(lexDeclarations, lexEnvRec, mv);
}
/* step 14 */
for (HoistableDeclaration f : functionsToInitialize) {
Name fn = BoundName(f);
InstantiateFunctionObject(context, lexEnv, f, mv);
mv.store(fo);
createGlobalFunctionBinding(varEnvRec, f, fn, fo, true, mv);
}
/* step 15 */
for (Map.Entry<Name, VariableDeclaration> e : declaredVarNames.entrySet()) {
createGlobalVarBinding(varEnvRec, e.getValue(), e.getKey(), true, mv);
}
/* step 16 */
mv._return();
}
use of com.github.anba.es6draft.compiler.assembler.Jump in project es6draft by anba.
the class EvalDeclarationInstantiationGenerator method checkLexicalRedeclaration.
/**
* 18.2.1.2, steps 5.b-d
*/
private void checkLexicalRedeclaration(Script evalScript, Variable<ExecutionContext> context, Variable<? extends LexicalEnvironment<? extends EnvironmentRecord>> varEnv, Variable<LexicalEnvironment<DeclarativeEnvironmentRecord>> lexEnv, Set<Name> varNames, InstructionVisitor mv) {
Variable<LexicalEnvironment<EnvironmentRecord>> thisLex = mv.newVariable("thisLex", LexicalEnvironment.class).uncheckedCast();
Variable<EnvironmentRecord> thisEnvRec = mv.newVariable("thisEnvRec", EnvironmentRecord.class).uncheckedCast();
Variable<DeclarativeEnvironmentRecord> envRec = mv.newVariable("envRec", DeclarativeEnvironmentRecord.class).uncheckedCast();
Set<Name> varForOfNames = evalScript.getScope().varForOfDeclaredNames();
final boolean catchVar = codegen.isEnabled(CompatibilityOption.CatchVarStatement);
final boolean hasWith = codegen.isEnabled(Parser.Option.EnclosedByWithStatement);
Jump loopTest = new Jump(), loop = new Jump(), objectEnv = new Jump();
mv.load(lexEnv);
if (hasLexicalEnvironment(evalScript)) {
// Don't need to check own lexical environment.
mv.invoke(Methods.LexicalEnvironment_getOuter);
}
mv.store(thisLex);
mv.nonDestructiveGoTo(loopTest);
{
mv.mark(loop);
getEnvironmentRecord(thisLex, thisEnvRec, mv);
if (hasWith) {
mv.load(thisEnvRec);
mv.instanceOf(Types.ObjectEnvironmentRecord);
mv.ifne(objectEnv);
}
mv.load(thisEnvRec);
mv.checkcast(Types.DeclarativeEnvironmentRecord);
mv.store(envRec);
for (Name name : varNames) {
mv.load(context);
mv.load(envRec);
mv.aconst(name.getIdentifier());
mv.iconst(catchVar && !varForOfNames.contains(name));
mv.invoke(Methods.ScriptRuntime_canDeclareVarOrThrow);
}
if (hasWith) {
mv.mark(objectEnv);
}
mv.load(thisLex);
mv.invoke(Methods.LexicalEnvironment_getOuter);
mv.store(thisLex);
}
mv.mark(loopTest);
mv.load(thisLex);
mv.load(varEnv);
mv.ifacmpne(loop);
}
use of com.github.anba.es6draft.compiler.assembler.Jump in project es6draft by anba.
the class ExpressionGenerator method EvaluateDirectCallEval.
/**
* [12.3.4.3 Runtime Semantics: EvaluateDirectCall( func, thisValue, arguments, tailPosition )]
*
* @param call
* the function call expression
* @param arguments
* the function arguments
* @param hasThisValue
* {@code true} if the thisValue is on the stack
* @param mv
* the code visitor
*/
private ValType EvaluateDirectCallEval(Expression call, List<Expression> arguments, boolean hasThisValue, CodeVisitor mv) {
Jump afterCall = new Jump(), notEval = new Jump();
if (hasThisValue) {
// stack: [func, thisValue] -> [thisValue, func]
mv.swap();
}
/* steps 1-2 (EvaluateDirectCall) */
// stack: [thisValue?, func] -> [thisValue?, args?, func]
boolean constantArguments = hasConstantArguments(arguments);
if (!constantArguments) {
ArgumentListEvaluation(call, arguments, mv);
mv.swap();
}
// Emit line info after evaluating arguments.
mv.lineInfo(call);
/* steps 3-4 (EvaluateDirectCall) */
// stack: [thisValue?, args?, func] -> [thisValue?, args?, func(Callable)]
mv.loadExecutionContext();
mv.invoke(Methods.ScriptRuntime_CheckCallable);
// stack: [thisValue?, args?, func(Callable)] -> [thisValue?, args?, func(Callable)]
mv.dup();
mv.loadExecutionContext();
mv.invoke(Methods.ScriptRuntime_IsBuiltinEval);
mv.ifeq(notEval);
{
PerformEval(call, arguments, hasThisValue, afterCall, mv);
}
mv.mark(notEval);
// stack: [thisValue?, args?, func(Callable)] -> [func(Callable), cx, thisValue, args]
if (constantArguments) {
if (hasThisValue) {
// stack: [thisValue, func(Callable)] -> [...]
mv.loadExecutionContext();
mv.swap1_2();
ArgumentListEvaluation(call, arguments, mv);
} else {
// stack: [func(Callable)] -> [...]
mv.loadExecutionContext();
mv.loadUndefined();
ArgumentListEvaluation(call, arguments, mv);
}
} else {
if (hasThisValue) {
// stack: [thisValue, args, func(Callable)] -> [...]
mv.loadExecutionContext();
mv.swap2();
} else {
// stack: [args, func(Callable)] -> [...]
mv.swap();
mv.loadExecutionContext();
mv.loadUndefined();
mv.swap1_2();
}
}
if (codegen.isEnabled(CompatibilityOption.Realm)) {
// direct-eval fallback hook
Jump noEvalHook = new Jump();
mv.loadExecutionContext();
mv.invoke(Methods.ScriptRuntime_directEvalFallbackHook);
mv.ifnull(noEvalHook);
{
// stack: [func(Callable), cx, thisValue, args] -> [args']
mv.invoke(Methods.ScriptRuntime_directEvalFallbackArguments);
// stack: [args'] -> []
Variable<Object[]> fallbackArguments = mv.newScratchVariable(Object[].class);
mv.store(fallbackArguments);
// stack: [] -> [func(Callable), cx, thisValue, args']
mv.loadExecutionContext();
mv.invoke(Methods.ScriptRuntime_directEvalFallbackHook);
mv.loadExecutionContext();
mv.loadExecutionContext();
mv.invoke(Methods.ScriptRuntime_directEvalFallbackThisArgument);
mv.load(fallbackArguments);
mv.freeVariable(fallbackArguments);
}
mv.mark(noEvalHook);
}
/* steps 5-9 (EvaluateDirectCall) */
if (isTailCall(call, mv)) {
// stack: [func(Callable), cx, thisValue, args'] -> [<func(Callable), thisValue, args>]
mv.invoke(Methods.ScriptRuntime_PrepareForTailCall);
} else {
// stack: [func(Callable), cx, thisValue, args] -> [result]
invokeDynamicCall(mv);
}
mv.mark(afterCall);
return ValType.Any;
}
use of com.github.anba.es6draft.compiler.assembler.Jump in project es6draft by anba.
the class FunctionCodeGenerator method generateAsyncFunctionCall.
/**
* Generate bytecode for:
*
* <pre>
* calleeContext = newFunctionExecutionContext(function, null, thisValue)
* function_init(calleeContext, function, arguments)
* return EvaluateBody(calleeContext, generator)
* </pre>
*
* @param node
* the function node
* @param mv
* the instruction visitor
*/
private void generateAsyncFunctionCall(FunctionNode node, InstructionVisitor mv) {
Variable<OrdinaryAsyncFunction> function = mv.getParameter(FUNCTION, OrdinaryAsyncFunction.class);
Variable<Object> thisValue = mv.getParameter(THIS_VALUE, Object.class);
Variable<Object[]> arguments = mv.getParameter(ARGUMENTS, Object[].class);
Variable<ExecutionContext> calleeContext = mv.newVariable("calleeContext", ExecutionContext.class);
// (1) Create a new ExecutionContext
prepareCallAndBindThis(node, calleeContext, function, thisValue, mv);
// (2) Perform FunctionDeclarationInstantiation
{
TryCatchLabel startCatch = new TryCatchLabel();
TryCatchLabel endCatch = new TryCatchLabel(), handlerCatch = new TryCatchLabel();
Jump noException = new Jump();
mv.mark(startCatch);
functionDeclarationInstantiation(node, calleeContext, function, arguments, mv);
mv.goTo(noException);
mv.mark(endCatch);
mv.catchHandler(handlerCatch, Types.ScriptException);
{
// stack: [exception] -> [cx, exception]
mv.load(calleeContext);
mv.swap();
// stack: [cx, exception] -> [promise]
mv.invoke(Methods.PromiseAbstractOperations_PromiseOf);
mv._return();
}
mv.mark(noException);
mv.tryCatch(startCatch, endCatch, handlerCatch, Types.ScriptException);
}
// (3) Perform EvaluateBody
mv.load(calleeContext);
mv.load(function);
mv.invoke(Methods.OrdinaryAsyncFunction_EvaluateBody);
// (4) Return result value
mv._return();
}
use of com.github.anba.es6draft.compiler.assembler.Jump in project es6draft by anba.
the class FunctionCodeGenerator method returnResultOrThis.
/**
* Generate bytecode for:
*
* <pre>
* if (tailCall && result instanceof TailCallInvocation) {
* return ((TailCallInvocation) result).toConstructTailCall(envRec);
* }
* if (Type.isObject(result)) {
* return Type.objectValue(result);
* }
* if (!Type.isUndefined(result)) {
* throw Errors.newTypeError();
* }
* return envRec.getThisBinding(callerContext);
* </pre>
*
* @param callerContext
* the variable which holds the caller context
* @param calleeContext
* the variable which holds the callee context
* @param tailCall
* {@code true} if the constructor function contains a tail-call
* @param mv
* the instruction visitor
*/
private void returnResultOrThis(Variable<ExecutionContext> callerContext, Variable<ExecutionContext> calleeContext, boolean tailCall, InstructionVisitor mv) {
if (tailCall) {
Jump noTailCall = new Jump();
mv.dup();
mv.instanceOf(Types.TailCallInvocation);
mv.ifeq(noTailCall);
{
mv.checkcast(Types.TailCallInvocation);
mv.load(calleeContext);
mv.invoke(Methods.ExecutionContext_getFunctionVariableEnvironmentRecord);
mv.invoke(Methods.TailCallInvocation_toConstructTailCallWithEnvironment);
mv._return();
}
mv.mark(noTailCall);
}
Jump notObject = new Jump();
mv.dup();
mv.instanceOf(Types.ScriptObject);
mv.ifeq(notObject);
{
mv.checkcast(Types.ScriptObject);
mv._return();
}
mv.mark(notObject);
Jump notUndefined = new Jump();
mv.dup();
mv.loadUndefined();
mv.ifacmpeq(notUndefined);
{
mv.load(callerContext);
mv.get(Fields.MessagesKey_NotObjectTypeFromConstructor);
mv.invoke(Methods.Errors_newTypeError);
mv.athrow();
}
mv.mark(notUndefined);
mv.pop();
mv.load(calleeContext);
mv.invoke(Methods.ExecutionContext_getFunctionVariableEnvironmentRecord);
mv.load(callerContext);
mv.invoke(Methods.FunctionEnvironmentRecord_getThisBinding);
// If the this-binding is present it's a ScriptObject; if it's not present calling
// getThisBinding() will result in a ReferenceError being thrown. So emitting a
// checkcast instruction is safe here.
mv.checkcast(Types.ScriptObject);
mv._return();
}
Aggregations