Created
April 3, 2012 17:06
-
-
Save cognominal/2293712 to your computer and use it in GitHub Desktop.
update for nqpq
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
bash-3.2$ git diff | |
diff --git a/src/NQPQ/World.pm b/src/NQPQ/World.pm | |
index 2daaf22..db3df17 100644 | |
--- a/src/NQPQ/World.pm | |
+++ b/src/NQPQ/World.pm | |
@@ -2,6 +2,52 @@ use NQPP6Regex; | |
use QRegex; | |
class NQP::World is HLL::World { | |
+ # The stack of lexical pads, actually as PAST::Block objects. The | |
+ # outermost frame is at the bottom, the latest frame is on top. | |
+ has @!BLOCKS; | |
+ | |
+ # Set of code objects that need to be fixed up if dynamic compilation | |
+ # takes place (that is, compiling parts of the program early during | |
+ # compile time because they're needed at a BEGIN phase). Maps subid | |
+ # to a list of code objects. | |
+ has %!code_objects_to_fix_up; | |
+ | |
+ # Mapping of PAST::Stmts node containing fixups, keyed by sub ID. If | |
+ # we do dynamic compilation then we do the fixups immediately and | |
+ # then clear this list. | |
+ has %!code_object_fixup_list; | |
+ | |
+ # Mapping of sub IDs to SC indexes of code stubs. | |
+ has %!code_stub_sc_idx; | |
+ | |
+ # Creates a new lexical scope and puts it on top of the stack. | |
+ method push_lexpad($/) { | |
+ # Create pad, link to outer and add to stack. | |
+ my $pad := PAST::Block.new( PAST::Stmts.new(), :node($/) ); | |
+ if +@!BLOCKS { | |
+ $pad<outer> := @!BLOCKS[+@!BLOCKS - 1]; | |
+ } | |
+ @!BLOCKS[+@!BLOCKS] := $pad; | |
+ $pad | |
+ } | |
+ | |
+ # Pops a lexical scope off the stack. | |
+ method pop_lexpad() { | |
+ @!BLOCKS.pop() | |
+ } | |
+ | |
+ # Gets the top lexpad. | |
+ method cur_lexpad() { | |
+ @!BLOCKS[+@!BLOCKS - 1] | |
+ } | |
+ | |
+ # XXX This goes away really soon...after the multi refactor. | |
+ method get_legacy_block_list() { | |
+ my @x := nqp::clone(@!BLOCKS); | |
+ @x.reverse(); | |
+ @x | |
+ } | |
+ | |
# XXX We need to load the module loader to load modules, which means we | |
# can't just use ...; it, which means we can't get the ModuleLoader symbol | |
# merged into anywhere...anyway, we chop the circularity by finding it | |
@@ -20,7 +66,7 @@ class NQP::World is HLL::World { | |
# Do load for pre-compiled situation. | |
if self.is_precompilation_mode() { | |
- self.add_event(:deserialize_past(PAST::Stmts.new( | |
+ self.add_load_dependency_task(:deserialize_past(PAST::Stmts.new( | |
PAST::Op.new( | |
:pirop('load_bytecode vs'), 'ModuleLoader.pbc' | |
), | |
@@ -37,7 +83,7 @@ class NQP::World is HLL::World { | |
} | |
else { | |
# Needs fixup. | |
- self.add_event(:fixup_past(PAST::Op.new( | |
+ self.add_fixup_task(:fixup_past(PAST::Op.new( | |
:pasttype('callmethod'), :name('set_outer_ctx'), | |
PAST::Var.new( :name('block'), :scope('register') ), | |
PAST::Op.new( | |
@@ -60,15 +106,14 @@ class NQP::World is HLL::World { | |
# Make sure we do the loading during deserialization. | |
if self.is_precompilation_mode() { | |
- self.add_event(:deserialize_past(PAST::Stmts.new( | |
+ self.add_load_dependency_task(:deserialize_past(PAST::Stmts.new( | |
PAST::Op.new( | |
:pirop('load_bytecode vs'), 'ModuleLoader.pbc' | |
), | |
PAST::Op.new( | |
:pasttype('callmethod'), :name('load_module'), | |
PAST::Var.new( :name('ModuleLoader'), :namespace([]), :scope('package') ), | |
- $module_name, | |
- self.get_slot_past_for_object($cur_GLOBALish) | |
+ $module_name | |
)))); | |
} | |
@@ -87,23 +132,6 @@ class NQP::World is HLL::World { | |
$target := pir::nqp_get_package_through_who__PPs($target, $_); | |
} | |
($target.WHO){$name} := $obj; | |
- | |
- # Add deserialization installation of the symbol. | |
- if self.is_precompilation_mode() { | |
- my $path := self.get_slot_past_for_object($package); | |
- for @sym { | |
- $path := PAST::Op.new(:pirop('nqp_get_package_through_who PPs'), $path, ~$_); | |
- } | |
- self.add_event(:deserialize_past(PAST::Op.new( | |
- :pasttype('bind_6model'), | |
- PAST::Var.new( | |
- :scope('keyed'), | |
- PAST::Op.new( :pirop('get_who PP'), $path ), | |
- $name | |
- ), | |
- self.get_slot_past_for_object($obj) | |
- ))); | |
- } | |
} | |
# Installs a lexical symbol. Takes a PAST::Block object, name and | |
@@ -130,7 +158,7 @@ class NQP::World is HLL::World { | |
PAST::Val.new( :value($block), :returns('LexInfo' )) | |
) | |
); | |
- self.add_event(:deserialize_past($fixup), :fixup_past($fixup)); | |
+ self.add_fixup_task(:deserialize_past($fixup), :fixup_past($fixup)); | |
} | |
# Adds a fixup to install a specified PAST::Block in a package under the | |
@@ -145,7 +173,163 @@ class NQP::World is HLL::World { | |
), | |
PAST::Val.new( :value($past_block) ) | |
); | |
- self.add_event(:deserialize_past($fixup), :fixup_past($fixup)); | |
+ self.add_fixup_task(:deserialize_past($fixup), :fixup_past($fixup)); | |
+ } | |
+ | |
+ # Registers a code object, and gives it a dynamic compilation thunk. | |
+ # Makes a real code object if it's a dispatcher. | |
+ method create_code($past, $name, $is_dispatcher) { | |
+ # See if NQPRoutine is available to wrap this up in. | |
+ my $code_type; | |
+ my $have_code_type := 0; | |
+ try { | |
+ $code_type := self.find_sym(['NQPRoutine']); | |
+ $have_code_type := $*PACKAGE.HOW.name($*PACKAGE) ne 'NQPRoutine'; | |
+ } | |
+ | |
+ # For code refs, we need a "stub" that we'll clone and use for the | |
+ # compile-time representation. If it ever gets invoked it'll go | |
+ # and compile the code and run it. | |
+ # XXX Lexical environment. | |
+ my $stub_code := sub (*@args, *%named) { | |
+ # Do the compilation. | |
+ self.set_nqp_language_defaults($past); | |
+ my $nqpcomp := pir::compreg__Ps('nqp'); | |
+ my $post := $nqpcomp.post($past); | |
+ my $pir := $nqpcomp.pir($post); | |
+ my $compiled := $nqpcomp.evalpmc($pir); | |
+ | |
+ # Fix up any code objects holding stubs with the real compiled thing. | |
+ my $c := nqp::elems($compiled); | |
+ my $i := 0; | |
+ while $i < $c { | |
+ my $subid := $compiled[$i].get_subid(); | |
+ if pir::exists(%!code_objects_to_fix_up, $subid) { | |
+ # First, go over the code objects. Update the $!do, and the | |
+ # entry in the SC. Make sure the newly compiled code is marked | |
+ # as a static code ref. | |
+ my $static := %!code_objects_to_fix_up{$subid}.shift(); | |
+ nqp::bindattr($static, $code_type, '$!do', $compiled[$i]); | |
+ for %!code_objects_to_fix_up{$subid} { | |
+ nqp::bindattr($_, $code_type, '$!do', pir::clone($compiled[$i])); | |
+ } | |
+ pir::setprop__vPsP($compiled[$i], 'STATIC_CODE_REF', $compiled[$i]); | |
+ self.update_root_code_ref(%!code_stub_sc_idx{$subid}, $compiled[$i]); | |
+ | |
+ # Clear up the fixup statements. | |
+ my $fixup_stmts := %!code_object_fixup_list{$subid}; | |
+ $fixup_stmts.shift() while +@($fixup_stmts); | |
+ } | |
+ $i := $i + 1; | |
+ } | |
+ | |
+ $compiled(|@args, |%named); | |
+ }; | |
+ | |
+ # See if we already have our compile-time dummy. If not, create it. | |
+ my $fixups := PAST::Stmts.new(); | |
+ my $dummy; | |
+ my $code_ref_idx; | |
+ if pir::defined($past<compile_time_dummy>) { | |
+ $dummy := $past<compile_time_dummy>; | |
+ } | |
+ else { | |
+ # Create a fresh stub code, and set its name. | |
+ $dummy := pir::nqp_fresh_stub__PP($stub_code); | |
+ pir::assign__vPS($dummy, $name); | |
+ | |
+ # Tag it as a static code ref and add it to the root code refs set. | |
+ pir::setprop__vPsP($dummy, 'STATIC_CODE_REF', $dummy); | |
+ $code_ref_idx := self.add_root_code_ref($dummy, $past); | |
+ %!code_stub_sc_idx{$past.subid()} := $code_ref_idx; | |
+ $past<compile_time_dummy> := $dummy; | |
+ | |
+ # Attach PAST as a property to the stub code. | |
+ pir::setprop__vPsP($dummy, 'PAST', $past); | |
+ | |
+ # Things with code objects may be methods in roles or multi-dispatch | |
+ # routines. We need to handle their cloning and maintain the fixup | |
+ # list. | |
+ if $have_code_type { | |
+ %!code_object_fixup_list{$past.subid()} := $fixups; | |
+ if self.is_precompilation_mode() { | |
+ pir::setprop__vPsP($dummy, 'CLONE_CALLBACK', sub ($orig, $clone, $code_obj) { | |
+ %!code_objects_to_fix_up{$past.subid()}.push($code_obj); | |
+ }); | |
+ } | |
+ else { | |
+ pir::setprop__vPsP($dummy, 'CLONE_CALLBACK', sub ($orig, $clone, $code_obj) { | |
+ # Emit fixup code. | |
+ self.add_object($code_obj); | |
+ $fixups.push(PAST::Op.new( | |
+ :pirop('setattribute vPPsP'), | |
+ self.get_ref($code_obj), | |
+ self.get_ref($code_type), | |
+ '$!do', | |
+ PAST::Op.new( | |
+ :pirop('set_sub_code_object 0PP'), | |
+ PAST::Op.new( :pirop('clone PP'), PAST::Val.new( :value($past) ) ), | |
+ self.get_ref($code_obj) | |
+ ) | |
+ )); | |
+ | |
+ # Add to dynamic compilation fixup list. | |
+ %!code_objects_to_fix_up{$past.subid()}.push($code_obj); | |
+ }); | |
+ } | |
+ } | |
+ } | |
+ | |
+ # Add fixups task node; it'll get populated or cleared during the compile. | |
+ self.add_fixup_task(:fixup_past($fixups)); | |
+ | |
+ # Provided we have the code type, now wrap what we have up in a | |
+ # code object. | |
+ if $have_code_type { | |
+ # Create it now. | |
+ my $code_obj := nqp::create($code_type); | |
+ nqp::bindattr($code_obj, $code_type, '$!do', $dummy); | |
+ nqp::bindattr($code_obj, $code_type, '$!dispatchees', nqp::list()) | |
+ if $is_dispatcher; | |
+ my $slot := self.add_object($code_obj); | |
+ | |
+ # Add deserialization fixup task. | |
+ self.add_fixup_task( | |
+ :deserialize_past(PAST::Op.new( | |
+ :pirop('set_sub_code_object vPP'), | |
+ PAST::Val.new( :value($past) ), | |
+ self.get_ref($code_obj) | |
+ ))); | |
+ | |
+ # Add fixup of the code object and the $!do attribute. | |
+ $fixups.push(PAST::Op.new( | |
+ :pirop('setattribute vPPsP'), | |
+ self.get_ref($code_obj), | |
+ self.get_ref($code_type), | |
+ '$!do', | |
+ PAST::Val.new( :value($past) ) | |
+ )); | |
+ $fixups.push(PAST::Op.new( | |
+ :pirop('set_sub_code_object vPP'), | |
+ PAST::Val.new( :value($past) ), | |
+ self.get_ref($code_obj) | |
+ )); | |
+ | |
+ # Add it to the dynamic compilation fixup todo list. | |
+ %!code_objects_to_fix_up{$past.subid()} := [$code_obj]; | |
+ | |
+ $code_obj | |
+ } | |
+ else { | |
+ # For fixup, if we have no code body, we need to assign the method body | |
+ # we actually compiled into the one that went into the SC. | |
+ $fixups.push(PAST::Op.new( | |
+ :pirop('assign vPP'), | |
+ self.get_slot_past_for_code_ref_at($code_ref_idx), | |
+ PAST::Val.new( :value($past) ) | |
+ )); | |
+ return $dummy; | |
+ } | |
} | |
# Creates a meta-object for a package, adds it to the root objects and | |
@@ -157,25 +341,6 @@ class NQP::World is HLL::World { | |
if pir::defined($repr) { %args<repr> := $repr; } | |
my $mo := $how.new_type(|%args); | |
my $slot := self.add_object($mo); | |
- | |
- # Add an event. There's no fixup to do, just a type object to create | |
- # on deserialization. | |
- if self.is_precompilation_mode() { | |
- my @how_ns := pir::split('::', $how.HOW.name($how)); | |
- my $how_name := @how_ns.pop(); | |
- my $setup_call := PAST::Op.new( | |
- :pasttype('callmethod'), :name('new_type'), | |
- self.get_ref($how) | |
- ); | |
- if pir::defined($name) { | |
- $setup_call.push(PAST::Val.new( :value($name), :named('name') )); | |
- } | |
- if pir::defined($repr) { | |
- $setup_call.push(PAST::Val.new( :value($repr), :named('repr') )); | |
- } | |
- self.add_event(:deserialize_past( | |
- self.add_object_to_cur_sc_past($slot, $setup_call))); | |
- } | |
# Result is just the object. | |
return $mo; | |
@@ -187,249 +352,144 @@ class NQP::World is HLL::World { | |
# as named arguments, but where these passed objects also live in a | |
# serialization context. The type would be passed in this way. | |
method pkg_add_attribute($obj, $meta_attr, %lit_args, %obj_args) { | |
- # Create and add right away. | |
my $attr := $meta_attr.new(|%lit_args, |%obj_args); | |
$obj.HOW.add_attribute($obj, $attr); | |
- | |
- # Emit code to create and add it when deserializing. | |
- if self.is_precompilation_mode() { | |
- my $create_call := PAST::Op.new( | |
- :pasttype('callmethod'), :name('new'), | |
- self.get_ref($meta_attr) | |
- ); | |
- for %lit_args { | |
- $create_call.push(PAST::Val.new( :value($_.value), :named($_.key) )); | |
- } | |
- for %obj_args { | |
- my $lookup := self.get_ref($_.value); | |
- $lookup.named($_.key); | |
- $create_call.push($lookup); | |
- } | |
- my $obj_slot_past := self.get_slot_past_for_object($obj); | |
- self.add_event(:deserialize_past(PAST::Op.new( | |
- :pasttype('callmethod'), :name('add_attribute'), | |
- PAST::Op.new( :pirop('get_how PP'), $obj_slot_past ), | |
- $obj_slot_past, | |
- $create_call | |
- ))); | |
- } | |
} | |
- # Adds a method to the meta-object, and stores an event for the action. | |
- # Note that methods are always subject to fixing up since the actual | |
- # compiled code isn't available until compilation is complete. | |
- method pkg_add_method($obj, $meta_method_name, $name, $method_past, $is_dispatcher) { | |
- # For methods, we need a "stub" that we'll clone and use for the | |
- # compile-time representation. If it ever gets invoked it'll go | |
- # and compile the code and run it. | |
- # XXX Lexical environment. | |
- # XXX Cache compiled output. | |
- my $stub_code := sub (*@args, *%named) { | |
- my $compiled := PAST::Compiler.compile($method_past); | |
- $compiled(|@args, |%named); | |
- }; | |
+ # Adds a method to the meta-object. | |
+ method pkg_add_method($obj, $meta_method_name, $name, $code, $is_dispatcher) { | |
+ $obj.HOW."$meta_method_name"($obj, $name, $code, $is_dispatcher); | |
+ } | |
+ | |
+ # Associates a signature with a code object. | |
+ method set_routine_signature($code_obj, $types, $definednesses) { | |
+ my $sig_type := self.find_sym(['NQPSignature']); | |
+ my $code_type := self.find_sym(['NQPRoutine']); | |
+ my $sig_obj := nqp::create($sig_type); | |
+ nqp::bindattr($sig_obj, $sig_type, '$!types', $types); | |
+ nqp::bindattr($sig_obj, $sig_type, '$!definednesses', $definednesses); | |
+ nqp::bindattr($code_obj, $code_type, '$!signature', $sig_obj); | |
+ } | |
+ | |
+ # Associates a signature with a Parrot sub. | |
+ method set_routine_signature_on_parrot_sub($routine, $types, $definednesses) { | |
+ # Build signature object and put it in place now. | |
+ my $sig_type := self.find_sym(['NQPSignature']); | |
+ my $sig_obj := nqp::create($sig_type); | |
+ nqp::bindattr($sig_obj, $sig_type, '$!types', $types); | |
+ nqp::bindattr($sig_obj, $sig_type, '$!definednesses', $definednesses); | |
+ my $slot := self.add_object($sig_obj); | |
- # See if we already have our compile-time dummy. If not, create it. | |
- my $fixups := PAST::Stmts.new(); | |
- my $dummy; | |
- if pir::defined($method_past<compile_time_dummy>) { | |
- $dummy := $method_past<compile_time_dummy>; | |
+ if self.is_precompilation_mode() { | |
+ self.add_fixup_task(:deserialize_past(PAST::Op.new( | |
+ :pirop('set_sub_multisig vPP'), | |
+ PAST::Val.new( :value($routine) ), | |
+ self.get_ref($sig_obj) | |
+ ))); | |
} | |
else { | |
- # What we create depends on whether it's a dispatcher or not. | |
- # If it is a dispatcher, set the PMC type it uses and then use | |
- # that for the dummy. | |
- if $is_dispatcher { | |
- $method_past.pirflags(':instanceof("DispatcherSub")'); | |
- $dummy := pir::assign__0PP(pir::new__Ps('DispatcherSub'), $stub_code); | |
- | |
- # The dispatcher will get cloned if more candidates are added in | |
- # a subclass; this makes sure that we fix up the clone also. | |
- pir::setprop__vPsP($dummy, 'CLONE_CALLBACK', sub ($orig, $clone) { | |
- self.add_code($clone); | |
- $fixups.push(PAST::Op.new( | |
- :pirop('assign vPP'), | |
- self.get_slot_past_for_object($clone), | |
- PAST::Val.new( :value(pir::getprop__PPs($orig, 'PAST')) ) | |
- )); | |
- }); | |
+ # Fixup code depends on if we have the routine in the SC for | |
+ # fixing up. | |
+ my $fixup := PAST::Op.new( :pirop('set_sub_multisig vPP'), self.get_ref($sig_obj) ); | |
+ if pir::defined($routine<compile_time_dummy>) { | |
+ $fixup.unshift(self.get_slot_past_for_object($routine<compile_time_dummy>)); | |
} | |
else { | |
- $dummy := pir::clone__PP($stub_code); | |
+ $fixup.unshift(PAST::Val.new( :value($routine) )); | |
} | |
- pir::assign__vPS($dummy, $name); | |
- self.add_code($dummy); | |
- $method_past<compile_time_dummy> := $dummy; | |
+ self.add_fixup_task(:fixup_past($fixup)); | |
} | |
- | |
- # Attach PAST as a property to the dummy. | |
- pir::setprop__vPsP($dummy, 'PAST', $method_past); | |
- | |
- # Add it to the compile time meta-object. | |
- $obj.HOW."$meta_method_name"($obj, $name, $dummy); | |
- | |
- # For fixup, need to assign the method body we actually compiled | |
- # onto the one that went into the SC. Deserializing is easier - | |
- # just the straight meta-method call. | |
- $fixups.push(PAST::Op.new( | |
- :pirop('assign vPP'), | |
- self.get_slot_past_for_object($dummy), | |
- PAST::Val.new( :value($method_past) ) | |
- )); | |
- my $slot_past := self.get_slot_past_for_object($obj); | |
- self.add_event( | |
- :deserialize_past(PAST::Op.new( | |
- :pasttype('callmethod'), :name($meta_method_name), | |
- PAST::Op.new( :pirop('get_how PP'), $slot_past ), | |
- $slot_past, | |
- $name, | |
- PAST::Val.new( :value($method_past) ) | |
- )), | |
- :fixup_past($fixups)); | |
- } | |
- | |
- # Associates a signature with a routine. | |
- method set_routine_signature($routine, $types, $definednesses) { | |
- # Fixup code depends on if we have the routine in the SC for | |
- # fixing up. Deserialization always goes on the blockref. | |
- my $fixup := PAST::Op.new( :pirop('set_sub_multisig vPPP'), $types, $definednesses ); | |
- if pir::defined($routine<compile_time_dummy>) { | |
- $fixup.unshift(self.get_slot_past_for_object($routine<compile_time_dummy>)); | |
- } | |
- else { | |
- $fixup.unshift(PAST::Val.new( :value($routine) )); | |
- } | |
- my $des := PAST::Op.new( :pirop('set_sub_multisig vPPP'), | |
- PAST::Val.new( :value($routine) ), $types, $definednesses | |
- ); | |
- self.add_event(:deserialize_past($des), :fixup_past($fixup)); | |
} | |
# This handles associating the role body with a role declaration. | |
method pkg_set_body_block($obj, $body_past) { | |
- # In fixup, we'll actually run the real body block, but we | |
- # need to run it with the parameters that were used at compile | |
- # time. We rely on those being in the SC. The "dummy" body block | |
- # we supply will simply capture those and append to the body | |
- # invoke PAST. That's the "easy" part. The harder part is that | |
- # it also sets up the fixups for all the reified (cloned) methods. | |
- # Note that the fact we back-reference it always to the original | |
- # method, which in fact was just captured by running the block for | |
- # each role setup, means we get the timing right in order to end | |
- # up with methods capturing the correct type argument. | |
- my $fixups := PAST::Stmts.new(); | |
- my $dummy := sub (*@type_args) { | |
- # Set up call to invoke body block with the type arguments. | |
- my $invoke_body := PAST::Op.new( | |
- :pasttype('call'), | |
- PAST::Val.new( :value($body_past) ) | |
- ); | |
- for @type_args { | |
- $invoke_body.push(self.get_slot_past_for_object($_)); | |
- } | |
- $fixups.push($invoke_body); | |
- | |
- # Set a reification callback on all the dummy methods. | |
- for $obj.HOW.methods($obj, :local(1)) { | |
- pir::setprop__vPsP($_, 'REIFY_CALLBACK', sub ($meth) { | |
- # Make a clone and add it to the SC. | |
- my $clone := pir::clone($meth); | |
- self.add_code($clone); | |
- | |
- # Add fixup for the cloned code. | |
- $fixups.push(PAST::Op.new( | |
- :pirop('assign vPP'), | |
- self.get_slot_past_for_object($clone), | |
- PAST::Val.new( :value(pir::getprop__PPs($meth, 'PAST')) ) | |
- )); | |
- | |
- # Result is the cloned method that will be fixed up. | |
- $clone | |
- }); | |
- } | |
- }; | |
+ # Get a code object for the body block. | |
+ my $body_code_obj := self.create_code($body_past, $body_past.name, 0); | |
- # Pass the dummy along as the role body block. | |
- $obj.HOW.set_body_block($obj, $dummy); | |
- | |
- # In deserialization, easy - just do the meta-object call. | |
- my $slot_past := self.get_slot_past_for_object($obj); | |
- my $des := PAST::Op.new( | |
- :pasttype('callmethod'), :name('set_body_block'), | |
- PAST::Op.new( :pirop('get_how PP'), $slot_past ), | |
- $slot_past, | |
- PAST::Val.new( :value($body_past) ) | |
- ); | |
- | |
- self.add_event(:deserialize_past($des), :fixup_past($fixups)); | |
+ # Set it as the body block. | |
+ $obj.HOW.set_body_block($obj, $body_code_obj); | |
} | |
- # Adds a parent or role to the meta-object, and stores an event for | |
- # the action. | |
+ # Adds a parent or role to the meta-object. | |
method pkg_add_parent_or_role($obj, $meta_method_name, $to_add) { | |
- # Do the actual addition on the meta-object immediately. | |
$obj.HOW."$meta_method_name"($obj, $to_add); | |
- | |
- # Emit code to add it when deserializing. | |
- if self.is_precompilation_mode() { | |
- my $slot_past := self.get_slot_past_for_object($obj); | |
- self.add_event(:deserialize_past(PAST::Op.new( | |
- :pasttype('callmethod'), :name($meta_method_name), | |
- PAST::Op.new( :pirop('get_how PP'), $slot_past ), | |
- $slot_past, | |
- self.get_ref($to_add) | |
- ))); | |
- } | |
} | |
method pkg_add_parrot_vtable_handler_mapping($obj, $name, $att_name) { | |
- # Do the actual addition on the meta-object immediately. | |
$obj.HOW.add_parrot_vtable_handler_mapping($obj, $name, $att_name); | |
- | |
- # Emit code to add it when deserializing. | |
- if self.is_precompilation_mode() { | |
- my $slot_past := self.get_slot_past_for_object($obj); | |
- self.add_event(:deserialize_past(PAST::Op.new( | |
- :pasttype('callmethod'), :name('add_parrot_vtable_handler_mapping'), | |
- PAST::Op.new( :pirop('get_how PP'), $slot_past ), | |
- $slot_past, | |
- $name, $att_name | |
- ))); | |
- } | |
} | |
- # Composes the package, and stores an event for this action. | |
+ # Composes the package. | |
method pkg_compose($obj) { | |
- # Compose. | |
$obj.HOW.compose($obj); | |
+ } | |
+ | |
+ # Runs a block at BEGIN time. | |
+ method run_begin_block($past) { | |
+ # Create a wrapper that makes all outer symbols visible. | |
+ my $wrapper := PAST::Block.new( | |
+ PAST::Stmts.new(), | |
+ $past | |
+ ); | |
+ my %seen; | |
+ my $i := +@!BLOCKS; | |
+ while $i > 0 { | |
+ $i := $i - 1; | |
+ my %symbols := @!BLOCKS[$i].symtable(); | |
+ for %symbols { | |
+ if !%seen{$_.key} && pir::exists($_.value, 'value') { | |
+ try { | |
+ $wrapper[0].push(PAST::Var.new( | |
+ :name($_.key), :scope('lexical_6model'), :isdecl(1), | |
+ :viviself(self.get_ref(($_.value)<value>)) | |
+ )); | |
+ }; | |
+ %seen{$_.key} := 1; | |
+ } | |
+ } | |
+ } | |
- # Emit code to do the composition when deserializing. | |
- if self.is_precompilation_mode() { | |
- my $slot_past := self.get_slot_past_for_object($obj); | |
- self.add_event(:deserialize_past(PAST::Op.new( | |
- :pasttype('callmethod'), :name('compose'), | |
- PAST::Op.new( :pirop('get_how PP'), $slot_past ), | |
- $slot_past | |
- ))); | |
+ # Compile and run it. | |
+ my $code := self.create_code($wrapper, 'BEGIN block', 0); | |
+ my $old_global := pir::get_hll_global__PPs(nqp::list(), 'GLOBAL'); | |
+ pir::set_hll_global__vPsP(nqp::list(), 'GLOBAL', $*GLOBALish); | |
+ $code(); | |
+ pir::set_hll_global__vPsP(nqp::list(), 'GLOBAL', $old_global); | |
+ | |
+ # Need any nested blocks inside the BEGIN block to make it into the | |
+ # output code. | |
+ $wrapper.shift(); | |
+ return $wrapper; | |
+ } | |
+ | |
+ # Sets NQP language defaults on a block for compilation. | |
+ method set_nqp_language_defaults($block) { | |
+ # Need to load the NQP dynops/dympmcs, plus any extras requested. | |
+ my @loadlibs := ['nqp_group', 'nqp_ops', 'nqp_bigint_ops', 'trans_ops', 'io_ops']; | |
+ if %*COMPILING<%?OPTIONS><vmlibs> { | |
+ for pir::split(',', %*COMPILING<%?OPTIONS><vmlibs>) { | |
+ @loadlibs.push($_); | |
+ } | |
} | |
+ $block.loadlibs(|@loadlibs); | |
+ | |
+ # Set HLL. | |
+ $block.hll('nqp'); | |
} | |
# Generates a series of PAST operations that will build this context if | |
# it doesn't exist, and fix it up if it already does. | |
method to_past() { | |
- my $des := PAST::Stmts.new(); | |
- my $fix := PAST::Stmts.new(); | |
- for self.event_stream() { | |
- $des.push(PAST::Stmt.new( $_.deserialize_past() )) if pir::defined($_.deserialize_past()); | |
- $fix.push(PAST::Stmt.new( $_.fixup_past() )) if pir::defined($_.fixup_past()); | |
- } | |
- make PAST::Op.new( | |
- :pasttype('if'), | |
- PAST::Op.new( | |
- :pirop('isnull IP'), | |
- PAST::Op.new( :pirop('nqp_get_sc Ps'), self.handle() ) | |
- ), | |
- PAST::Stmts.new( | |
+ if self.is_precompilation_mode() { | |
+ my $load_tasks := PAST::Stmts.new(); | |
+ for self.load_dependency_tasks() { | |
+ $load_tasks.push(PAST::Stmt.new($_)); | |
+ } | |
+ my $fixup_tasks := PAST::Stmts.new(); | |
+ for self.fixup_tasks() { | |
+ $fixup_tasks.push(PAST::Stmt.new($_)); | |
+ } | |
+ return PAST::Stmts.new( | |
PAST::Op.new( :pirop('nqp_dynop_setup v') ), | |
PAST::Op.new( :pirop('nqp_bigint_setup v') ), | |
PAST::Op.new( | |
@@ -448,9 +508,119 @@ class NQP::World is HLL::World { | |
PAST::Var.new( :name('cur_sc'), :scope('register') ), | |
self.sc.description | |
), | |
- $des | |
- ), | |
- $fix | |
- ); | |
+ $load_tasks, | |
+ self.serialize_and_produce_deserialization_past('cur_sc'), | |
+ $fixup_tasks | |
+ ); | |
+ } | |
+ else { | |
+ my $tasks := PAST::Stmts.new(); | |
+ for self.load_dependency_tasks() { | |
+ $tasks.push(PAST::Stmt.new($_)); | |
+ } | |
+ for self.fixup_tasks() { | |
+ $tasks.push(PAST::Stmt.new($_)); | |
+ } | |
+ return $tasks | |
+ } | |
+ } | |
+ | |
+ # Checks if the given name is known anywhere in the lexpad | |
+ # and with lexical scope. | |
+ method is_lexical($name) { | |
+ self.is_scope($name, 'lexical') | |
+ } | |
+ | |
+ # Checks if the given name is known anywhere in the lexpad | |
+ # and with package scope. | |
+ method is_package($name) { | |
+ self.is_scope($name, 'package') | |
+ } | |
+ | |
+ # Checks if a given name is known in the lexpad anywhere | |
+ # with the specified scope. | |
+ method is_scope($name, $wanted_scope) { | |
+ my $i := +@!BLOCKS; | |
+ while $i > 0 { | |
+ $i := $i - 1; | |
+ my %sym := @!BLOCKS[$i].symbol($name); | |
+ if +%sym { | |
+ return %sym<scope> eq $wanted_scope; | |
+ } | |
+ } | |
+ 0; | |
+ } | |
+ | |
+ # Checks if the symbol is known. | |
+ method known_sym($/, @name) { | |
+ my $known := 0; | |
+ try { | |
+ self.find_sym(@name); | |
+ $known := 1; | |
+ } | |
+ $known | |
+ } | |
+ | |
+ # Finds a symbol that has a known value at compile time from the | |
+ # perspective of the current scope. Checks for lexicals, then if | |
+ # that fails tries package lookup. | |
+ method find_sym(@name) { | |
+ # Make sure it's not an empty name. | |
+ unless +@name { pir::die("Cannot look up empty name"); } | |
+ | |
+ # If it's a single-part name, look through the lexical | |
+ # scopes. | |
+ if +@name == 1 { | |
+ my $final_name := @name[0]; | |
+ my $i := +@!BLOCKS; | |
+ while $i > 0 { | |
+ $i := $i - 1; | |
+ my %sym := @!BLOCKS[$i].symbol($final_name); | |
+ if +%sym { | |
+ if pir::exists(%sym, 'value') { | |
+ return %sym<value>; | |
+ } | |
+ else { | |
+ pir::die("No compile-time value for $final_name"); | |
+ } | |
+ } | |
+ } | |
+ } | |
+ | |
+ # If it's a multi-part name, see if the containing package | |
+ # is a lexical somewhere. Otherwise we fall back to looking | |
+ # in GLOBALish. | |
+ my $result := $*GLOBALish; | |
+ if +@name >= 2 { | |
+ my $first := @name[0]; | |
+ my $i := +@!BLOCKS; | |
+ while $i > 0 { | |
+ $i := $i - 1; | |
+ my %sym := @!BLOCKS[$i].symbol($first); | |
+ if +%sym { | |
+ if pir::exists(%sym, 'value') { | |
+ $result := %sym<value>; | |
+ @name.shift(); | |
+ $i := 0; | |
+ } | |
+ else { | |
+ pir::die("No compile-time value for $first"); | |
+ } | |
+ } | |
+ } | |
+ } | |
+ | |
+ # Try to chase down the parts of the name. | |
+ for @name { | |
+ if pir::exists($result.WHO, ~$_) { | |
+ $result := ($result.WHO){$_}; | |
+ } | |
+ else { | |
+ pir::die("Could not locate compile-time value for symbol " ~ | |
+ pir::join('::', @name)); | |
+ } | |
+ } | |
+ | |
+ $result; | |
} | |
} | |
diff --git a/src/how/NQPClassHOW.pm b/src/how/NQPClassHOW.pm | |
index a146603..a972f24 100644 | |
--- a/src/how/NQPClassHOW.pm | |
+++ b/src/how/NQPClassHOW.pm | |
@@ -67,7 +67,7 @@ knowhow NQPClassHOW { | |
{}); | |
} | |
- method add_method($obj, $name, $code_obj) { | |
+ method add_method($obj, $name, $code_obj, $is_dispatcher?) { | |
if %!methods{$name} { | |
pir::die("This class already has a method named " ~ $name); | |
} | |
bash-3.2$ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment