Skip to content

Instantly share code, notes, and snippets.

@cognominal
Created April 3, 2012 17:06
Show Gist options
  • Save cognominal/2293712 to your computer and use it in GitHub Desktop.
Save cognominal/2293712 to your computer and use it in GitHub Desktop.
update for nqpq
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