| … | |
… | |
| 11 | use Safe; |
11 | use Safe; |
| 12 | use Net::SMTP; |
12 | use Net::SMTP; |
| 13 | use WeBWorK::Utils qw(runtime_use); |
13 | use WeBWorK::Utils qw(runtime_use); |
| 14 | use WeBWorK::PG::IO; |
14 | use WeBWorK::PG::IO; |
| 15 | |
15 | |
|
|
16 | |
| 16 | # loading GD within the Safe compartment has occasionally caused infinite recursion |
17 | # loading GD within the Safe compartment has occasionally caused infinite recursion |
| 17 | # Putting these use statements here seems to avoid this problem |
18 | # Putting these use statements here seems to avoid this problem |
| 18 | # It is not clear that this is essential once things are working properly. |
19 | # It is not clear that this is essential once things are working properly. |
| 19 | #use Exporter; |
20 | #use Exporter; |
| 20 | #use DynaLoader; |
21 | #use DynaLoader; |
| … | |
… | |
| 91 | |
92 | |
| 92 | =cut |
93 | =cut |
| 93 | |
94 | |
| 94 | sub evaluate_modules { |
95 | sub evaluate_modules { |
| 95 | my $self = shift; |
96 | my $self = shift; |
|
|
97 | my @modules = @_; |
| 96 | local $SIG{__DIE__} = "DEFAULT"; # we're going to be eval()ing code |
98 | local $SIG{__DIE__} = "DEFAULT"; # we're going to be eval()ing code |
| 97 | foreach (@_) { |
99 | foreach (@modules) { |
| 98 | #warn "attempting to load $_\n"; |
100 | #warn "attempting to load $_\n"; |
| 99 | # ensure that the name is in fact a base name |
101 | # ensure that the name is in fact a base name |
| 100 | s/\.pm$// and warn "fixing your broken package name: $_.pm => $_"; |
102 | s/\.pm$// and warn "fixing your broken package name: $_.pm => $_"; |
| 101 | # call runtime_use on the package name |
103 | # call runtime_use on the package name |
| 102 | # don't worry -- runtime_use won't load a package twice! |
104 | # don't worry -- runtime_use won't load a package twice! |
| … | |
… | |
| 104 | warn "Failed to evaluate module $_: $@" if $@; |
106 | warn "Failed to evaluate module $_: $@" if $@; |
| 105 | # record this in the appropriate place |
107 | # record this in the appropriate place |
| 106 | push @{$self->{ra_included_modules}}, "\%${_}::"; |
108 | push @{$self->{ra_included_modules}}, "\%${_}::"; |
| 107 | } |
109 | } |
| 108 | } |
110 | } |
| 109 | |
111 | # old code for runtime_use |
|
|
112 | # if ( -r "${courseScriptsDirectory}${module_name}.pm" ) { |
|
|
113 | # eval(qq! require "${courseScriptsDirectory}${module_name}.pm"; import ${module_name};! ); |
|
|
114 | # warn "Errors in including the module ${courseScriptsDirectory}$module_name.pm $@" if $@; |
|
|
115 | # } else { |
|
|
116 | # eval(qq! require "${module_name}.pm"; import ${module_name};! ); |
|
|
117 | # warn "Errors in including either the module $module_name.pm or ${courseScriptsDirectory}${module_name}.pm $@" if $@; |
|
|
118 | # } |
| 110 | =head2 load_extra_packages |
119 | =head2 load_extra_packages |
| 111 | |
120 | |
| 112 | Usage: $obj -> load_extra_packages('AlgParserWithImplicitExpand', |
121 | Usage: $obj -> load_extra_packages('AlgParserWithImplicitExpand', |
| 113 | 'Expr','ExprWithImplicitExpand'); |
122 | 'Expr','ExprWithImplicitExpand'); |
| 114 | |
123 | |
| … | |
… | |
| 235 | # 2, there is no longer a global namespace. To get around this, IO functions |
244 | # 2, there is no longer a global namespace. To get around this, IO functions |
| 236 | # which need access to course-specific data are now defined in the IO.pl macro |
245 | # which need access to course-specific data are now defined in the IO.pl macro |
| 237 | # file, which has access to the problem environment. Several entries have been |
246 | # file, which has access to the problem environment. Several entries have been |
| 238 | # added to the problem environment to support this move. |
247 | # added to the problem environment to support this move. |
| 239 | # |
248 | # |
|
|
249 | |
|
|
250 | |
|
|
251 | # Useful for timing portions of the translating process |
|
|
252 | # The timer $WeBWorK::timer0 is defined in the module WeBWorK.pm |
|
|
253 | # You must make sure that the code in that script for initialzing the |
|
|
254 | # timer is active. |
|
|
255 | |
|
|
256 | sub time_it { |
|
|
257 | my $msg = shift; |
|
|
258 | $WeBWorK::timer0->continue($msg) if defined($WeBWorK::timer0); |
|
|
259 | } |
|
|
260 | |
| 240 | my %shared_subroutine_hash = ( |
261 | my %shared_subroutine_hash = ( |
|
|
262 | 'time_it' => 'Translator', |
| 241 | '&PG_answer_eval' => 'Translator', |
263 | '&PG_answer_eval' => 'Translator', |
| 242 | '&PG_restricted_eval' => 'Translator', |
264 | '&PG_restricted_eval' => 'Translator', |
| 243 | '&be_strict' => 'Translator', |
265 | '&be_strict' => 'Translator', |
| 244 | '&PGsort' => 'Translator', |
266 | '&PGsort' => 'Translator', |
| 245 | '&dumpvar' => 'Translator', |
267 | '&dumpvar' => 'Translator', |
| … | |
… | |
| 250 | #'&surePathToTmpFile' => 'IO', # moved to IO.pl |
272 | #'&surePathToTmpFile' => 'IO', # moved to IO.pl |
| 251 | '&fileFromPath' => 'IO', |
273 | '&fileFromPath' => 'IO', |
| 252 | '&directoryFromPath' => 'IO', |
274 | '&directoryFromPath' => 'IO', |
| 253 | '&createFile' => 'IO', |
275 | '&createFile' => 'IO', |
| 254 | '&createDirectory' => 'IO', |
276 | '&createDirectory' => 'IO', |
| 255 | '&getImageDimmensions' => 'IO', |
277 | # '&getImageDimmensions' => 'IO', |
| 256 | '&dvipng' => 'IO', |
278 | # '&dvipng' => 'IO', |
| 257 | ); |
279 | ); |
| 258 | |
280 | |
| 259 | sub initialize { |
281 | sub initialize { |
| 260 | my $self = shift; |
282 | my $self = shift; |
| 261 | my $safe_cmpt = $self->{safe}; |
283 | my $safe_cmpt = $self->{safe}; |
| … | |
… | |
| 268 | #local($rf_answer_eval) = sub { $self->PG_answer_eval(@_); }; |
290 | #local($rf_answer_eval) = sub { $self->PG_answer_eval(@_); }; |
| 269 | #local($rf_restricted_eval) = sub { $self->PG_restricted_eval(@_); }; |
291 | #local($rf_restricted_eval) = sub { $self->PG_restricted_eval(@_); }; |
| 270 | #$safe_cmpt -> share('$rf_answer_eval'); |
292 | #$safe_cmpt -> share('$rf_answer_eval'); |
| 271 | #$safe_cmpt -> share('$rf_restricted_eval'); |
293 | #$safe_cmpt -> share('$rf_restricted_eval'); |
| 272 | use strict; |
294 | use strict; |
| 273 | |
295 | |
| 274 | # ra_included_modules is now populated independantly of @class_modules: |
|
|
| 275 | #$self->{ra_included_modules} = [@class_modules]; |
|
|
| 276 | |
|
|
| 277 | $safe_cmpt -> share_from('main', $self->{ra_included_modules} ); |
296 | $safe_cmpt -> share_from('main', $self->{ra_included_modules} ); |
| 278 | # the above line will get changed when we fix the PG modules thing. heh heh. |
297 | # the above line will get changed when we fix the PG modules thing. heh heh. |
|
|
298 | } |
|
|
299 | |
|
|
300 | |
|
|
301 | ################################################################ |
|
|
302 | # Preloading the macro files |
|
|
303 | ################################################################ |
|
|
304 | |
|
|
305 | # Preloading the macro files can significantly speed up the translation process. |
|
|
306 | # Files are read into a separate safe compartment (typically Safe::Root1::) |
|
|
307 | # This means that all non-explicit subroutine references and those explicitly prefixed by main:: |
|
|
308 | # are prefixed by Safe::Root1:: |
|
|
309 | # These subroutines (but not the constants) are then explicitly exported to the current |
|
|
310 | # safe compartment Safe::Rootx:: |
|
|
311 | |
|
|
312 | # Although they are not large, it is important to import PG.pl and dangerousMacros.pl into the |
|
|
313 | # cached safe compartment as well. This is because a call in PGbasicmacros.pl to NEW_ANSWER_NAME |
|
|
314 | # which is defined in PG.pl would actually be a call to Safe::Root1::NEW_ANSWER_NAME since |
|
|
315 | # PGbasicmacros is compiled into the SAfe::Root1:: compartment. If PG.pl has only been compiled into |
|
|
316 | # the current Safe compartment, this call will fail. There are many calls between PG.pl, dangerousMacros, |
|
|
317 | # PGbasicmacros and PGanswermacros so it is easiest to have all of them defined in Safe::Root1:: |
|
|
318 | # There subroutines are still available in the current safe compartment. |
|
|
319 | # Sharing the hash %Safe::Root1:: in the current compartment means that any references to Safe::Root1::NEW_ANSWER_NAME |
|
|
320 | # will be found as long as NEW_ANSWER_NAME has been defined in Safe::Root1:: |
|
|
321 | # |
|
|
322 | # Constants and references to subroutines in other macro files have to be handled carefully in preloaded files. |
|
|
323 | # For example a call to main::display_matrix (defined in PGmatrixmacros.pl) will become Safe::Root1::display_matrix and |
|
|
324 | # will fail since PGmatrixmacros.pl is loaded only into the current safe compartment Safe::Rootx::. |
|
|
325 | # The value of main:: has to be evaluated at runtime in order to make this work. Hence something like |
|
|
326 | # my $temp_code = eval('\&main::display_matrix'); |
|
|
327 | # &$temp_code($matrix_object_to_be_displayed); |
|
|
328 | # in PGanswermacros.pl |
|
|
329 | # would reference the run time value of main::, namely Safe::Rootx:: |
|
|
330 | # There may be a clearer or more efficient way to obtain the runtime value of main:: |
|
|
331 | |
|
|
332 | |
|
|
333 | sub pre_load_macro_files { |
|
|
334 | time_it("Begin pre_load_macro_files"); |
|
|
335 | my $self = shift; |
|
|
336 | my $cached_safe_cmpt = shift; |
|
|
337 | my $dirName = shift; |
|
|
338 | my @fileNameList = @_; |
|
|
339 | my $debugON = 0; # This helps with debugging the loading of macro files |
|
|
340 | |
|
|
341 | ################################################################ |
|
|
342 | # prepare safe_cache |
|
|
343 | ################################################################ |
|
|
344 | $cached_safe_cmpt -> share(keys %shared_subroutine_hash); |
|
|
345 | no strict; |
|
|
346 | local(%envir) = %{ $self ->{envir} }; |
|
|
347 | $cached_safe_cmpt -> share('%envir'); |
|
|
348 | use strict; |
|
|
349 | $cached_safe_cmpt -> share_from('main', $self->{ra_included_modules} ); |
|
|
350 | $cached_safe_cmpt->mask(Opcode::full_opset()); # allow no operations |
|
|
351 | $cached_safe_cmpt->permit(qw( :default )); |
|
|
352 | $cached_safe_cmpt->permit(qw(time)); # used to determine whether solutions are visible. |
|
|
353 | $cached_safe_cmpt->permit(qw( atan2 sin cos exp log sqrt )); |
|
|
354 | |
|
|
355 | # just to make sure we'll deny some things specifically |
|
|
356 | $cached_safe_cmpt->deny(qw(entereval)); |
|
|
357 | $cached_safe_cmpt->deny(qw ( unlink symlink system exec )); |
|
|
358 | $cached_safe_cmpt->deny(qw(print require)); |
|
|
359 | |
|
|
360 | ################################################################ |
|
|
361 | # read in macro files |
|
|
362 | ################################################################ |
|
|
363 | |
|
|
364 | foreach my $fileName (@fileNameList) { |
|
|
365 | # determine whether the file has already been loaded by checking for |
|
|
366 | # subroutine named _${macro_file_name}_init |
|
|
367 | my $macro_file_name = $fileName; |
|
|
368 | $macro_file_name =~s/\.pl//; # trim off the extension |
|
|
369 | $macro_file_name =~s/\.pg//; # sometimes the extension is .pg (e.g. CAPA files) |
|
|
370 | my $init_subroutine_name = "_${macro_file_name}_init"; |
|
|
371 | my $macro_file_loaded = defined(&{$cached_safe_cmpt->root."::$init_subroutine_name"}) ? 1 : 0; |
|
|
372 | |
|
|
373 | |
|
|
374 | if ( $macro_file_loaded ) { |
|
|
375 | warn "$macro_file_name is already loaded" if $debugON; |
|
|
376 | }else { |
|
|
377 | warn "reading and evaluating $macro_file_name from $dirName/$fileName" if $debugON; |
|
|
378 | ### read in file |
|
|
379 | my $filePath = "$dirName/$fileName"; |
|
|
380 | local(*MACROFILE); |
|
|
381 | local($/); |
|
|
382 | $/ = undef; # allows us to treat the file as a single line |
|
|
383 | open(MACROFILE, "<$filePath") || die "Cannot open file: $filePath"; |
|
|
384 | my $string = <MACROFILE>; |
|
|
385 | close(MACROFILE); |
|
|
386 | |
|
|
387 | |
|
|
388 | ################################################################ |
|
|
389 | # Evaluate macro files |
|
|
390 | ################################################################ |
|
|
391 | # FIXME The following hardwired behavior should be modifiable |
|
|
392 | # either in the procedure call or in global.conf: |
|
|
393 | # |
|
|
394 | # PG.pl, IO.pl and dangerousMacros.pl are loaded without restriction |
|
|
395 | # All other files are loaded with restriction |
|
|
396 | # |
|
|
397 | my $store_mask; |
|
|
398 | if ($fileName =~ /PG.pl|dangerousMacros.pl|IO.pl/) { |
|
|
399 | $store_mask = $cached_safe_cmpt->mask(); |
|
|
400 | $cached_safe_cmpt ->mask(Opcode::empty_opset()); |
|
|
401 | } |
|
|
402 | $cached_safe_cmpt -> reval("package main;\n" .$string); |
|
|
403 | warn "preload Macros: errors in compiling $macro_file_name:<br> $@" if $@; |
|
|
404 | if ($fileName eq 'PG.pl') { |
|
|
405 | $cached_safe_cmpt ->mask($store_mask); |
|
|
406 | warn "mask restored after $fileName" if $debugON; |
|
|
407 | } |
|
|
408 | |
|
|
409 | |
|
|
410 | } |
|
|
411 | } |
|
|
412 | |
|
|
413 | ################################################################################ |
|
|
414 | # load symbol table |
|
|
415 | ################################################################################ |
|
|
416 | warn "begin loading symbol table " if $debugON; |
|
|
417 | no strict 'refs'; |
|
|
418 | my %symbolHash = %{$cached_safe_cmpt->root.'::'}; |
|
|
419 | use strict 'refs'; |
|
|
420 | my @subroutine_names; |
|
|
421 | |
|
|
422 | foreach my $name (keys %symbolHash) { |
|
|
423 | # weed out internal symbols |
|
|
424 | next if $name =~ /^(INC|_|__ANON__|main::)$/; |
|
|
425 | if ( defined(&{*{$symbolHash{$name}}}) ) { |
|
|
426 | # warn "subroutine $name" if $debugON;; |
|
|
427 | push(@subroutine_names, "&$name"); |
|
|
428 | } |
|
|
429 | } |
|
|
430 | |
|
|
431 | warn "Loading symbols into active safe compartment:<br> ", join(" ",sort @subroutine_names) if $debugON; |
|
|
432 | $self->{safe} -> share_from($cached_safe_cmpt->root,[@subroutine_names]); |
|
|
433 | |
|
|
434 | # Also need to share the cached safe compartment symbol hash in the current safe compartment. |
|
|
435 | # This is necessary because the macro files have been read into the cached safe compartment |
|
|
436 | # So all subroutines have the implied names Safe::Root1::subroutine |
|
|
437 | # When they call each other we need to make sure that they can reach each other |
|
|
438 | # through the Safe::Root1 symbol table. |
|
|
439 | |
|
|
440 | $self->{safe} -> share('%'.$cached_safe_cmpt->root.'::'); |
|
|
441 | warn 'Sharing '.'%'. $cached_safe_cmpt->root. '::' if $debugON; |
|
|
442 | time_it("End pre_load_macro_files"); |
|
|
443 | # return empty string. |
|
|
444 | ''; |
| 279 | } |
445 | } |
| 280 | |
446 | |
| 281 | sub environment{ |
447 | sub environment{ |
| 282 | my $self = shift; |
448 | my $self = shift; |
| 283 | my $envirref = shift; |
449 | my $envirref = shift; |