/* -*- 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, (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* labels = this->labels.get() ; labels != NULL ; labels = labels->tl ) { uint32_t addr = labels->hd->address; bool backward = false; AvmAssert(addr != ~0U); for ( Seq* 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, (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, (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 param_types(allocator); SeqBuilder default_values(allocator); uint32_t numdefaults = 0; for ( Seq* 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* openNamespaces = vs->openNamespaces; uint32_t nsset = vs->nsset; FunctionCtx ctx0(cogen->allocator, nsset, openNamespaces, ctx); for ( Seq* 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* 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* 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* 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* 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* ns) { SeqBuilder 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* 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(ty); // All interface Seq* 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(ty); Str* base = cls->extends; if (base != NULL && base != cogen->compiler->SYM_Object) cogenTypeHierarchy(cogen, ctx, global_traits, findClass(cogen, cls->pos, base)); Seq* 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* 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* inst_methods, Seq* inst_vars) { Allocator* allocator = cogen->allocator; Compiler* compiler = cogen->compiler; ABCFile* abc = cogen->abc; ABCTraitsTable* traits = ALLOC(ABCTraitsTable, (compiler)); for ( Seq* 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* 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* classes=this->classes ; classes != NULL ; classes=classes->tl ) { if (classes->hd->name == name) { if (classes->hd->is_interface) break; return static_cast(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* classes=this->classes ; classes != NULL ; classes=classes->tl ) { if (classes->hd->name == name) { if (!classes->hd->is_interface) break; return static_cast(classes->hd); } } cogen->compiler->syntaxError(pos, SYNTAXERR_MISSING_BASE_CLASS); /*NOTREACHED*/ return NULL; } } } #endif // VMCFG_EVAL