Search in sources :

Example 1 with LabelledHashKey

use of com.github.anba.es6draft.compiler.CodeVisitor.LabelledHashKey in project es6draft by anba.

the class DefaultCodeGenerator method ClassDefinitionEvaluation.

/**
 * 14.5.14 Runtime Semantics: ClassDefinitionEvaluation
 *
 * @param def
 *            the class definition node
 * @param className
 *            the class name or {@code null} if not present
 * @param mv
 *            the code visitor
 */
protected final void ClassDefinitionEvaluation(ClassDefinition def, Name className, CodeVisitor mv) {
    mv.enterVariableScope();
    List<Expression> classDecoratorsList = def.getDecorators();
    Variable<Callable[]> classDecorators = null;
    if (!classDecoratorsList.isEmpty()) {
        classDecorators = mv.newVariable("classDecorators", Callable[].class);
        mv.anewarray(classDecoratorsList.size(), Types.Callable);
        mv.store(classDecorators);
        int index = 0;
        for (Expression decorator : classDecoratorsList) {
            mv.astore(classDecorators, index++, __ -> {
                expressionBoxed(decorator, mv);
                CheckCallable(decorator, mv);
            });
        }
    }
    mv.enterClassDefinition();
    // step 1 (not applicable)
    // steps 2-4
    BlockScope scope = def.getScope();
    Variable<DeclarativeEnvironmentRecord> classScopeEnvRec = null;
    if (scope != null) {
        assert scope.isPresent() == (className != null);
        if (scope.isPresent()) {
            // stack: [] -> [classScope]
            newDeclarativeEnvironment(scope, mv);
            classScopeEnvRec = mv.newVariable("classScopeEnvRec", DeclarativeEnvironmentRecord.class);
            getEnvRec(classScopeEnvRec, mv);
            if (className != null) {
                // stack: [classScope] -> [classScope]
                Name innerName = scope.resolveName(className);
                BindingOp<DeclarativeEnvironmentRecord> op = BindingOp.of(classScopeEnvRec, innerName);
                op.createImmutableBinding(classScopeEnvRec, innerName, true, mv);
            }
            // stack: [classScope] -> []
            pushLexicalEnvironment(mv);
        }
        mv.enterScope(def);
    }
    // steps 5-7
    // stack: [] -> [<constructorParent,proto>]
    Expression classHeritage = def.getHeritage();
    if (classHeritage == null) {
        mv.loadExecutionContext();
        mv.invoke(Methods.ClassOperations_getDefaultClassProto);
    } else if (classHeritage instanceof NullLiteral) {
        mv.loadExecutionContext();
        mv.invoke(Methods.ClassOperations_getClassProto_Null);
    } else {
        expressionBoxed(classHeritage, mv);
        mv.loadExecutionContext();
        mv.lineInfo(def);
        mv.invoke(Methods.ClassOperations_getClassProto);
    }
    // stack: [<protoParent,constructorParent>] -> [<protoParent,constructorParent>]
    Variable<OrdinaryObject> proto = mv.newVariable("proto", OrdinaryObject.class);
    mv.dup();
    mv.aload(0, Types.ScriptObject);
    mv.loadExecutionContext();
    mv.invoke(Methods.ClassOperations_createProto);
    mv.store(proto);
    // stack: [<protoParent,constructorParent>] -> [constructorParent, proto]
    mv.aload(1, Types.ScriptObject);
    mv.load(proto);
    // Push the private-name environment to ensure private names are accessible in the constructor.
    BlockScope bodyScope = def.getBodyScope();
    if (bodyScope != null) {
        List<Name> privateBoundNames = PrivateBoundNames(def);
        assert bodyScope.isPresent() == !privateBoundNames.isEmpty();
        if (bodyScope.isPresent()) {
            // stack: [] -> [classPrivateEnv]
            newDeclarativeEnvironment(bodyScope, mv);
            Variable<DeclarativeEnvironmentRecord> classPrivateEnvRec = mv.newVariable("classPrivateEnvRec", DeclarativeEnvironmentRecord.class);
            getEnvRec(classPrivateEnvRec, mv);
            HashSet<Name> declaredPrivateNames = new HashSet<>();
            for (Name name : privateBoundNames) {
                // FIXME: spec bug - missing check for already declared private names for getter/setter pairs
                if (declaredPrivateNames.add(name)) {
                    BindingOp<DeclarativeEnvironmentRecord> op = BindingOp.of(classPrivateEnvRec, name);
                    op.createImmutableBinding(classPrivateEnvRec, name, true, mv);
                }
            }
            // stack: [classPrivateEnv] -> []
            pushLexicalEnvironment(mv);
        }
        mv.enterScope(bodyScope);
    }
    // steps 8-9
    // stack: [constructorParent, proto] -> [constructorParent, proto, <rti>]
    MethodName method = mv.compile(def, codegen::classDefinition);
    // Runtime Semantics: Evaluation -> MethodDefinition
    mv.invoke(method);
    // step 10 (not applicable)
    // steps 11-18
    // stack: [constructorParent, proto, <rti>] -> [F]
    mv.iconst(classHeritage != null);
    mv.loadExecutionContext();
    mv.lineInfo(def);
    mv.invoke(Methods.ClassOperations_EvaluateConstructorMethod);
    // stack: [F] -> []
    Variable<OrdinaryConstructorFunction> F = mv.newVariable("F", OrdinaryConstructorFunction.class);
    mv.store(F);
    // steps 19-21
    ClassPropertyGenerator.Result result = ClassPropertyEvaluation(codegen, def, F, proto, mv);
    Variable<Object[]> methodDecorators = result.methodDecorators;
    if (!classDecoratorsList.isEmpty()) {
        int index = 0;
        for (Expression decorator : classDecoratorsList) {
            mv.aload(classDecorators, index++, Types.Callable);
            invokeDynamicCall(mv, decorator, mv.executionContext(), mv.undefinedValue(), F);
            mv.pop();
        }
    }
    if (methodDecorators != null) {
        LabelledHashKey hashKey = new LabelledHashKey(def, "decorators");
        MethodName decoratorsMethod = mv.compile(hashKey, () -> classMethodDecorators(def, mv));
        // 0 = hint for stacktraces to omit this frame
        mv.lineInfo(0);
        mv.invoke(decoratorsMethod, mv.executionContext(), F, proto, methodDecorators);
    }
    if (scope != null) {
        // steps 22-23 (moved)
        if (className != null) {
            // stack: [] -> []
            Name innerName = scope.resolveName(className);
            BindingOp<DeclarativeEnvironmentRecord> op = BindingOp.of(classScopeEnvRec, innerName);
            op.initializeBinding(classScopeEnvRec, innerName, F, mv);
        }
    }
    if (result.instanceClassField != null || result.instanceClassMethods != null) {
        MethodName initializer = compileClassFieldInitializer(def, MethodDefinition.MethodAllocation.Prototype, mv);
        // stack: [] -> [F]
        mv.load(F);
        // stack: [F] -> [F, initializer]
        mv.load(proto);
        mv.invoke(initializer);
        if (result.instanceClassField != null) {
            mv.load(result.instanceClassField);
        } else {
            mv.anull();
        }
        if (result.instanceClassMethods != null) {
            mv.load(result.instanceClassMethods);
        } else {
            mv.anull();
        }
        mv.loadExecutionContext();
        mv.invoke(Methods.ClassOperations_CreateClassFieldInitializer);
        // stack: [F, initializer] -> []
        mv.invoke(Methods.ClassOperations_setInstanceFieldsInitializer);
    }
    // Class fields: Call InitializeStaticFields.
    if (result.staticClassField != null) {
        MethodName initializer = compileClassFieldInitializer(def, MethodDefinition.MethodAllocation.Class, mv);
        // stack: [] -> [staticInitializer]
        mv.load(F);
        mv.invoke(initializer);
        mv.load(result.staticClassField);
        mv.loadExecutionContext();
        mv.invoke(Methods.ClassOperations_CreateStaticClassFieldInitializer);
        // stack: [staticInitializer] -> []
        invokeDynamicCall(mv, def, mv.executionContext(), F);
        mv.pop(ValType.Any);
    }
    if (bodyScope != null) {
        mv.exitScope();
        if (bodyScope.isPresent()) {
            popLexicalEnvironment(mv);
        }
    }
    if (scope != null) {
        mv.exitScope();
        if (scope.isPresent()) {
            popLexicalEnvironment(mv);
        }
    }
    // stack: [] -> [F]
    mv.load(F);
    mv.exitVariableScope();
    // step 24 (return F)
    mv.exitClassDefinition();
}
Also used : Name(com.github.anba.es6draft.ast.scope.Name) OrdinaryConstructorFunction(com.github.anba.es6draft.runtime.types.builtins.OrdinaryConstructorFunction) BlockScope(com.github.anba.es6draft.ast.scope.BlockScope) OrdinaryObject(com.github.anba.es6draft.runtime.types.builtins.OrdinaryObject) LabelledHashKey(com.github.anba.es6draft.compiler.CodeVisitor.LabelledHashKey) DeclarativeEnvironmentRecord(com.github.anba.es6draft.runtime.DeclarativeEnvironmentRecord) HashSet(java.util.HashSet)

Aggregations

BlockScope (com.github.anba.es6draft.ast.scope.BlockScope)1 Name (com.github.anba.es6draft.ast.scope.Name)1 LabelledHashKey (com.github.anba.es6draft.compiler.CodeVisitor.LabelledHashKey)1 DeclarativeEnvironmentRecord (com.github.anba.es6draft.runtime.DeclarativeEnvironmentRecord)1 OrdinaryConstructorFunction (com.github.anba.es6draft.runtime.types.builtins.OrdinaryConstructorFunction)1 OrdinaryObject (com.github.anba.es6draft.runtime.types.builtins.OrdinaryObject)1 HashSet (java.util.HashSet)1