Files
2013-04-25 09:59:27 -07:00

825 lines
36 KiB
C++

/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "avmplus.h"
#ifdef VMCFG_EVAL
#include "eval.h"
namespace avmplus
{
namespace RTC
{
using namespace ActionBlockConstants;
VarScopeCtx * Ctx::findVarScope()
{
Ctx* ctx = this;
while (ctx->tag != CTX_Function && ctx->tag != CTX_ClassMethod && ctx->tag != CTX_Program)
ctx = ctx->next;
return (VarScopeCtx*)ctx;
}
const Cogen::BinopMapping Cogen::binopMapping[] = {
{0, 0}, // unused
{0, OP_astypelate}, // OPR_as
{0, OP_add}, // OPR_plus,
{0, 0}, // OPR_assign,
{0, 0}, // OPR_init,
{0, 0}, // OPR_comma,
{0, OP_subtract}, // OPR_minus,
{0, OP_multiply}, // OPR_multiply,
{0, OP_divide}, // OPR_divide,
{0, OP_modulo}, // OPR_remainder,
{0, OP_lshift}, // OPR_leftShift,
{0, OP_rshift}, // OPR_rightShift,
{0, OP_urshift}, // OPR_rightShiftUnsigned,
{0, OP_instanceof}, // OPR_instanceof
{0, OP_in}, // OPR_in
{0, OP_istypelate}, // OPR_is
{0, OP_bitand}, // OPR_bitwiseAnd,
{0, OP_bitor}, // OPR_bitwiseOr,
{0, OP_bitxor}, // OPR_bitwiseXor,
{0, 0}, // OPR_logicalAnd,
{0, 0}, // OPR_logicalOr,
{0, OP_lessthan}, // OPR_less,
{0, OP_lessequals}, // OPR_lessOrEqual,
{0, OP_greaterthan}, // OPR_greater,
{0, OP_greaterequals}, // OPR_greaterOrEqual,
{0, OP_equals}, // OPR_equal,
{1, OP_equals}, // OPR_notEqual,
{0, OP_strictequals}, // OPR_strictEqual,
{1, OP_strictequals}, // OPR_strictNotEqual
};
Cogen::Cogen(Compiler *compiler, ABCFile* abc, ABCTraitsTable* traits, ABCMethodBodyInfo* body, uint32_t first_temp)
: compiler(compiler)
, abc(abc)
, allocator(compiler->allocator)
, code(compiler->allocator)
, labels(compiler->allocator)
, traits(traits)
, body(body)
, last_linenum(0)
, label_counter(0)
, temp_counter(first_temp)
, sets_dxns(false)
, need_activation(false)
, stack_depth(0)
, max_stack_depth(0)
, scope_depth(0)
{
}
uint8_t* Cogen::serializeCodeBytes(uint8_t* b) const
{
code.serialize(b);
fixupBackpatches(b);
return b + code.size();
}
/* Stack height tracking is "simplest possible": we assume that even for
* unconditional branches the stack height in the taken branch and in the
* untaken (impossible) branch are the same; this means that anyone jumping
* to a label following a jump must be careful to match the stack height.
* This does not seem to be an issue in practice; the verifier keeps us
* honest; and we don't need to track the expected stack height at every
* label. Also, we're not interested in the actual stack height everywhere,
* just a conservative approximation to it.
*/
void Cogen::stackMovement(AbcOpcode opcode)
{
stack_depth = stack_depth + opcodeInfo[opcode].stack;
AvmAssert((int32_t)stack_depth >= 0);
if (stack_depth > max_stack_depth)
max_stack_depth = stack_depth;
}
void Cogen::stackMovement(AbcOpcode opcode, bool hasRTNS, bool hasRTName, uint32_t pops)
{
stack_depth = stack_depth + opcodeInfo[opcode].stack - hasRTNS - hasRTName - pops;
AvmAssert((int32_t)stack_depth >= 0);
if (stack_depth > max_stack_depth)
max_stack_depth = stack_depth;
}
void Cogen::emitOp(AbcOpcode opcode)
{
code.emitU8((uint8_t)opcode);
stackMovement(opcode);
}
void Cogen::emitOpU30(AbcOpcode opcode, uint32_t u30)
{
code.emitU8((uint8_t)opcode);
code.emitU30(u30);
stackMovement(opcode);
}
void Cogen::emitOpU30Special(AbcOpcode opcode, uint32_t u30, uint32_t pops)
{
code.emitU8((uint8_t)opcode);
code.emitU30(u30);
stackMovement(opcode, false, false, pops);
}
void Cogen::emitOpU30U30(AbcOpcode opcode, uint32_t u30_1, uint32_t u30_2)
{
code.emitU8((uint8_t)opcode);
code.emitU30(u30_1);
code.emitU30(u30_2);
stackMovement(opcode);
}
void Cogen::emitOpU8(AbcOpcode opcode, uint8_t b)
{
code.emitU8((uint8_t)opcode);
code.emitU8(b);
stackMovement(opcode);
}
void Cogen::emitOpS8(AbcOpcode opcode, int8_t b)
{
code.emitU8((uint8_t)opcode);
code.emitS8(b);
stackMovement(opcode);
}
void Cogen::I_getlocal(uint32_t index) {
if (index < 4)
emitOp((AbcOpcode)(OP_getlocal0 + index));
else
emitOpU30(OP_getlocal, index);
}
void Cogen::I_setlocal(uint32_t index) {
if (index < 4)
emitOp((AbcOpcode)(OP_setlocal0 + index));
else
emitOpU30(OP_setlocal, index);
}
void Cogen::I_debugfile(uint32_t index)
{
if (compiler->debugging)
emitOpU30(OP_debugfile, index);
}
void Cogen::I_debugline(uint32_t linenum)
{
if (compiler->debugging && linenum > last_linenum)
{
last_linenum = linenum;
emitOpU30(OP_debugline, linenum);
}
}
void Cogen::callMN(AbcOpcode opcode, uint32_t index, uint32_t nargs) {
code.emitU8((uint8_t)opcode);
code.emitU30(index);
code.emitU30(nargs);
stackMovement(opcode, abc->hasRTNS(index), abc->hasRTName(index), nargs);
}
void Cogen::propU30(AbcOpcode opcode, uint32_t index)
{
code.emitU8((uint8_t)opcode);
code.emitU30(index);
stackMovement(opcode, abc->hasRTNS(index), abc->hasRTName(index), 0);
}
Label* Cogen::newLabel()
{
Label* l = ALLOC(Label, ());
labels.addAtEnd(l);
return l;
}
void Cogen::emitJump(AbcOpcode opcode, Label* label)
{
// OPTIMIZEME: don't need to register backpatches for branches to known labels.
code.emitU8((uint8_t)opcode);
code.emitS24(3);
stackMovement(opcode);
label->backpatches = ALLOC(Seq<uint32_t>, (code.size() - 3, label->backpatches));
}
void Cogen::I_label(Label* label)
{
AvmAssert(label->address == ~0U);
label->address = code.size();
code.emitU8((uint8_t)OP_label);
}
// The location to be patched must contain a signed adjustment that will be
// added to the offset value. For regular jump instructions this should be '3',
// because the jump is relative to the end of the instruction - 3 bytes after
// the address of the offset field. For lookupswitch it is a value that depends
// on the location within the lookupswitch instruction of the offset word,
// because the jump is relative to the start of the instruction.
void Cogen::fixupBackpatches(uint8_t* b) const
{
for ( Seq<Label*>* labels = this->labels.get() ; labels != NULL ; labels = labels->tl ) {
uint32_t addr = labels->hd->address;
bool backward = false;
AvmAssert(addr != ~0U);
for ( Seq<uint32_t>* backpatches = labels->hd->backpatches ; backpatches != NULL ; backpatches = backpatches->tl ) {
uint32_t loc = backpatches->hd;
int32_t adjustment = readS24(b + loc);
int32_t offset = (int32_t)(addr - (loc + adjustment));
backward = backward || offset < 0;
emitS24(b + loc, offset);
}
if (!backward) {
// Work around verifier bug: if a branch to this label is never a backward
// branch then replace OP_label with OP_nop. The verifier always assumes
// that OP_label is the target of a backward branch.
b[addr] = OP_nop;
}
}
}
uint32_t Cogen::emitException(uint32_t from, uint32_t to, uint32_t target, uint32_t type, uint32_t name_index)
{
return body->exceptions.addAtEnd(ALLOC(ABCExceptionInfo, (from, to, target, type, name_index)));
}
/*static*/
uint32_t Cogen::emitTypeName(Compiler* compiler, Type* t)
{
ABCFile* abc = &compiler->abc;
if (t == NULL)
return 0;
if (t->tag() == TAG_simpleType)
{
QualifiedName* n = ((SimpleType*)t)->name;
if (n->qualifier != NULL) {
AvmAssert(n->qualifier->tag() == TAG_simpleName);
uint32_t ns = abc->addNamespace(CONSTANT_Namespace, abc->addString(((SimpleName*)n->qualifier)->name));
return abc->addQName(ns, abc->addString(((SimpleName*)n->name)->name));
}
else {
AvmAssert(n->name->tag() == TAG_simpleName);
return abc->addMultiname(compiler->NSS_public, abc->addString(((SimpleName*)n->name)->name));
}
}
else if (t->tag() == TAG_instantiatedType)
{
checkVectorType(compiler, ((InstantiatedType*)t)->basename);
return abc->addTypeName(compiler->ID_Vector, emitTypeName(compiler, ((InstantiatedType*)t)->tparam));
}
else
{
compiler->internalError(t->pos, "Should not happen");
return 0;
}
}
/*static*/
void Cogen::checkVectorType(Compiler* compiler, QualifiedName* t)
{
// Note we do not allow eg "public::Vector" since Vector is actually not
// in the public namespace. It is in a namespace that is magically opened
// by the use of type application syntax.
if (t->qualifier != NULL ||
t->name->tag() != TAG_simpleName ||
((SimpleName*)t->name)->name != compiler->SYM_Vector)
compiler->syntaxError(t->pos, SYNTAXERR_ILLEGAL_TYPENAME);
}
/*static*/
void Cogen::checkVectorType(Compiler* compiler, Type* t)
{
if (t->tag() != TAG_simpleType)
compiler->syntaxError(t->pos, SYNTAXERR_ILLEGAL_TYPENAME);
checkVectorType(compiler, ((SimpleType*)t)->name);
}
void Cogen::I_lookupswitch(Label* default_label, Label** case_labels, uint32_t ncases)
{
AvmAssert( ncases > 0 );
AvmAssert( default_label != NULL );
// AvmAssert( forall c in case_labels c != NULL );
uint32_t here = code.size();
code.emitU8((uint8_t)OP_lookupswitch);
code.emitS24((int32_t)(here - code.size()));
default_label->backpatches = ALLOC(Seq<uint32_t>, (code.size() - 3, default_label->backpatches));
code.emitU30(ncases - 1);
for ( uint32_t i=0 ; i < ncases ; i++ ) {
Label* label = case_labels[i];
code.emitS24((int32_t)(here - code.size()));
label->backpatches = ALLOC(Seq<uint32_t>, (code.size() - 3, label->backpatches));
}
stackMovement(OP_lookupswitch);
}
void FunctionDefn::cogenGuts(Compiler* compiler, Ctx* ctx, bool isMethod, ABCMethodInfo** info, ABCMethodBodyInfo** body)
{
Allocator* allocator = compiler->allocator;
ABCFile* abc = &compiler->abc;
ABCTraitsTable* traits;
Str* name = this->name;
if (name == NULL)
name = compiler->SYM_anonymous;
SeqBuilder<uint32_t> param_types(allocator);
SeqBuilder<DefaultValue*> default_values(allocator);
uint32_t numdefaults = 0;
for ( Seq<FunctionParam*>* params = this->params ; params != NULL ; params = params->tl ) {
param_types.addAtEnd(Cogen::emitTypeName(compiler, params->hd->type_name));
if (params->hd->default_value != NULL) {
Expr* dv = params->hd->default_value;
uint32_t cv = 0;
uint32_t ct = 0;
switch (dv->tag()) {
case TAG_literalString:
ct = CONSTANT_Utf8;
cv = abc->addString(((LiteralString*)dv)->value);
break;
case TAG_literalUInt:
ct = CONSTANT_UInt;
cv = abc->addUInt(((LiteralUInt*)dv)->value);
break;
case TAG_literalInt:
ct = CONSTANT_Int;
cv = abc->addInt(((LiteralInt*)dv)->value);
break;
case TAG_literalDouble:
ct = CONSTANT_Double;
cv = abc->addDouble(((LiteralDouble*)dv)->value);
break;
#ifdef VMCFG_FLOAT
case TAG_literalFloat:
ct = CONSTANT_Float;
cv = abc->addFloat(((LiteralFloat*)dv)->value);
break;
#endif
case TAG_literalBoolean:
if (((LiteralBoolean*)dv)->value)
ct = CONSTANT_True;
else
ct = CONSTANT_False;
break;
case TAG_literalNull:
ct = CONSTANT_Null;
break;
default:
// EXTENDME: we can sort-of support arbitrary default values here if we want to.
//
// AS3 does not support default value other than the six cases above. Doing better
// would be nice.
//
// We can use one of the obscure namespace default values as a placeholder, then
// generate code to test for that value and compute the correct default value.
// But the signature of the function won't be right; the type of the argument
// must be '*'. May be close enough, as long as we assign a provided argument
// value to a typed slot and get a type check on entry.
compiler->syntaxError(params->hd->default_value->pos, SYNTAXERR_IMPOSSIBLE_DEFAULT);
}
numdefaults++;
default_values.addAtEnd(ALLOC(DefaultValue, (ct, cv)));
}
}
*info = ALLOC(ABCMethodInfo, (compiler, abc->addString(name), numparams, param_types.get(), numdefaults, default_values.get(), Cogen::emitTypeName(compiler, return_type_name)));
traits = ALLOC(ABCTraitsTable, (compiler));
*body = ALLOC(ABCMethodBodyInfo, (compiler, *info, traits, 1 + numparams + (uses_arguments || (rest_param != NULL)), empty_body));
// Skipping code generation here is a fairly minor optimization, this will
// only kick in for interface and native bodies (which will subsequently be
// discarded).
if (!empty_body) {
Cogen* body_cogen = &(*body)->cogen;
if (isMethod) {
body_cogen->I_getlocal(0);
body_cogen->I_pushscope();
}
cogen(body_cogen, ctx);
if (isMethod)
body_cogen->I_popscope();
}
uint32_t flags = 0;
AvmAssert( !(uses_arguments && (rest_param != NULL)) );
if (uses_arguments)
flags |= abcMethod_NEED_ARGUMENTS;
if (rest_param != NULL)
flags |= abcMethod_NEED_REST;
if (uses_dxns)
flags |= abcMethod_SETS_DXNS;
(*info)->setFlags((uint8_t)((*body)->getFlags() | flags));
}
void CodeBlock::cogen(Cogen* cogen, Ctx* ctx)
{
Compiler* compiler = cogen->compiler;
ABCFile* abc = cogen->abc;
uint32_t activation = 0; // 0 means "unallocated"
FunctionDefn* fn = NULL;
if (tag == CODE_Function)
fn = (FunctionDefn*)this;
cogen->I_debugfile(cogen->emitString(compiler->str_filename));
if (tag == CODE_Program) {
cogen->I_getlocal(0);
cogen->I_pushscope();
}
if (fn && fn->inits != NULL) {
// Erect a non-program context for the inits to execute within, to avoid actions being taken as for CODE_Program.
VarScopeCtx* vs = ctx->findVarScope();
Seq<Namespace*>* openNamespaces = vs->openNamespaces;
uint32_t nsset = vs->nsset;
FunctionCtx ctx0(cogen->allocator, nsset, openNamespaces, ctx);
for ( Seq<Stmt*>* inits=fn->inits ; inits != NULL ; inits = inits->tl )
inits->hd->cogen(cogen, &ctx0);
}
if (fn && (fn->bindings != NULL || fn->uses_arguments)) {
activation = cogen->getTemp();
cogen->I_newactivation();
cogen->I_dup();
cogen->I_setlocal(activation);
cogen->I_pushscope();
}
for ( Seq<Binding*>* bindings = this->bindings ; bindings != NULL ; bindings = bindings->tl ) {
Binding* b = bindings->hd;
uint32_t id = abc->addQName(cogen->emitNamespace(b->ns), cogen->emitString(b->name));
uint32_t type_id = cogen->emitTypeName(compiler, b->type_name);
switch (bindings->hd->kind) {
case TAG_namespaceBinding: // FIXME: namespace bindings should be const, but the VM does not allow TAG_constBinding
case TAG_varBinding:
cogen->emitSlotTrait(id, type_id);
break;
case TAG_constBinding:
cogen->emitConstTrait(id, type_id);
break;
case TAG_classBinding:
case TAG_interfaceBinding:
// Handled elsewhere
break;
default:
compiler->internalError(0, "Unknown binding tag");
}
}
for ( Seq<NamespaceDefn*>* namespaces = this->namespaces ; namespaces != NULL ; namespaces = namespaces->tl ) {
uint32_t id = abc->addQName(cogen->emitNamespace(namespaces->hd->ns), cogen->emitString(namespaces->hd->name));
uint32_t ns = 0;
Expr* value = namespaces->hd->value;
if (value == NULL)
ns = abc->addNamespace(CONSTANT_Namespace, cogen->emitString(compiler->intern(compiler->namespace_counter++)));
else if (value->tag() == TAG_literalString)
ns = abc->addNamespace(CONSTANT_ExplicitNamespace, cogen->emitString(((LiteralString*)value)->value));
if (tag == CODE_Program)
cogen->I_getlocal(0);
else {
AvmAssert(activation != 0);
cogen->I_getlocal(activation);
}
if (ns != 0)
cogen->I_pushnamespace(ns);
else {
// FIXME: semantic check for namespaces.
// Check that the name on the RHS is actually a ns
// Code is the same as for 'use default namespace'
// If we can't tell (name may be shadowed?) then
// emit code that checks at run-time. If we can tell,
// then don't emit code for looking it up at run-time
// here, but just reference the definition of the other
// binding? (That's an optimization.)
//value->cogen(cogen);
compiler->internalError(0, "Namespace should have been resolved before code generation");
}
cogen->I_initproperty(id);
}
if (fn) {
uint32_t i=1;
Seq<FunctionParam*>* params=fn->params;
for ( ; params != NULL ; params = params->tl, i++ ) {
uint32_t id = abc->addQName(cogen->emitNamespace(compiler->parser.defaultNamespace()),
cogen->emitString(params->hd->name));
AvmAssert(activation != 0);
cogen->I_getlocal(activation);
cogen->I_getlocal(i);
cogen->I_setproperty(id);
}
if (fn->uses_arguments || fn->rest_param) {
AvmAssert(activation != 0);
cogen->I_getlocal(activation);
cogen->I_getlocal(i);
if (fn->uses_arguments)
cogen->I_setproperty(abc->addQName(cogen->emitNamespace(compiler->parser.defaultNamespace()),
cogen->emitString(compiler->SYM_arguments)));
else
cogen->I_setproperty(abc->addQName(cogen->emitNamespace(compiler->parser.defaultNamespace()),
cogen->emitString(fn->rest_param->name)));
}
}
for ( Seq<FunctionDefn*>* functions = this->functions ; functions != NULL ; functions = functions->tl ) {
FunctionDefn* func = functions->hd;
ABCMethodInfo* fn_info;
ABCMethodBodyInfo* fn_body;
func->cogenGuts(compiler, ctx, &fn_info, &fn_body);
uint32_t fname = abc->addQName(cogen->emitNamespace(func->ns), cogen->emitString(func->name));
cogen->I_getlocal(activation);
cogen->I_newfunction(fn_info->index);
cogen->I_setproperty(fname);
}
cogenBody(cogen, ctx, activation);
}
uint32_t Cogen::buildNssetWithPublic(Seq<Namespace*>* ns)
{
SeqBuilder<uint32_t> s(allocator);
s.addAtEnd(compiler->NS_public);
s.addAtEnd(compiler->NS_internal);
while (ns != NULL) {
if (ns->hd->tag() != TAG_commonNamespace)
compiler->internalError(0, "Namespace should have been resolved before now.");
CommonNamespace* cns = (CommonNamespace*)ns->hd;
s.addAtEnd(abc->addNamespace(CONSTANT_Namespace, abc->addString(cns->name)));
ns = ns->tl;
}
return abc->addNsset(s.get());
}
uint32_t Cogen::emitNamespace(NameComponent* ns)
{
if (ns->tag() != TAG_builtinNamespace)
compiler->internalError(0, "No support for user-defined namespaces yet");
uint32_t nsid = compiler->NS_public;
switch (((BuiltinNamespace*)ns)->t) {
case T_Internal:
// FIXME: this could be "file internal" or "package internal"
// For now: file internal
nsid = compiler->NS_internal;
break;
case T_Private:
nsid = compiler->NS_private;
break;
case T_Protected:
compiler->internalError(0, "No code generation for protected namespaces yet");
break;
case T_Public:
// FIXME: This could be "public public" or "package public"
// For now: public public
break;
}
return nsid;
}
void Program::cogenTypes(Cogen* cogen, Ctx* ctx, ABCTraitsTable* global_traits, Seq<TypeDefn*>* classes)
{
cogen->I_getlocal(0);
cogen->I_pushscope();
for ( ; classes != NULL ; classes = classes->tl )
cogenTypeHierarchy(cogen, ctx, global_traits, classes->hd);
cogen->I_popscope();
}
void Program::cogenTypeHierarchy(Cogen* cogen, Ctx* ctx, ABCTraitsTable* global_traits, TypeDefn* ty)
{
if (!ty->is_generated)
{
if (ty->is_searching)
cogen->compiler->syntaxError(ty->pos, SYNTAXERR_CIRCULAR_TYPE_HIERARCHY);
ty->is_searching = true;
if (ty->is_interface) {
InterfaceDefn* iface = static_cast<InterfaceDefn*>(ty);
// All interface
Seq<Str*>* bases = iface->extends;
while (bases != NULL) {
cogenTypeHierarchy(cogen, ctx, global_traits, findInterface(cogen, iface->pos, bases->hd));
bases = bases->tl;
}
cogenType(cogen, ctx, global_traits, iface);
}
else {
ClassDefn* cls = static_cast<ClassDefn*>(ty);
Str* base = cls->extends;
if (base != NULL && base != cogen->compiler->SYM_Object)
cogenTypeHierarchy(cogen, ctx, global_traits, findClass(cogen, cls->pos, base));
Seq<Str*>* bases = cls->implements;
while (bases != NULL) {
cogenTypeHierarchy(cogen, ctx, global_traits, findInterface(cogen, cls->pos, bases->hd));
bases = bases->tl;
}
cogenType(cogen, ctx, global_traits, cls);
}
ty->is_searching = false;
}
}
void Program::cogenType(Cogen* cogen, Ctx* ctx, ABCTraitsTable* global_traits, ClassDefn* cls)
{
Allocator* allocator = cogen->allocator;
Compiler* compiler = cogen->compiler;
ABCFile* abc = cogen->abc;
uint32_t class_name = abc->addQName(cogen->emitNamespace(cls->ns), cogen->emitString(cls->name));
uint32_t base_name = abc->addMultiname(compiler->NSS_public, // FIXME: should be set of open namespaces
cogen->emitString(cls->extends ? cls->extends : compiler->SYM_Object));
uint8_t flags = ((cls->is_final ? CONSTANT_ClassFinal : 0) |
(cls->is_dynamic ? 0 : CONSTANT_ClassSealed));
uint32_t protectedNS = 0; // FIXME, if not 0 then set CONSTANT_ClassProtectedNS
uint32_t interface_count;
uint32_t* interfaces = interfaceRefs(cogen, cls->implements, &interface_count);
uint32_t cinit = cogenConstructor(cogen, ctx, cls->cls_init);
uint32_t iinit = cogenConstructor(cogen, ctx, cls->inst_init);
ABCTraitsTable* traits = cogenClassTraits(cogen, ctx, cls->inst_methods, cls->inst_vars);
ABCTraitsTable* ctraits = cogenClassTraits(cogen, ctx, cls->cls_methods, cls->cls_vars); // FIXME: namespaces?
ABCClassInfo* clsinfo = ALLOC(ABCClassInfo, (cinit, ctraits));
ABCInstanceInfo* instinfo =
ALLOC(ABCInstanceInfo, (class_name,
base_name,
flags,
protectedNS,
interface_count,
interfaces,
iinit,
traits));
uint32_t class_traits = abc->addClassAndInstance(clsinfo, instinfo);
global_traits->addTrait(ALLOC(ABCClassTrait, (class_name, 0, class_traits)));
int pushes=pushBaseClasses(cogen, cls);
cogen->I_getglobalscope();
cogen->I_findpropstrict(base_name);
cogen->I_getproperty(base_name);
cogen->I_newclass(class_traits);
while (pushes--)
cogen->I_popscope();
cogen->I_initproperty(class_name);
cls->is_generated = true;
}
void Program::cogenType(Cogen* cogen, Ctx* ctx, ABCTraitsTable* global_traits, InterfaceDefn* iface)
{
Allocator* allocator = cogen->allocator;
ABCFile* abc = cogen->abc;
uint32_t iface_name = abc->addQName(cogen->emitNamespace(iface->ns), cogen->emitString(iface->name));
uint32_t base_name = 0;
uint8_t flags = CONSTANT_ClassInterface;
uint32_t protectedNS = 0;
uint32_t interface_count;
uint32_t* interfaces = interfaceRefs(cogen, iface->extends, &interface_count);
BodyInfo iface_body(allocator);
SignatureInfo iface_signature(allocator);
FunctionDefn* iface_cinit = ALLOC(FunctionDefn, (iface_signature, iface_body, NULL));
uint32_t cinit = cogenConstructor(cogen, ctx, iface_cinit); // must have a function that just returns
iface_body.empty_body = true;
FunctionDefn* iface_iinit = ALLOC(FunctionDefn, (iface_signature, iface_body, NULL));
uint32_t iinit = cogenConstructor(cogen, ctx, iface_iinit); // must have an empty body
ABCTraitsTable* traits = cogenClassTraits(cogen, ctx, iface->inst_methods, NULL);
ABCTraitsTable* ctraits = cogenClassTraits(cogen, ctx, NULL, NULL);
ABCClassInfo* clsinfo = ALLOC(ABCClassInfo, (cinit, ctraits));
ABCInstanceInfo* instinfo =
ALLOC(ABCInstanceInfo, (iface_name,
base_name,
flags,
protectedNS,
interface_count,
interfaces,
iinit,
traits));
uint32_t class_traits = abc->addClassAndInstance(clsinfo, instinfo);
global_traits->addTrait(ALLOC(ABCClassTrait, (iface_name, 0, class_traits)));
cogen->I_getglobalscope();
cogen->I_pushnull();
cogen->I_newclass(class_traits);
cogen->I_initproperty(iface_name);
iface->is_generated = true;
}
uint32_t* Program::interfaceRefs(Cogen* cogen, Seq<Str*>* ifaces, uint32_t* interface_count)
{
Allocator* allocator = cogen->allocator;
Compiler* compiler = cogen->compiler;
ABCFile* abc = cogen->abc;
uint32_t n = length(ifaces);
uint32_t* refs = (uint32_t*)allocator->alloc(sizeof(uint32_t)*n);
uint32_t i=0;
while (ifaces != NULL) {
refs[i] = abc->addMultiname(compiler->NSS_public, cogen->emitString(ifaces->hd));
i++;
ifaces = ifaces->tl;
}
*interface_count = n;
return refs;
}
ABCTraitsTable* Program::cogenClassTraits(Cogen* cogen, Ctx* ctx, Seq<FunctionDefn*>* inst_methods, Seq<Binding*>* inst_vars)
{
Allocator* allocator = cogen->allocator;
Compiler* compiler = cogen->compiler;
ABCFile* abc = cogen->abc;
ABCTraitsTable* traits = ALLOC(ABCTraitsTable, (compiler));
for ( Seq<FunctionDefn*>* ms = inst_methods ; ms != NULL ; ms = ms->tl ) {
ABCMethodInfo* fn_info;
ABCMethodBodyInfo* fn_body;
ms->hd->cogenGuts(cogen->compiler, ctx, true, &fn_info, &fn_body);
traits->addTrait(ALLOC(ABCMethodTrait, (abc->addQName(cogen->emitNamespace(ms->hd->ns), cogen->emitString(ms->hd->name)),
fn_info->index)));
}
for ( Seq<Binding*>* bs = inst_vars ; bs != NULL ; bs = bs->tl ) {
Binding* b = bs->hd;
if (b->kind != TAG_varBinding && b->kind != TAG_constBinding)
continue;
traits->addTrait(ALLOC(ABCSlotTrait, (abc->addQName(cogen->emitNamespace(b->ns), cogen->emitString(b->name)),
0, // FIXME: the type
(b->kind == TAG_varBinding ? TRAIT_Slot : TRAIT_Const))));
}
return traits;
}
uint32_t Program::cogenConstructor(Cogen* cogen, Ctx* ctx, FunctionDefn* constructor)
{
ABCMethodInfo* fn_info;
ABCMethodBodyInfo* fn_body;
constructor->cogenGuts(cogen->compiler, ctx, true, &fn_info, &fn_body);
return fn_info->index;
}
int Program::pushBaseClasses(Cogen* cogen, ClassDefn* currcls)
{
Compiler* compiler = cogen->compiler;
ABCFile* abc = &compiler->abc;
Str* base = currcls->extends;
if (base == NULL)
base = compiler->SYM_Object;
int n = 0;
if (base != compiler->SYM_Object)
n = pushBaseClasses(cogen, findClass(cogen, currcls->pos, base));
uint32_t basenm = abc->addMultiname(compiler->NSS_public, cogen->emitString(base));
cogen->I_findpropstrict(basenm);
cogen->I_getproperty(basenm);
cogen->I_pushscope();
return n+1;
}
// FIXME: These need to know about imported classes and interfaces! They need not always return
// a full ClassDefn / InterfaceDefn, just a rump;
// mostly we want the names of the base classes and interfaces. The "imported" types
// are the types already present in the environment (in the run-time compiler) or the
// actually imported types from an ABC, in the batch compiler. Looks like there will be some
// hook in the HostContext.
ClassDefn* Program::findClass(Cogen* cogen, uint32_t pos, Str* name)
{
for ( Seq<TypeDefn*>* classes=this->classes ; classes != NULL ; classes=classes->tl ) {
if (classes->hd->name == name) {
if (classes->hd->is_interface)
break;
return static_cast<ClassDefn*>(classes->hd);
}
}
cogen->compiler->syntaxError(pos, SYNTAXERR_MISSING_BASE_CLASS);
/*NOTREACHED*/
return NULL;
}
InterfaceDefn* Program::findInterface(Cogen* cogen, uint32_t pos, Str* name)
{
for ( Seq<TypeDefn*>* classes=this->classes ; classes != NULL ; classes=classes->tl ) {
if (classes->hd->name == name) {
if (!classes->hd->is_interface)
break;
return static_cast<InterfaceDefn*>(classes->hd);
}
}
cogen->compiler->syntaxError(pos, SYNTAXERR_MISSING_BASE_CLASS);
/*NOTREACHED*/
return NULL;
}
}
}
#endif // VMCFG_EVAL