From ec93674fbc6bcc703563f960f029128cae46b619 Mon Sep 17 00:00:00 2001 From: "lth@acm.org" Date: Wed, 9 May 2007 21:01:10 -0700 Subject: [PATCH] Sketches for the back end --HG-- branch : com.mozilla.es4.smlnj extra : convert_revision : bda4f10dfe1f6e5ad5fced0917f7edaf4e65782d --- tests/self/assembler.es | 483 ++++++++++++++++++ tests/self/bytestream.es | 142 ++++++ tests/self/constantpool.es | 200 ++++++++ tests/self/emitter.as | 974 +++++++++++++++++++++++++++++++++++++ 4 files changed, 1799 insertions(+) create mode 100644 tests/self/assembler.es create mode 100644 tests/self/bytestream.es create mode 100644 tests/self/constantpool.es create mode 100644 tests/self/emitter.as diff --git a/tests/self/assembler.es b/tests/self/assembler.es new file mode 100644 index 00000000..87f50bc7 --- /dev/null +++ b/tests/self/assembler.es @@ -0,0 +1,483 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is [Open Source Virtual Machine.]. + * + * The Initial Developer of the Original Code is + * Adobe System Incorporated. + * Portions created by the Initial Developer are Copyright (C) 2004-2006 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Adobe AS3 Team + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +package es4 +{ + + /******************************************************************* + * ABC constants + */ + + const CONSTANT_Utf8 = 0x01; + const CONSTANT_Integer = 0x03; + const CONSTANT_UInt = 0x04; + const CONSTANT_PrivateNamespace = 0x05; + const CONSTANT_Double = 0x06; + const CONSTANT_Qname = 0x07; // ns::name, const ns, const name + const CONSTANT_Namespace = 0x08; + const CONSTANT_Multiname = 0x09; // [ns...]::name, const [ns...], const name + const CONSTANT_False = 0x0A; + const CONSTANT_True = 0x0B; + const CONSTANT_Null = 0x0C; + const CONSTANT_QnameA = 0x0D; // @ns::name, const ns, const name + const CONSTANT_MultinameA = 0x0E; // @[ns...]::name, const [ns...], const name + const CONSTANT_RTQname = 0x0F; // ns::name, var ns, const name + const CONSTANT_RTQnameA = 0x10; // @ns::name, var ns, const name + const CONSTANT_RTQnameL = 0x11; // ns::[name], var ns, var name + const CONSTANT_RTQnameLA = 0x12; // @ns::[name], var ns, var name + const CONSTANT_NameL = 0x13; // o[name], var name + const CONSTANT_NameLA = 0x14; // @[name], var name + const CONSTANT_NamespaceSet = 0x15; + const CONSTANT_PackageNamespace = 0x16; // namespace for a package + const CONSTANT_PackageInternalNS = 0x17; + const CONSTANT_ProtectedNamespace = 0x18; + const CONSTANT_ExplicitNamespace = 0x19; + const CONSTANT_StaticProtectedNS = 0x1A; + const CONSTANT_MultinameL = 0x1B; + const CONSTANT_MultinameLA = 0x1C; + + const SLOT_var = 0; + const SLOT_method = 1; + const SLOT_getter = 2; + const SLOT_setter = 3; + const SLOT_class = 4; + const SLOT_function = 6; + + const METHOD_Arguments = 0x1; + const METHOD_Activation = 0x2; + const METHOD_Needrest = 0x4; + const METHOD_HasOptional = 0x8; + const METHOD_IgnoreRest = 0x10; + const METHOD_Native = 0x20; + const METHOD_Setsdxns = 0x40; + const METHOD_HasParamNames = 0x80; + + const OP_bkpt = 0x01; + const OP_nop = 0x02; + const OP_throw = 0x03; + const OP_getsuper = 0x04; + const OP_setsuper = 0x05; + const OP_dxns = 0x06; + const OP_dxnslate = 0x07; + const OP_kill = 0x08; + const OP_label = 0x09; + const OP_ifnlt = 0x0C; + const OP_ifnle = 0x0D; + const OP_ifngt = 0x0E; + const OP_ifnge = 0x0F; + const OP_jump = 0x10; + const OP_iftrue = 0x11; + const OP_iffalse = 0x12; + const OP_ifeq = 0x13; + const OP_ifne = 0x14; + const OP_iflt = 0x15; + const OP_ifle = 0x16; + const OP_ifgt = 0x17; + const OP_ifge = 0x18; + const OP_ifstricteq = 0x19; + const OP_ifstrictne = 0x1A; + const OP_lookupswitch = 0x1B; + const OP_pushwith = 0x1C; + const OP_popscope = 0x1D; + const OP_nextname = 0x1E; + const OP_hasnext = 0x1F; + const OP_pushnull = 0x20; + const OP_pushundefined = 0x21; + const OP_nextvalue = 0x23; + const OP_pushbyte = 0x24; + const OP_pushshort = 0x25; + const OP_pushtrue = 0x26; + const OP_pushfalse = 0x27; + const OP_pushnan = 0x28; + const OP_pop = 0x29; + const OP_dup = 0x2A; + const OP_swap = 0x2B; + const OP_pushstring = 0x2C; + const OP_pushint = 0x2D; + const OP_pushuint = 0x2E; + const OP_pushdouble = 0x2F; + const OP_pushscope = 0x30; + const OP_pushnamespace = 0x31; + const OP_newfunction = 0x40; + const OP_call = 0x41; + const OP_construct = 0x42; + const OP_callmethod = 0x43; + const OP_callstatic = 0x44; + const OP_callsuper = 0x45; + const OP_callproperty = 0x46; + const OP_returnvoid = 0x47; + const OP_returnvalue = 0x48; + const OP_constructsuper = 0x49; + const OP_constructprop = 0x4A; + const OP_callsuperid = 0x4B; + const OP_callproplex = 0x4C; + const OP_callinterface = 0x4D; + const OP_callsupervoid = 0x4E; + const OP_callpropvoid = 0x4F; + const OP_newobject = 0x55; + const OP_newarray = 0x56; + const OP_newactivation = 0x57; + const OP_newclass = 0x58; + const OP_getdescendants = 0x59; + const OP_findpropstrict = 0x5D; + const OP_findproperty = 0x5E; + const OP_finddef = 0x5F; + const OP_getlex = 0x60; + const OP_setproperty = 0x61; + const OP_getlocal = 0x62; + const OP_setlocal = 0x63; + const OP_getglobalscope = 0x64; + const OP_getscopeobject = 0x65; + const OP_getproperty = 0x66; + const OP_getpropertylate = 0x67; + const OP_initproperty = 0x68; + const OP_deleteproperty = 0x6A; + const OP_deletepropertylate = 0x6B; + const OP_getslot = 0x6C; + const OP_setslot = 0x6D; + const OP_getglobalslot = 0x6E; + const OP_setglobalslot = 0x6F; + const OP_convert_s = 0x70; + const OP_esc_xelem = 0x71; + const OP_esc_xattr = 0x72; + const OP_convert_i = 0x73; + const OP_convert_u = 0x74; + const OP_convert_d = 0x75; + const OP_convert_b = 0x76; + const OP_convert_o = 0x77; + const OP_coerce = 0x80; + const OP_coerce_b = 0x81; + const OP_coerce_a = 0x82; + const OP_coerce_i = 0x83; + const OP_coerce_d = 0x84; + const OP_coerce_s = 0x85; + const OP_astype = 0x86; + const OP_astypelate = 0x87; + const OP_coerce_u = 0x88; + const OP_coerce_o = 0x89; + const OP_negate = 0x90; + const OP_increment = 0x91; + const OP_inclocal = 0x92; + const OP_decrement = 0x93; + const OP_declocal = 0x94; + const OP_typeof = 0x95; + const OP_not = 0x96; + const OP_bitnot = 0x97; + const OP_concat = 0x9A; + const OP_add_d = 0x9B; + const OP_add = 0xA0; + const OP_subtract = 0xA1; + const OP_multiply = 0xA2; + const OP_divide = 0xA3; + const OP_modulo = 0xA4; + const OP_lshift = 0xA5; + const OP_rshift = 0xA6; + const OP_urshift = 0xA7; + const OP_bitand = 0xA8; + const OP_bitor = 0xA9; + const OP_bitxor = 0xAA; + const OP_equals = 0xAB; + const OP_strictequals = 0xAC; + const OP_lessthan = 0xAD; + const OP_lessequals = 0xAE; + const OP_greaterthan = 0xAF; + const OP_greaterequals = 0xB0; + const OP_instanceof = 0xB1; + const OP_istype = 0xB2; + const OP_istypelate = 0xB3; + const OP_in = 0xB4; + const OP_increment_i = 0xC0; + const OP_decrement_i = 0xC1; + const OP_inclocal_i = 0xC2; + const OP_declocal_i = 0xC3; + const OP_negate_i = 0xC4; + const OP_add_i = 0xC5; + const OP_subtract_i = 0xC6; + const OP_multiply_i = 0xC7; + const OP_getlocal0 = 0xD0; + const OP_getlocal1 = 0xD1; + const OP_getlocal2 = 0xD2; + const OP_getlocal3 = 0xD3; + const OP_setlocal0 = 0xD4; + const OP_setlocal1 = 0xD5; + const OP_setlocal2 = 0xD6; + const OP_setlocal3 = 0xD7; + const OP_abs_jump = 0xEE; + const OP_debug = 0xEF; + const OP_debugline = 0xF0; + const OP_debugfile = 0xF1; + const OP_bkptline = 0xF2; + const OP_timestamp = 0xF3; + const OP_verifypass = 0xF5; + const OP_alloc = 0xF6; + const OP_mark = 0xF7; + const OP_wb = 0xF8; + const OP_prologue = 0xF9; + const OP_sendenter = 0xFA; + const OP_doubletoatom = 0xFB; + const OP_sweep = 0xFC; + const OP_codegenop = 0xFD; + const OP_verifyop = 0xFE; + const OP_decode = 0xFF; + + + /********************************************************************************* + * Assembler for one code block. + * + * This is a lightweight class that is used to emit bytes for + * instructions and data, and to maintain stack and scope depths, + * but which has no code generation logic save for simple + * abstractions (eg, GetLocal maps to GetLocalN or to the general + * GetLocal instruction, depending on its parameter value). + */ + class ABCAssembler + { + function ABCAssembler(constants, numberOfFormals) { + this.constants = constants; + this.code.endian = "littleEndian"; + this.nextTemp = numberOfFormals; + } + + private function nullOp(name, opcode) { + stack(0); + Debug.log_mode::log(" "+code.length+":" + name, code_out); + code.uint8(opcode); + } + + private function immediateOp(name, opcode) { + stack(1); + Debug.log_mode::log(" "+code.length+":" + name, code_out); + code.uint8(opcode); + } + + private function constantOp(name, opcode, index) { + stack(1); + Debug.log_mode::log(" "+code.length+":" + name, code_out); + code.uint8(opcode); + code.int32(index); + } + + function i_bkpt() { nullOp("bkpt", OP_bkpt) } + function i_nop() { nullOp("nop", OP_nop) } + function i_throw() { nullOp("throw", OP_throw) } + + + function LoadThis() { immediateOp("LoadThis", OP_getlocal0) } + function PushNull() { immediateOp("PushNull", OP_pushnull) } + function PushTrue() { immediateOp("PushTrue", OP_pushtrue) } + function PushFalse() { immediateOp("PushFalse", OP_pushfalse) } + function PushUndefined() { immediateOp("PushUndefined", OP_pushundefined) } + function GetGlobalScope() { immediateOp("GetGlobalScope", OP_getglobalscope) } + + function PushString(str) { constantOp("PushString " + str, OP_pushstring, ConstantUtf8(str)) } + function PushNumber(num:Number) { constantOp("PushNumber", OP_pushdouble, ConstantDouble(num)) } + function PushInt(num:int) { constantOp("PushInt", OP_pushint, ConstantInt(num)) } + function NewFunction(method_index) { constantOp("NewFunction", OP_newfunction, method_index) } + + private function stackOp(name, opcode, movement) { + stack(movement); + Debug.log_mode::log(" "+code.length+":" + name, code_out); + makeByte(code,opcode); + } + + function Pop() { stackOp("Pop", OP_pop, -1) } + function Dup() { stackOp("Dup", OP_dup, 1) } + function Swap() { stackOp("Swap", OP_swap, 0) } + + private function binOp(name, opcode) { + stack(-1); + Debug.log_mode::log(" "+code.length+":" + name + " ", code_out); + makeByte(code, opcode); + } + + function Add() { binOp("Add", OP_add) } + function Subtract() { binOp("Subtract", OP_subtract) } + function Multiply() { binOp("Multiply", OP_multiply) } + function Divide() { binOp("Divide", OP_divide) } + function Modulo() { binOp("Modulo", OP_modulo) } + function LShift() { binOp("LShift", OP_lshift) } + function RShift() { binOp("RShift", OP_rshift) } + function URShift() { binOp("URShift", OP_urshift) } + function BitAnd() { binOp("BitAnd", OP_bitand) } + function BitOr() { binOp("BitOr", OP_bitor) } + function BitXor() { binOp("BitXor", OP_bitxor) } + function Equals() { binOp("Equals", OP_equals) } + function StrictEquals() { binOp("StrictEquals", OP_strictequals) } + function LessThan() { binOp("LessThan", OP_lessthan) } + function LessEquals() { binOp("LessEquals", OP_lessequals) } + function GreaterThan() { binOp("GreaterThan", OP_greaterthan) } + function GreaterEquals() { binOp("GreaterEquals", OP_greaterequals) } + function InstanceOf() { binOp("InstanceOf", OP_instanceof) } + function IsType() { binOp("IsType", OP_istype) } + function In() { binOp("In", OP_in) } + + private function unOp(name, opcode) + { + stack(0); + Debug.log_mode::log(" "+code.length+":" + name + " ", code_out); + makeByte(code, opcode); + } + + function BitNot() { unOp("BitNot", OP_bitnot) } + function Not() { unOp("Not", OP_not) } + function TypeOf() { unOp("TypeOf", OP_typeof) } + function Negate() { unOp("Negate", OP_negate) } + function Convert_B() { unOp("Convert_B", OP_convert_b) } + function Increment() { unOp("Increment", OP_increment) } + function Decrement() { unOp("Decrement", OP_decrement) } + + function Jump(lnum) { + stack(0); + Debug.log_mode::log(" "+code.length+":Jump L" + lnum, code_out); + code.uint8(OP_jump); + code.int24(0); + } + + function ReturnValue() { + stack(-1); + Debug.log_mode::log(" "+code.length+":ReturnValue", code_out); + code.uint8(code,OP_returnvalue) + } + + function ReturnVoid() { nullOp("ReturnVoid", OP_returnvoid) } + + function NewActivation() { + stack(1); + Debug.log_mode::log(" "+code.length+":NewActivation", code_out) + makeByte(code,OP_newactivation) + } + + function GetScopeObject(n) { + stack(1); + Debug.log_mode::log(" "+code.length+":GetScopeObject "+n, code_out) + makeByte(code,OP_getscopeobject) + makeInt32(code,n) + } + + function Call(size:uint) { + stack(1-(size+1)); // one more for "this" + Debug.log_mode::log(" "+code.length+":Call", code_out) + makeByte(code,OP_call) + makeInt32(code,size) + } + + function PushWith() { + stack(-1); + Debug.log_mode::log(" "+code.length+":PushWithe", code_out) + makeByte(code,OP_pushwith) + scope_depth++ + } + + function PushScope() { + stack(-1); + Debug.log_mode::log(" "+code.length+":PushScope", code_out) + makeByte(code,OP_pushscope) + scope_depth++ + } + + function PopScope() { + stack(0); + Debug.log_mode::log(" "+code.length+":PopScope", code_out) + makeByte(code,OP_popscope) + scope_depth-- + } + + function NewArray(count) { + stack (1-count); + Debug.log_mode::log(" "+code.length+":NewArray "+count, code_out); + makeByte(code, OP_newarray); + makeInt32(code, count); + } + + function Kill(n) + { + stack(0); + Debug.log_mode::log(" "+code.length+":Kill " + n, code_out) + makeByte(code,OP_kill) + makeInt32(code,n) + } + + function GetLocal(n) { + stack(1); + Debug.log_mode::log(" "+code.length+":GetLocal "+n, code_out) + if( n <= 3 ) + makeByte(code,OP_getlocal0+n) + else { + makeByte(code,OP_getlocal) + makeInt32(code,n) + } + } + + function SetLocal(n) { + stack(-1); + Debug.log_mode::log(" "+code.length+":SetLocal "+n, code_out) + if( n <= 3 ) + makeByte(code,OP_setlocal0+n) + else { + makeByte(code,OP_setlocal) + makeInt32(code,n) + } + } + + function getTemp() { + if (freeTemps.length > 0) + return freeTemps.pop(); + else + return nextTemp++; + } + + function killTemp(t) { + freeTemps.push(t); + Kill(t); + } + + private function stack(size:int):void { + current_stack_depth += size; + if (current_stack_depth > max_stack_depth) + max_stack_depth = current_stack_depth; + } + + private var code = new ByteArray; + private var scope_depth = 1; + private var current_stack_depth = 0; + private var max_stack_depth = 0; + private var nextTemp; + private var freeTemps = []; + private var constants; + } + +} diff --git a/tests/self/bytestream.es b/tests/self/bytestream.es new file mode 100644 index 00000000..ad2ae8cf --- /dev/null +++ b/tests/self/bytestream.es @@ -0,0 +1,142 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is [Open Source Virtual Machine.]. + * + * The Initial Developer of the Original Code is + * Adobe System Incorporated. + * Portions created by the Initial Developer are Copyright (C) 2004-2006 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Adobe AS3 Team + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +package es4 +{ + /************************************************************************ + * Emitter for various data in ABC-compatible formats, as a byte stream. + * + * FIXME: this uses ActionScript's ByteArray, which is not at all + * similar to the ByteArray in ES4. + */ + + final class ABCByteStream + { + function ABCByteStream() { + bytes.endian = "littleEndian"; + } + + function size() { + return bytes.length; + } + + function uint8(val:uint) { + assert(val < 0x100); + bytes.writeByte(val); + } + + function int16(val:int) { + assert(-0x8000 <= val && val < 0x8000); + bytes.writeByte(val & 0xFF); + bytes.writeByte((val >> 8) & 0xFF); + } + + function int24(val:int) { + assert(-0x1000000 <= val && val < 0x1000000); + bytes.writeByte(val & 0xFF); + bytes.writeByte((val >> 8) & 0xFF); + bytes.writeByte((val >> 16) & 0xFF); + } + + function uint30(val:uint) { + assert(val < 0x40000000); + uint32(val); + } + + function int30(val:int) { + assert(-0x40000000 <= val && val < 0x40000000); + uint32(uint(val)); + } + + function uint32(val:uint) { + if( val < 0x80 ) // 7 bits + bytes.writeByte(val & 0x7F); + else if ( val < 0x4000 ) { // 14 bits + bytes.writeByte((val & 0x7F) | 0x80); + bytes.writeByte((val >> 7) & 0x7F); + } + else if ( val < 0x200000 ) { // 21 bits + bytes.writeByte((val & 0x7F) | 0x80); + bytes.writeByte(((val >> 7) & 0x7F) | 0x80); + bytes.writeByte((val >> 14) & 0x7F); + } + else if ( val < 0x10000000 ) { // 28 bits + bytes.writeByte((val & 0x7F) | 0x80); + bytes.writeByte(((val >> 7) & 0x7F) | 0x80); + bytes.writeByte(((val >> 14) & 0x7F) | 0x80); + bytes.writeByte((val >> 21) & 0x7F); + } + else { // 32 bits + bytes.writeByte((val & 0x7F) | 0x80); + bytes.writeByte(((val >> 7) & 0x7F) | 0x80); + bytes.writeByte(((val >> 14) & 0x7F) | 0x80); + bytes.writeByte(((val >> 21) & 0x7F) | 0x80); + bytes.writeByte((val >> 28) & 0x7F); + } + } + + function float64(val:double) { + bytes.writeDouble(val); + } + + function byteStream(from:ByteStream) { + uint32(from.bytes.length); + bytes.writeBytes(from.bytes); + } + + function writeToArray(a) { + bytes.position = 0; + while ( bytes.bytesAvailable > 0 ) + a.push(bytes.readByte()); + return a; + } + + private const bytes = new ByteArray(); + } + + public function testABCByteStream() { + var bytes = new ABCByteStream; + + bytes.uint8(10); + bytes.int32(10); + bytes.int32(0x0fffabcd); + + var a = bytes.writeToArray([]); + for ( var i=0 ; i < a.length ; i++ ) + print(i + ": " + a[i]); + } +} diff --git a/tests/self/constantpool.es b/tests/self/constantpool.es new file mode 100644 index 00000000..b5d9ef96 --- /dev/null +++ b/tests/self/constantpool.es @@ -0,0 +1,200 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is [Open Source Virtual Machine.]. + * + * The Initial Developer of the Original Code is + * Adobe System Incorporated. + * Portions created by the Initial Developer are Copyright (C) 2004-2006 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Adobe AS3 Team + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +package es4 +{ + /**************************************************************** + * Constant pool manager. + * + * Reuse constants whenever possible. + * + * FIXME: we should be using hash tables here, not linear searching. + * Not hard to fix. + */ + class ABCConstantPool + { + function int32(n:int):uint { + var i; + + for ( i=0 ; i < int_pool.length ; i++ ) + if (int_pool[i] === n) + return i; + + int_bytes.int32(n); + int_pool[i] = n; + return i; + } + + function uint32(n:uint):uint { + var i; + + for ( i=0 ; i < uint_pool.length ; i++ ) + if (uint_pool[i] === n) + return i; + + uint_bytes.int32(n); + uint_pool[i] = n; + return i; + } + + function float64(n:double):uint { + var i; + + for ( i=0 ; i < double_pool.length ; i++ ) + if (double_pool[i] === n) + return i; + + double_bytes.float64(n); + double_pool[i] = n; + return i; + } + + function stringUtf8(s:string):uint { + Debug.enter("ConstantUtf8",str.length,str) + + var bytes = new ByteArray + bytes.endian = "littleEndian"; + makeInt32(bytes,str.length) + bytes.writeUTFBytes(str) + var index = addBytesToPool(bytes,utf8_pool) + + Debug.exit("ConstantUtf8",index) + Debug.log_mode::log("ConstantUtf8 "+str+" -> "+index,utf8_pool_out) + return index + } + + function qName(name_index,ns_index,is_attr) { + var bytes = new ByteArray + bytes.endian = "littleEndian"; + makeByte(bytes,is_attr?CONSTANT_QnameA:CONSTANT_Qname) + makeInt32(bytes,ns_index) + makeInt32(bytes,name_index) + var index = addBytesToPool(bytes,multiname_pool) + Debug.log_mode::log("ConstantQualifiedName "+name_index+" "+ns_index+" "+is_attr+" -> "+index,multiname_pool_out) + return index + } + + function ConstantNamespace(uri_index:uint,kind) + { + var kind_str = kind==CONSTANT_PackageNamespace ? "public" : // package public + kind==CONSTANT_PackageInternalNS ? "internal" : // package internal + kind==CONSTANT_ProtectedNamespace ? "protected" : + kind==CONSTANT_StaticProtectedNS ? "static protected" : + kind==CONSTANT_Namespace ? "user" : + kind==CONSTANT_PrivateNamespace ? "private" : "**error**" + + + Debug.enter("ConstantNamespace",uri_index,kind_str) + var bytes = new ByteArray + bytes.endian = "littleEndian"; + makeByte(bytes,kind) + makeInt32(bytes,uri_index) + var index = addBytesToPool(bytes,namespace_pool) + Debug.exit("ConstantNamespace",index) + Debug.log_mode::log("ConstantNamespace "+uri_index+" "+kind_str+" -> "+index,namespace_pool_out) + return index + } + + function ConstantNamespaceSet(namespaces) + { + var bytes = new ByteArray + bytes.endian = "littleEndian"; + var count = namespaces.length + + makeInt32(bytes,count) + var nsset_out = " " + for( var i = 0; i < count; i++ ) + { + var name = namespaces[i].@name + var kind = namespaces[i].@kind=="internal"?CONSTANT_PackageInternalNS: + "public"?CONSTANT_PackageNamespace: + CONSTANT_Namespace + var utf8_index = ConstantUtf8(name) + var ns_index = ConstantNamespace(utf8_index,kind) + nsset_out += ns_index + " " + makeInt32(bytes,ns_index) + } + var index = addBytesToPool(bytes,namespaceset_pool) + Debug.log_mode::log("ConstantNamespaceSet ["+nsset_out+"] -> "+index,namespaceset_pool_out) + return index + } + + function ConstantMultiname(name_index,nsset_index,is_attr) + { + var bytes = new ByteArray + bytes.endian = "littleEndian"; + makeByte(bytes,is_attr?CONSTANT_MultinameA:CONSTANT_Multiname) + makeInt32(bytes,name_index) + makeInt32(bytes,nsset_index) + var index = addBytesToPool(bytes,multiname_pool) + Debug.log_mode::log("ConstantMultiname "+name_index+" "+nsset_index+" -> "+index,multiname_pool_out) + return index + } + + function ConstantMultinameL(nsset_index,is_attr) + { + var bytes = new ByteArray + bytes.endian = "littleEndian"; + makeByte(bytes,is_attr?CONSTANT_MultinameLA:CONSTANT_MultinameL) + makeInt32(bytes,nsset_index) + var index = addBytesToPool(bytes,multiname_pool) + Debug.log_mode::log("ConstantMultinameL "+nsset_index+" -> "+index, multiname_pool_out) + return index + } + + function writeToByteStream(bs) { + // FIXME: write the fully formatted constant pool to the stream bs + } + + private const int_pool = new Array; + private const uint_pool = new Array; + private const double_pool = new Array; + private const utf8_pool = new Array; + private const namespace_pool = new Array; + private const namespaceset_pool = new Array; + private const multiname_pool = new Array; + + private const int_bytes = new ABCByteStream; + private const uint_bytes = new ABCByteStream; + private const double_bytes = new ABCByteStream; + private const utf8_bytes = new ABCByteStream; + private const namespace_bytes = new ABCByteStream; + private const namespaceset_bytes = new ABCByteStream; + private const multiname_bytes = new ABCByteStream; + } + +} diff --git a/tests/self/emitter.as b/tests/self/emitter.as new file mode 100644 index 00000000..0ac0248c --- /dev/null +++ b/tests/self/emitter.as @@ -0,0 +1,974 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is [Open Source Virtual Machine.]. + * + * The Initial Developer of the Original Code is + * Adobe System Incorporated. + * Portions created by the Initial Developer are Copyright (C) 2004-2006 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Adobe AS3 Team + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +package es4 +{ + use namespace release; + + function compareByteArrays(ba1:ByteArray, ba2:ByteArray) { + if( ba1.length !== ba2.length ) + return false; + + for( var i = ba1.length-1; i >= 0; i-- ) + if( ba1[i] !== ba2[i] ) + return false; + + return true; + } + + class ABCEmitter + { + // IF kinds + + public static const IF_false = 0; + public static const IF_true = IF_false + 1; + public static const IF_lt = IF_true + 1; + public static const IF_lti = IF_lt + 1; + public static const IF_le = IF_lti + 1; + public static const IF_gt = IF_le + 1; + public static const IF_ge = IF_gt + 1; + public static const IF_gei = IF_ge + 1; + public static const IF_eq = IF_gei + 1; + public static const IF_ne = IF_eq + 1; + public static const IF_stricteq = IF_ne + 1; + public static const IF_strictne = IF_stricteq + 1; + public static const IF_nlt = IF_strictne + 1; + public static const IF_nle = IF_nlt + 1; + public static const IF_ngt = IF_nle + 1; + public static const IF_nge = IF_ngt + 1; + + var if_addrs = new Array; // used for fixup + var else_addrs = new Array; + var lblnum = 0; + + // ABC parts + + var minor_version = 16; + var major_version = 46; + var int_pool = new Array; + var uint_pool = new Array; + var double_pool = new Array; + var utf8_pool = new Array; + var namespace_pool = new Array; + var namespaceset_pool = new Array; + var multiname_pool = new Array; + var method_infos = new Array; + var metadata_infos = new Array; + var instance_infos = new Array; + var class_infos = new Array; + var script_infos = new Array; + var method_bodys = new Array; + + function methodCount() { + return method_infos.length; + } + + function ABCEmitter(minor=16,major=46) { + if( major != 46 ) + throw "major version " + major + " not supported!"; + + minor_version = minor; + major_version = major; + } + + function addBytesToPool(bytes,pool) { + var count = pool.length; + for( var i = 0 ; i < count && !compareByteArrays(bytes, pool[i]) ; ++i ) + ; + pool[i] = bytes; + return i+1; + } + + var info_out = ["---------","I N F O S","---------"] + var body_out = ["-----------","B O D I E S","-----------"] + var code_out = [] + +/* + +MethodInfo { + U30 param_count + U30 ret_type // CONSTANT_Multiname, 0=Object + U30 param_types[param_count] // CONSTANT_Multiname, 0=Object + U30 name_index // 0=no name. + // 1=need_arguments, 2=need_activation, 4=need_rest 8=has_optional 16=ignore_rest, 32=explicit, 64=setsdxns, 128=has_paramnames + U8 flags + U30 optional_count // if has_optional + ValueKind[optional_count] // if has_optional + U30 param_names[param_count] // if has_paramnames +} + +*/ + function MethodInfo(param_count,type,types,name_index,flags,optional_count,optional_kinds) + { + Debug.enter("MethodInfo",param_count,type,name_index,flags,optional_count,optional_kinds) + + var method_info = method_infos.length //getMethodInfo(name) + var bytes = new ByteArray + bytes.endian = "littleEndian"; + + makeInt32(bytes,param_count); + makeInt32(bytes,type); + for (var i=0; i < param_count; i++) + { + makeInt32(bytes,types[i]); + } + makeInt32(bytes,name_index); + makeByte(bytes,flags); + if( false /*flags & HAS_OPTIONAL*/ ) + { + makeInt32(bytes,optional_count); + for (var i=0; i < optional_count; i++) + { + makeInt32(bytes,optional_kinds[i]); + } + } + + Debug.log_mode::log("MethodInfo "+param_count+" "+type+" "+types+" name="+name_index+" "+flags+" "+optional_count+" "+optional_kinds+" -> "+method_info,info_out) + + method_infos.push(bytes) + Debug.exit("MethodInfo") + return method_info + } + + function dumpBytes(bytes) + { + bytes.position = 0; + var str = ""; + while( bytes.bytesAvailable ) { str += " "+bytes.readByte() } + print(str); + } + + function MethodBody(info_index,max_stack,max_locals,scope_depth,max_scope,code,exceptions,slot_infos) + { + Debug.enter("MethodBody",info_index,max_stack,max_locals,scope_depth,max_scope) + var bytes = new ByteArray + bytes.endian = "littleEndian"; + + makeInt32(bytes,info_index) + makeInt32(bytes,max_stack) + makeInt32(bytes,max_locals) + makeInt32(bytes,scope_depth) + makeInt32(bytes,max_scope) + makeBytes(bytes,code) + makeBytes(bytes,exceptions) + emitInfos(bytes,slot_infos) + + method_bodys.push(bytes) + Debug.log_mode::log("MethodBody "+info_index+" "+max_stack+" "+max_locals+" "+scope_depth+" "+max_scope+" length="+code.length+" slots="+slot_infos.length+" size="+bytes.length,body_out) + Debug.exit("MethodBody") + return method_bodys.length + } + + function ScriptInfo(init_index,slot_infos) + { + Debug.log_mode::log("ScriptInfo init_index="+init_index+" slots="+slot_infos.length,info_out) + var bytes = new ByteArray + bytes.endian = "littleEndian"; + + makeInt32(bytes,init_index) + emitInfos(bytes,slot_infos) + + script_infos.push(bytes) + return script_infos.length + } + + // Emitter methods + + function emitVersion(bytes,minor,major) + { + Debug.enter("emitVersion",minor,major) + makeInt16(bytes,minor) + makeInt16(bytes,major) + Debug.exit("emitVersion",bytes.length) + } + + function emitConstantPool(bytes,pool) + { + Debug.enter("emitConstantPool",bytes.length,pool.length) + var count = pool.length + makeInt32(bytes,count==0?0:count+1) + for( var i = 0; i < count; ++i ) + { + bytes.writeBytes(pool[i]) + } + Debug.exit("emitConstantPool",bytes.length) + } + + function emitInfos(bytes,infos) + { + Debug.enter("emitInfos",infos.length) + var count = infos.length + makeInt32(bytes,count) + for( var i = 0; i < count; ++i ) + { + bytes.writeBytes(infos[i]) + } + Debug.exit("emitInfos",bytes.length) + } + + function emitClassInfos(bytes,instance_infos,class_infos) + { + Debug.enter("emitClassInfos") + var count = instance_infos.length + makeInt32(bytes,count) + for( var i = 0; i < count; ++i ) + { + bytes.writeBytes(instance_infos[i]) + } + for( var i = 0; i < count; ++i ) + { + bytes.writeBytes(class_infos[i]) + } + Debug.exit("emitClassInfos",bytes.length) + } + +/* + AbcFile { + U16 minor_version // = 16 + U16 major_version // = 46 + U30 constant_int_pool_count + ConstantInteger[constant_int_pool_count] // Cpool entries for integers + U30 constant_uint_pool_count + ConstantUInteger[constant_uint_pool_count] // Cpool entries for uints + U30 constant_double_pool_count + ConstantDouble[constant_double_pool_count] // Cpool entries for doubles + U30 constant_string_pool_count + ConstantString[constant_string_pool_count] // Cpool entries for strings + U30 constant_namespace_pool_count + ConstantNamespace[constant_namespace_pool_count] // Cpool entries for namespaces + U30 constant_namespace_set_pool_count + ConstantNamespaceSet[constant_namespace_set_pool_count] //Cpool entries for namespace sets + U30 constant_multiname_pool_count + ConstantMultiname[constant_multiname_pool_count] //Cpool entries for Multinames, Qnames, RTQnames, and RTQnamesLate + U30 methods_count + MethodInfo[methods_count] + U30 metadata_count + MetadataInfo[metadata_count] + U30 class_count + InstanceInfo[class_count] + ClassInfo[class_count] + U30 script_count + ScriptInfo[script_count] // ScriptInfo[script_count-1] is main entry point + U30 bodies_count + MethodBody[bodies_count] +} +*/ + + public function emit() { + Debug.enter("emit") + + var bytes = new ByteArray + bytes.endian = "littleEndian"; + + emitVersion(bytes,minor_version,major_version) + emitConstantPool(bytes,int_pool) + emitConstantPool(bytes,uint_pool) + emitConstantPool(bytes,double_pool) + emitConstantPool(bytes,utf8_pool) + emitConstantPool(bytes,namespace_pool) + emitConstantPool(bytes,namespaceset_pool) + emitConstantPool(bytes,multiname_pool) + emitInfos(bytes,method_infos) + emitInfos(bytes,metadata_infos) + emitClassInfos(bytes,instance_infos,class_infos) + emitInfos(bytes,script_infos) + emitInfos(bytes,method_bodys) + + Debug.log_mode::dump(int_pool_out) + Debug.log_mode::dump(uint_pool_out) + Debug.log_mode::dump(double_pool_out) + Debug.log_mode::dump(utf8_pool_out) + Debug.log_mode::dump(namespace_pool_out) + Debug.log_mode::dump(namespaceset_pool_out) + Debug.log_mode::dump(multiname_pool_out) + Debug.log_mode::dump(info_logs) + Debug.log_mode::dump(body_out) + Debug.log_mode::dump(code_logs) + + Debug.exit("emit",bytes.length) + + return bytes + } + + var code + var code_blocks = [] + var code_logs = ["-------","C O D E","-------"] + var info_logs = ["---------","I N F O S","---------"] + var pending_code_logs = [] + var pending_info_logs = [] + var initial_scope_depth_stack = [] + + /* RES Kludge - probably do something more complicated */ + + var temp_pool_stack = []; + var tempPool; + + function newTempPool( first:int ) { + return {max_locals: first, available: new Array()}; + } + + function getLocalTemp() { + if (tempPool.available.length > 0) + return tempPool.available.pop(); + return tempPool.max_locals++; + } + + function killLocalTemp(addr) { + // put out a Kill instruction? + tempPool.available.push(addr); + FreeTemp(addr); + } + + var local_count_stack = [] + + var max_method_stack:int; + var cur_method_stack:int; + + function stack(size:int):void + { + cur_method_stack += size; + if (cur_method_stack > max_method_stack) + { + max_method_stack = cur_method_stack; + } + } + + var stackDepthStack:Array = new Array(); + var scopeDepthStack:Array = new Array(); + + function saveStackDepth():void { + stackDepthStack.push(cur_method_stack); + } + + function restoreStackDepth():void { + cur_method_stack = stackDepthStack.pop(); + } + + var stackInfoStack:Array = new Array(); + + + function StartMethod(node, name) + { + Debug.enter("StartMethod") + + stackInfoStack.push({cur: cur_method_stack, max: max_method_stack}); + cur_method_stack = max_method_stack = 0; + this.pending_code_logs.push(code_out) // save current code log + this.code_out = [] // and start a new one + this.pending_info_logs.push(info_out) // save current info log + this.info_out = [] // and start a new one + + Debug.log_mode::log("StartMethod "+name,code_out) + + this.code_blocks.push(code) // save the current code block + this.code = new ByteArray // create a new one + this.code.endian = "littleEndian"; + initial_scope_depth_stack.push(scope_depth) + temp_pool_stack.push(tempPool); + var firstTemp = 1; + if (node.localName() == "Function") { + var params:int = node.@paramCount; + firstTemp = params + 1; + } + tempPool = newTempPool(firstTemp); + + Debug.exit("StartMethod") + } + + function FinishMethod(node, name,slot_infos, need_activation) + { + Debug.enter("FinishMethod",name) + Debug.log_mode::log("FinishMethod "+name,code_out) + + var param_count = 0 + if (node.localName() == "Function") { + var params:int = node.@paramCount; + param_count = params; + } + var type_index = 0 + var types = new Array + var name_index = ConstantUtf8(name) + var flags = (need_activation)? METHOD_Activation: 0; + var optional_count = 0 + var optional_kinds = new Array + + var info_index = MethodInfo(param_count,type_index,types,name_index,flags,optional_count,optional_kinds) + + var max_stack = max_method_stack; + var max_scope_depth = scope_depth + var exceptions = new ByteArray + exceptions.endian = "littleEndian"; + var initial_scope_depth = initial_scope_depth_stack.pop() + + MethodBody(info_index,max_stack,tempPool.max_locals,initial_scope_depth,max_scope_depth,this.code,exceptions,slot_infos) + + this.code = this.code_blocks.pop() // restore the previously active code block + tempPool = temp_pool_stack.pop(); + + this.code_logs.push(this.code_out) // save the finish code log + this.code_out = this.pending_code_logs.pop() // resume the outer code log, pushing inner one deeper + this.info_logs.push(this.info_out) + this.info_out = this.pending_info_logs.pop() + + scope_depth = initial_scope_depth + var stackDepths = stackInfoStack.pop(); + cur_method_stack = stackDepths.cur; + max_method_stack = stackDepths.max; + + Debug.exit("FinishMethod",info_index) + return info_index + } + + function getNamespaceKind(kind_str : String) + { + var result = + kind_str == "internal" ? CONSTANT_PackageInternalNS : + kind_str == "public" ? CONSTANT_PackageNamespace : + kind_str == "user" ? CONSTANT_Namespace : + kind_str == "private" ? CONSTANT_PrivateNamespace : CONSTANT_Namespace + return result + } + + function SlotInfo(slot_infos,kind,name,namespace) + { + Debug.enter("SlotInfo",name,kind,namespace) + + var slot_info = slot_infos.length //getMethodInfo(name) + var bytes = new ByteArray + bytes.endian = "littleEndian"; + + var identifier_index = ConstantUtf8(name) + var namespace_index = ConstantNamespace(ConstantUtf8(namespace.@name),getNamespaceKind(namespace.@kind)) + var name_index = ConstantQualifiedName(identifier_index,namespace_index,false) // is_attr = false + + makeInt32(bytes,name_index) + makeByte(bytes,kind) + + var kind_str = kind==SLOT_var?"var": + kind==SLOT_function?"function": + "unimplemented slot kind" + Debug.log_mode::log("SlotInfo "+kind_str+" "+name+" {"+dumpNamespaces([namespace])+"}",info_out) + + switch( kind ) + { + case SLOT_var: + makeInt32(bytes,0) // 0 = autoassign + makeInt32(bytes,0) // type * + makeInt32(bytes,0) // no default value + break + case SLOT_method: + case SLOT_getter: + case SLOT_setter: + case SLOT_class: + case SLOT_function: + throw "slot kind not implemented" + break + } + + slot_infos.push(bytes) + var result = slot_info + + Debug.exit("SlotInfo") + return result + } + + function getIP():int { + return code.position; + } + + function StartClass() + { + } + + function FinishClass() + { + } + + function StartProgram() + { + this.pending_info_logs.push(info_out) // save current code log + this.info_out = [] // and start a new one + + Debug.log_mode::log("StartProgram",code_out) + } + + function FinishProgram(init_index,slot_infos) + { + Debug.enter("FinishProgram",init_index) + Debug.log_mode::log("FinishProgram",code_out) + + ScriptInfo(init_index,slot_infos) + + this.info_logs.push(this.info_out) + this.info_out = this.pending_info_logs.pop() + + Debug.exit("FinishProgram") + } + + // Abstract machine methods + + // If statement processsing + + function ifUnOp(name, opcode, offset = 0) { + stack(-1); + Debug.log_mode::log(" "+code.length+":" + name + " L" + lblnum, code_out); + makeByte(code, opcode); + makeInt24(code, offset); + } + + function ifBinOp(name, opcode, offset = 0) { + stack(-2); + Debug.log_mode::log(" "+code.length+":" + name + " L" + lblnum, code_out); + makeByte(code, opcode); + makeInt24(code, offset); + } + + public function If(kind) { + switch (kind) { + case IF_false: ifUnOp("IfFalse", OP_iffalse); break; + case IF_true: ifUnOp("IfTrue", OP_iftrue); break; + case IF_nlt: ifBinOp("IfNlt", OP_ifnlt); break; + case IF_nle: ifBinOp("IfNle", OP_ifnle); break; + case IF_ngt: ifBinOp("IfNgt", OP_ifngt); break; + case IF_nge: ifBinOp("IfNge", OP_ifnge); break; + case IF_lt: ifBinOp("IfLt", OP_iflt); break; + case IF_le: ifBinOp("IfLe", OP_ifle); break; + case IF_gt: ifBinOp("IfGt", OP_ifgt); break; + case IF_ge: ifBinOp("IfGe", OP_ifge); break; + case IF_eq: ifBinOp("IfEq", OP_ifeq); break; + case IF_ne: ifBinOp("IfNe", OP_ifne); break; + case IF_stricteq: ifBinOp("IfStrictEq", OP_ifstricteq); break; + case IF_strictne: ifBinOp("IfStrictNe", OP_ifstrictne); break; + default: + throw new Error("invalid if kind " + kind); + break; + } + if_addrs.push({addr: code.position - 3, lbl:lblnum++}); + saveStackDepth(); // to be correct at else branch + } + + public function PatchIf(target:int) { + restoreStackDepth(); + var if_addr = if_addrs.pop(); + Debug.log_mode::log(" "+code.length+": L" + if_addr.lbl + ":", code_out); + var offset:int = target - if_addr.addr + 1 - 4; + var savePos = code.position; + code.position = if_addr.addr; + makeInt24(code, offset); + code.position = savePos; + } + + public function Else() { + Jump(lblnum); + else_addrs.push({addr:code.position - 3, lbl:lblnum++}); + } + + public function PatchElse(target:int) { + var else_addr = else_addrs.pop(); + Debug.log_mode::log(" "+code.length+": L" + else_addr.lbl + ":", code_out); + var offset:int = target - else_addr.addr + 1 - 4; + var savePos = code.position; + code.position = else_addr.addr; + makeInt24(code, offset); + code.position = savePos; + } + + + // Unconditional jump + + function dumpNamespaces(nsset) + { + Debug.enter("dumpNamespaces",nsset.length) + + var result = "" + if( nsset != void ) + for each( var ns in nsset ) + { + if( result != "" ) + { + result += "," + } + result += ns.@kind +':"'+ns.@name+'"' + } + + Debug.exit("dumpNamespaces",result) + + return result + } + + function FindProperty(name,namespaces,is_qualified,is_attr,is_strict, is_late) + { + Debug.enter("FindProperty",name,namespaces,is_strict,is_qualified,is_attr) + Debug.log_mode::log(" "+code.length+":FindProperty "+name+" {"+dumpNamespaces(namespaces)+"} is_qualified="+is_qualified+" is_strict="+is_strict, code_out) + + var index,name_index,ns_index + + if( name == "*" ) + { + name_index = 0 + } + else + { + name_index = ConstantUtf8(name) + } + + /* We're not handling RTQname or RTQnameL style properties for now. + When we do, the stack effects will be different */ + if (!is_late) + stack(1); + + if (is_late) { + ns_index = ConstantNamespaceSet(namespaces) + index = ConstantMultinameL(ns_index,is_attr) + } + else if( is_qualified && namespaces.length == 1 ) + { + var ns_name = namespaces[0].@name + var ns_kind = namespaces[0].@kind=="internal"?CONSTANT_PackageInternalNS:CONSTANT_Namespace + var ns_utf8_index = ConstantUtf8(ns_name) + var ns_index = ConstantNamespace(ns_utf8_index,ns_kind) + index = ConstantQualifiedName(name_index,ns_index,is_attr) + } + else + { + ns_index = ConstantNamespaceSet(namespaces) + index = ConstantMultiname(name_index,ns_index,is_attr) + } + + if( is_strict ) + { + makeByte(code,OP_findpropstrict); + makeInt32(code,index); + } + else + { + makeByte(code,OP_findproperty); + makeInt32(code,index); + } + Debug.exit("FindProperty") + } + + function CallProperty(name,namespaces,size,is_qualified,is_super,is_attr,is_lex) + { + Debug.enter("CallProperty",name,dumpNamespaces(namespaces),size,is_qualified,is_super,is_attr,is_lex) + Debug.log_mode::log(" "+code.length+":CallProperty "+name+" {"+dumpNamespaces(namespaces)+"} "+size,code_out) + + var index,name_index,ns_index + + /* Currently doesn't implement RTQname or any L, such things load the + function object and use Call instead. If we start running them through + here, we need to rethink stack effect */ + + stack(-size); // pop obj + size args, push result + + if( name == "*" ) + { + name_index = null + } + else + { + name_index = ConstantUtf8(name) + } + + if( is_qualified && namespaces.length == 1 ) + { + var ns_name = namespaces[0].@name + var ns_kind = namespaces[0].@kind=="public"?CONSTANT_PackageNamespace:CONSTANT_PackageInternalNS + var ns_utf8_index = ConstantUtf8(ns_name) + var ns_index = ConstantNamespace(ns_utf8_index,ns_kind) + index = ConstantQualifiedName(name_index,ns_index,is_attr) + } + else + { + ns_index = ConstantNamespaceSet(namespaces) + index = ConstantMultiname(name_index,ns_index,is_attr) + } + + if( is_super ) + { + makeByte(code,OP_callsuper); + makeInt32(code,index); + makeInt32(code,size); + } + else + if( is_lex ) + { + makeByte(code,OP_callproplex); + makeInt32(code,index); + makeInt32(code,size); + } + else + { + makeByte(code,OP_callproperty); + makeInt32(code,index); + makeInt32(code,size); + } + } + + function SetProperty(name,namespaces,is_qualified,is_super,is_attr,is_constinit, is_late) + { + Debug.enter("SetProperty",name,namespaces,is_qualified,is_super,is_attr,is_constinit); + Debug.log_mode::log(" "+code.length+":SetProperty "+name+" {"+dumpNamespaces(namespaces)+"} is_qualified="+is_qualified, code_out); + + var index,name_index,ns_index; + stack((is_late)?-3:-2); + + if( name == "*" ) + { + name_index = 0 + } + else + { + name_index = ConstantUtf8(name) + } + + if (is_late) { + ns_index = ConstantNamespaceSet(namespaces) + index = ConstantMultinameL(ns_index,is_attr) + } + else if( is_qualified && namespaces.length == 1 ) + { + var ns_name = namespaces[0].@name + var ns_kind = namespaces[0].@kind=="public"?CONSTANT_PackageNamespace:CONSTANT_PackageInternalNS + var ns_utf8_index = ConstantUtf8(ns_name) + var ns_index = ConstantNamespace(ns_utf8_index,ns_kind) + index = ConstantQualifiedName(name_index,ns_index,is_attr) + } + else + { + ns_index = ConstantNamespaceSet(namespaces) + index = ConstantMultiname(name_index,ns_index,is_attr) + } + + if( is_super ) + { + makeByte(code,OP_setsuper); + makeInt32(code,index); + } + if( is_constinit ) + { + makeByte(code,OP_initproperty); + makeInt32(code,index); + } + else + { + makeByte(code,OP_setproperty); + makeInt32(code,index); + } + } + + function GetProperty(name,namespaces,is_qualified,is_super,is_attr, is_late) + { + Debug.enter("GetProperty",name,namespaces,is_qualified,is_super,is_attr); + Debug.log_mode::log(" "+code.length+":GetProperty "+name+" {"+dumpNamespaces(namespaces)+"}", code_out); + + var index,name_index,ns_index; + + if (is_late) + stack(-1); + + if( name == "*" ) + { + name_index = 0 + } + else + { + name_index = ConstantUtf8(name) + } + + if (is_late) { + ns_index = ConstantNamespaceSet(namespaces) + index = ConstantMultinameL(ns_index,is_attr) + } + else if( is_qualified && namespaces.length == 1 ) + { + var ns_name = namespaces[0].@name + var ns_kind = namespaces[0].@kind=="public"?CONSTANT_PackageNamespace:CONSTANT_PackageInternalNS + var ns_utf8_index = ConstantUtf8(ns_name) + var ns_index = ConstantNamespace(ns_utf8_index,ns_kind) + index = ConstantQualifiedName(name_index,ns_index,is_attr) + } + else + { + ns_index = ConstantNamespaceSet(namespaces) + index = ConstantMultiname(name_index,ns_index,is_attr) + } + + if( is_super ) + { + makeByte(code,OP_getsuper) + makeInt32(code,index) + } + else + { + makeByte(code,OP_getproperty) + makeInt32(code,index) + } + } + + function DeleteProperty(name,namespaces,is_qualified,is_super,is_attr, is_late) + { + Debug.enter("DeleteProperty",name,namespaces,is_qualified,is_super,is_attr); + Debug.log_mode::log(" "+code.length+":DeleteProperty "+name+" {"+dumpNamespaces(namespaces)+"}", code_out); + + var index,name_index,ns_index; + + if (is_late) + stack(-1); + + if( name == "*" ) + { + name_index = 0 + } + else + { + name_index = ConstantUtf8(name) + } + + if (is_late) { + ns_index = ConstantNamespaceSet(namespaces) + index = ConstantMultinameL(ns_index,is_attr) + } + else if( is_qualified && namespaces.length == 1 ) + { + var ns_name = namespaces[0].@name + var ns_kind = namespaces[0].@kind=="public"?CONSTANT_PackageNamespace:CONSTANT_PackageInternalNS + var ns_utf8_index = ConstantUtf8(ns_name) + var ns_index = ConstantNamespace(ns_utf8_index,ns_kind) + index = ConstantQualifiedName(name_index,ns_index,is_attr) + } + else + { + ns_index = ConstantNamespaceSet(namespaces) + index = ConstantMultiname(name_index,ns_index,is_attr) + } + + if( is_super ) + { + } + else + { + makeByte(code,OP_deleteproperty) + makeInt32(code,index) + } + } + + function CheckType(name) + { + Debug.log_mode::log(" "+code.length+":CheckType "+name,code_out) + + var fullname = name.toString(); + if ("*" == fullname) + { + makeByte(code,OP_coerce_a); + } + else + if ("Object" == fullname) + { + makeByte(code,OP_coerce_o); + } + else + if ("String" == fullname) + { + makeByte(code,OP_coerce_s); + } + else + if ("Boolean" == fullname) + { + makeByte(code,OP_coerce_b); + } + else + if ("Number" == fullname) + { + makeByte(code,OP_coerce_d); + } + else + if ("int" == fullname) + { + makeByte(code,OP_coerce_i) + } + else if ("uint" == fullname) + { + makeByte(code,OP_coerce_u) + } + else + { +/* + int type_index = AddClassName(name); + Coerce(*ab->code,class_index); + makeByte(code,OP_coerce) + makeInt32(code,type_index) +*/ + } + + } + } + + import avmplus.Domain + + public function testABCEmitter() + { +//print("testABCEmitter") + var emitter = new ABCEmitter() + emitter.StartProgram() + emitter.StartMethod() + emitter.LoadThis() + emitter.PushScope() + emitter.FindProperty("print",[""],true,false,false) + emitter.PushString("hello, world") + emitter.CallProperty("print",[""],1,false,false,false,false) + emitter.Pop() + emitter.FindProperty("print",[""],true,false,false) + emitter.PushString("goodbye, jeff") + emitter.CallProperty("print",[""],1,false,false,false,false) + emitter.Pop() + emitter.LoadRegister(1) + emitter.ReturnValue() + emitter.FreeTemp(1) + var init_index = emitter.FinishMethod("") + emitter.FinishProgram(init_index) + var abc = emitter.emit() + //ByteArray.writeFile("temp.abc",abc) + + //print("temp.abc, "+abc.length+" bytes written") + + var dom = Domain.currentDomain + dom.loadBytes(abc) + } + + +}